Commit graph

42355 commits

Author SHA1 Message Date
yuneng-jiang
cac57a82b2
Merge pull request #26467 from BerriAI/litellm_passthrough_path_join
[Fix] Harden pass-through target URL construction
2026-04-24 21:21:09 -07:00
Ryan Crabbe
1130107a3a
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix-ui-unconditional-cost-write 2026-04-24 20:53:56 -07:00
user
796844ee0a
test(mcp): explicit registry mock in TestMCPPublicRouteGuard
Greptile review feedback (P2): the two negative `.well-known`-substring
tests fell through to `_target_servers_use_oauth2`, which queries
`global_mcp_server_manager.get_mcp_server_by_name`. Without an explicit
mock the tests passed only because the real registry happens to be empty
in the test process. Mock the manager to return None so the assertion
exercises the fail-closed path explicitly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 03:46:45 +00:00
Ryan Crabbe
28f43a7a37
fix(rbac): also gate /key/service-account/generate for org admins
/key/service-account/generate is in org_admin_allowed_routes (via
key_management_routes), so without this check an org admin could bypass
disable_key_generate_for_org_admin by calling the service-account
endpoint instead.
2026-04-24 19:47:58 -07:00
Ryan Crabbe
57c6a1b38b
refactor(rbac): narrow org-admin restriction to key generation only
Drops the team_create and model_add disable flags that were added in the
prior feat commit. Only disable_key_generate_for_org_admin remains, with
the helper renamed check_org_admin_can_generate_keys for clarity. Team
and model creation can be added back the same way later if needed.
2026-04-24 19:36:31 -07:00
Krrish Dholakia
70492cee42
feat(proxy): add /v1/memory CRUD endpoints (#26218)
Some checks are pending
Unit Tests: Caching (Redis) / caching-redis (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
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 DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / schema-migration (push) Blocked by required conditions
Unit Tests: Security / security (push) Waiting to run
* feat(proxy): add /v1/memory CRUD endpoints with user/team scoping

New LiteLLM_MemoryTable stores user/team-scoped key/value entries with
optional JSON metadata. Value is a String (LLM-readable text) and metadata
is an optional Json? envelope, matching the Letta + mem0 hybrid model so
future structured fields can be added without a schema migration.

Endpoints:
  POST   /v1/memory         - create
  GET    /v1/memory         - list (caller-scoped; admins see all)
  GET    /v1/memory/{key}   - fetch one
  PUT    /v1/memory/{key}   - upsert
  DELETE /v1/memory/{key}   - delete

Non-admin callers cannot set a user_id/team_id other than their own.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(proxy/memory): omit metadata field when None on create

Prisma's Python client rejects `metadata=None` on a `Json?` field with
"A value is required but not set" — the field must be omitted from the
`data` dict entirely to store SQL NULL. Build the create payload
conditionally in both `create_memory` and the PUT-create branch of
`upsert_memory`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(ui): add Memory page to view/manage /v1/memory entries

Adds a new "Memory" sidebar item under Tools so users can see what their
agents have stored. Lists all memories visible to the caller (scoped by
the backend), with a key-search filter, preview column, scope tags, and
view/edit/delete actions. Create modal accepts optional JSON metadata.

- networking.tsx: fetchMemoryList / createMemory / updateMemory / deleteMemory
  wired to the /v1/memory CRUD endpoints.
- MemoryView + MemoryEditModal: new antd-based components (per CLAUDE.md:
  use antd for new UI, not tremor).
- page.tsx + leftnav.tsx: wire the "memory" route + sidebar entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(memory): add key_prefix filter + promote Memory to AI GATEWAY nav

Backend:
- GET /v1/memory now accepts `key_prefix` for Redis-style namespace
  scans (e.g. `?key_prefix=user:`). When both `key` and `key_prefix`
  are passed, `key_prefix` wins.
- Prefix filter sits under the visibility filter in the Prisma where
  clause, so it can never leak rows across user/team scopes.
- New tests: prefix match, and cross-scope isolation (another user's
  `user:*` rows must not appear in the caller's results).

UI:
- Memory moved from a Tools submenu to a top-level AI GATEWAY item
  (alongside Agents, MCP Servers, Skills) — it's an API primitive,
  not a tool-management surface.
- Search box now drives prefix search, matching the Redis mental
  model ("type the namespace, see everything under it").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(memory): enforce unique key per scope by using NULLS NOT DISTINCT

The unique constraint `(key, user_id, team_id)` on LiteLLM_MemoryTable
silently allowed duplicates when user_id or team_id was NULL, because
Postgres treats every NULL as distinct by default (ANSI semantics). A
caller with no team_id could POST the same key three times and get
three rows.

Migration:
1. Dedupe existing rows, keeping the most recent per (key, user_id,
   team_id), using `IS NOT DISTINCT FROM` so NULL == NULL.
2. Drop the old unique index.
3. Recreate it with `NULLS NOT DISTINCT` (Postgres 15+).

No code change: POST already returns 409 on unique-violation error
messages — it just wasn't firing before because the constraint didn't
catch the NULL-team case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(memory): make key globally unique, 409 on any duplicate

Switches from the compound unique `(key, user_id, team_id)` to a simple
`key @unique`. The compound form silently allowed duplicates when
user_id or team_id was NULL (Postgres treats each NULL as distinct), so
callers could POST the same key repeatedly. Globally-unique key means
one row per key, period — any duplicate create → 409.

- schema.prisma (×3): `key String @unique`, drop `@@unique(...)`.
- initial add_memory_table migration: unique index on (key) only.
- Remove the now-unused follow-up NULLS NOT DISTINCT migration.
- Endpoint error message simplified ("already exists" — no "for this scope").
- Test fake's create() now enforces global key uniqueness.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ui/memory): full-width layout + user/teams-style columns

- Add `w-full` to the MemoryView outer div so the page fills the
  flex-flex-1 container (was collapsing to intrinsic width).
- Replace the combined "Scope" column with separate User ID / Team ID
  columns, matching the layout of the Users / Teams pages: ID, Name,
  Preview, User ID, Team ID, Updated, Actions.
- IDs render with a truncated mono label + copy-to-clipboard button,
  same pattern as view_users.
- Detail drawer now shows Memory ID / User ID / Team ID as separate
  fields instead of stacked color tags.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ui/memory): use clean MCP-style ID pill, drop copy icons

The ID / User ID / Team ID columns showed a mono text blob with a
copy-to-clipboard icon next to each value — too busy compared to the
MCP Servers page. Swap the renderer for MCP's pill style:

- Truncated mono ID inside a blue Tailwind pill
  (`font-mono text-blue-600 bg-blue-50 ... rounded-md border`).
- No copy icon. Full ID surfaces via tooltip.
- ID column is a button that opens the detail drawer on click;
  user/team ID pills are static (not clickable).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(memory): address greptile review feedback

Addresses 5 greptile findings (3/5 → higher confidence target):

1. Identity-less orphan rows (P1): non-admin callers with no user_id AND
   no team_id could create rows that the visibility filter would never
   match again. Now rejected up front with 400 — caller must authenticate
   with a scoped key or act as PROXY_ADMIN.

2. Upsert race returning 500 (P1): PUT's check-then-create isn't atomic;
   a concurrent writer could slip a row in between the 404-check and the
   create call. Now catch unique-violation on create, re-read, and fall
   through to update — PUT stays idempotent. If the conflicting row
   belongs to a different scope, surface a 409 instead of 500.

3. PUT-create scope inconsistency (P2): PUT's create branch always used
   the caller's own user_id/team_id, so admins couldn't bootstrap rows
   scoped elsewhere via PUT (only POST). Now PUT-create calls the shared
   `_resolve_scope()` helper, matching POST semantics.

4. Stale schema comment (P2): schema said "Keyed by (key, user_id,
   team_id)" but `key` is globally unique. Updated all three schema
   copies to reflect the actual design.

5. UI silently truncated at 200 (P2): MemoryView fetched pageSize=200
   with no load-more. Swapped to real server-side pagination driven by
   `data.total`; page size is now 50 and the pager is a real AntD
   control.

Also extracts a shared `_resolve_scope()` helper and `_is_unique_violation()`
from create_memory so POST and PUT don't drift on the scope/error logic.

Tests: +3 new (identity-less 400, PUT admin bootstrap, PUT race →
update), 18/18 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(memory): typed Prisma error + explicit-null metadata on PUT

Two more greptile threads from the last review:

- Unique-violation detection was string-matching "Unique"/"UniqueViolation"
  in the exception message, fragile across Prisma/driver versions. Now
  check the typed error `code == "P2002"` first, with string fallback.

- PUT could not distinguish "metadata omitted" from "metadata: null" —
  both parsed as `None`, so callers had no way to clear stored metadata.
  Switch to Pydantic v2's `model_fields_set` to tell which fields the
  caller actually sent; explicit null now clears the column.

New tests:
- explicit null clears metadata
- omitted metadata preserves existing value

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ui/memory): send explicit null when user clears metadata

Addresses the remaining P1 from the last greptile review:

When the edit modal's metadata textarea was cleared and saved,
`metadataParsed` stayed `undefined`, `JSON.stringify` dropped the key
entirely, and the backend's `model_fields_set` guard therefore left
the stored metadata untouched — UI showed success but nothing changed.

Now: empty textarea on edit → send explicit `null` so the backend
sees `metadata` in `model_fields_set` and clears the column.
Empty textarea on create still maps to `undefined` (field omitted)
to avoid Prisma's `Json? = None` quirk on insert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ui/memory): preserve slashes in key path encoding

The backend route `/v1/memory/{key:path}` supports keys with slashes,
but `encodeURIComponent` encoded `/` as `%2F`. Some proxies (nginx
default, CloudFlare, AWS ALB) reject or re-decode `%2F` mid-flight,
so UI update/delete calls on slash-containing keys could fail or
silently misroute.

New helper `encodeMemoryKeyForPath` splits by `/`, URL-encodes each
segment, then rejoins with literal `/`. Every other unsafe char
(spaces, `?`, `#`, `%`) stays encoded per-segment; slashes stay as
path delimiters, matching what the `:path` converter expects.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ui/memory): drop misleading client-side column sorters

With server-side pagination, client sorters on `key` and `updated_at`
only reorder the current page while pretending to sort the full
dataset — users would see "sorted by name" but only the visible 50
rows would actually be sorted.

Remove the sorters. The backend already returns rows in
`updated_at DESC` order (sensible default for a memory view), and
users can narrow the result with the key-prefix filter.

Greptile also flagged missing `@@map` on the new model as a
"consistency" issue, but only 1 of 59 tables in this repo uses
`@@map` — the dominant pattern is to rely on Prisma's default
(model name == table name). Skipping that finding as a
false-positive on convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(memory): compose visibility + key filters via explicit AND

Greptile P1 (filter-fragility): `where.update(vis)` was semantically
correct today, but dict-merging by key meant any future visibility
filter that grew a new top-level "OR" would silently clobber the
existing key filter.

Compose explicitly instead:

    where = {"AND": [key_filter, vis]}

Applied to both `list_memory` and `_find_memory_for_caller`. When
either side is empty (admin has no visibility filter; list has no
key filter), skip the wrapper and use the non-empty side directly
to keep the generated SQL clean.

Test fake's `_matches` now understands top-level `AND` too.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(ui/memory): wrap write helpers with react-query useMutation

Previously the Memory view read via `useQuery` but called the raw
create/update/delete fetch helpers directly in handlers, tracking
loading state with a local `submitting` flag and invalidating state
via `refetch()`. That mixes two concerns:

- it skips react-query's mutation state (isPending / isError / isSuccess)
- `refetch()` only retouches the currently-mounted query instance, not
  other cached pages, so navigating back to an older page could show
  stale rows

Switch the three write paths to `useMutation`:

- `createMutation`, `updateMutation`, `deleteMutation` — each owns
  the mutation fn, success toast, and error toast.
- Success handlers invalidate the whole `["memoryList", ...]` prefix
  via `queryClient.invalidateQueries`, so every cached page refetches
  (pagination + filter-aware).
- Refresh button now invalidates instead of `refetch()`, keeping all
  behavior consistent.
- handleSave/handleDelete become thin adapters that call `.mutateAsync`;
  their errors are swallowed locally since the mutation's onError has
  already surfaced the toast.

Also tightened the edit modal's key-field tooltip to reflect the
actual global-unique semantics (was "Unique per user/team scope").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(memory): close cross-user write gap + sanitize 500 errors (Veria)

Addresses two Veria findings:

**High — cross-user memory tampering via team membership.** The
visibility filter uses an OR (`user_id == caller OR team_id == caller`)
so team members can SEE each other's team-scoped rows. That's
intentional for list/get. But because PUT/DELETE used the same filter
to find the target row, any team member could overwrite or delete a
teammate's *personal* row whenever both `user_id` and `team_id` were
stamped on it — broader visibility was being silently treated as
broader authority.

New `_assert_write_access(row, caller)` enforces ownership for
mutations. Non-admin rules:

- The row's `user_id` must match the caller (personal ownership), OR
- The row has no `user_id` and its `team_id` matches the caller's
  team (a "pure team row" intended for shared writes).

Admins bypass the check. The same gate runs in PUT (both regular
and post-race-recovery branches) and DELETE.

**Medium — DB internals leaked through 500 detail.** Every `except`
block was raising `HTTPException(500, detail=str(e))`, which surfaces
Prisma error strings (table/column names, host:port, error class
names) to API callers. New `_internal_error()` helper logs the real
exception server-side and returns a generic, caller-safe `detail`.
Applied to create, list, upsert (general fallthrough), and delete.

Also tightened the race-recovery 409 message to drop the "in a
different scope" wording — the caller never needs to know whose
scope it lives in.

Tests (+5):
- teammate cannot overwrite personal row → 403
- teammate cannot delete personal row → 403
- teammate CAN modify pure team row (no user_id stamped) → 200
- admin bypasses write-auth → 200
- 500 response never echoes Prisma internals (table/host/class names)

25/25 unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(memory): require team admin to modify pure team rows

Tightens the write-authorization rule for "pure team rows" (rows with
no user_id stamped, only team_id) to match the pattern used by
team-management endpoints (`_is_user_team_admin` + `_is_user_org_admin_for_team`):

- Plain team members can READ team rows via the OR visibility filter
  (intentional, unchanged).
- Only PROXY_ADMIN, team admins of the row's team_id, or org admins
  for the team's organization may MODIFY them. Plain members get 403.

`_assert_write_access` is now async and takes the prisma_client so it
can fetch the team and run the existing `_is_user_team_admin` /
`_is_user_org_admin_for_team` helpers from
`litellm.proxy.management_endpoints.common_utils`. The org-admin path
is best-effort: it calls `get_user_object`, which depends on the
proxy_server module being initialized, so any exception there is
treated as "not an org admin" rather than crashing the request.

Tests:
- team admin can modify pure team row → 200
- plain team member cannot modify pure team row → 403
- plain team member cannot delete pure team row → 403

Updates the test fake to add a tiny `litellm_teamtable.find_unique`
implementation and a `_make_team(team_id, admin_user_ids=[...])`
helper.

27/27 unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: mypy + UI page-metadata sync for memory page

Two CI failures:

1. mypy: `_find_memory_for_caller` had `key_filter` inferred as
   `dict[str, str]` (literal type) and the conditional `{"AND": [key_filter, vis]}`
   returned `dict[str, list[...]]`, so the join site failed
   `dict-item` typing. Annotate both intermediates as `dict` so mypy
   widens the value type.

2. UI test (`page_utils.test.ts > should have descriptions for all
   pages`): every leftnav entry must have a description in
   `page_metadata.ts`, and `memory` was missing. Added a one-line
   description, matching the style of neighboring entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [Feat] Day-0 support for GPT-5.5 and GPT-5.5 Pro (#26449)

* feat(openai): day-0 support for GPT-5.5 and GPT-5.5 Pro

Add pricing + capability entries for the new GPT-5.5 family launched by
OpenAI on 2026-04-24:

- gpt-5.5 / gpt-5.5-2026-04-23 (chat): $5/$30/$0.50 per 1M
  input/output/cached input
- gpt-5.5-pro / gpt-5.5-pro-2026-04-23 (responses-only): $60/$360/$6
  per 1M input/output/cached input

Other fees (long-context >272k, flex, batches, priority, cache
discounts) follow the same ratios as GPT-5.4, with context window
retained at 1.05M input / 128K output.

No transformation / classifier code changes are required:
OpenAIGPT5Config.is_model_gpt_5_4_plus_model() already matches 5.5+ via
numeric version parsing, and model registration is driven from the
JSON. The existing responses-API bridge for tools + reasoning_effort
(litellm/main.py:970) already covers gpt-5.5-pro.

Tests:
- GPT5_MODELS regression list now covers gpt-5.5-pro and dated variants
- New test_generic_cost_per_token_gpt55_pro cost-calc test
- Updated test_generic_cost_per_token_gpt55 for long-context fields

* fix(openai): mirror reasoning_effort flags onto gpt-5.5 dated variants

gpt-5.5-2026-04-23 and gpt-5.5-pro-2026-04-23 were missing the
supports_none_reasoning_effort, supports_xhigh_reasoning_effort, and
supports_minimal_reasoning_effort flags that their non-dated
counterparts define. Reasoning-effort routing in OpenAIGPT5Config is
fully capability-driven from these JSON flags — since an absent flag
is treated as False for opt-in levels (xhigh), users pinning to a
dated snapshot would silently lose xhigh support and diverge from the
base alias on logprobs + flexible temperature handling.

Copy the flags onto both dated variants so every dated snapshot
inherits the base model's reasoning-effort capability profile.

Adds a parametrized regression test that asserts
supports_{none,minimal,xhigh}_reasoning_effort parity between each
dated variant and its non-dated counterpart, preventing future drift
when new snapshots are added.

* fix(schema): close LiteLLM_MemoryTable model brace dropped during merge

The rebase against `litellm_internal_staging` (which added
`LiteLLM_AdaptiveRouterState` / `LiteLLM_AdaptiveRouterSession`) left
the closing brace of `LiteLLM_MemoryTable` missing in all three
schema copies — the next model declaration ended up parsed as a field
of the memory table, surfacing as the CI prisma error:

    error: This line is not a valid field or attribute definition.
      -->  schema.prisma:1250
       |
    1249 | // Per-(router, request_type, model) Beta posterior for the adaptive router.
    1250 | model LiteLLM_AdaptiveRouterState {

Add the missing `}` (and the standard blank line) after the memory
table's `@@index([team_id])` in `schema.prisma`,
`litellm/proxy/schema.prisma`, and
`litellm-proxy-extras/litellm_proxy_extras/schema.prisma`.

`prisma generate --schema litellm/proxy/schema.prisma` now runs clean;
27/27 memory unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Mateo Wang <277851410+mateo-berri@users.noreply.github.com>
2026-04-24 18:38:07 -07:00
Yuneng Jiang
5be4ed911e
fix: preserve trailing slash when joining pass-through paths
posixpath.normpath strips trailing slashes, which silently changed
endpoints like /v1/models/ to /v1/models. Track whether the original
input ended in a slash and re-append it after normalization.
2026-04-24 18:23:59 -07:00
Mateo Wang
0beec45c13
[Feat] Add azure/gpt-5.5 + azure/gpt-5.5-pro entries (+ dated variants) (#26361)
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
Read Version from pyproject.toml / read-version (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (auth-checks, tests/proxy_unit_tests/test_auth_checks.py tests/proxy_unit_tests/test_user_api_key_auth.py, 20, 8) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (key-generation, tests/proxy_unit_tests/test_key_generate_prisma.py, 30, 0) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (proxy-utils, tests/proxy_unit_tests/test_proxy_utils.py, 20, 8) (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-db (remaining, tests/proxy_unit_tests --ignore=tests/proxy_unit_tests/test_key_generate_prisma.py --ignore=tests/proxy_unit_tests/test_auth_checks.py --ignore=tests/proxy_unit_tests/test_user_api_key_auth.py --ignore=tests/proxy_unit_tests/test_p… (push) Has been cancelled
Unit Tests: Security / security (push) Has been cancelled
GitHub Actions Security Analysis / zizmor (push) Has been cancelled
* feat(azure): add azure/gpt-5.5 + azure/gpt-5.5-pro entries (+ dated variants)

Azure variants of OpenAI's GPT-5.5 family. Microsoft has not yet
shipped GPT-5.5 on Azure OpenAI (latest GA on the Foundry models page
is GPT-5.4 as of 2026-04-24), but adding the entries day-0 mirrors the
established precedent for azure/gpt-5.4* (which were in the cost map
before the Azure rollout) so cost tracking and capability flags work
the moment customers deploy.

Schema follows the existing azure/gpt-5.4* shape:
- Same base/long-context pricing as openai/gpt-5.5*: $5/$30 chat,
  $60/$360 pro per 1M, with priority tier 2x base
- Azure variants drop the flex/batches keys (Azure has no flex tier)
  but keep priority pricing, matching gpt-5.4* precedent
- mode=chat for the thinking model, mode=responses for pro

reasoning_effort capability flags mirror the OpenAI variants exactly
since Azure proxies the same API contract: minimal rejection on both
chat and pro, low/none rejection on pro. Once #26456 (which sets
supports_low_reasoning_effort + minimal=false on openai/gpt-5.5*)
lands, OpenAI and Azure flag profiles align.

Tests pin entry presence + pricing for all four Azure variants and
verify the live-API-derived reasoning_effort flags.

* test: register supports_low_reasoning_effort in cost-map JSON schema

azure/gpt-5.5-pro and azure/gpt-5.5-pro-2026-04-23 added in this branch
carry supports_low_reasoning_effort=false. The strict
'additionalProperties: false' schema in
test_aaamodel_prices_and_context_window_json_is_valid rejected the new
key. Register it alongside the other supports_*_reasoning_effort
entries.

Note: the runtime side of this flag (code that reads it) lands in
#26456. Until that PR merges the flag is inert for both Azure and
OpenAI pro entries, but having the schema accept it lets cost-map
tests pass on either merge order.
2026-04-24 18:10:21 -07:00
Yuneng Jiang
961672efa2
fix: harden pass-through target URL construction
Use posixpath-based joining when combining the operator-configured base
target with request subpaths and provider endpoint paths so any path
prefix on the base URL is preserved and '..' segments cannot climb out
of it. Adds a shared HttpPassThroughEndpointHelpers.join_base_and_endpoint_path
helper and routes the generic and provider pass-through handlers
(Anthropic, Gemini, Mistral, Cohere, Cursor, Milvus, OpenAI, etc.)
through it.
2026-04-24 18:09:49 -07:00
yuneng-jiang
9521b74e9a
Merge pull request #26464 from BerriAI/litellm_team_metadata_hardening
Some checks failed
Unit Tests: Security / security (push) Has been cancelled
Unit Tests: Caching (Redis) / caching-redis (push) Has been cancelled
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-utils (push) Has been cancelled
Unit Tests: Proxy DB Operations / auth-checks (push) Has been cancelled
Unit Tests: Proxy DB Operations / budgets (push) Has been cancelled
Unit Tests: Proxy DB Operations / custom-logging (push) Has been cancelled
Unit Tests: Proxy DB Operations / db-and-spend (push) Has been cancelled
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Has been cancelled
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Has been cancelled
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Has been cancelled
Unit Tests: Proxy DB Operations / key-generation (push) Has been cancelled
Unit Tests: Proxy DB Operations / logging-misc (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-runtime (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-server-core (push) Has been cancelled
Unit Tests: Proxy DB Operations / schema-migration (push) Has been cancelled
[Fix] Harden team metadata handling in /team/new and /team/update
2026-04-24 17:57:15 -07:00
milan-berri
3c0d172d4e
fix(proxy): single-team DB fallback when JWT has no team_id (#26418)
* 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
2026-04-24 17:51:42 -07:00
harish-berri
f08a8534ab fix failing test
Some checks failed
Unit Tests: Caching (Redis) / caching-redis (push) Has been cancelled
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Has been cancelled
Unit Tests: Security / security (push) Has been cancelled
Unit Tests: Proxy DB Operations / custom-logging (push) Has been cancelled
Unit Tests: Proxy DB Operations / logging-misc (push) Has been cancelled
Unit Tests: Proxy DB Operations / auth-checks (push) Has been cancelled
Unit Tests: Proxy DB Operations / budgets (push) Has been cancelled
Unit Tests: Proxy DB Operations / db-and-spend (push) Has been cancelled
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Has been cancelled
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Has been cancelled
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Has been cancelled
Unit Tests: Proxy DB Operations / key-generation (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-runtime (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-server-core (push) Has been cancelled
Unit Tests: Proxy DB Operations / schema-migration (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-utils (push) Has been cancelled
2026-04-25 00:44:59 +00:00
user
0a4640fbd0
fix(mcp): don't coerce ProxyException.code with int()
Greptile flagged a regression introduced in the previous commit's merged
exception handler: ``ProxyException.__init__`` normalizes ``code`` via
``str(code)``, so a ``code=None`` (valid per the type signature) becomes
the string ``"None"``. Coercing that with ``int(...)`` raises
``ValueError``, which propagates uncaught and rewrites the auth error as
an unhandled 500 — degrading security posture compared to the pre-merge
``str(e.code) in ("401", "403")`` shape.

Compare against both int and str forms of the auth-error codes instead
of coercing. Adds a regression test for the ``code=None`` case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:42:43 +00:00
yuneng-jiang
7723a54478
Merge pull request #25677 from BerriAI/litellm_migration_projects
[Refactor] Proxy: move projects management to enterprise package
2026-04-24 17:40:33 -07:00
yuneng-jiang
91d6626254
Merge pull request #25808 from BerriAI/litellm_sendInviteEmailToggle
[Feature] UI - Users: Add Send Invitation Email Toggle
2026-04-24 17:40:14 -07:00
harish-berri
b3c6566ee0 Merge origin/litellm_internal_staging into litellm_token_verification_query_opt
Conflicts resolved:
- docs/my-website/docs/proxy/config_settings.md: deleted (docs moved to litellm-docs repo)
- litellm/proxy/auth/handle_jwt.py: kept both CacheCodec import (our branch) and RouteChecks import (staging)

Made-with: Cursor
2026-04-25 00:34:43 +00:00
Yuneng Jiang
0a729388f1
fix: harden team metadata handling in /team/new and /team/update
Restrict caller-supplied metadata so server-owned keys can only be set by
the code paths that manage the underlying rows.
2026-04-24 17:27:17 -07:00
harish-berri
77847752fa feat(auth): add Prometheus metrics for combined_view SQL queries
- Introduced `litellm_auth_combined_view_queries_total` metric to track the number of combined_view SQL queries issued for virtual-key authentication.
- Added `AuthMetrics` class to encapsulate metric incrementing logic.
- Updated relevant code to increment the new metric on database lookups, aiding in the validation of Redis cache effectiveness.
- Updated Prometheus metric definitions to include the new auth diagnostic metrics.
2026-04-25 00:21:33 +00:00
yuneng-jiang
fdac444956
Merge pull request #26390 from BerriAI/litellm_guardrail_param_masking
[Fix] Guardrail param handling in list and submission endpoints
2026-04-24 17:20:07 -07:00
user
73869f0faf
fix(mcp): tighten public-route detection and OAuth2 fallback gating
Two related issues in `MCPRequestHandler.process_mcp_request`:

1. Public-route detection used `".well-known" in str(request.url)`, a
   substring match against the full URL. Attackers could smuggle the
   marker via the query string, hostname, or a deeper path segment to
   bypass authentication on any MCP route. Replaced with an exact path
   prefix on `request.url.path` (`startswith("/.well-known/")`).

2. The OAuth2 passthrough fallback (added in #20602 to support
   `auth_type=oauth2` upstream MCP servers like Atlassian) caught any
   401/403 from `user_api_key_auth` and replaced the result with an
   anonymous `UserAPIKeyAuth()`. That fallback fired regardless of the
   target server's configured `auth_type`, so an attacker presenting a
   garbage `Authorization` header could exchange a failed LiteLLM auth
   for an anonymous session against any server. The fallback now runs
   only when EVERY MCP server the request targets is operator-configured
   for `auth_type=oauth2`. For any non-oauth2 server (api_key,
   bearer_token, basic, etc.), the auth error propagates as before.

Target resolution prefers the `x-mcp-servers` header when present
(including the explicitly-empty case, which fails closed) and otherwise
parses the standard `/mcp/{server_name}` and `/{server_name}/mcp`
transport URL patterns. Routes that don't match either form fail closed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:18:36 +00:00
ishaan-berri
8a9faa81b2
feat(guardrails): LLM-as-a-Judge guardrail (#26360)
* 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
2026-04-24 17:15:32 -07:00
Yuneng Jiang
7a91d80f9a
fix(auth): exempt auth=false pass-through endpoints from centralized gate
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.
2026-04-24 17:04:37 -07:00
Milan
22439a119e
Handle cleanup delete races and accurate counts
Made-with: Cursor
2026-04-25 03:01:34 +03:00
Yuneng Jiang
3560823196
fix(passthrough): strip Server/Date/Connection from upstream response headers
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.
2026-04-24 16:56:44 -07:00
Yuneng Jiang
06e3691bc3
fix(auth): exempt public routes and trust admin token role in centralized authz gate
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
2026-04-24 16:50:29 -07:00
harish-berri
ce087aa796 refactor(proxy): update documentation and code references for enable_redis_auth_cache
- Removed the deprecated enable_redis_auth_cache field from ConfigGeneralSettings.
- Updated references in proxy_server.py to use litellm_settings for enable_redis_auth_cache.
- Clarified documentation to reflect the new configuration structure and its impact on Redis integration for user_api_key_cache.
2026-04-24 23:44:57 +00:00
mateo-berri
68d4420233 fix(ci): strip trailing class segment from JUnit classnames before pytest
Pytest tests inside a class produce JUnit XML classnames like
'tests.local_testing.test_file_types.TestFileConsts' (module + class).
The previous awk preprocessor would convert this to
'tests/local_testing/test_file_types/TestFileConsts.py', which doesn't
exist, causing pytest to collect 0 items on rerun.

Strip a trailing '.<UppercaseSegment>' before the dot-to-slash
conversion. Module path segments are lowercase (test files start with
'test_'), and the class name is the only segment beginning with an
uppercase letter, so this is unambiguous.

Verified affected files in tests/local_testing/: test_file_types.py
(TestFileConsts), test_gcs_cache_unit_tests.py, test_disk_cache_unit_tests.py,
test_docker_no_network_on_deploy.py, test_sagemaker_nova_integration.py,
test_cache_preset_key.py.
2026-04-24 16:42:21 -07:00
Milan
69c5840e5f
Test cleanup of multiple expired UI session keys
Made-with: Cursor
2026-04-25 02:21:28 +03:00
Yuneng Jiang
ae9f12fb8a
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_yj_apr23 2026-04-24 16:13:52 -07:00
Milan
60f6a5dcfa
Tune expired UI session cleanup lock logging
Made-with: Cursor
2026-04-25 02:12:31 +03:00
yuneng-jiang
70a986e689
Merge pull request #26457 from BerriAI/litellm_enterpriseLicenseMetadata
[Infra] Declare proprietary license in litellm-enterprise metadata
2026-04-24 16:10:32 -07:00
ryan-crabbe-berri
c91a22a001
Merge pull request #26438 from BerriAI/litellm_fix-jwt-admin-bypass
fix(jwt-auth): apply team TPM/RPM + attribution for admins using x-litellm-team-id
2026-04-24 16:07:50 -07:00
ishaan-berri
7cf6a95b62
fix(vertex passthrough): log :embedContent and :batchEmbedContents responses (#26146)
* 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
2026-04-24 16:07:11 -07:00
Milan
21cf42f568
Add expired UI session key cleanup job
Made-with: Cursor
2026-04-25 01:57:22 +03:00
Yuneng Jiang
ea5a34c59a
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_sendInviteEmailToggle 2026-04-24 15:34:34 -07:00
mateo-berri
c3338384c9 test: register supports_low_reasoning_effort in cost-map JSON schema
The strict 'additionalProperties: false' schema in
test_aaamodel_prices_and_context_window_json_is_valid rejected the new
flag added in this PR's earlier commit. Register it alongside the other
supports_*_reasoning_effort entries so the schema validation passes.
2026-04-24 15:33:30 -07:00
mateo-berri
94f8f12a00 feat(openai): add supports_low_reasoning_effort flag; reject low on gpt-5.5-pro
gpt-5.5-pro only accepts reasoning_effort in {medium, high, xhigh}
(verified live against OpenAI's API on 2026-04-24). LiteLLM previously
had no way to express this constraint — the existing JSON schema
covered none/minimal/xhigh but not low. Result: drop_params=true users
saw an avoidable 400 from OpenAI.

Add supports_low_reasoning_effort following the existing opt-out
pattern (default-allow, explicit false to block). Mirror the minimal
branch in OpenAIGPT5Config.map_openai_params so 'low' goes through the
same _is_reasoning_effort_level_explicitly_disabled gate.

Set the flag to false on gpt-5.5-pro and gpt-5.5-pro-2026-04-23 in
both model_prices JSON files (kept in sync). Other models leave the
key absent so behavior is unchanged.

Tests cover: rejection on pro variants (no drop_params), drop on pro
with drop_params=True, passthrough on gpt-5.5 chat, passthrough on
unknown models, and the helper-level _is_reasoning_effort_level_explicitly_disabled
contract.
2026-04-24 15:05:43 -07:00
Yuneng Jiang
5d8ef97fa1
chore(packaging): declare proprietary license in litellm-enterprise metadata
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.
2026-04-24 14:56:28 -07:00
shin-berri
082a8faf46
Merge pull request #26454 from BerriAI/litellm_remove_docs_repo_moved
[Infra] Remove docs/my-website, point contributors to litellm-docs repo
2026-04-24 14:54:35 -07:00
Ryan Crabbe
a0bba43cea
fix(jwt-auth): soft-fail unresolvable x-litellm-team-id for admins
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.
2026-04-24 14:31:36 -07:00
mateo-berri
34c93645e9 fix(openai): gpt-5.5 does not support reasoning_effort=minimal
Verified against OpenAI's live Chat Completions API on 2026-04-24:

  POST /v1/chat/completions
  {"model": "gpt-5.5", "reasoning_effort": "minimal", ...}
  -> 400 Unsupported value: 'reasoning_effort' does not support 'minimal'
     with this model. Supported values are: 'none', 'low', 'medium',
     'high', and 'xhigh'.

  POST /v1/chat/completions
  {"model": "gpt-5.5-pro", "reasoning_effort": "minimal", ...}
  -> 400 Unsupported value: 'minimal' is not supported with the
     'gpt-5.5-pro' model. Supported values are: 'medium', 'high', and
     'xhigh'.

Set supports_minimal_reasoning_effort=false on all four entries
(gpt-5.5, gpt-5.5-2026-04-23, gpt-5.5-pro, gpt-5.5-pro-2026-04-23) so
OpenAIGPT5Config._is_reasoning_effort_level_explicitly_disabled fires
and LiteLLM either drops the param (drop_params=True) or raises a
local UnsupportedParamsError, instead of round-tripping to OpenAI for
a 400.

Adds a parametrized test_gpt55_reasoning_effort_flags_match_live_openai_api
test that pins supports_{none,minimal,xhigh}_reasoning_effort on each
entry to OpenAI's actual API contract.

Note: gpt-5.5-pro additionally rejects 'none' and 'low'. 'none' is
already handled (supports_none_reasoning_effort=false). 'low' is not
representable in the current JSON schema (no supports_low flag);
filing separately.
2026-04-24 14:30:41 -07:00
Yuneng Jiang
d42281338e ci: check out litellm-docs directly into docs/my-website
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.
2026-04-24 14:21:18 -07:00
Yuneng Jiang
c35f3a50ae docs: remove docs/my-website, point contributors to litellm-docs
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.
2026-04-24 14:17:46 -07:00
Mateo Wang
d21e90f683
[Feat] Day-0 support for GPT-5.5 and GPT-5.5 Pro (#26449)
* feat(openai): day-0 support for GPT-5.5 and GPT-5.5 Pro

Add pricing + capability entries for the new GPT-5.5 family launched by
OpenAI on 2026-04-24:

- gpt-5.5 / gpt-5.5-2026-04-23 (chat): $5/$30/$0.50 per 1M
  input/output/cached input
- gpt-5.5-pro / gpt-5.5-pro-2026-04-23 (responses-only): $60/$360/$6
  per 1M input/output/cached input

Other fees (long-context >272k, flex, batches, priority, cache
discounts) follow the same ratios as GPT-5.4, with context window
retained at 1.05M input / 128K output.

No transformation / classifier code changes are required:
OpenAIGPT5Config.is_model_gpt_5_4_plus_model() already matches 5.5+ via
numeric version parsing, and model registration is driven from the
JSON. The existing responses-API bridge for tools + reasoning_effort
(litellm/main.py:970) already covers gpt-5.5-pro.

Tests:
- GPT5_MODELS regression list now covers gpt-5.5-pro and dated variants
- New test_generic_cost_per_token_gpt55_pro cost-calc test
- Updated test_generic_cost_per_token_gpt55 for long-context fields

* fix(openai): mirror reasoning_effort flags onto gpt-5.5 dated variants

gpt-5.5-2026-04-23 and gpt-5.5-pro-2026-04-23 were missing the
supports_none_reasoning_effort, supports_xhigh_reasoning_effort, and
supports_minimal_reasoning_effort flags that their non-dated
counterparts define. Reasoning-effort routing in OpenAIGPT5Config is
fully capability-driven from these JSON flags — since an absent flag
is treated as False for opt-in levels (xhigh), users pinning to a
dated snapshot would silently lose xhigh support and diverge from the
base alias on logprobs + flexible temperature handling.

Copy the flags onto both dated variants so every dated snapshot
inherits the base model's reasoning-effort capability profile.

Adds a parametrized regression test that asserts
supports_{none,minimal,xhigh}_reasoning_effort parity between each
dated variant and its non-dated counterpart, preventing future drift
when new snapshots are added.
2026-04-24 14:10:42 -07:00
Yuneng Jiang
b300dc3e54
Preserve UISettings docstring on effective class for GET schema
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.
2026-04-24 13:56:59 -07:00
mateo-berri
ed0a965208 fix(ci): convert dot-notation test paths to file paths for CircleCI rerun
CircleCI's 'Rerun failed tests' feature passes test identifiers from the
JUnit XML classname attribute (dot notation, e.g.
'tests.local_testing.test_router') via stdin. pytest receives these paths
and collects 0 items, causing the rerun to exit 123 with no tests run.

Add an awk preprocessor before xargs that detects dot-notation module
paths and converts them to file paths (tests/local_testing/test_router.py).
File paths already containing '.py' are passed through unchanged.

Applied to all three jobs using the 'circleci tests run' + 'xargs pytest'
pattern: local_testing_part1, local_testing_part2, and the router test job.
2026-04-24 13:40:02 -07:00
Ryan Crabbe
e1bb542556
chore: fix linting (ruff PLR0915, black) on admin team-header fix
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.
2026-04-24 13:38:28 -07:00
Yuneng Jiang
85b6cae508
Type _get_effective_ui_settings_class as Type[UISettings] for mypy 2026-04-24 13:19:02 -07:00
Yuneng Jiang
32d4ae79f6
Validate UI settings PATCH body against effective class
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.
2026-04-24 13:09:16 -07:00
yuneng-jiang
51d4c6c2f2
Merge pull request #26274 from stuxf/fix/mcp-byok-oauth-auth
fix(mcp): harden OAuth authorize/token endpoints (BYOK + discoverable)
2026-04-24 13:05:44 -07:00