diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index f937e4d2f75..6d3d38d7b35 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 868dcdad74d..7fcec4f22da 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -59737,14 +59737,142 @@ "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, + "max_output_tokens": 128000, + "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, + "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": { + "cache_read_input_token_cost": 6e-08, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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": { + "cache_read_input_token_cost": 2.2e-07, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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/glm5.3-flash": { + "cache_read_input_token_cost": 3e-08, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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/gemma4": { + "cache_read_input_token_cost": 1.1e-07, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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": { + "cache_read_input_token_cost": 1e-08, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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, "litellm_provider": "openai", diff --git a/litellm/proxy/public_endpoints/provider_create_fields.json b/litellm/proxy/public_endpoints/provider_create_fields.json index 5ded2fa5f5e..52da6d5ff87 100644 --- a/litellm/proxy/public_endpoints/provider_create_fields.json +++ b/litellm/proxy/public_endpoints/provider_create_fields.json @@ -3687,6 +3687,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/model_prices_and_context_window.json b/model_prices_and_context_window.json index 868dcdad74d..7fcec4f22da 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -59737,14 +59737,142 @@ "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, + "max_output_tokens": 128000, + "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, + "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": { + "cache_read_input_token_cost": 6e-08, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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": { + "cache_read_input_token_cost": 2.2e-07, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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/glm5.3-flash": { + "cache_read_input_token_cost": 3e-08, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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/gemma4": { + "cache_read_input_token_cost": 1.1e-07, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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": { + "cache_read_input_token_cost": 1e-08, + "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, + "source": "https://www.sarvam.ai/token-factory/pricing", + "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, "litellm_provider": "openai", 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..cb2fe562c6a --- /dev/null +++ b/tests/unit/llms/openai_like/test_sarvam_provider.py @@ -0,0 +1,154 @@ +""" +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 + +SARVAM_MODELS = ( + "sarvam/sarvam-105b", + "sarvam/sarvam-105b-conversations", + "sarvam/glm5.3", + "sarvam/glm5.3-flash", + "sarvam/gemma4", + "sarvam/deepseekv4.1-flash-1017", +) + + +class TestSarvamProviderConfig: + 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" + + def test_sarvam_listed_in_add_model_provider_fields(self): + 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 + + 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.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_cached_input_cost_is_set(self, model): + entry = litellm.model_cost[model] + assert entry["cache_read_input_token_cost"] > 0 + + 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