Commit graph

926 commits

Author SHA1 Message Date
yuneng-jiang
38676aa599
Merge pull request #41078 from BerriAI/litellm_integration_extensions
test: add extension and browser integration contracts
2026-09-16 17:55:25 -07:00
kerry-berri
db408f68ae
Merge pull request #41494 from BerriAI/litellm_auto_merge_price_sync
ci: auto-merge provider-info-sync PRs when CI, Greptile and Bugbot are clean
2026-09-16 17:36:02 -07:00
kerry
4b60682600 ci: auto-merge provider-info-sync PRs when CI, Greptile and Bugbot are clean
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 21:48:08 +00:00
Yuneng Jiang
c00f1b4a5c
test: add extension and browser integration contracts
Some checks failed
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
Adds integration contracts for MCP lifecycle, protocol errors and OAuth
configuration, A2A wire versions, the OpenAI consumer path, persisted
toolsets, callback delivery, guardrail effects, configured prices, the
filtered spend ledger, and a CircleCI-owned browser flow for project
detachment, with the ASGI, browser-state, client and MCP helpers they use.
Consolidates the eleven commits previously stacked on
litellm_integration_providers onto its rebased tip
2026-09-16 13:15:53 -07:00
Yuneng Jiang
5d38240ada
Merge remote-tracking branch 'origin/main' into HEAD
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
2026-09-16 12:46:42 -07:00
Yuneng Jiang
3dda798c8a
ci(e2e): consolidate cache contracts in filtered CircleCI job 2026-09-15 19:28:50 -07:00
Yuneng Jiang
6969bd9c54
ci: run replay harness on every admitted CircleCI pipeline 2026-09-14 21:12:16 -07:00
Yuneng Jiang
16fb44f23a
ci: run provider replay harness in CircleCI 2026-09-14 21:03:58 -07:00
Yuneng Jiang
94f2a4702b
test: remove redundant integration harness commentary 2026-09-14 09:40:21 -07:00
Yuneng Jiang
c900053c87
test: remove redundant integration harness commentary 2026-09-14 09:40:21 -07:00
Yuneng Jiang
474fd5ff8d
test: remove redundant integration harness commentary 2026-09-14 09:40:20 -07:00
Yuneng Jiang
682296ad68
test: verify database transactions and persisted accounting contracts 2026-09-14 05:00:32 -07:00
Yuneng Jiang
cfda6dea3e
test: cover persisted updates and warmed authorization policies 2026-09-14 04:20:06 -07:00
Yuneng Jiang
7ff8919827
test: collect owned cleanup processes immutably 2026-09-14 04:18:59 -07:00
Yuneng Jiang
16d7e695c0
test: stop proxy before cleaning up its query engine 2026-09-14 04:04:21 -07:00
Yuneng Jiang
d7c13625e9
test: qualify integration cleanup and replay invocation 2026-09-14 03:48:23 -07:00
Yuneng Jiang
92e0b72e2d
test: add CircleCI integration contract foundation 2026-09-14 03:30:52 -07:00
mateo-berri
a7ebc12673 ci: drop the removed rust bridge test path from the ocr job 2026-09-12 15:53:03 -07:00
yassin
eeef03f122 chore: merge litellm_internal_staging into litellm_lit_7039_least_busy_shared_counts
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-08 17:11:05 +00:00
Yuneng Jiang
b0e53bfbe4
fix(e2e/ui): fail the run when the Presidio fixture never comes up
Both readiness loops broke out on success and fell through on timeout, so a
mock Presidio server that failed to bind left the run going with nothing
serving /analyze. The guardrail then errored at request time and the failure
surfaced as an unrelated Playwright assertion in presidioUserStory.spec.ts
rather than as the missing fixture it actually was.

Fail the local runner with the port in the message, and add the matching wait
step to both CircleCI UI jobs, which had no readiness check at all.
2026-09-06 03:13:09 -07:00
Yuneng Jiang
274a489c46
test(e2e/ui): automate the RC checklist's Presidio guardrail walk
The guardrail section of the release checklist is done by hand every cut:
create a Presidio guardrail through the wizard, send a sentence with PII from
the playground, then open Logs and check the guardrail caught it. Nothing
covered that path, so a break anywhere along it surfaced only when someone
happened to repeat the steps.

Adds a spec that walks it once and turns the eyeball checks into assertions.
The strongest of them is the leak check: it reads the request back and fails if
the stored prompt still carries the raw address or number, which is what the
manual step is really looking for.

The stack gains a Presidio stand-in that answers the two routes the guardrail
calls, detecting a fixed regex set with a Luhn check on card numbers. Real
Presidio's detection quality is Presidio's business, and pinning the UI lane to
it would mean two heavy containers with spaCy models on every CI run for a test
that is about LiteLLM's integration. The real analyzer stays covered in the
Python lane. The stand-in returns the same entities at the same spans as the
real one for the checklist's sentence, and driving the real guardrail against it
produces the same record shape, so a test written against it is written against
the product's real behavior.

Making the stand-in return the text unmasked turns the spec red on the raw
address reaching the spend log, so the leak assertion reads live data.

run_e2e.sh and both CircleCI UI jobs start the stand-in alongside the mock LLM.
Its port is overridable like the others so two checkouts can run at once.
2026-09-06 02:36:46 -07:00
mateo-berri
ef5f51abca fix(least-busy): count for every router, and keep the expiry through a clamp
Two routers in one process shared a single handler, because the callback
manager dedupes on the class name plus the handler's public attributes and the
handler had none. The second router's requests were never counted. The handler
now carries the id of the cache it was built on, so routers with different
caches both register while the two selectors one router builds for its routing
groups still collapse into one.

Clamping a negative count back to zero used SET, which drops the key's TTL, so
the next write started the hour over. It uses INCRBY by the negative amount now,
which leaves the expiry alone.

The Lua script had no test that ran it, so tests/local_testing covers both the
sync and async paths against a real Redis, and the file is wired into the
CircleCI job that provides one.
2026-09-06 01:59:15 -07:00
Yuneng Jiang
4bd3cd9e06
ci: report every failing test in a job instead of stopping at the first
Drops `-x` from all 24 pytest invocations in .circleci/config.yml. With
`-x`, a job stops at its first failure, so a second broken test in the
same suite stays invisible until the first is fixed and CI is re-run.
That turns one round trip into N when a job has several broken tests.

This is exactly what happened in #39770: fixing
test_missing_model_parameter_curl in
tests/store_model_in_db_tests/test_openai_error_handling.py immediately
unmasked test_chat_completion_bad_model_with_spend_logs in the same
file, which had been failing for a long time without ever being
reported.

Only `-x` is removed; -v/-vv/-s/-n/--reruns and every other flag are
untouched.
2026-09-04 11:05:58 -07:00
Yujong Lee
38150dfc2c
fix(ci): pin workflow toolchain dependencies 2026-09-02 12:16:25 -07:00
Yuneng Jiang
fd72a39b1b
revert: default the proxy back to the v1 migration resolver
This reverts merge commit 2b1bd20834 (#31125)

Two CircleCI jobs on the staging-to-main promotion went red the moment
that PR landed. proxy_multi_instance_tests boots two proxies against one
database, and both now race the same migration:

  Error: P3018 A migration failed to apply
  Database error code: 40P01, deadlock detected
  Process 73 waits for ShareLock on virtual transaction 4/11;
  blocked by process 75. Process 75 waits for ExclusiveLock on
  advisory lock [16384,0,72707369,1]; blocked by process 73

Neither proxy comes up, so the job times out after 300s waiting on
localhost:4000. The same wait took 36.5s on the last green run

Timeline: #31125 merged at 18:46:14Z and the failing run started at
18:49:59Z. The merge commit is not an ancestor of the last green
revision (194a3cc) and is an ancestor of the first failing one
(01de2837)

The v2 resolver was meant to avoid exactly this class of contention, so
the deadlock looks like a bug in it rather than a reason to abandon it.
Putting the default back to v1 buys time to fix it without holding up
the release
2026-09-01 12:56:08 -07:00
yuneng-jiang
2b1bd20834
Merge pull request #31125 from BerriAI/litellm_/stoic-jones-7de871
feat(proxy): default to the v2 migration resolver, keep v1 as an opt-out
2026-09-01 11:46:13 -07:00
Yuneng Jiang
4f6fd85ab1
feat(proxy): default to the v2 migration resolver, keep v1 as an opt-out
The v2 resolver skips the diff-and-force recovery that caused schema
thrashing when two LiteLLM versions contend for one database during a
rolling deploy. The standalone migration Job already defaulted to v2; this
aligns the proxy-server path.

v1 stays reachable two ways: --use_legacy_migration_resolver on the CLI, and
USE_V2_MIGRATION_RESOLVER=false for containerised deploys, where
prisma_migration.py calls run_server with a fixed argv and the env var is the
only route in. --use_v2_migration_resolver still parses, so existing commands
do not die on an unknown option.

Because v2 fails fast where v1 retried every failed deploy, a database that is
not accepting connections yet, or another instance holding the migration
advisory lock, would now kill a boot that used to ride it out. Those two
failures are retried, with Prisma's stderr logged each round, and still raise
once the attempts are spent.

Moves the resolver tests from litellm-proxy-extras/tests, which no CI job
runs, into tests/litellm-proxy-extras, and repoints the dedicated Postgres
CircleCI job at the legacy path so v1 keeps real-DB and proxy-boot coverage.
2026-08-28 01:10:05 -07:00
Yuneng Jiang
feffb62266
test: refresh the suites that drifted from langfuse and OpenAI's retired Assistants API
Two unrelated causes, both leaving staging red with tests that no longer describe
anything true.

#38264 gave LangFuseLogger a langfuse_environment argument and started carrying it
in the credentials dict. The handler test's fake logger did not accept the new
keyword, so constructing it raised TypeError, and four cases in
test_langfuse_unit_tests rebuilt the cache key by hand from three fields and missed
on the four-field key production now writes. Caching itself was never broken: the
handler sets and gets with the same dict. The fake now takes the argument and
asserts it is forwarded, and the cache assertion issues a second identical request
and expects the same logger back, which is the behaviour that matters and cannot
rot the next time a credential field is added.

OpenAI has retired the Assistants API. /v1/assistants and /v1/threads both answer
404 with a valid key, where every live route answers 401, so nothing calling them
can pass again. test_custom_logger_passthrough covered generic passthrough logging
and only used assistants because it is a route with no provider-specific handler;
it moves to /v1/moderations, which is still unclaimed by
_is_supported_openai_endpoint, so the same generic branch is exercised. The two
tests there asserted the same thing against different dead routes, so they collapse
into one. The Ruby suite existed solely to drive assistants, threads, messages and
runs, so it goes along with the RVM and bundler steps that were installed only to
run it, and the two dead OpenAI assistants cases leave
test_openai_assistants_passthrough.

The Azure assistants case in that file stays. Azure runs its own lifecycle and I
could not reach the CI deployment to check whether that API is still there.
2026-08-27 23:24:36 -07:00
yuneng-jiang
68489f62ff
ci: run the enterprise package suite in GitHub Actions (#37798)
tests/enterprise is 13 files and 244 tests that only CircleCI runs, and CircleCI
gates nothing: it triggers on PR labeled events, none of its jobs are required,
and red runs get merged past. So the suite that covers the enterprise package's
guardrails, auth and management endpoints has had no say in whether a change
lands.

Measured on 2026-08-21 with every credential stripped from the environment: 240
passed, 4 skipped, nothing failed. It needs no provider key, so it can be a
required shard rather than a scheduled lane, unlike the other CircleCI suites in
this group, which each carry a live-API minority.

The CircleCI job is removed in the same commit so the suite runs once, not twice.
2026-08-22 22:57:50 -07:00
yuneng-jiang
6c30b4331d
ci: measure enterprise/ coverage (#37788)
codecov.yaml has carried an `Enterprise` component scoped to `enterprise/**`
since it was written, and it has never received a line of data. Every one of
the 19 coverage invocations across the unit base, the MCP workflow and the
CircleCI config passes `--cov=./litellm` and nothing else, so 11,203 lines of
paid-customer code sat outside the measured universe while the reported number
described only the rest.

litellm-enterprise is a uv workspace member and a direct dependency, so every
job that syncs already has it installed and importable; only the measurement
was missing.

Measured on tests/test_litellm/enterprise, the shard that exercises this code:
0 enterprise files in the report before, 142 after, at `enterprise/...` paths
that match the component's existing glob. That shard alone puts enterprise at
30.8%, which nudged its own total from 24.09% to 24.20% rather than down. The
aggregate direction across every shard is not knowable until they all report,
and a drop there is the instrument working, not a regression.
2026-08-22 22:55:01 -07:00
yuneng-jiang
346c693860
ci: port the Postgres suites off CircleCI onto service containers (#37785)
* ci: port the Postgres suites off CircleCI onto service containers

proxy_behavior_tests, proxy_security_tests and schema_migration_check were
near-identical CircleCI jobs: a Postgres sidecar, a schema seed, and one pytest
tree each. They ran nowhere else, and CircleCI holds none of the branch
ruleset's required checks, so the signal they produced gated nothing.

test-postgres.yml runs the same three trees on a Postgres service container as
one matrix, keeping each suite's own seeding rather than normalising it: the
behavior and security trees keep `prisma db push`, and the migration tree keeps
an empty database, which is what it needs to apply every committed migration
itself.

Their CircleCI definitions and workflow entries go with them, taking the config
from 47 jobs to 44. assert_ci_coverage.py stays green: dropping the new
workflow fails the census on exactly these trees, so the coverage moved rather
than went missing.

auth_ui_unit_tests is deliberately left behind. Ported, two of its
tests fail because prepare_metadata_fields refuses enterprise-only keys without
LITELLM_LICENSE, which exists as a CircleCI project variable and has no
GitHub Actions secret. Creating that secret is a human action, so the job stays
on CircleCI until it exists rather than shipping a red shard or quietly
deselecting the two tests.

* chore(ci): drop the narrative header from test-postgres.yml
2026-08-22 22:45:30 -07:00
yuneng-jiang
5b1a9563d6
chore(ci): close the test-census blind spots and move scripts out of workflows/ (#37586)
The agent job's CircleCI glob collected `tests/agent_tests/**/test_*.py` and then
piped it through `grep -v` to drop `local_only_agent_tests/`. `assert_ci_coverage.py`
reads the glob but not the pipeline, so those two files looked covered and were
invisible to the census. The glob now excludes them structurally and they carry an
allowlist entry instead, which is a decision on the record rather than a hidden
filter. The collected file set is unchanged: `tests/agent_tests/` holds exactly one
CI-runnable test at the top level.

`tests/scim_tests/` held a single JSON fixture and no tests, referenced from nowhere.

`.github/workflows/` is for workflows. Both stray scripts move to `.github/scripts/`
with their callers updated: the price-file updater is invoked by
`auto_update_price_and_context_window.yml`, and the translation-report runner by
`make test-llm-translation`. The audit listed the latter as orphaned, but Makefile
line 317 still runs it, so it moves rather than being deleted.

The rollout heads-up workflow was a deliberate one-shot for the agent-shin rollout.
That rollout is done, the triage and auto-close workflows have been running daily
since June, so the pre-flip warning window is long past. Its script and dedicated
test go with it, and the sibling workflow-invariant test drops its entry.
2026-08-20 10:07:14 -07:00
yuneng-jiang
7b6e16cfd3
perf(ci): gate the lint, MCP and dashboard jobs on the pull request's file list (#37559)
PR #37550 taught the backend unit-test shards to read the pull request's own
file list, but four required jobs were never wired to that gate and ran in full
on every pull request regardless of what it touched. A UI-only pull request
still paid roughly 17 runner-minutes of Python work it could not have affected,
and a backend-only one still installed and built the dashboard.

Lint and the MCP suite now take the existing backend decision. The dashboard
build and unit tests take a new ui decision, which tracks ui/ rather than
reusing client: client deliberately runs whenever the backend changes, because
it gates CircleCI's end-to-end jobs that drive a real proxy, while the build and
the unit tests cannot see the backend at all. CI config counts as ui-relevant
too, so a pull request that rewrites the dashboard workflows still exercises
them instead of shipping unvalidated.

The gate stays inside the job rather than moving to on.paths or to a job-level
condition on the shard callers. A workflow filtered out by on.paths never starts
and never reports, so a required check waits forever, and a skipped caller job
publishes its own name instead of the nested "<shard> / Run tests" the ruleset
requires. Both were measured before settling on this shape.

Three setup steps in the shard base and in the documentation job also leaked
past the gate, so a skipped shard still spent about twelve seconds installing uv
and restoring its cache. They now carry the same condition, and the documentation
job stops cloning litellm-docs when it has nothing to validate.
2026-08-19 18:32:21 -07:00
yuneng-jiang
26c5ec3c8b
ci: drop the CircleCI ui_build and ui_unit_tests jobs (#36893)
Both are covered on GitHub Actions. test-litellm-ui-build.yml runs the
dashboard build on every PR, and test-litellm-ui-unit.yml runs the vitest
suite with ui-unit-tests already a required check, so neither CircleCI job
gates anything that GHA does not already gate.

ui_build additionally produced nothing anyone consumed. It persisted
litellm/proxy/_experimental/out to the workspace, and the only job
downstream of it was ui_unit_tests, which never attached the workspace and
reinstalled from source instead. The requires edge was pure sequencing, so
the build output was written and discarded on every client-touching PR.

One real narrowing comes with this, and it is deliberate. ui_unit_tests ran
the full vitest suite on PRs, while the GHA job scopes PR runs to tests
reachable from the diff and keeps the full suite on pushes to staging. That
split was a measured decision in #34175 and it still holds: the suite is
252s and 248s of that is CreateMCPServer.integration.test.tsx alone, so
running everything per PR buys about four minutes to re-run one file.

Note that assert-ci-coverage does not speak to this. It walks
tests/**/test_*.py only, so it is blind to vitest files by construction;
it stays green here because no Python test lost a runner, which is a
narrower claim than the UI side being unaffected.

auth_ui_unit_tests is a different job, a Python suite on a Postgres
sidecar, and is untouched
2026-08-13 23:52:46 -07:00
Yuneng Jiang
64aab7be85
ci: pin Node on the Playwright UI lanes so npm ci meets the engines floor
e2e_ui_testing and e2e_ui_testing_server_root_path run on
cimg/python:3.12-browsers, the one UI executor whose image supplies Node
rather than taking it from a cimg/node tag. That image ships Node 24.14.0,
which bundles npm 11.9.0, so both lanes have failed EBADENGINE against the
engines floor added in #35801. Every Node 24 release through 24.14.0 bundles
an npm below 11.10.0, so engines.node also rises to 24.14.1 (npm 11.11.0),
the first release where the two floors agree

The pinned install goes into /opt/node with /opt/node/bin prepended to PATH
instead of unpacking over /usr/local. On this image /usr/local already holds
npm 11.9.0, and extracting the tarball on top of it merges the two trees into
an npm that reports 11.17.0 and then exits 1 on npm ci printing no error text
at all, which is a worse failure than the one being fixed

The install moves into a reusable install_node command so the version and its
checksum have one home, shared with proxy_pass_through_endpoint_tests, and the
command refuses to run when it disagrees with ui/litellm-dashboard/.nvmrc. A
lane drifting off the version the rest of the toolchain uses is what produced
this failure, so that mismatch now stops the job instead of surfacing later as
an install error

The e2e node_modules cache key moves to v4 because the saved trees were built
by the old npm
2026-08-04 16:31:38 -07:00
yuneng-jiang
487074f602
chore(build): move the Admin UI toolchain to Node 24 (#35801)
* chore(build): move the Admin UI toolchain to Node 24

Node 18 and Node 20 both reached end of life (2025-04-30 and 2026-04-30), and
the release images along with every CI lane were still building on them. Node 24
is the current LTS through 2028-04-30, so this moves the four UI build images,
the CircleCI lanes, and the four GitHub Actions workflows onto it

Node 24 also ships npm 11.17, which is the first line that implements the
min-release-age setting this repo already carries in its .npmrc files. On npm 10
the key is parsed and discarded, so the release-age gate has had no effect
regardless of its value. Tightening the dashboard's engines range and turning on
engine-strict makes an unsupported npm fail loudly rather than skip the gate
quietly, and a new step in the UI build workflow probes an impossible cooldown
so an inert setting cannot pass unnoticed again

Node 24's bundled undici tightened its brand check on RequestInit.signal, which
rejects the AbortSignal jsdom installs and broke the two cases in
src/lib/http/api.test.ts that rebase a request onto a runtime base url. Under
jsdom the Request global comes from Node while AbortSignal comes from jsdom;
tests/jsdomFetchEnv.ts delegates to the jsdom environment and then restores
Node's native AbortController and AbortSignal so both come from one realm.
Upgrading jsdom does not address this, as jsdom still does not own Request

The workflows now read ui/litellm-dashboard/.nvmrc instead of repeating a
literal, so the Node version has a single source of truth, and ui/Dockerfile is
pinned by digest to match the other three build images. The lockfile changes are
npm 11 normalising the engines range and dropping optional peer entries it no
longer records

* fix(build): point every Admin UI build script at .nvmrc

The enterprise Docker path was left on Node 18. docker/build_admin_ui.sh runs
only when enterprise/enterprise_ui/enterprise_colors.json is present, which it
never is in the OSS tree, so neither CI nor a default image build reaches it;
it pinned nvm to v18.17.0 and then built the dashboard, which now requires Node
24, so a customized enterprise image would have failed EBADENGINE

All three UI build scripts now resolve the version from
ui/litellm-dashboard/.nvmrc rather than carrying their own pin, so the Node
version has a single home across Docker, CI, and local builds. build_ui.sh was
on v20 and build_ui_custom_path.sh on v18.17.0

Also drops the dependency-cooldown probe from the UI build workflow. The
engines floor plus engine-strict already fails an unsupported npm loudly at
install time, so the probe was redundant, and treating any nonzero exit from a
live registry call as proof of enforcement made it unsound besides
2026-08-04 12:36:07 -07:00
yuneng-jiang
965968e052
ci(circleci): install a pinned Rust toolchain on the Linux jobs (#35519)
* ci(circleci): install a pinned Rust toolchain on the Linux jobs

The cimg/python images have no Rust toolchain, so every Linux job that
runs `uv sync` or `uv build` builds litellm-rust through maturin with no
cargo on PATH. maturin's puccinialin helper then fetches rustup-init from
the unversioned /rustup/dist/ path with no checksum and provisions a
floating `stable` toolchain, so the compiler a job builds with drifts
with whatever upstream published that day. uv hides build-backend output
on a successful sync, so none of this shows up in the job log.

Add an install_rust command that mirrors the Windows job: download a
pinned rustup 1.28.2, verify its SHA-256 against rust-lang's published
sidecar, install toolchain 1.97.1 with the minimal profile, and export
~/.cargo/bin through BASH_ENV. Run it after install_uv in every job that
builds the workspace; upload-coverage only runs `uv tool run coverage`
and is left alone.

Net download cost is unchanged, since puccinialin was already pulling a
rustup and a toolchain in each of these jobs.

* test(ci): guard that no CircleCI job builds the workspace without a pinned Rust

A green CI run does not notice the gap this closes: uv suppresses
build-backend output on a successful sync, so a job that syncs with no
cargo on PATH silently gets maturin's own unpinned rustup and a floating
toolchain, and the log looks identical either way.

Pin the invariant statically instead. Every job and reusable command is
walked in step order, and reaching a `uv sync` / `uv build` without a
Rust toolchain provisioned first is a failure. install_rust and the
Windows job's inline pinned install both satisfy it, so a new job that
forgets one is named in the assertion message at PR time. Separate cases
cover install_rust's own pins: a versioned /rustup/archive/ URL, a
SHA-256 verified before the installer is executed, and an exact
toolchain version rather than a channel name.

* ci(circleci): provision Rust for base_sdk_install

base_sdk_install landed on staging while this branch was open. It runs
`uv build --wheel` on cimg/python:3.12 behind install_uv alone, so it
built the bridge with maturin's own unpinned rustup. The guardrail added
here caught it on the merge result, which is the case it exists for.
2026-08-03 18:39:35 -07:00
yuneng-jiang
cd87fee9c5
feat(team): custom metadata validation hook for team create and update (#33353)
* feat(team): custom metadata validation hook for team create and update

Operators can point general_settings.custom_team_metadata_validate at an
async Python function that validates team metadata before /team/new,
POST /team/update, and PATCH /team/{team_id} commit their writes. The
hook receives the metadata that will actually be written (the merged
result on PATCH) plus the stored metadata and requester context, and
fails closed: a rejected value returns the function's own message as a
400 while any exception or timeout blocks the write with a configurable
generic message as a 503. Premium-gated like enforced_params.

* fix(team): validate metadata before model alias writes and strip system keys from validator input

Review follow-ups on the team metadata validation hook: run the validator
before the model_aliases table insert so a rejected create leaves no
orphaned model rows, strip system-managed keys from existing_metadata so
the validator sees symmetric input on both fields, and accept class
instances exposing an async __call__ as validators. Adds a three-way
validator implementation matrix (allowlist function, HTTP-service-backed
function, immutability-enforcing class instance) driven through the real
create, update, and patch endpoints, including an HTTP stub service and
outage coverage.

* test(team): run the metadata validation matrix against the DB-backed proxy in CI

Adds the validator matrix to the proxy_store_model_in_db_tests CircleCI
job so every scenario runs full e2e against a Postgres-backed proxy. The
proxy config registers a dispatching validator that routes each request
to one of the three implementations via a metadata key and accepts
anything that does not opt in, keeping the rest of the suite unaffected.
CI starts a stand-in cost center service on the host for the HTTP-backed
implementation, reached from the container via host.docker.internal, and
the outage path targets a closed port to prove the fail-closed 503
without stopping services.

* feat(ui): edit team metadata as key-value pairs in team create and edit forms

The team create and edit forms asked for metadata as a raw JSON blob in a
textarea buried under Additional Settings. Both forms now render a key-value
pair editor directly under the TPM/RPM limit fields, backed by a shared
MetadataKeyValueFields component. Values round-trip losslessly: non-string
values display as JSON and parse back to their typed form on save, and
JSON-ambiguous strings are quoted so their type survives the trip. The edit
form hides UI-managed keys (logging, guardrails, model rate limits, etc.)
that dedicated controls already own and re-add on save.

* fix(ui): explain typed JSON parsing in the team metadata help text

* feat(team): schema-driven metadata fields from team_metadata_schema config

* refactor(team): render schema metadata fields as locked key-value rows, drop allowed_values

* refactor(team): schema fields reduce to key and label, tag-rendered keys, clean rejection toasts

* refactor(ui): prepopulate declared metadata keys as ordinary key-value rows

* fix(team): let non-admin dashboard users read the team metadata schema

* test(proxy): pin timeout wiring, boundary, and error-message contracts for team metadata validation

* fix(proxy): use pooled async httpx client in the e2e team metadata validator example

* refactor(team): satisfy staging lint ratchets inherited by the merge
2026-08-03 18:37:45 -07:00
Yuneng Jiang
7447f9babc
fix(deps): move pydantic-settings into the base dependencies
`import litellm` reaches litellm/integrations/otel/model/config.py via
litellm_core_utils/litellm_logging.py, so pydantic-settings is needed at import
time. It was declared only in the `proxy` extra, which left a plain
`pip install litellm` unimportable on every platform.

Adds tests/base_sdk_tests/check_base_sdk_install.py and a base_sdk_install
CircleCI job that builds the wheel, installs it into a clean venv with no extras,
and smoke-checks the import, a mock completion, a mock embedding, the bundled
pricing metadata and the token counter. The check is stdlib-only on purpose;
installing pytest into that venv would add packaging, pluggy and iniconfig and
could mask the class of undeclared dependency it exists to catch.

Previously the Windows job was the only one installing without extras, so this
class of break was caught by accident rather than by design.
2026-08-01 15:25:26 -07:00
ryan-crabbe-berri
0fcaadf11c
test(e2e): move Admin UI Playwright suite to tests/e2e/ui (#34196)
Relocates ui/litellm-dashboard/e2e_tests to tests/e2e/ui so all end to end
suites live under tests/e2e. The suite stays in TypeScript and becomes a
self-contained npm package with its own package.json, lockfile and tsconfig
instead of leaning on the dashboard's toolchain; the dashboard drops its
@playwright/test dependency, e2e scripts and knip/vitest/tsconfig carve-outs.

CI paths follow the move: both CircleCI jobs (main e2e and the
SERVER_ROOT_PATH migration smoke) and the test_server_root_path workflow now
install and run Playwright from tests/e2e/ui, with the node cache keyed on
both lockfiles. classify_changes.sh treats tests/e2e/ui as client so spec
edits keep skipping backend jobs. The suite's mock LLM fixture is excluded
from the e2e basedpyright zero-error gate in pyrightconfig.json since it
belongs to the TS suite, not the typed Python harness.
2026-07-22 19:43:10 +00:00
Mateo Wang
404ec7fc2e
ci(llm_responses_api_testing): bound live re-record calls and rerun timeout-only failures to stop 15m no-output kills (#32420) 2026-07-07 21:57:46 -07:00
Yassin Kortam
3c5ae3d0cd
refactor(helm): move litellm-helm chart to helm/ and drop deploy folder (#32234)
* refactor(helm): move litellm-helm chart to helm/ and drop deploy folder

* chore(gitignore): drop ignore on vendored litellm-helm subcharts
2026-07-07 15:18:33 +03:00
yuneng-jiang
2e076b110f
Merge pull request #32167 from BerriAI/litellm_/suspicious-jennings-5b6ef7
test: de-flake langfuse callbacks-in-db e2e test
2026-07-04 19:54:53 -07:00
ryan-crabbe-berri
f5438d121a
ci: gate CircleCI jobs on changed paths (#32080)
* ci: gate CircleCI jobs on changed paths

Every CircleCI job used to run on every PR. Now each job starts with a
lightweight `skip_if_unrelated_changes` step that inspects the PR diff and
halts the job as successful when nothing relevant changed. Docs-only PRs
(*.md, *.mdx, docs/) run nothing, UI-only PRs (ui/) run just the frontend
jobs, and any backend change still runs both the backend and frontend jobs.

The decision logic lives in .circleci/scripts/classify_changes.sh (pure,
reads the changed-file list on stdin) so it can be unit tested, while
path_filter.sh handles the git plumbing and fails open (runs the job) on
any uncertainty such as a missing merge base or a non-PR pipeline. Halting
via `circleci-agent step halt` keeps the job green, so required status
checks are never left pending. The Windows smoke job is intentionally left
ungated to avoid cross-platform shell fragility

* fix(ci): keep path filter fail-open when classifier errors

Guard the classify_changes.sh invocation with `|| run_full` so a broken or
non-zero classifier runs the job instead of falling through to a silent
halt, and mark the advisory logging pipe best-effort with `|| true`. Add
path_filter.sh regression tests covering the docs-only halt, backend run,
non-PR fail-open, and classifier-failure fail-open paths
2026-07-04 19:15:08 -07:00
Yuneng Jiang
b905682413
test: de-flake langfuse callbacks-in-db e2e test
The test posted /config/update, slept a fixed 20s, then fired a single chat request with no readiness check or retry. When the single-process proxy was momentarily not accepting connections in that window, the request failed with a bare openai.APIConnectionError and took the whole job down, since the suite runs against one shared container with pytest -x

Gate the chat request behind a /health/liveliness poll, retry it on connection errors only so real HTTP errors and the Langfuse assertion still fail the test, close the previously leaked aiohttp session, and target 127.0.0.1 instead of the 0.0.0.0 bind address. In CI, give the proxy container --restart on-failure so an intermittent crash recovers instead of leaving the port dead for the rest of the run
2026-07-04 19:14:53 -07:00
ryan-crabbe-berri
daf1aab429
ci: run proxy containers without debug logging (#32128)
The CircleCI proxy containers passed --detailed_debug, and litellm's log
level defaults to DEBUG when LITELLM_LOG is unset, so CI produced very
verbose debug output for no reason. Drop --detailed_debug and set
LITELLM_LOG=ERROR on the proxy containers so real failures still surface
without the debug noise
2026-07-04 13:48:11 -07:00
Yuneng Jiang
fff6a5396c
fix(ci): stop ui_unit_tests vitest onTaskUpdate RPC timeout flake
The ui_unit_tests job runs vitest with maxForks=8 on an 8-vCPU xlarge
container, leaving no headroom for the main vitest process that services
worker RPCs. Under full CPU saturation the coordinator misses the
onTaskUpdate ack, vitest raises "Timeout calling onTaskUpdate" as an
unhandled error, and the job exits 1 even though every test passes.

Lower maxForks to 6 so the coordinator, jsdom, and OS keep two cores, and
raise teardownTimeout to 60s for extra slack on heavy runs.
2026-07-03 14:00:43 -07:00
ishaan-berri
62f93a3343
feat: add Rust OCR providers (#31272)
* feat: port OCR providers to Rust gateway

* chore(deps): update langgraph checkpoint lock

* ci: scope ruff format check to changed files

* ci: fix OCR lint and patch coverage

* fix(ocr): block mapped IPv6 fetch targets

* test(ocr): include rust bridge coverage in OCR shard

* ci: rerun responses shard
2026-06-25 15:12:30 -07:00
ishaan-berri
a2d04ccdbb
ci: harden cargo fetches during maturin builds (#31348) 2026-06-25 14:31:05 -07:00
ishaan-berri
d8ef1da49d
feat: package Rust OCR bridge in LiteLLM wheel (#31267)
* feat: package rust ocr bridge in litellm wheel

* Install Rust in Windows CircleCI job

* Address Rust wheel review feedback

* Pin Windows rustup installer hash
2026-06-25 12:32:55 -07:00