From b4c35a68a841a99bd4b14df0828f52841101200e Mon Sep 17 00:00:00 2001 From: C-L-STARK Date: Sat, 19 Sep 2026 09:32:49 +0800 Subject: [PATCH 1/8] feat(providers): add TopxAI as a JSON-configured OpenAI-compatible provider TopxAI (https://ai.topxea.com) relays Claude, GPT, Grok, Kimi and a fine-tuned GLM through one OpenAI-compatible endpoint at fixed USD prices. This registers topxai/ in providers.json with the Responses API declared, adds the nine text models to the cost map with their cache lanes and the OpenAI and Grok whole-request long-context tiers, and registers the provider in the endpoint matrix and the dashboard. --- litellm/constants.py | 2 + litellm/llms/openai_like/providers.json | 6 + ...odel_prices_and_context_window_backup.json | 188 +++++++++++++++ .../provider_endpoints_support_backup.json | 17 ++ .../provider_create_fields.json | 28 +++ litellm/types/utils.py | 1 + model_prices_and_context_window.json | 188 +++++++++++++++ provider_endpoints_support.json | 17 ++ .../llms/openai_like/test_topxai_provider.py | 214 ++++++++++++++++++ .../public/assets/logos/topxai.svg | 1 + .../components/provider_info_helpers.test.tsx | 15 ++ .../src/components/provider_info_helpers.tsx | 5 + 12 files changed, 682 insertions(+) create mode 100644 tests/test_litellm/llms/openai_like/test_topxai_provider.py create mode 100644 ui/litellm-dashboard/public/assets/logos/topxai.svg diff --git a/litellm/constants.py b/litellm/constants.py index a7d4eba0f15..03af4ee67b6 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -925,6 +925,7 @@ openai_compatible_endpoints: Final[list] = [ "https://api.meta.ai/v1", "https://api.cognition.ai/v1", "https://api.scx.ai/v1", + "https://ai.topxea.com/v1", "https://gigachat.devices.sberbank.ru/api/v1", ] @@ -997,6 +998,7 @@ openai_compatible_providers: Final[list] = [ "meta", # Meta Model API (Muse Spark) - JSON-configured provider "cognition", "scx-ai", + "topxai", # TopxAI - JSON-configured provider ] openai_text_completion_compatible_providers: Final[list] = [ # providers that support `/v1/completions` "together_ai", diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index fe10293c420..a4f2371f8c7 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -200,5 +200,11 @@ "temperature_max": 1.99 }, "supported_endpoints": ["/v1/chat/completions"] + }, + "topxai": { + "base_url": "https://ai.topxea.com/v1", + "api_key_env": "TOPXAI_API_KEY", + "api_base_env": "TOPXAI_API_BASE", + "supported_endpoints": ["/v1/chat/completions", "/v1/responses"] } } diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 0e63653e2f2..6c95d4506d6 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -45031,6 +45031,194 @@ "supports_tool_choice": true, "supports_vision": true }, + "topxai/claude-sonnet-5": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_1hr": 2e-06, + "cache_read_input_token_cost": 1e-07, + "input_cost_per_token": 1e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 5e-06, + "source": "https://ai.topxea.com/pricing/claude-sonnet-5", + "supports_adaptive_thinking": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/claude-opus-5": { + "cache_creation_input_token_cost": 3.125e-06, + "cache_creation_input_token_cost_above_1hr": 5e-06, + "cache_read_input_token_cost": 2.5e-07, + "input_cost_per_token": 2.5e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.25e-05, + "source": "https://ai.topxea.com/pricing/claude-opus-5", + "supports_adaptive_thinking": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/claude-fable-5-1": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_1hr": 1e-05, + "cache_read_input_token_cost": 1.25e-07, + "input_cost_per_token": 5e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 2.5e-05, + "source": "https://ai.topxea.com/pricing/claude-fable-5-1", + "supports_adaptive_thinking": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/claude-fable-5": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_1hr": 1e-05, + "cache_read_input_token_cost": 5e-07, + "input_cost_per_token": 5e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 2.5e-05, + "source": "https://ai.topxea.com/pricing/claude-fable-5", + "supports_adaptive_thinking": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/gpt-5.6-sol": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_read_input_token_cost": 2e-07, + "cache_read_input_token_cost_above_272k_tokens": 4e-07, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_272k_tokens": 4e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "output_cost_per_token_above_272k_tokens": 1.5e-05, + "source": "https://ai.topxea.com/pricing/gpt-5.6-sol", + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/gpt-6-astra": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 2.5e-05, + "output_cost_per_token_above_272k_tokens": 3.75e-05, + "source": "https://ai.topxea.com/pricing/gpt-6-astra", + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/grok-4.6": { + "cache_creation_input_token_cost": 1e-06, + "cache_read_input_token_cost": 2.5e-07, + "cache_read_input_token_cost_above_200k_tokens": 5e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_200k_tokens": 2e-06, + "litellm_provider": "topxai", + "max_input_tokens": 500000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-06, + "output_cost_per_token_above_200k_tokens": 6e-06, + "source": "https://ai.topxea.com/pricing/grok-4.6", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/kimi-k3": { + "cache_creation_input_token_cost": 2.4e-06, + "cache_read_input_token_cost": 2.4e-07, + "input_cost_per_token": 2.4e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1048576, + "max_output_tokens": 131072, + "max_tokens": 131072, + "mode": "chat", + "output_cost_per_token": 1.2e-05, + "source": "https://ai.topxea.com/pricing/kimi-k3", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/GLM-5.3-Abliterated": { + "cache_creation_input_token_cost": 4e-06, + "cache_read_input_token_cost": 4e-07, + "input_cost_per_token": 4e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 131072, + "max_tokens": 131072, + "mode": "chat", + "output_cost_per_token": 7e-06, + "source": "https://ai.topxea.com/pricing/glm-5.3-abliterated", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false + }, "snowflake/claude-3-5-sonnet": { "litellm_provider": "snowflake", "max_input_tokens": 200000, diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json index dbeaccdda2d..0cf1dbbe0ec 100644 --- a/litellm/provider_endpoints_support_backup.json +++ b/litellm/provider_endpoints_support_backup.json @@ -2082,6 +2082,23 @@ "a2a": false } }, + "topxai": { + "display_name": "TopxAI (`topxai`)", + "url": "https://docs.litellm.ai/docs/providers/topxai", + "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 + } + }, "snowflake": { "display_name": "Snowflake (`snowflake`)", "url": "https://docs.litellm.ai/docs/providers/snowflake", diff --git a/litellm/proxy/public_endpoints/provider_create_fields.json b/litellm/proxy/public_endpoints/provider_create_fields.json index e6673ec99aa..070fd311b71 100644 --- a/litellm/proxy/public_endpoints/provider_create_fields.json +++ b/litellm/proxy/public_endpoints/provider_create_fields.json @@ -2897,6 +2897,34 @@ ], "default_model_placeholder": "scx-ai/GLM-5.2" }, + { + "provider": "TOPXAI", + "provider_display_name": "TopxAI", + "litellm_provider": "topxai", + "credential_fields": [ + { + "key": "api_base", + "label": "API Base", + "placeholder": "https://ai.topxea.com/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": "topxai/claude-sonnet-5" + }, { "provider": "Snowflake", "provider_display_name": "Snowflake", diff --git a/litellm/types/utils.py b/litellm/types/utils.py index c63d971b89b..9d781cb61d3 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -4121,6 +4121,7 @@ class LlmProviders(str, Enum): PINSTRIPES = "pinstripes" COGNITION = "cognition" SCX_AI = "scx-ai" + TOPXAI = "topxai" DARKBLOOM = "darkbloom" META = "meta" LITELLM_AGENT = "litellm_agent" diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 0e63653e2f2..6c95d4506d6 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -45031,6 +45031,194 @@ "supports_tool_choice": true, "supports_vision": true }, + "topxai/claude-sonnet-5": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_1hr": 2e-06, + "cache_read_input_token_cost": 1e-07, + "input_cost_per_token": 1e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 5e-06, + "source": "https://ai.topxea.com/pricing/claude-sonnet-5", + "supports_adaptive_thinking": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/claude-opus-5": { + "cache_creation_input_token_cost": 3.125e-06, + "cache_creation_input_token_cost_above_1hr": 5e-06, + "cache_read_input_token_cost": 2.5e-07, + "input_cost_per_token": 2.5e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1.25e-05, + "source": "https://ai.topxea.com/pricing/claude-opus-5", + "supports_adaptive_thinking": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/claude-fable-5-1": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_1hr": 1e-05, + "cache_read_input_token_cost": 1.25e-07, + "input_cost_per_token": 5e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 2.5e-05, + "source": "https://ai.topxea.com/pricing/claude-fable-5-1", + "supports_adaptive_thinking": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/claude-fable-5": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_1hr": 1e-05, + "cache_read_input_token_cost": 5e-07, + "input_cost_per_token": 5e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 2.5e-05, + "source": "https://ai.topxea.com/pricing/claude-fable-5", + "supports_adaptive_thinking": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/gpt-5.6-sol": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_read_input_token_cost": 2e-07, + "cache_read_input_token_cost_above_272k_tokens": 4e-07, + "input_cost_per_token": 2e-06, + "input_cost_per_token_above_272k_tokens": 4e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "output_cost_per_token_above_272k_tokens": 1.5e-05, + "source": "https://ai.topxea.com/pricing/gpt-5.6-sol", + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/gpt-6-astra": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_read_input_token_cost": 5e-07, + "cache_read_input_token_cost_above_272k_tokens": 1e-06, + "input_cost_per_token": 5e-06, + "input_cost_per_token_above_272k_tokens": 1e-05, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 2.5e-05, + "output_cost_per_token_above_272k_tokens": 3.75e-05, + "source": "https://ai.topxea.com/pricing/gpt-6-astra", + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/grok-4.6": { + "cache_creation_input_token_cost": 1e-06, + "cache_read_input_token_cost": 2.5e-07, + "cache_read_input_token_cost_above_200k_tokens": 5e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_200k_tokens": 2e-06, + "litellm_provider": "topxai", + "max_input_tokens": 500000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 3e-06, + "output_cost_per_token_above_200k_tokens": 6e-06, + "source": "https://ai.topxea.com/pricing/grok-4.6", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/kimi-k3": { + "cache_creation_input_token_cost": 2.4e-06, + "cache_read_input_token_cost": 2.4e-07, + "input_cost_per_token": 2.4e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1048576, + "max_output_tokens": 131072, + "max_tokens": 131072, + "mode": "chat", + "output_cost_per_token": 1.2e-05, + "source": "https://ai.topxea.com/pricing/kimi-k3", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/GLM-5.3-Abliterated": { + "cache_creation_input_token_cost": 4e-06, + "cache_read_input_token_cost": 4e-07, + "input_cost_per_token": 4e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1000000, + "max_output_tokens": 131072, + "max_tokens": 131072, + "mode": "chat", + "output_cost_per_token": 7e-06, + "source": "https://ai.topxea.com/pricing/glm-5.3-abliterated", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false + }, "snowflake/claude-3-5-sonnet": { "litellm_provider": "snowflake", "max_input_tokens": 200000, diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index c71f4a82a4a..e9c38e4d578 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -2316,6 +2316,23 @@ "a2a": false } }, + "topxai": { + "display_name": "TopxAI (`topxai`)", + "url": "https://docs.litellm.ai/docs/providers/topxai", + "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 + } + }, "snowflake": { "display_name": "Snowflake (`snowflake`)", "url": "https://docs.litellm.ai/docs/providers/snowflake", diff --git a/tests/test_litellm/llms/openai_like/test_topxai_provider.py b/tests/test_litellm/llms/openai_like/test_topxai_provider.py new file mode 100644 index 00000000000..cd426de96e4 --- /dev/null +++ b/tests/test_litellm/llms/openai_like/test_topxai_provider.py @@ -0,0 +1,214 @@ +""" +Tests for the TopxAI provider configuration and integration. +""" + +import litellm + +TOPXAI_BASE_URL = "https://ai.topxea.com/v1" + + +class TestTopxAIProviderConfig: + def test_topxai_in_provider_list(self): + from litellm import LlmProviders + + assert hasattr(LlmProviders, "TOPXAI") + assert LlmProviders.TOPXAI.value == "topxai" + assert "topxai" in litellm.provider_list + + def test_topxai_json_config_exists(self): + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + assert JSONProviderRegistry.exists("topxai") + + topxai = JSONProviderRegistry.get("topxai") + assert topxai is not None + assert topxai.base_url == TOPXAI_BASE_URL + assert topxai.api_key_env == "TOPXAI_API_KEY" + assert topxai.api_base_env == "TOPXAI_API_BASE" + assert "/v1/responses" in topxai.supported_endpoints + + def test_topxai_in_openai_compatible_providers(self): + from litellm.constants import openai_compatible_providers + + assert "topxai" in openai_compatible_providers + + def test_topxai_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="topxai/claude-sonnet-5", + custom_llm_provider=None, + api_base=None, + api_key=None, + ) + + assert model == "claude-sonnet-5" + assert provider == "topxai" + assert api_base == TOPXAI_BASE_URL + + def test_topxai_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="topxai/claude-sonnet-5", + custom_llm_provider=None, + api_base="https://relay.example.com/v1", + api_key="sk-test", + ) + + assert provider == "topxai" + assert api_base == "https://relay.example.com/v1" + assert api_key == "sk-test" + + def test_topxai_url_autodetection(self): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + model, provider, api_key, api_base = get_llm_provider( + model="gpt-5.6-sol", + custom_llm_provider=None, + api_base=TOPXAI_BASE_URL, + api_key=None, + ) + assert provider == "topxai" + assert api_base == TOPXAI_BASE_URL + + def test_topxai_resolves_env_api_key(self, monkeypatch): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + provider = JSONProviderRegistry.get("topxai") + assert provider is not None + config = create_config_class(provider)() + monkeypatch.setenv("TOPXAI_API_KEY", "sk-test") + api_base, api_key = config._get_openai_compatible_provider_info(None, None) + assert api_base == TOPXAI_BASE_URL + assert api_key == "sk-test" + + def test_topxai_complete_url_appends_endpoint(self): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + provider = JSONProviderRegistry.get("topxai") + assert provider is not None + config = create_config_class(provider)() + url = config.get_complete_url( + api_base=TOPXAI_BASE_URL, + api_key="sk-test", + model="topxai/kimi-k3", + optional_params={}, + litellm_params={}, + stream=False, + ) + assert url == f"{TOPXAI_BASE_URL}/chat/completions" + + def test_topxai_router_config(self): + from litellm import Router + + router = Router( + model_list=[ + { + "model_name": "sonnet", + "litellm_params": { + "model": "topxai/claude-sonnet-5", + "api_key": "test-key", + }, + } + ] + ) + + assert len(router.model_list) == 1 + assert router.model_list[0]["model_name"] == "sonnet" + + +class TestTopxAIModelMetadata: + TOPXAI_MODELS = ( + "topxai/claude-sonnet-5", + "topxai/claude-opus-5", + "topxai/claude-fable-5-1", + "topxai/claude-fable-5", + "topxai/gpt-5.6-sol", + "topxai/gpt-6-astra", + "topxai/grok-4.6", + "topxai/kimi-k3", + "topxai/GLM-5.3-Abliterated", + ) + TEXT_ONLY_MODELS = ("topxai/GLM-5.3-Abliterated",) + TIERED_MODELS = { + "topxai/gpt-5.6-sol": "272k", + "topxai/gpt-6-astra": "272k", + "topxai/grok-4.6": "200k", + } + + @staticmethod + def _load(path_parts): + import json + from pathlib import Path + + json_path = Path(__file__).parents[4].joinpath(*path_parts) + with open(json_path) as f: + return json.load(f) + + def test_topxai_models_registered_with_correct_metadata(self): + model_cost = self._load(("model_prices_and_context_window.json",)) + for model in self.TOPXAI_MODELS: + info = model_cost.get(model) + assert info is not None, f"{model} missing from model_prices_and_context_window.json" + assert info["litellm_provider"] == "topxai" + assert info["mode"] == "chat" + assert info["input_cost_per_token"] > 0 + assert info["output_cost_per_token"] > 0 + assert info["supports_function_calling"] is True + assert info["supports_tool_choice"] is True + assert info["supports_reasoning"] is True + assert info["supports_response_schema"] is True + assert info["supports_vision"] is (model not in self.TEXT_ONLY_MODELS) + + assert info["supports_prompt_caching"] is True + assert 0 < info["cache_read_input_token_cost"] < info["input_cost_per_token"] + assert info["cache_creation_input_token_cost"] >= info["input_cost_per_token"] + + assert info["max_tokens"] == info["max_output_tokens"] + assert info["max_input_tokens"] >= 500_000 + assert info["source"].startswith("https://ai.topxea.com/pricing/") + + def test_topxai_long_context_tiers_double_the_input_rate(self): + model_cost = self._load(("model_prices_and_context_window.json",)) + for model, tier in self.TIERED_MODELS.items(): + info = model_cost[model] + assert info[f"input_cost_per_token_above_{tier}_tokens"] == 2 * info["input_cost_per_token"] + assert info[f"cache_read_input_token_cost_above_{tier}_tokens"] == 2 * info["cache_read_input_token_cost"] + assert info[f"output_cost_per_token_above_{tier}_tokens"] > info["output_cost_per_token"] + + def test_topxai_models_synced_to_backup(self): + model_cost = self._load(("model_prices_and_context_window.json",)) + backup = self._load(("litellm", "model_prices_and_context_window_backup.json")) + for model in self.TOPXAI_MODELS: + assert model in backup, f"{model} missing from backup json" + assert backup[model] == model_cost[model], f"{model} differs between root and backup json" + + +class TestTopxAIDashboardRegistration: + @staticmethod + def _provider_create_fields(): + import json + from pathlib import Path + + import litellm + + path = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json" + with open(path) as f: + return json.load(f) + + def test_topxai_is_selectable_in_the_add_model_form(self): + entries = [e for e in self._provider_create_fields() if e["litellm_provider"] == "topxai"] + assert len(entries) == 1, "topxai must appear exactly once in provider_create_fields.json" + + entry = entries[0] + assert entry["provider"] == "TOPXAI" + assert entry["provider_display_name"] == "TopxAI" + assert entry["default_model_placeholder"].startswith("topxai/") + + fields = {f["key"]: f for f in entry["credential_fields"]} + assert fields["api_key"]["required"] is True + assert fields["api_key"]["field_type"] == "password" + assert fields["api_base"]["required"] is False diff --git a/ui/litellm-dashboard/public/assets/logos/topxai.svg b/ui/litellm-dashboard/public/assets/logos/topxai.svg new file mode 100644 index 00000000000..ef7bb927fb7 --- /dev/null +++ b/ui/litellm-dashboard/public/assets/logos/topxai.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx index 4c68e302267..1efe9554fc0 100644 --- a/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx +++ b/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx @@ -73,6 +73,17 @@ describe("provider_info_helpers", () => { expect(fromEnumKey.logo).toBe(providerLogoMap[Providers.SCX_AI]); }); + it("should map topxai slug and TOPXAI enum key to the TopxAI display name and logo", () => { + const fromSlug = getProviderLogoAndName("topxai"); + expect(fromSlug.displayName).toBe(Providers.TOPXAI); + expect(fromSlug.logo).toBe(providerLogoMap[Providers.TOPXAI]); + expect(fromSlug.logo).toBeTruthy(); + + const fromEnumKey = getProviderLogoAndName("TOPXAI"); + expect(fromEnumKey.displayName).toBe(Providers.TOPXAI); + expect(fromEnumKey.logo).toBe(providerLogoMap[Providers.TOPXAI]); + }); + it("should map bedrock_mantle slug to Bedrock Mantle display name and logo", () => { const result = getProviderLogoAndName("bedrock_mantle"); expect(result.displayName).toBe(Providers.BedrockMantle); @@ -205,6 +216,10 @@ describe("provider_info_helpers", () => { expect(getPlaceholder(Providers.SCX_AI)).toBe("scx-ai/GLM-5.2"); }); + it("should return a topxai model placeholder for TOPXAI provider", () => { + expect(getPlaceholder(Providers.TOPXAI)).toBe("topxai/claude-sonnet-5"); + }); + it("should return claude-3-opus placeholder for Anthropic provider", () => { expect(getPlaceholder(Providers.Anthropic)).toBe("claude-3-opus"); }); diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx index 1a1a2fd73aa..936cfaede82 100644 --- a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx +++ b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx @@ -55,6 +55,7 @@ import scxAiLogo from "../../public/assets/logos/scx_ai.svg"; import snowflakeLogo from "../../public/assets/logos/snowflake.svg"; import sonioxLogo from "../../public/assets/logos/soniox.svg"; import togetheraiLogo from "../../public/assets/logos/togetherai.svg"; +import topxaiLogo from "../../public/assets/logos/topxai.svg"; import topazLogo from "../../public/assets/logos/topaz.svg"; import v0Logo from "../../public/assets/logos/v0.svg"; import vercelLogo from "../../public/assets/logos/vercel.svg"; @@ -164,6 +165,7 @@ export enum Providers { Snowflake = "Snowflake", Soniox = "Soniox", TEXT_COMPLETION_CODESTRAL = "Text-Completion-Codestral", + TOPXAI = "TopxAI", TogetherAI = "TogetherAI", TOPAZ = "Topaz", Triton = "Triton", @@ -281,6 +283,7 @@ export const provider_map: Record = { Snowflake: "snowflake", Soniox: "soniox", TEXT_COMPLETION_CODESTRAL: "text-completion-codestral", + TOPXAI: "topxai", TogetherAI: "together_ai", TOPAZ: "topaz", Triton: "triton", @@ -379,6 +382,7 @@ export const providerLogoMap: Partial> = { [Providers.Snowflake]: snowflakeLogo.src, [Providers.Soniox]: sonioxLogo.src, [Providers.TEXT_COMPLETION_CODESTRAL]: mistralLogo.src, + [Providers.TOPXAI]: topxaiLogo.src, [Providers.TogetherAI]: togetheraiLogo.src, [Providers.TOPAZ]: topazLogo.src, [Providers.Triton]: nvidiaTritonLogo.src, @@ -445,6 +449,7 @@ const providerPlaceholderMap: Partial> = { [Providers.SageMaker]: "sagemaker/jumpstart-dft-meta-textgeneration-llama-2-7b", [Providers.SCX_AI]: "scx-ai/GLM-5.2", [Providers.Snowflake]: "snowflake/mistral-7b", + [Providers.TOPXAI]: "topxai/claude-sonnet-5", [Providers.Vertex_AI]: "gemini-pro", [Providers.VolcEngine]: "volcengine/", [Providers.Voyage]: "voyage/", From 8e9da0a5b0b71d59c81029b1288e280256fbb0aa Mon Sep 17 00:00:00 2001 From: C-L-STARK Date: Sat, 19 Sep 2026 21:09:20 +0800 Subject: [PATCH 2/8] fix(topxai): validate Responses routing and context pricing --- litellm/constants.py | 2 +- ...odel_prices_and_context_window_backup.json | 9 +- model_prices_and_context_window.json | 9 +- model_prices_and_context_window.schema.json | 20 +++ .../llms/openai_like/test_topxai_provider.py | 120 +++++++++++++++--- 5 files changed, 137 insertions(+), 23 deletions(-) diff --git a/litellm/constants.py b/litellm/constants.py index 03af4ee67b6..e398c93fb21 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -998,7 +998,7 @@ openai_compatible_providers: Final[list] = [ "meta", # Meta Model API (Muse Spark) - JSON-configured provider "cognition", "scx-ai", - "topxai", # TopxAI - JSON-configured provider + "topxai", ] openai_text_completion_compatible_providers: Final[list] = [ # providers that support `/v1/completions` "together_ai", diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 6c95d4506d6..fb03f926974 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -45117,6 +45117,7 @@ }, "topxai/gpt-5.6-sol": { "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_272k_tokens": 5e-06, "cache_read_input_token_cost": 2e-07, "cache_read_input_token_cost_above_272k_tokens": 4e-07, "input_cost_per_token": 2e-06, @@ -45139,6 +45140,7 @@ }, "topxai/gpt-6-astra": { "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05, "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, "input_cost_per_token": 5e-06, @@ -45161,17 +45163,18 @@ }, "topxai/grok-4.6": { "cache_creation_input_token_cost": 1e-06, + "cache_creation_input_token_cost_above_199999_tokens": 2e-06, "cache_read_input_token_cost": 2.5e-07, - "cache_read_input_token_cost_above_200k_tokens": 5e-07, + "cache_read_input_token_cost_above_199999_tokens": 5e-07, "input_cost_per_token": 1e-06, - "input_cost_per_token_above_200k_tokens": 2e-06, + "input_cost_per_token_above_199999_tokens": 2e-06, "litellm_provider": "topxai", "max_input_tokens": 500000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-06, - "output_cost_per_token_above_200k_tokens": 6e-06, + "output_cost_per_token_above_199999_tokens": 6e-06, "source": "https://ai.topxea.com/pricing/grok-4.6", "supports_function_calling": true, "supports_pdf_input": false, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 6c95d4506d6..fb03f926974 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -45117,6 +45117,7 @@ }, "topxai/gpt-5.6-sol": { "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_272k_tokens": 5e-06, "cache_read_input_token_cost": 2e-07, "cache_read_input_token_cost_above_272k_tokens": 4e-07, "input_cost_per_token": 2e-06, @@ -45139,6 +45140,7 @@ }, "topxai/gpt-6-astra": { "cache_creation_input_token_cost": 6.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 1.25e-05, "cache_read_input_token_cost": 5e-07, "cache_read_input_token_cost_above_272k_tokens": 1e-06, "input_cost_per_token": 5e-06, @@ -45161,17 +45163,18 @@ }, "topxai/grok-4.6": { "cache_creation_input_token_cost": 1e-06, + "cache_creation_input_token_cost_above_199999_tokens": 2e-06, "cache_read_input_token_cost": 2.5e-07, - "cache_read_input_token_cost_above_200k_tokens": 5e-07, + "cache_read_input_token_cost_above_199999_tokens": 5e-07, "input_cost_per_token": 1e-06, - "input_cost_per_token_above_200k_tokens": 2e-06, + "input_cost_per_token_above_199999_tokens": 2e-06, "litellm_provider": "topxai", "max_input_tokens": 500000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-06, - "output_cost_per_token_above_200k_tokens": 6e-06, + "output_cost_per_token_above_199999_tokens": 6e-06, "source": "https://ai.topxea.com/pricing/grok-4.6", "supports_function_calling": true, "supports_pdf_input": false, diff --git a/model_prices_and_context_window.schema.json b/model_prices_and_context_window.schema.json index f924df1f1b2..f0ba7176472 100644 --- a/model_prices_and_context_window.schema.json +++ b/model_prices_and_context_window.schema.json @@ -84,6 +84,11 @@ "minimum": 0, "description": "Rate applied once the prompt exceeds the token threshold in the field name." }, + "cache_creation_input_token_cost_above_199999_tokens": { + "type": "number", + "minimum": 0, + "description": "Rate applied once the prompt exceeds the token threshold in the field name." + }, "cache_creation_input_token_cost_above_1hr": { "type": "number", "minimum": 0, @@ -143,6 +148,11 @@ "minimum": 0, "description": "Rate applied once the prompt exceeds the token threshold in the field name." }, + "cache_read_input_token_cost_above_199999_tokens": { + "type": "number", + "minimum": 0, + "description": "Rate applied once the prompt exceeds the token threshold in the field name." + }, "cache_read_input_token_cost_above_200k_tokens": { "type": "number", "minimum": 0, @@ -310,6 +320,11 @@ "minimum": 0, "description": "Rate applied once the prompt exceeds the token threshold in the field name." }, + "input_cost_per_token_above_199999_tokens": { + "type": "number", + "minimum": 0, + "description": "Rate applied once the prompt exceeds the token threshold in the field name." + }, "input_cost_per_token_above_200k_tokens": { "type": "number", "minimum": 0, @@ -509,6 +524,11 @@ "minimum": 0, "description": "Rate applied once the prompt exceeds the token threshold in the field name." }, + "output_cost_per_token_above_199999_tokens": { + "type": "number", + "minimum": 0, + "description": "Rate applied once the prompt exceeds the token threshold in the field name." + }, "output_cost_per_token_above_200k_tokens": { "type": "number", "minimum": 0, diff --git a/tests/test_litellm/llms/openai_like/test_topxai_provider.py b/tests/test_litellm/llms/openai_like/test_topxai_provider.py index cd426de96e4..f24b7f361b4 100644 --- a/tests/test_litellm/llms/openai_like/test_topxai_provider.py +++ b/tests/test_litellm/llms/openai_like/test_topxai_provider.py @@ -2,9 +2,17 @@ Tests for the TopxAI provider configuration and integration. """ -import litellm +import json +from typing import Final -TOPXAI_BASE_URL = "https://ai.topxea.com/v1" +import httpx +import pytest + +import litellm +from litellm.llms.custom_httpx.http_handler import HTTPHandler + +# Provider endpoint: https://ai.topxea.com/docs (verified 2026-09-19) +TOPXAI_BASE_URL: Final = "https://ai.topxea.com/v1" class TestTopxAIProviderConfig: @@ -101,6 +109,52 @@ class TestTopxAIProviderConfig: ) assert url == f"{TOPXAI_BASE_URL}/chat/completions" + @pytest.mark.parametrize("api_base", (None, "https://relay.example.com/v1/")) + def test_topxai_responses_routes_and_transforms( + self, monkeypatch: pytest.MonkeyPatch, api_base: str | None + ) -> None: + monkeypatch.setenv("TOPXAI_API_KEY", "sk-topxai-test") + expected_base: Final = (api_base or TOPXAI_BASE_URL).rstrip("/") + + def respond(request: httpx.Request) -> httpx.Response: + assert request.method == "POST" + assert str(request.url) == f"{expected_base}/responses" + assert request.headers["Authorization"] == "Bearer sk-topxai-test" + payload: Final = json.loads(request.content) + assert payload["model"] == "gpt-5.6-sol" + assert payload["input"] == "Reply with OK" + return httpx.Response( + 200, + json={ + "id": "resp_topxai_test", + "object": "response", + "created_at": 1, + "model": "gpt-5.6-sol", + "status": "completed", + "output": [{ + "type": "message", + "id": "msg_test", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "OK", "annotations": []}], + }], + "usage": {"input_tokens": 4, "output_tokens": 1, "total_tokens": 5}, + }, + ) + + with httpx.Client(transport=httpx.MockTransport(respond)) as client: + response: Final = litellm.responses( + model="topxai/gpt-5.6-sol", + input="Reply with OK", + api_base=api_base, + client=HTTPHandler(client=client), + ) + + assert response.status == "completed" + assert response.output[0].content[0].text == "OK" + assert response.usage.input_tokens == 4 + assert response.usage.output_tokens == 1 + def test_topxai_router_config(self): from litellm import Router @@ -121,7 +175,8 @@ class TestTopxAIProviderConfig: class TestTopxAIModelMetadata: - TOPXAI_MODELS = ( + # Catalog and capabilities: https://ai.topxea.com/pricing (verified 2026-09-19) + TOPXAI_MODELS: Final = ( "topxai/claude-sonnet-5", "topxai/claude-opus-5", "topxai/claude-fable-5-1", @@ -132,12 +187,16 @@ class TestTopxAIModelMetadata: "topxai/kimi-k3", "topxai/GLM-5.3-Abliterated", ) - TEXT_ONLY_MODELS = ("topxai/GLM-5.3-Abliterated",) - TIERED_MODELS = { - "topxai/gpt-5.6-sol": "272k", - "topxai/gpt-6-astra": "272k", - "topxai/grok-4.6": "200k", - } + TEXT_ONLY_MODELS: Final = ("topxai/GLM-5.3-Abliterated",) + # First premium token and output multiplier, verified 2026-09-19: + # https://ai.topxea.com/pricing/gpt-5.6-sol + # https://ai.topxea.com/pricing/gpt-6-astra + # https://ai.topxea.com/pricing/grok-4.6 + TIERED_MODELS: Final = ( + ("topxai/gpt-5.6-sol", 272_001, 1.5), + ("topxai/gpt-6-astra", 272_001, 1.5), + ("topxai/grok-4.6", 200_000, 2.0), + ) @staticmethod def _load(path_parts): @@ -171,13 +230,42 @@ class TestTopxAIModelMetadata: assert info["max_input_tokens"] >= 500_000 assert info["source"].startswith("https://ai.topxea.com/pricing/") - def test_topxai_long_context_tiers_double_the_input_rate(self): - model_cost = self._load(("model_prices_and_context_window.json",)) - for model, tier in self.TIERED_MODELS.items(): - info = model_cost[model] - assert info[f"input_cost_per_token_above_{tier}_tokens"] == 2 * info["input_cost_per_token"] - assert info[f"cache_read_input_token_cost_above_{tier}_tokens"] == 2 * info["cache_read_input_token_cost"] - assert info[f"output_cost_per_token_above_{tier}_tokens"] > info["output_cost_per_token"] + @pytest.mark.parametrize("model,first_premium_token,output_multiplier", TIERED_MODELS) + @pytest.mark.parametrize("offset", (-1, 0, 1)) + @pytest.mark.parametrize("cached_tokens,cache_write_tokens", ((0, 0), (1000, 0), (0, 500), (1000, 500))) + def test_topxai_cost_at_context_boundary( + self, + monkeypatch: pytest.MonkeyPatch, + model: str, + first_premium_token: int, + output_multiplier: float, + offset: int, + cached_tokens: int, + cache_write_tokens: int, + ) -> None: + info: Final = self._load(("model_prices_and_context_window.json",))[model] + monkeypatch.setitem(litellm.model_cost, model, info) + prompt_tokens: Final = first_premium_token + offset + completion_tokens: Final = 17 + # Whole-request input/cache rates double at these boundaries; sources above + input_multiplier: Final = 2 if offset >= 0 else 1 + expected_input: Final = input_multiplier * ( + (prompt_tokens - cached_tokens - cache_write_tokens) * info["input_cost_per_token"] + + cached_tokens * info["cache_read_input_token_cost"] + + cache_write_tokens * info["cache_creation_input_token_cost"] + ) + expected_output: Final = ( + completion_tokens * info["output_cost_per_token"] * (output_multiplier if offset >= 0 else 1) + ) + prompt_cost, completion_cost = litellm.cost_per_token( + model=model, + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + cache_read_input_tokens=cached_tokens, + cache_creation_input_tokens=cache_write_tokens, + ) + assert prompt_cost == pytest.approx(expected_input) + assert completion_cost == pytest.approx(expected_output) def test_topxai_models_synced_to_backup(self): model_cost = self._load(("model_prices_and_context_window.json",)) From 276aa35e6e0cb979081dfb9655be8c1c61b04057 Mon Sep 17 00:00:00 2001 From: C-L-STARK Date: Sat, 19 Sep 2026 21:27:33 +0800 Subject: [PATCH 3/8] fix(topxai): use supported schema for inclusive context tier --- ...odel_prices_and_context_window_backup.json | 28 +++++++++++++++---- model_prices_and_context_window.json | 28 +++++++++++++++---- model_prices_and_context_window.schema.json | 20 ------------- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index fb03f926974..d791c1aaec9 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -45163,18 +45163,14 @@ }, "topxai/grok-4.6": { "cache_creation_input_token_cost": 1e-06, - "cache_creation_input_token_cost_above_199999_tokens": 2e-06, "cache_read_input_token_cost": 2.5e-07, - "cache_read_input_token_cost_above_199999_tokens": 5e-07, "input_cost_per_token": 1e-06, - "input_cost_per_token_above_199999_tokens": 2e-06, "litellm_provider": "topxai", "max_input_tokens": 500000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-06, - "output_cost_per_token_above_199999_tokens": 6e-06, "source": "https://ai.topxea.com/pricing/grok-4.6", "supports_function_calling": true, "supports_pdf_input": false, @@ -45182,7 +45178,29 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "tiered_pricing": [ + { + "cache_creation_input_token_cost": 1e-06, + "cache_read_input_token_cost": 2.5e-07, + "input_cost_per_token": 1e-06, + "output_cost_per_token": 3e-06, + "range": [ + 0, + 199999 + ] + }, + { + "cache_creation_input_token_cost": 2e-06, + "cache_read_input_token_cost": 5e-07, + "input_cost_per_token": 2e-06, + "output_cost_per_token": 6e-06, + "range": [ + 199999, + 500000 + ] + } + ] }, "topxai/kimi-k3": { "cache_creation_input_token_cost": 2.4e-06, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index fb03f926974..d791c1aaec9 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -45163,18 +45163,14 @@ }, "topxai/grok-4.6": { "cache_creation_input_token_cost": 1e-06, - "cache_creation_input_token_cost_above_199999_tokens": 2e-06, "cache_read_input_token_cost": 2.5e-07, - "cache_read_input_token_cost_above_199999_tokens": 5e-07, "input_cost_per_token": 1e-06, - "input_cost_per_token_above_199999_tokens": 2e-06, "litellm_provider": "topxai", "max_input_tokens": 500000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-06, - "output_cost_per_token_above_199999_tokens": 6e-06, "source": "https://ai.topxea.com/pricing/grok-4.6", "supports_function_calling": true, "supports_pdf_input": false, @@ -45182,7 +45178,29 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "tiered_pricing": [ + { + "cache_creation_input_token_cost": 1e-06, + "cache_read_input_token_cost": 2.5e-07, + "input_cost_per_token": 1e-06, + "output_cost_per_token": 3e-06, + "range": [ + 0, + 199999 + ] + }, + { + "cache_creation_input_token_cost": 2e-06, + "cache_read_input_token_cost": 5e-07, + "input_cost_per_token": 2e-06, + "output_cost_per_token": 6e-06, + "range": [ + 199999, + 500000 + ] + } + ] }, "topxai/kimi-k3": { "cache_creation_input_token_cost": 2.4e-06, diff --git a/model_prices_and_context_window.schema.json b/model_prices_and_context_window.schema.json index f0ba7176472..f924df1f1b2 100644 --- a/model_prices_and_context_window.schema.json +++ b/model_prices_and_context_window.schema.json @@ -84,11 +84,6 @@ "minimum": 0, "description": "Rate applied once the prompt exceeds the token threshold in the field name." }, - "cache_creation_input_token_cost_above_199999_tokens": { - "type": "number", - "minimum": 0, - "description": "Rate applied once the prompt exceeds the token threshold in the field name." - }, "cache_creation_input_token_cost_above_1hr": { "type": "number", "minimum": 0, @@ -148,11 +143,6 @@ "minimum": 0, "description": "Rate applied once the prompt exceeds the token threshold in the field name." }, - "cache_read_input_token_cost_above_199999_tokens": { - "type": "number", - "minimum": 0, - "description": "Rate applied once the prompt exceeds the token threshold in the field name." - }, "cache_read_input_token_cost_above_200k_tokens": { "type": "number", "minimum": 0, @@ -320,11 +310,6 @@ "minimum": 0, "description": "Rate applied once the prompt exceeds the token threshold in the field name." }, - "input_cost_per_token_above_199999_tokens": { - "type": "number", - "minimum": 0, - "description": "Rate applied once the prompt exceeds the token threshold in the field name." - }, "input_cost_per_token_above_200k_tokens": { "type": "number", "minimum": 0, @@ -524,11 +509,6 @@ "minimum": 0, "description": "Rate applied once the prompt exceeds the token threshold in the field name." }, - "output_cost_per_token_above_199999_tokens": { - "type": "number", - "minimum": 0, - "description": "Rate applied once the prompt exceeds the token threshold in the field name." - }, "output_cost_per_token_above_200k_tokens": { "type": "number", "minimum": 0, From c3eb1ffe65d6e718965fbdf2e0cbbad88800616d Mon Sep 17 00:00:00 2001 From: C-L-STARK Date: Mon, 28 Sep 2026 22:11:48 +0800 Subject: [PATCH 4/8] fix(topxai): replace retired models, add DeepSeek, sync with /api/pricing TopxAI retired claude-fable-5, claude-opus-5, gpt-5.6-sol and grok-4.6 on 2026-09-24; its API now answers 403 for them. Their topxai/* entries are removed, and topxai/claude-opus-5-5, topxai/gpt-6-sol and topxai/grok-4.7 take the places of their predecessors. topxai/deepseek-flash and topxai/deepseek-v4-pro are added. Every number comes from https://ai.topxea.com/api/pricing (2026-09-28), on the route the existing entries already used: the model's auto route, which is the shared pool where one exists (claude-shared, openai-shared, grok-shared) and otherwise its single official line (kimi-official, z-official, deepseek-official). - gpt-6-astra: max_input_tokens follows the catalogue's 1,050,000 context window (was 1,000,000). - Cache-write rates only where the catalogue publishes one. GLM and Grok publish none, so cache_creation_input_token_cost is dropped from GLM-5.3-Abliterated and not set on grok-4.7; an absent rate bills cache writes at the input rate, the value the removed field repeated. - grok-4.7 has no max_output_tokens: the catalogue lists no output limit. Its 200K tier keeps the inclusive boundary through tiered_pricing. - DeepSeek lists the peak lanes and an off_peak_pricing block with the weekday windows, the same shape as the upstream deepseek rows. Chinese public holidays, which TopxAI bills off-peak, cannot be expressed. Tests follow the current models and add DeepSeek peak/off-peak costs at pinned billing times. Co-Authored-By: Claude Opus 5.5 --- ...odel_prices_and_context_window_backup.json | 161 ++++++++++++------ model_prices_and_context_window.json | 161 ++++++++++++------ .../llms/openai_like/test_topxai_provider.py | 112 +++++++++--- 3 files changed, 312 insertions(+), 122 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 836c2d7b3db..f30269e82b0 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -45502,18 +45502,18 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/claude-opus-5": { - "cache_creation_input_token_cost": 3.125e-06, - "cache_creation_input_token_cost_above_1hr": 5e-06, - "cache_read_input_token_cost": 2.5e-07, - "input_cost_per_token": 2.5e-06, + "topxai/claude-opus-5-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 1e-07, + "input_cost_per_token": 2e-06, "litellm_provider": "topxai", "max_input_tokens": 1000000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", - "output_cost_per_token": 1.25e-05, - "source": "https://ai.topxea.com/pricing/claude-opus-5", + "output_cost_per_token": 1e-05, + "source": "https://ai.topxea.com/pricing/claude-opus-5-5", "supports_adaptive_thinking": true, "supports_function_calling": true, "supports_pdf_input": true, @@ -45544,42 +45544,21 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/claude-fable-5": { - "cache_creation_input_token_cost": 6.25e-06, - "cache_creation_input_token_cost_above_1hr": 1e-05, - "cache_read_input_token_cost": 5e-07, - "input_cost_per_token": 5e-06, + "topxai/gpt-6-sol": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_read_input_token_cost": 1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, "litellm_provider": "topxai", - "max_input_tokens": 1000000, + "max_input_tokens": 1050000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", - "output_cost_per_token": 2.5e-05, - "source": "https://ai.topxea.com/pricing/claude-fable-5", - "supports_adaptive_thinking": true, - "supports_function_calling": true, - "supports_pdf_input": true, - "supports_prompt_caching": true, - "supports_reasoning": true, - "supports_response_schema": true, - "supports_tool_choice": true, - "supports_vision": true - }, - "topxai/gpt-5.6-sol": { - "cache_creation_input_token_cost": 2.5e-06, - "cache_creation_input_token_cost_above_272k_tokens": 5e-06, - "cache_read_input_token_cost": 2e-07, - "cache_read_input_token_cost_above_272k_tokens": 4e-07, - "input_cost_per_token": 2e-06, - "input_cost_per_token_above_272k_tokens": 4e-06, - "litellm_provider": "topxai", - "max_input_tokens": 1000000, - "max_output_tokens": 128000, - "max_tokens": 128000, - "mode": "chat", - "output_cost_per_token": 1e-05, - "output_cost_per_token_above_272k_tokens": 1.5e-05, - "source": "https://ai.topxea.com/pricing/gpt-5.6-sol", + "output_cost_per_token": 5e-06, + "output_cost_per_token_above_272k_tokens": 7.5e-06, + "source": "https://ai.topxea.com/pricing/gpt-6-sol", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, @@ -45596,7 +45575,7 @@ "input_cost_per_token": 5e-06, "input_cost_per_token_above_272k_tokens": 1e-05, "litellm_provider": "topxai", - "max_input_tokens": 1000000, + "max_input_tokens": 1050000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", @@ -45611,17 +45590,14 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/grok-4.6": { - "cache_creation_input_token_cost": 1e-06, + "topxai/grok-4.7": { "cache_read_input_token_cost": 2.5e-07, "input_cost_per_token": 1e-06, "litellm_provider": "topxai", "max_input_tokens": 500000, - "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-06, - "source": "https://ai.topxea.com/pricing/grok-4.6", + "source": "https://ai.topxea.com/pricing/grok-4.7", "supports_function_calling": true, "supports_pdf_input": false, "supports_prompt_caching": true, @@ -45631,7 +45607,6 @@ "supports_vision": true, "tiered_pricing": [ { - "cache_creation_input_token_cost": 1e-06, "cache_read_input_token_cost": 2.5e-07, "input_cost_per_token": 1e-06, "output_cost_per_token": 3e-06, @@ -45641,7 +45616,6 @@ ] }, { - "cache_creation_input_token_cost": 2e-06, "cache_read_input_token_cost": 5e-07, "input_cost_per_token": 2e-06, "output_cost_per_token": 6e-06, @@ -45672,7 +45646,6 @@ "supports_vision": true }, "topxai/GLM-5.3-Abliterated": { - "cache_creation_input_token_cost": 4e-06, "cache_read_input_token_cost": 4e-07, "input_cost_per_token": 4e-06, "litellm_provider": "topxai", @@ -45690,6 +45663,98 @@ "supports_tool_choice": true, "supports_vision": false }, + "topxai/deepseek-flash": { + "cache_read_input_token_cost": 4.8e-09, + "input_cost_per_token": 2.4e-07, + "litellm_provider": "topxai", + "max_input_tokens": 1048576, + "max_output_tokens": 393216, + "max_tokens": 393216, + "mode": "chat", + "off_peak_pricing": { + "cache_read_input_token_cost": 2.4e-09, + "input_cost_per_token": 1.2e-07, + "output_cost_per_token": 4.8e-07, + "windows": [ + { + "hours_utc": [ + "00:00-01:00", + "04:00-06:00", + "10:00-00:00" + ], + "weekdays": [ + 1, + 2, + 3, + 4, + 5 + ] + }, + { + "hours_utc": "00:00-00:00", + "weekdays": [ + 6, + 7 + ] + } + ] + }, + "output_cost_per_token": 9.6e-07, + "source": "https://ai.topxea.com/pricing/deepseek-flash", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/deepseek-v4-pro": { + "cache_read_input_token_cost": 3.52e-08, + "input_cost_per_token": 1.056e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1048576, + "max_output_tokens": 393216, + "max_tokens": 393216, + "mode": "chat", + "off_peak_pricing": { + "cache_read_input_token_cost": 1.76e-08, + "input_cost_per_token": 5.28e-07, + "output_cost_per_token": 1.584e-06, + "windows": [ + { + "hours_utc": [ + "00:00-01:00", + "04:00-06:00", + "10:00-00:00" + ], + "weekdays": [ + 1, + 2, + 3, + 4, + 5 + ] + }, + { + "hours_utc": "00:00-00:00", + "weekdays": [ + 6, + 7 + ] + } + ] + }, + "output_cost_per_token": 3.168e-06, + "source": "https://ai.topxea.com/pricing/deepseek-v4-pro", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false + }, "snowflake/claude-3-5-sonnet": { "litellm_provider": "snowflake", "max_input_tokens": 200000, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 836c2d7b3db..f30269e82b0 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -45502,18 +45502,18 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/claude-opus-5": { - "cache_creation_input_token_cost": 3.125e-06, - "cache_creation_input_token_cost_above_1hr": 5e-06, - "cache_read_input_token_cost": 2.5e-07, - "input_cost_per_token": 2.5e-06, + "topxai/claude-opus-5-5": { + "cache_creation_input_token_cost": 2.5e-06, + "cache_creation_input_token_cost_above_1hr": 4e-06, + "cache_read_input_token_cost": 1e-07, + "input_cost_per_token": 2e-06, "litellm_provider": "topxai", "max_input_tokens": 1000000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", - "output_cost_per_token": 1.25e-05, - "source": "https://ai.topxea.com/pricing/claude-opus-5", + "output_cost_per_token": 1e-05, + "source": "https://ai.topxea.com/pricing/claude-opus-5-5", "supports_adaptive_thinking": true, "supports_function_calling": true, "supports_pdf_input": true, @@ -45544,42 +45544,21 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/claude-fable-5": { - "cache_creation_input_token_cost": 6.25e-06, - "cache_creation_input_token_cost_above_1hr": 1e-05, - "cache_read_input_token_cost": 5e-07, - "input_cost_per_token": 5e-06, + "topxai/gpt-6-sol": { + "cache_creation_input_token_cost": 1.25e-06, + "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, + "cache_read_input_token_cost": 1e-07, + "cache_read_input_token_cost_above_272k_tokens": 2e-07, + "input_cost_per_token": 1e-06, + "input_cost_per_token_above_272k_tokens": 2e-06, "litellm_provider": "topxai", - "max_input_tokens": 1000000, + "max_input_tokens": 1050000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", - "output_cost_per_token": 2.5e-05, - "source": "https://ai.topxea.com/pricing/claude-fable-5", - "supports_adaptive_thinking": true, - "supports_function_calling": true, - "supports_pdf_input": true, - "supports_prompt_caching": true, - "supports_reasoning": true, - "supports_response_schema": true, - "supports_tool_choice": true, - "supports_vision": true - }, - "topxai/gpt-5.6-sol": { - "cache_creation_input_token_cost": 2.5e-06, - "cache_creation_input_token_cost_above_272k_tokens": 5e-06, - "cache_read_input_token_cost": 2e-07, - "cache_read_input_token_cost_above_272k_tokens": 4e-07, - "input_cost_per_token": 2e-06, - "input_cost_per_token_above_272k_tokens": 4e-06, - "litellm_provider": "topxai", - "max_input_tokens": 1000000, - "max_output_tokens": 128000, - "max_tokens": 128000, - "mode": "chat", - "output_cost_per_token": 1e-05, - "output_cost_per_token_above_272k_tokens": 1.5e-05, - "source": "https://ai.topxea.com/pricing/gpt-5.6-sol", + "output_cost_per_token": 5e-06, + "output_cost_per_token_above_272k_tokens": 7.5e-06, + "source": "https://ai.topxea.com/pricing/gpt-6-sol", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, @@ -45596,7 +45575,7 @@ "input_cost_per_token": 5e-06, "input_cost_per_token_above_272k_tokens": 1e-05, "litellm_provider": "topxai", - "max_input_tokens": 1000000, + "max_input_tokens": 1050000, "max_output_tokens": 128000, "max_tokens": 128000, "mode": "chat", @@ -45611,17 +45590,14 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/grok-4.6": { - "cache_creation_input_token_cost": 1e-06, + "topxai/grok-4.7": { "cache_read_input_token_cost": 2.5e-07, "input_cost_per_token": 1e-06, "litellm_provider": "topxai", "max_input_tokens": 500000, - "max_output_tokens": 128000, - "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 3e-06, - "source": "https://ai.topxea.com/pricing/grok-4.6", + "source": "https://ai.topxea.com/pricing/grok-4.7", "supports_function_calling": true, "supports_pdf_input": false, "supports_prompt_caching": true, @@ -45631,7 +45607,6 @@ "supports_vision": true, "tiered_pricing": [ { - "cache_creation_input_token_cost": 1e-06, "cache_read_input_token_cost": 2.5e-07, "input_cost_per_token": 1e-06, "output_cost_per_token": 3e-06, @@ -45641,7 +45616,6 @@ ] }, { - "cache_creation_input_token_cost": 2e-06, "cache_read_input_token_cost": 5e-07, "input_cost_per_token": 2e-06, "output_cost_per_token": 6e-06, @@ -45672,7 +45646,6 @@ "supports_vision": true }, "topxai/GLM-5.3-Abliterated": { - "cache_creation_input_token_cost": 4e-06, "cache_read_input_token_cost": 4e-07, "input_cost_per_token": 4e-06, "litellm_provider": "topxai", @@ -45690,6 +45663,98 @@ "supports_tool_choice": true, "supports_vision": false }, + "topxai/deepseek-flash": { + "cache_read_input_token_cost": 4.8e-09, + "input_cost_per_token": 2.4e-07, + "litellm_provider": "topxai", + "max_input_tokens": 1048576, + "max_output_tokens": 393216, + "max_tokens": 393216, + "mode": "chat", + "off_peak_pricing": { + "cache_read_input_token_cost": 2.4e-09, + "input_cost_per_token": 1.2e-07, + "output_cost_per_token": 4.8e-07, + "windows": [ + { + "hours_utc": [ + "00:00-01:00", + "04:00-06:00", + "10:00-00:00" + ], + "weekdays": [ + 1, + 2, + 3, + 4, + 5 + ] + }, + { + "hours_utc": "00:00-00:00", + "weekdays": [ + 6, + 7 + ] + } + ] + }, + "output_cost_per_token": 9.6e-07, + "source": "https://ai.topxea.com/pricing/deepseek-flash", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "topxai/deepseek-v4-pro": { + "cache_read_input_token_cost": 3.52e-08, + "input_cost_per_token": 1.056e-06, + "litellm_provider": "topxai", + "max_input_tokens": 1048576, + "max_output_tokens": 393216, + "max_tokens": 393216, + "mode": "chat", + "off_peak_pricing": { + "cache_read_input_token_cost": 1.76e-08, + "input_cost_per_token": 5.28e-07, + "output_cost_per_token": 1.584e-06, + "windows": [ + { + "hours_utc": [ + "00:00-01:00", + "04:00-06:00", + "10:00-00:00" + ], + "weekdays": [ + 1, + 2, + 3, + 4, + 5 + ] + }, + { + "hours_utc": "00:00-00:00", + "weekdays": [ + 6, + 7 + ] + } + ] + }, + "output_cost_per_token": 3.168e-06, + "source": "https://ai.topxea.com/pricing/deepseek-v4-pro", + "supports_function_calling": true, + "supports_pdf_input": false, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false + }, "snowflake/claude-3-5-sonnet": { "litellm_provider": "snowflake", "max_input_tokens": 200000, diff --git a/tests/unit/llms/openai_like/test_topxai_provider.py b/tests/unit/llms/openai_like/test_topxai_provider.py index f24b7f361b4..f235e6593cf 100644 --- a/tests/unit/llms/openai_like/test_topxai_provider.py +++ b/tests/unit/llms/openai_like/test_topxai_provider.py @@ -3,12 +3,14 @@ Tests for the TopxAI provider configuration and integration. """ import json +from datetime import datetime, timezone from typing import Final import httpx import pytest import litellm +from litellm._internal_context import pinned_billing_time from litellm.llms.custom_httpx.http_handler import HTTPHandler # Provider endpoint: https://ai.topxea.com/docs (verified 2026-09-19) @@ -72,7 +74,7 @@ class TestTopxAIProviderConfig: from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider model, provider, api_key, api_base = get_llm_provider( - model="gpt-5.6-sol", + model="gpt-6-sol", custom_llm_provider=None, api_base=TOPXAI_BASE_URL, api_key=None, @@ -121,7 +123,7 @@ class TestTopxAIProviderConfig: assert str(request.url) == f"{expected_base}/responses" assert request.headers["Authorization"] == "Bearer sk-topxai-test" payload: Final = json.loads(request.content) - assert payload["model"] == "gpt-5.6-sol" + assert payload["model"] == "gpt-6-sol" assert payload["input"] == "Reply with OK" return httpx.Response( 200, @@ -129,22 +131,24 @@ class TestTopxAIProviderConfig: "id": "resp_topxai_test", "object": "response", "created_at": 1, - "model": "gpt-5.6-sol", + "model": "gpt-6-sol", "status": "completed", - "output": [{ - "type": "message", - "id": "msg_test", - "role": "assistant", - "status": "completed", - "content": [{"type": "output_text", "text": "OK", "annotations": []}], - }], + "output": [ + { + "type": "message", + "id": "msg_test", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "OK", "annotations": []}], + } + ], "usage": {"input_tokens": 4, "output_tokens": 1, "total_tokens": 5}, }, ) with httpx.Client(transport=httpx.MockTransport(respond)) as client: response: Final = litellm.responses( - model="topxai/gpt-5.6-sol", + model="topxai/gpt-6-sol", input="Reply with OK", api_base=api_base, client=HTTPHandler(client=client), @@ -175,27 +179,57 @@ class TestTopxAIProviderConfig: class TestTopxAIModelMetadata: - # Catalog and capabilities: https://ai.topxea.com/pricing (verified 2026-09-19) + # Catalog and capabilities: https://ai.topxea.com/api/pricing (verified 2026-09-28) TOPXAI_MODELS: Final = ( "topxai/claude-sonnet-5", - "topxai/claude-opus-5", + "topxai/claude-opus-5-5", "topxai/claude-fable-5-1", - "topxai/claude-fable-5", - "topxai/gpt-5.6-sol", + "topxai/gpt-6-sol", "topxai/gpt-6-astra", - "topxai/grok-4.6", + "topxai/grok-4.7", "topxai/kimi-k3", "topxai/GLM-5.3-Abliterated", + "topxai/deepseek-flash", + "topxai/deepseek-v4-pro", ) - TEXT_ONLY_MODELS: Final = ("topxai/GLM-5.3-Abliterated",) - # First premium token and output multiplier, verified 2026-09-19: - # https://ai.topxea.com/pricing/gpt-5.6-sol + TEXT_ONLY_MODELS: Final = ("topxai/GLM-5.3-Abliterated", "topxai/deepseek-v4-pro") + # Only these routes publish a cache-write price; the rest bill cache writes as input + CACHE_WRITE_MODELS: Final = ( + "topxai/claude-sonnet-5", + "topxai/claude-opus-5-5", + "topxai/claude-fable-5-1", + "topxai/gpt-6-sol", + "topxai/gpt-6-astra", + "topxai/kimi-k3", + ) + # The catalogue publishes no output limit for these + NO_OUTPUT_LIMIT_MODELS: Final = ("topxai/grok-4.7",) + # First premium token and output multiplier, verified 2026-09-28: + # https://ai.topxea.com/pricing/gpt-6-sol # https://ai.topxea.com/pricing/gpt-6-astra - # https://ai.topxea.com/pricing/grok-4.6 + # https://ai.topxea.com/pricing/grok-4.7 TIERED_MODELS: Final = ( - ("topxai/gpt-5.6-sol", 272_001, 1.5), + ("topxai/gpt-6-sol", 272_001, 1.5), ("topxai/gpt-6-astra", 272_001, 1.5), - ("topxai/grok-4.6", 200_000, 2.0), + ("topxai/grok-4.7", 200_000, 2.0), + ) + # USD for 1M prompt tokens (400K of them cache reads) and 1M completion tokens at the + # deepseek-official peak lanes, verified 2026-09-28: flash $0.24 in, $0.0048 cached, $0.96 out; + # v4-pro $1.056 in, $0.0352 cached, $3.168 out. Off-peak lanes are half. + # https://ai.topxea.com/pricing/deepseek-flash + # https://ai.topxea.com/pricing/deepseek-v4-pro + DEEPSEEK_PEAK_COSTS: Final = ( + ("topxai/deepseek-flash", 1.10592), + ("topxai/deepseek-v4-pro", 3.81568), + ) + # Peak hours are 01:00-04:00 and 06:00-10:00 UTC, Monday to Friday + DEEPSEEK_MOMENTS: Final = ( + pytest.param(datetime(2026, 9, 28, 1, 0, tzinfo=timezone.utc), 1.0, id="monday-01:00"), + pytest.param(datetime(2026, 9, 28, 9, 59, tzinfo=timezone.utc), 1.0, id="monday-09:59"), + pytest.param(datetime(2026, 9, 28, 0, 59, tzinfo=timezone.utc), 0.5, id="monday-00:59"), + pytest.param(datetime(2026, 9, 28, 4, 0, tzinfo=timezone.utc), 0.5, id="monday-04:00"), + pytest.param(datetime(2026, 9, 28, 10, 0, tzinfo=timezone.utc), 0.5, id="monday-10:00"), + pytest.param(datetime(2026, 9, 27, 8, 0, tzinfo=timezone.utc), 0.5, id="sunday-08:00"), ) @staticmethod @@ -224,9 +258,14 @@ class TestTopxAIModelMetadata: assert info["supports_prompt_caching"] is True assert 0 < info["cache_read_input_token_cost"] < info["input_cost_per_token"] - assert info["cache_creation_input_token_cost"] >= info["input_cost_per_token"] + assert ("cache_creation_input_token_cost" in info) is (model in self.CACHE_WRITE_MODELS) + assert ( + info.get("cache_creation_input_token_cost", info["input_cost_per_token"]) + >= info["input_cost_per_token"] + ) - assert info["max_tokens"] == info["max_output_tokens"] + assert ("max_output_tokens" in info) is (model not in self.NO_OUTPUT_LIMIT_MODELS) + assert info.get("max_tokens") == info.get("max_output_tokens") assert info["max_input_tokens"] >= 500_000 assert info["source"].startswith("https://ai.topxea.com/pricing/") @@ -247,12 +286,13 @@ class TestTopxAIModelMetadata: monkeypatch.setitem(litellm.model_cost, model, info) prompt_tokens: Final = first_premium_token + offset completion_tokens: Final = 17 - # Whole-request input/cache rates double at these boundaries; sources above + # Whole-request input/cache rates double at these boundaries; sources above. + # Without a published cache-write price a cache write bills as input. input_multiplier: Final = 2 if offset >= 0 else 1 expected_input: Final = input_multiplier * ( (prompt_tokens - cached_tokens - cache_write_tokens) * info["input_cost_per_token"] + cached_tokens * info["cache_read_input_token_cost"] - + cache_write_tokens * info["cache_creation_input_token_cost"] + + cache_write_tokens * info.get("cache_creation_input_token_cost", info["input_cost_per_token"]) ) expected_output: Final = ( completion_tokens * info["output_cost_per_token"] * (output_multiplier if offset >= 0 else 1) @@ -267,6 +307,26 @@ class TestTopxAIModelMetadata: assert prompt_cost == pytest.approx(expected_input) assert completion_cost == pytest.approx(expected_output) + @pytest.mark.parametrize("model,peak_cost", DEEPSEEK_PEAK_COSTS) + @pytest.mark.parametrize("moment,rate_share", DEEPSEEK_MOMENTS) + def test_topxai_deepseek_bills_peak_lanes_only_in_weekday_peak_hours( + self, + monkeypatch: pytest.MonkeyPatch, + model: str, + peak_cost: float, + moment: datetime, + rate_share: float, + ) -> None: + monkeypatch.setitem(litellm.model_cost, model, self._load(("model_prices_and_context_window.json",))[model]) + with pinned_billing_time(moment): + prompt_cost, completion_cost = litellm.cost_per_token( + model=model, + prompt_tokens=1_000_000, + completion_tokens=1_000_000, + cache_read_input_tokens=400_000, + ) + assert prompt_cost + completion_cost == pytest.approx(peak_cost * rate_share) + def test_topxai_models_synced_to_backup(self): model_cost = self._load(("model_prices_and_context_window.json",)) backup = self._load(("litellm", "model_prices_and_context_window_backup.json")) From 323e34edb50c8a17d57f48197d115be4e4033ff4 Mon Sep 17 00:00:00 2001 From: C-L-STARK Date: Wed, 30 Sep 2026 20:59:07 +0800 Subject: [PATCH 5/8] fix(topxai): gpt-6.1-sol replaces the retired gpt-6-sol TopxAI retired gpt-6-sol on 2026-09-30 with no forwarding alias; requests for it now answer HTTP 403. topxai/gpt-6.1-sol takes the place of its entry in both cost maps. Prices are the openai-shared route that auto_route names in https://ai.topxea.com/api/pricing (2026-09-30), encoded the way the gpt-6-sol entry was: input 1e-06, output 5e-06, cache read 5e-08 (half of GPT-6 Sol's 1e-07), cache write 1.25e-06. Above 272,000 input tokens, cache tokens included, the whole request bills 2e-06 / 7.5e-06 / 1e-07 / 2.5e-06. Context 1,050,000 and output 128,000 tokens as the catalogue lists them; the capability flags are unchanged. The tests follow the new id. Their catalogue comments now cite 2026-09-30, when all ten entries were checked against /api/pricing again. Co-Authored-By: Claude Opus 5.5 --- ...odel_prices_and_context_window_backup.json | 8 ++++---- model_prices_and_context_window.json | 8 ++++---- .../llms/openai_like/test_topxai_provider.py | 20 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 729cd339222..420a0a50392 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -45799,11 +45799,11 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/gpt-6-sol": { + "topxai/gpt-6.1-sol": { "cache_creation_input_token_cost": 1.25e-06, "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, - "cache_read_input_token_cost": 1e-07, - "cache_read_input_token_cost_above_272k_tokens": 2e-07, + "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1e-07, "input_cost_per_token": 1e-06, "input_cost_per_token_above_272k_tokens": 2e-06, "litellm_provider": "topxai", @@ -45813,7 +45813,7 @@ "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_above_272k_tokens": 7.5e-06, - "source": "https://ai.topxea.com/pricing/gpt-6-sol", + "source": "https://ai.topxea.com/pricing/gpt-6.1-sol", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 729cd339222..420a0a50392 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -45799,11 +45799,11 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/gpt-6-sol": { + "topxai/gpt-6.1-sol": { "cache_creation_input_token_cost": 1.25e-06, "cache_creation_input_token_cost_above_272k_tokens": 2.5e-06, - "cache_read_input_token_cost": 1e-07, - "cache_read_input_token_cost_above_272k_tokens": 2e-07, + "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost_above_272k_tokens": 1e-07, "input_cost_per_token": 1e-06, "input_cost_per_token_above_272k_tokens": 2e-06, "litellm_provider": "topxai", @@ -45813,7 +45813,7 @@ "mode": "chat", "output_cost_per_token": 5e-06, "output_cost_per_token_above_272k_tokens": 7.5e-06, - "source": "https://ai.topxea.com/pricing/gpt-6-sol", + "source": "https://ai.topxea.com/pricing/gpt-6.1-sol", "supports_function_calling": true, "supports_pdf_input": true, "supports_prompt_caching": true, diff --git a/tests/unit/llms/openai_like/test_topxai_provider.py b/tests/unit/llms/openai_like/test_topxai_provider.py index f235e6593cf..611ff0a84fc 100644 --- a/tests/unit/llms/openai_like/test_topxai_provider.py +++ b/tests/unit/llms/openai_like/test_topxai_provider.py @@ -74,7 +74,7 @@ class TestTopxAIProviderConfig: from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider model, provider, api_key, api_base = get_llm_provider( - model="gpt-6-sol", + model="gpt-6.1-sol", custom_llm_provider=None, api_base=TOPXAI_BASE_URL, api_key=None, @@ -123,7 +123,7 @@ class TestTopxAIProviderConfig: assert str(request.url) == f"{expected_base}/responses" assert request.headers["Authorization"] == "Bearer sk-topxai-test" payload: Final = json.loads(request.content) - assert payload["model"] == "gpt-6-sol" + assert payload["model"] == "gpt-6.1-sol" assert payload["input"] == "Reply with OK" return httpx.Response( 200, @@ -131,7 +131,7 @@ class TestTopxAIProviderConfig: "id": "resp_topxai_test", "object": "response", "created_at": 1, - "model": "gpt-6-sol", + "model": "gpt-6.1-sol", "status": "completed", "output": [ { @@ -148,7 +148,7 @@ class TestTopxAIProviderConfig: with httpx.Client(transport=httpx.MockTransport(respond)) as client: response: Final = litellm.responses( - model="topxai/gpt-6-sol", + model="topxai/gpt-6.1-sol", input="Reply with OK", api_base=api_base, client=HTTPHandler(client=client), @@ -179,12 +179,12 @@ class TestTopxAIProviderConfig: class TestTopxAIModelMetadata: - # Catalog and capabilities: https://ai.topxea.com/api/pricing (verified 2026-09-28) + # Catalog and capabilities: https://ai.topxea.com/api/pricing (verified 2026-09-30) TOPXAI_MODELS: Final = ( "topxai/claude-sonnet-5", "topxai/claude-opus-5-5", "topxai/claude-fable-5-1", - "topxai/gpt-6-sol", + "topxai/gpt-6.1-sol", "topxai/gpt-6-astra", "topxai/grok-4.7", "topxai/kimi-k3", @@ -198,18 +198,18 @@ class TestTopxAIModelMetadata: "topxai/claude-sonnet-5", "topxai/claude-opus-5-5", "topxai/claude-fable-5-1", - "topxai/gpt-6-sol", + "topxai/gpt-6.1-sol", "topxai/gpt-6-astra", "topxai/kimi-k3", ) # The catalogue publishes no output limit for these NO_OUTPUT_LIMIT_MODELS: Final = ("topxai/grok-4.7",) - # First premium token and output multiplier, verified 2026-09-28: - # https://ai.topxea.com/pricing/gpt-6-sol + # First premium token and output multiplier, verified 2026-09-30: + # https://ai.topxea.com/pricing/gpt-6.1-sol # https://ai.topxea.com/pricing/gpt-6-astra # https://ai.topxea.com/pricing/grok-4.7 TIERED_MODELS: Final = ( - ("topxai/gpt-6-sol", 272_001, 1.5), + ("topxai/gpt-6.1-sol", 272_001, 1.5), ("topxai/gpt-6-astra", 272_001, 1.5), ("topxai/grok-4.7", 200_000, 2.0), ) From b6aa049545d40532839a822e300cf3ca92390e3c Mon Sep 17 00:00:00 2001 From: C-L-STARK Date: Mon, 5 Oct 2026 22:42:11 +0800 Subject: [PATCH 6/8] topxai: claude-sonnet-5-5 replaces the retired claude-sonnet-5 TopxAI retired claude-sonnet-5 on 2026-10-02 (requests answer 403) and offers claude-sonnet-5-5 at the same shared-pool prices (https://ai.topxea.com/pricing/claude-sonnet-5-5). --- litellm/model_prices_and_context_window_backup.json | 4 ++-- model_prices_and_context_window.json | 4 ++-- tests/unit/llms/openai_like/test_topxai_provider.py | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index be1b2702cb5..0ce2efb81b6 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -45750,7 +45750,7 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/claude-sonnet-5": { + "topxai/claude-sonnet-5-5": { "cache_creation_input_token_cost": 1.25e-06, "cache_creation_input_token_cost_above_1hr": 2e-06, "cache_read_input_token_cost": 1e-07, @@ -45761,7 +45761,7 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-06, - "source": "https://ai.topxea.com/pricing/claude-sonnet-5", + "source": "https://ai.topxea.com/pricing/claude-sonnet-5-5", "supports_adaptive_thinking": true, "supports_function_calling": true, "supports_pdf_input": true, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index be1b2702cb5..0ce2efb81b6 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -45750,7 +45750,7 @@ "supports_tool_choice": true, "supports_vision": true }, - "topxai/claude-sonnet-5": { + "topxai/claude-sonnet-5-5": { "cache_creation_input_token_cost": 1.25e-06, "cache_creation_input_token_cost_above_1hr": 2e-06, "cache_read_input_token_cost": 1e-07, @@ -45761,7 +45761,7 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 5e-06, - "source": "https://ai.topxea.com/pricing/claude-sonnet-5", + "source": "https://ai.topxea.com/pricing/claude-sonnet-5-5", "supports_adaptive_thinking": true, "supports_function_calling": true, "supports_pdf_input": true, diff --git a/tests/unit/llms/openai_like/test_topxai_provider.py b/tests/unit/llms/openai_like/test_topxai_provider.py index 611ff0a84fc..71f9a5071ca 100644 --- a/tests/unit/llms/openai_like/test_topxai_provider.py +++ b/tests/unit/llms/openai_like/test_topxai_provider.py @@ -46,13 +46,13 @@ class TestTopxAIProviderConfig: from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider model, provider, api_key, api_base = get_llm_provider( - model="topxai/claude-sonnet-5", + model="topxai/claude-sonnet-5-5", custom_llm_provider=None, api_base=None, api_key=None, ) - assert model == "claude-sonnet-5" + assert model == "claude-sonnet-5-5" assert provider == "topxai" assert api_base == TOPXAI_BASE_URL @@ -60,7 +60,7 @@ class TestTopxAIProviderConfig: from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider model, provider, api_key, api_base = get_llm_provider( - model="topxai/claude-sonnet-5", + model="topxai/claude-sonnet-5-5", custom_llm_provider=None, api_base="https://relay.example.com/v1", api_key="sk-test", @@ -167,7 +167,7 @@ class TestTopxAIProviderConfig: { "model_name": "sonnet", "litellm_params": { - "model": "topxai/claude-sonnet-5", + "model": "topxai/claude-sonnet-5-5", "api_key": "test-key", }, } @@ -181,7 +181,7 @@ class TestTopxAIProviderConfig: class TestTopxAIModelMetadata: # Catalog and capabilities: https://ai.topxea.com/api/pricing (verified 2026-09-30) TOPXAI_MODELS: Final = ( - "topxai/claude-sonnet-5", + "topxai/claude-sonnet-5-5", "topxai/claude-opus-5-5", "topxai/claude-fable-5-1", "topxai/gpt-6.1-sol", @@ -195,7 +195,7 @@ class TestTopxAIModelMetadata: TEXT_ONLY_MODELS: Final = ("topxai/GLM-5.3-Abliterated", "topxai/deepseek-v4-pro") # Only these routes publish a cache-write price; the rest bill cache writes as input CACHE_WRITE_MODELS: Final = ( - "topxai/claude-sonnet-5", + "topxai/claude-sonnet-5-5", "topxai/claude-opus-5-5", "topxai/claude-fable-5-1", "topxai/gpt-6.1-sol", From bcdc3e72a9584181fa542ce6d32027ebb40d51be Mon Sep 17 00:00:00 2001 From: C-L-STARK Date: Mon, 5 Oct 2026 22:44:06 +0800 Subject: [PATCH 7/8] topxai: dashboard example model is claude-sonnet-5-5 --- .../src/components/provider_info_helpers.test.tsx | 2 +- ui/litellm-dashboard/src/components/provider_info_helpers.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx index 79f7faae65c..eec5a53530e 100644 --- a/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx +++ b/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx @@ -245,7 +245,7 @@ describe("provider_info_helpers", () => { }); it("should return a topxai model placeholder for TOPXAI provider", () => { - expect(getPlaceholder(Providers.TOPXAI)).toBe("topxai/claude-sonnet-5"); + expect(getPlaceholder(Providers.TOPXAI)).toBe("topxai/claude-sonnet-5-5"); }); it("should return claude-3-opus placeholder for Anthropic provider", () => { diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx index c7ef86257dc..0d9954eed09 100644 --- a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx +++ b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx @@ -462,7 +462,7 @@ const providerPlaceholderMap: Partial> = { [Providers.SCX_AI]: "scx-ai/GLM-5.2", [Providers.Snowflake]: "snowflake/mistral-7b", [Providers.Tencent]: "tencent/deepseek-v4-pro", - [Providers.TOPXAI]: "topxai/claude-sonnet-5", + [Providers.TOPXAI]: "topxai/claude-sonnet-5-5", [Providers.Vertex_AI]: "gemini-pro", [Providers.VolcEngine]: "volcengine/", [Providers.Voyage]: "voyage/", From 805235bb050738d1a09af6a5a526292404e1dbf9 Mon Sep 17 00:00:00 2001 From: C-L-STARK Date: Mon, 5 Oct 2026 22:44:54 +0800 Subject: [PATCH 8/8] topxai: provider placeholder model is claude-sonnet-5-5 --- litellm/proxy/public_endpoints/provider_create_fields.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/proxy/public_endpoints/provider_create_fields.json b/litellm/proxy/public_endpoints/provider_create_fields.json index 0a5088f84bb..79595ae0aae 100644 --- a/litellm/proxy/public_endpoints/provider_create_fields.json +++ b/litellm/proxy/public_endpoints/provider_create_fields.json @@ -3141,7 +3141,7 @@ "default_value": null } ], - "default_model_placeholder": "topxai/claude-sonnet-5" + "default_model_placeholder": "topxai/claude-sonnet-5-5" }, { "provider": "Snowflake",