From f0b0bba082daf2a5e6fe4e6042176fe76e75a3fb Mon Sep 17 00:00:00 2001 From: bylinelog-bot <344363242+bylinelog-bot@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:03:58 +0800 Subject: [PATCH] feat(tokenlab): add TokenLab as a JSON-configured OpenAI-compatible provider --- litellm/constants.py | 3 ++ litellm/llms/openai_like/providers.json | 10 ++++ .../provider_endpoints_support_backup.json | 26 ++++++++++ litellm/types/utils.py | 1 + provider_endpoints_support.json | 26 ++++++++++ .../openai_like/test_tokenlab_provider.py | 51 +++++++++++++++++++ .../public_endpoints/test_public_endpoints.py | 1 + 7 files changed, 118 insertions(+) create mode 100644 tests/unit/llms/openai_like/test_tokenlab_provider.py diff --git a/litellm/constants.py b/litellm/constants.py index cfee777f36a..d802196cf70 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1051,6 +1051,7 @@ openai_compatible_endpoints: Final[list[str]] = [ "https://api.aiand.com/v1", "https://api.synthetic.new/openai/v1", "https://serverless.tensormesh.ai/v1", + "https://api.tokenlab.sh/v1", "https://api.stima.tech/v1", "https://nano-gpt.com/api/v1", "https://api.poe.com/v1", @@ -1120,6 +1121,7 @@ openai_compatible_providers: Final[list[str]] = [ "aiand", # ai& - JSON-configured provider "synthetic", # Synthetic - JSON-configured provider "tensormesh", # Tensormesh - JSON-configured provider + "tokenlab", # TokenLab - JSON-configured provider "apertis", # Apertis - JSON-configured provider "nano-gpt", # Nano-GPT - JSON-configured provider "poe", # Poe - JSON-configured provider @@ -1181,6 +1183,7 @@ openai_text_completion_compatible_providers: Final[list] = [ # providers that s "publicai", "synthetic", "tensormesh", + "tokenlab", "apertis", "nano-gpt", "poe", diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index ad47b334ed4..1bcd24951e9 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -144,6 +144,16 @@ }, "supported_endpoints": ["/v1/chat/completions", "/v1/responses"] }, + "tokenlab": { + "base_url": "https://api.tokenlab.sh/v1", + "api_key_env": "TOKENLAB_API_KEY", + "api_base_env": "TOKENLAB_API_BASE", + "base_class": "openai_gpt", + "param_mappings": { + "max_completion_tokens": "max_tokens" + }, + "supported_endpoints": ["/v1/chat/completions"] + }, "tensormesh": { "base_url": "https://serverless.tensormesh.ai/v1", "api_key_env": "TENSORMESH_INFERENCE_API_KEY", diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json index d33e61026df..9e3d86d1898 100644 --- a/litellm/provider_endpoints_support_backup.json +++ b/litellm/provider_endpoints_support_backup.json @@ -3837,6 +3837,32 @@ "generateContent": true } }, + "tokenlab": { + "display_name": "TokenLab (`tokenlab`)", + "url": "https://docs.litellm.ai/docs/providers/openai_compatible", + "endpoints": { + "chat_completions": true, + "responses": false, + "text_completion": false, + "embeddings": false, + "image_generations": false, + "image_edits": false, + "audio_transcriptions": false, + "audio_speech": false, + "rerank": false, + "ocr": false, + "search": false, + "moderations": false, + "realtime": false, + "video_generations": false, + "batches": false, + "messages": false, + "interactions": false, + "systemone": false, + "a2a": false, + "generateContent": false + } + }, "streamlake": { "display_name": "StreamLake (`streamlake`)", "url": "https://docs.litellm.ai/docs/providers/openai_compatible", diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 5905aaa3380..d67a2d7e1fa 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -4604,6 +4604,7 @@ class LlmProviders(str, Enum): PARASAIL = "parasail" XIAOMI_MIMO = "xiaomi_mimo" TENSORMESH = "tensormesh" + TOKENLAB = "tokenlab" LIBERTAI = "libertai" PINSTRIPES = "pinstripes" COGNITION = "cognition" diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index d33e61026df..9e3d86d1898 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -3837,6 +3837,32 @@ "generateContent": true } }, + "tokenlab": { + "display_name": "TokenLab (`tokenlab`)", + "url": "https://docs.litellm.ai/docs/providers/openai_compatible", + "endpoints": { + "chat_completions": true, + "responses": false, + "text_completion": false, + "embeddings": false, + "image_generations": false, + "image_edits": false, + "audio_transcriptions": false, + "audio_speech": false, + "rerank": false, + "ocr": false, + "search": false, + "moderations": false, + "realtime": false, + "video_generations": false, + "batches": false, + "messages": false, + "interactions": false, + "systemone": false, + "a2a": false, + "generateContent": false + } + }, "streamlake": { "display_name": "StreamLake (`streamlake`)", "url": "https://docs.litellm.ai/docs/providers/openai_compatible", diff --git a/tests/unit/llms/openai_like/test_tokenlab_provider.py b/tests/unit/llms/openai_like/test_tokenlab_provider.py new file mode 100644 index 00000000000..60d014c2721 --- /dev/null +++ b/tests/unit/llms/openai_like/test_tokenlab_provider.py @@ -0,0 +1,51 @@ +""" +Tests for TokenLab provider configuration (JSON-configured OpenAI-like provider). +""" + +import litellm + + +class TestTokenLabProviderConfig: + def test_tokenlab_in_provider_list(self): + from litellm import LlmProviders + + assert LlmProviders.TOKENLAB.value == "tokenlab" + assert "tokenlab" in litellm.provider_list + + def test_tokenlab_json_config_exists(self): + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + assert JSONProviderRegistry.exists("tokenlab") + tokenlab = JSONProviderRegistry.get("tokenlab") + assert tokenlab is not None + assert tokenlab.base_url == "https://api.tokenlab.sh/v1" + assert tokenlab.api_key_env == "TOKENLAB_API_KEY" + assert tokenlab.param_mappings.get("max_completion_tokens") == "max_tokens" + + def test_tokenlab_provider_resolution(self): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + model, provider, _api_key, api_base = get_llm_provider( + model="tokenlab/gpt-5.4-mini", + custom_llm_provider=None, + api_base=None, + api_key=None, + ) + + assert model == "gpt-5.4-mini" + assert provider == "tokenlab" + assert api_base == "https://api.tokenlab.sh/v1" + + def test_tokenlab_api_base_override(self): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + _model, provider, api_key, api_base = get_llm_provider( + model="tokenlab/gpt-5.4-mini", + custom_llm_provider=None, + api_base="https://custom.example.com/v1", + api_key="sk-test", + ) + + assert provider == "tokenlab" + assert api_base == "https://custom.example.com/v1" + assert api_key == "sk-test" diff --git a/tests/unit/proxy/public_endpoints/test_public_endpoints.py b/tests/unit/proxy/public_endpoints/test_public_endpoints.py index 48672c7a305..60d9c2c0ca6 100644 --- a/tests/unit/proxy/public_endpoints/test_public_endpoints.py +++ b/tests/unit/proxy/public_endpoints/test_public_endpoints.py @@ -587,6 +587,7 @@ ADD_MODEL_UNLISTED_PROVIDERS: Final = frozenset( "synthetic", "tensormesh", "text-completion-inception", + "tokenlab", "transcribe", "valkey", "xiaomi_mimo",