* fix(proxy): infer team from DB when JWT has no team and user has one team
- When team_id is unset after JWT auth but the user row has exactly one
team, set team_id, team_object, and team_membership from DB.
- Skip when zero or multiple teams (ambiguous).
- Add parametrized unit tests in test_handle_jwt.py.
Made-with: Cursor
* fix(proxy): JWT single-team DB fallback: catch errors, tests match get_team_object
- Wrap get_team_object + get_team_membership in one try/except; log and skip on failure (stale/missing team id no longer fails auth).
- Parametrize tests: HTTP 404/500, membership error; use side_effect not return_value=None for missing team row.
Made-with: Cursor
* refactor(jwt): extract single-team fallback into _resolve_single_team_fallback helper
Made-with: Cursor
* feat(guardrails): add LLM_AS_A_JUDGE to SupportedGuardrailIntegrations
* feat(types): add EvalVerdict, StandardLoggingEvalInformation; wire eval_information into SpendLogsMetadata
* feat(guardrails): add self-contained llm_as_a_judge guardrail hook
* fix(a2a): filter agent-only litellm_params from acompletion kwargs; pass agent_id into body
* feat(ui): add LLMJudgeFields criteria builder component
* feat(ui): wire LLM-as-a-Judge into add guardrail form
* feat(ui): update EvalViewer — title 'LLM Judge Results', weighted score column, summary row
* fix(ui): wire EvalViewer into LogDetailContent to show LLM judge results on logs page
* fix(guardrails-ui): route llm_as_a_judge to criteria builder step; rename to LiteLLM LLM as a Judge; add litellm logo
* fix(guardrail-viewer): stack lifecycle + eval details vertically to avoid badge overflow in narrow drawer
* fix(guardrail-create): surface config validation errors on create instead of silently orphaning guardrail in DB
* fix(guardrail-registry): hardcode llm_as_a_judge in initializer registry so it loads regardless of package install path
* fix(llm-as-a-judge): fix P1 code quality issues - validate weights/on_failure, guard pre_call, handle multimodal, move imports to module level, fix spurious finally logging
* fix(guardrail_endpoints): use correct PK field in rollback delete and log rollback failure
* fix(llm_as_a_judge): support Pydantic object in _get_litellm_param fallback chain
* fix(LLMJudgeFields): replace @tremor/react Button with antd Button
* fix(llm_as_a_judge): remove dead registry dicts, fix KeyError in prompt builder, set correct status on judge failure
* test(llm_as_a_judge): add unit tests for guardrail hook
* fix(llm_as_a_judge): remove @log_guardrail_information decorator to fix duplicate guardrail_information entries
The decorator and the manual finally block both called add_standard_logging_guardrail_information_to_request_data, producing two entries per request. The decorator also misclassified HTTPException(422) blocks as guardrail_failed_to_respond (it checks for 400). The finally block correctly tracks status throughout, so removing the decorator is sufficient.
* fix(test_gcs_pub_sub): ignore metadata.eval_information in comparison
* fix(test_spend_management): ignore metadata.eval_information in payload comparison
* fix(types/guardrails): add input_type and messages to ApplyGuardrailRequest
* fix(guardrail_endpoints): pass input_type and messages through apply_guardrail endpoint
* fix(guardrail_endpoints): auto-detect post_call guardrails and use input_type=response
* fix(a2a_endpoints): merge agent litellm_params guardrails into data before post_call hooks
* fix(llm_as_a_judge): use float sum with tolerance for weight validation
* fix(guardrail_registry): split long import line for black formatting
* fix(llm_as_a_judge): guard guardrail_name Optional for mypy
* fix(llm_as_a_judge): set guardrail_status=guardrail_intervened when score fails, regardless of on_failure mode
* fix(a2a_endpoints): use try/finally so deferred spend log fires even when guardrail blocks with 422
* fix(litellm_logging): declare _defer_async_logging and _enqueue_deferred_logging on Logging class for mypy
* fix(logging_worker): restore queue.join() in flush() to wait for in-flight callbacks
User-configured pass-through endpoints with ``auth: false`` are
explicitly unauthenticated — the builder short-circuits at
check_api_key_for_custom_headers_or_pass_through_endpoints and returns
a fresh empty UserAPIKeyAuth() without an api_key, user_id, or role.
Pre-refactor, that empty token never reached common_checks. After the
centralization, it does — and common_checks rejects it as admin-only,
breaking every Langfuse / custom unauthenticated pass-through.
This is the same regression class as the public-routes one: a
builder fast-path whose return value cannot survive common_checks.
Honor the same contract here — when the matched endpoint config has
auth != True, skip the centralized gate. auth=True endpoints still
run the full gate (covered by a companion test).
No security regression: ``auth: false`` is the operator's explicit
opt-out from LiteLLM auth on this path. The original commit closed
seven authenticated bypasses; this exemption applies only to a path
the operator has already declared unauthenticated.
The passthrough helper copied the upstream provider's Server: header
(e.g. "cloudflare" from Anthropic) onto the FastAPI response. uvicorn
then added its own Server: header on top, producing two Server: lines
in the wire response. Strict HTTP parsers (aiohttp's, used in CI's
passthrough tests) reject this with "Duplicate 'Server' header found"
and the request fails with a 400.
Same risk for Date, Content-Length, Connection, Keep-Alive: the ASGI
server writes its own copy at serialization time. Forwarding the
upstream's value either duplicates the header or lies about the
re-serialized body length.
Drop these from the forwarded set. Application/business headers
(content-type, x-request-id, anthropic-ratelimit-*, etc.) still pass
through unchanged.
Two regressions introduced by 3737d6a1f3 (centralized common_checks):
1. Public routes (e.g. /health/readiness, /metrics) are exempted by the
builder fast-path but the wrapper then ran common_checks on the
synthetic INTERNAL_USER_VIEW_ONLY token, which has no user_id, no
team, no scopes — so common_checks rejected the request as admin-
only. This broke every k8s readiness probe when master_key is set
(helm chart job confirmed: pod never goes Ready, service has no
endpoints).
2. The admin user_object synthesis only triggered when
user_object is None. After any team-creation flow runs, the row
for litellm_proxy_admin_name (default "default_user_id") exists
in litellm_usertable with the default user_role=internal_user.
get_user_object then returned that row, the synthesis was skipped,
and master_key requests were demoted to internal_user — failing
/team/update, /team/block, etc. The token's user_role is the
source of truth for these paths (set inside the authenticated
master_key / JWT-admin builders); a stale DB row must not override
it.
Fix:
- Short-circuit _run_centralized_common_checks for routes already in
LiteLLMRoutes.public_routes (or general_settings.public_routes).
Same exemption surface the builder already trusts.
- When the token's user_role is PROXY_ADMIN, force the synthesized
admin user_object regardless of what get_user_object returned.
Preserves the spend value from the DB row.
Neither change reopens any of the seven bypasses the original commit
closed: OAuth2, JWT non-admin, DB-fallback, /user/auth, pass-through
headers, etc., still go through the gate. Only paths that were
already admin or already public skip it.
Adds two regression tests:
- test_centralized_common_checks_skips_public_routes
- test_centralized_common_checks_master_key_admin_overrides_db_user_role
* fix(vertex passthrough): log :embedContent and :batchEmbedContents responses
* test(vertex passthrough): add unit tests for :embedContent and :batchEmbedContents logging
* fix(vertex passthrough): extract input text from request body for embedContent token counting
* fix(vertex passthrough): add embedContent and batchEmbedContents to TRACKED_VERTEX_ROUTES
* fix(vertex passthrough): detect Google AI Studio URLs in embedContent handler
* test(vertex passthrough): add unit test for Google AI Studio URL embedContent provider detection
* style: black format vertex_passthrough_logging_handler
The enterprise package ships under the BerriAI Enterprise License defined
in enterprise/LICENSE.md, which is not an SPDX-listed license. Declare it
via PEP 639's LicenseRef-Proprietary expression so metadata-reading tools
(PyPI classifiers, Nexus IQ, pip-licenses) resolve it instead of reporting
License-None. The existing license-files entry already ships the full terms.
Previously, an admin JWT sending a stale/typo'd/missing x-litellm-team-id
on an LLM API route received a hard 404 from get_team_object, blocking the
request. Restore pre-PR admin behavior: if the header can't be resolved,
skip team attribution and proceed with admin access, logging a warning
with the header value and route so the misconfigured caller is diagnosable.
Replaces the rm-and-symlink hack with a plain actions/checkout
using path: docs/my-website. The previous approach failed on this
branch because docs/my-website no longer exists in the repo (its
parent docs/ directory was also removed), so ln -s had nowhere
to create the symlink.
Also adds the same checkout step to test-unit-documentation.yml,
which was silently relying on docs/my-website existing in-tree
for test_env_keys.py and test_router_settings.py.
The documentation source has moved to a separate repository,
BerriAI/litellm-docs, served at docs.litellm.ai. This PR removes
docs/my-website/ from this repo and updates README.md, AGENTS.md,
and CLAUDE.md to direct doc contributions to the new repo.
Also fixes a broken relative link in
litellm/integrations/levo/README.md.
The existing CI symlink in .github/workflows/test-code-quality.yml
(which clones litellm-docs and symlinks docs/my-website to it for
tests/documentation_tests/*) continues to work without change.
create_model does not inherit the base class docstring, so once an
extension registered a field the effective class had no description.
The UI renders schema.description as a header paragraph — losing it
broke the 'Configuration for UI-specific flags' text. Pass __doc__
through explicitly and add a regression test.
Extract the admin team-header attachment into a helper so
auth_builder stays under the 50-statement lint threshold; apply
black formatting to the two files flagged on the prior commit.
No behavior change.
GET /get/ui_settings returns a schema built from the effective UISettings
class (base + enterprise-registered fields), but PATCH /update/ui_settings
declared its body as the base UISettings. Enterprise fields still worked
via extra="allow", but the OpenAPI schema was asymmetric between GET and
PATCH.
Accept the body as a dict and validate with the effective class so both
sides are in sync and enterprise-registered fields are type-checked.
Add _depth/_max_depth guards (default 20) so the nested dict masking
cannot run away, and allowlist the function in the recursive_detector
CI check alongside the other bounded recursive helpers.
The file was moved to tests/enterprise/litellm_enterprise/proxy/management_endpoints/
and is covered by the CircleCI litellm_mapped_enterprise_tests job. The stale path
was causing pytest to error with 'file or directory not found'.
`_check_byok_credential` previously returned silently when `prisma_client`
was None, bypassing BYOK ownership validation during database-outage
windows. Any proxy-authenticated user could invoke BYOK-protected MCP
tools without a stored credential during the outage window.
Now raises HTTP 503 with a structured error so the flow fails closed.
Regression test asserts 503 is raised when `prisma_client` is None.
Reported by @brodmart in GHSA-6762-2m23-5mxp.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds three new UISettings flags gated by check_org_admin_feature_access:
- disable_key_generate_for_org_admin
- disable_team_create_for_org_admin
- disable_model_add_for_org_admin
When enabled by a proxy admin, users with the ORG_ADMIN role receive a 403
on /key/generate, /team/new, and /model/new respectively. All other roles
(proxy admin, internal user, team) are unaffected and continue through
their existing auth checks. Flags are persisted to litellm_uisettings and
synced into general_settings via _RUNTIME_GENERAL_SETTINGS_FLAGS so the
enforcement helper can read them at request time.
Scope the header-driven team fetch to LLM API routes so admin
management routes keep the pre-existing bypass behavior (no
phantom teams, no 404s on mgmt calls). Team context is threaded
onto UserAPIKeyAuth so spend logs, rate limits, and team_models
attribution are correctly applied when admins act on behalf of
a team via x-litellm-team-id.