From 9b7f5574c9fe5ec250cede46fa933428372ea4d2 Mon Sep 17 00:00:00 2001 From: Vikash Maddi Date: Mon, 2 Nov 2026 19:04:28 +0530 Subject: [PATCH 1/4] feat(providers): add Sarvam Token Factory v2 models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🪷 Generated with Sarvam Code --- litellm/llms/openai_like/providers.json | 12 +- ...odel_prices_and_context_window_backup.json | 120 ++++++++++++++++-- model_prices_and_context_window.json | 120 ++++++++++++++++-- .../routing/test_prompt_cache_pin_ttl.py | 2 +- .../llms/openai_like/test_sarvam_provider.py | 98 ++++++++++++++ 5 files changed, 326 insertions(+), 26 deletions(-) create mode 100644 tests/unit/llms/openai_like/test_sarvam_provider.py diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index a71a9c3bb9b..3dd723eaec4 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -87,15 +87,13 @@ "api_key_env": "GMI_API_KEY" }, "sarvam": { - "base_url": "https://api.sarvam.ai/v1", + "base_url": "https://api.sarvam.ai/v2", "api_key_env": "SARVAM_API_KEY", "base_class": "openai_gpt", - "param_mappings": { - "max_completion_tokens": "max_tokens" - }, - "headers": { - "api-subscription-key": "{api_key}" - } + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/responses" + ] }, "assemblyai": { "base_url": "https://llm-gateway.assemblyai.com/v1", diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index d68eb3ce775..479717337bc 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -58635,17 +58635,119 @@ "mode": "embedding", "source": "https://docs.libertai.io/apis/text/" }, - "sarvam/sarvam-m": { - "cache_creation_input_token_cost": 0, - "cache_creation_input_token_cost_above_1hr": 0, - "cache_read_input_token_cost": 0, - "input_cost_per_token": 0, + "sarvam/sarvam-105b": { + "input_cost_per_token": 1.7e-07, "litellm_provider": "sarvam", - "max_input_tokens": 8192, - "max_output_tokens": 32000, + "max_input_tokens": 128000, + "max_output_tokens": 128000, + "max_tokens": 128000, "mode": "chat", - "output_cost_per_token": 0, - "supports_reasoning": true + "output_cost_per_token": 6.7e-07, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": false + }, + "sarvam/sarvam-105b-conversations": { + "input_cost_per_token": 1.7e-07, + "litellm_provider": "sarvam", + "max_input_tokens": 32000, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 6.7e-07, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": false, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": false + }, + "sarvam/glm5.3": { + "input_cost_per_token": 1.4e-06, + "litellm_provider": "sarvam", + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "mode": "chat", + "output_cost_per_token": 4.33e-06, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": false + }, + "sarvam/glm5.3-flash": { + "input_cost_per_token": 1.5e-07, + "litellm_provider": "sarvam", + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "mode": "chat", + "output_cost_per_token": 5.0e-07, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": true + }, + "sarvam/gemma4": { + "input_cost_per_token": 3.9e-07, + "litellm_provider": "sarvam", + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "mode": "chat", + "output_cost_per_token": 1.0e-06, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": true + }, + "sarvam/deepseekv4.1-flash-1017": { + "input_cost_per_token": 2.8e-07, + "litellm_provider": "sarvam", + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "mode": "chat", + "output_cost_per_token": 1.17e-06, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": true }, "tts-1-1106": { "input_cost_per_character": 1.5e-05, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index d68eb3ce775..479717337bc 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -58635,17 +58635,119 @@ "mode": "embedding", "source": "https://docs.libertai.io/apis/text/" }, - "sarvam/sarvam-m": { - "cache_creation_input_token_cost": 0, - "cache_creation_input_token_cost_above_1hr": 0, - "cache_read_input_token_cost": 0, - "input_cost_per_token": 0, + "sarvam/sarvam-105b": { + "input_cost_per_token": 1.7e-07, "litellm_provider": "sarvam", - "max_input_tokens": 8192, - "max_output_tokens": 32000, + "max_input_tokens": 128000, + "max_output_tokens": 128000, + "max_tokens": 128000, "mode": "chat", - "output_cost_per_token": 0, - "supports_reasoning": true + "output_cost_per_token": 6.7e-07, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": false + }, + "sarvam/sarvam-105b-conversations": { + "input_cost_per_token": 1.7e-07, + "litellm_provider": "sarvam", + "max_input_tokens": 32000, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 6.7e-07, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": false, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": false + }, + "sarvam/glm5.3": { + "input_cost_per_token": 1.4e-06, + "litellm_provider": "sarvam", + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "mode": "chat", + "output_cost_per_token": 4.33e-06, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": false + }, + "sarvam/glm5.3-flash": { + "input_cost_per_token": 1.5e-07, + "litellm_provider": "sarvam", + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "mode": "chat", + "output_cost_per_token": 5.0e-07, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": true + }, + "sarvam/gemma4": { + "input_cost_per_token": 3.9e-07, + "litellm_provider": "sarvam", + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "mode": "chat", + "output_cost_per_token": 1.0e-06, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": true + }, + "sarvam/deepseekv4.1-flash-1017": { + "input_cost_per_token": 2.8e-07, + "litellm_provider": "sarvam", + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "mode": "chat", + "output_cost_per_token": 1.17e-06, + "supports_audio_input": false, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": true }, "tts-1-1106": { "input_cost_per_character": 1.5e-05, diff --git a/tests/integration/routing/test_prompt_cache_pin_ttl.py b/tests/integration/routing/test_prompt_cache_pin_ttl.py index f6ee96a9206..268a868c76e 100644 --- a/tests/integration/routing/test_prompt_cache_pin_ttl.py +++ b/tests/integration/routing/test_prompt_cache_pin_ttl.py @@ -174,7 +174,7 @@ def _model_list(rig_wires: Mapping[str, str]) -> list[JsonValue]: deployment( "pin-zero-one-hour-price", "pin-zero-one-hour-price", - model="sarvam/sarvam-m", + model="sarvam/sarvam-105b", api_base=f"{rig_wires['openai']}/v1", api_key=PROVIDER_KEY, ), diff --git a/tests/unit/llms/openai_like/test_sarvam_provider.py b/tests/unit/llms/openai_like/test_sarvam_provider.py new file mode 100644 index 00000000000..442ffb9c083 --- /dev/null +++ b/tests/unit/llms/openai_like/test_sarvam_provider.py @@ -0,0 +1,98 @@ +""" +Tests for the Sarvam provider configuration and cost map entries. +""" + +import pytest + +import litellm +from litellm.llms.openai_like.json_loader import JSONProviderRegistry + +SARVAM_MODELS = ( + "sarvam/sarvam-105b", + "sarvam/sarvam-105b-conversations", + "sarvam/glm5.3", + "sarvam/glm5.3-flash", + "sarvam/gemma4", + "sarvam/deepseekv4.1-flash-1017", +) + +BETA_MODELS = ( + "sarvam/deepseekv4-pro", + "sarvam/kimi-k3", + "sarvam/mimo-v2.6-flash", + "sarvam/mimo-v2.6-pro", +) + + +class TestSarvamProviderConfig: + def test_sarvam_json_config_exists(self): + assert JSONProviderRegistry.exists("sarvam") + + sarvam = JSONProviderRegistry.get("sarvam") + assert sarvam is not None + assert sarvam.base_url == "https://api.sarvam.ai/v2" + assert sarvam.api_key_env == "SARVAM_API_KEY" + assert sarvam.base_class == "openai_gpt" + assert "/v1/chat/completions" in sarvam.supported_endpoints + assert "/v1/responses" in sarvam.supported_endpoints + + def test_sarvam_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="sarvam/sarvam-105b", + custom_llm_provider=None, + api_base=None, + api_key=None, + ) + + assert model == "sarvam-105b" + assert provider == "sarvam" + assert api_base == "https://api.sarvam.ai/v2" + + def test_sarvam_responses_api_enabled(self): + from litellm.utils import ProviderConfigManager + + assert JSONProviderRegistry.supports_responses_api("sarvam") is True + config = ProviderConfigManager.get_provider_responses_api_config( + provider="sarvam", + model="sarvam/sarvam-105b", + ) + assert config is not None + assert config.custom_llm_provider == "sarvam" + + +class TestSarvamCostMap: + @pytest.fixture(autouse=True) + def _use_local_model_cost_map(self, monkeypatch): + original_model_cost = litellm.model_cost + monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") + litellm.model_cost = litellm.get_model_cost_map(url="") + litellm.get_model_info.cache_clear() + try: + yield + finally: + litellm.model_cost = original_model_cost + litellm.get_model_info.cache_clear() + + @pytest.mark.parametrize("model", SARVAM_MODELS) + def test_model_registered(self, model): + assert model in litellm.model_cost + + entry = litellm.model_cost[model] + assert entry["litellm_provider"] == "sarvam" + assert entry["mode"] == "chat" + + @pytest.mark.parametrize("model", SARVAM_MODELS) + def test_output_limit_matches_context_window(self, model): + entry = litellm.model_cost[model] + + assert entry["max_output_tokens"] == entry["max_input_tokens"] + assert entry["max_tokens"] == entry["max_output_tokens"] + + def test_beta_models_absent(self): + for model in BETA_MODELS: + assert model not in litellm.model_cost + + def test_stale_sarvam_m_absent(self): + assert "sarvam/sarvam-m" not in litellm.model_cost From 379d98b270b6a975536f3f3fe4d1470b014e30c1 Mon Sep 17 00:00:00 2001 From: Vikash Maddi Date: Mon, 2 Nov 2026 19:22:51 +0530 Subject: [PATCH 2/4] test(providers): drop beta model names from Sarvam tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🪷 Generated with Sarvam Code --- tests/unit/llms/openai_like/test_sarvam_provider.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/unit/llms/openai_like/test_sarvam_provider.py b/tests/unit/llms/openai_like/test_sarvam_provider.py index 442ffb9c083..8e91240a241 100644 --- a/tests/unit/llms/openai_like/test_sarvam_provider.py +++ b/tests/unit/llms/openai_like/test_sarvam_provider.py @@ -16,13 +16,6 @@ SARVAM_MODELS = ( "sarvam/deepseekv4.1-flash-1017", ) -BETA_MODELS = ( - "sarvam/deepseekv4-pro", - "sarvam/kimi-k3", - "sarvam/mimo-v2.6-flash", - "sarvam/mimo-v2.6-pro", -) - class TestSarvamProviderConfig: def test_sarvam_json_config_exists(self): @@ -90,9 +83,5 @@ class TestSarvamCostMap: assert entry["max_output_tokens"] == entry["max_input_tokens"] assert entry["max_tokens"] == entry["max_output_tokens"] - def test_beta_models_absent(self): - for model in BETA_MODELS: - assert model not in litellm.model_cost - def test_stale_sarvam_m_absent(self): assert "sarvam/sarvam-m" not in litellm.model_cost From 6e2a9ce4a7a22f8fa374d999d6816ee1f5729987 Mon Sep 17 00:00:00 2001 From: Vikash Maddi Date: Wed, 4 Nov 2026 14:23:32 +0530 Subject: [PATCH 3/4] feat(providers): list Sarvam in the Add Model provider fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🪷 Generated with Sarvam Code --- .../provider_create_fields.json | 28 +++++++++++++++++++ .../llms/openai_like/test_sarvam_provider.py | 17 +++++++++++ 2 files changed, 45 insertions(+) diff --git a/litellm/proxy/public_endpoints/provider_create_fields.json b/litellm/proxy/public_endpoints/provider_create_fields.json index 044b8a1de6e..de9e96c0fc9 100644 --- a/litellm/proxy/public_endpoints/provider_create_fields.json +++ b/litellm/proxy/public_endpoints/provider_create_fields.json @@ -3603,6 +3603,34 @@ ], "default_model_placeholder": "sap/gpt-4" }, + { + "provider": "SARVAM", + "provider_display_name": "Sarvam", + "litellm_provider": "sarvam", + "credential_fields": [ + { + "key": "api_base", + "label": "API Base", + "placeholder": "https://api.sarvam.ai/v2", + "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": "sarvam/sarvam-105b" + }, { "provider": "SCX_AI", "provider_display_name": "SCX.ai", diff --git a/tests/unit/llms/openai_like/test_sarvam_provider.py b/tests/unit/llms/openai_like/test_sarvam_provider.py index 8e91240a241..da5acdeaef4 100644 --- a/tests/unit/llms/openai_like/test_sarvam_provider.py +++ b/tests/unit/llms/openai_like/test_sarvam_provider.py @@ -54,6 +54,23 @@ class TestSarvamProviderConfig: assert config is not None assert config.custom_llm_provider == "sarvam" + def test_sarvam_listed_in_add_model_provider_fields(self): + import json + from pathlib import Path + + fields_path = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json" + entries = json.loads(fields_path.read_text()) + sarvam_entries = [entry for entry in entries if entry["litellm_provider"] == "sarvam"] + + assert len(sarvam_entries) == 1 + entry = sarvam_entries[0] + assert entry["provider"] == "SARVAM" + assert entry["provider_display_name"] == "Sarvam" + assert entry["default_model_placeholder"] == "sarvam/sarvam-105b" + field_keys = {field["key"] for field in entry["credential_fields"]} + assert "api_key" in field_keys + assert "api_base" in field_keys + class TestSarvamCostMap: @pytest.fixture(autouse=True) From 0ac8516056528605969b8e543bd3a7b16af26bb0 Mon Sep 17 00:00:00 2001 From: Vikash Maddi Date: Wed, 4 Nov 2026 22:39:24 +0530 Subject: [PATCH 4/4] fix(providers): address Sarvam review comments on pricing and tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add cached input pricing and source URLs to the six GA model rows - mark glm5.3 and glm5.3-flash as prompt-cache capable - restore the sarvam-m deprecation row with zero cache costs - keep the pin-zero-one-hour-price integration case on sarvam/sarvam-m - rewrite the unit tests with respx and the shared cost-map fixture 🪷 Generated with Sarvam Code --- ...odel_prices_and_context_window_backup.json | 30 ++++- model_prices_and_context_window.json | 30 ++++- .../routing/test_prompt_cache_pin_ttl.py | 2 +- .../llms/openai_like/test_sarvam_provider.py | 110 +++++++++++++----- 4 files changed, 137 insertions(+), 35 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 479717337bc..d22d1c84ac4 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -58635,7 +58635,22 @@ "mode": "embedding", "source": "https://docs.libertai.io/apis/text/" }, + "sarvam/sarvam-m": { + "cache_creation_input_token_cost": 0, + "cache_creation_input_token_cost_above_1hr": 0, + "cache_read_input_token_cost": 0, + "deprecation_date": "2026-06-05", + "input_cost_per_token": 0, + "litellm_provider": "sarvam", + "max_input_tokens": 8192, + "max_output_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 0, + "source": "https://docs.sarvam.ai/changelog/2026/6/5.md", + "supports_reasoning": true + }, "sarvam/sarvam-105b": { + "cache_read_input_token_cost": 6e-08, "input_cost_per_token": 1.7e-07, "litellm_provider": "sarvam", "max_input_tokens": 128000, @@ -58643,6 +58658,7 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.7e-07, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -58655,6 +58671,7 @@ "supports_vision": false }, "sarvam/sarvam-105b-conversations": { + "cache_read_input_token_cost": 6e-08, "input_cost_per_token": 1.7e-07, "litellm_provider": "sarvam", "max_input_tokens": 32000, @@ -58662,6 +58679,7 @@ "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 6.7e-07, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -58674,6 +58692,7 @@ "supports_vision": false }, "sarvam/glm5.3": { + "cache_read_input_token_cost": 2.2e-07, "input_cost_per_token": 1.4e-06, "litellm_provider": "sarvam", "max_input_tokens": 1048576, @@ -58681,10 +58700,11 @@ "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.33e-06, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, - "supports_prompt_caching": false, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_system_messages": true, @@ -58693,6 +58713,7 @@ "supports_vision": false }, "sarvam/glm5.3-flash": { + "cache_read_input_token_cost": 3e-08, "input_cost_per_token": 1.5e-07, "litellm_provider": "sarvam", "max_input_tokens": 1048576, @@ -58700,10 +58721,11 @@ "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.0e-07, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, - "supports_prompt_caching": false, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_system_messages": true, @@ -58712,6 +58734,7 @@ "supports_vision": true }, "sarvam/gemma4": { + "cache_read_input_token_cost": 1.1e-07, "input_cost_per_token": 3.9e-07, "litellm_provider": "sarvam", "max_input_tokens": 262144, @@ -58719,6 +58742,7 @@ "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.0e-06, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -58731,6 +58755,7 @@ "supports_vision": true }, "sarvam/deepseekv4.1-flash-1017": { + "cache_read_input_token_cost": 1e-08, "input_cost_per_token": 2.8e-07, "litellm_provider": "sarvam", "max_input_tokens": 1048576, @@ -58738,6 +58763,7 @@ "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.17e-06, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 479717337bc..d22d1c84ac4 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -58635,7 +58635,22 @@ "mode": "embedding", "source": "https://docs.libertai.io/apis/text/" }, + "sarvam/sarvam-m": { + "cache_creation_input_token_cost": 0, + "cache_creation_input_token_cost_above_1hr": 0, + "cache_read_input_token_cost": 0, + "deprecation_date": "2026-06-05", + "input_cost_per_token": 0, + "litellm_provider": "sarvam", + "max_input_tokens": 8192, + "max_output_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 0, + "source": "https://docs.sarvam.ai/changelog/2026/6/5.md", + "supports_reasoning": true + }, "sarvam/sarvam-105b": { + "cache_read_input_token_cost": 6e-08, "input_cost_per_token": 1.7e-07, "litellm_provider": "sarvam", "max_input_tokens": 128000, @@ -58643,6 +58658,7 @@ "max_tokens": 128000, "mode": "chat", "output_cost_per_token": 6.7e-07, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -58655,6 +58671,7 @@ "supports_vision": false }, "sarvam/sarvam-105b-conversations": { + "cache_read_input_token_cost": 6e-08, "input_cost_per_token": 1.7e-07, "litellm_provider": "sarvam", "max_input_tokens": 32000, @@ -58662,6 +58679,7 @@ "max_tokens": 32000, "mode": "chat", "output_cost_per_token": 6.7e-07, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -58674,6 +58692,7 @@ "supports_vision": false }, "sarvam/glm5.3": { + "cache_read_input_token_cost": 2.2e-07, "input_cost_per_token": 1.4e-06, "litellm_provider": "sarvam", "max_input_tokens": 1048576, @@ -58681,10 +58700,11 @@ "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 4.33e-06, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, - "supports_prompt_caching": false, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_system_messages": true, @@ -58693,6 +58713,7 @@ "supports_vision": false }, "sarvam/glm5.3-flash": { + "cache_read_input_token_cost": 3e-08, "input_cost_per_token": 1.5e-07, "litellm_provider": "sarvam", "max_input_tokens": 1048576, @@ -58700,10 +58721,11 @@ "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 5.0e-07, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, - "supports_prompt_caching": false, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_system_messages": true, @@ -58712,6 +58734,7 @@ "supports_vision": true }, "sarvam/gemma4": { + "cache_read_input_token_cost": 1.1e-07, "input_cost_per_token": 3.9e-07, "litellm_provider": "sarvam", "max_input_tokens": 262144, @@ -58719,6 +58742,7 @@ "max_tokens": 262144, "mode": "chat", "output_cost_per_token": 1.0e-06, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, @@ -58731,6 +58755,7 @@ "supports_vision": true }, "sarvam/deepseekv4.1-flash-1017": { + "cache_read_input_token_cost": 1e-08, "input_cost_per_token": 2.8e-07, "litellm_provider": "sarvam", "max_input_tokens": 1048576, @@ -58738,6 +58763,7 @@ "max_tokens": 1048576, "mode": "chat", "output_cost_per_token": 1.17e-06, + "source": "https://www.sarvam.ai/token-factory/pricing", "supports_audio_input": false, "supports_function_calling": true, "supports_parallel_function_calling": true, diff --git a/tests/integration/routing/test_prompt_cache_pin_ttl.py b/tests/integration/routing/test_prompt_cache_pin_ttl.py index 268a868c76e..f6ee96a9206 100644 --- a/tests/integration/routing/test_prompt_cache_pin_ttl.py +++ b/tests/integration/routing/test_prompt_cache_pin_ttl.py @@ -174,7 +174,7 @@ def _model_list(rig_wires: Mapping[str, str]) -> list[JsonValue]: deployment( "pin-zero-one-hour-price", "pin-zero-one-hour-price", - model="sarvam/sarvam-105b", + model="sarvam/sarvam-m", api_base=f"{rig_wires['openai']}/v1", api_key=PROVIDER_KEY, ), diff --git a/tests/unit/llms/openai_like/test_sarvam_provider.py b/tests/unit/llms/openai_like/test_sarvam_provider.py index da5acdeaef4..cb2fe562c6a 100644 --- a/tests/unit/llms/openai_like/test_sarvam_provider.py +++ b/tests/unit/llms/openai_like/test_sarvam_provider.py @@ -2,7 +2,10 @@ Tests for the Sarvam provider configuration and cost map entries. """ +import json + import pytest +import respx import litellm from litellm.llms.openai_like.json_loader import JSONProviderRegistry @@ -18,17 +21,6 @@ SARVAM_MODELS = ( class TestSarvamProviderConfig: - def test_sarvam_json_config_exists(self): - assert JSONProviderRegistry.exists("sarvam") - - sarvam = JSONProviderRegistry.get("sarvam") - assert sarvam is not None - assert sarvam.base_url == "https://api.sarvam.ai/v2" - assert sarvam.api_key_env == "SARVAM_API_KEY" - assert sarvam.base_class == "openai_gpt" - assert "/v1/chat/completions" in sarvam.supported_endpoints - assert "/v1/responses" in sarvam.supported_endpoints - def test_sarvam_provider_resolution(self): from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider @@ -55,7 +47,6 @@ class TestSarvamProviderConfig: assert config.custom_llm_provider == "sarvam" def test_sarvam_listed_in_add_model_provider_fields(self): - import json from pathlib import Path fields_path = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json" @@ -71,20 +62,78 @@ class TestSarvamProviderConfig: assert "api_key" in field_keys assert "api_base" in field_keys + def test_sarvam_chat_completion_request(self): + with respx.mock() as upstream: + route = upstream.post("https://api.sarvam.ai/v2/chat/completions").respond( + 200, + json={ + "id": "chatcmpl_sarvam", + "object": "chat.completion", + "created": 1, + "model": "sarvam-105b", + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "Hello"}, + "finish_reason": "stop", + } + ], + "usage": {"prompt_tokens": 4, "completion_tokens": 3, "total_tokens": 7}, + }, + ) + response = litellm.completion( + model="sarvam/sarvam-105b", + messages=[{"role": "user", "content": "Say hello"}], + api_key="sarvam-test-key", + max_completion_tokens=200, + ) + request = route.calls.last.request + body = json.loads(request.content) + assert route.call_count == 1 + assert str(request.url) == "https://api.sarvam.ai/v2/chat/completions" + assert request.headers["authorization"] == "Bearer sarvam-test-key" + # LiteLLM maps max_completion_tokens to max_tokens for OpenAI-compatible providers. + assert body.get("max_completion_tokens", body.get("max_tokens")) == 200 + assert response.choices[0].message.content == "Hello" + + def test_sarvam_responses_request(self): + with respx.mock() as upstream: + route = upstream.post("https://api.sarvam.ai/v2/responses").respond( + 200, + json={ + "id": "resp_sarvam", + "object": "response", + "created_at": 1, + "model": "sarvam-105b", + "status": "completed", + "output": [ + { + "id": "msg_sarvam", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "Hello", "annotations": []}], + } + ], + "usage": {"input_tokens": 4, "output_tokens": 3, "total_tokens": 7}, + }, + ) + response = litellm.responses( + model="sarvam/sarvam-105b", + input="Say hello", + api_key="sarvam-test-key", + ) + + request = route.calls.last.request + assert route.call_count == 1 + assert str(request.url) == "https://api.sarvam.ai/v2/responses" + assert request.headers["authorization"] == "Bearer sarvam-test-key" + assert response.output_text == "Hello" + + +@pytest.mark.usefixtures("local_model_cost_map") class TestSarvamCostMap: - @pytest.fixture(autouse=True) - def _use_local_model_cost_map(self, monkeypatch): - original_model_cost = litellm.model_cost - monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True") - litellm.model_cost = litellm.get_model_cost_map(url="") - litellm.get_model_info.cache_clear() - try: - yield - finally: - litellm.model_cost = original_model_cost - litellm.get_model_info.cache_clear() - @pytest.mark.parametrize("model", SARVAM_MODELS) def test_model_registered(self, model): assert model in litellm.model_cost @@ -94,11 +143,12 @@ class TestSarvamCostMap: assert entry["mode"] == "chat" @pytest.mark.parametrize("model", SARVAM_MODELS) - def test_output_limit_matches_context_window(self, model): + def test_cached_input_cost_is_set(self, model): entry = litellm.model_cost[model] + assert entry["cache_read_input_token_cost"] > 0 - assert entry["max_output_tokens"] == entry["max_input_tokens"] - assert entry["max_tokens"] == entry["max_output_tokens"] - - def test_stale_sarvam_m_absent(self): - assert "sarvam/sarvam-m" not in litellm.model_cost + def test_sarvam_m_kept_for_deprecation(self): + # Source: https://docs.sarvam.ai/changelog/2026/6/5.md (2026-06-05) + entry = litellm.model_cost["sarvam/sarvam-m"] + assert entry["deprecation_date"] == "2026-06-05" + assert entry["cache_creation_input_token_cost_above_1hr"] == 0