Commit graph

8121 commits

Author SHA1 Message Date
Mateo Wang
1b77dbc71e
Merge pull request #37345 from BerriAI/litellm_lit_5720_openai_responses_prefix
fix(responses): strip the responses/ routing prefix on the Responses API path
2026-08-18 14:21:27 -07:00
Tianhe Zhang
607e4a4e30 feat(spend-logs): add lifecycle timestamps 2026-08-18 14:16:08 -07:00
mateo-berri
be594f5984 feat(guardrails): count bedrock guardrail cost against spend and budgets
Price ApplyGuardrail usage units recorded by PR #37225 with a new
bedrock/guardrails entry in the model cost map (regional override via
bedrock/{region}/guardrails), add the per-request guardrail_cost to the
standard logging payload's response_cost and CostBreakdown, surface it in
the x-litellm-response-cost header, and bill blocked requests through the
failure hook so key and team budgets see what AWS bills
2026-08-18 14:16:07 -07:00
Mateo Wang
f119a83adc
Merge pull request #36240 from BerriAI/litellm_fix_chained_proxy_file_upload
fix(router): forward target_model_names on file uploads to litellm_proxy deployments
2026-08-18 14:15:44 -07:00
Mateo Wang
d03ef8be03
Merge pull request #36246 from BerriAI/litellm_lit_5307_advisor_router
fix(advisor): resolve the advisor sub-call through the proxy router
2026-08-18 14:09:54 -07:00
Mateo Wang
840c5f680d
Merge pull request #37242 from bruno-olivia/litellm_fw_skip_prefix_rewrite
fix(fireworks): skip accounts/ rewrite for FW-* Foundry deployment ids
2026-08-18 14:09:49 -07:00
tin-berri
1f4acbb924
feat(complexity_router): custom classifier plugins via classifier_type 'custom' (#37249)
* feat(complexity_router): custom classifier plugins via classifier_type 'plugin'

Adds a third classification mode where an operator-supplied hook decides the
tier instead of the heuristic scorer or the LLM classifier. The hook implements
an async classify(context) returning a tier name (built-in value, tier_labels
label, or tier_definitions name) or None to decline; failures, timeouts, and
unknown tiers fall back exactly like a failed LLM classifier. The context
carries the request messages and metadata, including caller identity, so a
plugin can route by team, spend, or any business rule.

The plugin resolves from a dotted path at proxy startup with a load-time check
that classify is a coroutine function, and is closed off over HTTP like the
routing plugins list. Routing decisions record the new classifier_plugin cause.
tier_definitions now accepts classifier_type 'plugin' alongside 'llm'.

* fix(proxy): resolve plugin dotted paths in _delete_deployment before hashing ids

The db-sync reconcile re-reads the raw config and hashes litellm_params to
compute which ids the config wants served, but the router's ids were hashed
from the resolved params where plugin dotted paths are live instances. The
mismatched ids made the reconcile evict every plugin-bearing auto-router one
sync after startup, on any proxy with a database connected. This also affected
the existing routing plugins list, not just the new classifier plugin.

Resolving the plugins in _delete_deployment the same way load_config does makes
both sides hash the same canonical form. A plugin module broken on disk at
reconcile time skips cleanup instead of evicting valid deployments, matching
how a get_config failure is handled

* fix(complexity_router): treat non-string plugin verdicts as declines, centralize the empty-mapping sentinel

A hook returning a non-string raised inside resolve_classified_tier outside the
plugin exception boundary, failing the request instead of falling back. Also
moves the read-only empty mapping to constants.py per repo convention and moves
the classifier plugin product docs out of the package README for the docs repo

* refactor(complexity_router): rename the plugin classifier mode to classifier_type 'custom'

The mode value now names the operator's intent while classifier_plugin keeps
naming the mechanism; routing decisions keep the classifier_plugin cause

* refactor(proxy): pin plugin-bearing deployment ids from the raw params instead of resolving in the reconcile

Replaces the previous approach of re-running plugin resolution inside
_delete_deployment, which imported operator modules on every reconcile cycle
and skipped the whole cleanup pass when any one module was broken on disk.
load_config now stamps model_info.id from the raw litellm_params before
resolution swaps dotted paths for live instances, so the reconcile's raw-config
hash matches by construction and needs no resolution at all: a broken module
cannot stall cleanup for unrelated models, and any future param-transforming
resolution is covered by the same pin. _generate_model_id becomes a staticmethod
so the pin can run before the Router exists; its statically dead non-string key
branches are removed. Also documents candidate_models as an informational
snapshot for classifier plugins, unlike the narrowing surface RoutingPlugin
filters

* fix(router): restore _generate_model_id key handling, align classifier context with the routing-plugin pattern

The staticmethod conversion accidentally dropped the non-string-key branches
from _generate_model_id, a silent hash change for any params with non-string
keys; they are restored verbatim. The classifier plugin context now follows
the Router-level routing-plugin recipe exactly: structured messages come from
resolve_structured_messages over the raw messages, and the metadata key comes
from the shared get_metadata_variable_name_from_kwargs helper, which also
replaces the duplicated inline sniff in _pick_model_for_tier. This removes the
raw-or-resolved fallback where a plugin could silently receive resolved
messages when a call site forgot to pass the raw ones

* refactor(router): make generate_model_id public, guard classifier context construction

Two modules legitimately hash deployment ids with the same helper now (Router
and the proxy's config-load pin), so the private name was lying about its
audience and the cross-module call needed a pyright suppression; renaming it
public restores the static safety net. The classifier plugin's RoutingContext
construction moves inside the failure boundary, matching the LLM path where
litellm-side prompt building also falls back rather than failing the request,
and a prompt-only call with no message list is now covered by a test
2026-08-18 14:09:19 -07:00
mateo-berri
d2fbaff2c9 fix(proxy): record estimated input tokens in spend logs for dispatched failed requests
Failure rows in the spend log only carried token counts when a broken
stream stashed recovered partial usage; non-stream requests that reached
the provider and then failed (timeouts, provider 4xx/5xx) logged
0/0/0 even though the provider billed the input tokens. Estimate the
input side in post_call_failure_hook with the same tokenizer fallback
interrupted streams use, gated to requests that were actually dispatched
(first_api_call_start_time set and no litellm_no_upstream_llm_call
marker), and pin response_cost to 0.0 so failed requests never bill
spend. Recovered partial-stream usage still wins over the estimate.
2026-08-18 14:05:28 -07:00
mateo-berri
9b837fccde test: parameterize the bridge tool turn fixture return type 2026-08-18 14:05:21 -07:00
Mateo Wang
69be083146
Merge pull request #37346 from BerriAI/litellm_lit_5755_client_side_timeout
fix(proxy): stop leaking the client_side_timeout marker to providers
2026-08-18 14:01:45 -07:00
Mateo Wang
3324122f45
Merge pull request #34445 from ayaangazali/litellm_azure_ai_strip_non_openai_message_fields
fix(azure_ai): strip non-OpenAI-spec message fields before request
2026-08-18 14:00:21 -07:00
mateo-berri
f8cc26a51f test(anthropic): pin one content_block_stop per tool_use block on the Responses adapter 2026-08-18 13:57:23 -07:00
mateo-berri
ec35098108 fix(main): forward store and prompt_cache_key on the MCP gateway early-return 2026-08-18 13:57:07 -07:00
mateo-berri
c1c23bf39a fix(proxy): reserve measured input tokens for multimodal project ITPM
Image, file, video, and previous_response_id requests reserved the whole
project ITPM limit up front, so any window with existing usage rejected
them and one in-flight multimodal request blocked the entire project.
Reserve the token_counter estimate instead, like every other request;
post-call reconciliation already charges actual usage.
2026-08-18 13:51:47 -07:00
mateo-berri
4e8efa041d fix(advisor): exclude in-sequence system rows from the advisor sub-call context 2026-08-18 13:51:01 -07:00
mateo-berri
39c5ccaed4 test(responses): inject a mocked http client instead of patching AsyncHTTPHandler.post 2026-08-18 13:45:48 -07:00
mateo-berri
76a2340685 test(router): drop docstrings from the new chained-proxy file upload tests 2026-08-18 13:45:36 -07:00
mateo
ac17352594 fix(cost_calculator): recognize the ultrafast service tier in cost calculation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-18 20:43:55 +00:00
Mateo Wang
035bd76669
Merge pull request #33767 from BerriAI/litellm_lit_4561_bedrock_passthrough_content_type
fix(proxy): forward Bedrock event-stream content-type on unbuffered passthrough
2026-08-18 13:43:47 -07:00
mateo-berri
e3a93c40be fix(proxy): stop leaking the client_side_timeout marker to providers 2026-08-18 13:37:07 -07:00
mateo-berri
93f08ed938 refactor(advisor): collapse router resolution to is_recognized_model plus wildcard check 2026-08-18 13:36:29 -07:00
mateo-berri
61625723a7 fix(responses): strip the responses/ routing prefix on the Responses API path
openai/responses/<model> deployments reached OpenAI as the literal model id
responses/<model> on /v1/responses and on /v1/messages (which rides the
Responses API for the openai provider) and 400ed with model_not_found, while
/v1/chat/completions already stripped the prefix. Strip a leading responses/
right after provider resolution so every Responses API entrypoint (HTTP,
websocket, compaction, the /v1/messages adapter) sends the real model id
2026-08-18 13:28:22 -07:00
mateo-berri
6bbc45ddaa Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_chained_proxy_file_upload
# Conflicts:
#	litellm/router.py
2026-08-18 13:14:30 -07:00
mateo-berri
842bb7e917 Merge branch 'litellm_internal_staging' into feature/bedrock-mantle-quota-project-itr1 2026-08-18 13:14:14 -07:00
mateo-berri
7b60bd89c9 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_pr34445_local 2026-08-18 13:09:41 -07:00
mateo-berri
0c5fcde883 Revert "feat(key_management): let any authenticated user resolve a raw key via /key/info"
This reverts commit d0c1d2be8a.
2026-08-18 13:08:28 -07:00
mateo-berri
34db226dcc Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_pr32448_tinyfish_headers 2026-08-18 13:03:33 -07:00
mateo-berri
0e0768df9f Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_pr_33195_head
# Conflicts:
#	litellm/main.py
#	litellm/utils.py
#	tests/test_litellm/test_main.py
2026-08-18 13:03:04 -07:00
mateo-berri
addb77a9ea Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_5307_advisor_router 2026-08-18 13:02:24 -07:00
mateo-berri
791b478d77 fix(router): only resolve structured input when messages is absent
Keeps an explicit empty messages list on the pre-existing path (default
model, provider validation error) instead of dropping the routing
decision and surfacing a misleading tags 401.
2026-08-18 12:18:53 -07:00
mateo-berri
dd18389365 fix(router): route Responses API input through the auto-router
Auto-router strategy hook returned None whenever the request carried
input instead of messages, so tagged /v1/responses requests (Codex CLI)
never picked a tier and tag filtering left nothing to route to. Resolve
input through the shared prompt-template helper before matching routes.
2026-08-18 12:04:27 -07:00
ryan-crabbe-berri
9ec0145986
feat(proxy): add /team/daily/activity/aggregated and switch the Usage team tab to it (#36562)
* feat(proxy): add /team/daily/activity/aggregated and use it in the Usage UI

The Team Usage tab drained row-paginated pages client side, which painted
newest days first and drew duplicate bars when a day's rows straddled a
page boundary. Serve the whole range in one SQL GROUPING SETS pass instead:
the aggregated query gains optional per-entity rollup levels (entity as the
most-significant GROUPING bit) so breakdown.entities keeps per-team spend,
aliases, and per-key splits. The endpoint shares the paginated route's
scoping via _resolve_team_daily_activity_scope, accepts the timezone the UI
already sends, and the api_key filter now takes a list so non-admin member
scoping works. The dashboard tries the aggregated endpoint first and falls
back to page draining on failure.

* chore: ratchet B008 budget down by the endpoint converted to Annotated Depends

* chore: keep mutable-ok suppressions on their annotation lines after formatting

* fix(proxy): reject malformed or over-wide ranges on team aggregated activity

The aggregated endpoint has no pagination bounding its work, so validate
start_date and end_date as real dates and cap the span at 400 days. The
dashboard's widest presets fit well inside the cap, and an over-cap range
falls back to the paginated flow. Also trim implementation comments that
restated the grouping-set code.

* fix(proxy): parse aggregated range bounds as UTC to satisfy DTZ007

* refactor(proxy): fetch entity rollups with a companion query instead of extending the main one

The entity-as-extra-GROUPING-bit approach made the bitmask layout
mode-dependent: the same constant meant (date) for normal rows and
(date, entity) for entity rows, disambiguated by masking. Split it out:
the shared WHERE builder feeds both the untouched main query and a small
per-entity rollup query keyed by GROUPING(api_key), run concurrently, and
a fold writes breakdown.entities onto the built response.

* refactor(proxy): share the daily-activity error and entity-metadata shapes

The type-discipline ceiling for LIT002 ratcheted down on staging, so the new
aggregated endpoint had to stop hand-rolling collections the codebase already
builds elsewhere. Funnel the `{"error": ...}` detail through one construction
site, turn the range validator into an error-as-value, reuse a single
entity-metadata lookup for both breakdown paths, and widen
get_api_key_metadata to any set so callers stop copying frozensets.
2026-08-18 11:29:57 -07:00
tin-berri
8159f240c4
feat(complexity_router): plan-mode tier floor for coding-agent clients (#37230)
* feat(complexity_router): plan-mode tier floor for coding-agent clients

Claude Code and Copilot signal plan mode only through client-injected prompt
text, which the ask-extraction path deliberately strips, so the router could
never see it. Detect the sentinels on the raw wire body and route those
requests to at least plan_mode_min_tier.

The floor is raise-only and transient: classifier results above it still win,
it overrides a session-affinity pin only on turns carrying the sentinel
without rewriting the pin, and plan_mode decisions are not pinnable, so the
first turn after plan mode exits routes as if plan mode had never happened.
Classification is skipped when the floor is the top configured tier. On
adaptive routers the floor rides _soft_floor_pick as a hard_floor that
excludes below-floor candidates, closing the adaptive_eligible=all gap where
a request classified at or above the floor could still route below it.
Detection is staleness-aware: only leading system content and the newest-ask
tail count, so sentinels surviving in history after plan mode exits, built-in
or operator-supplied, never fire. Custom tier sets are supported with
severity from the tier_definitions list order, same as keyword_tier_rules.
Off by default; decisions are recorded with the new plan_mode cause and the
matched sentinel in matched_keyword

* fix(complexity_router): gate pin writes and the failure exit on sentinel presence, not the floor binding

A plan-mode turn classified at or above the floor keeps its ordinary cause,
but pinning it would carry a plan-mode-shaped choice past plan mode's exit
(on adaptive routers the hard floor constrained that pick), so no
sentinel-carrying turn writes the session pin. The default_model failure
exit is skipped for sentinel turns for the same reason: default_model's
placeholder tier can equal the floor while default_model itself sits in no
pool the floor can vouch for
2026-08-18 10:24:11 -07:00
Mateo Wang
4fd7a73ef5
Merge pull request #36599 from daniel-meismer-zocdoc/bugfix/mcp-oauth-startup-discovery
fix(mcp): oauth discovery must not cause outages
2026-08-17 22:32:00 -07:00
Mateo Wang
333ccf244c
Merge pull request #37247 from BerriAI/litellm_guardrail_usage_retry_safe_errors
fix(guardrails): retry usage upserts only on connection errors
2026-08-17 21:01:26 -07:00
yucheng-berri
a738c45fc7
fix(proxy): strip callback credentials from the auth object stamped into request metadata (#37233)
Some checks failed
Publish basedpyright base counts / publish (push) Waiting to run
Code Quality Checks / code-quality (push) Waiting to run
UI Unit Tests / ui-unit-tests (push) Waiting to run
Unit Tests: Core Utilities / core-utils (push) Waiting to run
Unit Tests: Documentation Validation / documentation (push) Waiting to run
Unit Tests: Enterprise, Google GenAI & Routing / enterprise-routing (push) Waiting to run
Unit Tests: Integrations (Callbacks & Logging) / integrations (push) Waiting to run
Unit Tests: LLM Provider Transformations / All Other Providers (push) Waiting to run
Unit Tests: LLM Provider Transformations / Vertex AI (push) Waiting to run
Unit Tests: MCP, Secrets, Containers & Misc / misc (push) Waiting to run
Unit Tests: Proxy Auth & Key Management / proxy-auth (push) Waiting to run
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 / proxy-utils (push) Blocked by required conditions
Unit Tests: Proxy API Endpoints / proxy-endpoints (push) Waiting to run
Unit Tests: Proxy API Endpoints / proxy-server (push) Waiting to run
Unit Tests: Proxy Infrastructure / proxy-infra (push) Waiting to run
Unit Tests: Responses, Caching & Types / responses-caching-types (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
* fix(proxy): strip callback credentials from the auth object stamped into request metadata

* style(proxy): drop the restating half of the stamp-site comment

* test(proxy): pin that the stamped auth copy carries header-derived identity
2026-08-17 19:50:05 -07:00
mateo-berri
15823b1be3 fix(guardrails): degrade usage units to empty when the units table is missing
GET /guardrails/usage/overview and GET /guardrails/usage/detail/{id} 500ed on a
database that has not applied 20260817143646_add_daily_guardrail_usage_units yet
(pip installs on litellm-proxy-extras 0.4.86 with DISABLE_SCHEMA_UPDATE=true).
Both endpoints now return their metrics with empty units and log one warning
until the migration lands.
2026-08-17 19:38:43 -07:00
mateo-berri
35176fa64a fix(guardrails): retry usage upserts only on connection errors
The daily guardrail metrics and usage-unit upserts are non-idempotent
increments, but the retry loop re-sent every failed row on any exception.
An ambiguous post-send failure such as a read timeout after the write had
already committed therefore stacked a second increment and inflated the
billable unit totals served by the guardrail usage endpoints.

Retry only DB_RETRY_SAFE_ERROR_TYPES (httpx.ConnectError), the same rule
the spend writer and autorouter rollup use for increment upserts, and log
any other failure once as terminal for that row while the rest of the
batch still lands.

Follows up #37225
2026-08-17 19:05:50 -07:00
Mateo Wang
4d57bf0bdd
Merge pull request #37225 from BerriAI/litellm_lit5650_guardrail_usage_units
feat(guardrails): track bedrock guardrail usage units per invocation
2026-08-17 18:55:22 -07:00
Mateo Wang
b69068c290
Merge pull request #26900 from BerriAI/litellm_model-deprecation-alerts-55bc
feat(proxy): proactive model deprecation alerts and `/model/deprecations` endpoint
2026-08-17 18:15:20 -07:00
tin-berri
b20314efcf
fix(shadow_eval): schema-constrain the judge verdict like the classifier (#37239) 2026-08-17 18:12:07 -07:00
mateo-berri
7017df5732 fix(alerting): back off a day after a deprecation pass raises and label the alert in the UI
A pass that raises (a missing Slack webhook, say) now waits the daily interval instead of logging the
same exception every 30 seconds, and the Admin UI alerting settings list the new alert type so it can be
toggled like the others
2026-08-17 17:57:37 -07:00
tin-berri
e2d8fc919f
feat(complexity_router): operator-defined tier sets for the LLM classifier (#37226) 2026-08-17 17:57:30 -07:00
ryan-crabbe-berri
6e9a3b50c3
test(cli): use example.com placeholder host in base-url trailing slash test (#37240)
The trailing-slash normalization test used gateway.litellm-sandbox.ai as
its base URL. Swap it for gateway.example.com so the test file does not
reference a real-looking hostname. The test is fully mocked, so the host
value has no effect on what is exercised.

Co-authored-by: yuneng-jiang <yuneng@berri.ai>
2026-08-18 00:52:10 +00:00
Bruno Felthes
dc54b16d3c
fix(fireworks): skip accounts/ rewrite for FW-* Foundry deployment ids
resolve_fireworks_resource_name prefixes bare names with
accounts/fireworks/models/ (or routers/ for *-fast). Azure AI Foundry
hosts Fireworks models under deployment ids like FW-Kimi-K3; rewriting
those yields 404 DeploymentNotFound.

Leave names that already start with FW- unchanged. Native Fireworks
short names still get the accounts/ path.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 21:43:24 -03:00
mateo-berri
ae23bf85d2 fix(guardrails): retry failed daily metrics and usage unit upserts with backoff
A transient DB error during the spend log flush dropped that batch's guardrail
metrics and usage unit rows for good. Retry only the rows that failed, up to 3
times with 1s/2s/4s backoff, mirroring the daily spend writer, and inject the
sleep so tests stay fast. Lowers the lint budgets the refactor freed up
2026-08-17 17:41:57 -07:00
mateo-berri
308865bad0 fix(alerting): claim the deprecation lock only with content and retry failed claims next poll
An empty pass no longer holds the daily lock, a False lock claim (held or redis
error) is retried on the next 30 second poll instead of sleeping a day, and a
sent alert is stamped in the shared cache for a day so sibling pods and restarts
stay quiet
2026-08-17 17:38:05 -07:00
Mateo Wang
9cd7696156
Merge pull request #37229 from BerriAI/litellm_comprehend_medical_passthrough
feat(proxy): add Amazon Comprehend Medical passthrough provider
2026-08-17 17:25:01 -07:00
mateo-berri
8ba2263d4c perf(guardrails): aggregate usage units in one sorted pass
The flush and the usage endpoints summed units with a scan per distinct key,
quadratic in rows times keys; group sorted rows instead. Skip payloads without
a request_id like the metrics path, type the flush key as a NamedTuple, and drop
the (guardrail_id, date) index that the primary key already covers
2026-08-17 17:19:05 -07:00
tin-berri
5277dab4f2
fix(shadow_eval): copy messages before router call and raise judge output cap (#37232)
* fix(shadow_eval): copy messages before router call and raise judge output cap

* fix(shadow_eval): lead failure detail with location and pin post-failure continuation
2026-08-17 17:10:52 -07:00