Commit graph

13596 commits

Author SHA1 Message Date
Mateo Wang
0acca3e86a
Merge pull request #24548 from mpcusack-altos/fix/bedrock-batch-credential-fields
fix(router): include Bedrock batch/S3 fields and model in deployment credentials
2026-08-05 22:39:39 -07:00
tin-berri
86890654c5
fix(proxy): include today's UTC bucket when a daily activity range ends at the caller's current day (#36051)
* fix(proxy): include today's UTC bucket when a daily activity range ends at the caller's current day

* fix(proxy): gate the current-UTC-day extension behind an opt-in param sent by the cost optimization dashboard

* fix(ui): label cost optimization savings dates as UTC days
2026-08-05 22:33:54 -07:00
Devin AI
0c5583b83f fix(google_genai): price streamed generateContent with the provider that served it
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-06 05:18:49 +00:00
Michael Cusack
3d275d97fe fix(router): return model and Bedrock batch fields in deployment credentials
get_deployment_credentials_with_provider dropped s3_region_name,
s3_encryption_key_id, and aws_batch_role_arn because
CredentialLiteLLMParams never declared them, and it never returned the
deployment's model, so proxy batch creation against Bedrock failed with
"LiteLLM doesn't support custom_llm_provider=bedrock for 'create_batch'"
or "AWS IAM role ARN is required" (#25104)

Provider-only file and batch calls keep their no-model contract:
get_team_provider_credentials strips the model key so a provider-scoped
request is not pinned to an arbitrary matching deployment
2026-08-05 21:49:31 -07:00
mateo-berri
e2cb01c87c
fix(types): correct annotations that were false about their runtime values
An adversarial review of the previous commit found annotations that
described what the code wished were true rather than what flows through.
A false annotation is worse than the Any it replaced, since it launders a
wrong assumption past the type checker.

- purview: `_resolve_user_id` claimed every request-body value was a
  Mapping, contradicting `_resolve_trusted_user_id` one method over, which
  types the same argument `Mapping[str, object]`. `_should_block` claimed
  every Graph response value was a sequence of str->str mappings and was
  not assignable from its own producer's return type.
- cato: `_CatoAnalyzeResponse.required_action` was required and
  non-nullable while the API returns null, as seven fixtures in the
  guardrail's own suite assert. `analysis_result` had the same problem.
  The streaming hook narrowed an override parameter below what
  `ProxyLogging` actually passes it.
- marketplace: `_PluginRecord.manifest_json` was `str` against a nullable
  column. Making it honest surfaced a latent crash, covered below.
- ownership: two functions took an attribute Protocol while their own
  bodies branch on `isinstance(response, dict)`, which no Protocol can
  satisfy.
- openapi generator: `paths` claimed every path-item value was an
  operation, though path items also carry `parameters`, `summary` and
  `$ref`.
- custom openapi spec: a TypedDict asserted a shape that the function
  returns raw Pydantic sub-schemas out of. Reverted to Any, which is
  imprecise but not false.

`get_marketplace` did an unguarded `json.loads` on the nullable
`manifest_json` inside an `except json.JSONDecodeError`, which cannot
catch the TypeError a NULL raises, so one NULL row 500s the endpoint. It
now skips the plugin like the file's other two read sites already do, with
a regression test that fails without the guard.

Where honesty cost precision, precision lost. `_should_block` went back to
its original signature entirely: the narrowing needed to type it turned a
fail-closed DLP control fail-open, because the TypeError it used to raise
on a malformed response reached `except Exception` and became a 400.
2026-08-06 04:37:48 +00:00
mateo-berri
fa47c47020 fix(lint): measure the basedpyright budget gate in a gate-owned venv
The gate previously measured whatever environment the caller happened to
have. Locally that is the fat bootstrap venv (--extra proxy pulls in
fastapi-sso, whose type info flips a reportUnnecessaryIsInstance
diagnostic in ui_sso.py), while CI's publisher venv only has the
proxy-dev and e2e-dev groups, so identical trees measured 866 locally vs
865 in CI and every local gate run breached by a phantom +1

scripts/type_check_gate.py now provisions .venv-typecheck itself: a
frozen uv sync of the canonical proxy-dev and e2e-dev groups, the
interpreter pinned to pyrightconfig.json's pythonVersion, plus the
generated Prisma client. Every measurement pass is pinned to that env
with --pythonpath, because basedpyright auto-detects a .venv in the
project root and that auto-detection beats both PATH order and
VIRTUAL_ENV, so the CLI flag is the only pin that actually works. The
dependency-group set is folded into the environment fingerprint, so
artifacts or caches recorded under a different group set never match
and the gate falls back to computing base counts locally instead of
comparing mismatched environments

The publisher workflow drops its own install and prisma steps and lets
the script build the measurement env, and the node heap for the
full-tree pass drops from 12GB to 8GB (peak RSS measured at 5.4GB)
2026-08-05 21:33:24 -07:00
Mateo Wang
ba91768146
Merge pull request #35925 from BerriAI/litellm_tier_aware_reasoning_token_cost
fix(cost): bill reasoning tokens at the service tier output rate
2026-08-05 21:09:55 -07:00
Mateo Wang
b45b4b7300
Merge pull request #35923 from BerriAI/litellm_dated_variant_tier_pricing_sync
fix(pricing): sync flex/priority tier keys to dated OpenAI snapshot variants
2026-08-05 21:09:37 -07:00
tin-berri
7c621b3141
fix(auto-router): accept every reminder marker pair a harness emits (#36029)
* fix(auto-router): accept every reminder marker pair a harness emits

reminder_markers held one (open, close) pair, so a harness that wraps
injected context differently per agent type only got the slice of traffic
using the configured envelope stripped. Every other agent type kept hitting
the original bug: its reminder-only turn never stripped to empty, won
"newest human ask", and the harness blob got classified in place of the
real question, choosing the tier and therefore the spend.

The field now takes a list of ReminderMarkerPair, following the
KeywordTierRule pattern already in this file so each pair validates itself
and errors point at reminder_markers.N.close rather than a bare index.

Blocks from different pairs can nest, which the gap construction could not
handle: resuming the kept text at an inner block's end walks back inside
the enclosing block and leaks its remainder. Running the block ends through
a maximum collapses nested and overlapping spans without a separate merge
pass, and stays linear in block count, which a fold over a growing tuple
of merged spans would not.

A single pair's ends already increase, so the maximum is the identity and
the default path is byte-identical: verified against the shipped function
over 200k generated inputs, and every existing reminder test passes
unchanged. The prior single-pair config shape is rejected loudly at
startup and at /model/new rather than silently stripping nothing.

* docs(auto-router): document reminder_markers in the complexity router README

* chore(ui): regenerate dashboard API types for the reminder_markers shape

---------

Co-authored-by: Abhimanyu Kapur <38531241+akapur99@users.noreply.github.com>
2026-08-05 21:03:36 -07:00
devin-ai-integration[bot]
0bae9708a7
fix(arize_phoenix): lowercase OTLP/gRPC auth metadata key (#34883) 2026-08-05 20:57:50 -07:00
mateo-berri
c2998dea75 fix(guardrails): guard tools write-back under scan_only_tool_results and warn on role-filtered no-op scans 2026-08-05 20:49:15 -07:00
mateo-berri
7d00f9d019 fix(managed_files): return unified output file ids from GET /batches
list_user_batches parsed each stored batch blob and returned it as-is, so any
row whose blob still carried raw provider file ids (for example a batch that
reached a terminal state through the cost poller, or rows written before
output registration existed) leaked raw output_file_id and error_file_id
values that clients cannot fetch through the proxy. The list path now runs
each row through ensure_batch_response_managed_file_ids, which swaps in
existing managed ids and registers missing ones under the batch owner's
identity, matching what GET /batches/{id} already does
2026-08-05 20:46:00 -07:00
mateo-berri
1b30b1bc20 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_tier_aware_reasoning_token_cost
# Conflicts:
#	litellm/types/utils.py
2026-08-05 20:45:34 -07:00
mateo-berri
0991692e68 test: roll back runtime model registrations between tests
Since #35491, register_model records every registration in the process-global
_runtime_registered_model_cost ledger, and every cost map swap replays that
ledger on top of the freshly adopted map. Under pytest-xdist, any earlier test
in the same worker that registered gpt-3.5-turbo leaked into
TestPriceDataReloadIntegration::test_distributed_reload_check_function: the
replay ballooned its sparse mocked entry into a full ModelInfo dict and failed
the exact-equality assert, breaking the proxy-infra shard whenever loadscope
happened to co-schedule such a test first (reruns cannot help since the
pollution is process-wide)

The autouse isolate_litellm_state fixture now snapshots the ledger before each
test and restores it in place on teardown, so no test's registrations outlive
it. A regression pair in test_conftest_isolation.py asserts the rollback
2026-08-05 20:44:25 -07:00
mateo-berri
5339ec50e7 fix(batches): persist managed file ids for cancelled/failed/expired batches
When the batch cost poller found a batch in a terminal failed, expired, or
cancelled state it wrote the provider response straight to the managed object
table, so the stored blob kept raw provider file ids and a raw batch id. Since
the row is final after batch_processed=True and the read paths only resolve
existing managed ids, every later GET /batches/{id} and GET /batches leaked
raw provider output and error file ids that clients cannot fetch through the
proxy. The terminal branch now normalizes the response with
ensure_batch_response_managed_file_ids before persisting, minting managed ids
under the batch owner's identity

POST /batches/{id}/cancel had the same gap: it called update_batch_in_database
without the caller's auth context, so a cancel response that already carried
provider file ids could never mint managed ids. The endpoint now forwards
user_api_key_dict
2026-08-05 20:42:20 -07:00
Scott Wilson
889c1f584a test(responses): annotate the tool_choice bridge test signature 2026-08-05 23:23:12 -04:00
Mateo Wang
d26ef670e2
Merge pull request #36031 from BerriAI/litellm_b13_unscoped_files_list
fix(managed_files): return unified ids from unscoped file listing
2026-08-05 20:11:15 -07:00
mateo-berri
4b9872e7e8 fix(managed_files): return unified ids from unscoped file listing 2026-08-05 19:53:44 -07:00
Scott Wilson
ebf6167d8a fix(anthropic): stop emitting empty thinking blocks on the Responses adapter
OpenAI emits a reasoning output item on every reasoning turn, but only emits
reasoning_summary_text deltas when a summary was requested and actually
produced. The Anthropic /v1/messages Responses stream adapter opened the
thinking content block eagerly on response.output_item.added, so a summary-less
reasoning item surfaced as {"type": "thinking", "thinking": ""}. Clients persist
that in their session transcript and replay it on the next turn; an Anthropic
model then rejects the request with "each thinking block must contain thinking",
which is what users hit when a resumed session falls back to the default
Anthropic model.

Open the thinking block on the first non-empty summary delta instead, and only
emit content_block_stop for items that actually have an open block.
2026-08-05 22:42:41 -04:00
Mateo Wang
b617e672e3
Merge pull request #36024 from BerriAI/litellm_anthropic_sse_keepalive
fix(proxy): send keepalive pings on anthropic messages SSE streams during upstream silence
2026-08-05 19:41:35 -07:00
Scott Wilson
80d8e95228 fix(responses): unwrap object-form tool_choice before calling the Responses API
Clients send tool_choice as {"type": "auto"} (Cursor on chat completions,
Claude Code's Anthropic tool_choice shape). validate_chat_completion_tool_choice
recognized that shape but returned it verbatim, and the chat -> Responses API
bridge only normalized {"type": "function"}, so the wrapper reached OpenAI and
the whole call failed with:

  Invalid value: 'auto'. Supported values are: 'code_interpreter', ...,
  'web_search_preview', ... (param: tool_choice.type)

That broke every tool call, web search included, on responses-mode models.

Unwrap {"type": "auto"|"none"|"required"} to the bare string at both layers:
the chat completions validation boundary where the shape is first accepted,
and the Responses API bridge that owns the Responses tool_choice contract.
No OpenAI surface accepts the object form for these values, so the previous
passthrough only deferred the 400 to the provider.
2026-08-05 22:41:18 -04:00
mateo-berri
2434c1b904 Merge origin/litellm_internal_staging into litellm_deterministic_output_file_ids 2026-08-05 19:04:27 -07:00
mateo-berri
3c808f9c8f Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_scan_only_tool_results 2026-08-05 18:55:43 -07:00
Chenlu Ji
711ef5dbf0 Merge origin/litellm_internal_staging into feat/tinyfish-search-headers-and-extras
Resolves conflicts from the LIT010/LIT011 Final-enforcement lint pass
landing on litellm_internal_staging after this branch diverged. Keeps
this PR's behavior changes (float support in _UrlEncodableParams,
in-place results truncation + header stashing in
transform_search_response) and adopts the upstream Final annotations
and updated _TINYFISH_RESULT_CAP comment.
2026-08-05 18:44:59 -07:00
mateo-berri
00cbebf503 fix(managed_files): source the unified input file id from the response so retrieve-time mints converge with the cost job 2026-08-05 18:33:20 -07:00
mateo-berri
eef908d4ad fix(batches): register managed output files on batch cancel
update_batch_in_database now fetches the batch row by unified_object_id
when the caller omits db_batch_object, so the cancel endpoint attributes
newly registered output and error files to the batch owner and returns
unified managed ids instead of raw provider ids. Idempotent cancels that
do not change the stored status also skip the redundant DB write now.

Repair two pre-existing mock tests in test_openai_batches_endpoint.py
that asserted values inside lazy percent-format log strings, and give
the cancel test's prisma mock an awaitable find_first.
2026-08-05 18:28:52 -07:00
mateo-berri
d70e10982a fix(guardrails): keep tool-results-only scans off function definitions and merge scoped write-backs
Gate the OpenAI handler's tools forwarding behind scan_only_tool_results,
matching the Anthropic handler, so a tool-results-only scan can no longer
evaluate or rewrite trusted function definitions.

When a guardrail returns a replacement structured_messages list, substitute
the returned messages back into the positions their scoped originals came
from instead of installing the scoped list as the whole conversation, so
out-of-scope messages (system prompt, prior turns) survive redaction on
both the OpenAI and Anthropic paths.
2026-08-05 18:21:58 -07:00
Mateo Wang
60d9e6012c
Merge pull request #35999 from BerriAI/litellm_guardrails_v1_messages_tool_traffic
fix(guardrails): scan /v1/messages tool traffic
2026-08-05 18:08:47 -07:00
Mateo Wang
b9b239b0fb
Merge pull request #35980 from BerriAI/litellm_content_filter_post_mcp_call
fix(guardrails): allow litellm_content_filter to run on post_mcp_call
2026-08-05 18:08:02 -07:00
mateo-berri
f3bfa19ce5 fix(managed_files): resolve model_name identically across all output file registration paths so full unified ids converge 2026-08-05 17:36:22 -07:00
mateo-berri
097c03eebb fix(proxy): tolerate non-scalar sse keepalive interval config shapes 2026-08-05 17:33:09 -07:00
mateo-berri
6ca120a674 fix(proxy): coerce and validate the sse keepalive ping interval from config 2026-08-05 17:23:39 -07:00
Mateo Wang
eabcafc1df
perf(pre-commit): fetch basedpyright base counts from CI artifacts (#35970) 2026-08-05 17:21:16 -07:00
mateo-berri
1b6f3cebf1
fix(managed_files): log sanitized validation errors when skipping rows
The skip warning interpolated the full pydantic ValidationError, whose
string embeds input_value with the rejected row's contents. Managed-file
rows carry a caller-supplied filename, so a malformed row copied that
into operational logs.

Log the error locations, types, and messages via errors() with input,
url, and context excluded, keeping the field-level diagnostics without
the values. Non-validation failures fall back to the exception type.
2026-08-06 00:16:05 +00:00
mateo-berri
3d673f9534
fix(managed_files): skip unparseable rows when listing managed files
get_user_created_file_ids validated every row's file_object without a
guard, so a single row failing OpenAIFileObject validation raised
ValidationError and turned the whole GET /v1/files response into a 500.
#35365 covered the null case only, leaving malformed or partial rows
able to take the entire listing down.

Rows now parse through a helper that returns None on failure and logs a
warning, matching how list_user_batches already tolerates rows it cannot
parse, so one bad row costs its own entry instead of the caller's whole
listing. Null rows stay silent since the batch cost poller registers
those legitimately.

Refs #35361
2026-08-05 23:58:20 +00:00
Souravrajvi0
388943ac17
fix(proxy): register managed batch output files on terminal retrieve (#34092)
Terminal batch retrieve could return the raw provider output_file_id, which
skips managed-file ownership checks on /v1/files/{id}/content and lets any key
on the proxy download another user's batch output.

Retrieve now registers the missing managed-file row before responding, and
attributes ownership to the durable batch owner rather than the retrieving
caller, so output and error ids always come back as unified managed ids.

Fixes #33989
2026-08-05 16:38:49 -07:00
Akash Naickar
e6e18d406a
fix(model-prices): correct replicate model key typo (#34800) 2026-08-05 16:37:44 -07:00
tin-berri
55e666a05f
feat(complexity_router): report LLM classifier cost per request via routing_decision and x-litellm-classifier-cost header (#36015) 2026-08-05 16:27:32 -07:00
ryan-crabbe-berri
7e8d0d3130
fix: rebuild models_by_provider in add_known_models so cost map reloads reach wildcard expansion (#36010)
* fix: rebuild models_by_provider in add_known_models so cost map reloads reach wildcard expansion

* fix: refresh models_by_provider in place so captured references survive reloads
2026-08-05 16:24:43 -07:00
yuneng-jiang
c898d341c0
Merge pull request #36011 from BerriAI/litellm_maint_batch_2026_07
fix(proxy)!: apply request-parameter checks consistently across body, path and form inputs
2026-08-05 16:23:16 -07:00
mateo-berri
131339d8e5 fix(proxy): send keepalive pings on anthropic messages SSE streams during upstream silence 2026-08-05 16:15:04 -07:00
mateo-berri
e87b8a098a fix(managed_files): derive unified output file ids deterministically so concurrent registrations converge 2026-08-05 16:08:56 -07:00
Yuneng Jiang
298fb8ce56
fix(health): drop a stored-credential reference along with the credentials it names
A connection test that redirects the destination already leaves the configured
credentials behind. It kept litellm_credential_name, which names the same stored
secrets and is resolved further down the call, so the reference is now dropped
with them. A request that sets no connection fields of its own is unaffected,
which is how the Admin UI tests a configured model.
2026-08-05 16:07:04 -07:00
Yuneng Jiang
59173c3a20
feat(health): let allow_client_side_credentials re-enable configured-credential reuse
The proxy-wide opt-in that already governs callers supplying their own
connection parameters now also governs whether a connection test may pair a
request-supplied endpoint with the configured deployment's credentials. Off by
default, which keeps configured credentials scoped to the endpoint the
configuration names; on, the previous merge behaviour is available unchanged.
2026-08-05 15:58:49 -07:00
Yuneng Jiang
b468acb31c
fix(health): stop inheriting configured credentials when a connection test sets its own
A request that supplies its own connection fields describes a connection of its
own, so the configured deployment's credentials are no longer merged underneath
it. Anything the request leaves unset still comes from the configuration, so
naming a configured model and testing it as configured is unchanged, and adding
a second deployment for an already-configured name works as before.

Replaces the earlier outright rejection, which also refused requests that
supplied a complete connection of their own.
2026-08-05 15:49:41 -07:00
devin-ai-integration[bot]
aa1180c0c9
fix(core_helpers): map generic 'error' finish_reason to 'stop' (#33972)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-05 22:39:12 +00:00
mateo-berri
2ba4e91766 feat(guardrails): add scan_only_tool_results to scope unified guardrails to tool results 2026-08-05 15:38:08 -07:00
Yuneng Jiang
e5effcb861
fix(health)!: let configured deployment parameters win over request overrides
When a connection test names a model that resolves to a configured deployment,
that deployment's routing and credential parameters are authoritative. A request
supplying a complete connection of its own is unaffected.

BREAKING CHANGE: /health/test_connection no longer lets a request replace the
routing or credential parameters of a configured model it names. Supply the full
connection parameters instead of naming a configured model.
2026-08-05 15:17:43 -07:00
Yuneng Jiang
5b2c92d749
fix(proxy)!: parse bracket-notation form metadata the same way its JSON form is parsed
Multipart callers express nested metadata as flat bracket-notation keys, which
reach the request-body check as literal keys rather than as a metadata dict.
The check now rebuilds them with the same helper the endpoints use, so both
encodings are handled identically and cannot drift apart.

BREAKING CHANGE: a multipart field such as `litellm_metadata[api_base]` is now
subject to the same request-body parameter rules as its JSON equivalent. Set
`general_settings.allow_client_side_credentials`, or the deployment's
`configurable_clientside_auth_params`, to keep passing these.
2026-08-05 15:17:43 -07:00
Yuneng Jiang
fc4be70a37
fix(proxy)!: share one destination check between body and path-supplied model
The URL-destination check previously ran over request-body fields only. The
per-field logic moves into reject_url_valued_destination(field, value) so a
deployment name resolved from the request path runs the same check against the
same admin allowlist.

BREAKING CHANGE: a deployment name supplied in the request path that parses as
an http/https destination is now refused. Add the host to
`provider_url_destination_allowed_hosts` in litellm_settings to keep it working.
2026-08-05 15:17:43 -07:00