Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
13dd954
Mark xAI models retiring on 2026-05-15 (#28788)
yudelevi Jun 5, 2026
21596c8
feat(moonshot): advertise json_schema response support on live models…
yudelevi Jun 5, 2026
5660281
chore(moonshot): mark models retired from api.moonshot.ai as deprecat…
yudelevi Jun 5, 2026
17734eb
fix(moonshot): drop temperature for reasoning models (kimi-k2.5/k2.6)…
yudelevi Jun 5, 2026
82a4f15
feat(mcp): add per-server timeout configuration (#29672)
hectorc98 Jun 5, 2026
5c79b08
Add jp. Bedrock cross-region inference profile for claude-opus-4-7 (#…
Cyberfilo Jun 5, 2026
38f2660
feat(soniox): add soniox audio transcription integration (#29508)
dan2k3k4 Jun 5, 2026
dacce6e
feat(openmeter): add OPENMETER_TRUST_REQUEST_USER to prevent forged a…
saswatds Jun 5, 2026
eab34a3
feat(search): add you_com as a search provider (#28370)
brainsparker Jun 5, 2026
4a6d586
docs: fix README typo (#29419)
Zhao73 Jun 5, 2026
94e4dd3
Fix(langfuse): pass httpx_client to Langfuse in langfuse_prompt_manag…
urainshah Jun 5, 2026
4b5b107
feat(models): add minimax/MiniMax-M3 to model cost map (#29412)
kapelame Jun 5, 2026
5a09db9
fix(logging): handle ResponseCompletedEvent in anthropic_messages str…
danisalvaa Jun 5, 2026
a419c7b
feat(provider): Add Neosantara provider as OpenAI Compatible (#29646)
ErRickow Jun 5, 2026
bc983b0
fix: address greptile and veria review feedback
Sameerlite Jun 5, 2026
63a6ac4
chore(mcp): add migration for per-server timeout column
Sameerlite Jun 5, 2026
8e45e8a
fix(test): add tool_use_system_prompt_tokens to model prices schema v…
Sameerlite Jun 5, 2026
de1307b
fix: mcp timeout test uses real asyncio.wait_for timeout; you_com get…
Sameerlite Jun 5, 2026
aac59f7
fix: forward resolved api_key into you_com endpoint selection and app…
mateo-berri Jun 5, 2026
a0f25d5
fix(soniox): price stt-async-v4 per second so transcriptions are billed
mateo-berri Jun 5, 2026
828e1b0
fix(langfuse): use a dedicated httpx client for the SDK injection
mateo-berri Jun 5, 2026
01528ff
fix(soniox): prefer caller-supplied api_base over SONIOX_API_BASE env…
mateo-berri Jun 5, 2026
fa18cba
fix(cohere): support max_completion_tokens on cohere v2 chat (default…
abhay23-AI Jun 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ Support for more providers. Missing a provider or LLM Platform, raise a [feature
### Run in Developer Mode
#### Services
1. Setup .env file in root
2. Run dependant services `docker-compose up db prometheus`
2. Run dependent services `docker-compose up db prometheus`

#### Backend
1. (In root) create virtual environment `python -m venv .venv`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "LiteLLM_MCPServerTable" ADD COLUMN "timeout" DOUBLE PRECISION;

1 change: 1 addition & 0 deletions litellm-proxy-extras/litellm_proxy_extras/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ model LiteLLM_MCPServerTable {
byok_description String[] @default([])
byok_api_key_help_url String?
source_url String?
timeout Float?
// BYOM submission lifecycle
approval_status String? @default("active")
submitted_by String?
Expand Down
5 changes: 5 additions & 0 deletions litellm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ def identify(event_details):
galadriel_models: Set = set()
nvidia_nim_models: Set = set()
nvidia_riva_models: Set = set()
soniox_models: Set = set()
sambanova_models: Set = set()
sambanova_embedding_models: Set = set()
novita_models: Set = set()
Expand Down Expand Up @@ -844,6 +845,8 @@ def add_known_models(model_cost_map: Optional[Dict] = None):
nvidia_nim_models.add(key)
elif value.get("litellm_provider") == "nvidia_riva":
nvidia_riva_models.add(key)
elif value.get("litellm_provider") == "soniox":
soniox_models.add(key)
elif value.get("litellm_provider") == "sambanova":
sambanova_models.add(key)
elif value.get("litellm_provider") == "sambanova-embedding-models":
Expand Down Expand Up @@ -1009,6 +1012,7 @@ def add_known_models(model_cost_map: Optional[Dict] = None):
| galadriel_models
| nvidia_nim_models
| nvidia_riva_models
| soniox_models
| sambanova_models
| azure_text_models
| novita_models
Expand Down Expand Up @@ -1109,6 +1113,7 @@ def add_known_models(model_cost_map: Optional[Dict] = None):
"galadriel": galadriel_models,
"nvidia_nim": nvidia_nim_models,
"nvidia_riva": nvidia_riva_models,
"soniox": soniox_models,
"sambanova": sambanova_models | sambanova_embedding_models,
"novita": novita_models,
"nebius": nebius_models | nebius_embedding_models,
Expand Down
5 changes: 5 additions & 0 deletions litellm/_lazy_imports_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@
"LemonadeChatConfig",
"SnowflakeEmbeddingConfig",
"AmazonNovaChatConfig",
"SonioxAudioTranscriptionConfig",
)

# Types that support lazy loading via _lazy_import_types
Expand Down Expand Up @@ -1195,6 +1196,10 @@
".llms.amazon_nova.chat.transformation",
"AmazonNovaChatConfig",
),
"SonioxAudioTranscriptionConfig": (
".llms.soniox.audio_transcription.transformation",
"SonioxAudioTranscriptionConfig",
),
}

# Import map for utils module lazy imports
Expand Down
4 changes: 3 additions & 1 deletion litellm/experimental_mcp_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,9 @@ async def _call_tool_operation(session: ClientSession):
)
return tool_result
except asyncio.CancelledError:
verbose_logger.warning("MCP client tool call was cancelled")
verbose_logger.warning(
f"MCP client tool call timed out after {self.timeout}s for {self.server_url}"
)
raise
except Exception as e:
import traceback
Expand Down
6 changes: 6 additions & 0 deletions litellm/integrations/langfuse/langfuse_prompt_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ def langfuse_client_init(
if Version(langfuse.version.__version__) >= Version("2.6.0"):
parameters["sdk_integration"] = "litellm"

from ...llms.custom_httpx.http_handler import _get_httpx_client

if Version(langfuse.version.__version__) >= Version("2.7.3"):
http_client = _get_httpx_client()
parameters["httpx_client"] = http_client.client
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

client = Langfuse(**parameters)

return client
Expand Down
10 changes: 9 additions & 1 deletion litellm/integrations/openmeter.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,15 @@ def _common_logic(self, kwargs: dict, response_obj):
"total_tokens": response_obj["usage"].get("total_tokens"),
}

user_param = kwargs.get("user", None) # end-user passed in via 'user' param
# OPENMETER_TRUST_REQUEST_USER (default "true"): when set to "false",
# the request-supplied `user` field is ignored and the subject is
# resolved solely from the key-bound user_api_key_user_id. Proxies
# serving multi-tenant traffic enable this to prevent clients from
# forging attribution by setting `user` in the request body.
trust_request_user = (
os.getenv("OPENMETER_TRUST_REQUEST_USER", "true").lower() != "false"
)
user_param = kwargs.get("user", None) if trust_request_user else None

# If no user provided directly, try to get it from token user_id
if user_param is None:
Expand Down
3 changes: 3 additions & 0 deletions litellm/integrations/websearch_interception/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ search_tools:
- search_tool_name: "my-tavily-tool"
litellm_params:
search_provider: "tavily"
- search_tool_name: "my-you-com-tool"
litellm_params:
Comment thread
Sameerlite marked this conversation as resolved.
search_provider: "you_com"
```

---
Expand Down
5 changes: 5 additions & 0 deletions litellm/litellm_core_utils/get_llm_provider_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,11 @@ def _get_openai_compatible_provider_info( # noqa: PLR0915
or get_secret_str("NVIDIA_RIVA_API_KEY")
or get_secret_str("NVIDIA_NIM_API_KEY")
)
elif custom_llm_provider == "soniox":
api_base = (
api_base or get_secret_str("SONIOX_API_BASE") or "https://api.soniox.com"
)
dynamic_api_key = api_key or get_secret_str("SONIOX_API_KEY")
elif custom_llm_provider == "cerebras":
api_base = (
api_base or get_secret("CEREBRAS_API_BASE") or "https://api.cerebras.ai/v1"
Expand Down
5 changes: 5 additions & 0 deletions litellm/litellm_core_utils/get_supported_openai_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,11 @@ def get_supported_openai_params( # noqa: PLR0915
return ElevenLabsAudioTranscriptionConfig().get_supported_openai_params(
model=model
)
elif custom_llm_provider == "soniox":
if request_type == "transcription":
return litellm.SonioxAudioTranscriptionConfig().get_supported_openai_params(
model=model
)
elif custom_llm_provider in litellm._custom_providers:
if request_type == "chat_completion":
provider_config = litellm.ProviderConfigManager.get_provider_chat_config(
Expand Down
13 changes: 12 additions & 1 deletion litellm/litellm_core_utils/litellm_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -3503,7 +3503,9 @@ def _get_assembled_streaming_response(
else:
return None

def _handle_anthropic_messages_response_logging(self, result: Any) -> ModelResponse:
def _handle_anthropic_messages_response_logging(
self, result: Any
) -> Union[ModelResponse, ResponsesAPIResponse]:
"""
Handles logging for Anthropic messages responses.

Expand All @@ -3522,6 +3524,15 @@ def _handle_anthropic_messages_response_logging(self, result: Any) -> ModelRespo
return result
elif isinstance(result, ModelResponse):
return result
elif isinstance(
result,
(ResponseCompletedEvent, ResponseIncompleteEvent, ResponseFailedEvent),
):
# anthropic_messages() can route to OpenAI Responses API; in that path
# the assembled streaming result is one of these terminal events rather than
# a ModelResponse. Return the inner response so downstream handlers
# (_transform_usage_objects, normalize_logging_result) can process it.
return result.response

httpx_response = self.model_call_details.get("httpx_response", None)
if httpx_response and isinstance(httpx_response, httpx.Response):
Expand Down
10 changes: 7 additions & 3 deletions litellm/llms/moonshot/chat/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,15 @@ def map_openai_params(

##########################################
# temperature limitations
# 1. `temperature` on KIMI API is [0, 1] but OpenAI is [0, 2]
# 2. If temperature < 0.3 and n > 1, KIMI will raise an exception.
# 1. reasoning models (kimi-k2.5, kimi-k2.6, ...) reject every temperature
# except 1, so the param is dropped and the model's default is used
# 2. `temperature` on KIMI API is [0, 1] but OpenAI is [0, 2]
# 3. If temperature < 0.3 and n > 1, KIMI will raise an exception.
# If we enter this condition, we set the temperature to 0.3 as suggested by Moonshot AI
##########################################
if "temperature" in optional_params:
if supports_reasoning(model=model, custom_llm_provider="moonshot"):
optional_params.pop("temperature", None)
elif "temperature" in optional_params:
if optional_params["temperature"] > 1:
optional_params["temperature"] = 1
if optional_params["temperature"] < 0.3 and optional_params.get("n", 1) > 1:
Expand Down
9 changes: 9 additions & 0 deletions litellm/llms/openai_like/providers.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@
"max_completion_tokens": "max_tokens"
}
},
"neosantara": {
"base_url": "https://api.neosantara.xyz/v1",
"api_key_env": "NEOSANTARA_API_KEY",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High: Provider API key exfiltration

JSON-backed providers resolve the API key from this env var but preserve a caller-supplied api_base before falling back to the configured base URL. A user who can call neosantara/... can set api_base to their own server and receive Authorization: Bearer <NEOSANTARA_API_KEY> on chat or responses requests; pin the base URL whenever the proxy is using the environment key, or only allow custom api_base when the caller also supplies their own api_key.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This neosantara block is pure data added to providers.json; it carries no credential-handling code of its own. Key and base resolution is the shared JSONProviderConfig (_get_openai_compatible_provider_info) for chat and JSONProviderResponsesConfig for /v1/responses, the same dynamic config classes that already back every openai_like JSON provider in this file (tensormesh and the rest). Both do resolved_base = api_base or get_secret_str(api_base_env) or base_url and resolved_key = api_key or get_secret_str(api_key_env), which is the identical api_key or env + api_base or env or default precedence that OpenAI, Azure, and Perplexity already use in litellm/main.py. Adding a JSON row gives neosantara exactly the behavior of the providers already shipped; it introduces no new code path to exploit

Honoring a request-supplied api_base is intentional litellm behavior, not a neosantara-specific gap. The proxy treats clientside api_base/api_key as the highest-precedence override and routes it through the dynamic-deployment path in get_dynamic_litellm_params, which is where an admin's config-stored secrets (organization, extra_headers, region, provider tokens, etc.) are scrubbed when the caller redirects the base. Constraining env-resolved keys belongs in that core layer and applies uniformly to every provider; special-casing it in this one JSON entry, or in the shared config class, would diverge from every other openai_like provider without closing the generic behavior

Pinning the base whenever the env key is used would break legitimate operator usage. config.yaml litellm_params.api_base and SDK calls like litellm.completion(model="neosantara/...", api_base=...) reach _get_openai_compatible_provider_info as the same api_base argument and are indistinguishable from a request override at the provider layer, so pinning would break admin-configured custom endpoints and self-hosted neosantara deployments

BYOK stays safe: a caller who passes their own api_key alongside api_base sends their own key rather than the server's NEOSANTARA_API_KEY, because resolved_key = api_key or env prefers the caller-supplied 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",
Expand Down
1 change: 1 addition & 0 deletions litellm/llms/soniox/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Soniox LLM provider implementation."""
1 change: 1 addition & 0 deletions litellm/llms/soniox/audio_transcription/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Soniox audio transcription implementation."""
Loading
Loading