Commit graph

39351 commits

Author SHA1 Message Date
mateo-berri
ee4f0f3c69
fix(chatgpt/responses): strip whitespace before parsing SSE chunks
_parse_sse_json_chunk in ChatGPTResponsesAPIConfig passed the raw chunk
directly to _strip_sse_data_from_chunk, which only matches the 'data:'
prefix at position 0. Chunks with leading whitespace (e.g. '  data: {...}')
were returned unchanged and silently failed JSON parsing, dropping the
contained event.

Mirror the existing fix in LiteLLMResponsesTransformationHandler._parse_raw_sse_chunk
by calling chunk.strip() before stripping the SSE prefix.

Adds a regression test using whitespace-padded data: lines and verifies
that the response.output_item.done payload is recovered into the final
ResponsesAPIResponse output.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-05-20 14:34:07 +05:30
mateo-berri
97e86f93bf
fix(rubrik): preserve events on batch send failure
Previously, _log_batch_to_rubrik swallowed all HTTP errors and exceptions,
and the parent flush_queue unconditionally drained the queue afterwards.
On Rubrik 5xx responses, network errors, or timeouts the in-flight events
were silently dropped without ever being delivered.

- Re-raise from _log_batch_to_rubrik so failures surface to the caller.
- In CustomBatchLogger.flush_queue, catch exceptions from async_send_batch
  and leave the queue intact for retry on the next flush. Existing loggers
  that override flush_queue (e.g. Datadog) or that swallow their own errors
  inside async_send_batch (e.g. Langsmith, GCS, Argilla) are unaffected.
- Tests now assert events are preserved on HTTP errors, network errors,
  and that mid-flush appended events are also preserved on failure.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-05-20 14:34:07 +05:30
Cursor Agent
756a6ba9fd
fix(responses): merge text-only items with output items in SSE recovery
When recovering output from raw SSE, OUTPUT_ITEM_DONE and OUTPUT_TEXT_DONE
events were treated as mutually exclusive fallbacks. If a stream emitted
OUTPUT_ITEM_DONE for some output indices and only OUTPUT_TEXT_DONE for
others, the text-only items at the missing indices were silently dropped.

Merge both dicts before returning, with OUTPUT_ITEM_DONE entries taking
precedence at any shared index (preserving the existing behavior covered
by test_transform_response_preserves_output_item_when_text_done_arrives_later).

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-05-20 14:34:06 +05:30
Cursor Agent
40465bc0e9
Fix router mode preservation and Rubrik batch flushing 2026-05-20 14:34:04 +05:30
Cursor Agent
e7b67cecfa
Fix Rubrik batch drain and SSE recovery mutation 2026-05-20 14:32:14 +05:30
Cursor Agent
630993856a
Fix xAI usage and Fireworks parallel tool params 2026-05-20 14:32:02 +05:30
Sameer Kankute
0e2a6c6272
Fix mypy 2026-05-20 14:31:39 +05:30
Cursor Agent
ba37a0119c
fix: preserve async batch and reauth coordination 2026-05-20 14:31:39 +05:30
Cursor Agent
15da7fd1bc
fix: guard asyncio.create_task in RubrikLogger.__init__ against missing event loop
asyncio.create_task() raises RuntimeError when called outside a running
event loop. Wrap the call in a try/except RuntimeError so that RubrikLogger
can be instantiated in synchronous contexts (e.g. during startup, testing)
without crashing. The periodic_flush background task simply won't start in
those cases; it starts normally when the constructor is called inside an
event loop.

Add a test that verifies instantiation outside an event loop does not raise
(does not patch asyncio.create_task).

Co-authored-by: Sameer Kankute <Sameerlite@users.noreply.github.com>
2026-05-20 14:31:39 +05:30
Cursor Agent
b8c1f37a4c
fix: evict completed background refresh tasks from _background_refresh_tasks
Completed asyncio.Task objects were never removed from
_background_refresh_tasks. In long-running proxies with many distinct
credential keys the dict grows indefinitely, retaining references to
finished tasks and their results.

Fix:
- Pop the existing (done) entry before creating a replacement task.
- Attach a done_callback to each new task that removes its entry from
  the dict once the task finishes (success or failure).

Tests:
- test_background_refresh_task_removed_after_completion: verifies the
  done-callback cleans up a single entry after the task completes.
- test_background_refresh_tasks_no_accumulation_across_many_keys:
  drives 20 distinct credential keys and confirms the dict is empty
  after all background refreshes finish.

Co-authored-by: Sameer Kankute <Sameerlite@users.noreply.github.com>
2026-05-20 14:31:39 +05:30
Cursor Agent
186e32290e
Fix OCR and Fireworks capability handling 2026-05-20 14:31:39 +05:30
Cursor Agent
005f67bff3
Fix OCR zero credit and Bedrock support checks 2026-05-20 14:31:35 +05:30
Cursor Agent
7fb5eeae92
Fix stale Vertex token refresh fallback 2026-05-20 14:29:38 +05:30
Sameer Kankute
e75e08864b
fix(xai): normalize usage total_tokens for prompt caching
xAI can return total_tokens inconsistent with prompt_tokens +
completion_tokens when caching is enabled. Align with OpenAI-style
usage so shared LLM tests and downstream consumers see coherent totals.
Apply to non-streaming responses and streaming usage chunks.

Made-with: Cursor
2026-05-20 14:28:29 +05:30
Sameer Kankute
a689654fcd
Fix test 2026-05-20 14:26:43 +05:30
Sameer Kankute
c1132382ac
Fix vertex ai cred test 2026-05-20 14:26:43 +05:30
Sameer Kankute
fbfd8bbe55
Fix tests 2026-05-20 14:26:39 +05:30
Sameer Kankute
5223f5cbb3
Fix failing tests 2026-05-20 14:25:46 +05:30
Sameer Kankute
38beaaa877
Fix failing tests 2026-05-20 14:25:46 +05:30
Sameer Kankute
415c7b8f6e
Fix failing tests 2026-05-20 14:25:46 +05:30
Sameer Kankute
0396426f8b
Fix failing tests 2026-05-20 14:25:42 +05:30
Sameer Kankute
80fed17a12
Replaced black formatting 2026-05-20 14:25:10 +05:30
Sameer Kankute
5c67cf0e0c
Replaced the async client violation 2026-05-20 14:24:33 +05:30
Sameer Kankute
1ca48e931e
Fix code qa 2026-05-20 14:24:33 +05:30
Sameer Kankute
a49e2f3ad8
Fix failing tests 2026-05-20 14:24:33 +05:30
Maruti Agarwal
1587a1e91c
feat(ocr): add Reducto parse OCR support (#26068)
* feat(ocr): add Reducto parse OCR support

* fix(reducto): address OCR review feedback

* chore: refresh uv lockfile

* Revert "chore: refresh uv lockfile"

This reverts commit 47200c0e60.
2026-05-20 14:24:33 +05:30
Joseph Barker
a7641dcc43
Add Rubrik as officially-supported guardrail plugin (#25305)
* Add Rubrik as officially-supported guardrail plugin

Adds tool blocking and batch logging integration with an external Rubrik
webhook service. The plugin validates LLM tool calls against a policy
service (fail-open on errors) and batch-logs all requests/responses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Update Rubrik docs: config.yaml as primary, env vars as fallback

Restructures the Quick Start to present config.yaml as the recommended
approach with tabbed UI, and environment variables as an alternative
fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Rubrik env vars to config_settings reference

Fixes documentation validation by adding RUBRIK_API_KEY,
RUBRIK_BATCH_SIZE, RUBRIK_SAMPLING_RATE, and RUBRIK_WEBHOOK_URL
to the environment settings reference table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add fallback message when blocking service returns empty explanation

Prevents whitespace-only violation message when the tool blocking
service blocks tools but returns an empty content field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-20 14:24:21 +05:30
Tai An
a17279a845
fix(deps): relax core runtime dependency pins from exact == to ranges
When litellm migrated from Poetry to uv (PR #24905, v1.83.1), the core
dependency specifications in pyproject.toml changed from Poetry bare-version
strings (e.g. openai = "2.30.0") to PEP 621 exact pins (openai==2.24.0).

Poetry bare-version strings are actually caret ranges (^X.Y.Z == >=X.Y.Z,<X+1),
but PEP 621 == is exact. This means every downstream package that installs
litellm as a library dependency is now forced to downgrade aiohttp, pydantic,
openai, click, and 8 other common packages to exact old versions.

Fix: restore range specifiers for the 12 core runtime dependencies. The
optional extras (proxy, proxy-runtime, etc.) are consumed primarily by
Docker images where exact pins are appropriate and are left unchanged.
The uv.lock file continues to provide exact reproducibility for Docker
builds and CI.

Fixes: #26154
2026-05-20 14:23:52 +05:30
Krrish Dholakia
4ecd8bf81b
fix(chatgpt): preserve responses routing and recover empty output (#25403) (#26219)
- preserve existing shared backend `mode` when router deployment registration
  reuses a provider/model key already in `litellm.model_cost` (prevents alias
  with `mode: chat` from downgrading shared `chatgpt/gpt-5.4` from `responses`
  to `chat` and triggering 403s on /v1/chat/completions)
- teach the ChatGPT Responses parser to recover `response.output_item.done`
  entries when `response.completed.output` is empty
- add defensive /responses -> /chat/completions bridge fallback that
  reconstructs output items from raw SSE when `raw_response.output` is empty
- regression coverage for shared alias routing, empty completed.output
  parsing, and SSE bridge recovery

Closes #25403

Co-authored-by: afoninsky <andrey.afoninsky@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:23:41 +05:30
Elon Azoulay
265d925b8a
fix(fireworks): add glm-5p1 metadata and parallel_tool_calls (#26069) 2026-05-20 14:19:01 +05:30
Matthew Lapointe
a891ed3393
fix: reuse cached credentials in VertexAIPartnerModels (#26065)
* fix: reuse cached credentials in VertexAIPartnerModels instead of creating new VertexLLM per request

VertexAIPartnerModels.completion() was creating a throwaway VertexLLM()
instance on every call to get an access token, bypassing the credential
cache inherited from VertexBase. This caused a fresh token fetch for
every single request, adding significant latency overhead.

Fix: call super().__init__() to initialize VertexBase's credential cache,
and use self._ensure_access_token() instead of a new VertexLLM instance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: apply same credential caching fix to VertexAIGemmaModels and VertexAIModelGardenModels

Same bug as VertexAIPartnerModels: both classes had `pass` in __init__
instead of `super().__init__()`, and created throwaway VertexLLM()
instances per request instead of using self._ensure_access_token().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-20 14:17:48 +05:30
Matthew Lapointe
e7655420f5
fix(vertex_ai): single-flight credential refresh to prevent thundering herd (#26024)
* fix(vertex_ai): single-flight credential refresh to prevent thundering herd

When GCP credentials expire under high concurrency, all requests
simultaneously call credentials.refresh() via asyncify, saturating the
40-thread anyio pool and blocking the proxy for 20+ seconds.

This adds:
- Per-credential asyncio.Lock in get_access_token_async for single-flight
  refresh (1 coroutine refreshes, others wait on the lock)
- Background refresh when token_state is STALE (usable but near expiry),
  returning the current token immediately with zero added latency
- threading.Lock on the sync get_access_token path
- Uses google-auth's TokenState enum (FRESH/STALE/INVALID) instead of
  reimplementing expiry logic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review comments

- Use asyncio.create_task() instead of deprecated get_event_loop().create_task()
- Track in-flight background refresh tasks to prevent duplicate refreshes
  when multiple STALE-path callers pass through the lock before the first
  background task completes
- Add token validation in the STALE branch (consistent with FRESH/INVALID)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: lazy-import TokenState to avoid breaking when google-auth is not installed

Also extract helper methods to bring get_access_token_async under the
PLR0915 statement limit (50).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: apply Black formatting to test file and update uv.lock

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove user-provided project_id from log messages (CodeQL log injection)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: avoid leaking token value in error message, log type instead

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: restore uv.lock to match litellm_oss_branch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove project_id from remaining log message (CodeQL log injection)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove remaining project_id from log and error messages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-20 14:17:48 +05:30
Kevin Zhao
30f3b92f10
fix(bedrock): use model info lookup for output_config support instead of hardcoded check
Replace hardcoded _is_claude_4_6_model() string matching with
supports_output_config flag in model_prices_and_context_window.json,
accessed via _supports_factory(). This follows the project's established
pattern for model capability checks (per AGENTS.md rule #8).

Bedrock Invoke now conditionally preserves output_config for models
that declare supports_output_config=true (currently Claude 4.6 models),
while stripping it for older models to avoid request rejection.

Ref: https://github.com/BerriAI/litellm/issues/22797
2026-05-20 14:17:45 +05:30
Sameer Kankute
e59e34bed3
Gemini managed agents support (#28270)
Some checks are pending
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / schema-migration (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Security / security (push) Waiting to run
* Add support for environment variable in interactions api

* Add sdk  support for gemini create agent

* Add agents endpoint support via proxy

* Add outputs of each api

* Add routing for model and agents param

* Remove redundant condition in get_provider_agents_api_config

LlmProviders.GEMINI.value is literally the string "gemini", so the
second clause of the or was checking the exact same thing as the first.

Co-authored-by: Sameer Kankute <Sameerlite@users.noreply.github.com>

* fix: forward query-param credentials to list/get/delete/versions Gemini agent endpoints

The list_gemini_agents, get_gemini_agent, delete_gemini_agent, and
list_gemini_agent_versions endpoints previously constructed a hardcoded
data dict with no mechanism to pass provider credentials.  Unlike
create_gemini_agent (POST, reads litellm_params_template from body),
these GET/DELETE endpoints gave no way for multi-tenant callers to
supply a per-request api_key or other LiteLLM params.

Fix:
- Add _merge_query_params_into_data() helper that reads query parameters
  from the request and merges them into the data dict without overwriting
  already-set keys (e.g. path params like 'name').
- Support a JSON-encoded litellm_params_template query parameter
  (matching the POST body pattern) as well as flat key=value pairs
  (e.g. api_key=AIza...).
- Apply the helper in all four affected endpoints.
- Add 13 unit tests covering the helper and each endpoint.

Co-authored-by: Sameer Kankute <Sameerlite@users.noreply.github.com>

* fix: pass model=None for managed agent proxy endpoints to prevent agent name polluting data["model"]

Endpoints acreate_agent, aget_agent, adelete_agent, and alist_agent_versions
were passing model=<agent_name> to base_process_llm_request. This caused
common_processing_pre_call_logic to write the agent name into self.data["model"],
which then triggered spurious model-alias mapping, rate-limiting lookups, and
logging tied to a non-existent model deployment.

The agent name is already carried in data["name"] and is passed correctly to
the SDK functions (litellm.interactions.agents.*). There is no reason to also
set model=<agent_name>; the correct value is model=None for all five managed-agent
management routes.

Adds tests/test_litellm/proxy/google_endpoints/test_managed_agents_model_param.py
to verify all five managed-agent endpoints pass model=None.

Co-authored-by: Sameer Kankute <Sameerlite@users.noreply.github.com>

* fix: address greptile P1/P2 review comments

P1 (router.py): Restore fallback/retry support for acreate_interaction
and create_interaction. Both were silently moved to _init_interactions_api_endpoints
(direct call, no fallbacks). Moved them back to _ageneric_api_call_with_fallbacks
so users with configured fallback models keep retry behaviour.

P1 security (agents_endpoints.py): Remove flat query-param credential
path (e.g. ?api_key=AIza...) from _merge_query_params_into_data.
Credentials in URL query strings appear verbatim in server access logs,
CDN edge logs, and browser history. Only the JSON-encoded
litellm_params_template query param (matching the POST body pattern) is
retained.

P2 (interactions/http_handler.py): Extract _BaseHTTPHandler with shared
_handle_error, _sync_client, and _async_client helpers. InteractionsHTTPHandler
now extends _BaseHTTPHandler. The _async_client reads the provider from
litellm_params instead of hardcoding GEMINI.

P2 (interactions/agents/http_handler.py): AgentsHTTPHandler now extends
InteractionsHTTPHandler (which inherits _BaseHTTPHandler) so all shared
HTTP infrastructure is reused rather than duplicated. Removes the
hardcoded LlmProviders.GEMINI from the async client path.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: address CI failures from greptile review fixes

- black: format interactions/agents/main.py and utils.py
- tests: update test_gemini_agents_endpoints.py to match new
  _merge_query_params_into_data behaviour (flat credential params are
  rejected; only JSON-encoded litellm_params_template is accepted)
- ci: add test_gemini_agents_endpoints.py to endpoints-and-responses
  shard in test-unit-proxy-db.yml so assert-shard-coverage passes
- tests: add _initialize_managed_agents_endpoints and
  _init_managed_agents_api_endpoints test coverage so router_code_coverage
  passes; also fix TestRouterCreateInteractionRouting to reflect that
  acreate_interaction now correctly routes through
  _ageneric_api_call_with_fallbacks (restoring fallback support)

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: remove InteractionsHTTPHandler._handle_error override to fix type errors

AgentsHTTPHandler extends InteractionsHTTPHandler and calls
self._handle_error(provider_config=agents_api_config) where
agents_api_config is BaseAgentsAPIConfig. Python MRO resolved _handle_error
to InteractionsHTTPHandler._handle_error which expected BaseInteractionsAPIConfig,
causing 10 mypy arg-type errors in interactions/agents/http_handler.py.

Removing the redundant override lets both classes inherit _BaseHTTPHandler._handle_error
(provider_config: Any) which is structurally correct for both config types.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: agent-only interactions and managed agents provider routing

Resolve None custom_llm_provider in agents HTTP client lookup and set
custom_llm_provider on GenericLiteLLMParams for all agent CRUD paths.

Stop mapping agent names to proxy model routing; route interactions
through _init_interactions_api_endpoints with fallbacks only when model
is set. Consolidate duplicate router elif branches for interaction APIs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix greptile review

* test(agents): add unit tests for managed agents SDK and HTTP handler

Adds coverage for the new `litellm.interactions.agents` surface area:
- main.py: sync/async entry points (create/list/get/delete/list_versions),
  provider config lookup, logging-obj helper, async error wrapping
- http_handler.py: every CRUD method (sync + async paths), `_is_async`
  dispatch branches, and provider error mapping through GeminiAgentsConfig
- utils.py: get_provider_agents_api_config for supported / unsupported
  providers

Brings patch coverage on these files from <25% to ~100% so codecov/patch
is satisfied.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* docs(gemini-agents): fix misleading credential-passing examples in GET/DELETE docstrings (#28293)

The four GET/DELETE endpoint docstrings (list_gemini_agents,
get_gemini_agent, delete_gemini_agent, list_gemini_agent_versions)
documented passing per-request credentials as flat query parameters
(e.g. ?api_key=AIza...). However, _merge_query_params_into_data only
reads the JSON-encoded litellm_params_template query parameter and
intentionally ignores flat params (URL query strings appear verbatim
in access logs, browser history, and Referer headers).

Callers following the documented curl examples would have their
credentials silently dropped and hit auth failures against Gemini.

Update the examples to use the supported JSON-encoded
litellm_params_template query parameter, matching _merge_query_params_into_data's own docstring.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* refactor(agents): rename provider-agnostic agent response types

Move GeminiAgent{ListResponse,DeleteResult,VersionsResponse} to
provider-neutral names (AgentListResponse, AgentDeleteResult,
AgentVersionsResponse) so the BaseAgentsAPIConfig interface no longer
references Gemini-specific type names.

* fix(gemini-agents): close veria-flagged credential-escalation gaps

Two high-severity findings from the veria-ai PR review are addressed:

1. **api_base override could leak the shared Gemini key**
   GeminiAgentsConfig.validate_environment falls back to GOOGLE_API_KEY /
   GEMINI_API_KEY when no api_key is supplied. Combined with caller-controlled
   api_base on the proxy CRUD endpoints, an authenticated user could redirect
   the outbound request to an attacker-controlled host and capture the
   operator's shared Gemini key from the x-goog-api-key header. The config
   now refuses env-fallback whenever api_base is explicitly overridden.

2. **Managed-agent CRUD exposed to ordinary LLM keys**
   The new /v1beta/agents routes live in google_routes (i.e. llm_api_routes),
   so any non-admin LLM key can reach them. Unlike /v1beta/models/...:
   generateContent these endpoints are NOT model-routed and have no
   model_list-supplied credentials, so env-fallback would let any LLM key
   list / create / delete agents inside the operator's Gemini project. Each
   endpoint now calls _enforce_caller_supplied_provider_key, which requires
   non-admin callers to supply their own Gemini api_key via
   litellm_params_template. Proxy admins keep the env-fallback convenience.

Tests cover non-admin rejection, admin allow-through, the api_base override
guard, and SDK env-fallback when api_base is not overridden.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(router): restore strict assert_called_once_with on interactions default-provider test

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Sameer Kankute <Sameerlite@users.noreply.github.com>
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-05-19 16:02:03 -07:00
Sameer Kankute
3c3d131f01
Day 0 support : Gemini 3.5 Flash (#28268)
* Add day 0 support for gemini 3.5 flash

* Fix pricing

* Fix greptile review

* Fix failing test

* Fix tests

* Fix: revert tool removing logic

* fix greptile and test

---------

Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-05-19 15:50:54 -07:00
Yassin Kortam
cde4b1a94d
feat: propagate team_id and team_alias to all child OTEL spans (#28273)
- Add `_set_team_attributes_on_span` helper to stamp team_id/team_alias
  onto any span, ensuring these attributes are not limited to the root
  litellm_request span
- Add `_set_team_attributes_from_kwargs` helper to extract team metadata
  from the standard_logging_object in kwargs and apply them to a span
- Apply team attributes to raw request spans via `_maybe_log_raw_request`
  so downstream consumers can filter traces by team without needing the
  root span
- Apply team attributes to guardrail spans so guardrail activity can be
  correlated to teams in tracing backends
- Apply team attributes to exception logging spans to preserve team
  context during failure paths
- Add comprehensive unit tests covering all new helpers, including edge
  cases where metadata or standard_logging_object is absent

Co-authored-by: Yassin Kortam <yassinkortam@g.ucla.edu>
2026-05-19 15:31:25 -07:00
yuneng-jiang
1480ec698b
chore(ci): bump versions (#28287)
* bump: version 0.4.72 → 0.4.73

* bump: version 1.86.0 → 1.87.0

* uv lock
2026-05-19 15:10:37 -07:00
yuneng-jiang
92de7423ef
fix(tests): replace shut-down gpt-4o-audio-preview with gpt-audio-1.5 (#28281)
* fix(tests): replace shut-down gpt-4o-audio-preview with gpt-audio-1.5

OpenAI shut down gpt-4o-audio-preview on 2026-05-07, so the live audio
calls in test_stream_chunk_builder_openai_audio_output_usage and
test_standard_logging_payload_audio now hard-fail with a model-not-found
error on every PR. The error was not "openai-internal", so the except
block swallowed it and execution fell through to an unbound
completion/response (UnboundLocalError).

Switch both tests to gpt-audio-1.5, OpenAI's recommended successor
(GA, not deprecated, already present in the litellm cost map so the
response_cost assertion still resolves). Also broaden the except to
skip with the real error in the reason instead of crashing, so a
transient upstream blip can't reintroduce the UnboundLocalError.

* fix(tests): narrow audio-test skip to model-not-found, re-raise the rest

Address review feedback: an unconditional skip on any exception would
silently mask a litellm-internal regression in the audio path (broken
param transformation, serialization, bad header) instead of failing CI.

Skip only on the upstream-unavailable class (model_not_found / "does not
exist" / openai-internal) and re-raise everything else, so genuine
regressions still fail loudly. The UnboundLocalError is still fixed
because the handler either skips or raises - it never falls through.

* fix(tests): add budget_exceeded to expected Interaction status enum

Staging added budget_exceeded to the Interaction OpenAPI status enum; the staging merge into this branch picked up the spec change but not the matching test update, so test_status_enum_values failed in CI. Align the test's expected list (exact-match by design) with the live spec.

* fix(tests): mock HTTP fetch in test_img_url_token_counter

The test parameterized a live third-party image URL (blog.purpureus.net) which now 404s, causing get_image_dimensions to fall through to its base64 decode path and crash with 'not enough values to unpack' on every PR run. Mock safe_get with a tiny 1x1 PNG so the URL branch is still exercised without any network dependency.

* fix(tests): swap gpt-4o-audio-preview to gpt-audio-1.5 in test_gpt4o_audio

OpenAI shut down gpt-4o-audio-preview on 2026-05-07, so both live tests in test_gpt4o_audio.py (test_audio_output_from_model and test_audio_input_to_model) hard-fail model_not_found on every PR. Swap the hardcoded model to OpenAI's successor gpt-audio-1.5 (same chat-completions audio surface; already in the litellm cost map). Mirror the narrowed-skip pattern from the prior audio fixes: skip on model_not_found / does-not-exist / openai-internal, re-raise everything else so genuine litellm regressions still fail CI loudly.
2026-05-19 14:48:30 -07:00
ryan-crabbe-berri
727a471ae9
[Refactor] UI - Spend Logs: consolidate filter state and extract components (#25847)
* [Refactor] UI - Spend Logs: consolidate filter state, extract components, remove dead code

- Lift filter state into index.tsx and pass to hook (removes selectedX vars + sync useEffect)
- Move main useQuery into useLogFilterLogic hook (removes isMainQueryEnabled toggle)
- Delete dead RequestViewer component (300 lines, replaced by LogDetailsDrawer)
- Extract LogsTableToolbar component (search, date range, pagination, live tail)
- Extract filter options config to filter_options.ts
- Remove dead code: handleRefresh, handleSelectLog, handleCloseDrawer, formatTimeUnit,
  showFilters/showColumnDropdown state, dropdownRef/filtersRef

* Fix PR feedback: use antd Switch instead of Tremor in new file, fix typo

* Collapse dual-path filtering into single React Query

All 10 filter keys now go through the useQuery — the imperative
performSearch / debouncedSearch / backendFilteredLogs path is deleted.
Filter values are debounced via useDebouncedValue(300ms) before hitting
the query key so text inputs don't fire per-keystroke.

Removed: performSearch, debouncedSearch, backendFilteredLogs,
lastSearchTimestamp, hasBackendFilters, clientDerivedFilteredLogs,
the sort/page/time refetch useEffect, and the filteredLogs chooser memo.

* Clean up remaining smells: remove isFetchingDeferred, internalize selectedTimeInterval, fix circular import

- Remove useDeferredValue/isButtonLoading — pass logsQuery.isFetching directly
- Move selectedTimeInterval into LogsTableToolbar as internal state
- Move PaginatedResponse type from index.tsx to log_filter_logic.tsx

* Fix quick-select dropdown overlapping sidebar

* Fix stale quick-select label after Reset Filters

Move selectedTimeInterval back to parent so handleFilterReset can
reset it to the 24-hour default. The toolbar receives it as a prop.

* refactor useLogFilterLogic tests for controlled-hook + backend-query shape

The hook no longer owns filter state or does client-side filtering — it
receives filters/setFilters as props and drives filteredLogs from a
useQuery over uiSpendLogsCall. Reshape the tests around that contract:
introduce a controlled harness that owns filter state, collapse the 10
per-filter assertions into a single it.each over filterKey → API param,
and drop the client-side passthrough tests (the .min test file and the
"return all logs when no filters" / "empty when logs null" cases) that
no longer correspond to any hook behavior.

* cover new useLogFilterLogic invariants: activeTab gate, filterByCurrentUser fallback, debounce negative, partial merge

Follow-up to the test refactor. Adds coverage for invariants the
refactored hook contract introduced but that the first pass didn't
assert:

- query enablement: expand the single accessToken-null case into an
  it.each over all four credential props (accessToken, token, userRole,
  userID), plus a separate test for activeTab !== "request logs"
- filterByCurrentUser: when true with a blank User ID filter, the
  outbound request carries user_id = userID
- debounce: also assert the negative case — no call in the first 100ms
  after a filter change (first waiting out the initial mount fire)
- handleFilterChange: partial updates merge without clobbering other
  filter keys (protects the spread + default-fill semantics)
- handleFilterReset: calls setCurrentPage(1) alongside restoring
  filters

* fix typo dropping the live-tail banner border

Tailwind silently ignores unknown classes, so border-greem-200 was
leaving the auto-refresh banner with only its bg-green-50 fill and no
outline.

* memoize columns and derived table data in SpendLogsTable

The table's columns array, four-pass data pipeline, and sort-change
handler were all being rebuilt on every parent render. That made every
filter click re-instance all 23 TanStack-Table columns, re-run
filter/reduce/map over all rows, and recreate per-row click closures —
all before the intentional 300ms debounce timer even got a chance to
fire.

Local measurement (40 rows, dev mode):

    filter click → query fires: 1957ms → 1217ms (−38%)

Wrap createColumns in useMemo keyed on sortBy/sortOrder, hoist
onSortChange into a useCallback, and move the searchedLogs /
sessionComposition / sessionRepresentativeMap / filteredData derivations
into a single useMemo keyed on filteredLogs.data + searchTerm.

These were pre-existing issues on main — not regressions from the
hook refactor — but the refactor made them user-visible because the
new query debounce put render cost on the critical path.

* apply dropdown filters instantly, debounce only text inputs

Dropdown selects now bypass the 300ms debounce so a click updates the
table immediately. Text inputs (Key Hash, Error Message, Request ID,
User ID) still debounce. handleFilterReset also clears the pending
debounced value so a half-typed text filter can't re-fire after reset.

* fix(ui/spend-logs): restore lost loading/debounce behavior + cover dropped tests

Regressions from the spend-logs-view refactor:
- debounce the 'Public model / search tool' text filter (was firing a
  backend query per keystroke) via TEXT_FILTER_KEYS
- restore Fetch-button smoothing through table repaint using
  useDeferredValue on the rendered data (explicit staleness)
- show AntDLoadingSpinner during the auth-resolve phase instead of a
  blank screen on first load
- only live-tail-poll while the tab is visible
  (refetchIntervalInBackground: false)
- extract getLiveTailRefetchInterval helper for the poll decision

Tests:
- LogDetailContent: retries display (>0 / 0 / absent), overhead-absent
- log_filter_logic: regression guard that the public-model filter
  debounces; getLiveTailRefetchInterval unit tests
- logs_utils: getTimeRangeDisplay quick-select window labels

* test(ui/spend-logs): cover the cold-load auth-not-ready spinner guard

Asserts SpendLogsTable shows a loading spinner (not a blank screen)
while credentials are unresolved, and renders the table once present.
2026-05-19 10:58:48 -07:00
harish-berri
cff3e0b75e
refactor(bedrock/sagemaker): switch to lazy loading for response stre… (#28189)
Some checks failed
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Has been cancelled
Unit Tests: Security / security (push) Has been cancelled
Unit Tests: Proxy DB Operations / auth-checks (push) Has been cancelled
Unit Tests: Proxy DB Operations / budgets (push) Has been cancelled
Unit Tests: Proxy DB Operations / custom-logging (push) Has been cancelled
Unit Tests: Proxy DB Operations / db-and-spend (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-runtime (push) Has been cancelled
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Has been cancelled
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Has been cancelled
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Has been cancelled
Unit Tests: Proxy DB Operations / key-generation (push) Has been cancelled
Unit Tests: Proxy DB Operations / logging-misc (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-server-core (push) Has been cancelled
Unit Tests: Proxy DB Operations / schema-migration (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-utils (push) Has been cancelled
* refactor(bedrock/sagemaker): switch to lazy loading for response stream shapes

- Replace eager loading of BEDROCK_RESPONSE_STREAM_SHAPE and SAGEMAKER_RESPONSE_STREAM_SHAPE with lazy loading via get_bedrock_response_stream_shape() and get_sagemaker_response_stream_shape() respectively.
- This change optimizes performance by avoiding unnecessary imports and logging warnings unless the response stream shapes are actually needed.
- Update relevant classes and tests to utilize the new lazy loading functions, ensuring consistent behavior across the codebase.

* test(bedrock/sagemaker): add fixtures to clear response stream shape cache

- Introduced `_reset_bedrock_response_stream_shape_cache` and `_reset_sagemaker_response_stream_shape_cache` fixtures to prevent lru_cache leakage between tests in their respective modules.
- Updated tests to utilize these fixtures, ensuring that the response stream shape cache is cleared before and after each test run.
- Added `pytest.importorskip("botocore")` to ensure that tests are skipped if the botocore library is not available.
2026-05-18 23:21:04 -07:00
Sameer Kankute
0290c7bc00
fix(proxy): decode bytes and pass-through SSE for Google-native streamGenerateContent (#27444) (#28213)
* fix(proxy): decode bytes and pass-through SSE for Google-native streamGenerateContent (#27444)

* fix(proxy): address Greptile review on Google-native SSE bytes path

Remove unreachable try/except around SSE pass-through yield and add a
unit test covering pre-formatted SSE bytes, terminator padding, and
non-SSE byte fallback wrapping.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Tai An <antai12232931@outlook.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-18 22:46:11 -07:00
Sameer Kankute
581882879d
feat(ui): add Interactions API endpoint to playground with SSE streaming (#28156)
* feat(ui): add Interactions API support to playground with streaming

Adds /v1beta/interactions as a selectable endpoint in the UI playground.
Uses SSE streaming (stream=true) and parses content.delta events for real-time output.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(interactions): remove forced gemini provider so all providers work via interactions API

Proxy endpoint was hardcoding custom_llm_provider="gemini" before routing,
preventing non-Gemini models from using the litellm_responses bridge.
Also reverts the UI Gemini-only model filter.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(interactions): fix streaming for non-gemini providers via bridge

Two bugs in LiteLLMResponsesInteractionsStreamingIterator:
1. content.delta was emitted without "type":"text" in delta dict, so the
   UI type-check always failed and no tokens were displayed
2. First OutputTextDeltaEvent was silently dropped (used to emit content.start
   with empty text); fixed by handling ResponsePartAddedEvent for content.start
   so text deltas go directly to content.delta

Co-authored-by: Cursor <cursoragent@cursor.com>

* undo unrelated changes

* fix(ui): extract model from top-level field in interactions bridge events

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* test(interactions): remove tautological gemini-provider assertion

The test_no_forced_gemini_provider_in_request_data check only asserted
against dict literals it had just constructed, so it always passed and
did not exercise the create_interaction endpoint. The endpoint
deliberately defaults custom_llm_provider to gemini, so the assertion
was also factually incorrect. Drop the misleading test.

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* fix(interactions): use ContentPartAddedEvent and guard interaction.start ordering

- ResponsePartAddedEvent corresponds to reasoning summary parts, not text
  content parts. Use ContentPartAddedEvent which is the event emitted before
  text output deltas (type response.content_part.added).
- Mirror the OutputTextDeltaEvent ordering guard: if interaction.start has
  not been sent yet, emit it first before content.start to honor the
  documented event ordering contract.

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* test(interactions): cover ContentPartAddedEvent ordering and no-op paths

* fix(tests): treat corrupt VCR cassette payloads as cache miss + use gpt-realtime in OpenAI realtime guardrails test

VCR redis persister was raising UnicodeDecodeError on cached payloads that
fail to UTF-8 decode (e.g. legacy entries written by another version of
the persister), failing tests at fixture setup instead of degrading to a
cache miss. Wrap decode+deserialize in a try/except so corrupt cache
entries are treated as CassetteNotFoundError, surfacing the failure via
the existing _record_cache_failure / VCRCassetteCacheWarning path.

OpenAI shut down gpt-4o-realtime-preview-2024-12-17 (and the entire
gpt-4o-realtime-preview family) on 2026-05-07. The live realtime
guardrails integration test now fails with model_not_found instead of
receiving session.created. Point OPENAI_REALTIME_URL at the current GA
model gpt-realtime, and relax the assertion in
test_text_message_blocked_by_guardrail_no_ai_response to also accept the
model's refusal-to-repeat the block message (gpt-realtime declines
verbatim-repeat instructions, which is still a safe outcome since the
original user message was blocked before reaching OpenAI). The
BLOCKED_PHRASE leak check is preserved as a hard invariant.

* fix(tests): migrate realtime + nvidia_nim rerank tests off shut-down upstream models

OpenAI shut down the entire gpt-4o-realtime-preview family (including the
undated alias) on 2026-05-07. The live realtime tests still connected
with that dead alias and failed with messages_received=1 (an error event
'The model gpt-4o-realtime-preview does not exist' instead of
session.created). Point the live OpenAI realtime tests at gpt-realtime,
the current GA realtime model:

- test_openai_realtime_simple.py: get_model() -> gpt-realtime
- test_openai_realtime.py: test_openai_realtime_direct_call_no_intent and
  test_openai_realtime_direct_call_with_intent -> openai/gpt-realtime

Mocked unit tests (test_realtime_query_params_construction,
test_realtime_query_params_use_normalized_model_name) are left as-is:
they never hit the network and assert string plumbing only.

NVIDIA reached end-of-life for the hosted
nvidia/llama-3.2-nv-rerankqa-1b-v2 rerank API on 2026-05-18 with no
published replacement, so the live BaseLLMRerankTest.test_basic_rerank
for nvidia_nim now returns HTTP 410 ('Gone'). NVIDIA's hosted catalog
rotates on a schedule, so swapping in another live model would only
defer the failure. Override test_basic_rerank in TestNvidiaNim to mock
the sync/async HTTP transport (same pattern as
test_nvidia_nim_rerank_ranking_endpoint in this file) and inject a fake
NVIDIA_NIM_API_KEY via monkeypatch. The request/response transformation
and cost calculation stay covered offline.

* test(callbacks): harden flaky proxy callback-leak detector

The proxy callback-leak detector (test_check_num_callbacks_on_lowest_latency)
was failing on this PR with 'abs(85 - 95) <= 4' — a bounded one-time
registration jump caused by switching to latency-based-routing
(+LowestLatencyLoggingHandler, +SlackAlerting). The count then plateaus
under load, so this is pollution from the test's own config update, not a
leak.

Replace the brittle two-sample diff threshold with a sampler that settles
past the deliberate config switch and only flags sustained monotonic
per-type growth, with a terminal-burst confirmation pass for leaks that
would otherwise escape the >=2-interval guard. Normalizes instance
addresses so identical callbacks at different memory locations collapse,
and names the leaking type on failure.

* fix(interactions): preserve first text token when both start events are missing

When OutputTextDeltaEvent arrived before any ResponseCreatedEvent or
ContentPartAddedEvent, the double-fallback path emitted interaction.start
and silently dropped the first delta's text — the second delta's
content.start carried only that chunk's delta, and the first token never
made it to any content.delta event consumed by the UI.

Queue a content.start that carries the first delta's text alongside the
interaction.start emission, and drain pending events before pulling the
next upstream chunk.

* chore(ui): remove unused InteractionOutput/InteractionResponse interfaces

Co-authored-by: Yassin Kortam <yassin@berri.ai>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Yassin Kortam <yassin@berri.ai>
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-05-18 19:55:40 -07:00
Mateo Wang
761c280a6e
fix(deepseek): use native /anthropic/v1/messages endpoint and sanitize tools (#28200)
* fix(deepseek): route messages api through anthropic config

Add a DeepSeek-specific Anthropic Messages config so deepseek/... models use the native messages endpoint and preserve thinking blocks. Strip Anthropic custom tool type markers that DeepSeek rejects while keeping hosted tool types intact.

* fix(deepseek): normalize anthropic messages api base

Handle OpenAI-style DeepSeek api_base values ending in /v1 or /v1/messages by stripping those suffixes before adding the /anthropic messages path.

* chore(deepseek): format messages transformation

* chore(deepseek): add test package markers

* fix(deepseek): tighten anthropic url path check and fall back to DEEPSEEK_API_BASE

Author: mateo-berri <277851410+mateo-berri@users.noreply.github.com>

* fix(tests): normalize smart quotes in realtime guardrail refusal check

gpt-realtime nondeterministically returns refusals with Unicode curly
apostrophes (e.g. 'I’m sorry, but I can’t assist with that.'), but the
safe_markers tuple in test_text_message_blocked_by_guardrail_no_ai_response
only contains straight ASCII apostrophes. The substring match then fails
even though the response is a clear refusal, flipping CI red.

Normalize the AI text to ASCII quotes before the marker check so both
straight and curly variants count as safe outcomes.

* fix(deepseek): drop redundant anthropic v1/messages endswith check

* fix(deepseek): strip /beta suffix in anthropic messages URL normalization

Co-authored-by: Yassin Kortam <yassin@berri.ai>

---------

Co-authored-by: Felipe Rodrigues Gare Carnielli <felipe.gare@hotmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Yassin Kortam <yassin@berri.ai>
2026-05-18 18:14:13 -07:00
Mateo Wang
761ab19209
fix(bedrock): sanitize batch metadata to prevent Pydantic ValidationError (#28202)
* fix(bedrock): sanitize batch metadata to prevent Pydantic ValidationError

Proxy guardrail hooks (Model Armor, OpenAI Moderations) and internal
processing inject non-string values (dicts, floats) into the request
metadata. When the Bedrock batch handler passes this metadata directly
to LiteLLMBatch (which inherits OpenAI's Batch Pydantic model with
metadata: Dict[str, str]), Pydantic raises a ValidationError. This
causes the router retry loop to re-submit the same Bedrock job
multiple times before ultimately failing.

Add _get_openai_compatible_batch_metadata() that serializes non-string
values to JSON strings via safe_dumps, skips None values and internal
logging keys, ensuring the response object always validates.

* test(bedrock): add tests for batch metadata sanitization

Covers _get_openai_compatible_batch_metadata: string passthrough, dict/float
serialization, None/internal key exclusion, and LiteLLMBatch compatibility.

---------

Co-authored-by: Noah Nistler <60981020+noahnistler@users.noreply.github.com>
2026-05-18 18:00:18 -07:00
yuneng-jiang
a7f3dbcbe3
test(callbacks): harden flaky proxy callback-leak detector (#28195)
* test(callbacks): TEMP diagnostic probe for callback-leak flake

Hardened leak detector (sample N, flag sustained monotonic per-type
growth, normalize instance addresses) + a temporary always-fail probe
on test_check_num_callbacks_on_lowest_latency that dumps the per-type
series and raw reprs via the JUnit failure message, to settle real-leak
vs bounded-pollution on CCI. Diagnostic block is clearly marked and
will be reverted before the PR.

* test(callbacks): harden proxy callback-leak detector, drop diagnostic

CCI diagnostic confirmed the 85->95 jump is a bounded one-time
registration from the test's own switch to latency-based-routing
(+LowestLatencyLoggingHandler, +SlackAlerting), flat at 95 for 2.5 min
under load — not a leak. Final detector: settle past the deliberate
config/update, sample N times, flag only sustained monotonic per-type
growth, normalize instance addresses, name the leaking type on failure.
Removes the temporary always-fail probe.

* test(callbacks): address review - drop redundant settle, close terminal-burst blind spot

- test_check_num_callbacks: remove leftover sleep(30) before sleep(SETTLE_SECONDS) (60s -> 30s dead wait).
- Add _terminal_suspects + _detect_leaks_confirmed: when monotonic net growth is confined to the final interval (escapes the >=2-interval guard), take one confirmation sample. A real ongoing leak keeps climbing and is flagged; a one-time terminal registration plateaus and is ignored.
2026-05-18 16:39:02 -07:00
Sameer Kankute
73e32a31bf
feat(prometheus): add user_email and user_alias to user budget metrics (#28155)
* feat(prometheus): add user_email and user_alias to user budget metrics

User budget Prometheus gauges now expose human-readable labels alongside
user_id, matching team and API key budget metrics for Grafana filtering.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(prometheus): gate user budget email/alias labels behind opt-in flag

Address greptile review: adding labels to existing metrics is a
breaking cardinality change. Gate behind
prometheus_user_budget_label_include_email_alias=True (default: False)
so existing dashboards and recording rules are unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-18 16:28:14 -07:00
Sameer Kankute
36c494fdd2
Litellm oss staging (#28161)
* fix(opentelemetry): JSON-serialize dict metadata fields for OTEL span attributes (#27451) (#27455)

Squash-merged by litellm-agent from Anai-Guo's PR.

* feat(dashscope): add embeddings and reranks(qwen3-rerank) support via OpenAI-compatible endpoint (#27508)

Squash-merged by litellm-agent from yimao's PR.

* fix(vertex_ai/gemini): raise BadRequestError when image_url or url fi… (#24550)

Squash-merged by litellm-agent from krisxia0506's PR.

* fix(vertex_ai): raise error on mid-stream 429/error chunks instead of silently swallowing (#23711)

Squash-merged by litellm-agent from krisxia0506's PR.

* fix: raise BadRequestError for file content blocks missing 'file' sub… (#24503)

Squash-merged by litellm-agent from krisxia0506's PR.

* Fix Gemini MIME detection for extensionless GCS URIs (#27278)

Squash-merged by litellm-agent from krisxia0506's PR.

* fix(vertex_ai/partner_models): drop unused vertexai SDK gate from count_tokens (closes #28084) (#28107)

Squash-merged by litellm-agent from voidborne-d's PR.

* feat(chart): add support for autoscaling behavior in HPA (#27990)

Squash-merged by litellm-agent from FabrizioCafolla's PR.

* feat(proxy): add blocked flag to models for pause/resume from the UI (#27927)

Squash-merged by litellm-agent from Cyberfilo's PR.

* fix: pass socket timeouts to Redis cluster clients (#27920)

Squash-merged by litellm-agent from tomdee's PR.

* Fix/cache token (#28009)

Squash-merged by litellm-agent from escon1004's PR.

* fix(deepseek): forward reasoning_content in multi-turn thinking mode conversations (#28080)

Squash-merged by litellm-agent from Divyansh8321's PR.

* fix(guardrails): return HTTP 400 instead of 500 for blocked requests (#27617)

* fix: reset org and tag budgets (#27326)

* reset org budgets

* reset tag budgets

---------

Co-authored-by: Michael Riad Zaky <michaelr@Mac.localdomain>

* fix(ui): omit allowed_routes from key edit save when unchanged (#27553)

* fix(ui): omit allowed_routes from key edit save when unchanged

When a team admin opens Edit Settings on a key with key_type=AI APIs and
saves without changing anything, the UI re-sends the existing allowed_routes
value, which the backend's _check_allowed_routes_caller_permission gate
rejects for non-proxy-admins (LIT-2681).

Strip allowed_routes from the patch in handleSubmit when it deep-equals the
original keyData.allowed_routes. The backend treats absence as "leave alone,"
so no-op saves now succeed for non-admins. Admins explicitly editing the
field still send the new value.

* fix(ui): order-insensitive allowed_routes diff + cover null-original case

Address Greptile review:

- Switch the "is allowed_routes unchanged" check to a Set-based comparison so
  a server-side reorder of the array doesn't register as a user edit and
  re-trigger LIT-2681.
- Add two regression tests: (1) keyData.allowed_routes is null and the form
  is untouched — patch should strip the field; (2) server returned routes in
  a different order than the user originally entered — patch should still
  recognize the value as unchanged.

* chore(ui): strip ticket refs and tighten comments in key edit fix

- Remove internal-tracker references from in-code comments
- Tighten the WHY comment in handleSubmit to two lines
- Drop redundant test-block comments — test names already describe the case

* fix(ui): annotate Set<string> generic in allowed_routes diff to fix tsc

* fix(guardrails): return HTTP 400 instead of 500 for guardrail-blocked requests

GuardrailRaisedException and BlockedPiiEntityError both lacked a
status_code attribute.  When these exceptions reached the proxy
exception handler (getattr(e, 'status_code', 500)), the fallback
defaulted to HTTP 500 — making intentional guardrail blocks
indistinguishable from server errors and causing unnecessary client
retries.

Changes:
- Add status_code=400 (keyword-only) to GuardrailRaisedException
- Add status_code=400 (keyword-only) to BlockedPiiEntityError
- Update _is_guardrail_intervention() to recognize both exceptions
  so downstream loggers record 'guardrail_intervened' instead of
  'guardrail_failed_to_respond'
- Add 6 unit tests for default/custom status codes and getattr pattern
- Strengthen existing blocked-action test with status_code assertion

Fixes #24348

---------

Co-authored-by: Michael-RZ-Berri <michael@berri.ai>
Co-authored-by: Michael Riad Zaky <michaelr@Mac.localdomain>
Co-authored-by: ryan-crabbe-berri <ryan@berri.ai>
Co-authored-by: Krrish Dholakia <krrish+github@berri.ai>

* fix(router/proxy): address Greptile P1+P2 review comments on PR #28161

- router: raise ServiceUnavailableError (503) instead of RouterRateLimitErrorBasic (429)
  when a specifically-addressed deployment is administratively blocked; 429 misleads
  retry-enabled clients into spinning forever against a paused model
- proxy_server: compute get_fully_blocked_model_names() once before both branches in
  model_list() instead of duplicating the call in each branch
- deepseek: upgrade silent debug log to warning when injecting placeholder
  reasoning_content so callers are clearly notified of degraded multi-turn quality
- tests: update two blocked-deployment assertions to expect ServiceUnavailableError

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: address bug detection findings (cache token order, mutable defaults)

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* fix: address bugs in async pass-through, anthropic cache token detection, rerank tests

- async_get_available_deployment_for_pass_through: enforce blocked check on specific deployments
- cost_calculator: detect anthropic-style usage by attribute presence (not truthiness) to avoid mixing OpenAI cached_tokens into anthropic normalization when read=0
- dashscope rerank tests: pass request to httpx.Response constructions for consistency

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* fix code qa

* fix(vertex_ai/gemini): strip MIME parameters from GCS contentType

GCS object metadata's contentType field can include parameters such as
'text/html; charset=utf-8'. Strip them in _apply_gemini_mime_type_aliases
so downstream get_file_extension_from_mime_type sees a bare MIME type.

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* fix(vertex_ai/gemini): clarify mime-type error message string concatenation

Co-authored-by: Yassin Kortam <yassin@berri.ai>

---------

Co-authored-by: Tai An <antai12232931@outlook.com>
Co-authored-by: Vincent <yimao1231@gmail.com>
Co-authored-by: Kris Xia <xiajiayi0506@gmail.com>
Co-authored-by: d 🔹 <liusway405@gmail.com>
Co-authored-by: Fabrizio Cafolla <developer@fabriziocafolla.com>
Co-authored-by: Filippo Menghi <113345637+Cyberfilo@users.noreply.github.com>
Co-authored-by: Tom Denham <tom@tomdee.co.uk>
Co-authored-by: escon1004 <70471150+escon1004@users.noreply.github.com>
Co-authored-by: Divyansh Singhal <97736786+Divyansh8321@users.noreply.github.com>
Co-authored-by: robin-fiddler <robin@fiddler.ai>
Co-authored-by: Michael-RZ-Berri <michael@berri.ai>
Co-authored-by: Michael Riad Zaky <michaelr@Mac.localdomain>
Co-authored-by: ryan-crabbe-berri <ryan@berri.ai>
Co-authored-by: Krrish Dholakia <krrish+github@berri.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Yassin Kortam <yassin@berri.ai>
2026-05-18 16:27:44 -07:00
Sameer Kankute
477b63c5ea
fix(caching): replay openai/responses bridge cache hits as chat streams (#28158)
* fix(caching): replay openai/responses bridge cache hits as chat streams

When chat completions route through openai/responses, cached ModelResponse
payloads under aresponses keys were deserialized as ResponsesAPIResponse
(500) or re-translated as responses events (empty streaming deltas). Deserialize
chat-shaped cache entries as acompletion and bypass the responses stream iterator
for cached CustomStreamWrapper replay.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(caching): map responses bridge call_type for sync vs async stream replay

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* fix: handle ModelResponse cache return in responses bridge and drop dead acompletion check

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* fix(caching): detect chat cache hits via object field before choices fallback

Prefer chat.completion object type over the broad choices-key heuristic so
Responses API cached payloads are not misclassified if their schema changes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(caching): cover responses bridge cache-hit paths in CI-tracked test suite

The new bridge cache replay logic in caching_handler.py and the
preformatted-stream guard in litellm_responses_transformation/handler.py
were exercised only by tests under tests/local_testing/, which the
responses-caching-types and misc shards do not run. Codecov flagged the
patch as 29.72% covered.

Add equivalent unit tests under tests/test_litellm/ so the responses,
caching, types, and misc shards execute them and ship their coverage
data to Codecov:

- _is_chat_completion_cached_dict happy/sad paths
- aresponses streaming bridge cache hit -> CustomStreamWrapper
- responses non-streaming bridge cache hit -> ModelResponse
- legacy ResponsesAPIResponse stream + non-stream replay
- _is_preformatted_cached_chat_stream true/false
- completion/acompletion early return on cached ModelResponse
- completion/acompletion skip rewrap on preformatted cached stream

* fix: add negative guard on object field in _is_chat_completion_cached_dict

Co-authored-by: Yassin Kortam <yassin@berri.ai>

* fix(vcr): treat corrupt cassette payloads as cache miss

* test: bump EOL'd NVIDIA rerank and OpenAI realtime models in CI

The NVIDIA hosted rerank endpoint for nvidia/llama-3_2-nv-rerankqa-1b-v2
reached end-of-life on 2026-05-18 and now returns HTTP 410 Gone, breaking
TestNvidiaNim::test_basic_rerank. Switch to nvidia/nv-rerankqa-mistral-4b-v3,
which is still hosted on the NVIDIA API catalog and is already listed in
model_prices_and_context_window.json.

OpenAI also retired the gpt-4o-realtime-preview-2024-12-17 model used by
test_realtime_guardrails_openai (now returns model_not_found). Switch the
realtime test URL to the GA gpt-realtime alias.

Unrelated to the responses-bridge cache fix in this PR, but committing
here to unblock CI per maintainer guidance.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(realtime): switch retired gpt-4o-realtime-preview to gpt-realtime

OpenAI removed gpt-4o-realtime-preview and all its date snapshots on
2026-05-18 (every variant now returns model_not_found), breaking the
live-WebSocket OpenAI realtime tests in CI:

  - test_openai_realtime_direct_call_no_intent
  - test_openai_realtime_direct_call_with_intent
  - TestOpenAIRealtime.test_realtime_connection
  - TestOpenAIRealtime.test_realtime_with_query_params

Point each of those to the current GA alias gpt-realtime (verified live).
Pure unit/mock tests that just assert the string value (e.g. in
test_realtime_query_params_construction and the
test_realtime_query_params_use_normalized_model_name mock) are left
alone since they do not depend on model availability.

Also relax the AI-response assertion in
test_text_message_blocked_by_guardrail_no_ai_response: gpt-realtime
occasionally produces a polite refusal ("I'm sorry, but I can't say
that") when the cancel arrives after the model has already started
generating, which is the expected outcome (no real AI content) but does
not contain the words 'blocked' or 'guardrail'. The primary guardrail
behaviour (guardrail_violation error event + transcript_delta block
message) is still asserted unchanged.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(nvidia_nim): mock rerank live API instead of hitting EOL'd endpoint

NVIDIA reached end-of-life for the hosted nvidia/llama-3.2-nv-rerankqa-1b-v2
rerank API on 2026-05-18 (returns HTTP 410 Gone), and the proposed
replacement nv-rerankqa-mistral-4b-v3 returns HTTP 404 for the CI account,
breaking TestNvidiaNim::test_basic_rerank.

Override test_basic_rerank to mock the HTTP transport (same pattern as
test_nvidia_nim_rerank_ranking_endpoint above) so the request/response
transformation and cost calculation stay covered without depending on
NVIDIA's hosted catalog rotation. The model identifier reverts to the
original llama-3.2-nv-rerankqa-1b-v2 since the request never leaves
the test process.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Yassin Kortam <yassin@berri.ai>
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-05-18 16:27:06 -07:00
yuneng-jiang
ce87c411bf
fix(tests): migrate realtime + rerank tests off shut-down upstream models (#28191)
* fix(tests): use gpt-realtime in realtime guardrails test

OpenAI shut down gpt-4o-realtime-preview-2024-12-17 on 2026-05-07, so
the live OpenAI realtime guardrails integration test now fails with
model_not_found (session.created never arrives, _wait_for_event times
out). Point OPENAI_REALTIME_URL at the current GA model, gpt-realtime.

Scope limited to this test: the pricing-catalog JSON keeps the retired
entries intentionally (historical cost calc + separate Azure timeline),
and the Azure realtime cost-calc test is unaffected.

* fix(tests): mock nvidia_nim rerank instead of hitting EOL'd endpoint

NVIDIA reached end-of-life for the hosted nvidia/llama-3.2-nv-rerankqa-1b-v2
rerank API on 2026-05-18 with no published replacement, so the live
BaseLLMRerankTest.test_basic_rerank for nvidia_nim now returns HTTP 410
("Gone"). NVIDIA's hosted catalog rotates on a schedule, so swapping in
another live model would only defer the failure.

Override test_basic_rerank in TestNvidiaNim to mock the sync/async HTTP
transport (same pattern as test_nvidia_nim_rerank_ranking_endpoint in this
file) and inject a fake NVIDIA_NIM_API_KEY via monkeypatch. The
request/response transformation and cost calculation stay covered offline.
Scope limited to nvidia_nim; other BaseLLMRerankTest providers untouched.

* fix(tests): migrate remaining realtime tests off shut-down gpt-4o-realtime-preview

OpenAI's 2026-05-07 shutdown removed the entire gpt-4o-realtime-preview
family, including the undated 'gpt-4o-realtime-preview' alias (not just the
dated snapshot fixed earlier). Three live tests still connected with the
dead alias and failed with messages_received=1 (an error event instead of
session.created):

- test_openai_realtime_simple.py: get_model() -> gpt-realtime (drives
  TestOpenAIRealtime.test_realtime_connection / test_realtime_with_query_params)
- test_openai_realtime.py: test_openai_realtime_direct_call_no_intent and
  test_openai_realtime_direct_call_with_intent -> openai/gpt-realtime
  (the with_intent test shares the same dead alias even though it was not
  in the failing set this run)

Mocked unit tests (test_realtime_query_params_construction,
test_realtime_query_params_use_normalized_model_name) are left as-is: they
never hit the network and assert string plumbing only.

Also fixes test_text_message_blocked_by_guardrail_no_ai_response, which now
connects (the earlier URL swap worked) but tripped a model-wording-brittle
assertion. The guardrail flow asks the model to voice the block message
verbatim; gpt-4o-realtime-preview complied (output contained 'blocked'),
gpt-realtime refuses verbatim-repeat instructions ('I'm sorry, but I can't
repeat that message.'). Since the original user message is blocked before
it reaches OpenAI, the refusal is still a safe outcome. Assertion #3 now
accepts both voicing and refusal, and adds a hard check that the blocked
phrase never leaks into AI output.
2026-05-18 15:41:51 -07:00
ishaan-berri
8c6625216b
fix(bedrock/cohere): send embedding_types as JSON array, not string (#28172)
* fix(bedrock/cohere): wrap embedding_types as list in map_openai_params

Bedrock Cohere expects embedding_types as a JSON array but
encoding_format was passed through as a raw string, causing:
  Malformed input request: #/embedding_types: expected type: JSONArray, found: String

* test(bedrock/cohere): assert embedding_types is sent as JSON array

---------

Co-authored-by: Ishaan Jaffer <ishaanjaffer0324@gmail.com>
2026-05-18 12:16:20 -07:00