Commit graph

48834 commits

Author SHA1 Message Date
mateo-berri
0c2d0f4777 fix(spend-tracking): forget an empty spend-log lookup after thirty seconds
The daily spend rows and the spend logs of one batch are written a moment apart, so a usage read landing between them used to remember the session as nameless for ten minutes on that worker. Found identities keep the ten minute entry
2026-09-08 13:28:13 -07:00
mateo-berri
071f83980c refactor(proxy): type the post_call deferral helpers' request-state parameters 2026-09-08 13:26:17 -07:00
Yuneng Jiang
9d164fa341
bump: litellm-proxy-extras 0.4.94 -> 0.4.95, litellm 1.101.0 -> 1.102.0 2026-09-08 13:24:15 -07:00
mateo-berri
89e11949c8 fix(guardrails): key Responses stream tool-call rewrites by call_id
Bridged Responses streams give reasoning and message items output_index 0
and start function calls at 1, so keying rewrites by output_index rewrote
the wrong items. Rewrites now follow each function call's call_id through
the buffered item and argument events, refuse when an event cannot be
resolved to a rewritten call, and the refusal branches on all three
handlers get regression tests
2026-09-08 13:22:30 -07:00
mateo-berri
f59354d09b refactor(guardrails): patch Anthropic SSE rewrites by field
The ended-stream rewriters now describe the one field they change as a
frozen _SSEFieldRewrite and a single applier builds the patched event, so
the handler adds no mutable-collection constructions over staging's total
2026-09-08 13:22:29 -07:00
mateo-berri
f4aef5a1db fix(spend-tracking): make the spend-log metadata cache limits plain constants
The ten minute TTL and the 10000 item ceiling need no env override, and the documentation env-key check flags any os.getenv read that the docs do not list
2026-09-08 13:19:35 -07:00
moe-berri
a5cfe625e3 refactor: trim the comments this PR added
Cuts the explanatory comments and docstrings added here down to one line each, or
removes them where the code already says it. Restores the four pre-existing
docstrings this PR had reworded to their original text; the one remaining edit to
existing text is TierDefinition.description, whose hardcoded tier list would
otherwise misstate that a tier named NON_REASONING may also omit its description.
2026-09-08 13:14:52 -07:00
mateo-berri
8ce3fe4fcf style: give the header slot rebuilds and the in-place slot write their lint reasons 2026-09-08 13:13:36 -07:00
mateo-berri
720f2ca775 fix(proxy): label a 408 invalid_request_error again and pin the in-route status on the files and realtime tails 2026-09-08 13:11:24 -07:00
mateo-berri
550e733bf3 perf(spend-tracking): recover key identity in one bounded spend-log pass with a per-worker cache
The spend-log lookup for permanently unresolvable digests is back to a single DISTINCT ON scan over the requested window, keeping only rows that carry an alias, user, or team so a newer nameless row cannot hide an older named one. Results and misses are cached per worker for ten minutes keyed by digest and window, failed queries are not cached, and JWT rows keyed hashed-jwt-<sha256> now pass the digest gate. Tests cover the JWT gate, cache reuse and partial misses, window changes, error handling, the with-window guard, and the daily activity wiring
2026-09-08 13:09:43 -07:00
devin-ai-integration[bot]
6a425a5cc5
fix(responses): record spend for native Responses API WebSocket sessions (#38856)
* fix(responses): record spend for native Responses API WebSocket sessions

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

* fix(responses): bill usage from response.incomplete WebSocket turns

A turn cut short by max_output_tokens ends in response.incomplete, which
OpenAI bills but the processor only read response.completed, so those
sessions still logged zero spend

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

* test(responses): hoist websocket usage test imports to module scope

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

* fix(responses): price websocket sessions through the standard cost path

The realtime completion_cost branch skips cost_discount_config and cost_margin_config, so a native
Responses WebSocket session was priced differently from the same usage over HTTP /v1/responses.
Drop the explicit widening so the LiteLLMRealtimeStreamLoggingObject built by
normalize_logging_result flows through the generic usage path, and pin WS == HTTP cost under a
50% provider discount in the regression test

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

* ci: rerun proxy-infra after flaky test_check_migration process tree test

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yucheng <yucheng@berri.ai>
2026-09-08 13:08:26 -07:00
devin-ai-integration[bot]
d36e032241
fix(proxy): initialize string success/failure callbacks at startup after config load (#38226)
* fix(proxy): eagerly initialize string callbacks

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

* fix(proxy): materialize string callbacks after load_config so later litellm_settings keys are applied

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

* fix(proxy): drop casts when snapshotting string callbacks so LIT006 stays at base

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yucheng <yucheng@berri.ai>
2026-09-08 13:00:14 -07:00
mateo-berri
91091fd93e fix: withdraw policy header claims while a background response is pending and log ungoverned retrievals 2026-09-08 12:59:58 -07:00
moe-berri
fbd8fa8d3e refactor: tighten the comments added for the non-reasoning tier
Review flagged the added comments as over-explaining. Cut the call-site comment
that restated the helper's own docstring, and shortened the rest to the fact the
code cannot state itself: why the constant excludes the tier, why the flag is
cleared on a classifier change, and why the edit modal reads both keys back.
2026-09-08 12:59:07 -07:00
mateo-berri
384eee26a5 fix(bedrock): reject s3 Marengo media without bucketOwner and skip items without an embedding 2026-09-08 12:53:01 -07:00
Mateo Wang
77bfeb5708
Merge pull request #40269 from BerriAI/litellm_lit7212_responses_bridge_keep_system_in_input
fix(responses bridge): keep mid-conversation system messages in input instead of folding them into instructions
2026-09-08 12:52:33 -07:00
mateo-berri
5386ba37ff Merge remote-tracking branch 'origin/litellm_fix_post_call_policy_pipeline' into litellm_post_call_pipeline_background_responses
# Conflicts:
#	tests/test_litellm/proxy/test_common_request_processing.py
2026-09-08 12:48:32 -07:00
mateo-berri
61ab4307ec test(cost_map): assert provenance without patching module state 2026-09-08 12:47:31 -07:00
mateo-berri
6bfcfdbc50 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_bedrock_sign_request_off_loop
# Conflicts:
#	tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py
2026-09-08 12:45:27 -07:00
mateo-berri
5e056a264e refactor(azure): move the passthrough deployment-segment helpers under llms
Some checks failed
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Waiting to run
Terraform Modules / fmt, validate, test (gcp) (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
2026-09-08 12:45:05 -07:00
mateo
8eb6f6d835 test(cost_calc): type web search test params and drop docstrings
Some checks failed
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Waiting to run
Terraform Modules / fmt, validate, test (gcp) (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-08 19:43:32 +00:00
mateo-berri
cf0488316e fix(spend-tracking): take each unresolved key's newest spend row and drop docstrings 2026-09-08 12:40:28 -07:00
mateo-berri
3fbcb422ee Merge remote-tracking branch 'origin/litellm_fix_post_call_policy_pipeline' into litellm_lit_7174_stream_tool_call_rewrites 2026-09-08 12:39:56 -07:00
mateo-berri
fe836e830d Merge remote-tracking branch 'origin/litellm_fix_post_call_policy_pipeline' into litellm_legacy_hook_streaming_pipeline_step 2026-09-08 12:39:55 -07:00
mateo-berri
3f30c05493 fix(guardrails): skip only stream-gated pipeline guardrails in the deferred post-call pass 2026-09-08 12:39:39 -07:00
moe-berri
6a950df549 fix(ui): put the non-reasoning switch above the tier rows
The switch adds a row at the top of the tier list, so sitting below Reasoning
put the control and the thing it changes at opposite ends of the card. It now
heads the list, with a separator between it and the first row.
2026-09-08 12:37:34 -07:00
mateo-berri
cecd481ae3 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_post_call_policy_pipeline 2026-09-08 12:36:13 -07:00
moe-berri
91f1d98fb0 fix(auto_router): clear the non-reasoning tier when the classifier changes
Three review findings, all in the dashboard.

Switching off the LLM classifier left the toggle checked but disabled, so the
flag could not be cleared and every save was refused by the backend. The
classifier-change handler now drops the flag and the tier's pool the same way it
already drops the other classifier-specific keys.

builtInTierInfo resolved rows against the four-tier order, so the new row
rendered with no description, no examples and no rename field. It now resolves
against every built-in tier, and the duplicate BUILT_IN_TIER_ORDER constant is
gone in favour of the one in tier_rows.

The preset schema widening is reverted. It was speculative, no published catalog
carries the tier, and prefill would have discarded it while the route-wide null
exclusion changed the endpoint's passthrough contract for every other field.

Also splits NonReasoningTierToggle and TierConfigIntro into their own files to
get ComplexityRouterConfig.tsx back under the max-lines limit, and applies ruff
format to the two backend files CI flagged.
2026-09-08 12:30:42 -07:00
mateo-berri
2823d09e1b refactor(policy_engine): type the request-state parameter of the retrieval hook 2026-09-08 12:30:05 -07:00
mateo
d497434680 fix(model_prices): update provider and web search pricing
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-08 19:28:04 +00:00
mateo
bd2a35514d Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_add-claude-sonnet-5-pricing 2026-09-08 19:23:06 +00:00
mateo-berri
db59e99932 fix(azure): let the caller's api-version override a full-URL api_base on relays 2026-09-08 12:21:34 -07:00
mateo-berri
946d6f665e test(cost_map): assert runtime reloads refresh loaded_at 2026-09-08 12:18:26 -07:00
yujonglee
01c68c199b
fix(guardrails): allow framework-supported logging-only mode (#40267) 2026-09-08 12:18:09 -07:00
mateo-berri
8b89c909a9 fix(proxy): keep a ProxyException's status and label 408s in the OpenAI error payload
error_status_code only read status_code, so a ProxyException raised
before routing (which stores its status as the string code) answered
500 with its 4xx type through the rerank, images, realtime, files, and
pass-through tails. It now falls back to a decimal code. A 408 maps to
timeout_error instead of invalid_request_error.

Tail regressions for rerank, images, realtime calls, and the chat
pass-through fail at the merge base with ('None', 'None'); the new
files-test helpers are fully typed.
2026-09-08 12:16:08 -07:00
mateo-berri
da2e1ff619 fix(fireworks): fold system and developer items into instructions on the responses path
Fireworks renders a Responses request through a chat template that only
accepts a system message at the very beginning, so a request carrying
`instructions`, a developer item, and a replayed reasoning item (the shape
Codex CLI sends from its second prompt on) came back 400 with "System
message must be at the beginning".

The leading system or developer items, and any developer item later in
the conversation, now fold their text into top-level `instructions`,
joined with blank lines, and leave `input`. A developer item that closes
the conversation right after an assistant turn stays where it is as a
system item, as does any system or developer item with an image or file
part, so those parts still reach Fireworks. Mid-conversation system items
stay untouched. Non-string `instructions` pass through unchanged.

Folding into `instructions` rather than a leading system item keeps
`previous_response_id` chaining working, since Fireworks prepends the
stored history to `input` and a leading system item would land after it.
This supersedes the leading system item approach from deaadc21d3 and
4807630c2a on this branch. The leading and closing block rules match the
chat path change in #39852.
2026-09-08 12:12:34 -07:00
yujonglee
35d1d40a67
fix(ocr): run post-call logging hooks (#40154) 2026-09-08 12:07:25 -07:00
moe-berri
629b464cd6 feat(auto_router): opt-in NON_REASONING tier below SIMPLE
Agent harnesses send a lot of operational turns that relay or reformat tool
output rather than reason about it, and the cheapest built-in tier was SIMPLE.
NON_REASONING adds a rung below it, behind enable_non_reasoning_tier so an
already-deployed router cannot move.

The toggle is what keeps it safe. The tier set feeds the classifier rubric, the
response-format enum, the escalation ladder and the savings baseline, so a
default-on fifth tier would have changed what every existing router sends and
where its traffic lands. Off, the ladder, rubric, wire labels and baseline are
byte-identical to before. On, the rung is added at index 0, escalation walks up
out of it, and it can never win the savings baseline.

It requires an llm or custom classifier and a model of its own: the v1 score
ladder has no rung below simple_medium and the v2 artifact is trained on four
classes, so the heuristic scorers cannot produce the tier and a router that
enabled it there would pay for a bullet nothing reaches.

The dashboard follows the same flag, and the edit modal now reads the tier back
from the stored config rather than assuming four keys, since it rewrites tiers
wholesale on save and would otherwise delete a hand-written tier on any edit.
2026-09-08 12:06:52 -07:00
mateo-berri
c6f5763443 feat(guardrails): run legacy post-call hooks as streaming pipeline steps
A post_call pipeline step whose guardrail only implements the older
async_post_call_success_hook used to skip the stream entirely: PR #38721
fails that shape open with a warning. The streaming step now assembles the
buffered stream into the response the hook expects, runs the hook, ends the
stream with the hook's exception when it raises, and delivers the hook's
rewrite through the same event write-back the unified guardrails use on
chat, Responses, and Messages streams (Messages gets the Anthropic shape).
A stream a pipeline manages no longer runs the same hook again after the
stream ends. A guardrail with neither the unified interface nor a post-call
hook keeps the fail-open, as does a rewrite the buffer cannot be patched
with.
2026-09-08 12:04:52 -07:00
mateo-berri
8c878f3686 Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_lit6852_spend_attribution 2026-09-08 12:02:50 -07:00
mateo-berri
2285640eea fix(spend-tracking): recover key alias for session tokens from spend logs
CLI session tokens are in-memory only and never get a LiteLLM_VerificationToken
row, so the usage APIs could not resolve key_alias, team_id, or user_email for
their spend rows: the exact join and the reverse-hash recovery both miss. The
owner is written to LiteLLM_SpendLogs.metadata at request time under the same
hashed api_key, so read it back from there for keys still unresolved after the
token-table passes.

The lookup is sha256-gated like the existing reverse-hash recovery and bounded
to the records' startTime window (min date minus one day, max date plus two) so
it stays on the startTime index. No migration.

Also guard the window parser against the date=None rollup rows GROUPING SETS
aggregation emits, which raised TypeError from strptime and turned the
aggregated usage endpoints into HTTP 500s.
2026-09-08 12:02:39 -07:00
mateo-berri
3caa3b60d5 feat(guardrails): run post_call policy pipelines on background Responses retrieval
A POST /v1/responses with background: true returns a queued response, so the
post_call pipelines attached at submit time had nothing to inspect. They now
defer on queued and in_progress responses and run on GET /v1/responses/{id}
instead: the retrieval resolves the response id back to its deployment,
re-attaches the policies that governed the original model, and reports them
in the x-litellm-applied-* headers of the retrieval response.
2026-09-08 11:58:18 -07:00
mateo-berri
89c3a8216b fix(bedrock): sign requests off the event loop on every async path
SigV4 signing resolves AWS credentials, and botocore refreshes expiring
credentials inside that signing with a blocking HTTP call. Every async
Bedrock path that still signed on the event loop (/v1/messages, Converse,
count tokens, the agent-runtime and Comprehend Medical pass-throughs,
async-invoke status polling, realtime, AgentCore, SQS, S3) now signs on a
worker thread, so one Bedrock request no longer stalls the whole worker.

Fixes #40165
2026-09-08 11:54:43 -07:00
mateo-berri
4807630c2a fix(fireworks_ai): keep non-text system and developer parts on the folded leading system message 2026-09-08 11:48:01 -07:00
devin-ai-integration[bot]
ee1a6407cb
fix(guardrails): keep guardrail telemetry when a policy pipeline blocks or modifies the response (#40211)
* fix(guardrails): keep guardrail telemetry when a policy pipeline blocks or modifies the response

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

* fix(guardrails): count every raw-snapshot guardrail evaluation and type the telemetry carry helpers

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

* test(policy_engine): type the recording guardrail hooks and telemetry test parameters

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-08 11:45:02 -07:00
mateo-berri
3448175184 fix(responses bridge): keep mid-conversation system messages in input
Only the leading run of system messages (before the first non-system
message) is joined into the Responses `instructions` field. A system
message that arrives after a user, assistant, or tool turn now becomes a
system input item at its position, whether its content is a string or a
list, so a client that re-sends the same reminder as a string on the
next request produces byte-identical input and `instructions` stays
stable. Claude Code >= 2.1.237 appends such reminders after every user
turn, and folding them into `instructions` made Azure treat every
request as a cold prompt (cached_tokens 0 on every request of a
session).

Fixes #40198
2026-09-08 11:44:49 -07:00
mateo-berri
e65c56876f fix(guardrails): write tool-call rewrites into typed Responses envelopes
The response.completed envelope carries its function_call output items as
SDK objects without a get shim, so the write-back skipped them and the
envelope still showed the original arguments after every stream event had
been rewritten. Write the item whenever one is present, and cover the typed
event shape the live proxy carries in the handler test.
2026-09-08 11:42:51 -07:00
mateo-berri
cbe340a31c feat(guardrails): deliver tool-call rewrites into buffered streams
A post_call pipeline guardrail that rewrites a streamed tool call (its
arguments or its name) now has that rewrite written back across the buffered
chunks on chat, Responses, and Messages streams, so the client receives the
rewritten tool call instead of the original. The chat handler rewrites the
first fragment of each tool-call index and blanks the rest, the Responses
handler syncs the function_call output items and their argument events, and
the Messages handler rewrites the tool_use content_block_start and
input_json_delta events in both dict and SSE-bytes chunks.

The delivers_ended_stream_text_rewrites flag becomes
delivers_ended_stream_rewrites, since the write-back now covers both text and
tool calls, and the executor only discards a tool-call rewrite on translations
without write-back or on a shape the translation refuses.
2026-09-08 11:38:18 -07:00
mateo-berri
423499c519 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_lit_2133_cost_map_provenance 2026-09-08 11:17:25 -07:00
mateo-berri
41c0897c7a refactor(cost_map): drop docstrings from the provenance helpers and their tests 2026-09-08 11:14:41 -07:00