Commit graph

45221 commits

Author SHA1 Message Date
Mateo Wang
77765fd302
Merge pull request #36055 from BerriAI/devin_ai_fix_gemini_stream_billing_36042
fix(google_genai): price streamed generateContent with the provider that served it
2026-08-26 18:05:50 -07:00
mateo-berri
26b7bc3583 fix(prompts): propagate prompt deletes to every worker and pod 2026-08-26 18:01:08 -07:00
tin-berri
1df25e26cf
revert(proxy): remove router_model_name from auto-routed response bodies (#38429)
Reverts #37725. The field existed so SDK callers that cannot read
`x-litellm-model-id` could tell which tier an auto-router picked, and the
framework that motivated it was LangChain. `@langchain/openai` builds
`additional_kwargs` and `response_metadata` from fixed key allowlists and drops
unknown fields at both the chunk top level and inside `delta`, so no
proxy-side placement of a namespaced key can reach a LangChain caller.

The complexity router's existing `return_raw_model_name` already covers that
case: it puts the resolved model in the standard `model` field, which
LangChain does propagate (`model_name` is on its metadata allowlist), and the
proxy honors it on both the streaming and non-streaming paths.

Keeps the unrelated cleanup from #37725 that dropped the redundant
function-local `ProxyBaseLLMRequestProcessing` import shadowing the
module-level one in `async_data_generator`.

`TestModelGroupAliasReachesPreRoutingStrategies` asserted on the marker as a
proof of strategy dispatch; the surviving `response.model == "gemini-flash"`
assertion already proves it.
2026-08-26 17:58:30 -07:00
Yuneng Jiang
84dfc18f6b
test(e2e): de-flake the cost-header cache read and the router fallback control
Two e2e tests fail on timing rather than on litellm behaviour. Measured over the
last ~35 litellm-e2e / litellm-e2e-ui runs:

  routerSettings.spec.ts:254  9/35 runs (7 flaky-on-retry, 2 hard failures)
  test_cost_headers_e2e.py    1/29 runs it appeared in

Router fallback control
-----------------------
The e2e stack runs replicaCount 2 with proxy_config_reload_interval_seconds 7,
and every request is routed independently, so an observation of the new config
only proves the replica that served it reloaded. patchRouterSettings returns as
soon as /config/update returns, and clearBrokenFallback never waits at all, so a
retry's one-shot control assertion could be answered by a sibling replica still
holding the previous attempt's fallback. That is exactly the observed pair of
errors: "fallback never took effect" on the first attempt and "broken primary
unexpectedly succeeded on its own" on the retry.

Both assertions now poll for a consecutive streak spanning more than one reload
cycle, mirroring the PROPAGATION_TIMEOUT / settle_propagation doctrine the Python
suite already applies in e2e_config.py.

Cost-header cache read
----------------------
The prime and measure calls fired back to back with no gap, and each retry threw
away the prefix it had just paid to prime in favour of a fresh one. OpenAI
publishes a primed prefix asynchronously and routes cache lookups by
prompt_cache_key, so the test was rerolling the least likely path to a hit.

Each round now pins a prompt_cache_key and re-reads the same primed prefix up to
CACHE_REREADS times before rotating, so a fresh prefix is spent only after the
primed one has genuinely failed to become readable.

No production code changes; prompt_cache_key is added to the e2e ChatBody model,
which serializes exclude_none and so is inert for every other caller.
2026-08-26 17:57:14 -07:00
Yuneng Jiang
315144c9cc
test(budget): annotate the new locals with Final 2026-08-26 17:55:33 -07:00
mateo-berri
f7af44a505 fix(anthropic-adapter): pass provider-native and OpenAI-format tools through on /v1/messages 2026-08-26 17:55:15 -07:00
Mateo Wang
147fcf767e
Merge pull request #38399 from BerriAI/litellm_mcp_http_extra
fix(mcp): add litellm[mcp] extra and actionable error when streamable_http_client is missing
2026-08-26 17:54:48 -07:00
Mateo Wang
e0248ac8fa
Merge pull request #38424 from BerriAI/litellm_flex_breakdown_tier
fix(cost): make cost-breakdown headers respect service tier
2026-08-26 17:52:53 -07:00
Mateo Wang
5175fda0af
Merge pull request #38407 from BerriAI/litellm_fix_dotprompt_model_swap
fix(prompts): apply prompt templates before routing on /v1/responses and honor ignore_prompt_manager_model
2026-08-26 17:50:49 -07:00
mateo-berri
e8a683e7a8 test(cost): cover warm prefix cache spanning text and image tokens 2026-08-26 17:48:43 -07:00
mateo-berri
c23ce4069b Merge remote-tracking branch 'origin/litellm_internal_staging' into lit6252_vehicle_37407 2026-08-26 17:47:07 -07:00
Yuneng Jiang
595ada1ef7
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/budget-update-e2e-skip-81b8fa 2026-08-26 17:46:28 -07:00
Yuneng Jiang
4d6786d420
fix(budget): serialize model_max_budget before the /budget/update write
/budget/update handed prisma the raw update dict, so a model_max_budget
payload reached the Json? column as a nested python dict. prisma-client-py
renders that into the GraphQL mutation as bare object keys rather than a
JSON string, and the query engine rejects it, so every per-model budget
update returned a 500 and the cap was never stored. Model ids carrying
punctuation (glm-5.2) also produced an invalid GraphQL name.

/budget/new already ran its payload through jsonify_object for exactly this
reason. Do the same on the update path. Team member and organization member
budget updates route through this handler too, so they were failing the same
way.

The existing unit tests mocked the prisma table with an AsyncMock that
accepts any dict, which is why this never showed up outside a live proxy.
The new test asserts on what the endpoint hands prisma.
2026-08-26 17:46:17 -07:00
yucheng-berri
ecc49764af
feat(guardrails): track Azure Prompt Shield usage and cost with spend isolation (#38387)
* Track Azure Prompt Shield guardrail usage and cost with spend isolation (LIT-5917)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Resolve credential references and pydantic extras in in-place guardrail updates

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Suppress LIT001 on the dict-accepting update helper signature

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 17:42:17 -07:00
mateo-berri
8697a9ffa9 Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_fix_dotprompt_model_swap 2026-08-26 17:36:03 -07:00
mateo-berri
ac2e07f6f4 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_dotprompt_model_swap
# Conflicts:
#	litellm/responses/main.py
2026-08-26 17:36:01 -07:00
yuneng-jiang
f677292901
Merge pull request #38392 from BerriAI/litellm_/search-tools-sync-issue-e522a2
fix(proxy): sync search tools into the router on management writes
2026-08-26 17:34:51 -07:00
Mateo Wang
d8595cb647
Merge pull request #38423 from BerriAI/litellm_gemini_latest_cache_read_rates
fix(model_prices): bill gemini -latest/preview alias cache reads at 10% of input
2026-08-26 17:25:24 -07:00
Mateo Wang
53a607e088
Merge pull request #38411 from BerriAI/litellm_fix_prompt_patch_sync
fix(prompts): propagate PATCHed prompt templates to every worker and pod
2026-08-26 17:20:27 -07:00
mateo-berri
e8ec34c4c8 refactor(google_genai): pick the stream logging endpoint type at construction 2026-08-26 17:18:44 -07:00
Mateo Wang
1ac39b10ba
Merge pull request #38412 from BerriAI/litellm_fix_gemini_tts_native_audio_rates
fix(cost-map): correct Gemini TTS and native-audio rates
2026-08-26 17:15:46 -07:00
Mateo Wang
b54f7505a3
Merge pull request #38419 from BerriAI/litellm_gemini_live_realtime_cost
fix(cost): price gemini-live-2.5-flash-native-audio realtime sessions
2026-08-26 17:15:41 -07:00
Mateo Wang
4e295e8eb9
Merge pull request #38422 from BerriAI/litellm_gemini35_flashlite_flex_cache_price
fix(model_prices): correct gemini-3.5-flash-lite flex cache-read pricing
2026-08-26 17:11:07 -07:00
Mateo Wang
39dd46397e
Merge pull request #38379 from BerriAI/litellm_mcp_oauth_admin_entered_authorize_urls
fix(mcp): honor admin-entered OAuth URLs on authorize after issuer yield
2026-08-26 17:11:00 -07:00
mateo-berri
c0f9af0802 fix(cost): make cost-breakdown headers respect service tier
The breakdown priced reasoning tokens at the flat standard rate while the
total billed them tier-aware, so on flex requests the reasoning sub-cost
header could exceed the whole response cost. Route the breakdown's
reasoning rate through the same tier-aware resolver as the total.

On /v1/messages the response is a TypedDict that can never carry hidden
params, yet the client wrapper still recomputed cost on it, clobbering the
already-correct breakdown with a tier-less, reasoning-less one. Skip the
metadata pass for results that cannot hold hidden params, since apply()
discarded it anyway.
2026-08-26 17:09:30 -07:00
yuneng-jiang
e80ba92cfa
Merge pull request #38313 from BerriAI/litellm_/hide-unhealthy-virtual-key-models-922c37
feat(proxy): hide unhealthy models from model listings, opt-in
2026-08-26 17:05:41 -07:00
mateo-berri
901bea41e7 Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_gemini_latest_cache_read_rates
# Conflicts:
#	tests/test_litellm/llms/gemini/test_cost_calculator.py
2026-08-26 17:04:59 -07:00
yuneng-jiang
cbefebbd9f
Merge branch 'litellm_internal_staging' into litellm_/search-tools-sync-issue-e522a2 2026-08-26 17:04:01 -07:00
mateo-berri
5461bb3b48 fix(prompts): sync only the newest row when environments share a versioned prompt id 2026-08-26 17:00:24 -07:00
devin-ai-integration[bot]
8a9d5b15b4
feat(langfuse): support langfuse_environment as a per-key dynamic callback param (#38264)
* feat(langfuse): support langfuse_environment as a per-key dynamic callback param

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(langfuse): type the langfuse_environment constructor param

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(langfuse): only pass environment when the SDK client supports it

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(langfuse): drop the request-body metadata test for langfuse_environment

The proxy bans request-body callback params by default (derived from
_supported_callback_params in auth_utils), so the metadata channel this
test asserted is rejected with a 401 on the proxy. The supported channel
is admin-set key/team callback_vars, with LANGFUSE_TRACING_ENVIRONMENT
as the deployment-wide fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(langfuse): validate langfuse_environment, avoid redundant clients, honor it in langfuse_otel

Closes the review gaps on the langfuse_environment param:

- Validate values against Langfuse's environment pattern at save time
  (/key/generate, /key/update, /team callback all 400 on e.g. 'Production'
  instead of 200-then-silently-dropping every trace server-side) and at
  logger init; non-string values are str()-coerced instead of crashing
  the SDK's regex check per event.
- Treat empty/whitespace values and values equal to the deployment-wide
  LANGFUSE_TRACING_ENVIRONMENT as non-dynamic so an environment-only
  override that changes nothing no longer mints a duplicate SDK client
  against MAX_LANGFUSE_INITIALIZED_CLIENTS.
- langfuse_otel now reads the per-key/team langfuse_environment from
  standard_callback_dynamic_params instead of only the env var.
- Advertise the param on the discovery surfaces: callback_configs.json
  (langfuse + langfuse_otel), the dashboard callback registry, and the
  /team/{team_id}/callback docstring (schema.d.ts regenerated).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style: ruff format langfuse files

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(lint): remove duplicate test import, LIT002 dict literal, and mock-echo otel test

- drop redundant in-function import of callback_config_error (F811)
- avoid the `or {}` mutable literal in _set_langfuse_specific_attributes (LIT002)
- rewrite the dynamic-env otel test to observe span.set_attribute output
  instead of patching litellm internals (TQ002/TQ008)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: milan <milan@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yucheng-berri <yucheng@berri.ai>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 16:56:55 -07:00
mateo-berri
057781a187 test(pass_through): pin stream pricing tests to injected divergent rate cards 2026-08-26 16:54:05 -07:00
mateo-berri
1e1c231076 fix(model_prices): bill gemini -latest/preview alias cache reads at 10% of input 2026-08-26 16:52:09 -07:00
Mateo Wang
40005cf7f8
Merge pull request #37724 from bisma-nawaz/fix-37647-staging
fix: map Gemini ON_DEMAND_FLEX traffic type to flex service tier
2026-08-26 16:51:56 -07:00
Mateo Wang
d77eef3d11
Merge pull request #38414 from BerriAI/litellm_fix_speech_metadata_spend_tracking
fix(speech): keep proxy metadata and completion cost through the TTS completion bridge
2026-08-26 16:51:50 -07:00
mateo-berri
bd75c38e84 fix(model_prices): scope flash-lite flex cache-read cut to vertex entries 2026-08-26 16:48:32 -07:00
Mateo Wang
ad00d90b99
Merge pull request #38418 from BerriAI/litellm_gemini_maps_grounding_cost
fix(gemini): bill Google Maps grounding as its own SKU
2026-08-26 16:47:05 -07:00
mateo-berri
fed7a48b3c Merge remote-tracking branch 'origin/litellm_internal_staging' into devin_ai_fix_gemini_stream_billing_36042
# Conflicts:
#	litellm/proxy/pass_through_endpoints/llm_provider_handlers/vertex_passthrough_logging_handler.py
2026-08-26 16:44:11 -07:00
tin-berri
cebf0d6f21
fix(responses): let cache-control injection reach the system prompt from instructions (#38120)
`AnthropicCacheControlHook` spends the configured injection points on the first
message list it is shown and drops the message points that matched nothing. That is
right when the messages it sees are the ones going upstream. It is wrong for
/v1/responses: the system prompt lives in `instructions`, which only becomes a system
message once the chat-completion bridge builds one, so a role-targeted point matched
nothing and was thrown away before the message it wanted existed. Injection silently
did nothing across the whole surface.

Hand those points back instead, stamped as judged, when the caller says its message
list is provisional. The stamp is what makes carrying them safe: without it the next
pass re-judges the points against messages this pass has already marked and stands the
whole configuration down. Callers holding the final messages -- /chat/completions and
/v1/messages -- do not raise the signal and keep dropping unmatched points as before.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 16:43:26 -07:00
tin-berri
d8edfb69c2
fix(proxy): derive auto-router health from its underlying models (#38174)
An auto_router deployment is a marker, not something a probe can contact, so
`_run_model_health_check` returns `{}` for it and it lands healthy whatever is
behind it. This derives its verdict from the models it actually resolves.

Rules and owners:

- `strategy_router_dependencies` is the single answer to "what does this router
  call": tier, default, classifier and embedding names per router kind, aligned
  with what init and the request path actually use.
- `_health_check_eligible` is the single probe-eligibility gate, applied to the
  requested set and to the pool a router's dependencies are drawn from alike, so
  an opted-out deployment cannot re-enter through a router that depends on it.
- `_resolved_deployment_ids` resolves names through `get_model_list`, the same
  composition of alias, routing-group and wildcard channels a request uses.
- A dependency reds its router only when *every* deployment behind the name is
  known unhealthy. A replica this run never judged, hidden from the caller or
  opted out of health checks, can still serve what the dead one drops, so
  partial evidence leaves the verdict green. Absent information never reds.
- Verdicts settle over rounds, because a marker never fails a probe of its own
  and a parent whose tier is a red router must inherit that fault. Both sweeps
  are bounded loops, so a router cycle terminates green.
- Dependency probes are added only on the targeted `/health?model_id=` path the
  dashboard uses per deployment, and are dropped from the response.

Resolves LIT-6073

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 16:41:54 -07:00
mateo-berri
885d95d71b Merge remote-tracking branch 'origin/litellm_internal_staging' into pr37724
# Conflicts:
#	tests/test_litellm/test_cost_calculator.py
2026-08-26 16:33:28 -07:00
mateo-berri
e7b843d69b test: trim realtime cost test docstrings to one line 2026-08-26 16:33:02 -07:00
Mateo Wang
b98b2d562b
Merge pull request #38416 from BerriAI/litellm_fix_lazy_openapi_stubs_for_imported_modules
fix(proxy): key lazy openapi stubs off registered features, not sys.modules
2026-08-26 16:32:02 -07:00
mateo-berri
f758e9c30a Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_speech_metadata_spend_tracking 2026-08-26 16:30:30 -07:00
mateo-berri
ac3f987883 fix: thread service_tier through vertex cost_per_character fallbacks
Vertex Gemini 3.x models route through cost_per_character (the cost_router
token-path gate only matches gemini-2), and its token fallbacks dropped
service_tier, so ON_DEMAND_FLEX responses were still billed at the standard
rate. Pass the tier through the call site and all four fallbacks.
2026-08-26 16:29:45 -07:00
mateo-berri
6c07fd547b test: accept the Maps grounding rate in the intended cost map schema 2026-08-26 16:28:00 -07:00
mateo-berri
dca5144dba Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_gemini_maps_grounding_cost
# Conflicts:
#	litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py
2026-08-26 16:27:40 -07:00
mateo-berri
b48bff7b54 fix(cost_calculator): require real values when detecting declared realtime pricing 2026-08-26 16:27:28 -07:00
mateo-berri
93e7e8d980 fix(mcp): token exchange rejoins discovery for a clientless DCR bridge missing its registration endpoint 2026-08-26 16:26:44 -07:00
mateo-berri
0243c5dee4 fix(model_prices): correct gemini-3.5-flash-lite flex cache-read pricing 2026-08-26 16:25:15 -07:00
mateo-berri
fdeab570a1 fix(speech): forward api_key to the TTS bridge and isolate response hidden params 2026-08-26 16:23:33 -07:00