Commit graph

13345 commits

Author SHA1 Message Date
mateo-berri
6648ebe3cc fix(router): always compute a group's reasoning efforts rather than seeding them from model_info
The ModelGroupInfo splat let a supported_reasoning_efforts value left in a deployment's
model_info seed the group, so it narrowed the group from whichever deployment was read
first and was silently ignored on every other one. The field is derived from the group's
deployments, so start it unset and let the intersection fill it in.

Also correct two docstring claims that did not match the code: the anthropic chat path
gates xhigh and max on the output_config path only, and the mode signal separates an
unknown deployment from a known non-reasoning one only while that deployment carries no
model_info of its own.
2026-08-24 15:59:48 -04:00
mateo-berri
96acac1f91 fix(router): tell an unknown deployment apart from a known non-reasoning one
get_model_info answers supports_reasoning None both for a model absent from
the map, which the router registers under a synthesized entry, and for a
mapped model that simply is not a reasoning model. Reading both as "adds no
levels" let one custom deployment wipe every level its mapped siblings agreed
on.

The synthesized entry carries no mode, which every real map entry for a
routable model does, so an unset flag with no mode now resolves to unknown and
never narrows its group. A group that genuinely shares no level still
advertises none, and the dashboard drops the effort control for it instead of
offering levels routing would refuse.
2026-08-24 15:59:48 -04:00
mateo-berri
18528b1a63 fix(reasoning): keep the chat gate on xhigh and stop empty groups zeroing the picker
The chat-completions gate only ever owned xhigh. Widening it to max and ultra
made gpt-5.6 answer 400 on requests litellm itself converts to /v1/responses,
where max is valid, because the gate runs before the bridge decision. No map
entry asserts either flag, so the widened gate could only ever reject.

An empty per-group intersection now falls back to the capability-blind level
list in the dashboard, matching what the picker showed before the field
existed, and ModelGroupInfo tolerates whatever shape an operator writes under
supported_reasoning_efforts instead of failing the whole /model_group/info
response.
2026-08-24 15:59:48 -04:00
mateo-berri
6ec49ea7dd fix(map): stop claiming max reasoning effort for gpt-5.6 on chat completions
/v1/chat/completions rejects reasoning_effort=max on every gpt-5.6 snapshot with
"Unsupported value: 'reasoning_effort' does not support 'max' with this model.
Supported values are: 'none', 'low', 'medium', 'high', and 'xhigh'", so the 16
gpt-5.6 entries that asserted supports_max_reasoning_effort had model groups
advertising a level routing would always get a 400 for.

The flag reads as chat-surface capability everywhere else in the map: before
this branch no openai or azure entry carried it at all, only anthropic-family
ones whose chat endpoint really does take max. Dropping it lines the gpt-5.6
advertisement up with what the endpoint accepts and lets the chat gate refuse
the level with our own error instead of forwarding it into a provider 400.

/v1/responses does accept max for gpt-5.6 and is unaffected: the responses
config gates none alone, so the cursor thinking-max variant keeps resolving.
2026-08-24 15:59:48 -04:00
Mateo
b0fee551fd fix(router): resolve the none reasoning effort as opt-in on azure
AzureOpenAIGPT5Config raises UnsupportedParamsError on reasoning_effort='none'
unless the model map flags it, while OpenAI never refuses the level, so a single
opt-out polarity advertised none for 61 azure deployments that reject it. Defer
to _supports_factory when the azure flag is absent so the advertised list and the
request gate agree on every azure gpt-5 model in the map.

Also wrap the widened reasoning_effort Literal in main.py, which ruff format
flagged over the line limit.
2026-08-24 15:59:47 -04:00
Tin Chi Lo
a5dd020023 fix: keep ultra plumbed but unasserted, and stop the bridge forwarding default 2026-08-24 15:59:47 -04:00
Tin Chi Lo
0e96491554 feat(router): per-group supported reasoning efforts with max and ultra levels 2026-08-24 15:59:47 -04:00
mateo-berri
a1f1aa9cb5 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_harden_retry_breadcrumb_credentials 2026-08-24 12:57:42 -07:00
mateo-berri
721227e9ee refactor(router): scrub retry-breadcrumb credentials by pattern, not a denylist
Enumerating credential-bearing kwargs in RETRY_BREADCRUMB_EXCLUDED_KWARGS is always one
new kwarg behind: it missed top-level extra_headers and provider token fields, which
log_retry still copied into router.previous_models verbatim. Scrub the breadcrumb with
mask_credentials_in_payload instead, so credential-named values are masked at any depth
(extra_headers.authorization, api_key, aws_secret_access_key, vertex_credentials,
azure_ad_token, and future kwargs), and leave the exclusion set to the request payload and
router walk state only.

This hardens the in-memory breadcrumb; it is not a fix for a reproduced SpendLogs leak. The
SpendLogs metadata allowlist and the universal previous_models stripping already keep this
breadcrumb off every persisted surface.

Parametrize the regression test over provider_specific_header, extra_headers, and api_key,
asserting the raw credential value never survives into previous_models for any shape while
the container key still reaches the breadcrumb
2026-08-24 12:57:32 -07:00
mateo-berri
15d8f0b43c refactor: drop the output-neutral pad on the executed-literal recursion
The call-detection restore splices into a fixed-position list, so its
.ljust(end - start) holds that length invariant and a test guards it. The
DML-scan recursion instead hands the undoubled literal to a fresh scan_region
as its own region, whose length feeds nothing, so the pad only appends
trailing spaces that shift no keyword and change no reported line. Drop it and
the docstring clause that claimed it kept the offsets landing
2026-08-24 12:54:57 -07:00
mateo-berri
aef7424637 fix(azure/search): reject bool and non-positive max_results for bing_grounding count
Extract a shared _valid_max_results predicate that rejects bools (an int
subclass) and non-positive values, and reuse it from both the connection-mode
request count and the response-side cap so both paths honor the same contract.
2026-08-24 12:53:44 -07:00
mateo-berri
ec47bbaaaa feat(e2e): record and replay streamed provider responses chunk-for-chunk
The record/replay harness stored a streamed provider response as one
buffered body, so a replayed stream arrived coalesced and the
/v1/messages streaming test could not be edge-wired. Keep each SSE
transfer chunk in the bundle in the order the provider sent it (a new
streamed response shape at BUNDLE_FORMAT_VERSION 4) so replay reproduces
the provider's split points, the recorded usage chunk keeps its
position, and a mid-stream upstream error replays as the same
mid-stream error rather than a clean body.

Resolves LIT-5742
2026-08-24 12:51:44 -07:00
mateo-berri
3ffec658fe test(images): pin scalar-array edit params survive as repeated multipart fields 2026-08-24 12:49:54 -07:00
mateo-berri
a703378915 fix(images): forward scalar-array edit params as repeated multipart fields
Flatten dict-backed multipart bodies so a scalar list becomes one field with a
tuple value, which httpx emits as a repeated part per element, instead of
collapsing to the last element under dict.update. Nested objects still flatten
to key[subkey] like the OpenAI SDK, and the file-tuple video path is untouched.
2026-08-24 12:48:44 -07:00
mateo-berri
ab93636e2c fix(vertex-passthrough): derive credential-header drop set from SpecialHeaders
The hand-rolled drop set missed Ocp-Apim-Subscription-Key, so a caller
Azure APIM secret in that header was forwarded to Google on the
credential-less branch. Derive the name-drop set from the canonical
SpecialHeaders.litellm_credential_header_names(), minus Authorization and
x-goog-api-key which double as real Google credentials and are value-stripped
instead. New credential headers added there are now dropped automatically.
2026-08-24 12:45:41 -07:00
mateo-berri
ac29505f3d feat(proxy): enforce vector-store upload security controls on /v1/rag/ingest
Uploaded files reaching the RAG ingest path were trusted by client
filename and content-type, so archives and executable scripts were
ingested and malicious content was never screened. Enforce controls at
the upload boundary before the file leaves the proxy:

- classify content by magic bytes and a strict UTF-8 decode, never by
  the client filename or content-type
- allowlist PDF and UTF-8 text; reject archives and executables/scripts
- cap upload size (512MB) via a bounded read
- run every accepted upload through a dependency-injected malware
  scanner, failing closed on scan error; the default scanner flags the
  EICAR test file so the hook is validated end to end
- give accepted uploads a server-generated filename so the client name
  never reaches storage
- set Content-Disposition attachment and X-Content-Type-Options nosniff
  on vector-store file downloads
2026-08-24 12:41:15 -07:00
mateo-berri
422b24023c fix: undouble an executed literal before scanning, so a doubled-quote comment can't hide a rewrite 2026-08-24 12:39:19 -07:00
Mateo Wang
d0da90ee6d
Merge pull request #38115 from BerriAI/litellm_fix_runwayml_video_provider
fix(runwayml): route every generation endpoint and fix video cost tracking
2026-08-24 12:35:59 -07:00
mateo-berri
b1035368f8 fix(passthrough): thread router-model attribution on the litellm_metadata bucket
The router hop _ageneric_api_call_with_fallbacks canonicalises the passthrough
call type onto litellm_metadata, and the cost callback reads spend attribution
from that bucket while only backfilling user_api_key* keys from metadata. The
helper was building on metadata, so agent_id and user_api_end_user_max_budget
were silently dropped before the callback ever saw them. Build and pass the
attribution under litellm_metadata so every field survives.
2026-08-24 12:35:52 -07:00
mateo-berri
1c18d3eda3 fix(router): stop copying forwarded credentials into retry breadcrumbs
log_retry copied every kwarg into the previous_models breadcrumb, so a client's
forwarded Authorization (provider_specific_header) and the deployment api_key /
headers rode along in an in-memory structure whose comment says it reaches spend
logs and logging callbacks. Those values have no diagnostic use in a breadcrumb.

Add provider_specific_header, headers, and api_key to RETRY_BREADCRUMB_EXCLUDED_KWARGS
so the credential is never placed there in the first place. This is defense in depth:
no persisted leak exists today, since the SpendLogs metadata allowlist and every
logging integration already drop previous_models before serialization. Removing the
credential at the source means a future logging path cannot expose it either
2026-08-24 12:34:27 -07:00
Mateo Wang
09f4096641
Merge pull request #38101 from BerriAI/litellm_health_test_connection_health_check_params
fix(health): apply model_info.health_check_params to health check probes
2026-08-24 12:34:13 -07:00
ryan-crabbe-berri
776b585f70
Merge pull request #37908 from BerriAI/litellm_ruff_dead_test_code
fix(proxy): keep every value of a repeated form key, and gate the tests that hid it
2026-08-24 12:31:55 -07:00
mateo-berri
ee0363249d fix(vertex-passthrough): strip virtual key sent via custom key header
user_api_key_auth also authenticates a caller from the operator-configured
general_settings.litellm_key_header_name, reading that header straight off
the request, so a virtual key sent there survived the credential-less Vertex
forwarding filter and reached Google alongside a real bring-your-own
credential. Value-strip every header whose value matches the caller's key
from any accepted source, including that custom header.
2026-08-24 12:28:46 -07:00
Tin Chi Lo
26e47aea32 fix(auto-router): list configured auto-routers in the usage picker before they have traffic 2026-08-24 15:28:20 -04:00
mateo-berri
a0c319878b fix(search): harden bing_grounding auth, result cap, status, and cost
- send a caller api_key via the Azure api-key header instead of Authorization: Bearer
- cap web_search results to the requested max_results (the tool has no count knob)
- surface a Foundry failed/incomplete response status as a 502 error
- zero the per-query cost in web_search mode; keep the map price for connection mode
- trim the example config to terse env-var pointers
2026-08-24 12:26:07 -07:00
ryan-crabbe-berri
fdae9306d8
Merge pull request #38130 from BerriAI/litellm_missing_raise_anthropic_import
fix(utils): make prompt_token_calculator count claude models again
2026-08-24 12:26:02 -07:00
mateo-berri
3030e974b8 fix(runwayml): skip progress scaling when Runway returns a null progress 2026-08-24 12:20:00 -07:00
Mateo Wang
37b659e864
Merge pull request #38108 from BerriAI/litellm_fix_46_thinking_budget
fix(anthropic): keep legacy thinking budget_tokens on Claude 4.6 models on /v1/messages
2026-08-24 12:19:37 -07:00
ryan-crabbe-berri
6975b8ea4b fix(utils): make prompt_token_calculator count claude models again
The claude branch called the anthropic SDK's `Anthropic().count_tokens`, which the
SDK removed, so every claude call raised AttributeError. Counting now goes through
litellm's own token_counter, which handles anthropic models offline and drops the
SDK dependency entirely.

Hiding that was a swallowed error: `except Exception: Exception("Anthropic import
failed please run `pip install anthropic`")` built the exception without raising
it, so an environment missing the SDK fell through to the unguarded
`from anthropic import ...` on the next line and got a bare ModuleNotFoundError
instead of the install hint.

That was the codebase's last PLW0133, so the rule graduates from the ratcheted
budget into ruff.toml where it hard-fails, and editors get the diagnostic inline.
2026-08-24 12:19:03 -07:00
mateo-berri
e7c2ede159 fix(vertex-passthrough): never forward proxy auth headers to Google
On the credential-less Vertex passthrough branch, drop every header that
can only carry LiteLLM caller auth (x-litellm-api-key, api-key, x-api-key)
by name, since Google never consumes them, and strip the virtual key by
value from Authorization / x-goog-api-key, which may instead hold a genuine
bring-your-own Google credential. This closes the residual leak where a
distinct caller secret in api-key or x-api-key still reached upstream.
2026-08-24 12:08:43 -07:00
Mateo Wang
f818a48ae5
Merge pull request #36513 from Souravrajvi0/bugfix/video-edits-form-body-36487-8fc4
fix(proxy): parse form-encoded video edit/extension bodies after auth
2026-08-24 12:03:10 -07:00
mateo-berri
088a700933 test(passthrough): cover virtual key echoed in api-key and x-api-key
Adds a regression asserting the value-based strip also drops the caller's
virtual key when it is duplicated into the api-key and x-api-key headers,
while a genuine bring-your-own Google credential still forwards.
2026-08-24 12:00:21 -07:00
mateo-berri
458a63935f test(health): guard that test_connection authorizes on post-merge probe params
Add an endpoint-level regression test asserting can_user_make_model_call
receives the litellm_params after health_check_params are merged in, so the
merge-before-auth ordering cannot silently regress and let a request smuggle
a field past authorization.
2026-08-24 11:58:33 -07:00
mateo-berri
b00afc03a5 Merge origin/litellm_internal_staging into litellm_fix_36493_image_video_routes 2026-08-24 11:58:22 -07:00
mateo-berri
4ddf1e5c6d test: guard the restore padding against a long escaped-quote run before a definition 2026-08-24 11:57:45 -07:00
devin-ai-integration[bot]
a1134755ca
fix(ui): boot the UI image as an arbitrary uid by anchoring nginx writes under /tmp (#37982)
* fix(ui): boot the UI image as an arbitrary uid by anchoring nginx writes under /tmp

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

* test(ui): type the arbitrary-uid image test fixture

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-08-24 11:57:36 -07:00
Mateo Wang
33c51e84b0
Merge pull request #35415 from BerriAI/litellm_azure_ai_entra_auth
feat(azure_ai): support entra id / oauth auth on every azure ai foundry route
2026-08-24 11:54:48 -07:00
mateo-berri
51ab4c7486 test(videos): lock AzureVideoConfig inherited file-less multipart behavior
AzureVideoConfig subclasses OpenAIVideoConfig and so inherits the new
use_multipart_form_data() -> True. Azure's /openai/v1/videos surface is
OpenAI-SDK-compatible, so the JSON->multipart flip is intentional; assert it
through the real handler so the inherited behavior can't silently regress.
2026-08-24 11:54:29 -07:00
mateo-berri
671a454baa Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_bing_grounding_search_provider 2026-08-24 11:53:45 -07:00
mateo-berri
0322107414 fix(images): flatten nested image-edit params to SDK multipart form
The openai/azure/compat image-edit funnel merged non_default_params and
extra_body straight into the multipart body, so a nested value (e.g.
extra_body={"metadata": {...}}) reached the httpx encoder and 500'd with
"Invalid type for value. Expected primitive type". Route the funnel through
a shared flattener that serializes nested values as OpenAI-SDK bracket fields
(key[subkey], lists as key[], bools lowercased, None/empty dropped), matching
the wire format of the rest of this fix.
2026-08-24 11:51:28 -07:00
mateo-berri
4bc097733f fix(passthrough): strip virtual key from all headers on credential-less Vertex forward
The credential-less Vertex passthrough dropped the caller's LiteLLM
virtual key only from Authorization by exact match. A caller who sent
the same key in x-goog-api-key (which doubles as a real Google
credential) had it accepted as a credential and forwarded upstream.

Drop the virtual key by value across every forwarded header, normalizing
any Bearer prefix, so no header name carries it to Google.
2026-08-24 11:51:23 -07:00
mateo-berri
25b379f3c7 test: register supports_legacy_thinking in model-prices schema
Regenerate model_prices_and_context_window.schema.json and add the flag to
the inline validator schema in test_utils.py so the new cost-map key passes
validate-model-prices-json and the JSON-valid test.
2026-08-24 11:47:21 -07:00
mateo-berri
e43b496000 fix: undouble single-quoted payloads before reading them for routine calls
Call detection restored a single-quoted DO or EXECUTE payload through
without_comments while its `''` escapes were still doubled. The first quote of
a pair opened an empty string and closed it on the second, leaving a `--` or
`/*` from a nested string bare, so it blanked the real call after it and the
routine read as uncalled: its rewrite body then went unscanned at boot. Undouble
each single-quoted payload before restoring it, and pad it back to the span it
fills so the later offsets still land. Dollar-quoted bodies do not escape quotes
and are left as they were.
2026-08-24 11:45:04 -07:00
Darien Kindlund
b36f34813a
fix(anthropic): reconcile enum with declared type in output_format schema (#37882)
* fix(anthropic): reconcile enum with declared type in output_format schema

Anthropic cross-validates `enum` against `type` in structured outputs: every
enum value must match a single declared type. A union `type` array, or an enum
value whose JSON type differs from a scalar `type`, is rejected with
"Invalid schema: Enum value 'low' does not match declared type '['string','null']'"

filter_anthropic_output_schema had no enum/type reconciliation, so both keys
reached Anthropic untouched. Drop the conflicting `type` -- `enum` is the
tighter constraint, and an enum with no `type` is accepted

The drop is conditional: `type` is only removed when it is a union array, or
when some enum value does not match the scalar type. A matching enum plus
scalar `type` is left exactly as-is, so existing behaviour is unchanged

Pydantic emits the failing shape for Optional[SomeEnum], so this affects any
caller with a nullable enum field on the native output_format path. vertex_ai
is unaffected because it is forced onto the permissive tool-use path

Fixes #37881

* refactor(anthropic): make enum/type reconciliation immutable and precisely typed

Address review: the predicate registry was a mutable `dict[str, Any]`, and the
reconciliation removed `type` by mutating the built result with `pop`

- registry is now `Final[Mapping[str, Callable[[Any], bool]]]` wrapped in
  `MappingProxyType`, so predicate signatures are statically checked and the
  table cannot be mutated
- the conflict decision moves into a pure helper evaluated once against the
  input schema, and the conflicting `type` key is skipped at build time in the
  existing loop instead of being popped afterwards, so nothing is mutated

Behaviour is unchanged; all 27 tests in the schema-filter suite still pass
2026-08-24 11:43:47 -07:00
Mateo Wang
a626170c89
Merge pull request #36806 from BerriAI/litellm_bedrock_converse_no_trailing_empty_chunk
fix(bedrock): stop emitting an empty assistant delta after the finish_reason chunk
2026-08-24 11:37:04 -07:00
mateo-berri
37f8b9600a Merge origin/litellm_internal_staging into litellm_azure_ai_entra_auth
Bring the Entra ID / OAuth auth work for Azure AI Foundry routes up to date
with staging and fix the lint-budget regressions the merge surfaced:

- widen get_azure_ai_auth_headers return type to Mapping[str, str] (LIT001)
- build the azure_ai image_generation request headers into a new Final local
  instead of rebinding the Final headers dict (reportGeneralTypeIssues)
- order HuggingFace rerank validate_environment params to match BaseRerankConfig
  so litellm_params lines up positionally (reportIncompatibleMethodOverride)
- add a match= to the credential-error test and document the handler-boundary
  patches the auth wiring tests rely on
2026-08-24 11:33:18 -07:00
mateo-berri
a7cd2cd439 Merge branch 'litellm_internal_staging' into litellm_fix_46_thinking_budget 2026-08-24 11:31:10 -07:00
mateo-berri
531aafdf75 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_health_test_connection_health_check_params
# Conflicts:
#	tests/test_litellm/proxy/test_health_check_max_tokens.py
2026-08-24 11:30:04 -07:00
mateo-berri
603d3358d6 Merge origin/litellm_internal_staging into bugfix/video-edits-form-body-36487-8fc4
Pulls in the detect-changes CI action and the test-unit job timeout bump, which clears the red lint and code-quality checks on this PR

The merged, tightened lint budgets flag this PR's own code, so this merge also makes video_reference_to_id a pure function instead of a helper that mutates its input dict, and rewrites the form-body regression test to call the video_edit and video_extension handlers directly rather than patching an internal class method. Adds pure-logic unit tests for video_reference_to_id
2026-08-24 11:26:03 -07:00
mateo-berri
bf4069a80c fix(proxy): authorize health test-connection on final probe params
Move _update_litellm_params_for_health_check before can_user_make_model_call
so health_check_params cannot retarget the probe after the auth check. Type
the Pegasus test helper signature and drop the redundant test narrative.
2026-08-24 11:24:21 -07:00