Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
273855b
fix(responses-bridge): map system-only chat request to system input i…
milan-berri Jun 6, 2026
d61f774
feat(bedrock): forward strict and additionalProperties to Converse to…
mateo-berri Jun 6, 2026
aeb55e7
fix(mcp): highlight MCP cards red when the logged-in user is missing …
mateo-berri Jun 6, 2026
f31d059
feat(ui): add budget duration to edit team member form (#29717)
ryan-crabbe-berri Jun 7, 2026
7bfce05
fix(ui): make workflow runs page fill full width (#29868)
yuneng-berri Jun 7, 2026
13924fa
feat: standardize rate limit errors with category, rate_limit_type, m…
mateo-berri Jun 7, 2026
3448bf7
fix(ui): default guardrails page to first tab for admins, not submitt…
yuneng-berri Jun 7, 2026
118176f
refactor(bedrock): build Converse toolSpec via a BedrockToolSpec dict…
mateo-berri Jun 7, 2026
5e2db7e
feat(litellm): add models and repository layers (#29686)
yassin-berriai Jun 7, 2026
aaf1e24
feat(ui): include internal routes in the dashboard's generated OpenAP…
ryan-crabbe-berri Jun 7, 2026
f5b11b7
feat(proxy): publish /v2/model/info in Swagger OpenAPI spec (#29900)
Sameerlite Jun 8, 2026
ff6cea4
refactor(ui): single source of truth for migrated-page routing (#29949)
ryan-crabbe-berri Jun 8, 2026
26fe26a
fix(ui/model-hub): render provider icons on the public model hub (#29…
yuneng-berri Jun 8, 2026
47b383d
fix(ui): keep create guardrail modal open on outside click (#29871)
yuneng-berri Jun 8, 2026
728f057
fix(ui): label default key type as "Full Access" on key edit page (#2…
yuneng-berri Jun 8, 2026
1afc41c
fix(ui): unify migrated-route URLs and migrate the API Reference page…
ryan-crabbe-berri Jun 8, 2026
1528f43
fix(mcp): let non-creator users OAuth into OBO-mode MCP servers from …
tin-berri Jun 8, 2026
32c88ca
Litellm oss staging 080626 (#29932)
Sameerlite Jun 8, 2026
dfb68a2
feat(galileo): add health check support for UI callback test (#29908)
Sameerlite Jun 8, 2026
69a7bdb
fix(model-management): allow deleting a BYOK model after its team is …
yuneng-berri Jun 8, 2026
a7ecf6b
feat(jwt-auth): opt-in fallback to DB team on unresolved JWT claim (#…
milan-berri Jun 8, 2026
9ccda11
fix(team_endpoints): don't block /team/update on unchanged team budge…
milan-berri Jun 8, 2026
1c881ee
fix(fireworks): enable tool calling for glm-5p1 in model cost map (#2…
milan-berri Jun 8, 2026
dfd6cbc
fix(vertex): propagate Vertex AI metadata in streaming success callba…
Sameerlite Jun 8, 2026
f59e4eb
fix(ui): show team projects to internal users (#28855)
milan-berri Jun 8, 2026
bac2590
build(deps): bump pyjwt to 2.13.0 and ws override to 8.20.1 (#29982)
yuneng-berri Jun 8, 2026
c24a360
fix(team-management): delete a team's BYOK models when the team is de…
yuneng-berri Jun 8, 2026
411bd3d
feat(vantage): include organization metadata in FOCUS Tags export (#2…
milan-berri Jun 8, 2026
1bbaf1c
fix(guardrails): read CrowdStrike AIDR identity from both metadata ba…
yuneng-berri Jun 9, 2026
92817cb
changing expires_in default to use actual slack return details (#29951)
tin-berri Jun 9, 2026
424db6a
feat(azure_ai): add MAI-Image-2.5 image generation support (#29688)
Sameerlite Jun 9, 2026
51ba6e3
fix(mcp): load MCP tool configuration tools via the OBO/passthrough-a…
tin-berri Jun 9, 2026
d84499e
fix(team): reserve team budget raises for proxy admins on /team/updat…
milan-berri Jun 9, 2026
6ae8a50
test(ui): data-driven App Router migration E2E smoke (default + serve…
ryan-crabbe-berri Jun 9, 2026
fe60f9d
fix(proxy): extend response headers hook to streaming, TTS, image gen…
michelligabriele Jun 9, 2026
38edf24
chore(ui): remove dead App Router route stubs under (dashboard) (#30045)
ryan-crabbe-berri Jun 9, 2026
d8fe091
fix(ui/mcp): reset OAuth state on create-server modal close so a prio…
tin-berri Jun 9, 2026
5b7063d
fix(mcp): allow team access-group grants in OAuth authorize/token acc…
tin-berri Jun 9, 2026
5052215
docs(security): require a reproduction video for vulnerability report…
yuneng-berri Jun 9, 2026
2481761
feat(ui): add admin flag to disable in-product UI nudges for everyone…
ryan-crabbe-berri Jun 10, 2026
9e0d92c
chore(ui): remove dead dashboard files and unused dependencies (#30047)
ryan-crabbe-berri Jun 10, 2026
2cd7e87
fix(proxy): authorize batch files using upload target_model_names (LI…
Sameerlite Jun 10, 2026
e15b37a
Add Claude Fable 5 across Anthropic, Bedrock, Vertex AI, and Azure AI…
mateo-berri Jun 10, 2026
2fe9fed
fix(caching): restore stored prompt_tokens on embedding cache hits in…
michelligabriele Jun 10, 2026
3b40ac9
Litellm oss 090626 (#30021)
Sameerlite Jun 10, 2026
f9293d4
fix(proxy): self-heal startup/reload prisma reads on engine disconnec…
michelligabriele Jun 10, 2026
a75ed00
chore(ui): make knip recognize .mjs scripts and openapi-typescript (#…
ryan-crabbe-berri Jun 10, 2026
410b892
fix(register_model): preserve built-in cache pricing when registering…
yassin-berriai Jun 10, 2026
a4a3348
[internal copy of #28007] Fix/gcp model garden streaming (#28363)
mateo-berri Jun 10, 2026
20e453f
feat(cli): per-agent `lite claude` / `codex` / `opencode` commands th…
mateo-berri Jun 10, 2026
7899463
fix(callbacks): forward callback_settings to callback initializers an…
yuneng-berri Jun 10, 2026
1436ee9
fix(mcp): drop orphaned per-user credential rows when an MCP server i…
tin-berri Jun 10, 2026
3bd3951
fix(proxy): recover from cached-plan errors by reconnecting the Prism…
yassin-berriai Jun 10, 2026
dff25fe
feat(proxy): add option to disable server-side prepared statements fo…
yassin-berriai Jun 10, 2026
b301d30
fix(release): stop backport releases from overwriting the latest badg…
yuneng-berri Jun 10, 2026
ba72ccf
feat: add conventional commits and coding guidelines (#30159)
mateo-berri Jun 10, 2026
da9d64b
fix(proxy): return 5xx on DB infra errors during auth; reserve 401 fo…
yassin-berriai Jun 10, 2026
496f5b9
fix(ui): dev server 404s on migrated-page links because uiBase hardco…
ryan-crabbe-berri Jun 11, 2026
4def691
refactor(ui): consolidate dashboard to one shell in the (dashboard) l…
ryan-crabbe-berri Jun 11, 2026
6068bb7
fix(proxy): align /v1/model/info with router deployments (#30025)
Sameerlite Jun 11, 2026
4a3860d
fix: completion_cost AttributeError on streaming Anthropic web_search…
ishaan-berri Jun 11, 2026
7a96b34
[internal copy of #30137] perf(realtime): eliminate redundant per-fra…
mateo-berri Jun 11, 2026
49ca04d
feat(bedrock): aws_bedrock_project_id for bedrock-mantle project / wo…
mateo-berri Jun 11, 2026
27c3730
fix(presidio): robust PII unmasking for Anthropic native SSE streams
arenstar Jun 10, 2026
91f56e4
fix(presidio): keep streamed tool-call args masked by default; opt-in…
arenstar Jun 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
118 changes: 118 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2690,6 +2690,122 @@ jobs:
path: ui/litellm-dashboard/playwright-report
destination: e2e-playwright-report

e2e_ui_testing_server_root_path:
docker:
- image: cimg/python:3.12-browsers@sha256:b432899af01c9a311bf74f4f22e9ada2e5306d4b1b4383f8d29e1228a5844ef2
auth:
username: ${DOCKERHUB_USERNAME}
password: ${DOCKERHUB_PASSWORD}
- image: cimg/postgres:16.0@sha256:b125148bc76e8e8eee5eb3ad6020a3a14110a14e8192f1c645128afebe2e2f84
environment:
POSTGRES_USER: e2euser
POSTGRES_PASSWORD: e2epassword
POSTGRES_DB: litellm_e2e
resource_class: large
working_directory: ~/project
environment:
DATABASE_URL: "postgresql://e2euser:e2epassword@localhost:5432/litellm_e2e"
CI: "true"
# The whole job exercises the proxy mounted under a prefix. SERVER_ROOT_PATH
# is read both by the proxy at boot (to rewrite the built UI bundle in place)
# and by migration.serverRootPath.config.ts, which refuses to run without it.
SERVER_ROOT_PATH: "/litellm"
steps:
- checkout
- setup_google_dns
- install_uv
- restore_cache:
keys:
- v1-uv-cache-{{ checksum "uv.lock" }}
- run:
name: Install Python dependencies
command: |
uv sync --frozen --all-groups --all-extras --python 3.12
uv run --no-sync python -m prisma generate --schema litellm/proxy/schema.prisma
- save_cache:
key: v1-uv-cache-{{ checksum "uv.lock" }}
paths:
- ~/.cache/uv
- restore_cache:
keys:
- ui-e2e-node-deps-v2-{{ checksum "ui/litellm-dashboard/package-lock.json" }}
- run:
name: Install Node dependencies and Playwright
command: |
cd ui/litellm-dashboard
npm ci
npx playwright install chromium
- save_cache:
key: ui-e2e-node-deps-v2-{{ checksum "ui/litellm-dashboard/package-lock.json" }}
paths:
- ui/litellm-dashboard/node_modules
- ~/.cache/ms-playwright
- run:
name: Build UI from source
command: |
cd ui/litellm-dashboard
npm run build
rm -rf ../../litellm/proxy/_experimental/out
mv out ../../litellm/proxy/_experimental/out
find ../../litellm/proxy/_experimental/out -name '*.html' ! -name 'index.html' | while read -r f; do
d="${f%.html}"; mkdir -p "$d"; mv "$f" "$d/index.html"
done
- wait_for_service:
url: tcp://localhost:5432
timeout: "30"
- run:
name: Push Prisma schema
command: uv run --no-sync python -m prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss
- run:
name: Seed database
command: |
PGPASSWORD=e2epassword psql -h localhost -p 5432 -U e2euser -d litellm_e2e \
-f ui/litellm-dashboard/e2e_tests/fixtures/seed.sql
- run:
name: Start mock LLM server
command: uv run --no-sync python ui/litellm-dashboard/e2e_tests/fixtures/mock_llm_server/server.py
background: true
- run:
name: Start LiteLLM proxy under a server root path
environment:
LITELLM_MASTER_KEY: "sk-1234"
MOCK_LLM_URL: "http://127.0.0.1:8090/v1"
DISABLE_SCHEMA_UPDATE: "true"
# Output flows to this step's own log, so a boot crash is visible here
# rather than swallowed by a downstream readiness probe.
command: |
LITELLM_LICENSE="$LITELLM_LICENSE" \
uv run --no-sync python -m litellm.proxy.proxy_cli \
--config ui/litellm-dashboard/e2e_tests/fixtures/config.yml \
--port 4000
background: true
- run:
name: Wait for prefixed proxy to be ready
command: |
for i in $(seq 1 60); do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" http://127.0.0.1:4000/litellm/health 2>/dev/null || true)
if [ "$HTTP_CODE" = "200" ]; then
echo "Prefixed proxy is ready"
exit 0
fi
sleep 2
done
echo "Prefixed proxy failed to start; see the 'Start LiteLLM proxy under a server root path' step for the boot log"
exit 1
- run:
name: Run migration smoke under SERVER_ROOT_PATH
command: |
cd ui/litellm-dashboard
LITELLM_LICENSE="$LITELLM_LICENSE" \
npx playwright test --config e2e_tests/migration.serverRootPath.config.ts
no_output_timeout: 10m
- store_artifacts:
path: ui/litellm-dashboard/test-results
destination: e2e-server-root-path-test-results
- store_artifacts:
path: ui/litellm-dashboard/playwright-report
destination: e2e-server-root-path-playwright-report

build_docker_database_image:
machine:
image: ubuntu-2204:2024.04.1
Expand Down Expand Up @@ -2795,6 +2911,8 @@ workflows:
filters: *main_branches
- e2e_ui_testing:
filters: *main_branches
- e2e_ui_testing_server_root_path:
filters: *main_branches
- build_and_test:
requires:
- build_docker_database_image
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ jobs:
// are stable maintenance releases, not pre-releases.
const isPrerelease = /(?:rc|nightly|alpha|beta|[-.]dev)/i.test(tag);

// A stable release should only claim the repo "latest" badge when its
// version is >= the current latest. Otherwise a backport (e.g. 1.84.6)
// would steal "latest" from a newer line (e.g. 1.88.1).
const versionKey = (rawTag) => {
const m = String(rawTag).match(/^v?(\d+)\.(\d+)\.(\d+)/);
if (!m) return null;
const maintenance = String(rawTag).match(/(?:\.post|\.patch\.)(\d+)/i);
return [Number(m[1]), Number(m[2]), Number(m[3]), maintenance ? Number(maintenance[1]) : 0];
};
const isAtLeast = (a, b) => {
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return a[i] > b[i];
}
return true;
};

const cosignSection = [
`## Verify Docker Image Signature`,
``,
Expand Down Expand Up @@ -90,6 +106,22 @@ jobs:
].join('\n');

try {
let makeLatest = "false";
const newVersion = versionKey(tag);
if (!isPrerelease && newVersion) {
let latestVersion = null;
try {
const latest = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo,
});
latestVersion = versionKey(latest.data.tag_name);
} catch (error) {
if (error.status !== 404) throw error;
}
makeLatest = (!latestVersion || isAtLeast(newVersion, latestVersion)) ? "true" : "false";
}

const response = await github.rest.repos.createRelease({
draft: true,
generate_release_notes: true,
Expand All @@ -108,6 +140,7 @@ jobs:
release_id: response.data.id,
body: updatedBody,
draft: false,
make_latest: makeLatest,
});

} catch (error) {
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-unit-misc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
tests/test_litellm/completion_extras
tests/test_litellm/containers
tests/test_litellm/experimental_mcp_client
tests/test_litellm/models
tests/test_litellm/repositories
tests/test_litellm/images
tests/test_litellm/interactions
tests/test_litellm/passthrough
Expand Down
18 changes: 18 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,24 @@ graph LR
7. `DBSpendUpdateWriter.update_database()` queues spend increments to Redis
8. Background job `update_spend` flushes queued spend to PostgreSQL every 60s

### Data Access Layer (Models & Repositories)

Database entities and the operations on them live in two packages at the root of `litellm/` so both the gateway (`proxy/`) and the SDK can use them without importing proxy internals:

- `litellm/models/` holds the canonical Pydantic definitions for every persisted entity (`LiteLLM_VerificationToken`, `LiteLLM_TeamTable`, `LiteLLM_UserTable`, etc.). `proxy/_types.py` re-exports these for backwards compatibility, so existing imports keep working.
- `litellm/repositories/` holds the data-access layer. `BaseRepository[T]` provides the generic CRUD (`find_by_id`, `find_many`, `create`, `update`, `delete`, `count`, `exists`); entity repositories such as `VerificationTokenRepository`, `TeamRepository`, and `UserRepository` add domain-specific queries and writes on top of it.

Conventions to follow when touching this layer:

| Concern | How it's handled |
|---------|------------------|
| JSON columns | Prisma `Json` columns are stored as JSON strings. Repositories `json.dumps()` on write and `json.loads()` on read (see `_to_model` and the `_build_*_data` helpers). |
| Archive-then-delete | `delete_team` / `delete_token` copy the row into the `LiteLLM_Deleted*` table and delete the original inside a single `prisma_client.db.tx()` transaction. Archive payloads are built explicitly so only columns that exist on the archive table are written. |
| Column vs. field names | Where a model field differs from its DB column (for example `org_id` maps to the `organization_id` column), the repository translates in both directions rather than relying on Pydantic to guess. |
| Array mutations | Adds use Prisma's atomic `push` (`add_member`, `add_admin`, `add_models`) to avoid read-modify-write races. Removals fall back to read-modify-write because Prisma has no atomic array remove. |

To add a new entity, define the model under `litellm/models/`, re-export it from `proxy/_types.py` if existing code imports it from there, and add a repository under `litellm/repositories/` (subclass `BaseRepository` for plain CRUD, or add bespoke methods when the entity needs encryption, archiving, or atomic array updates). Mirror the tests in `tests/test_litellm/repositories/`.

---

## 2. SDK Request Flow
Expand Down
13 changes: 13 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ Do not put names of customers or customer company names in code, PRs, and issues

CI supply-chain safety: Never pipe a remote script into a shell (`curl ... | bash`, `wget ... | sh`); download the artifact to a file, verify its SHA-256 checksum, then install. Pin every external tool to a specific version with a full URL (not `latest` or `stable`). Verify checksums for all downloaded binaries, using the provider's official `.sha256` / `.sha256sum` sidecar when available. These rules apply to every download in CI

Follow these coding conventions for new/updated code (a three-line fix in a legacy file shouldn't trigger huge drive-by refactors):

- Composition over inheritance
- Never-nester: early returns over deep nesting
- Don't throw; model failures as values (One function (e.g., raise_public) maps error union to existing public exception contracts via exhaustive match + assert_never)
- No mutation; instead of mutable lists and dicts, prefer tuples, NamedTuples, frozen dataclasses, etc.
- Use dependency injection
- Fully typed; no `Any` or coarse types like dict[str, Any]. Every function parameter must be strongly typed
- Use tagged unions + match
- No monster files or god objects

Follow conventional commits for commit names and PR titles

## Think Before Coding

**Don't assume. Don't hide confusion. Surface tradeoffs**
Expand Down
9 changes: 9 additions & 0 deletions litellm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,13 @@ def _dev_env_hot_reload_enabled() -> bool:
custom_prometheus_tags: List[str] = []
prometheus_metrics_config: Optional[List] = None
prometheus_emit_stream_label: bool = False
# Opt-in: emit `rate_limit_category` and `rate_limit_type` labels on
# `litellm_proxy_failed_requests_metric`. Off by default to preserve the
# pre-unification label set so existing dashboards / recording rules keyed on
# that metric keep matching after upgrade. Enable when downstream consumers
# are ready to split 429s by source (vendor vs. litellm) and dimension
# (RPM/TPM/concurrent/budget).
prometheus_emit_rate_limit_labels: bool = False
prometheus_user_budget_label_include_email_alias: bool = False
prometheus_end_user_metrics_max_series_per_metric: Optional[int] = 10000
prometheus_end_user_metrics_ttl_seconds: Optional[float] = 3600.0
Expand Down Expand Up @@ -1303,6 +1310,8 @@ def add_known_models(model_cost_map: Optional[Dict] = None):
NotFoundError,
PermissionDeniedError,
RateLimitError,
RateLimitErrorCategory,
RateLimitType,
ServiceUnavailableError,
BadGatewayError,
OpenAIError,
Expand Down
2 changes: 1 addition & 1 deletion litellm/anthropic_beta_headers_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
"bash_20241022": null,
"bash_20250124": null,
"code-execution-2025-08-25": null,
"compact-2026-01-12": null,
"compact-2026-01-12": "compact-2026-01-12",
"computer-use-2025-01-24": "computer-use-2025-01-24",
"computer-use-2025-11-24": "computer-use-2025-11-24",
"context-1m-2025-08-07": "context-1m-2025-08-07",
Expand Down
Loading
Loading