mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-29 06:33:28 +00:00
feat(refuseless): add provider and model pricing (#48497)
* feat(refuseless): add provider and model pricing * feat(refuseless): expose provider in Add Model catalog * test(refuseless): verify model input trimming after upstream fix * fix(refuseless): exclude unavailable free model from catalogs
This commit is contained in:
parent
b1f40b042b
commit
c905cd065d
8 changed files with 392 additions and 0 deletions
|
|
@ -249,5 +249,13 @@
|
|||
"/v1/responses",
|
||||
"/v1/messages"
|
||||
]
|
||||
},
|
||||
"refuseless": {
|
||||
"base_url": "https://api.refuseless.com/v1",
|
||||
"api_key_env": "REFUSELESS_API_KEY",
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/responses"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75900,5 +75900,110 @@
|
|||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"refuseless/glm-5.3-abliterated": {
|
||||
"cache_read_input_token_cost": 2.5e-07,
|
||||
"input_cost_per_token": 1.25e-06,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 131072,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.25e-06,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
},
|
||||
"refuseless/glm-5.3-flash-abliterated": {
|
||||
"cache_read_input_token_cost": 1.5e-07,
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 131072,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 8e-07,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
},
|
||||
"refuseless/deepseek-v4.1-flash-abliterated": {
|
||||
"cache_read_input_token_cost": 4.5e-08,
|
||||
"input_cost_per_token": 2.2e-07,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 130000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 7.5e-07,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
},
|
||||
"refuseless/deepseek-v4.1-flash-fast": {
|
||||
"cache_read_input_token_cost": 3e-09,
|
||||
"input_cost_per_token": 5e-08,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 262144,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-07,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
},
|
||||
"refuseless/minimax-m3.1-flash": {
|
||||
"cache_read_input_token_cost": 2e-09,
|
||||
"input_cost_per_token": 5e-08,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 512000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-07,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,23 @@
|
|||
}
|
||||
},
|
||||
"providers": {
|
||||
"refuseless": {
|
||||
"display_name": "Refuseless (`refuseless`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/refuseless",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"messages": false,
|
||||
"responses": true,
|
||||
"embeddings": false,
|
||||
"image_generations": false,
|
||||
"audio_transcriptions": false,
|
||||
"audio_speech": false,
|
||||
"moderations": false,
|
||||
"batches": false,
|
||||
"rerank": false,
|
||||
"a2a": false
|
||||
}
|
||||
},
|
||||
"a2a": {
|
||||
"display_name": "A2A (Agent-to-Agent) (`a2a`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/a2a",
|
||||
|
|
|
|||
|
|
@ -3231,6 +3231,34 @@
|
|||
],
|
||||
"default_model_placeholder": "gpt-3.5-turbo"
|
||||
},
|
||||
{
|
||||
"provider": "REFUSELESS",
|
||||
"provider_display_name": "Refuseless",
|
||||
"litellm_provider": "refuseless",
|
||||
"credential_fields": [
|
||||
{
|
||||
"key": "api_key",
|
||||
"label": "API Key",
|
||||
"placeholder": null,
|
||||
"tooltip": null,
|
||||
"required": true,
|
||||
"field_type": "password",
|
||||
"options": null,
|
||||
"default_value": null
|
||||
},
|
||||
{
|
||||
"key": "api_base",
|
||||
"label": "API Base",
|
||||
"placeholder": "https://api.refuseless.com/v1",
|
||||
"tooltip": "Optional. Leave blank to use the provider's default endpoint.",
|
||||
"required": false,
|
||||
"field_type": "text",
|
||||
"options": null,
|
||||
"default_value": null
|
||||
}
|
||||
],
|
||||
"default_model_placeholder": "refuseless/glm-5.3-abliterated"
|
||||
},
|
||||
{
|
||||
"provider": "REPLICATE",
|
||||
"provider_display_name": "Replicate",
|
||||
|
|
|
|||
|
|
@ -4415,6 +4415,7 @@ GenericBudgetConfigType = dict[str, BudgetConfig]
|
|||
|
||||
|
||||
class LlmProviders(str, Enum):
|
||||
REFUSELESS = "refuseless"
|
||||
OPENAI = "openai"
|
||||
CHATGPT = "chatgpt"
|
||||
OPENAI_LIKE = "openai_like" # embedding only
|
||||
|
|
|
|||
|
|
@ -75900,5 +75900,110 @@
|
|||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"refuseless/glm-5.3-abliterated": {
|
||||
"cache_read_input_token_cost": 2.5e-07,
|
||||
"input_cost_per_token": 1.25e-06,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 131072,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.25e-06,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
},
|
||||
"refuseless/glm-5.3-flash-abliterated": {
|
||||
"cache_read_input_token_cost": 1.5e-07,
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 131072,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 8e-07,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
},
|
||||
"refuseless/deepseek-v4.1-flash-abliterated": {
|
||||
"cache_read_input_token_cost": 4.5e-08,
|
||||
"input_cost_per_token": 2.2e-07,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 130000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 7.5e-07,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
},
|
||||
"refuseless/deepseek-v4.1-flash-fast": {
|
||||
"cache_read_input_token_cost": 3e-09,
|
||||
"input_cost_per_token": 5e-08,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 262144,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-07,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
},
|
||||
"refuseless/minimax-m3.1-flash": {
|
||||
"cache_read_input_token_cost": 2e-09,
|
||||
"input_cost_per_token": 5e-08,
|
||||
"litellm_provider": "refuseless",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 512000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-07,
|
||||
"source": "https://refuseless.com/pricing",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": false,
|
||||
"reasoning_effort_levels": [
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,23 @@
|
|||
}
|
||||
},
|
||||
"providers": {
|
||||
"refuseless": {
|
||||
"display_name": "Refuseless (`refuseless`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/refuseless",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"messages": false,
|
||||
"responses": true,
|
||||
"embeddings": false,
|
||||
"image_generations": false,
|
||||
"audio_transcriptions": false,
|
||||
"audio_speech": false,
|
||||
"moderations": false,
|
||||
"batches": false,
|
||||
"rerank": false,
|
||||
"a2a": false
|
||||
}
|
||||
},
|
||||
"a2a": {
|
||||
"display_name": "A2A (Agent-to-Agent) (`a2a`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/a2a",
|
||||
|
|
|
|||
111
tests/unit/llms/openai_like/test_refuseless_provider.py
Normal file
111
tests/unit/llms/openai_like/test_refuseless_provider.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import json
|
||||
from typing import Final
|
||||
|
||||
import pytest
|
||||
import respx
|
||||
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
from litellm.utils import trim_messages
|
||||
|
||||
|
||||
def test_refuseless_resolves_environment_and_explicit_credentials(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("REFUSELESS_API_KEY", "fake-environment-key")
|
||||
assert get_llm_provider(model="refuseless/glm-5.3-abliterated") == (
|
||||
"glm-5.3-abliterated",
|
||||
"refuseless",
|
||||
"fake-environment-key",
|
||||
"https://api.refuseless.com/v1",
|
||||
)
|
||||
assert get_llm_provider(
|
||||
model="refuseless/glm-5.3-abliterated", api_key="fake-explicit-key", api_base="https://gateway.example/v1"
|
||||
) == ("glm-5.3-abliterated", "refuseless", "fake-explicit-key", "https://gateway.example/v1")
|
||||
|
||||
|
||||
def test_refuseless_chat_forwards_reasoning_and_accounts_for_cache(local_model_cost_map: None) -> None:
|
||||
with respx.mock() as upstream:
|
||||
route: Final = upstream.post("https://api.refuseless.com/v1/chat/completions").respond(
|
||||
200,
|
||||
json={
|
||||
"id": "chatcmpl-test",
|
||||
"object": "chat.completion",
|
||||
"created": 1,
|
||||
"model": "glm-5.3-abliterated",
|
||||
"choices": [
|
||||
{"index": 0, "message": {"role": "assistant", "content": "hello"}, "finish_reason": "stop"}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 100,
|
||||
"completion_tokens": 5,
|
||||
"total_tokens": 105,
|
||||
"prompt_tokens_details": {"cached_tokens": 80},
|
||||
},
|
||||
},
|
||||
)
|
||||
response: Final = litellm.completion(
|
||||
model="refuseless/glm-5.3-abliterated",
|
||||
api_key="fake-test-key",
|
||||
messages=[{"role": "user", "content": "Say hello"}],
|
||||
reasoning_effort="low",
|
||||
)
|
||||
request: Final = route.calls.last.request
|
||||
body: Final = json.loads(request.content)
|
||||
assert request.headers["authorization"] == "Bearer fake-test-key"
|
||||
assert body["model"] == "glm-5.3-abliterated"
|
||||
assert body["reasoning_effort"] == "low"
|
||||
assert response.choices[0].message.content == "hello"
|
||||
rates: Final = litellm.model_cost["refuseless/glm-5.3-abliterated"]
|
||||
expected: Final = (
|
||||
20 * rates["input_cost_per_token"]
|
||||
+ 80 * rates["cache_read_input_token_cost"]
|
||||
+ 5 * rates["output_cost_per_token"]
|
||||
)
|
||||
assert litellm.completion_cost(completion_response=response) == pytest.approx(expected)
|
||||
|
||||
|
||||
def test_refuseless_uses_native_responses_endpoint() -> None:
|
||||
with respx.mock() as upstream:
|
||||
route: Final = upstream.post("https://api.refuseless.com/v1/responses").respond(
|
||||
200,
|
||||
json={
|
||||
"id": "resp-test",
|
||||
"object": "response",
|
||||
"created_at": 1,
|
||||
"model": "glm-5.3-abliterated",
|
||||
"status": "completed",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg-test",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"status": "completed",
|
||||
"content": [{"type": "output_text", "text": "hello", "annotations": []}],
|
||||
}
|
||||
],
|
||||
"usage": {"input_tokens": 4, "output_tokens": 3, "total_tokens": 7},
|
||||
},
|
||||
)
|
||||
response: Final = litellm.responses(
|
||||
model="refuseless/glm-5.3-abliterated",
|
||||
api_key="fake-test-key",
|
||||
input="Say hello",
|
||||
store=False,
|
||||
)
|
||||
assert route.calls.last.request.headers["authorization"] == "Bearer fake-test-key"
|
||||
assert json.loads(route.calls.last.request.content)["model"] == "glm-5.3-abliterated"
|
||||
assert response.output[0].content[0].text == "hello"
|
||||
|
||||
|
||||
def test_refuseless_models_trim_to_their_input_budget(local_model_cost_map: None) -> None:
|
||||
model_names: Final = tuple(model for model in litellm.model_cost if model.startswith("refuseless/"))
|
||||
assert model_names
|
||||
messages: Final = [{"role": "user", "content": "hello " * 500}]
|
||||
budget: Final = 100
|
||||
for model in model_names:
|
||||
trimmed: Final = trim_messages(
|
||||
messages,
|
||||
model=model,
|
||||
trim_ratio=budget / litellm.model_cost[model]["max_input_tokens"],
|
||||
)
|
||||
assert litellm.token_counter(model=model, messages=messages) > budget
|
||||
assert litellm.token_counter(model=model, messages=trimmed) <= budget
|
||||
Loading…
Add table
Reference in a new issue