Commit graph

307 commits

Author SHA1 Message Date
ryan-crabbe-berri
4439631e05
Merge pull request #41379 from runjivu/fix/fallback-budget-check
fix!: re-check budget on router fallback targets
2026-09-16 14:39:56 -07:00
yassin
6e5cdd8f12 fix(router): count TPM/RPM usage before building rate-limit headers
Router.make_call now increments the deployment TPM/RPM counter before set_response_headers reads remaining usage, so the headers carry post-increment values directly and the in-flight subtraction workaround from LIT-2719 is removed. deployment_callback_on_success adds only the tokens not yet counted (streams) and never a second request. The counter key uses the resolved deployment name so wildcard routes are read back correctly, and the proxy strips the router-owned counted-tokens marker from client metadata

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 18:45:01 +00:00
runjivu
4a70bc3ba3 fix: re-check budget on router fallback targets
Budget is enforced once during auth, against the requested model group.
`_is_model_cost_zero` waives every budget check for a zero-cost group, and the
router then picks a fallback target afterwards, inside `run_async_fallback`,
where nothing re-checks budget. A free model with a paid fallback therefore
bills with no budget gate at all.

Add `fallback_budget_check`, the budget sibling of the existing
`fallback_access_check`: a predicate awaited per fallback target that skips
targets the caller cannot pay for. The primary attempt is untouched, so a
zero-cost model is never blocked by budget and only the paid fallback is
refused.

Counter reads pass `max_budget` so `get_current_spend` verifies against
authoritative recorded spend, matching the auth-time key and user checks; a
counter restored from an older snapshot reads as a hit rather than a clean
miss, so without it a stale-low value would keep admitting paid fallbacks.

A zero-cost fallback target is always allowed, and a team key does not inherit
the key owner's personal budget unless `apply_user_budget_to_team_keys` is set,
matching `_PROXY_MaxBudgetLimiter`.

Scope is key and user budgets. Team, team-member, end-user, org, global and
per-model budgets are not covered yet: those auth-path functions enforce rather
than report, so reusing them would fire threshold alerts and take spend
reservations for a target that is then skipped. Two limitations of that scope
are documented in the module docstring: the check reads the spend counter
rather than reserving against it, so concurrent fallbacks can cross a cap
together; and a request reaching the router without
`metadata["user_api_key_auth"]` is not restricted. Both are shared with
`fallback_model_access.py`.

Opt-in via `general_settings.enforce_fallback_budget`.

Relates to #41344

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 19:06:38 +09:00
Tin Chi Lo
39cf1f302d feat(router): apply entitlement limits to forecast classifiers 2026-09-15 16:17:00 -07:00
yassin
595bec46ff fix(router): time fallback-hop 408s against now, not the previous hop's end_time
The failure logger skips fallback hops (has_logged_async_failure is already set), so
model_call_details.end_time still belongs to the previous hop and predates this hop's
api_call_start_time. The fallback cooldown guard measured a negative elapsed time and
cooled down deployments for caller-set timeouts.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 21:11:40 +00:00
yassin
5da497f4ac fix(router): only exempt 408s that arrive after the caller's timeout from cooldown
client_side_timeout records that the caller configured a timeout, not that
the timeout fired. A 408 the provider returns before that deadline is a
deployment failure and must still count toward cooldown.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 21:11:40 +00:00
mateo-berri
a9c422735f fix(router): stop counting caller-set timeout 408s toward deployment cooldown
A 408 produced by a timeout the caller set (a timeout body field or an
x-litellm-timeout header, which the proxy marks as client_side_timeout)
says nothing about the deployment's health, yet the router's primary
failure callback counted it toward allowed_fails and cooled the
deployment down. The fallback path already skipped it.

The marker never reached that callback because get_litellm_params drops
kwargs outside OPTIONAL_KWARGS_KEYS, so it is listed there now, and
deployment_callback_on_failure returns before the failure counter when
is_caller_timeout_408 holds. A 408 from a timeout the deployment or the
provider set still counts and still cools the deployment down.
2026-09-15 21:02:15 +00:00
Tin Chi Lo
2da9bbfc0f chore: merge main into capability classifier copy 2026-09-15 11:24:01 -07:00
tin-berri
3ad9a7f336
Merge pull request #41174 from BerriAI/litellm_tier_model_affinity
fix(router): preserve session model choice within each complexity tier
2026-09-15 09:54:53 -07:00
Tin Chi Lo
81340439fc fix(router): preserve session model choice within each complexity tier 2026-09-15 00:09:17 -07:00
Mateo Wang
c93708b2a5
Merge pull request #40228 from AaronHowell/litellm_fix_responses_credentials_affinity
fix(responses): preserve provider affinity
2026-09-14 23:09:55 -07:00
Yassin Kortam
e766277846
Merge pull request #40991 from BerriAI/litellm_team_model_cooldown_siblings
fix(router): cool down team deployments on 429 when a sibling serves the same public model
2026-09-14 14:57:07 -07:00
yassin
10f411e60d fix(router): name the all-deployments-in-cooldown error on 429 responses
RouterRateLimitError now carries the model group's deployment ids so it
can tell when every deployment is cooled down, and exposes that as
type=all_deployments_in_cooldown with an explicit message. A partial
cooldown keeps type=rate_limit_error. Either way the proxy no longer
reports type=internal_server_error next to code 429

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-13 09:34:16 +00:00
yassin
76b26e41ab fix(router): cool down team deployments on 429 when a sibling serves the same public model
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-13 09:07:53 +00:00
devin-ai-integration[bot]
dab7f6a86a
feat(proxy): expose complexity routing headers (#40792)
(cherry picked from commit c817faec7a)

Co-authored-by: Tin <tin@berri.ai>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-09-11 17:14:40 -07:00
Tin Chi Lo
82937d9969 feat(router): integrate benchmark calibration and merge staging 2026-09-11 14:20:59 -07:00
Anmol Jaiswal
dfab4794ec
docs(router): name both affinity TTL knobs in the _claim_pin docstring (#40663)
The docstring cited session_affinity_ttl_seconds as the keepalive bound,
but the Router-level knob feeding ttl_seconds is
deployment_affinity_ttl_seconds; session_affinity_ttl_seconds is the
separate per-request PreRoutingHookResponse override. Anyone grepping
the docstring's name to shrink the Router default finds only the
override. Name both, scoped correctly.
2026-09-11 10:22:32 -07:00
mateo-berri
7169ddaef6 fix(router): treat a breaker-refused Redis read as a miss in the health state cache
The sync Redis read now raises while the circuit breaker is open, and the
health state merge caught that as a generic error, skipping the local write
and logging an error on every background health check cycle. Read the shared
snapshot through a helper that treats the refused read as a miss so the merge
falls back to the pod-local copy the way a swallowed connection error already did
2026-09-10 18:16:59 -07:00
AaronHowell
f843e596e7 fix(responses): 同步上游亲和性改动
Co-authored-by: Bytechoreographer <Bytechoreographer@users.noreply.github.com>
2026-09-10 14:42:40 +08:00
Mateo Wang
69245feff4
Merge pull request #39863 from BerriAI/litellm_lit_7022_azure_ai_passthrough_config
fix(azure_ai): add passthrough config so router-model relays reach the deployment's own endpoint
2026-09-09 20:56:57 -07:00
Mateo Wang
8ec2f00955
Merge pull request #40451 from BerriAI/litellm_responses_bridge_replay_encrypted_reasoning
fix(anthropic): replay OpenAI encrypted reasoning byte for byte through the /v1/messages bridge
2026-09-09 19:45:11 -07:00
mateo-berri
a6681950e8 Merge branch 'litellm_internal_staging' into litellm_lit_7022_azure_ai_passthrough_config 2026-09-09 19:35:55 -07:00
mateo-berri
4716b46c24 fix(router): read the affinity pin from the messages argument and strip bridge reasoning on a cross-group route
The encrypted_content_affinity check only read the Anthropic history from request_kwargs["messages"], so a caller that passes it through the callback's messages argument alone skipped the pin. Read the argument first and fall back to the kwargs.

When the minting deployment is not a candidate of the routed group, the base already strips the Responses input's encrypted reasoning; do the same for the bridge-tagged thinking blocks in Anthropic messages so the routed deployment gets the readable thinking text instead of ciphertext it cannot decrypt.
2026-09-09 18:31:08 -07:00
mateo-berri
4d4906e94e Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_responses_bridge_replay_encrypted_reasoning
# Conflicts:
#	litellm/router_utils/pre_call_checks/encrypted_content_affinity_check.py
#	tests/test_litellm/router_utils/pre_call_checks/test_encrypted_content_affinity_check.py
2026-09-09 18:23:07 -07:00
mateo-berri
c1ca963d75 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_5546_count_tokens_offload 2026-09-09 18:18:17 -07:00
mateo-berri
e27a018a6c fix(router): pin bridge-replayed encrypted reasoning to the deployment that minted it
The encrypted_content_affinity check only read the pin from the Responses
input, which /v1/messages builds after the router has picked a deployment,
so a model group spread across OpenAI orgs sent follow-up turns to the
wrong org and got invalid_encrypted_content back. The check now also
decodes the pin from bridge-tagged thinking and redacted_thinking blocks
in the Anthropic messages. The bridge also keeps a deployment's own
include list next to reasoning.encrypted_content instead of replacing it.
2026-09-09 16:29:52 -07:00
mateo-berri
cc0c6087e3 chore: merge litellm_internal_staging into litellm_lit_7022_azure_ai_passthrough_config 2026-09-09 16:09:57 -07:00
tin-berri
eb45a088d3
fix(router): resolve team-scoped auto-routers by their public name (#40432)
A team-scoped auto-router is stored under an internal
model_name_{team_id}_{uuid} with the caller-facing name in
model_info.team_public_model_name, and the four pre-routing strategy
registries key on that internal name. A team key asks for the public name,
so the strategy lookup missed, the team early-resolve exit handed back the
marker deployment itself, and every call 400'd with "Unmapped LLM provider".

The strategy lookup now resolves the requested name through the same
team-first, then global, then admin-across-teams deployment resolution the
deployment path uses, and looks the registries up under the model_name of
whatever that resolves to. Both exits of _common_checks_available_deployment
drop strategy markers through one helper, so a marker-only resolution is
rejected as uncallable on every path. The request team id has one reader.

Resolves LIT-7363


Claude-Session: https://claude.ai/code/session_01NU97S7d2FUDDvTk59k53Wp

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 15:53:05 -07:00
mateo-berri
13745e89ac chore: merge litellm_internal_staging into litellm_lit_7022_azure_ai_passthrough_config 2026-09-09 15:36:06 -07:00
devin-ai-integration[bot]
264fc82dc1
fix(rate_limiter): attach v3 priority rate limit headers on /v1/messages (#37228)
Some checks are pending
LiteLLM Rust / rust-test (push) Waiting to run
Unit Tests: Documentation Validation / documentation (push) Waiting to run
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
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 / caching-local (push) Waiting to run
Unit Tests / core-utils (push) Waiting to run
Unit Tests / enterprise-package (push) Waiting to run
Unit Tests / enterprise-routing (push) Waiting to run
Unit Tests / integrations (push) Waiting to run
Unit Tests / All Other Providers (push) Waiting to run
Unit Tests / Vertex AI (push) Waiting to run
Unit Tests / misc (push) Waiting to run
Unit Tests / proxy-auth (push) Waiting to run
Unit Tests / proxy-endpoints (push) Waiting to run
Unit Tests / proxy-extras (push) Waiting to run
Unit Tests / proxy-infra (push) Waiting to run
Unit Tests / proxy-server (push) Waiting to run
Unit Tests / responses-caching-types (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
* fix(rate_limiter): attach v3 priority rate limit headers on /v1/messages

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

* feat(playground): honor the Stream responses toggle for /v1/messages

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

* test(rate_limiter): drop explanatory docstrings from v3 dict response tests

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

---------

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-09 15:14:10 -07:00
tin-berri
c82c9cbced
fix(router): strip encrypted reasoning on an auto-router tier change instead of a 503 (#40280)
A Responses API follow-up that replays reasoning.encrypted_content is pinned to the
deployment that minted it. Behind an auto-router the pre-routing hook rebinds the model
to the tier it picked before the candidate pool is built, so a turn that classifies into a
different tier never finds the origin and the affinity check raised its fail-fast 503,
whose text claims a cooldown that does not exist

When the deployment that minted the reasoning is not a member of the model group this turn
is routed to, strip the encrypted reasoning (keeping any readable summary, string or block
form) and dispatch to the routed group. Membership is tested by deployment id against the
candidate set the router itself resolved for the route (routing group, model_name, team,
and pattern alike), not by model-group name, so an alias, a provider-qualified spelling, a
team-public name, or a pattern route of the same group is not misread as a tier change.
An unknown origin (a removed deployment, or a forged/unauthenticated marker) is handled the
same as a cross-group one and its reasoning is stripped, so a real cross-group id and a
nonexistent id return the same response and cannot be used to enumerate deployment ids.
Unavailability within the origin's own group keeps the existing 429/503 fail-fast, so the
cooldown contract is unchanged

Resolves LIT-7195


Claude-Session: https://claude.ai/code/session_01KAumQbhzk6jdWWHFLA8Jar

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 11:04:11 -07:00
mateo-berri
b5d40bef2b fix(token_counter): bound offloaded token counts with a dedicated capacity limiter
Replace the thread semaphore around HuggingFace encodes with an anyio CapacityLimiter
applied at every offloaded count site through offload_token_count, so waiting counts no
longer hold slots in the shared 40-thread pool and inline counts on the event loop never
block on the bound. Rename TOKEN_COUNTER_MAX_CONCURRENT_HF_ENCODES to
TOKEN_COUNTER_MAX_CONCURRENT_COUNTS
2026-09-08 20:25:04 -07:00
mateo-berri
4fa34accb8 Merge remote-tracking branch 'origin/litellm_internal_staging' into HEAD
# Conflicts:
#	litellm/litellm_core_utils/litellm_logging.py
2026-09-08 17:17:31 -07:00
devin-ai-integration[bot]
935c7190eb
fix(router): count allowed_fails in the shared router cache so multi-worker proxies bench a deployment fleet-wide (#40224)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-08 13:54:48 -07:00
tin-berri
4a3a78c256
feat(complexity_router): rebalance heuristic weights in the dashboard and grade custom dimensions by match count (#40205)
The Advanced scoring editor now lists built-in and custom dimensions together. Editing any weight holds it and rescales the others proportionally so the vector totals 1.00, and Save stores those explicit values. The backend scores exactly what is stored, with no runtime normalization, so routers nobody edits keep their weights.

CustomDimension gains an opt-in scoring_mode. match_count scores 0, 0.5 or 1 by distinct matcher hits; the default stays binary. The tuning fingerprint omits a binary scoring_mode, so routers written before this change keep their recorded baseline and the upgrade does not consume the free heuristic-v1 tuning slot.
2026-09-08 13:28:57 -07:00
Mateo Wang
f769aa4675
fix(router): give cooldowns their own cache so siblings see a bench in ~1s (#40025)
Cooldown entries rode the router-wide DualCache, which re-reads a key that is
missing from memory at most once every 10s. A deployment benched on one replica
therefore kept taking traffic on its siblings for up to 10 seconds, and the same
shared in-memory tier could evict a live cooldown once 200 unrelated router keys
crowded it out, which sent even the benching replica back to the dead deployment.

CooldownCache now owns a DualCache over the router's Redis with a 1s read
interval and an in-memory tier that only holds cooldown keys. Redis is attached
lazily because the router builds the cooldown cache before it wires Redis up.
2026-09-08 10:11:20 -07:00
AaronHowell
2cd28b97f1 fix(responses): align credential boundary resolution 2026-09-08 18:02:31 +08:00
AaronHowell
d5cf5640b0 fix(responses): preserve provider affinity
Co-authored-by: Bytechoreographer <Bytechoreographer@users.noreply.github.com>
2026-09-08 16:30:29 +08:00
mateo-berri
d202885f8b fix(proxy): run prompt caching counts and custom tokenizer loads off the event loop 2026-09-07 22:21:45 -07:00
mateo-berri
9666a21cf0 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_7022_azure_ai_passthrough_config 2026-09-07 21:57:00 -07:00
mateo-berri
83d500bac9 fix(router): count deployment itpm reservations off the event loop 2026-09-07 21:29:26 -07:00
tin-berri
1761fe236f
feat(complexity_router): add declarative custom dimensions to the heuristic scorer (#40156)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-09-07 18:17:30 -07:00
tin-berri
7da6fe54b5
fix: skip one-shot Claude Code cache injection (#40175) 2026-09-07 18:03:43 -07:00
mateo-berri
614b151365 chore: merge origin/litellm_internal_staging into litellm_lit_7022_azure_ai_passthrough_config 2026-09-07 16:43:12 -07:00
tin-berri
1ae3216120
fix(router): preserve default heuristic updates (#40007) 2026-09-07 12:05:43 -07:00
Mateo Wang
02522a5441
Merge pull request #39983 from BerriAI/litellm_lit_7081_azure_ai_gpt_6_astra_pricing
feat(cost-map): add azure_ai/gpt-6-astra Foundry pricing
2026-09-06 01:27:22 -07:00
mateo-berri
e79f3ec520 fix(cost-map): stop advertising reasoning_effort max on the azure gpt-6-astra rows
Both Azure routes refuse it. A live call to the same deployment through
openai/deployments/gpt-6-astra/chat/completions on api-version 2025-04-01-preview
answers reasoning_effort max with a 400 unsupported_value naming none, low, medium,
high and xhigh as the values it takes, and xhigh returns 200, so azure/gpt-6-astra
and azure/us/gpt-6-astra now match the azure_ai row.
2026-09-05 22:31:32 -07:00
tin-berri
9fd60e4f95
feat(router): gate heuristic v1 tuning (#39952) 2026-09-05 19:24:00 -07:00
Tin
a6a58b3e5d feat(router): add Switchyard capability classifier 2026-09-05 15:31:17 -07:00
devin-ai-integration[bot]
a670a4621e
fix(proxy): make the invalid-model 403 path cheap under a burst of rejections (#39892)
* fix(proxy): make the invalid-model 403 path cheap under a burst of rejections

Keep the wildcard pattern registry in specificity order at registration time
so route() no longer re-sorts every pattern per lookup, and reuse the
standardized failure payload across the async and threaded sync failure
handlers regardless of what a callback did to log_event_type. Rejections
are still logged and observable.

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

* style(router): wrap the filtered pattern tuple the way ruff format wants

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

* test(router,logging): assert registry order and callback awaits instead of patching a class

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

* test(router): inject the pattern sorter so the lookup test observes that route() never sorts

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

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-05 11:53:04 -07:00