Commit graph

43876 commits

Author SHA1 Message Date
Tin Chi Lo
2f349f6cd1 fix(mcp): coerce numeric expires_in and make the active-key check total
Two correctness gaps in the bridge mint. _bridge_grant_from_token_response only
accepted an int expires_in, dropping a float (3600.0) or numeric-string ('3600')
lifetime to None so the envelope fell back to its 1h cap and could outlive a
shorter-lived upstream token; coerce it to a positive int (bool excluded). And
_key_is_active called datetime.fromisoformat on the str|datetime expires outside
the resolver's try, so a malformed stored expiry raised an unhandled 500 instead
of the fail-closed invalid_request; it now fails closed (inactive) on an
unparseable expiry. Regression tests cover int/float/string/bool coercion, the
short-float TTL, and the malformed-expiry fail-closed path.
2026-07-11 19:20:03 -07:00
Tin Chi Lo
ceff2d1f3c style(mcp): use X | None annotations on the touched key-resolution helpers
The keyless-user fix moved these signatures, so their pre-existing Optional[...]
annotations counted against the diff and tripped the UP045 strict-budget gate.
Modernize the four touched return annotations to the X | None form the gate
wants; runtime behavior is unchanged.
2026-07-11 19:20:03 -07:00
Tin Chi Lo
362c78e308 fix(mcp): let a keyless-user active key mint a bridge envelope
_resolve_active_litellm_key gated on _active_key_user_id, which returns None both
for blocked/expired keys AND for valid keys with no user_id, so a team-scoped or
service-account key was wrongly rejected with invalid_request at bridge token
exchange. Split the active-state gate (_key_is_active: blocked/expiry only) from
the user_id extraction; the mint seals the key hash, not the user, and admission
already handles a keyless-user key. The per-user token store still gets no user
for such a key, as there is none to key a stored credential by.
2026-07-11 19:20:03 -07:00
Tin Chi Lo
7df848aa6c fix(mcp): return 502 not KeyError when a bridge upstream response lacks access_token
The eager access_token = token_response["access_token"] extraction ran before
the dcr_bridge branch, so a missing upstream access_token raised an unhandled
KeyError and _bridge_grant_from_token_response's nil guard (which maps to a clean
502) was dead code. Move the extraction onto the non-bridge result path so the
bridge branch reaches its 502 guard.
2026-07-11 19:20:03 -07:00
Tin Chi Lo
85255c96fb feat(mcp): seal the authorizing key hash in the dcr_bridge envelope
The mint bound only user_id/server_id into the envelope, which gave admission
no way to reload the caller's key and enforce its current restrictions. Seal the
hashed authorizing key instead (a one-way digest, not a usable credential), so
admission reloads the live UserAPIKeyAuth by it and the key's team/org/tool
permissions and revocation apply per request.

Extract the token endpoint's key resolution into a shared _resolve_active_litellm_key
so the per-user token store (user_id) and the bridge mint (key hash) derive from one
active-key-gated path, and fail the mint closed with invalid_request when no active
key accompanies the request.
2026-07-11 19:20:03 -07:00
Tin Chi Lo
34c6cce705 feat(mcp): mint gateway-bound envelope at the token endpoint for dcr_bridge oauth_delegate 2026-07-11 19:20:03 -07:00
Mateo Wang
053e64e11b
Merge pull request #32979 from BerriAI/litellm_anchor_bedrock_routing_regex
fix(proxy-auth): stop unrecognized model namespaces slipping through provider wildcard keys
2026-07-11 19:18:27 -07:00
Abhimanyu Kapur
d6883d15b0 fix(auto_router): flag name field and tier fields together on empty submit
Clicking Add Auto Router with the name empty returned early with only a
toast, so blank tier selects never got their inline error state. The
empty-name branch now sets showValidationErrors and triggers antd
validation on the name field, so every unfilled mandatory field is
flagged at once. Adds a regression test for the tab component.
2026-07-11 19:08:07 -07:00
Abhimanyu Kapur
3464b5e7df fix(auto_router): reset inline validation errors when switching router type 2026-07-11 19:03:48 -07:00
mateo-berri
d0d1c0e346 fix(proxy-auth): deny provider-wildcard access inferred through an unrecognized model namespace 2026-07-11 19:00:03 -07:00
mateo-berri
2ed4ceb12e fix(model-cost-map): anchor the bedrock-claude-ids routing rule to the start of the id 2026-07-11 19:00:03 -07:00
Abhimanyu Kapur
0de308a5b8 fix(auto_router): filter embedding models out of tier selects, require all tiers, add inline validation
The Add Auto Router complexity tab let chat models fill the embedding-model
slot (and vice versa) since neither dropdown filtered on ModelGroup.mode, and
submit only required at least one of the four tiers instead of all four. Adds
getMissingTiersError alongside the existing getSemanticConfigError, and
highlights unfilled tier/embedding selects inline once a submit attempt fails.
2026-07-11 18:18:37 -07:00
ryan-crabbe-berri
5fc1a3c671
refactor(ui): convert endpoint usage charts to shadcn/recharts (#32723)
* refactor(ui): convert endpoint usage charts to shadcn/recharts

Adds a LineChart wrapper to the shared charts kit, mirroring the
BarChart/AreaChart composition with connectNulls and curveType props,
and converts EndpointUsageBarChart and EndpointUsageLineChart from
tremor to the shared wrappers. Both endpoint chart tests now assert on
real recharts SVG output instead of tremor mocks.

* refactor(ui): drop unused endpointData prop from EndpointUsageLineChart

* fix(ui): point endpoint chart test type imports at the UsagePage types alias after colocation move
2026-07-11 18:16:18 -07:00
mateo-berri
0008d96af4 docs(github): add Final Attestation and per-test sanity-check step to QA runbook 2026-07-11 18:07:37 -07:00
ryan-crabbe-berri
b6dbda48f9
refactor(ui): colocate the mcp-servers view, keeping the shared mcp_tools surface (#32968)
* refactor(ui): colocate the usage view, keeping the shared usage components

Split for the usage (UsagePage) segment. Most of the folder is the usage page's
own view, but four pieces are reused elsewhere and stay in @/components/UsagePage:
TopKeyView (old-usage), KeyModelUsageView and value_formatters (activity_metrics),
and the shared types (activity_metrics, chartUtils). The other 21 files move into
usage/_components, preserving the folder structure.

The external consumers import only the retained files, so they are untouched. The
moved files' imports of the retained files become @/components/UsagePage paths,
other escaping relative imports are absolutized, and lint suppressions are re-keyed
for moved files only. No behavior change.

* refactor(ui): colocate the mcp-servers view, keeping the shared mcp_tools surface
2026-07-11 18:01:41 -07:00
ryan-crabbe-berri
8c5473f198
feat(ui): adopt openapi-react-query ($api) and convert useCustomers (#32949)
* feat(ui): adopt openapi-react-query and convert useCustomers to $api

Add openapi-react-query and expose $api = createQueryClient(fetchClient)
alongside fetchClient. Rewrite useCustomers as
$api.useQuery("get", "/customer/list", {}, { enabled, select }), which
derives the query key from method + path (dropping the hand-written
createQueryKeys entry and the manual key) and forwards the request signal
for cancellation. The response type still flows from schema.d.ts as
CustomerResponse[]. Tests assert the path, the admin/token enabled gate,
and the empty-body select fallback.

* test(ui): read the last render's options in useCustomers helper

The lastCallOptions helper was named for the last call but read
mock.calls[0]. Harmless while each test renders once, but it would
silently assert against first-render options if a test ever re-renders.
Read the final call instead.
2026-07-11 18:01:33 -07:00
yucheng-berri
f947ef14a2
fix(xecguard): use StandardLoggingGuardrailInformation in logging hook (#32911)
XecGuard's async_logging_hook wrote a bare dict to
standard_logging_object["guardrail_information"] while the typed
contract is Optional[List[StandardLoggingGuardrailInformation]].
Readers that iterated the field walked dict keys, raised on
info.get, or silently dropped the entry from guardrail usage
tracking and spend-log writes

Construct the typed entry and append it to the existing list or
create a new one, matching the shared helper pattern. Record the
configured guardrail name instead of a hardcoded "xecguard" and
pass the GuardrailEventHooks enum for guardrail_mode
2026-07-11 17:50:13 -07:00
Abhimanyu Kapur
81db114c40
Merge pull request #32950 from BerriAI/litellm_auto_router_test_connection
feat(ui): working Test Connection for the complexity auto router
2026-07-11 17:45:46 -07:00
yuneng-jiang
10d5804b3e
Merge pull request #32884 from BerriAI/litellm_internal_staging
Some checks failed
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CodSpeed Benchmarks / benchmarks (push) Has been cancelled
Helm unit test / unit-test (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
GitHub Actions Security Analysis / zizmor (push) Has been cancelled
chore(ci): promote internal staging to main
2026-07-11 17:42:56 -07:00
ryan-crabbe-berri
d37ba79ebd
refactor(ui): convert projects page chart to shadcn/recharts (#32722) 2026-07-11 17:22:42 -07:00
mateo-berri
a6c8fb2d1c docs(github): shape QA runbook examples as node id plus behavior bullets 2026-07-11 17:21:36 -07:00
mateo-berri
1d589832c7 docs(github): scope the QA runbook to tests/e2e edits and add example checklists 2026-07-11 17:18:22 -07:00
mateo-berri
2deeeb73a7 docs(github): add QA runbook section to the PR template 2026-07-11 17:12:38 -07:00
mateo-berri
a76dd9bf8e test(e2e): cover model-aware mid-conversation system handling on Bedrock Invoke /v1/messages 2026-07-11 17:08:16 -07:00
yuneng-jiang
4baeb283f3
chore: update Next.js build artifacts (2026-07-11 23:35 UTC, node v20.20.2) (#32960) 2026-07-11 17:02:04 -07:00
ryan-crabbe-berri
ca877c78c3
refactor(ui): colocate the usage view, keeping the shared usage components (#32952)
Split for the usage (UsagePage) segment. Most of the folder is the usage page's
own view, but four pieces are reused elsewhere and stay in @/components/UsagePage:
TopKeyView (old-usage), KeyModelUsageView and value_formatters (activity_metrics),
and the shared types (activity_metrics, chartUtils). The other 21 files move into
usage/_components, preserving the folder structure.

The external consumers import only the retained files, so they are untouched. The
moved files' imports of the retained files become @/components/UsagePage paths,
other escaping relative imports are absolutized, and lint suppressions are re-keyed
for moved files only. No behavior change.
2026-07-11 16:51:44 -07:00
ryan-crabbe-berri
cb24864a7f
feat(proxy): add expires filter to GET /key/list (#32953)
* feat(proxy): add expires filter to GET /key/list

Add an opt-in expires query param to GET /key/list so callers can fetch
only expired or only active keys without paginating every page and
filtering client-side. 'expired' matches keys whose expires is in the
past (NULL expires excluded); 'active' matches keys that never expire or
expire in the future. Omitting the param preserves existing behavior for
every caller. An unrecognized value returns HTTP 400 rather than silently
returning all keys.

The filter is pushed to the database via the existing Prisma where
builder so callers avoid pulling the full key table into application
memory.

Resolves LIT-3387

* refactor(proxy): declare VALID_EXPIRES_FILTER_VALUES before its first use
2026-07-11 16:48:25 -07:00
yuneng-jiang
03970d7c3a
bump: litellm-proxy-extras 0.4.75 -> 0.4.76 (#32957) 2026-07-11 16:48:08 -07:00
mateo-berri
cc27528d4f test(main): assert the responses bridge forwards static aws keys as well as web identity params 2026-07-11 16:47:00 -07:00
Abhimanyu Kapur
a80f85692b fix(ui): drop max_tokens from the auto-router connection probe
max_tokens=1 makes reasoning models (o1/o3/...) return a 400 "max_tokens
reached" because reasoning tokens count against the cap, so a reachable
reasoning tier showed a false failure in Test Connection. Live-verified: o3
400s with the cap and succeeds without it.

Extract the request shape into a pure buildModelGroupTestRequest and cover it
with a test asserting the chat body carries no max_tokens (or
max_completion_tokens), so this regression is caught in unit tests instead of
only against a live reasoning model.
2026-07-11 16:43:36 -07:00
Mateo Wang
5e1928c753
Merge pull request #32928 from BerriAI/litellm_quota_management_registry
refactor(e2e): bucket rate limits, budgets, and spend tracking under quota_management
2026-07-11 16:40:43 -07:00
mateo-berri
201730efe5 fix(bedrock): allow bedrock-mantle:CreateInference in the web identity session policy 2026-07-11 16:35:13 -07:00
yucheng-berri
e7f41442d0
feat(guardrails): add pre_mcp_call support to Content Filter (#32936)
* feat(guardrails): add pre_mcp_call support to Content Filter

* test(guardrails): cover canonical MCP key gate under pre_mcp_call mode

* fix(guardrails): scan MCP arguments per value and gate mixed-mode scans by call type

* fix(guardrails): cap MCP argument scan depth and register the walker with the recursion detector

* test(guardrails): update LIT-4226 UI settings tests for content filter pre_mcp_call support

* fix(guardrails): use builtin generics in MCP scan annotations to satisfy strict-rule budget

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 16:17:49 -07:00
mateo-berri
36dff0f63a test(e2e): source the ratelimit suite's model from E2E_CHEAP_ANTHROPIC_MODEL 2026-07-11 16:15:16 -07:00
mateo-berri
ca55fc2deb refactor(e2e): model the tpm spend loop's two outcomes as values 2026-07-11 16:15:16 -07:00
mateo-berri
9f55925c31 test(e2e): name the tpm window deadline's latency margin 2026-07-11 16:15:16 -07:00
mateo-berri
6f2435b54a test(e2e): fail the rpm reset test when the limiter resets early 2026-07-11 16:15:16 -07:00
mateo-berri
1ca1a9cd03 test(e2e): assert the tpm block at its exact token crossing instead of a call-count heuristic 2026-07-11 16:15:16 -07:00
mateo-berri
b9aef1b810 test(e2e): cover key rpm/tpm rate limiting, window reset, and pacing headers 2026-07-11 16:15:16 -07:00
mateo-berri
d37daf0b9f refactor(e2e): move budgets and spend_tracking suites under quota_management 2026-07-11 16:14:21 -07:00
mateo-berri
8225ddf2a4 fix(e2e): name the route the spend_calculate registry cell actually exercises 2026-07-11 16:13:48 -07:00
mateo-berri
320a55f01f refactor(e2e): bucket rate limits, budgets, and spend tracking under quota_management 2026-07-11 16:13:48 -07:00
ryan-crabbe-berri
0710cf2990
refactor(ui): convert entity usage and usage page charts to shadcn/recharts (#32729)
* refactor(ui): convert entity usage and usage page charts to shadcn/recharts

Swap the tremor BarChart/DonutChart render sites in EntityUsage,
SpendByProvider, TopKeyView, TopModelView, KeyModelUsageView and
UsagePageView to the shared shadcn/recharts wrappers. Convert the two
sole-chart Daily Spend cards and the KeyModelUsageView card to the
shadcn Card primitives.

Close the donut parity gap with strictly additive optional DonutChart
props: showLabel/label render a center total (tremor showed
valueFormatter(sum) by default) and startAngle/endAngle forward to the
Pie so both provider donuts keep tremor's clockwise-from-12 layout.
Defaults preserve the previous wrapper behavior.

DailyData and two site-local row types move from interface to type
alias so they satisfy the wrappers' Record<string, unknown> constraint;
interfaces lack implicit index signatures.

Tests now assert on real recharts output: bar/sector counts, cyan
fills, axis labels, donut center totals, and the TopKeyView bar-click
drill-down into the key info modal. The dead tremor chart mocks in
UsagePageView.test.tsx are removed and lint metrics/suppressions are
regenerated for the dropped tremor imports.

* fix(ui): compute donut center label only when shown and assert Model Usage renders as a card title
2026-07-11 16:11:05 -07:00
mateo-berri
e0463a38ff fix(completion): forward aws credential kwargs into litellm_params so the responses bridge keeps WIF auth
Chat-completions requests to responses-only Bedrock Mantle models are
bridged to the Responses API, but completion() forwarded only
aws_bedrock_project_id into get_litellm_params, so aws_role_name,
aws_web_identity_token, aws_session_name and the other SigV4 credential
kwargs never reached sign_request and botocore fell back to the default
credential chain ("Bedrock Mantle auth failed: no Bearer token and no
usable AWS credentials"). Forward the whole AWS credential kwarg family,
extracted from the OPTIONAL_KWARGS_KEYS set get_litellm_params already
supports.
2026-07-11 16:10:39 -07:00
Abhimanyu Kapur
ddc13b331a fix(ui): probe auto-router tiers via real proxy routing, not /health/test_connection
Live testing showed the first cut was broken: /health/test_connection merges
{...configParams, ...requestParams}, so passing the public model_group name as
the request model overrode the resolved provider model and every tier failed
with "LLM Provider NOT provided". The frontend only has the public group name,
not the underlying litellm_params, so it cannot build the request that endpoint
needs.

Switch to testing each model group the way production actually routes it: send a
minimal request to /v1/chat/completions (or /v1/embeddings for the embedding
model) by public group name through the shared apiClient. The router resolves
the group, credentials, and provider itself, so a green row means the tier is
genuinely reachable. Verified live: voyage embedding returns 200, a tier with a
bad key returns the real provider auth error.

Also address Greptile feedback: rows now update progressively as each probe
settles instead of all at once, and TIER_ORDER is derived through a
`satisfies Record<keyof ComplexityTiers, null>` guard so adding a tier without
listing it is a compile error.
2026-07-11 16:04:04 -07:00
yuneng-jiang
f2fb6b8e73
refactor(ui): extract a shared CopyButton and fix the sidebar copy confirmation (#32945)
* fix(ui): show sidebar copy confirmation only on a successful write

The sidebar account menu's copy button switched to the checkmark
synchronously, before the clipboard write settled, so it confirmed a
copy that never happened when navigator.clipboard was undefined on
non-secure origins or when writeText rejected. The handler now guards
navigator.clipboard, awaits the write, and flips to the checkmark only
on success

Also updates the header accent emoji in the same menu

* refactor(ui): extract a shared CopyButton for the sidebar account menu

The copy-icon-to-checkmark pattern was hand-rolled in several places,
including the sidebar account menu whose private copy button held the
false-confirmation bug. Extract a single canonical CopyButton into
components/shared, built on the Button primitive with a guarded and
awaited clipboard write so the checkmark appears only on a real
success, and have SidebarAccountMenu consume it

The success and failure-mode coverage now lives in the shared
component's own test; the sidebar test keeps one case asserting the
email row is wired to it
2026-07-11 15:47:03 -07:00
Abhimanyu Kapur
2b2e8cf2bf feat(ui): working Test Connection for the complexity auto router
The consolidated auto-router tab dropped the Test Connection button because
the shared prepareModelAddRequest helper returns an empty array for an auto
router (it has no model_mappings), so the caller crashed destructuring
result[0].litellmParamsObj. That is the crash in #31590 and the open PR
#31794. #31794 only silenced the crash by pointing the test at
auto_router/complexity_router, which is not a provider model, so the
/health/test_connection health check (a real litellm.ahealth_check
completion) would still error.

Bring the button back and make it meaningful: an auto router dispatches to
saved model groups, so Test Connection now probes those directly. It builds
a deduped target list from the configured tiers (tiers sharing a model group
collapse to one probe) plus the embedding model when semantic keyword
matching is on, then runs a live /health/test_connection against each and
shows per-target pass/fail. This never touches prepareModelAddRequest, so the
original destructure crash cannot recur.

Scope is the recommended complexity router only; the to-be-deprecated
semantic router is untouched. No backend changes.

Supersedes #31794. Resolves #31590.
2026-07-11 15:44:50 -07:00
Shivam Rawat
173193d717
Merge pull request #32942 from BerriAI/litellm_dashscope_price_issues
fix(proxy): enforce budget and cost tracking for Dashscope tiered pricing
2026-07-11 15:40:20 -07:00
Abhimanyu Kapur
922e2480e5
Merge pull request #32876 from BerriAI/litellm_opus_4_5_effort_comment
docs(anthropic): clarify the Opus 4.5 branch in adaptive-effort translation
2026-07-11 15:35:25 -07:00
ryan-crabbe-berri
0ebcda3027
refactor(ui): convert user agent and per-user usage charts to shadcn/recharts (#32725)
* refactor(ui): convert user agent and per-user usage charts to shadcn/recharts

Swap the tremor BarChart import for the shared shadcn/recharts wrapper in
user_agent_activity.tsx (DAU/WAU/MAU charts) and per_user_usage.tsx (usage
distribution histogram). All chart props are unchanged; the wrapper exposes
the same tremor prop surface with matching defaults.

Extend user_agent_activity.test.tsx and add per_user_usage.test.tsx with
parity assertions on the real recharts SVG output: bar series per category,
stacked x positions, resolved fill colors, axis bucket labels, legend text,
and value formatter output on axis ticks. Remove the dead ResizeObserver
polyfill in user_agent_activity.test.tsx now that the scoped global mock in
tests/setupTests.ts renders charts, which also lowers the no-explicit-any
metric by one.

* test(ui): harden bar x-position parsing against recharts path format
2026-07-11 15:20:18 -07:00