Commit graph

42134 commits

Author SHA1 Message Date
mubashir1osmani
2927e88e79 fix(e2e/mcp): avoid shipping shared logs and in-repo browser sessions
Bridge prompts search a unique no-hit marker so real Datadog service logs
are not folded into an external model turn. Session capture defaults to
temp and refuses paths under the git tree; gitignore covers leftover names
2026-08-05 16:15:24 -07:00
mubashir1osmani
3e8774a3fc chore: drop local litellm-config.yml and mgmt registry expansion
Keep this PR MCP-focused; leave litellm-config.yml untracked and restore
mgmt.yaml to staging so management registry rows live elsewhere
2026-08-05 15:52:00 -07:00
mubashir1osmani
e83c09896f chore: add redis, datadog callback, and anthropic model to e2e config 2026-08-05 15:48:27 -07:00
mubashir1osmani
d016edeea3 test(e2e): add MCP bridge, filtering, auth, and OAuth coverage tests
Add e2e tests for the three LLM bridges (chat_completions, responses,
messages) that expand litellm_proxy MCP tool references and auto-execute
tools against the real Datadog MCP server. Add allowed_tools scoping,
namespaced multi-server, upstream_static_auth + transport_http, and
401-not-500 auth regression tests. Unskip the datadog round-trip and
key-access call_tool tests by dropping the rejected telemetry argument.

Add a Datadog OAuth2 PKCE path (dd_oauth.py) that registers the server
with auth_type=oauth2, drives the real authorize dance via DCR + browser
consent + token exchange, stores the per-user token in the gateway vault,
and tests list_tools + call_tool + chat_completion through the stored
token. Requires E2E_DD_STORAGE_STATE (captured via dd_session_capture.py).

Delete the stale guardrail test and the old oauth_chat_client helper.
Extend datadog_mcp.py to return DatadogMcpServer(server_id, alias) and
accept allowed_tools/toolsets kwargs. Add ResponsesMcp models and
bridge methods to mcp_client.py. Add AnthropicMcpTool to models.py.
2026-08-05 15:34:39 -07:00
mubashir1osmani
6d0a71cdb7 test(e2e): add 94 management endpoint entries to coverage registry
Add coverage registry entries for previously unmapped management endpoints:
- Access group management (create, list, update, delete)
- Cache operations (ping, delete, redis info, flushall, settings)
- Callback management (list, configs)
- Compliance (EU AI Act, GDPR)
- Config overrides (Vault integration)
- Coordination Redis settings
- Cost tracking (discount, margin configs)
- Credentials/encryption migration
- Customer block/unblock
- JWT key mapping (CRUD)
- Key operations (service account, aliases, reset spend)
- Model operations (patch update, v2 info, groups, hub)
- Organization management (info, list, activity, members)
- Plugin management (list, auth token)
- Router settings (config, fields)
- Routes discovery
- Adaptive router state
- Tag management (update, info, daily activity, analytics)
- Team operations (merge patch, metadata, permissions, callbacks, models)
- Tool management (policy, spend, details, logs, overrides)
- User management (bulk update, v2 info, activity metrics)
- Workflow management (runs, events, messages CRUD)
- Utility endpoints (token counter, params, transform)

This expands coverage from 87 to 181 entries across 38 endpoint families.

Tier distribution: 24 P0, 57 P1, 100 P2
All entries include: source file, line number, and rationale for testing
2026-08-05 14:42:47 -07:00
mubashir1osmani
a5b7f0aca1 test(e2e): drop Linear MCP OAuth path for bridge coverage
Remove the Linear session capture and chat-completion OAuth e2e so
chat/responses/messages bridge coverage can target a shared non-Linear
real OAuth MCP later. Keep the generic oauth_chat_client helper
2026-08-05 11:57:40 -07:00
mubashir1osmani
8b888a0666 test(e2e): expand MCP coverage registry from product feature map
Document MCP gateway surfaces and customer-reported gaps, grow mcp.yaml
and mgmt MCP cells so skipped coverage is honest, and mark the OAuth
chat path as covering the chat_completion auto-exec cell
2026-08-04 21:28:42 -07:00
tin-berri
bbc6e3feea
Update autorouter_presets.json (#35896) 2026-08-04 21:16:52 -07:00
Abhimanyu Kapur
31a86daa85
feat(auto-router): make reminder marker pair configurable (#35874)
* feat(auto-router): make reminder marker pair configurable

Some harnesses inject internal context using their own marker pair
instead of Claude Code's <system-reminder>/</system-reminder>
convention, and some send it as a separate follow-up user message
rather than inline with the ask. Both cases fall out of the same root
cause: the router's marker-matching is hardcoded, so foreign markers
never strip to empty and the reminder-only turn wins "newest human
ask" selection instead of being skipped.

Add an optional reminder_markers field to ComplexityRouterConfig so
operators can override the (open, close) pair via proxy config, with
the existing skip-when-empty selection logic handling both cases once
the markers match.

* test(auto-router): drop unsolicited comments from the reminder-markers regression test

Per Greptile review on #35874: no comments unless explicitly requested.
2026-08-05 03:08:49 +00:00
Yassin Kortam
1e265dc86c
fix(auth): name enable_jwt_auth when a JWT-shaped key is rejected (#35831)
A three-segment token presented while `general_settings.enable_jwt_auth` is
unset is never treated as JWT-shaped, so it falls through to the virtual-key
path and is rejected for not starting with 'sk-'. That reads as a missing
key in the verification table and sends the operator off to inspect virtual
keys, when the real cause is one missing config line. The rejection now
names `enable_jwt_auth`, appended to the existing text so the Prometheus
invalid-key filter and the admin UI keep matching what they match today.

The hint claims only that the key is JWT-shaped. Segment count cannot tell a
JWT from any other dotted credential, so asserting the key IS a JWT would
swap one confident misdiagnosis for a narrower one.

The enterprise gate on that same path raised a bare `ValueError`, which the
terminal handler turns into a 401. Every sibling enterprise gate answers
403, and a 401 tells the client to retry with a better credential, which no
credential can satisfy while the install is unlicensed. It now raises a 403
`ProxyException` like the SSO gate does.
2026-08-04 20:05:03 -07:00
Mateo Wang
5aeb34b58c
Merge pull request #35884 from BerriAI/litellm_precommit_stale_types_no_rerun
chore: stop advising pre-commit and bootstrap
2026-08-04 20:00:17 -07:00
Mateo Wang
548c67c120
Merge pull request #35888 from BerriAI/litellm_remove_flake8
chore: remove unused .flake8 config and flake8 dev dependency
2026-08-04 19:43:39 -07:00
Mateo Wang
a0d08b8143 chore: remove pre-commit and bootstrap advisories
They were taking too long
2026-08-04 19:38:34 -07:00
mateo-berri
38cd75342d Note the one case where staging schema.d.ts changes what runs
For a backend-only commit, staging the regenerated schema.d.ts newly
satisfies the ui file triggers, so the folder-wide dashboard lint
budgets run locally for the first time and CI's frontend-lint job
(budgets plus knip) activates on the PR. Those can only fail from
pre-existing dashboard-tree state, never from the regenerated file,
but the guidance should say so instead of implying a re-run is
always redundant.
2026-08-04 19:38:34 -07:00
mateo-berri
4a2ceed595 Stop advising a pre-commit re-run for stale dashboard API types
The stale-types failure already writes the regenerated schema.d.ts to the
working tree, and staging it cannot introduce a new failure: the file is
listed in .prettierignore and the eslint config ignores, so no lint pass
sees it, and gen:api derives it purely from the Python proxy code, so a
second regeneration is a no-op. The only reason left to re-run is when
other checks also failed, so say exactly that in the script message and
CLAUDE.md instead of prescribing an unconditional re-run.
2026-08-04 19:38:34 -07:00
yuneng-jiang
03de6280b6
Merge pull request #35802 from BerriAI/litellm_/modest-pascal-71b7b2
refactor(ui): inject the fetch client's base url instead of reading it at import
2026-08-04 19:34:39 -07:00
yuneng-jiang
96c66ce4b7
Merge pull request #35812 from BerriAI/litellm_/internal-user-endpoint-audit-36c1c5
feat(ui): add role capability gating, migrate Tool Policies route
2026-08-04 19:34:26 -07:00
Mateo Wang
faf3c51469
Merge pull request #35869 from BerriAI/litellm_gate_owns_basedpyright_heap
fix(lint): move the basedpyright heap flag into the type check gate
2026-08-04 19:26:24 -07:00
mateo-berri
27caf28924 chore: remove unused .flake8 config and flake8 dev dependency 2026-08-04 19:15:07 -07:00
yuneng-jiang
abcffa1e23
Merge pull request #35875 from BerriAI/litellm_/inspiring-franklin-058a96
test(e2e): skip view-backed global spend probes pending LIT-5211
2026-08-04 19:14:48 -07:00
yuneng-jiang
2e255191ab
Merge pull request #35844 from BerriAI/litellm_/terraform-provider-dep-bump-5feb4a
chore(deps): bump grpc and golang.org/x modules in the terraform provider
2026-08-04 19:14:38 -07:00
yuneng-jiang
1d39c5fa7d
Merge pull request #35881 from BerriAI/litellm_/revert-pr-34649-9f1755
revert: "test(e2e): vendor API strategy coverage across endpoints" (#34649)
2026-08-04 19:14:24 -07:00
yuneng-jiang
fa2d97f7bc
Merge pull request #35882 from BerriAI/litellm_fix_autorouter_preset_test_fixtures
fix(ui): derive auto-router preset tests from the bundled preset JSON
2026-08-04 19:13:36 -07:00
mateo-berri
5bf9246667 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_gate_owns_basedpyright_heap 2026-08-04 19:04:52 -07:00
Tin Chi Lo
337d5a10dd fix(ui): derive auto-router preset tests from the bundled preset JSON
The template tests hardcoded the model names the presets happened to ship
with, so editing autorouter_presets.json to name newer models turned every
preset red in the fixtures and hung six waitFor calls
2026-08-04 19:01:55 -07:00
Yuneng Jiang
a9208707da
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/revert-pr-34649-9f1755 2026-08-04 19:00:57 -07:00
devin-ai-integration[bot]
4781b53e72
feat(ui): add Test Routing to the auto router create form (#35859)
* feat(ui): add Test Routing to the auto router create form

Route a test prompt through the complexity-router config on screen before the router
is saved, showing the model it lands on and the same decision trace the Logs page renders.
Adds POST /auto_router/test_routing, which classifies with the live pre-routing hook and
sends nothing to the routed model.

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

* fix(ui): reset the routing test modal on reopen and expose /auto_router on the UI backend

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

* fix(proxy): enforce caller model access and key budget on the routing test's classifier call

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

---------

Co-authored-by: tin <tin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-04 19:00:54 -07:00
Yuneng Jiang
472dd2716f
revert: "test(e2e): vendor API strategy coverage across endpoints (#34649)"
This reverts commit dcb4e5033c.

The suites landed without the proof-of-fix and QA runbook the PR body
itself flagged as outstanding, so the coverage they claim is unverified
against a live proxy
2026-08-04 19:00:34 -07:00
yuneng-jiang
420d9a7e9f
Merge pull request #35877 from BerriAI/litellm_bump_proxy_extras_0_4_83
chore: bump litellm-proxy-extras 0.4.82 -> 0.4.83
2026-08-04 18:58:13 -07:00
Mateo Wang
727812beef
Merge pull request #35868 from BerriAI/litellm_gate_merge_head_base
fix(lint): pick the merge-aware base so in-progress merges are not blamed for base drift
2026-08-04 18:56:04 -07:00
jwang-gif
bb58f019a0
fix(proxy): fix zguard httpcode when block input (#31948)
* fix(zscaler_ai_guard): return 400 on guardrail block

* fix(zscaler_ai_guard): don't log error on intentional BLOCK

A BLOCK is expected guardrail behavior, not a failure. Before this
fix, raising HTTPException inside the try block caused the generic
except to log it as "Failed to apply guardrail", producing spurious
error-level noise for every normal block event.

Added except HTTPException: raise before the generic handler (matching
the existing pattern in make_zscaler_ai_guard_api_call), and a
regression test that asserts logger.error is not called on a BLOCK.

---------

Co-authored-by: yucheng-berri <yucheng@berri.ai>
2026-08-05 01:46:05 +00:00
ryan-crabbe-berri
0a42114847
fix(claude-code): create-only skill registration with a PUT update route (LIT-4110) (#31752)
* fix(claude-code): make skill registration create-only with a PUT update route

POST /claude-code/plugins upserted by name, so re-registering an existing
name silently overwrote the stored skill's source and metadata. The "Add
New Skill" UI button posts here, so a name collision clobbered a different
skill with no signal to the user.

Make POST create-only: it returns 409 if the name already exists, with a
unique-violation guard mapping the find-then-create race to the same 409.
Add an explicit PUT /claude-code/plugins/{plugin_name} for updates (404 if
the name is missing). PUT is a full replace and documents that omitted
fields reset to their defaults, so UpdatePluginRequest defaults version to
None instead of fabricating the create-time 1.0.0.

The shared mutable fields move to a PluginSpec base; RegisterPluginRequest
keeps its name and its generated schema unchanged, UpdatePluginRequest
carries no name. Regenerated the dashboard types and the lazy openapi
snapshot for the new route.

Resolves LIT-4110

* fix(ui): surface the proxy error detail so the skill 409 conflict is legible

The add-skill form rendered the raw HTTPException envelope on failure
because deriveErrorMessage did not unwrap an object-shaped detail
({"detail": {"error": ...}}), so the new create-only 409 reached the user
as a JSON blob. Unwrap object-shaped detail at the client layer, which
covers every handler that returns detail={"error": ...}, and surface the
resulting message verbatim on the form instead of burying it under a
generic prefix.

* refactor(claude-code): replace blind excepts in plugin mutations with typed handling

Narrow register_plugin's create-conflict guard from a broad 'except Exception'
+ isinstance dance to a direct 'except UniqueViolationError', using an Exception
subclass sentinel (not None) as the prisma-absent fallback so the sentinel can be
caught directly. Drop update_plugin's outer 'except Exception -> 500' wrapper so
HTTPExceptions propagate on their own and unexpected DB errors surface as FastAPI's
default 500 rather than echoing str(e). Keeps the BLE001 strict-rule budget green.

* fix(claude-code): restore structured 500 handling on update_plugin via typed PrismaError catch

Flattening update_plugin to satisfy the no-blind-except rule dropped its error
wrapper entirely, so a data-layer failure (e.g. a dropped DB connection) would
skip the intentional verbose_proxy_logger.exception call and degrade the response
from the endpoint's structured {"error": ...} body to FastAPI's default
{"detail": "Internal Server Error"}, inconsistent with every sibling route.

Wrap update_plugin in 'except PrismaError' instead of the blind 'except Exception'
the other routes use: it logs and returns the structured 500 for real DB failures
while letting genuine code bugs surface rather than masking them as 'Update failed',
and stays off the BLE001 budget. Add a regression test that a PrismaError during
the update maps to a structured 500.

* fix(claude-code): import prisma error types at function level to satisfy LIT009

* refactor(claude-code): typed plugin mutation responses and lint gate fixes

Return RegisterPluginResponse models from POST and PUT instead of ad-hoc
dicts, declare them as response_model so the OpenAPI schema and dashboard
types carry the real response shape, build the stored manifest via
model_dump, and drop update_plugin's unused auth parameter (the route
dependency already enforces auth). Keeps the LIT002/B008/UP045 budgets at
their ratcheted ceilings after merging litellm_internal_staging
2026-08-05 01:44:56 +00:00
Yuneng Jiang
6a0dcf1268
bump: litellm-proxy-extras 0.4.82 -> 0.4.83 2026-08-04 18:44:30 -07:00
Yuneng Jiang
e56a6cadc6
test(e2e): skip view-backed global spend probes pending LIT-5211 2026-08-04 18:40:13 -07:00
yuneng-jiang
24dbd2b2db
Merge pull request #35863 from BerriAI/litellm_/vigilant-bell-8e5b37
Some checks are pending
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Proxy Legacy Tests / proxy-server-extras (push) Waiting to run
Unit Tests: Responses, Caching & Types / responses-caching-types (push) Waiting to run
Unit Tests: LLM Provider Transformations / All Other Providers (push) Waiting to run
Unit Tests: MCP, Secrets, Containers & Misc / misc (push) Waiting to run
Unit Tests: Proxy Auth & Key Management / proxy-auth (push) Waiting to run
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy API Endpoints / proxy-endpoints (push) Waiting to run
Unit Tests: Proxy API Endpoints / proxy-server (push) Waiting to run
Unit Tests: Proxy Infrastructure / proxy-infra (push) Waiting to run
Unit Tests: Proxy Legacy Tests / auth-and-jwt (push) Waiting to run
Unit Tests: Proxy Legacy Tests / key-generation (push) Waiting to run
Unit Tests: Proxy Legacy Tests / proxy-config (push) Waiting to run
Unit Tests: Proxy Legacy Tests / proxy-response-and-misc (push) Waiting to run
Unit Tests: Proxy Legacy Tests / proxy-server (push) Waiting to run
Unit Tests: Proxy Legacy Tests / proxy-token-counter (push) Waiting to run
Unit Tests: Proxy Legacy Tests / proxy-user-auth-and-spend (push) Waiting to run
Unit Tests: Proxy Legacy Tests / proxy-utils (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
test(e2e): self-seed the ui suite's password-login users in global setup
2026-08-04 18:17:10 -07:00
devin-ai-integration[bot]
a01cac2132
fix(s3_v2): sign S3 object URLs with S3SigV4Auth so encoded paths verify (#35726)
Generic SigV4 double-encodes the canonical URI while S3 canonicalizes the wire path with single encoding, so any object key containing a character that percent-encodes (a team alias, key alias or s3_path with a space) was signed over %2520 while the request carried %20; S3 recomputed a different signature and answered 403.

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yucheng <yucheng@berri.ai>
2026-08-05 01:14:48 +00:00
Mateo Wang
cb27d998f6
Merge pull request #35826 from BerriAI/litellm_fix_toolcall_stream_linear_assembly
perf(streaming): assemble streamed tool-call arguments in linear time
2026-08-04 18:06:23 -07:00
Mateo Wang
4e5cd0b9f5
Merge pull request #35748 from BerriAI/litellm_budget_reset_uow
refactor(repositories): add prisma protocol seams and a spend-reset unit of work
2026-08-04 18:06:06 -07:00
mateo-berri
22a1c30603 fix(lint): move the basedpyright heap flag into the type check gate
The 12 GB NODE_OPTIONS setting lived only in the Makefile export and the
CI env line, so any hand-run gate pipeline forgot it and node OOMed at
the ~4 GB default after 80 seconds, with || true feeding the gate empty
output. The gate now spawns basedpyright itself for both the head and
base passes, appends the heap flag last so it wins node's last-flag-wins
resolution while preserving other caller flags, and fails loudly on
crash exit codes instead of reading them as zero errors.
2026-08-04 17:59:56 -07:00
mateo-berri
96c8c9cee1 fix(lint): pick the merge-aware base so in-progress merges are not blamed for base drift 2026-08-04 17:58:41 -07:00
Yuneng Jiang
fb353423d8
test(e2e): self-seed the ui suite's password-login users in global setup 2026-08-04 17:38:38 -07:00
Mateo Wang
d64e79bf05
Merge pull request #35856 from BerriAI/litellm_revert_evicted_client_closer
revert: "fix(caching): close evicted LLM clients so their connections are reclaimed (#35492)"
2026-08-04 17:35:19 -07:00
mateo-berri
8c7792a5cc Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_toolcall_stream_linear_assembly 2026-08-04 17:16:24 -07:00
mateo-berri
adb9a53ba1 revert: "fix(caching): close evicted LLM clients so their connections are reclaimed (#35492)"
This reverts commit 66bc70365f and the
follow-up 2-line type fix a6d4654261 (#35706), which only retyped a
signature #35492 introduced.

Closing evicted litellm-owned clients breaks every object that fetches
get_async_httpx_client once in __init__ and holds the handler for the
life of the process: 40 guardrail classes plus the pagerduty and email
callbacks. Once the cache entry is evicted (TTL 3600s or the 200-entry
size cap) and the 900s grace passes, the held client is closed and every
subsequent request through it fails with RuntimeError: Cannot send a
request, as the client has been closed. On a production deployment with
a default-on guardrail this surfaced as every request 500ing roughly 75
minutes after boot.

The connection-reclaim goal of #35492 can re-land once handlers survive
their inner client being closed.
2026-08-04 17:05:06 -07:00
yuneng-jiang
794338af67
Merge pull request #35725 from BerriAI/litellm_/spend-reports-implementation-25a080
feat(spend): add caller-scoped key/user/team/organization spend report endpoints
2026-08-04 17:03:31 -07:00
Mateo Wang
e926097fec
Merge pull request #35481 from mubashir1osmani/litellm_azure_gpt56_price_cut
fix(pricing): apply OpenAI's gpt-5.6 terra/luna cut to Azure cost map
2026-08-04 17:02:12 -07:00
yuneng-jiang
38a30a580e
Merge pull request #35848 from BerriAI/litellm_/node-24-npm-engine-0c64f0
ci: pin Node on the Playwright UI lanes so npm ci meets the engines floor
2026-08-04 16:59:06 -07:00
Yuneng Jiang
6e0627ed04
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/spend-reports-implementation-25a080
# Conflicts:
#	litellm/proxy/spend_tracking/spend_management_endpoints.py
2026-08-04 16:50:34 -07:00
yucheng-berri
bcce83a17e
fix(guardrails): scan model output on the /openai/v1/responses alias (#35818)
The proxy serves POST /openai/v1/responses alongside /responses and
/v1/responses, but only the latter two were in API_ROUTE_TO_CALL_TYPES.
UnifiedLLMGuardrails.async_post_call_success_hook resolves the call type
from request_route, so on the alias it resolved to None and returned the
response unscanned; model output reached the client with post-call
guardrails never running. The key and team tool allowlist was unenforced
on the same alias for the same reason.

Register the alias family in API_ROUTE_TO_CALL_TYPES and in
LiteLLMRoutes.openai_routes, mirroring how the /openai/v1/realtime
aliases are registered, and log a warning at the two points where the
unified guardrail skips post-call scanning so a future unmapped route is
visible instead of silent.

The Responses block of API_ROUTE_TO_CALL_TYPES moves from list to tuple
literals because the LIT002 budget rejects net-new mutable-collection
construction; the map is read-only, so it is now typed as a Mapping of
Sequence and the budgets ratchet down accordingly.
2026-08-04 16:46:45 -07:00
yuneng-jiang
0e593645d1
Merge pull request #35845 from BerriAI/litellm_/cci-failing-tests-f73740
test: repair three failing suites on litellm_internal_staging
2026-08-04 16:42:58 -07:00