Commit graph

46149 commits

Author SHA1 Message Date
Miles Adkins
6d80d05099 fix(fireworks_ai): align extras translation with the API gateway matrix
min_tokens is accepted natively by the Fireworks API (verified live), so
stop stripping it and let it pass through extra_body. Add the NIM-specific
include_reasoning and nvext keys to the strip set. enable_thinking=true
now omits reasoning_effort (model default) instead of forcing medium,
matching the gateway translation and preserving default-off models'
behavior; enable_thinking=false still maps to none.
2026-08-05 15:56:33 -05:00
Yassin Kortam
8ec562f279
fix(ai21): resolve the documented AI21_API_KEY instead of a misspelled name (#35985)
get_api_key resolved the ai21 key from AI211_API_KEY, with a doubled 1. Every other
ai21 code path reads AI21_API_KEY, including the validate_environment branches that
report it as the missing one, so the name a user is told to set was ignored here.

No user path reaches this branch today, since every provider-resolution site rewrites
custom_llm_provider to ai21_chat and sets the key from a correctly spelled read first,
so this is a correctness fix rather than a bug fix. It is worth making because the
env-var documentation gate reads this call site: leaving the misspelling in place would
require a row for AI211_API_KEY in the environment variables reference table, which
would turn a typo into public API
2026-08-05 13:53:13 -07:00
Devin AI
af2246c5b8 fix(anthropic,bedrock): report provider thinking tokens instead of classifying them as text
Resolves LIT-5244

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-05 20:47:05 +00:00
Mateo Wang
df831b807f
chore(typing): replace Any seams with real types across responses, proxy, and provider adapters (#35809)
* chore(typing): replace Any seams with real types across responses, proxy, and provider adapters

Replace Any-typed payload dicts, record shapes, and provider request/response
seams with TypedDicts, Protocols, and precise annotations in the ten litellm/
files carrying the highest combined basedpyright reportAny + reportExplicitAny
counts. No behavior changes.

Adds a regression test covering the managed-id list path so a prisma client
missing the managed tables keeps returning a fail-closed empty page.

* fix(passthrough): walk scalar request bodies through managed-id rewrite again

The top-level dispatch in rewrite_body_ids only handled dict and list
bodies, so a truthy scalar JSON body (bare string, number, bool) hit
dict.items() and raised AttributeError where the merge base passed it
through, and a bare managed-ID string body lost resolution. Restore the
base behavior by dispatching through _walk, widen the implementation to
object with a catch-all overload, and pin both paths with regression
tests
2026-08-05 13:45:21 -07:00
Yassin Kortam
267adf709a
fix(bedrock): sign Bedrock managed-file S3 requests with S3SigV4Auth (#35983)
S3 rebuilds the canonical request from the wire path with single
percent-encoding, which botocore models as S3SigV4Auth. Generic SigV4Auth
quotes the already encoded path a second time, so an object key holding any
character that percent-encodes was signed over %2520 while the request
carried %20, and S3 answered 403 SignatureDoesNotMatch.

The S3 logger was corrected in #35726; the Bedrock managed-files upload and
retrieval paths copied that same pre-fix pattern and were left behind, so a
configured bucket prefix with a space 403s every file upload and every file
content read.
2026-08-05 13:43:30 -07:00
Yassin Kortam
de00655363
fix(migrations): keep the toolchain heal from raising on an unreadable nodeenv cache (#35986)
heal_incomplete_nodeenv_cache() stats a $HOME-derived path with a bare
Path.is_dir(). pathlib only swallows ENOENT-shaped errnos, so a cache
directory the process cannot search raises PermissionError instead of
answering False. Images bake that cache under the build user's home, whose
mode is 0700, so a container started under any other uid dies there before
the Prisma CLI is ever invoked, and the migration never runs.

Tolerate OSError while inspecting the cache, matching the guard
nodeenv_cache_dir() already carries, so an unreachable cache means there is
nothing to heal rather than a crash. This restores the never-raises
contract ensure_prisma_toolchain() documents.
2026-08-05 13:38:53 -07:00
Yassin Kortam
7bd3a5e6ab
fix(docker): bake the componentized prisma engines at /opt/prisma so any uid can start (#35989)
The gateway and backend images generated the prisma client under
HOME=/home/nonroot, so the engine paths baked into the client sat inside a
directory the base image ships at mode 0700. Only uid 65532 can search it,
and prisma resolves those baked paths eagerly with an existence check that
propagates EACCES, so a container started under any other uid dies with a
PermissionError out of pathlib before the PRISMA_QUERY_ENGINE_BINARY
override is ever read. A chart that sets runAsUser, a docker run --user, or
an OpenShift namespace assigning an arbitrary uid all produce that shape,
and the gateway is the request-serving component, so the proxy does not
serve at all.

Bake to /opt/prisma instead, the fixed world-readable path the other three
images already use, and assert at build time that every baked path lands
there. chmod a+rX rather than a+r because prisma executes the engine to
check it can run on this machine. The runtime PRISMA_BINARY_CACHE_DIR pin
keeps the CLI wrapper's own resolution pointing at the bake rather than at
a /home/nonroot/.cache that no longer exists.
2026-08-05 13:38:46 -07:00
ryan-crabbe-berri
2dc49a913c
refactor(ui): replace hand-rolled query-param routing with nuqs (#35871)
* refactor(ui): replace hand-rolled query-param routing with nuqs

The dashboard carried five copies of the same pushState-based detail
routing hook plus a shared navigateWithParams helper, each with its own
plumbing test and a copy-pasted reactive useSearchParams mock in
component tests. nuqs provides the same shallow history-API routing
behind useQueryState/useQueryStates, so the key, team and org hooks are
deleted in favor of inline useQueryState at their single consumers,
while the models and logs hooks keep their interfaces but drop their
hand-rolled internals. Component tests now mount NuqsTestingAdapter
(via renderWithProviders or locally) instead of patching window.history,
and URL assertions go through onUrlUpdate spies that can additionally
distinguish push from replace, which the old window.location checks
could not

* test(ui): assert browser back closes the log drawer after in-drawer selection

Greptile flagged that the nuqs port of the switching-logs test stopped
at asserting emitted push and replace modes. The test now replays those
recorded modes against a history stack and performs the back step, so a
regression to push-on-select or broken URL-derived drawer state fails
the test instead of passing silently
2026-08-05 13:29:58 -07:00
mateo-berri
3728f3ea62 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_daily_any_cleanup_08_04_2026 2026-08-05 13:14:37 -07:00
Miles Adkins
599283584f feat(fireworks_ai): drop reasoning_effort=auto to the model default
Fireworks rejects reasoning_effort="auto" (accepted set: low, medium,
high, xhigh, max, none, adaptive), so OpenAI-compatible clients sending
it 400. Omitting the param means model default on Fireworks, which is
exactly what auto means on OpenAI's side, so skip it in
map_openai_params instead of forwarding.
2026-08-05 15:09:16 -05:00
tin-berri
32deaff015
feat(spend): rebuild the auto-router benchmarks backend as a per-session rollup (#35910)
Folds every successful auto-routed request into LiteLLM_AutoRouterSession with one
conditional upsert at spend-write time, classifying each turn (same model, first
visit, return to tier, out of order) against the row's own columns so nothing is
read before the write. The upsert's placeholders and argument tuple both derive
from the transaction dataclass's own field order, so the SQL and the call site
cannot drift apart. GET /auto_router/benchmarks aggregates the rollup, grouped
by the full (router, type) identity, and never scans LiteLLM_SpendLogs. A turn's
cache interaction is derived once from its usage record (savings.py owns the
extraction; compute_savings_spend derives cache reads from usage_object itself),
hits are counted order-independently so the overall hit rate matches its covered
denominator, caller-chosen session ids are bounded before entering the primary
key, and a poisoned statement drops only its own session's remaining turns.
Return misses inside the recorded TTL are named for what the telemetry shows
(within_ttl) rather than a presumed cause, since a provider can evict early.
Savings ride each router's derived baseline by default, so the response carries
no deployment-wide baseline label. Rollup retention has its own
maximum_autorouter_session_retention_period setting, pattern-identical to the
spend-logs knob and running in the same cleanup job on its own cutoff. Every
drain trigger sizes the queues through one owner and the enqueue honors
disable_spend_logs beside the tool-usage queue it mirrors.
2026-08-05 20:06:32 +00:00
Abhimanyu Kapur
bea65b6dcc
fix(autorouter): match CJK keyword_tier_rules that regex word boundaries miss (#35984)
* fix(autorouter): match CJK keyword_tier_rules that regex word boundaries miss

Single-word keywords were matched with a \b...\b regex. Every CJK character is a
regex word character and CJK is written without spaces, so \b never fires between
two of them and a rule like 发票 silently missed 我需要开发票, falling through to
complexity scoring instead of the configured tier.

Keywords containing CJK now match as plain substrings, the same way multi-word
phrases already did. The gate reads the keyword rather than the prompt, so a
keyword with no CJK in it keeps word boundary matching regardless of the script
the prompt is written in.

* fix(autorouter): cover Han extensions in planes 2 and 3, not just up to U+2FA1F

The supplementary range stopped at U+2FA1F, so Extension G and H ideographs kept
the word boundary path and stayed unmatchable. Both planes are dedicated to CJK
ideographs, so covering them whole also handles later extensions without chasing
each new block.
2026-08-05 20:02:54 +00:00
Mateo Wang
7c9c9ffbc7
fix(ci): fetch only head and merge-base in lint jobs instead of every branch (#35982) 2026-08-05 13:00:00 -07:00
Mateo Wang
c6dbf48944
Merge pull request #35916 from BerriAI/litellm_passthrough_live_credentials
fix(proxy): resolve pass-through credentials live from router deployments
2026-08-05 12:57:46 -07:00
Yassin Kortam
347798b80e
fix(router): keep custom model_info across a price data reload (#35491)
A price data reload replaced litellm.model_cost wholesale, discarding every
runtime registration: the deployment model_info the Router registers from
model_list, and pricing overrides passed to litellm.register_model. Custom
model groups lost max_input_tokens / max_output_tokens in /model_group/info,
and a deployment whose backend model is in the catalog silently reverted to
upstream values. Runtime registrations are now recorded and replayed on top of
the freshly fetched catalog.

Router._pre_call_checks resolved the per-deployment model name only after the
model-info lookup, so an unregistered model left it unset and the supported
params check ran against the bare model group name, raising "LLM Provider NOT
provided" out of deployment selection. The name is now resolved first, and an
unresolvable provider skips that check rather than failing the request.

Resolves LIT-4675
2026-08-05 19:56:50 +00:00
Yassin Kortam
7ac1085931
fix(auth): return 403 from the OAuth2 enterprise gate (#35838)
The enterprise gate on the OAuth2 auth path raised a bare `ValueError`,
which the terminal handler in auth_exception_handler.py converts to a 401.
Every sibling enterprise gate answers 403, including `_premium_user_check`
and the SSO gate. A 401 tells the client its credential was wrong and to
retry with a better one, and no credential can satisfy that while the
install is unlicensed, so it invites a retry loop that can never succeed.

It now raises a 403 `ProxyException` shaped like the SSO gate. Two response
fields move with it: the `Authentication Error, ` prefix goes away, since
the catch-all built that around `str(e)` and a `ProxyException` is re-raised
unmodified, and `param` becomes `premium_user`, naming the condition an
operator has to clear.

The gate's own text also gains the sentence break it was missing. The
message concatenated straight onto `CommonProxyErrors.not_premium_user`,
rendering as "premium usersYou must be a LiteLLM Enterprise user".
2026-08-05 12:53:56 -07:00
Yassin Kortam
4e8e4a7162
fix(docker): bake the pip image's prisma engines at a world-readable path (#35976)
The build_from_pip image ran a bare `prisma generate`, so prisma recorded
absolute engine paths under $HOME/.cache, which is /root/.cache in that
build. /root is mode 0700 on python:3.13-slim, so any runtime uid other
than 0 gets EACCES just traversing it and the proxy dies during prisma
client initialisation. That is exactly the shape a securityContext with
runAsUser produces.

Generate under a fixed /opt/prisma and chmod it a+rX, matching what the
shipped images already do, and pin PRISMA_BINARY_CACHE_DIR at runtime so
the client resolves the baked engines instead of looking under $HOME. A
build-time assertion fails the build if any recorded engine path lands
outside the pinned prefix, since the original breakage was silent at
build time and only surfaced as a runtime crash for non-root users.
2026-08-05 12:51:53 -07:00
Abhimanyu Kapur
b8df48cd7f
feat(auto-router): let operators replace the LLM classifier's system prompt (#35855)
* feat(auto-router): let operators replace the LLM classifier's system prompt

The complexity router's LLM classifier has always sent one built-in rubric, so the
router could only ever grade difficulty. Operators can now supply their own system
prompt, which replaces the rubric outright and repurposes the same tier machinery for
whatever taxonomy the prompt defines, data sensitivity being the obvious case.

Replacement is total: neither the rubric nor its closing line is appended, since both
describe grading difficulty over a "current message" and a prompt grading something
else is entitled to contradict them. That closing paragraph is also the classifier's
prompt-injection defense, so the config field and the dashboard editor both warn that
a replacement omitting it lets a caller ask for a tier and get it.

The heuristic fallback still scores complexity, which is meaningless for a repurposed
taxonomy, so classifier_fallback now chooses between the heuristic scorer and routing
straight to default_model. The default_model path bypasses tier pools, the adaptive
bandit, and escalation, because no tier was decided and the point of that fallback is
a known destination. It reports itself as default_model_fallback in the spend logs.

The dashboard's prompt editor prefills from a new
/auto_router/classifier/default_prompt endpoint rather than a copy of the rubric in
the frontend, and stores no override when the draft matches the default, so later
rubric improvements still reach every router that never customized it.

Tier names stay SIMPLE/MEDIUM/COMPLEX/REASONING; a custom prompt redefines what they
mean, not what they are called.

* fix(complexity-router): don't let the default_model classifier fallback bypass routing plugins

* fix(complexity-router): don't pin a session to the default model after a classifier failure

* fix(complexity-router): omit the tier from a default-model-fallback routing decision

The classifier never answered, so no tier was decided. The record reported the
tier whose pool happens to hold default_model, which reads in the spend log and
the UI as if the request was classified. Matches how default_fallback already
records a route that no tier produced.

* fix(proxy): allowlist /auto_router/ on the UI backend component

The new GET /auto_router/classifier/default_prompt is a UI-consumed management
route, so it belongs on the control plane. Without the prefix it was exposed by
neither component and test_gateway_plus_backend_covers_full_app failed.

* docs(ui): reword the classifier prompt disclaimer

Frames the closing paragraph as a strong recommendation rather than a
description of what gets dropped, names prompt injection explicitly, and
notes the tier names stay fixed regardless of their display names.

* fix(complexity-router): stop logging a fabricated tier on the plugin fallback path

The classifier-failed fallback resolves a tier so the routing-plugin pipeline has a
pool to filter, but nothing about the request produced that tier. The non-plugin
short-circuit already dropped it from the logged decision; the plugin path still
reported it, so a spend log claimed a classification the request never received.
Record the pool as a plugin-filtered-pool signal instead.

Also name the real problem when the resolved tier has no models at all: that raised
"No candidate models left after routing-plugin filtering" and sent operators hunting
for a policy plugin that never narrowed anything.
2026-08-05 19:48:11 +00:00
mateo-berri
2bd6af1b64 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_daily_any_cleanup_08_04_2026 2026-08-05 12:43:11 -07:00
Yassin Kortam
09dd167b5a
feat(sgr): make the gateway middleware the source of truth for successful requests (#35717)
SGR has had two independent definitions. The admin UI derived it from
SpendLogs, so it counted what litellm's logging callbacks observed and could
attribute and price. BillableRequestMetricsMiddleware counted what the proxy
actually answered at the ASGI edge, but only exported to OTLP for enterprise
metering. The two disagree by design in places, and the SpendLogs figure goes
quiet whenever spend logging is disabled or the callbacks are bypassed.

This adds LiteLLM_DailyGatewayRequests, written by the middleware, and points
the dashboard's Successful Requests tile at it.

Requests fold into an in-memory map at record time rather than going through a
queue like the spend path. A count is a pure aggregate, and every dimension of
the key is chosen by the proxy from a closed set: the date, the category, and a
route that the classifier maps to one of a fixed list of strings rather than
passing the raw path through. Nothing a caller sends can add a key, so the fold
and the table are bounded by (days x categories x routes) however much traffic
arrives; the spend queue blocks once full, which is not acceptable in the
response path. A scheduler job drains it on the existing batch interval, and a
failed flush merges its counts back so a database blip undercounts nothing.

The middleware previously returned early when no billing recorder was
injected, which is the unlicensed case. The new sink is not license-gated, so
that early return now requires both sinks to be absent. The billing recorder
keeps its 2xx-only gate; the sink takes every status so failed_requests is
real. The sink is not told which deployment served the request, unlike the
billing recorder. That id is a sha256 over litellm_params, credentials
included, so a caller who puts a credential in the request body mints a fresh
one per distinct value. No configuration is needed for that: api_base and
base_url are on _BANNED_REQUEST_BODY_PARAMS and need allow_client_side_
credentials, but api_key is not on that list, and both reach the same
_handle_clientside_credential branch. The read endpoint aggregates the
dimension away regardless, so the key is better off without it.

The new table carries no key, user or team dimension, so /gateway/daily/activity
is restricted to proxy admin roles and the per-key and per-model breakdowns
keep reading the daily spend tables. The old path is left running and marked
with TODOs.

A fetched result carries the range key it was fetched for, and the render
selects it only when that key matches the range on screen. Both the gateway
counts and the spend aggregate go through that rule: the request tiles read the
first and fall through to the second, so stamping only one of them would leave
the tile showing a superseded range by the other route.

The paginated pages behind that aggregate are reached through a failure flag,
so the flag is stamped too. A flag left over from the previous range would let
those pages through while a new range is in flight, which is the same defect
one fallback further down.
2026-08-05 12:40:47 -07:00
mateo-berri
18572fe86f Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_passthrough_live_credentials
# Conflicts:
#	basedpyright-code-budget.json
#	litellm/types/router.py
#	ruff-strict-budget.json
#	type-discipline-budget.json
2026-08-05 12:40:32 -07:00
mateo-berri
f2049a7d9b fix(proxy): narrow pass-through provider resolution to BadRequestError 2026-08-05 12:37:12 -07:00
Mateo Wang
332ec6c17a
Merge pull request #35926 from BerriAI/litellm_remove_types_ruff_exclusion
chore(lint): remove litellm/types from the ruff lint exclusion
2026-08-05 12:35:02 -07:00
mateo-berri
0a0c91483d Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_passthrough_live_credentials 2026-08-05 12:31:38 -07:00
mateo-berri
f7bdc10b21 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_passthrough_live_credentials
# Conflicts:
#	ruff-strict-budget.json
#	type-discipline-budget.json
2026-08-05 12:31:38 -07:00
Yassin Kortam
2a9843e649
fix(proxy): keep the connected DB client when a startup health check fails (#35837)
`_setup_prisma_client` ran `connect()`, then a `SELECT 1` health check, then
armed the DB health watchdog. Any failure fell into one handler that, with
`allow_requests_on_db_unavailable` set, swallowed the error and returned None,
which the caller assigns to the module-level `prisma_client`. A single
transient timeout on that health check therefore discarded a client that had
already connected, for the life of the process, and skipped the watchdog that
exists to reconnect it.

The watchdog now starts before the health check, and a swallowed post-connect
failure returns the connected client instead of None. A client whose
`connect()` failed is still discarded, and startup still hard-fails when
`allow_requests_on_db_unavailable` is not set.

The same check also misreported its own failure. `health_check()` labelled its
error `disconnect()`, a copy-paste from the real `disconnect()` below it, so
grepping the logs for the health check turned up nothing and read as "the check
never ran". Both it and the sibling `connect()` failure reported through
`print_verbose`, which reaches `verbose_proxy_logger.debug` and otherwise prints
only under the deprecated `litellm.set_verbose`, leaving a startup-blocking
database fault invisible at the verbosity operators actually run. Both now log
at warning under their own names. The proxy logger's handler carries the secret
redaction filter, so a connection string in the exception text is redacted
exactly as it was on the old print path.
2026-08-05 12:27:49 -07:00
Mateo Wang
54f83b2614
Merge pull request #35870 from BerriAI/litellm_reland_evicted_client_closer
fix(caching): re-land evicted LLM client closing (#35492) atop self-healing handlers
2026-08-05 12:22:12 -07:00
mateo-berri
6c76f5f9c6 chore(lint): clear grandfathered over-limit lint drift and ratchet budgets down
Every ruff-strict rule that sat above its budget limit (FURB188, RUF022,
SIM118, UP007, UP032, UP037) is now at zero, LIT001 and LIT006 are back
under their ceilings, and the freed headroom is ratcheted out of
ruff-strict-budget.json, type-discipline-budget.json, and
basedpyright-code-budget.json so the gates take the fast path again
2026-08-05 12:18:13 -07:00
mateo-berri
83aca91dde fix(guardrails): allow litellm_content_filter to run on post_mcp_call
ContentFilterGuardrail implements apply_guardrail, which is everything the
generic post_mcp_call_hook machinery needs to scan an MCP tool result before
it reaches the model, but post_mcp_call was missing from
get_supported_event_hooks. _validate_event_hook rejects any mode outside that
list, so a config with `mode: post_mcp_call` failed proxy startup with
"Event hook GuardrailEventHooks.post_mcp_call is not in the supported event
hooks" instead of scanning tool output.

Declaring the hook makes the indirect-prompt-injection case enforceable: an
MCP fetch tool returns a page whose body carries "IGNORE ALL PREVIOUS
INSTRUCTIONS ...", and the gateway blocks the result rather than handing it
to the model.
2026-08-05 12:17:01 -07:00
mateo-berri
d259070fdf Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_daily_any_cleanup_08_04_2026 2026-08-05 12:13:11 -07:00
Yassin Kortam
0b8c58735d
fix(ci): make the env-key doc gate see get_secret_bool reads (#35833)
The gate only matched os.getenv(, litellm.get_secret( and
litellm.get_secret_str(, so a bare get_secret_bool("X") matched nothing and
the key bypassed the documentation requirement entirely. Add a fourth pattern
for get_secret_bool, with or without the litellm. prefix, and a negative
lookbehind so an unrelated receiver's .get_secret*( call is not mistaken for
an env var read.

Extraction and table parsing move into functions behind a __main__ guard so
the patterns can be unit tested; the script is still invoked exactly the same
way by CI.

This surfaces 13 keys the gate never checked, 8 of which have no reference
row yet.
2026-08-05 12:03:15 -07:00
tin-berri
d3d30353aa
refactor(ui): remove the three dashboard lint-budget violations added by #35893 (#35960)
PR #35929 zeroed the eslint budget headroom while #35893 added UI code in parallel, so staging went over budget by one complexity violation and two no-large-inline-object-arg violations, failing frontend-lint on every UI-touching PR until #35964 reverted the ratchet. This removes the three violations at the source so the budgets can ratchet back down: the submit-blocked-reason chain in add_auto_router_tab moves to a module-level helper, taking the component arrow from complexity 21 to 18, and the two four-property object literals in build_complexity_router_config.test.ts move into named variables. No behavior change; the touched suites pass (101 tests)
2026-08-05 11:51:24 -07:00
ryan-crabbe-berri
2792887e47
fix(proxy): give proxy_admin_viewer read parity with proxy_admin (#35851)
* fix(proxy): give proxy_admin_viewer read parity with proxy_admin

Route-level checks already default-allow management GETs for the viewer
role, but ~15 handlers compared user_role to PROXY_ADMIN only, dropping
viewers into regular-user scoping (/key/list, /user/info, /model/info,
guardrails, prompts, agents, memory, workflows, MCP catalog, coordination
redis settings, credential migration check, enterprise projects). Swap
those read paths to user_api_key_has_admin_view; write gates unchanged.

The dashboard now presents the viewer session as Admin for all gating
(effectiveSessionRole) so every page fetches with admin visibility, with
userRoleLabel/isViewOnly preserving the account-menu label and the
playground cost guard. The server remains the write authority.

* refactor(agents): remove side-effectful health_check param from GET /v1/agents

Addresses a security review finding on the admin viewer read parity change:
listing agents with health_check=true made the proxy issue a server-side GET
to every agent URL, so a read-scoped caller could trigger request fan-out
beyond their object permissions. The list endpoint is now a pure read for
every role.

Removes the query param, the URL probing helper and its timeouts, the
AgentHealthCheck httpx provider tag, and the dashboard's Health Check
toggle. Requests still passing health_check=true get the full list back
with the param ignored.

* fix(proxy): keep credential encryption check proxy_admin only

The residual scan behind GET /credentials/migrate-encryption/check loads
every model, credential, MCP, team, and verification-token row and runs a
decryption attempt on each stored value. Extending it to proxy_admin_viewer
let a read-only account repeatedly trigger deployment-wide scans, so the
route keeps its original full-admin gate.

* fix(agents): restore health_check, keep list fast path proxy_admin only

Restores the agent health_check feature exactly as before this PR: the
query param, the URL probing helper, the httpx provider tag, and the
dashboard toggle all return, so existing callers keep the filtering
contract. The viewer expansion is instead reverted at its source: the
GET /v1/agents admin fast path stays PROXY_ADMIN only, so a
proxy_admin_viewer goes through the object-permission scoped branch as
before and cannot fan out health checks beyond their allowlist. The
viewer read of a single agent stays viewer-inclusive since it has no
side effects.
2026-08-05 18:33:55 +00:00
Miles Adkins
0c0e1e8374 feat(fireworks_ai): translate NIM/vLLM extra params to Fireworks-native args
Requests migrated from NIM/vLLM servers carry extras that flow through the
extra_body passthrough verbatim, but the Fireworks chat completions API
either names them differently or does not accept them at all. Add
FireworksAIConfig.map_extra_body_params, invoked from the fireworks chat
dispatch, which renames truncate_prompt_tokens to prompt_truncate_len,
maps chat_template_kwargs.enable_thinking to reasoning_effort, converts
guided_json/guided_grammar/guided_choice to response_format, and drops
the remaining extras (min_tokens, stop_token_ids, skip_special_tokens,
guided_regex, etc.) with a debug log. Alias and competing-constraint
combinations raise BadRequestError. Unrecognized extras keep passing
through untouched, as do fireworks-native params like top_k.
2026-08-05 13:29:28 -05:00
mateo-berri
d59135b5ea Merge branch 'litellm_internal_staging' into litellm_remove_types_ruff_exclusion
Import-block conflicts against #35962's unused-import removals resolve
to this branch's side: its modernization already removed every typing
import staging trimmed, plus the usages
2026-08-05 11:10:34 -07:00
mateo-berri
ebc31f7e06 fix(caching): clear strict-lint budget breaches in re-landed closer code 2026-08-05 11:08:19 -07:00
Mateo Wang
c19896029a
Merge pull request #35964 from BerriAI/revert-35929-litellm_zero_dashboard_eslint_headroom
chore(ui): don't zero out stale headroom except no-console
2026-08-05 11:06:16 -07:00
mateo-berri
1a693a3014 fix(types): resolve the four type-discipline additions surfaced by the staging merge 2026-08-05 11:05:53 -07:00
mateo-berri
51c54b56ac Merge branch 'litellm_internal_staging' into litellm_remove_types_ruff_exclusion
Resolve litellm/types/google_genai/main.py and litellm/types/utils.py by
keeping this branch's modernized annotations on top of staging's removal
of inert type: ignore comments. Rebuild ruff-strict-budget.json from
measured merged-tree counts where de-excluding litellm/types adds
violations, keeping the stricter of the two sides' limits everywhere
else so no rule gains headroom. Fix the four type-discipline additions
the merge surfaced: freeze GEMINI_1_5_ACCEPTED_FILE_TYPES, drop a
callback_args parameter rebind in guardrails, and give the two remaining
mutations reasoned suppressions
2026-08-05 11:05:33 -07:00
Mateo Wang
45ed654603
Merge pull request #35962 from BerriAI/litellm_fix_basedpyright_over_limits
fix(lint): bring basedpyright rule counts back under their budget limits
2026-08-05 11:03:53 -07:00
mateo-berri
8b874263e2 fix(passthrough): walk scalar request bodies through managed-id rewrite again
The top-level dispatch in rewrite_body_ids only handled dict and list
bodies, so a truthy scalar JSON body (bare string, number, bool) hit
dict.items() and raised AttributeError where the merge base passed it
through, and a bare managed-ID string body lost resolution. Restore the
base behavior by dispatching through _walk, widen the implementation to
object with a catch-all overload, and pin both paths with regression
tests
2026-08-05 11:02:07 -07:00
Mateo Wang
85aad29885
chore: make no-console max 12 2026-08-05 10:45:56 -07:00
Mateo Wang
64f83a23e1
Revert "chore(ui): zero stale headroom on local dashboard eslint budgets" 2026-08-05 10:44:02 -07:00
mateo-berri
469d5126f6 fix(lint): bring basedpyright rule counts back under their budget limits 2026-08-05 10:23:02 -07:00
mateo-berri
824608c42b Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_reland_evicted_client_closer
# Conflicts:
#	litellm/llms/azure/common_utils.py
2026-08-05 10:16:37 -07:00
Yassin Kortam
0659738b3e
fix(migrations): recover from an interrupted Prisma toolchain install (#35832)
The Prisma CLI is a Node program that installs a private Node runtime on its
first invocation. That one-time install shared the 60s budget that bounds each
migration command, so on a slow or cold machine it was killed before it could
finish. Prisma then decides whether to reinstall by testing the cache
directory for existence alone, and a killed install leaves that directory
behind, so every later attempt skipped the install and failed on a node binary
that was never written. The existing four-attempt retry loop could not help:
each attempt hit the same missing binary, which turned a slow start into a
container that never migrated again.

Migrations now prepare the toolchain as its own step under its own budget, and
a cache directory that exists without a node binary is deleted first so an
interrupted install reinstalls instead of persisting. Both budgets are
overridable, LITELLM_PRISMA_BOOTSTRAP_TIMEOUT for the install and
LITELLM_PRISMA_COMMAND_TIMEOUT for each Prisma command, and every previously
hardcoded timeout now goes through one helper rather than thirteen literals.
The per-command default stays at 60s.

An override is only honoured when it parses as a finite positive number.
Infinity and NaN parse as floats and survive a plain positivity check, and
subprocess treats either as no deadline at all, so a value like `inf` or a
fat-fingered `1e400` would have silently disabled the timeout it was meant to
configure.
2026-08-05 10:14:46 -07:00
Yassin Kortam
54fb717de1
fix(router): redact fallback tracebacks at the call site and cover the sync deferred stream (#35843)
Three follow-ups surfaced while merging current staging into this branch.

`exc_info=True` at both fallback-failure log sites handed a live exception to
the logging machinery. SecretRedactionFilter rewrites `record.exc_text`, but
`record.exc_info` stays an exception object no filter can reach, so a handler
that renders it itself (Datadog and OTel log bridges do) received the
unredacted provider key. Both sites now pass `redact_string(traceback.format_exc())`
as a `%s` arg, keeping staging's lazy-logging form. The existing test only
asserted on `exc_text`, so it passed under the bug; it now renders `exc_info`
the way a bridge handler would and covers every record the call emits.

The eager deferred-stream fetch existed only on the async path. Vertex and
Bedrock build the same `completion_stream=None` plus `make_call` wrapper on
their sync branches, so `Router.completion(stream=True)` still surfaced the
provider error on first iteration, outside `_completion`'s except block, and
never reached the fallback chain. `_completion` now calls `fetch_sync_stream()`
under the same guard `_acompletion` uses.

The first of the three header-strip passes in the proxy error path was dead:
only the custom-header update and the response-headers hook run before the
second pass re-filters everything. Collapsed to one `safe_headers` binding.
2026-08-05 10:06:52 -07:00
Mateo Wang
8fe9809a4b
Merge pull request #35365 from rimysore/fix-managed-files-null-object
fix(managed-files): skip rows without file objects
2026-08-05 10:01:42 -07:00
Mateo Wang
520e38e232
Merge pull request #35929 from BerriAI/litellm_zero_dashboard_eslint_headroom
chore(ui): zero stale headroom on local dashboard eslint budgets
2026-08-05 09:47:16 -07:00
Mateo Wang
0b8c7f29ed
Merge pull request #35927 from BerriAI/litellm_zero_more_local_basedpyright_rules
chore(lint): zero out seven more purely local basedpyright rules
2026-08-05 09:46:21 -07:00