diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index c9257677fd1..49b3801c82f 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -115,6 +115,15 @@ "max_completion_tokens": "max_tokens" } }, + "neosantara": { + "base_url": "https://api.neosantara.xyz/v1", + "api_key_env": "NEOSANTARA_API_KEY", + "api_base_env": "NEOSANTARA_API_BASE", + "param_mappings": { + "max_completion_tokens": "max_tokens" + }, + "supported_endpoints": ["/v1/chat/completions", "/v1/responses"] + }, "tensormesh": { "base_url": "https://serverless.tensormesh.ai/v1", "api_key_env": "TENSORMESH_INFERENCE_API_KEY", diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json index 0562b41d2cd..e0eeb014c51 100644 --- a/litellm/provider_endpoints_support_backup.json +++ b/litellm/provider_endpoints_support_backup.json @@ -1539,6 +1539,23 @@ "interactions": true } }, + "neosantara": { + "display_name": "Neosantara (`neosantara`)", + "url": "https://docs.litellm.ai/docs/providers/neosantara", + "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 + } + }, "nvidia_nim": { "display_name": "Nvidia NIM (`nvidia_nim`)", "url": "https://docs.litellm.ai/docs/providers/nvidia_nim", diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 93eef2d1d01..a7a0b0f6238 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -3375,6 +3375,7 @@ class LlmProviders(str, Enum): NANOGPT = "nano-gpt" POE = "poe" CHUTES = "chutes" + NEOSANTARA = "neosantara" XIAOMI_MIMO = "xiaomi_mimo" TENSORMESH = "tensormesh" LITELLM_AGENT = "litellm_agent" diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index 70e980ab2a0..a1ad20fffd1 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -1556,6 +1556,23 @@ "interactions": true } }, + "neosantara": { + "display_name": "Neosantara (`neosantara`)", + "url": "https://docs.litellm.ai/docs/providers/neosantara", + "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 + } + }, "nlp_cloud": { "display_name": "NLP Cloud (`nlp_cloud`)", "url": "https://docs.litellm.ai/docs/providers/nlp_cloud", diff --git a/tests/test_litellm/llms/neosantara/test_neosantara.py b/tests/test_litellm/llms/neosantara/test_neosantara.py new file mode 100644 index 00000000000..bef8c60d171 --- /dev/null +++ b/tests/test_litellm/llms/neosantara/test_neosantara.py @@ -0,0 +1,100 @@ +import os +from unittest.mock import patch + +NEOSANTARA_API_BASE = "https://api.neosantara.xyz/v1" + + +def test_neosantara_json_registry(): + import litellm + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + assert litellm.LlmProviders.NEOSANTARA.value == "neosantara" + assert litellm.LlmProviders("neosantara") == litellm.LlmProviders.NEOSANTARA + assert JSONProviderRegistry.exists("neosantara") + config = JSONProviderRegistry.get("neosantara") + assert config is not None + assert config.base_url == NEOSANTARA_API_BASE + assert config.api_key_env == "NEOSANTARA_API_KEY" + assert config.api_base_env == "NEOSANTARA_API_BASE" + assert config.param_mappings["max_completion_tokens"] == "max_tokens" + assert "/v1/chat/completions" in config.supported_endpoints + assert "/v1/responses" in config.supported_endpoints + + +def test_neosantara_dynamic_config_env_vars(): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + config = create_config_class(JSONProviderRegistry.get("neosantara"))() + + with patch.dict( + os.environ, + { + "NEOSANTARA_API_KEY": "test-key", + "NEOSANTARA_API_BASE": "https://custom.neosantara.example/v1", + }, + ): + api_base, api_key = config._get_openai_compatible_provider_info(None, None) + + assert api_base == "https://custom.neosantara.example/v1" + assert api_key == "test-key" + + +def test_neosantara_provider_detection_by_prefix(): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + model, provider, _, api_base = get_llm_provider("neosantara/gemini-3-flash") + + assert model == "gemini-3-flash" + assert provider == "neosantara" + assert api_base == NEOSANTARA_API_BASE + + +def test_neosantara_chat_complete_url(): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + config = create_config_class(JSONProviderRegistry.get("neosantara"))() + + assert ( + config.get_complete_url( + api_base=None, + api_key=None, + model="gemini-3-flash", + optional_params={}, + litellm_params={}, + ) + == "https://api.neosantara.xyz/v1/chat/completions" + ) + + +def test_neosantara_maps_max_completion_tokens_to_max_tokens(): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + config = create_config_class(JSONProviderRegistry.get("neosantara"))() + optional_params = config.map_openai_params( + non_default_params={"max_completion_tokens": 7}, + optional_params={}, + model="gemini-3-flash", + drop_params=False, + ) + + assert optional_params == {"max_tokens": 7} + + +def test_neosantara_responses_api_config(): + from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig + from litellm.utils import ProviderConfigManager + + config = ProviderConfigManager.get_provider_responses_api_config( + provider="neosantara", + model="claude-opus-4-6", + ) + + assert isinstance(config, OpenAIResponsesAPIConfig) + assert config.custom_llm_provider == "neosantara" + assert ( + config.get_complete_url(api_base=None, litellm_params={}) + == "https://api.neosantara.xyz/v1/responses" + )