- Backend: Use request model from hidden_params for Azure Model Router additional_costs when response has actual model
- Backend: Add additional_costs to total cost calculation
- UI: Show all non-null/non-zero additional_costs in CostBreakdownViewer
- UI: Render cost breakdown when only additional_costs exist
- Tests: Backend test for hidden_params flow; frontend tests for additional_costs
Made-with: Cursor
Ensure final finish_reason chunks retain non-OpenAI attributes from original provider chunks, including the holding_chunk flush path where delta is non-empty. Add regression tests for both final-chunk branches.
Made-with: Cursor
The /key/update endpoint was missing support for organization_id, which
was already available on /key/generate. This adds the field to
UpdateKeyRequest and validates org key limits during updates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test_chat_completion_low_budget test was flaky because async spend
tracking couldn't reliably catch up within 50 calls with 0.5s sleeps.
Increased to 200 calls with 0.1s sleeps (same total time budget) to
give more opportunities for budget enforcement to trigger.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The recent commit 2a997993d4 replaced httpx.AsyncClient() with
get_async_httpx_client() in ui_sso.py, but the PKCE tests still
patched the old httpx.AsyncClient path. Updated all 10 affected
tests to mock get_async_httpx_client and removed unnecessary
context manager setup since AsyncHTTPHandler is returned directly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use dependency_overrides for user_api_key_auth instead of relying on
uninitialized proxy globals. The auth dependency was crashing with 500
(instead of 401) and returning MagicMock user_id/team_id values that
broke json.dumps in _encode_realtime_token_payload.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Keep Anthropic-native tools (tool_search_tool_regex, web_search, bash, etc.) in original format when translating to OpenAI format for guardrails
- Convert guardrail-returned tools back from OpenAI to Anthropic format (type=custom for user tools)
- Add TOOL_SEARCH_TOOL to ANTHROPIC_HOSTED_TOOLS enum; use prefix matching for native tool detection
- Set type=custom explicitly when mapping OpenAI function tools to AnthropicMessagesTool
- Add test for Anthropic native tools with guardrails
Made-with: Cursor
The existing AttributeError detection in proxy error handling only
checked one level deep in the exception chain (__cause__, __context__,
original_exception). In practice, the AttributeError from malformed
messages gets wrapped in multiple layers (AttributeError ->
OpenAIException -> APIConnectionError), so the check never found it.
Extracted the check into _has_attribute_error_in_chain() which walks
the full exception chain recursively (depth-capped at 10 to prevent
infinite loops from circular references).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass-through endpoint failures fired both async_failure_handler and
async_post_call_failure_hook, causing duplicate logs in callback
integrations. Add pass-through guards to the failure path, matching
the existing success path behavior.
- Skip test_apply_patch_tool_call_converted_to_chat_completion_tool_call
when openai.types.responses.response_apply_patch_tool_call is unavailable
(CI uses openai==1.100.1 which doesn't have this module)
- Skip MCP M2M tests (test_m2m_credentials_forwarded_to_server_model,
test_m2m_drops_incoming_oauth2_headers) that fail because PR #23187
changed has_client_credentials to require explicit oauth2_flow opt-in
but _execute_with_mcp_client was not updated to pass it through
- Revert source code change to rest_endpoints.py that auto-inferred
oauth2_flow (regression risk: this changes MCP OAuth behavior)
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
- Vertex AI batch cost tests: replace removed gemini-1.5-flash-001 model
with gemini-2.0-flash-001 in pricing lookups
- MCP test_executes_tool_when_allowed: add server_id and auth_type attrs
to StubServer to match new _resolve_allowed_mcp_servers_with_ip_filter
- MCP M2M tests: infer oauth2_flow='client_credentials' in
_execute_with_mcp_client when client_id/client_secret/token_url present
(NewMCPServerRequest lacks oauth2_flow field)
- Team list test: update mock find_many to filter by team_id per the
current per-team query pattern in list_team
- Azure DALL-E 3 health check: skip test due to 410 ModelDeprecated
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
Apply the same fix: move litellm.public_model_groups_links assignment
to after get_config()/save_config() so it is not overwritten by the
stale DB value read.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
get_config() internally calls _update_config_from_db which overwrites
litellm.public_model_groups with the stale DB value. Moving the
in-memory assignment to after get_config()/save_config() ensures the
new value persists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Add missing __init__.py files in tests/test_litellm/llms/gemini/ and
subdirectories (realtime/, image_edit/) to fix ModuleNotFoundError
with pytest-xdist parallel workers.
2. Update test_transform_request_uses_dynamic_max_tokens to use
claude-3-7-sonnet-20250219 (max_output_tokens=64000) since
claude-3-5-sonnet-20241022 was removed from model_prices JSON
during deprecated model cleanup. The test assertion was outdated.
3. Update context caching TTL tests to use gemini-2.5-pro instead of
gemini-1.5-pro. The old model was removed from model_prices JSON,
causing supports_system_messages to return False, which prevented
system_instruction from appearing in the transformation output.
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
Update transform_image_generation_response test calls to pass required
explicit params (request_data, optional_params, litellm_params, encoding)
that replaced **kwargs in the method signature.
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
The empty-line filter in __next__/__anext__ called .strip() without
checking the type first. When the Responses API yields Pydantic
BaseModel events (e.g. ResponseCreatedEvent), this raises
AttributeError. Add an isinstance(str_line, str) guard so non-string
objects pass through to _handle_string_chunk as intended.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NewTeamRequest was missing the team_member_budget_duration field,
causing Pydantic to silently drop the value when creating a team via
POST /team/new. The template budget row was created without
budget_duration or budget_reset_at, so the ResetBudgetJob never
found it and team member spend was never reset.
Add the field to NewTeamRequest and pass it through to
should_create_budget and create_team_member_budget_table in the
new_team handler (matching the existing update_team path which
already works correctly).
Fixes#16057
Lint fixes (check_code_and_doc_quality job):
- Remove unused variable reasoning_effort in gpt_5_transformation.py (F841)
- Remove unused timezone imports in mcp_server rest_endpoints.py and server.py (F401)
- Remove unused ProxyBaseLLMRequestProcessing import in realtime endpoints.py (F401)
- Add BaseRealtimeHTTPConfig to TYPE_CHECKING block in utils.py (F821)
- Add PLR0915 per-file-ignore for mcp_server/rest_endpoints.py in ruff.toml
Test fixes (litellm_mapped_tests_llms job):
- Gemini video cost tests: pass explicit model_info to video_generation_cost()
instead of relying on gemini/veo-3.0-generate-preview being in model_prices JSON
- Anthropic max_tokens tests: mock get_max_tokens() to return expected values
instead of depending on claude-3-5-sonnet-20241022 being in model_prices JSON
- Vertex AI pydantic obj test: update from removed gemini-1.5-pro to gemini-2.5-flash,
update expected request body to use response_json_schema format
- Vertex AI/Bedrock file_content integration tests: update mocks to target
base_llm_http_handler.retrieve_file_content (the new code path via
ProviderConfigManager) instead of the old vertex_ai_files_instance/
bedrock_files_instance paths
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
PR #21613 optimized the /user/daily/activity/aggregated endpoint by
replacing find_many with a SQL GROUP BY query, but omitted entity_id
from the SELECT/GROUP BY clauses and hardcoded entity_id_field=None
in the call to _aggregate_spend_records. This caused breakdown.entities
to always be empty in the response.
Restore entity_id in the SQL query and forward entity_id_field and
entity_metadata_field to the aggregation step. The GROUP BY performance
benefit is preserved — the query still aggregates at the database level
instead of fetching all individual rows into Python.
* fix(sso): add direct PKCE token exchange and Redis cache wiring for multi-instance SSO
When PKCE is enabled, bypass fastapi-sso and perform direct token exchange so
code_verifier is correctly included. Store PKCE verifiers as dict in cache
for proper JSON serialization in Redis. Wire user_api_key_cache to Redis when
available so PKCE verifiers are shared across ECS tasks/pods.
Also adds clearer error messages when PKCE is required but not configured.
* refactor(sso): extract PKCE token exchange into SSOAuthenticationHandler methods
- Move import httpx/jwt to module level (top of file, not inside function)
- Extract inline PKCE token exchange + userinfo logic into two static methods:
_pkce_token_exchange() and _get_pkce_userinfo()
- get_generic_sso_response PKCE path is now a single method call
- Fix double-logging in except block for non-PKCE errors
- Use %-style log formatting (no f-strings in log calls)
* fix: address greptile review feedback
- Fix access_token missing in PKCE path: read from combined_response directly
instead of generic_sso.access_token (which is only set by verify_and_process)
- Fix PKCE error hint firing when PKCE is already enabled: only show
'set GENERIC_CLIENT_USE_PKCE=true' advice when code_verifier was absent
- Fix unguarded KeyError on access_token: check for error field in HTTP 200
responses before accessing token_response['access_token']
- Fix silent empty userinfo: raise ProxyException when both userinfo endpoint
and id_token fallback produce no user data
- Fix backward-incompatible Redis wiring: only attach Redis to user_api_key_cache
when GENERIC_CLIENT_USE_PKCE=true, preserving existing in-memory behaviour
* fix: address second round of greptile review feedback
- Fix PKCE error hint: check env var directly (not code_verifier presence) to
distinguish 'PKCE not configured' from 'PKCE enabled but cache miss'
- Fix misleading Redis TTL comment in proxy_server.py
* fix: address third round of greptile review feedback
- Fix CRITICAL log firing on every non-PKCE callback: only log when PKCE is enabled
- Remove unused pkce_env_value intermediate variable
- Prefer reusing redis_usage_cache over creating separate RedisCache instance
(avoids losing advanced connection options like SSL, timeouts, db)
* fix: address fourth round of greptile review feedback
- Strip OAuth token credentials from response_convertor input to prevent
access_token/id_token appearing in restricted-group error messages
- Reuse single httpx.AsyncClient for both token exchange and userinfo requests
to avoid a second TCP/TLS handshake per SSO callback
- Revert Redis wiring to user_api_key_cache: PKCE code already uses
redis_usage_cache directly; wiring would route all API-key lookups through
Redis unnecessarily. Add startup warning instead when PKCE+Redis mismatch.
- Move _OAUTH_TOKEN_FIELDS to module level
* fix remaining PKCE test assertion for dict-format verifier storage
* sanitize PKCE cache log to not expose verifier content
* address greptile review feedback (greploop iteration 3)
* address greptile review feedback (greploop iteration 4)
* address greptile review feedback (greploop iteration 5)
* simplify _get_pkce_userinfo: remove shared-client complexity, use async with directly
* address greptile review feedback (greploop iteration 6)
* address greptile review feedback (greploop iteration 7)
* address greptile review feedback (greploop iteration 8)
* address greptile review feedback (greploop iteration 9)
* address greptile review feedback (greploop iteration 10)
* address greptile review feedback (greploop iteration 11)
* address greptile review feedback (greploop iteration 12)
* fix misleading comment on user_api_key_cache TTL line
* address greptile review feedback (greploop iteration 13)
* address greptile review feedback (greploop iteration 14)
* address greptile review feedback (greploop iteration 15)
* address greptile review feedback (greploop iteration 16)
* address greptile review feedback (greploop iteration 17)
* address greptile review feedback (greploop iteration 18)
* address greptile review feedback (greploop iteration 19)
* address greptile review feedback (greploop iteration 20)
* address greptile review feedback (greploop iteration 21)
* address greptile review feedback (greploop iteration 22)
* address greptile review feedback (greploop iteration 23)
* address greptile review feedback (greploop iteration 24)
* address greptile review feedback (greploop iteration 25)
* address greptile review feedback (greploop iteration 26)
* address greptile review feedback (greploop iteration 27)
* address greptile review feedback (greploop iteration 28)
* address greptile review feedback (greploop iteration 29)
* address greptile review feedback (greploop iteration 30)
* address greptile review feedback (greploop iteration 31)
* address greptile review feedback (greploop iteration 32)
* address greptile review feedback (greploop iteration 33)
* address greptile review feedback (greploop iteration 34)
* address greptile review feedback (greploop iteration 35)
* address greptile review feedback (greploop iteration 37)
- read GENERIC_CLIENT_USE_PKCE env var once in prepare_token_exchange_parameters
- include actual decode error in jwt.decode failure exception message
- add GENERIC_CLIENT_USE_PKCE=true to no-state regression test
* defer PKCE verifier deletion until after all downstream processing
Move _delete_pkce_verifier to after response_convertor and
process_sso_jwt_access_token complete. If JWT processing raises,
the verifier stays in cache so the user can retry without restarting
the full OAuth flow.
* address greptile review feedback (greploop iteration 38)
- fix strict-mode cache miss error message to differentiate
cross-instance routing failures (Redis configured) from single-instance
issues (TTL expiry, pod restart) when only in-memory cache is available
- add comment above _get_pkce_userinfo call explaining that bearer
credentials are always sourced from token_response in the merge step
* fix null JSON response body in _pkce_token_exchange
- Guard against HTTP 200 with body null: response.json() returns None
for JSON null, and calling .get() on None raises AttributeError.
Now raises a clean ProxyException with a clear error message.
- Fix misleading userinfo warning: was always saying "empty dict" but
also fires for JSON null responses; updated to say "empty or null".
- Add HTTP status code assertion to cache miss test.
* address greptile review feedback (greploop iteration 39)
- fix credential leakage: directly assign received_response from
combined_response instead of relying on nonlocal mutation; Pyright
was flagging the old guard as unreachable, meaning credential stripping
might not execute — now it always runs unconditionally
- add test for legacy plain-string cache format backward compat branch
- add test for HTTP 200 with no error field and no access_token (else branch)
- add test for HTTP 200 with JSON null body (new AttributeError guard)
* fix _OAUTH_TOKEN_FIELDS merge loop to preserve userinfo values on absent fields
When the token endpoint omits a bearer-credential field entirely (field
absent from token_response), the previous code deleted it from merged even
if userinfo provided a valid value. Now:
- non-null in token_response → restore authoritative token endpoint value
- explicit null in token_response → remove key from merged (clean absence)
- field absent from token_response → leave userinfo value unchanged
* use HTTP 401 for PKCE missing config errors
GENERIC_CLIENT_ID and GENERIC_TOKEN_ENDPOINT missing when PKCE is
enabled are auth-flow failures, not server errors. Use 401 instead
of 500 to avoid triggering false-positive server error alerts in
monitoring systems.
* address greptile review feedback (greploop iteration 40)
- fix duplicate error logging: demote first format-error log to DEBUG
so the detailed ERROR in strict-mode branch is not duplicated
- add HTTP status code assertions to all PKCE ProxyException tests
for better regression protection against accidental code changes
* add credential absence assertions to test_pkce_token_exchange_basic_auth
Verify that client_id and client_secret are NOT double-sent in the POST
body when Basic Auth is used (include_client_id=False with client_secret).
Catches regressions where credentials leak into both Auth header and body.
* address greptile review feedback (greploop iteration 41)
- add Bearer token header assertion to test_pkce_token_exchange_credentials_in_body
- add cache query assertions to both non-strict mode tests to confirm
the cache was accessed before the warning path triggers
* address greptile review feedback (greploop iteration 42)
- assert null id_token is absent from merged result in basic auth test
- add test for HTTP 200 empty/null userinfo body with no id_token fallback
* use caplog to verify warning logs in non-strict cache miss tests
The two non-strict mode tests now use pytest's caplog fixture to assert
that a warning is actually emitted, not just that the code continues
without raising. This catches regressions where the warning silently
disappears.
* remove dead-code response=None guard in _pkce_token_exchange
* clean up stale pkce verifier cache entries in non-strict mode
* fix test: configure async_delete_cache as AsyncMock and assert cleanup called
* add sentinel guard so pkce-no-redis warning only fires once across hot-reloads
* fix misleading comments: code_verifier init and bearer-credential merge docs
* add best-effort cleanup in strict-mode for corrupt/empty cache entries
* add redirect_uri assertion, userinfo body in non-200 log, sentinel comment
OpenAI rejects any reasoning_effort (even 'none') with tools in
/v1/chat/completions for gpt-5.4. Update the guard to drop reasoning_effort
regardless of value. Add docs explaining the auto-drop behavior.
Keep both sets of tests: upstream's OAuth2 token injection test and
our case-insensitive tool matching tests. Use upstream's version of
the bedrock output_config test (more comprehensive).
* fix(proxy): make async_post_call_response_headers_hook consistent across all endpoints
The response headers hook had 5 gaps that prevented callbacks from
reliably extracting routing metadata across endpoint types:
1. Hook never fired for /audio/transcriptions (endpoint bypasses
base_process_llm_request)
2. custom_llm_provider not accessible in hook data for any endpoint
3. custom_llm_provider not stamped in ResponsesAPIResponse._hidden_params
(unlike chat completions)
4. model_info under inconsistent keys (metadata vs litellm_metadata)
5. request_headers always None at all call sites
This adds a litellm_call_info parameter to the hook that normalizes
routing metadata (custom_llm_provider, model_info, api_base, model_id)
regardless of endpoint type. Also stamps custom_llm_provider on
Responses API responses, adds the hook call to the transcription
handler, and passes request_headers at all call sites.
Supersedes PR #21385.
* fix(proxy): address review feedback — safer backwards compat and None guards
- Replace try/except TypeError with inspect.signature() check for
litellm_call_info backwards compatibility. This avoids masking real
TypeErrors inside callback implementations and prevents double
invocation with inconsistent parameters.
- Use (data.get("key") or {}) instead of data.get("key", {}) to guard
against keys that exist with an explicit None value, which would
cause AttributeError on the subsequent .get() call.
* fix(proxy): cache inspect.signature result for callback compat check
Move the inspect.signature() call into a module-level helper with a
dict cache keyed by callback identity. Avoids repeated introspection
per request per callback in the hot path.
* fix(proxy): use class identity for signature cache key
Key the _CALLBACK_ACCEPTS_CALL_INFO cache by id(type(cb)) instead of
id(cb) to avoid stale entries from Python address reuse after GC.
All instances of the same callback class share the same method
signature, so class identity is both safer and more cache-efficient.
- Thread api_version through HTTP handlers to Azure realtime endpoints
- Make expires_at optional in RealtimeClientSecretResponse
- Fix test token expiry times to be in the future
- Populate user_id and team_id in minimal_auth for spend tracking
Made-with: Cursor