Commit graph

1357 commits

Author SHA1 Message Date
yuneng-jiang
969161501a
feat(ui): add LITELLM_UI_USE_SI_TERMINOLOGY to show SI instead of AI in the Admin UI (#47440)
* feat(ui): add LITELLM_UI_USE_SI_TERMINOLOGY to show SI instead of AI in the Admin UI

Setting LITELLM_UI_USE_SI_TERMINOLOGY=true on the proxy makes the Admin UI
say "SI" where its own copy says "AI" and "Super Intelligence" where it says
"Artificial Intelligence". This covers the nav, page headings, settings text,
guardrail garden blurbs, the compliance playground prompts and the playground
sample prompts. Third-party product, provider and law names keep their
spelling (Vertex AI, Cisco AI Defense, EU AI Act, ...), and user data such as
guardrail names, gateway URLs and keys is never rewritten.

The proxy exposes the flag on /.well-known/litellm-ui-config, a
TerminologyProvider at the app root reads it, and components call
useTerminology() on their own strings. The context defaults to a pass-through,
so nothing changes when the flag is unset.

* fix(ui): apply SI terminology to the command palette, page visibility groups and guardrail garden search

* test(ui): cover the flag-off wording on the palette, page visibility and garden search tests

* fix(ui): apply SI terminology to the Aria usage assistant copy

* test(ui): keep the Aria ui-config stub within the inline object lint budget

* fix(ui): keep AI wording in prompts sent to models
2026-10-27 22:54:39 +00:00
Ambuj Upadhyay
cb70bc560e
fix(streaming): dict-usage arm drops prompt_tokens_details/completion_tokens_details its sibling arms preserve (#36678)
* fix(streaming): dict-usage arm no longer drops token detail fields

In _dispatch_provider_chunk's openai/azure else-branch, a plain-dict
usage was rebuilt into litellm.Usage from only prompt_tokens /
completion_tokens / total_tokens. prompt_tokens_details (cached_tokens),
completion_tokens_details (reasoning_tokens) and cache token counts were
silently dropped — while both sibling arms (Usage passthrough, BaseModel
via Usage(**model_dump())) preserve them.

Every in-repo chunk producer currently normalizes usage to
Usage/BaseModel before this point, so the dict arm is reachable today
only via chunks litellm did not construct (e.g. a custom
streaming_decoder feeding CustomStreamWrapper). When it does fire on a
usage-only final chunk (stream_options.include_usage), the stripped
Usage is exactly what the stream consumer receives — the early-return
path has no later aggregation to repair it.

Fix: pass the full dict through Usage(**usage_dict). Usage.__init__
coerces nested detail dicts into their wrapper types and tolerates
extra keys. Side effect: a legitimate 0 count is no longer coerced to
None by the old 'or None' expressions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: drop explanatory source comment per repo comment policy

Rationale lives in the PR body and the regression tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(streaming): type the dict-usage unpack for the basedpyright gate

Usage(**response_obj["usage"]) unpacks a dict whose value type pyright
cannot see, and the delta gate reports that once per typed constructor
parameter: five, against the three .get() calls the line replaced. Moving
the unpack behind _usage_from_payload keeps the same Usage(**payload)
call and is reported once, at the call site, so the rule count ends below
its merge base

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 15:21:11 -07:00
Kerry Lu
8f31c7171d
fix(router): keep a lone deployment warm under router allowed_fails unless the policy names the exception (#48644)
* fix(router): keep a lone deployment warm under router allowed_fails unless the policy names the exception

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

* test(router): give cooldown fixtures a sibling deployment so allowed_fails=0 still forces a cooldown

* fix(router): keep benching a lone deployment that has a fallback group or an empty deployment policy

* fix(router): accept dictionary fallback targets in the lone deployment check

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

---------

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-27 15:12:53 -07:00
yucheng-berri
a53ea5bd77
fix(proxy): keep every startup line JSON when json_logs is on (#47035)
* fix(proxy): keep every startup line JSON when json_logs is on

Boot-time messages the proxy printed straight to stdout (banner, feedback
box, model list, cache and callback notices, policy table, migration
notices, CLI launch notes) and the first records of uvicorn- or
granian-spawned workers came out as plain text while json_logs was on,
breaking log pipelines that parse every line as JSON.

- print_startup_message() prints unchanged in plain mode and, with
  json_logs on, emits one INFO record on the proxy logger (ANSI
  stripped) that skips the logger and handler level checks like the
  print it replaces; show_banner() routes to it in JSON mode only
- the CLI exports JSON_LOGS=true when the config sets json_logs so
  spawned workers log JSON from their first record
- the --enforce_prisma_migration_check deprecation notice prints after
  the config is read so a config-only json_logs covers it

Resolves LIT-1624

* fix(proxy): keep show_banner() SDK-free, pick the JSON banner in initialize()

Since #46803 the `lite` shell calls show_banner() against an uninitialized
litellm package, and reading litellm.json_logs inside it initialized the whole
SDK at the banner (0.14 s to 0.83 s for a bare `lite` here, the seconds that PR
removed on small VMs). show_banner() is back to its main version; the proxy's
initialize() now emits the JSON banner record itself and calls show_banner()
only in plain mode. The banner unit tests move to the initialize() call site.

* fix(proxy): keep the JSON banner when an operator --log_config names the proxy logger

uvicorn applies --log_config after proxy_cli has switched JSON logging on and before
initialize() switches it on again through load_config(). A logging config that names
"LiteLLM Proxy" removes that logger's handlers in the window between the two, which is where
initialize() emits the banner and the feedback box, so at one worker both went nowhere;
two-worker children import litellm after uvicorn's logging setup and were not affected.
print_startup_message() now falls back to a one-off JSON handler, on the stream the JSON
handler uses, when the proxy logger has no handler of its own.

Live, json_logs on plus a log_config naming the proxy logger, one worker: main prints the
banner and the feedback box as plaintext, e2303a3925 prints neither, this head prints both as
one JSON record each. New unit test fails on e2303a3925; new integration cell
test_json_logs_with_operator_log_config_keeps_every_line_json passes at one and two workers.

* fix(proxy): keep a long startup listing whole in its JSON record

print_startup_message ran its record through the proxy logger's filters, so
in JSON mode the required-migration SQL list and the policy list were
middle-truncated at MAX_STRING_LENGTH_STDOUT_LOG (4096 by default) with the
litellm_truncated marker, where the prints they replace showed them whole.
That cap bounds echoed request payloads; a startup message is operator text,
so the record now gets the uncapped processing of a DEBUG line: secret
redaction and base64 collapse still apply, the text cap does not.

Red on 41f92b745a, green here:
- tests/unit/test_logging.py: a listing longer than the cap comes out whole;
  a credential in a startup message is still redacted
- tests/integration/configuration/test_json_logs_startup.py: a 60-policy
  config boots with json_logs on and the policy listing record (9762 chars)
  carries every policy with no truncation marker (4096 chars, marker, on
  the unfixed tree)

* fix(proxy): keep the --enforce_prisma_migration_check notice on --health and --test

This PR moved the deprecation notice after the config is read, so that a
json_logs set only in the config applies to it; --health and --test return
before that point and stopped printing it, a visible change main did not
have. The notice is now also emitted before those two returns, through the
same helper, so a deployment running `litellm --health
--enforce_prisma_migration_check` still learns the flag is dead, as on main.
A normal server start emits it once, after the config, as in the previous
commit.

Red on 2d7a2d6844, green here: tests/unit/proxy/test_proxy_cli.py asserts
the notice on --health and on --test.

* fix(proxy): keep the JSON startup records on stdout under an operator handler on the proxy logger

An operator --log_config that gives "LiteLLM Proxy" a handler of its own, a NullHandler
that silences it or a plain WARNING-level handler on stderr, is in force in the window
where the banner, the feedback box and the cache notice are emitted: uvicorn applies it
after the CLI process switched JSON on and before initialize() re-installs the JSON
handler through load_config(). 41f92b745a sent the startup records through the proxy
logger's handlers and fell back to a JSON stdout handler only when there were none, so
under such a config the records landed in the operator's handler, as plaintext on stderr,
or vanished, where the prints they replace reached stdout whatever the logging config
said.

print_startup_message() now writes every startup record through the proxy's own JSON
handler on stdout, filters included, and never through the proxy logger's handlers; the
operator's handler gets nothing from a startup message, as it got nothing from the print.
The normal JSON handler is the same class, formatter and filters, so the records a
pipeline reads are unchanged when no operator config is in play.

Tests: tests/unit/test_logging.py gets the two handler shapes (red on f6f5957d16: the
record went to stderr as plaintext or nowhere); tests/integration/configuration/
test_json_logs_startup.py gets a silencing operator --log_config cell at 1 and 2 workers
(red on f6f5957d16: banner, feedback box and cache notice missing; green here).

* fix(proxy): print the --enforce_prisma_migration_check notice when the config fails to load

f6f5957d16 printed the deprecation notice after the config is read, so a json_logs set
only in the config makes it a JSON record. A boot whose config file is missing or does
not parse then exited before that point and never printed it, where main printed it
before the error (10-20 gauntlet finding).

The notice is printed where main printed it, unless a config file can still switch
json_logs on: then it is held until the config is read and printed right after the
json_logs switch, and a click close callback prints it on the way out if the boot fails
first. functools.cache keeps it to one print either way.

Tests: tests/unit/proxy/test_proxy_cli.py covers a failed config read (red on
75043aed83), a plain config printing it once and a config-only json_logs making it one
JSON record; tests/integration/configuration/test_json_logs_startup.py adds the JSON cell
and a missing and an unparseable config cell (red on 75043aed83, green here), through
refused_boot_log, which now takes extra CLI arguments.

* fix(proxy): keep startup records as printed instead of running secret redaction

In JSON mode the startup messages ran secret redaction, which the prints they replace never
had. Its sk- key pattern has no left boundary, so a model named risk-assessment-gpt4o was
logged as riREDACTED in the model list (10-21 gauntlet finding). The records now skip
redaction like the prints did; the processed record still counts as scrubbed, so the
handler's secret filter and the formatter leave it alone. The stdout cap stays off and base64
collapse stays on, as before.

Tests: tests/unit/test_logging.py checks the model name comes out whole (red on 7d17f126e5),
replacing the test that asserted startup records were redacted;
tests/integration/configuration/test_json_logs_startup.py adds a boot with a
risk-assessment-<id> model whose model list record carries the name (red on 7d17f126e5).

* refactor(proxy): meet the AGENTS.md rules Greptile flagged on the startup-message change

The policy listing builds its rows as tuples in two helpers instead of appending to a list;
plain output is unchanged (same bytes as before across 3000 random policy and attachment
configs). The explanatory comments added with this change are gone, and the docstring of
print_startup_message now says the record is whole and unredacted.

Tests: the migration-diff JSON test no longer runs the fake prisma CLI; it checks the
skipped-diff notice in-process, and the fixture extension it needed is reverted. The
json_logs export and deprecation-notice CLI tests run the real proxy module and a real
config file instead of a mocked ProxyConfig and proxy module, and the --health/--test
notice tests no longer patch our helpers. Each still fails when the export, the hold or
call_on_close is removed. New test parameters are typed.

---------

Co-authored-by: yucheng-berri <293926326+yucheng-berri@users.noreply.github.com>
2026-10-27 14:49:07 -07:00
tin-berri
2a9c630182
fix(proxy): release concurrency slots when synthetic streams complete (#48628) 2026-10-27 14:47:04 -07:00
tin-berri
ad4b35d791
fix(mcp): accept YAML OpenAPI documents (#48622) 2026-10-27 14:46:15 -07:00
tin-berri
c23cff7e45
fix(mcp): enforce deny-first tool policy consistently (#48625) 2026-10-27 14:30:48 -07:00
tin-berri
e0d1e64591
feat(router): respect caller output budgets in complexity routing (#48632)
Co-authored-by: stuti612 <stutiprasad63@gmail.com>
2026-10-27 14:04:30 -07:00
Kerry Lu
52562c47f8
refactor(proxy-extras): drop the spend-log api_key special case from the row check remedy (#48636)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-27 20:53:23 +00:00
moe-berri
34c10829b1
fix(ui): percent-encode request id in spend log detail URL (#48630)
* test: cover request ids with URL special characters in log detail fetches

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

* fix(ui): percent-encode request id in spend log detail URL

Co-authored-by: Charlie Freeman <243496084+cf3901646@users.noreply.github.com>
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: Charlie Freeman <243496084+cf3901646@users.noreply.github.com>
2026-10-27 13:33:47 -07:00
moe-berri
9e01b3c37b
fix(complexity_router): keep the held user_turn route for Codex compaction requests (#48627)
* test(complexity_router): Codex compaction requests hold the user_turn decision

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

* fix(complexity_router): treat Codex compaction traffic as user_turn continuation

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

* fix(complexity_router): validate Codex turn metadata with frozen models

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

* refactor(complexity_router): flatten body system text through _message_text

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

* test(complexity_router): drop unrelated reformatting from the mapped test file

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

* fix(complexity_router): skip pin escalation on Codex compaction turns

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>
2026-10-27 13:33:32 -07:00
Kerry Lu
d6edd37810
fix(aiohttp_transport): close an owned aiohttp session when its transport is collected (#48650)
An OpenAI or Azure SDK client litellm built and evicted from the client
cache is usually garbage collected inside the closer's 900 second grace
window, so the closer's weak reference dies and the client's aiohttp
session is never closed. LiteLLMAiohttpTransport.__del__ now hands an
owned, still-open ClientSession to _close_recycled_session, which already
covers the current-loop, foreign-loop and closed-loop cases.

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: BangerSoul <bangersoul@users.noreply.github.com>
2026-10-27 20:33:03 +00:00
moe-berri
1bff4e3dae
fix(anthropic): make opening usage estimates opt-in (#48633)
* fix(anthropic): make opening usage estimates opt-in

* docs(anthropic): follow README paragraph style
2026-10-27 12:57:21 -07:00
Kerry Lu
d65587593f
ci: spend-logs-guard requires kerry-berri approval on LiteLLM_SpendLogs schema and migration changes (#48518)
* ci: spend-logs-guard requires kerry-berri approval on LiteLLM_SpendLogs schema and migration changes

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

* ci(spend-logs-guard): flatten paginated reviews with jq, check out the base ref

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

* ci(spend-logs-guard): read the diff from the pulls/files API instead of fetching PR commits

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

* Revert "ci(spend-logs-guard): read the diff from the pulls/files API instead of fetching PR commits"

This reverts commit 36da4fe034.

---------

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-27 12:49:23 -07:00
ryan-crabbe-berri
34134d7ae5
fix(auth): log routine 401 rejections as one warning without a traceback (#48613)
A missing or malformed Authorization header, a blocked key, a non-admin key on an
admin-only or master-key-only route and a SCIM-deactivated user were raised as bare
exceptions, so the auth failure handler could not see the 401 and logged each one at
ERROR with a full traceback, which flooded the log under scanner traffic

They now raise a typed 401 carrying the quiet-log marker that the non-sk bearer
rejection already used, and the unknown sk- key rejection joins the same path, so
each lands as one WARNING line on stdout with no traceback. The client response is
unchanged
2026-10-27 12:46:22 -07:00
Mateo Wang
618d5aec71
fix(mcp): load deprecated package aliases on demand (#48615)
* fix(mcp): load deprecated package aliases on demand

* fix(mcp): preserve lazy child access and module reloads
2026-10-27 19:46:19 +00:00
moe-berri
29946e4b75
feat(hosted_vllm): accept images on decision deployments (#48621)
* test(decisions): cover hosted_vllm decision images

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

* feat(hosted_vllm): accept images on decision deployments

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>
2026-10-27 12:43:36 -07:00
yucheng-berri
788868a52f
fix(security)!: build proxy-only failure logs from an allowlist and drop caller guardrail info (#48599)
* fix(proxy): build proxy-only failure logs from an allowlist and ignore request-time GCS credentials

* fix(proxy): drop caller metadata from proxy-only failure logs

metadata and litellm_metadata leave the proxy-only failure-log allowlist, so a rejected request can no longer point its failure log at its own Langsmith or Langfuse host through nested metadata.

* fix(proxy): keep allowlisted metadata in proxy-only failure logs

* test(proxy): isolate proxy-only failure log metadata tests

* fix(logging): ignore non-list request tags when building the logging payload

A guardrail-rejected request with metadata tags set to a non-list value crashed
the standard logging payload, so its spend row lost model attribution.

* fix(proxy): keep router attribution and drop unauthenticated tags in proxy-only failure logs

A request rejected before routing now logs the configured deployment and model group even when its metadata or litellm_metadata carries its own values. Requests rejected at auth no longer write caller tags into failure logs and tag spend.

* fix(proxy): always strip client guardrail info and mirror failure attribution into litellm_metadata

allow_client_pricing_override no longer keeps a caller-supplied standard_logging_guardrail_information on the chat or pass-through paths, since its guardrail_cost is billed. Proxy-only failure logs now also carry key and team attribution in litellm_metadata.

* refactor(proxy): keep the proxy-only failure-log fixes inside the type gates

Types the picked helpers so basedpyright and the type-discipline gate stay at main's counts. No runtime change.

* fix(proxy): keep the WS frame guardrail-info strip and string interaction tags as sent

The guardrail-info key left the pricing strip list, so the Responses WS frame guard now drops it by name. get_request_tags no longer raises on string tags, so background interaction polling checks for them before keeping them as sent.

---------

Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-10-27 12:40:59 -07:00
devin-ai-integration[bot]
189ab85438
fix(bedrock): correct gpt max_input_tokens and add Mantle token counting for claude-opus-5-5 (#48479)
* fix(bedrock): set gpt-6.1-terra max_input_tokens to the 1,050,000 Bedrock card context window

* fix(bedrock): set gpt-5.x and gpt-6 max_input_tokens to the card context window minus max output

* fix(bedrock): add Mantle token_count_model to bedrock_mantle claude-opus-5-5 rows

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

---------

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: kerry <kerry@berri.ai>
2026-10-27 19:38:14 +00:00
moe-berri
2dc4847579
fix(token_counter): count Anthropic-format tool_choice without KeyError (#48624)
* test(token_counter): cover Anthropic tool_choice counting

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

* fix(token_counter): count Anthropic-format tool_choice without KeyError

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>
2026-10-27 12:33:48 -07:00
moe-berri
872bfd5102
fix(proxy): return 403 naming the Enterprise licence for get_spend_routes without a licence (#48623)
* test(proxy): cover enterprise spend permission errors on key and user creation

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

* fix(proxy): return 403 naming the Enterprise licence for get_spend_routes without a licence

Co-authored-by: er-s-an <92594860+er-s-an@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): narrow parsed permissions before checking spend access

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

* fix(ci): allow full-history database checkout to finish

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: er-s-an <92594860+er-s-an@users.noreply.github.com>
2026-10-27 12:32:48 -07:00
moe-berri
32297c9177
fix(logging): match extra_spend_tag_headers case-insensitively (#48620)
* test(logging): cover extra spend tag header casing

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

* fix(logging): match spend tag headers case-insensitively

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>
2026-10-27 12:16:34 -07:00
moe-berri
02605a14ad
fix(anthropic): estimate initial input tokens for bridged message streams (#48618)
* fix(anthropic): estimate initial input tokens for bridged message streams

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

* fix(anthropic): count iterable prompt blocks in initial usage

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

* fix(anthropic): avoid fetching images during initial usage estimation

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>
2026-10-27 12:16:15 -07:00
Kerry Lu
5667ac8497
feat(proxy): refuse migrations that existing rows would make fail, before the first one runs (#48493)
* feat(proxy): refuse migrations that existing rows would make fail, before the first one runs

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

* test(migrations): start the serving-proxy preflight case from a database without the api_key index

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

* fix(proxy): type the preflight count rows without casts

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

* style(proxy): ruff format the db preflight flag

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

* style(proxy-extras): keep the extras files at their existing formatting

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

* refactor(proxy-extras): type the ledger query and the reader pid so the type gate holds

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

* fix(proxy): declare the preflight report type where the extras package may be absent

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

* fix(proxy): suppress the optional-extras unknown on the preflight report variables

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

* fix(proxy): suppress the optional-extras unknown on the preflight import

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

* fix(proxy): return a typed preflight report when the extras package is unknown

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

* style(proxy): sort the preflight import block

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

* fix(proxy): mark the preflight report binding Final

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

* feat(proxy): size the whole btree entry in the row check and name the flag for what it does

The index check now lays out each row's entry the way index_form_tuple does (tuple header, null bitmap, every indexed column at its alignment, rounded to 8) and compares it with BTMaxItemSize for the server's block_size, so a key that passes octet_length > 2704 but still fails the (api_key, startTime) index is caught. --db_preflight becomes --check_migrations_against_existing_data, db_preflight.py becomes migration_data_checks.py and the timeout env var LITELLM_MIGRATION_DATA_CHECK_TIMEOUT

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>
2026-10-27 12:14:42 -07:00
Kerry Lu
0b66a27528
feat(bedrock): price native guardrailConfig trace usage into spend and x-litellm-response-cost (#48278)
* feat(bedrock): price native guardrailConfig trace usage into spend and x-litellm-response-cost

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

* test(integration): bill native Bedrock guardrail trace usage on chat, responses and pass-through

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

* fix(streaming): record the Bedrock guardrail trace of a streamed metadata chunk on the logging object

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

* feat(bedrock): apply guardrailConfig on /v1/messages and price its trace, record the stream trace on both builder paths, redact the native guardrail_response

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

* refactor(bedrock): validate the guardrail trace with one pydantic model

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

* fix(bedrock): record the native guardrail entry when litellm_params metadata is None

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

* test(bedrock): import the renamed messages invoke transformation module

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

* fix(bedrock): skip native guardrail billing on cache hits and redact the trace with message logging off

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

* fix(bedrock): walk the guardrail trace iteratively when redacting it

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

* refactor(bedrock): record the native guardrail trace where the Bedrock response is parsed

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

* test(bedrock): type the native guardrail trace fixtures and locals

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>
2026-10-27 12:10:45 -07:00
Kerry Lu
c39b5a3efd
feat: generate provider_endpoints_support.json from the cost map (#48619)
* feat: generate provider endpoint support flags

* test: remove derived endpoint flag assertions

* test: run provider endpoint generator in CI

---------

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-27 12:06:55 -07:00
prassu017
a2c23d95ae
fix(responses): replay web_search_call input items that have no action (#48417)
* fix(responses): replay web_search_call input items that have no action

* Inline web_search_call replay fallback

* fix(responses): validate replay fields when action is missing

---------

Co-authored-by: moe-berri <moe@berri.ai>
2026-10-27 11:58:01 -07:00
Mateo Wang
0a1dbc0fb4
refactor(mcp): drop the experimental label from the MCP server and client paths (#48513)
* refactor(mcp): drop the experimental label from the MCP server and client paths

Move litellm/proxy/_experimental/mcp_server to litellm/proxy/mcp_server and litellm/experimental_mcp_client to litellm/mcp_client, with the mirrored tests moved under tests/unit/proxy/mcp_server and tests/unit/mcp_client. Both old packages stay as shims that warn with DeprecationWarning, re-export the new package's public names and alias every submodule in sys.modules so deep imports keep resolving to the same objects. The OpenAPI docs gate now puts the merge base's enterprise package on PYTHONPATH so its base counts do not mix the checked-out enterprise code with the base litellm tree

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

* ci(mcp): keep tests/unit/proxy/_experimental as a sharded root for the shim tests

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

* fix(mcp): alias namespace subpackages and bind attributes in the deprecated MCP shims

pkgutil.walk_packages skipped mcp_server/auth because it has no __init__.py, so old imports of litellm.proxy._experimental.mcp_server.auth.* failed, and sys.modules entries alone left litellm.experimental_mcp_client.tools unreachable as an attribute. Walk the package on disk and setattr each alias on its old parent.

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>
2026-10-27 17:47:50 +00:00
devin-ai-integration[bot]
0657ed46cf
fix(langfuse_otel)!: derive trace-level name, input, output and tags on traces litellm starts (#42705)
* fix(langfuse_otel): derive trace-level name, input, output and tags on the generation span

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

* fix(langfuse_otel): keep every reasoning summary in the derived observation output

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

* fix(langfuse_otel): leave joined traces untouched by derived trace fields

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

* fix(langfuse_otel): restore preset cache key fallback and type the derived-field helpers

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

* fix(langfuse_otel): keep trace tags off joined traces entirely

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

* test(langfuse_otel): tolerate out-of-band upstream requests and add sink outage chaos cell

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

* test(integration): build the langfuse rig config without mutating the loaded yaml

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

* refactor(langfuse_otel): flatten output items with itertools.chain to satisfy LIT014 budget

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

* test(langfuse_otel): type the _RecordingSpan and _emitted unit helpers

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

* fix(langfuse_otel): keep joined-trace tags, mask trace io, leave v2 trace io to the root

Joined traces (existing_trace_id) keep the caller tags they carried before,
and only the derived trace fields are skipped for them. mask_input and
mask_output now redact the derived trace input and output, matching the
vanilla Langfuse callback. The v2 mapper derives the trace name only,
because the root observation already carries the trace input and output
there. Uses the public preset cache key helper, moves imports to module
top, restores the original formatting of the unit test file, and adds
integration cells for the v2 llm_only scope and SDK sync/async calls.

* fix(langfuse_otel): split string caller tags the way Langfuse reads them

A caller tags value given as a comma list or a JSON array string (body
metadata.tags or the langfuse_tags header) used to reach Langfuse raw,
where ingestion split it into separate tags. Merging it with the derived
tags kept it as one tag, so parse it the same way before the merge

* test(otel): expect langfuse.trace.name on the v2 langfuse_otel generation span

* fix(langfuse_otel): read string mask flags the way the classic callback does

The langfuse_mask_input / langfuse_mask_output headers arrive as strings,
so "false" redacted the derived trace io. Parse them with str_to_bool and
fall back to truthiness, as the classic langfuse callback does

* fix(langfuse_otel): derive trace fields only on traces litellm starts

A litellm span that joins a trace an app or caller started (inbound
traceparent, the app's active span, a caller-provided parent span) no
longer gets a derived trace name, input, output or tags, like a request
joining an existing_trace_id. Langfuse applies the later-starting span's
trace update, so the derived values overwrote the app's own trace name
and io.

OTel v2 derives the name on the request root only when it has no parent,
and the generation span keeps main's caller-only trace name.

* test(langfuse_otel): keep main's OTel v2 generation key set in the arize family cell

The OTel v2 generation span no longer carries a derived langfuse.trace.name,
so the pinned key set goes back to main's.

* fix(langfuse_otel): name the OTel v2 request root once, from the request's own call

The v2 Langfuse logger re-derived langfuse.trace.name on the request root
for every LLM call in the request. A post_call llm_as_a_judge guardrail's
judge call replaced the caller's trace_name on the root with
litellm-acompletion, and with another v2 callback (otel, arize_phoenix)
listed before langfuse_otel that vendor's root span gained
langfuse.trace.name on every request.

The derived name now fills only an unnamed root this logger's provider
started, and never comes from a call litellm makes for its own features.
A caller trace_name still names the root on every call, as on main.

* fix(langfuse_otel): keep the derived trace fields from pushing other attributes off a full span

The derived trace name, tags, input and output were written after the OpenInference attributes with no room
check, so a span near the SDK's attribute limit (about 34 tools at the default 128) evicted keys main
exported, such as openinference.span.kind, metadata and llm.model_name, and raised the dropped count.
They are now written last and only into the room the limit leaves next to the error attributes a failed
call adds, so a full span exports what it would without them.

* test(langfuse_otel): give the full-span cells' derived request a caller trace id, as the joined one's traceparent does

* chore(langfuse_otel): drop the docstrings and comments the house rule disallows and type the new tests' parameters

* test(langfuse_otel): set the span attribute limit the full-span tests run at and keep Final locals out of the app-trace loop

---------

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yucheng-berri <293926326+yucheng-berri@users.noreply.github.com>
2026-10-27 10:34:55 -07:00
Mateo Wang
14193435b8
refactor(anthropic): rename llms/anthropic/pass_through to llms/anthropic/messages (#48510)
* refactor(anthropic): rename llms/anthropic/pass_through to llms/anthropic/messages

Move the unified /v1/messages code out of a directory called pass_through, since it is not the
passthrough feature. The inner messages sub-package becomes native, and the Vertex AI partner
model experimental_pass_through package becomes messages. The old packages stay as import shims
that warn once and alias every submodule in sys.modules, so old deep imports still resolve

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

* refactor(anthropic): keep Final variables out of loops in the rename shim

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

* test(anthropic): match the warning text in the rename shim test

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

* refactor(anthropic): resolve old pass_through imports lazily through a meta path finder

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

* refactor(anthropic): mark the alias loader spec restore for the type discipline gate

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

* style(anthropic): format the renamed package shim

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

* style(anthropic): keep the rebind-ok marker on the spec restore line

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

* refactor(anthropic): carry the new package search path on alias specs

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

* test(anthropic): declare the shim test locals Final

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>
2026-10-27 10:08:25 -07:00
shrey-berri
cfe0100e1f
fix(router): preserve tag fail-open policy through affinity and cooldown (#48470)
Some checks are pending
unit / caching-local (push) Blocked by required conditions
unit / core-utils (push) Blocked by required conditions
unit / endpoints (push) Blocked by required conditions
unit / enterprise-package (push) Blocked by required conditions
unit / integrations (push) Blocked by required conditions
unit / llms-anthropic (push) Blocked by required conditions
unit / llms-bedrock (push) Blocked by required conditions
unit / llms-openai-meta (push) Blocked by required conditions
unit / llms-providers (push) Blocked by required conditions
unit / llms-vertex-ai (push) Blocked by required conditions
unit / mcp-elicitation (push) Blocked by required conditions
unit / mcp-server (push) Blocked by required conditions
unit / proxy-auth (push) Blocked by required conditions
unit / proxy-endpoints (push) Blocked by required conditions
unit / proxy-extras (push) Blocked by required conditions
unit / proxy-feature-endpoints (push) Blocked by required conditions
unit / OpenAI 2.20.0 compatibility (push) Waiting to run
unit / OpenAI 3.0.0 compatibility (push) Waiting to run
unit / proxy-hooks-client (push) Blocked by required conditions
unit / ui-unit (push) Waiting to run
unit / docs (push) Waiting to run
unit / helm (push) Waiting to run
unit / OpenAI 3.25.0 compatibility (push) Waiting to run
unit / redis-py 5.3.1 compatibility (push) Blocked by required conditions
unit / redis-py 6.4.0 compatibility (push) Blocked by required conditions
unit / redis-py 7.4.1 compatibility (push) Blocked by required conditions
unit / redis-py 8.0.1 compatibility (push) Blocked by required conditions
unit / coverage (push) Blocked by required conditions
unit / unit passed (push) Blocked by required conditions
GitHub Actions Security Analysis / zizmor (push) Waiting to run
2026-10-26 22:13:29 -07:00
ishaan-j-berri
aa33b052d2
fix(messages): honor OpenAI stop_sequences (#37118) (#48534) 2026-10-26 20:28:44 -07:00
ishaan-j-berri
74ce6d1d66
fix(databricks): reuse base OAuth for decisions (#48395) (#48527)
* fix(databricks): reuse base OAuth cache for decisions (#48395)

* test(databricks): update moved token cache inventory (#48395)
2026-10-26 20:18:16 -07:00
ishaan-j-berri
e5757934fd
fix(azure): add keyless Decisions authentication (#48386) (#48529)
* fix(azure): support Entra auth for Decisions APIs (#48386)

* fix(azure): simplify Decisions provider registration (#48386)

* refactor(azure): reuse base Azure auth for Decisions (#48386)
2026-10-27 03:09:58 +00:00
tin-berri
53a4cee0d8
fix(ui): honor dark and local logos across Connect and Chat (#48532) 2026-10-27 03:00:03 +00:00
ishaan-j-berri
ebe0871a28
fix(decisions): preserve omitted probabilities (#48449) (#48524) 2026-10-26 19:49:32 -07:00
tin-berri
f2b59be6ea
fix(vertex): keep Messages authentication off the event loop (#48523) 2026-10-26 19:43:23 -07:00
tin-berri
cc09b52616
fix(proxy): include Claude web search fees in streamed usage cost (#48528) 2026-10-26 19:42:38 -07:00
moe-berri
cd1ccb8d68
fix(chatgpt): forward client credentials for image generation (#48522)
* fix(chatgpt): forward client credentials for image generation

* fix(chatgpt): validate image headers at the typed boundary

* refactor(chatgpt): use read-only image header types

* fix(chatgpt): preserve image header credentials and workspace
2026-10-26 19:37:14 -07:00
ishaan-j-berri
a0d0ce1aa5
fix(token-counter): safely count inline auto image tiles (#48422) (#48526) 2026-10-27 02:31:31 +00:00
Jodok Batlogg
e16d79d635
feat(decisions): support LM Studio decision models (#48448)
* feat(decisions): support LM Studio decision models

Co-Authored-By: Codex <noreply@openai.com>

* fix(ci): resolve OSV dependency findings

Upgrade the extension MSAL dependency and document expiring exceptions for unused Windows filelock APIs while Python 3.10 remains supported.

Co-Authored-By: Codex <noreply@openai.com>

* Clean up ignored vulnerabilities in osv-scanner.tomlchore: remove unrelated dependency and scanner changes

Removed ignored vulnerabilities related to Windows issues and Python version compatibility.

* chore: remove unrelated dependency and scanner changes

* chore: remove unrelated dependency and scanner changes

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: ishaan-j-berri <ishaan.j@berri.ai>
2026-10-27 02:24:09 +00:00
Kerry Lu
3b8ad37296
fix(anthropic): raise DEFAULT_ANTHROPIC_CHAT_MAX_TOKENS from 4096 to 128000 (#48502)
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-26 18:54:50 -07:00
Mateo Wang
7e14638001
refactor(llms): rename model-version transformation files to family names (#48509)
gpt_5_transformation.py becomes gpt_reasoning_transformation.py (OpenAI and Azure),
the Bedrock anthropic_claude3_transformation.py files become anthropic_claude_transformation.py,
anthropic_claude2_transformation.py becomes anthropic_legacy_text_completions_transformation.py,
and vertex_ai_partner_models/llama3 becomes vertex_ai_partner_models/llama. Class and symbol
names are unchanged.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-26 18:53:16 -07:00
tin-berri
5276e5435f
fix(ui): persist model parameter deletions after refresh (#48456) 2026-10-26 18:50:12 -07:00
tin-berri
9ed502f375
fix(decisions): honor base_url alias for custom backends (#48505)
Co-authored-by: Azmi Muwahid <azmimuwahid@gmail.com>
2026-10-26 18:46:43 -07:00
yujonglee
6edbd27f74
feat(rust): add redis-client crate for standalone Redis (#48269)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-26 18:41:46 -07:00
moe-berri
c905cd065d
feat(refuseless): add provider and model pricing (#48497)
* feat(refuseless): add provider and model pricing

* feat(refuseless): expose provider in Add Model catalog

* test(refuseless): verify model input trimming after upstream fix

* fix(refuseless): exclude unavailable free model from catalogs
2026-10-26 18:08:42 -07:00
moe-berri
b1f40b042b
feat(abliteration): add model pricing and capabilities (#48498)
* feat(abliteration): add model pricing and capabilities

* feat(abliteration): expose provider in Add Model catalog

* chore(abliteration): keep provider entries independent of other additions

* fix(abliteration): correct reasoning capabilities and verify input trimming
2026-10-26 18:08:29 -07:00
yucheng-berri
715e9bb08d
fix(prometheus): count provider-side 401s in LLM failure and deployment metrics (#46668)
* fix(prometheus): count provider-side 401s in LLM failure and deployment metrics

The invalid virtual key skip no longer applies to an error the LLM provider
returned while the deployment was called with its own credential, so a 401
for a bad deployment credential increments litellm_llm_api_failed_requests_metric,
litellm_deployment_failure_responses and litellm_deployment_total_requests and
marks litellm_deployment_state as a partial outage again. Proxy-gate rejections
and a failing credential that belongs to the caller (a rejected per-user
credential, an api_key sent with the request, a per-user GitHub Copilot
session) keep the skip

* fix(prometheus): keep a caller's own Anthropic OAuth token failure out of the deployment metrics

A provider 401 on an Anthropic OAuth token the caller forwarded through the proxy is the caller's credential
failing, so it stays out of the deployment failure and outage series like a caller-supplied api_key. Adds
integration cells for an Anthropic deployment's own key, the forwarded caller OAuth token and a direct SDK
call that brings its own key

* test(prometheus): pin how a caller's own key or Authorization header on a deployment is counted

An SDK Router call that passes its own api_key is counted against the one-off deployment the router builds for that key, so the operator deployment keeps only its own failure. A provider 401 on an Authorization header the caller sends through the proxy counts against the deployment, as on v1.80.12

* fix(prometheus): keep caller-sent provider credentials and token-exchange 401s out of deployment metrics

A caller can send its own provider credential in the request body, such as
azure_ad_token, azure_scope, the Azure Entra app fields, watsonx token,
gigachat_access_token, OCI keys, AWS keys or Vertex credentials, at the top
level or inside extra_body. A provider 401 on that credential is the caller's
failure, so it stays out of the shared deployment's failure, request,
LLM-failed and state series, as on main. A token-exchange deployment trades the
caller's identity token on every call, so its 401s stay out as well, matching
the router cooldown.

* test(prometheus): Final locals, precise helper types and one for per comprehension in the new test helpers

* test(prometheus): pin that a provider 401 at a caller-sent api_base counts against its one-off deployment

* fix(prometheus): keep a provider 401 on caller-sent headers out of deployment metrics

A caller can override the deployment's credential with headers it sends in the
request body: headers or provider_specific_header, or an Authorization, api-key,
x-api-key, x-goog-api-key or Ocp-Apim-Subscription-Key entry in extra_headers.
A provider 401 on such a request is the caller's failure, so it stays out of the
shared deployment's failure, request, LLM-failed and state series, as on main.

* fix(prometheus): skip a provider 401 only when the caller's headers carry a credential

The proxy now records headers, extra_headers or provider_specific_header in
credential_fields only when they name a provider credential header
(Authorization, api-key, x-api-key, x-goog-api-key, Ocp-Apim-Subscription-Key),
the same way api_key is recorded. A caller that sends a plain header such as a
trace id still has a provider 401 on the deployment's own key counted against
the deployment.

* fix(prometheus): stamp credential_fields after the proxy forwards the caller's headers

A provider credential header the proxy forwards from the caller's HTTP request
(forward_llm_provider_auth_headers + forward_client_headers_to_llm_api) lands in
data["headers"] after proxy_server_request is built, so the stamp missed it and a
401 on that caller credential was counted against the deployment. Compute the
stamp after the header forwarding steps and pin both forwarded cases.

* fix(prometheus): skip a provider 401 when the caller sends any header or an OpenAI organization

The proxy now records headers, extra_headers or provider_specific_header in
credential_fields whenever the caller sends or the proxy forwards any header
there, and records organization when the caller sends one in the body or as a
forwarded OpenAI-Organization header. A forwarded header can be a gateway key
such as x-portkey-api-key or an OpenAI-Project id, and a wrong organization
gets a 401 from OpenAI, so these 401s stay out of the deployment metrics as on
main instead of marking a healthy deployment as a partial outage.

* fix(prometheus): ignore the user information the proxy adds when stamping caller headers

With add_user_information_to_llm_headers on and header forwarding enabled, the
proxy adds x-litellm-user_api_key_* headers to the forwarded headers. Those
carry the proxy's own key and user details, not the caller's, so they no longer
make a request look caller-scoped, and a provider 401 on the deployment's own
key on such a request is counted.

* fix(prometheus): skip a 401 raised before the provider is called or at the caller's own endpoint

A deployment with no key of its own fails with LiteLLM's local "Missing
Anthropic API Key" 401 before any provider call, and a caller who sends
aws_bedrock_runtime_endpoint (or another provider endpoint field) can get a
401 from their own gateway. Both were counted against the shared deployment
and marked it as a partial outage, where main skipped every 401.

Count a 401 only once the provider was called (api_call_start_time is set),
and treat caller-sent provider endpoint fields like caller credential fields.

* fix(prometheus): count the deployment's own 401 on requests that carry only anthropic-version or anthropic-beta

The Anthropic SDK sends anthropic-version on every request, and the proxy copies
anthropic-version and anthropic-beta into provider_specific_header for Anthropic,
Bedrock and Vertex. Both were treated as caller headers, so a 401 on an Anthropic
deployment's own key from any Anthropic SDK client stayed out of the deployment
metrics.

These two headers only pick the API version and beta features. Anthropic answers
a bad anthropic-version or an unknown anthropic-beta with a 400, never a 401, so
they cannot turn the deployment's own key into a 401 and no longer mark the
request as carrying a caller header.

* test(prometheus): read and write the owned proxy's own database in the expired-key and MCP cells

Since #47213 an owned proxy boots on a private database, so the expired-key update and the MCP guardrail row read now go through the gateway's database_url instead of the worker's.

* fix(prometheus): keep credential_fields as main stamps it and give Prometheus its own caller_credential_fields

credential_fields tells the Rust OCR bridge which stripped body fields the
caller sent. Repurposing it for the Prometheus caller-credential check changed
its value (organization and extra_headers added, anthropic-version-only
headers dropped). Restore main's stamp unchanged and stamp the Prometheus
classification under caller_credential_fields, which only Prometheus reads.

* fix(prometheus): skip a provider 401 on a stored credential the caller picks by litellm_credential_name

A request body litellm_credential_name swaps the deployment's stored
credential for one the caller chose, so a 401 on it is the caller's, not the
deployment's. Treat it like the other caller credential fields so it no longer
marks a healthy shared deployment as a partial outage.

---------

Co-authored-by: yucheng-berri <293926326+yucheng-berri@users.noreply.github.com>
2026-10-26 17:57:06 -07:00
devin-ai-integration[bot]
549e6d4f46
fix(router): keep DB-only deployments priced across price data reloads (#48486)
* fix(router): track every router for price data reloads so DB-only deployments keep custom pricing

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

* fix(router): register the router for reload replay only once model_list exists, and type the regression test

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

---------

Co-authored-by: joshua <joshua@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-26 17:17:52 -07:00