From 0169b1f7759e726b8754917dc4c0aeb15a150e0a Mon Sep 17 00:00:00 2001 From: hgaib Date: Wed, 4 Nov 2026 04:31:59 +0000 Subject: [PATCH 1/2] feat(providers): add Token Kiosk as an OpenAI-compatible provider - Registers token_kiosk through the JSON provider registry so token_kiosk/ routes to https://agent-router.gaib.ai/v1/chat/completions with TOKEN_KIOSK_API_KEY and TOKEN_KIOSK_API_BASE - Adds LlmProviders.TOKEN_KIOSK and openai_compatible_providers entry - Registers token_kiosk_models in litellm models list and mapping - Adds unit tests covering credential resolution and completion dispatch --- litellm/__init__.py | 5 ++ litellm/constants.py | 1 + litellm/llms/openai_like/providers.json | 9 +++ litellm/types/utils.py | 1 + .../openai_like/test_token_kiosk_provider.py | 64 +++++++++++++++++++ 5 files changed, 80 insertions(+) create mode 100644 tests/unit/llms/openai_like/test_token_kiosk_provider.py diff --git a/litellm/__init__.py b/litellm/__init__.py index c1f4bb6a3df..0d964fe6b8b 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -685,6 +685,7 @@ qwen_ai_platform_models: Set = set() moonshot_models: Set = set() publicai_models: Set = set() darkbloom_models: Set = set() +token_kiosk_models: Final[Set[str]] = set() openinfer_models: Final[Set[str]] = set() kyma_models: Final[set[str]] = set() # mutable-ok: populated when the shared model catalog loads streamlake_models: Final[Set[str]] = set() @@ -979,6 +980,8 @@ def _populate_provider_model_sets(model_cost_map: Mapping[str, Mapping[str, obje publicai_models.add(key) elif value.get("litellm_provider") == "darkbloom": darkbloom_models.add(key) + elif value.get("litellm_provider") == "token_kiosk": + token_kiosk_models.add(key) elif value.get("litellm_provider") == "openinfer": openinfer_models.add(key) elif value.get("litellm_provider") == "kyma": @@ -1165,6 +1168,7 @@ model_list = list( | moonshot_models | publicai_models | darkbloom_models + | token_kiosk_models | openinfer_models | kyma_models | streamlake_models @@ -1299,6 +1303,7 @@ def _build_models_by_provider() -> dict: "moonshot": moonshot_models, "publicai": publicai_models, "darkbloom": darkbloom_models, + "token_kiosk": token_kiosk_models, "openinfer": openinfer_models, "kyma": kyma_models, "streamlake": streamlake_models, diff --git a/litellm/constants.py b/litellm/constants.py index 32b47bc6b46..c5735a9a953 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1131,6 +1131,7 @@ openai_compatible_providers: Final[list[str]] = [ "lm_studio", "galadriel", "zerogpu", + "token_kiosk", "github_copilot", # GitHub Copilot Chat API "chatgpt", # ChatGPT subscription API "novita", diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index c89ede6e77e..903494551d6 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -311,5 +311,14 @@ "supports_function_calling": true, "supports_reasoning": true } + }, + "token_kiosk": { + "base_url": "https://agent-router.gaib.ai/v1", + "api_key_env": "TOKEN_KIOSK_API_KEY", + "api_base_env": "TOKEN_KIOSK_API_BASE", + "provider_info": { + "supports_function_calling": true, + "supports_reasoning": true + } } } diff --git a/litellm/types/utils.py b/litellm/types/utils.py index ee80b63f93b..8ff1a82d34c 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -4769,6 +4769,7 @@ class LlmProviders(str, Enum): INFERENCEAPIS = "inferenceapis" HOSTINGER_ROUTER = "hostinger_router" ZEROGPU = "zerogpu" + TOKEN_KIOSK = "token_kiosk" DARKBLOOM = "darkbloom" META = "meta" SAIL = "sail" diff --git a/tests/unit/llms/openai_like/test_token_kiosk_provider.py b/tests/unit/llms/openai_like/test_token_kiosk_provider.py new file mode 100644 index 00000000000..cc58c28ef12 --- /dev/null +++ b/tests/unit/llms/openai_like/test_token_kiosk_provider.py @@ -0,0 +1,64 @@ +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 + + +def test_token_kiosk_resolves_environment_and_explicit_credentials(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("TOKEN_KIOSK_API_KEY", "fake-token-kiosk-key") + assert get_llm_provider(model="token_kiosk/claude-3-5-sonnet") == ( + "claude-3-5-sonnet", + "token_kiosk", + "fake-token-kiosk-key", + "https://agent-router.gaib.ai/v1", + ) + assert get_llm_provider( + model="token_kiosk/claude-3-5-sonnet", + api_key="explicit-key", + api_base="https://custom-router.example/v1", + ) == ( + "claude-3-5-sonnet", + "token_kiosk", + "explicit-key", + "https://custom-router.example/v1", + ) + + +def test_token_kiosk_chat_completion_dispatch() -> None: + with respx.mock() as upstream: + route: Final = upstream.post("https://agent-router.gaib.ai/v1/chat/completions").respond( + 200, + json={ + "id": "chatcmpl-token-kiosk-test", + "object": "chat.completion", + "created": 1724000000, + "model": "claude-3-5-sonnet", + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "Hello from Token Kiosk!"}, + "finish_reason": "stop", + } + ], + "usage": { + "prompt_tokens": 10, + "completion_tokens": 5, + "total_tokens": 15, + }, + }, + ) + response: Final = litellm.completion( + model="token_kiosk/claude-3-5-sonnet", + api_key="test-key", + messages=[{"role": "user", "content": "Hello"}], + ) + + assert route.called + assert route.calls.last.request.headers["authorization"] == "Bearer test-key" + body = json.loads(route.calls.last.request.content) + assert body["model"] == "claude-3-5-sonnet" + assert response.choices[0].message.content == "Hello from Token Kiosk!" From 069a6454196658e0d9ef57f6a4f36a5e02cfd89a Mon Sep 17 00:00:00 2001 From: hgaib Date: Thu, 5 Nov 2026 06:30:22 +0000 Subject: [PATCH 2/2] fix(providers): update Token Kiosk default endpoint to api-token-kiosk.gaib.ai --- litellm/llms/openai_like/providers.json | 2 +- tests/unit/llms/openai_like/test_token_kiosk_provider.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index 903494551d6..8e1a093d9c1 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -313,7 +313,7 @@ } }, "token_kiosk": { - "base_url": "https://agent-router.gaib.ai/v1", + "base_url": "https://api-token-kiosk.gaib.ai/v1", "api_key_env": "TOKEN_KIOSK_API_KEY", "api_base_env": "TOKEN_KIOSK_API_BASE", "provider_info": { diff --git a/tests/unit/llms/openai_like/test_token_kiosk_provider.py b/tests/unit/llms/openai_like/test_token_kiosk_provider.py index cc58c28ef12..64c5d9b5200 100644 --- a/tests/unit/llms/openai_like/test_token_kiosk_provider.py +++ b/tests/unit/llms/openai_like/test_token_kiosk_provider.py @@ -14,7 +14,7 @@ def test_token_kiosk_resolves_environment_and_explicit_credentials(monkeypatch: "claude-3-5-sonnet", "token_kiosk", "fake-token-kiosk-key", - "https://agent-router.gaib.ai/v1", + "https://api-token-kiosk.gaib.ai/v1", ) assert get_llm_provider( model="token_kiosk/claude-3-5-sonnet", @@ -30,7 +30,7 @@ def test_token_kiosk_resolves_environment_and_explicit_credentials(monkeypatch: def test_token_kiosk_chat_completion_dispatch() -> None: with respx.mock() as upstream: - route: Final = upstream.post("https://agent-router.gaib.ai/v1/chat/completions").respond( + route: Final = upstream.post("https://api-token-kiosk.gaib.ai/v1/chat/completions").respond( 200, json={ "id": "chatcmpl-token-kiosk-test",