mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-12 03:48:56 +00:00
Merge addd917894 into 5cdebded90
This commit is contained in:
commit
859a25cd24
12 changed files with 8054 additions and 0 deletions
|
|
@ -394,6 +394,7 @@ Set `LITELLM_PROXY_API_BASE` and `LITELLM_PROXY_API_KEY` and every model call th
|
|||
| [Text Completion Codestral (`text-completion-codestral`)](https://docs.litellm.ai/docs/providers/codestral) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Text Completion OpenAI (`text-completion-openai`)](https://docs.litellm.ai/docs/providers/text_completion_openai) | ✅ | ✅ | ✅ | | | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [Together AI (`together_ai`)](https://docs.litellm.ai/docs/providers/togetherai) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [TokenLab (`tokenlab`)](https://tokenlab.sh/docs/en/integrations/litellm) | ✅ | ✅ | ✅ | ✅ | | | | | | |
|
||||
| [Topaz (`topaz`)](https://docs.litellm.ai/docs/providers/topaz) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Triton (`triton`)](https://docs.litellm.ai/docs/providers/triton-inference-server) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Typesafe Decisions API (`typesafe`)](https://docs.litellm.ai/docs/providers) | | | | | | | | | | |
|
||||
|
|
|
|||
|
|
@ -690,6 +690,7 @@ qwen_ai_platform_models: Set = set()
|
|||
moonshot_models: Set = set()
|
||||
publicai_models: Set = set()
|
||||
darkbloom_models: Set = set()
|
||||
tokenlab_models: Set[str] = set()
|
||||
v0_models: Set = set()
|
||||
morph_models: Set = set()
|
||||
lambda_ai_models: Set = set()
|
||||
|
|
@ -955,6 +956,8 @@ def _populate_provider_model_sets(model_cost_map: Dict) -> None:
|
|||
publicai_models.add(key)
|
||||
elif value.get("litellm_provider") == "darkbloom":
|
||||
darkbloom_models.add(key)
|
||||
elif value.get("litellm_provider") == "tokenlab" and isinstance(key, str):
|
||||
tokenlab_models.add(key)
|
||||
elif value.get("litellm_provider") == "v0":
|
||||
v0_models.add(key)
|
||||
elif value.get("litellm_provider") == "morph":
|
||||
|
|
@ -1118,6 +1121,7 @@ model_list = list(
|
|||
| moonshot_models
|
||||
| publicai_models
|
||||
| darkbloom_models
|
||||
| tokenlab_models
|
||||
| v0_models
|
||||
| morph_models
|
||||
| lambda_ai_models
|
||||
|
|
@ -1230,6 +1234,7 @@ def _build_models_by_provider() -> dict:
|
|||
"moonshot": moonshot_models,
|
||||
"publicai": publicai_models,
|
||||
"darkbloom": darkbloom_models,
|
||||
"tokenlab": tokenlab_models,
|
||||
"v0": v0_models,
|
||||
"morph": morph_models,
|
||||
"lambda_ai": lambda_ai_models,
|
||||
|
|
|
|||
|
|
@ -960,6 +960,7 @@ openai_compatible_endpoints: Final[list] = [
|
|||
"https://api.inference.wandb.ai/v1",
|
||||
"https://api.clarifai.com/v2/ext/openai/v1",
|
||||
"https://api.libertai.io/v1",
|
||||
"https://api.tokenlab.sh/v1",
|
||||
"https://pinstripes.io/v1",
|
||||
"https://api.meta.ai/v1",
|
||||
"https://api.sailresearch.com/v1",
|
||||
|
|
@ -1037,6 +1038,7 @@ openai_compatible_providers: Final[list] = [
|
|||
"docker_model_runner",
|
||||
"ragflow",
|
||||
"pinstripes", # Pinstripes - JSON-configured provider
|
||||
"tokenlab", # TokenLab - JSON-configured provider
|
||||
"darkbloom",
|
||||
"meta", # Meta Model API (Muse Spark) - JSON-configured provider
|
||||
"cognition",
|
||||
|
|
|
|||
|
|
@ -78,6 +78,14 @@ class JSONProviderRegistry:
|
|||
return False
|
||||
return "/v1/responses" in provider.supported_endpoints
|
||||
|
||||
@classmethod
|
||||
def supports_anthropic_messages_api(cls, slug: str) -> bool:
|
||||
"""Check if a JSON provider supports the Anthropic Messages API"""
|
||||
provider = cls._providers.get(slug)
|
||||
if provider is None:
|
||||
return False
|
||||
return "/v1/messages" in provider.supported_endpoints
|
||||
|
||||
@classmethod
|
||||
def list_providers(cls) -> list:
|
||||
"""List all registered provider slugs"""
|
||||
|
|
|
|||
|
|
@ -186,6 +186,15 @@
|
|||
"api_base_env": "CORTECS_API_BASE",
|
||||
"supported_endpoints": ["/v1/chat/completions", "/v1/responses", "/v1/messages"]
|
||||
},
|
||||
"tokenlab": {
|
||||
"base_url": "https://api.tokenlab.sh/v1",
|
||||
"api_key_env": "TOKENLAB_API_KEY",
|
||||
"api_base_env": "TOKENLAB_API_BASE",
|
||||
"param_mappings": {
|
||||
"max_completion_tokens": "max_tokens"
|
||||
},
|
||||
"supported_endpoints": ["/v1/chat/completions", "/v1/messages", "/v1/responses", "/v1/embeddings"]
|
||||
},
|
||||
"pinstripes": {
|
||||
"base_url": "https://pinstripes.io/v1",
|
||||
"api_key_env": "PINSTRIPES_API_KEY",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1664,6 +1664,23 @@
|
|||
"a2a": false
|
||||
}
|
||||
},
|
||||
"tokenlab": {
|
||||
"display_name": "TokenLab (`tokenlab`)",
|
||||
"url": "https://tokenlab.sh/docs/en/integrations/litellm",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"messages": true,
|
||||
"responses": true,
|
||||
"embeddings": true,
|
||||
"image_generations": false,
|
||||
"audio_transcriptions": false,
|
||||
"audio_speech": false,
|
||||
"moderations": false,
|
||||
"batches": false,
|
||||
"rerank": false,
|
||||
"a2a": false
|
||||
}
|
||||
},
|
||||
"nvidia_nim": {
|
||||
"display_name": "Nvidia NIM (`nvidia_nim`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/nvidia_nim",
|
||||
|
|
|
|||
|
|
@ -3263,6 +3263,34 @@
|
|||
],
|
||||
"default_model_placeholder": "gpt-3.5-turbo"
|
||||
},
|
||||
{
|
||||
"provider": "TOKENLAB",
|
||||
"provider_display_name": "TokenLab",
|
||||
"litellm_provider": "tokenlab",
|
||||
"credential_fields": [
|
||||
{
|
||||
"key": "api_base",
|
||||
"label": "API Base",
|
||||
"placeholder": "https://api.tokenlab.sh/v1",
|
||||
"tooltip": null,
|
||||
"required": false,
|
||||
"field_type": "text",
|
||||
"options": null,
|
||||
"default_value": null
|
||||
},
|
||||
{
|
||||
"key": "api_key",
|
||||
"label": "API Key",
|
||||
"placeholder": null,
|
||||
"tooltip": null,
|
||||
"required": true,
|
||||
"field_type": "password",
|
||||
"options": null,
|
||||
"default_value": null
|
||||
}
|
||||
],
|
||||
"default_model_placeholder": "tokenlab/gpt-5.5"
|
||||
},
|
||||
{
|
||||
"provider": "TOPAZ",
|
||||
"provider_display_name": "Topaz",
|
||||
|
|
|
|||
|
|
@ -4172,6 +4172,7 @@ class LlmProviders(str, Enum):
|
|||
PARASAIL = "parasail"
|
||||
XIAOMI_MIMO = "xiaomi_mimo"
|
||||
TENSORMESH = "tensormesh"
|
||||
TOKENLAB = "tokenlab"
|
||||
LIBERTAI = "libertai"
|
||||
PINSTRIPES = "pinstripes"
|
||||
COGNITION = "cognition"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2469,6 +2469,23 @@
|
|||
"a2a": false
|
||||
}
|
||||
},
|
||||
"tokenlab": {
|
||||
"display_name": "TokenLab (`tokenlab`)",
|
||||
"url": "https://tokenlab.sh/docs/en/integrations/litellm",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"messages": true,
|
||||
"responses": true,
|
||||
"embeddings": true,
|
||||
"image_generations": false,
|
||||
"audio_transcriptions": false,
|
||||
"audio_speech": false,
|
||||
"moderations": false,
|
||||
"batches": false,
|
||||
"rerank": false,
|
||||
"a2a": false
|
||||
}
|
||||
},
|
||||
"tensormesh": {
|
||||
"display_name": "Tensormesh (`tensormesh`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/tensormesh",
|
||||
|
|
|
|||
200
tests/unit/llms/openai_like/test_tokenlab_provider.py
Normal file
200
tests/unit/llms/openai_like/test_tokenlab_provider.py
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
import pytest
|
||||
|
||||
import litellm
|
||||
|
||||
|
||||
TOKENLAB_API_BASE = "https://api.tokenlab.sh/v1"
|
||||
TOKENLAB_SAMPLE_MODELS = [
|
||||
"tokenlab/gpt-5.5",
|
||||
"tokenlab/claude-opus-4-8",
|
||||
"tokenlab/gemini-3.5-flash",
|
||||
"tokenlab/deepseek-v4-pro",
|
||||
"tokenlab/qwen3.7-max",
|
||||
"tokenlab/text-embedding-3-small",
|
||||
]
|
||||
|
||||
|
||||
def test_tokenlab_json_registry():
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
|
||||
assert litellm.LlmProviders.TOKENLAB.value == "tokenlab"
|
||||
assert litellm.LlmProviders("tokenlab") == litellm.LlmProviders.TOKENLAB
|
||||
assert JSONProviderRegistry.exists("tokenlab")
|
||||
config = JSONProviderRegistry.get("tokenlab")
|
||||
assert config is not None
|
||||
assert config.base_url == TOKENLAB_API_BASE
|
||||
assert config.api_key_env == "TOKENLAB_API_KEY"
|
||||
assert config.api_base_env == "TOKENLAB_API_BASE"
|
||||
assert config.param_mappings["max_completion_tokens"] == "max_tokens"
|
||||
assert config.supported_endpoints == [
|
||||
"/v1/chat/completions",
|
||||
"/v1/messages",
|
||||
"/v1/responses",
|
||||
"/v1/embeddings",
|
||||
]
|
||||
assert JSONProviderRegistry.supports_responses_api("tokenlab") is True
|
||||
assert JSONProviderRegistry.supports_anthropic_messages_api("tokenlab") is True
|
||||
|
||||
|
||||
def test_tokenlab_provider_detection_by_prefix():
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
model, provider, _, api_base = get_llm_provider("tokenlab/gpt-5.5")
|
||||
|
||||
assert model == "gpt-5.5"
|
||||
assert provider == "tokenlab"
|
||||
assert api_base == TOKENLAB_API_BASE
|
||||
|
||||
|
||||
def test_tokenlab_api_base_override():
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
_, provider, api_key, api_base = get_llm_provider(
|
||||
model="tokenlab/gpt-5.5",
|
||||
api_base="https://custom.tokenlab.example/v1",
|
||||
api_key="sk-test",
|
||||
)
|
||||
|
||||
assert provider == "tokenlab"
|
||||
assert api_base == "https://custom.tokenlab.example/v1"
|
||||
assert api_key == "sk-test"
|
||||
|
||||
|
||||
def test_tokenlab_url_autodetection():
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
_, provider, _, api_base = get_llm_provider(
|
||||
model="gpt-5.5",
|
||||
api_base=TOKENLAB_API_BASE,
|
||||
)
|
||||
|
||||
assert provider == "tokenlab"
|
||||
assert api_base == TOKENLAB_API_BASE
|
||||
|
||||
|
||||
def test_tokenlab_chat_complete_url():
|
||||
from litellm.llms.openai_like.dynamic_config import create_config_class
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
|
||||
config = create_config_class(JSONProviderRegistry.get("tokenlab"))()
|
||||
|
||||
assert (
|
||||
config.get_complete_url(
|
||||
api_base=None,
|
||||
api_key=None,
|
||||
model="gpt-5.5",
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
)
|
||||
== "https://api.tokenlab.sh/v1/chat/completions"
|
||||
)
|
||||
|
||||
|
||||
def test_tokenlab_responses_api_config():
|
||||
from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig
|
||||
from litellm.utils import ProviderConfigManager
|
||||
|
||||
config = ProviderConfigManager.get_provider_responses_api_config(
|
||||
provider="tokenlab",
|
||||
model="tokenlab/gpt-5.5",
|
||||
)
|
||||
|
||||
assert isinstance(config, OpenAIResponsesAPIConfig)
|
||||
assert config.custom_llm_provider == "tokenlab"
|
||||
assert config.get_complete_url(api_base=None, litellm_params={}) == "https://api.tokenlab.sh/v1/responses"
|
||||
|
||||
|
||||
def test_tokenlab_anthropic_messages_config():
|
||||
from litellm.llms.openai_like.messages.transformation import (
|
||||
OpenAILikeAnthropicMessagesConfig,
|
||||
)
|
||||
from litellm.utils import ProviderConfigManager
|
||||
|
||||
config = ProviderConfigManager.get_provider_anthropic_messages_config(
|
||||
provider=litellm.LlmProviders.TOKENLAB,
|
||||
model="claude-opus-4-8",
|
||||
)
|
||||
|
||||
assert isinstance(config, OpenAILikeAnthropicMessagesConfig)
|
||||
assert (
|
||||
config.get_complete_url(
|
||||
api_base=TOKENLAB_API_BASE,
|
||||
api_key="sk-test",
|
||||
model="claude-opus-4-8",
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
)
|
||||
== "https://api.tokenlab.sh/v1/messages"
|
||||
)
|
||||
|
||||
|
||||
class TestTokenLabCostMap:
|
||||
@pytest.fixture(autouse=True)
|
||||
def _use_local_model_cost_map(self, monkeypatch):
|
||||
original_model_cost = litellm.model_cost
|
||||
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
||||
litellm.model_cost = litellm.get_model_cost_map(url="")
|
||||
litellm.get_model_info.cache_clear()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
litellm.model_cost = original_model_cost
|
||||
litellm.get_model_info.cache_clear()
|
||||
|
||||
def test_sample_models_registered(self):
|
||||
for model in TOKENLAB_SAMPLE_MODELS:
|
||||
info = litellm.get_model_info(model)
|
||||
assert info["litellm_provider"] == "tokenlab"
|
||||
assert info["mode"] in {"chat", "embedding"}
|
||||
|
||||
def test_claude_model_cost_map_keeps_schema_supported_endpoints(self):
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
|
||||
endpoints = litellm.model_cost["tokenlab/claude-opus-4-8"]["supported_endpoints"]
|
||||
assert endpoints == ["/v1/chat/completions", "/v1/responses"]
|
||||
|
||||
provider_config = JSONProviderRegistry.get("tokenlab")
|
||||
assert provider_config is not None
|
||||
assert "/v1/messages" in provider_config.supported_endpoints
|
||||
|
||||
def test_chat_cost_is_wired(self):
|
||||
prompt_cost, completion_cost = litellm.cost_per_token(
|
||||
model="tokenlab/gpt-5.5",
|
||||
prompt_tokens=100_000,
|
||||
completion_tokens=100_000,
|
||||
)
|
||||
assert prompt_cost == pytest.approx(litellm.model_cost["tokenlab/gpt-5.5"]["input_cost_per_token"] * 100_000)
|
||||
assert completion_cost == pytest.approx(
|
||||
litellm.model_cost["tokenlab/gpt-5.5"]["output_cost_per_token"] * 100_000
|
||||
)
|
||||
assert prompt_cost > 0 and completion_cost > 0
|
||||
|
||||
def test_chat_cost_uses_the_long_context_tier(self):
|
||||
assert (
|
||||
litellm.model_cost["tokenlab/gpt-5.5"]["input_cost_per_token_above_272k_tokens"]
|
||||
> litellm.model_cost["tokenlab/gpt-5.5"]["input_cost_per_token"]
|
||||
)
|
||||
|
||||
prompt_cost, completion_cost = litellm.cost_per_token(
|
||||
model="tokenlab/gpt-5.5",
|
||||
prompt_tokens=300_000,
|
||||
completion_tokens=100_000,
|
||||
)
|
||||
assert prompt_cost == pytest.approx(
|
||||
litellm.model_cost["tokenlab/gpt-5.5"]["input_cost_per_token_above_272k_tokens"] * 300_000
|
||||
)
|
||||
assert completion_cost == pytest.approx(
|
||||
litellm.model_cost["tokenlab/gpt-5.5"]["output_cost_per_token_above_272k_tokens"] * 100_000
|
||||
)
|
||||
|
||||
def test_embedding_cost_is_wired(self):
|
||||
prompt_cost, completion_cost = litellm.cost_per_token(
|
||||
model="tokenlab/text-embedding-3-small",
|
||||
prompt_tokens=1_000_000,
|
||||
completion_tokens=0,
|
||||
)
|
||||
assert prompt_cost == pytest.approx(
|
||||
litellm.model_cost["tokenlab/text-embedding-3-small"]["input_cost_per_token"] * 1_000_000
|
||||
)
|
||||
assert prompt_cost > 0
|
||||
assert completion_cost == 0
|
||||
Loading…
Add table
Reference in a new issue