Commit graph

752 commits

Author SHA1 Message Date
yujonglee
728d0953af
ci: simplify Rust checks and remove wheel PR comments (#39975)
* ci: limit Rust workflows to Rust directory changes

* ci: run Rust checks when their workflow changes

* ci: report Rust wheels only for successful Rust changes

* ci: keep Rust wheel reports in the workflow summary

* ci: group Rust lint and validation jobs

* ci: keep Rust job names distinct from required lint and test checks

* ci: drop the unused Python setup from the Rust lint job

---------

Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-09-07 09:00:58 -07:00
yujonglee
b9f5cd6036
ci: run unit tests on Python 3.12 (#39989) 2026-09-05 23:56:09 -07:00
mateo-berri
a9e918577b ci(e2e): run the access_control canary on harness changes and name failed tests
A harness-only change (proxy_client.py, conftest.py, pytest.ini, the gateway
config, .github/e2e-stack, or the workflow) selected nothing, so the stack was
never exercised by the change that touched it. select_tests.py keeps the
changed-file rule and adds the access_control suite whenever a harness file
changes. The run step now reports the pytest exit code before the evidence
check, prints pytest's summary line per pass so the rerun count is visible,
and assert_tests_ran.py names each failed or errored test as classname::name
2026-09-05 18:46:51 -07:00
mateo-berri
babc97f562 chore: merge litellm_internal_staging into litellm_/e2e-test-performance-7d53be 2026-09-05 16:15:11 -07:00
mateo-berri
65d8bbb8ac fix(e2e): wait for every gateway before using a new model and keep the network rerun
The changed-tests workflow overrode the suite's `--reruns 1` with `--reruns 0`, so a
transport blip failed a pass that pytest.ini already scopes to network errors and
5xx responses. Pass 2 of run 33692484803 also went red 15s after a model write with
"no healthy deployments": the barrier only polled /v1/models through nginx, which
proves one gateway converged, and the next request rolled the other. The stack now
exports LITELLM_PROXY_REPLICA_URLS, the barrier polls every replica with the full
budget before settling, and up.sh refuses to boot without DD_API_KEY, since the
gateway config enables the datadog callback on every run
2026-09-05 16:10:40 -07:00
cursor[bot]
8544faec91
fix(ci): grant pull_requests write for release wheel reporter (#39922)
* fix(ci): grant pull_requests write for release wheel reporter

The reporter posts a PR comment via github.rest.issues.createComment.
GitHub requires both issues=write and pull_requests=write to comment on
a PR issue, as returned in x-accepted-github-permissions. The workflow
had pull-requests: read, so the POST failed with 403 'Resource not
accessible by integration'. Bumping to pull-requests: write fixes the
create path; the read-only pulls.get call still works. Same-repo scope
is preserved by the existing head_repository.full_name check.

Co-authored-by: Krrish Dholakia <krrish-berri-2@users.noreply.github.com>

* fix(ci): scope release wheel reporter permissions to pull requests

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Krrish Dholakia <krrish-berri-2@users.noreply.github.com>
Co-authored-by: Yujong Lee <yujong@berri.ai>
2026-09-05 13:04:31 -07:00
devin-ai-integration[bot]
b290dd410e
feat(terraform/gcp): dependencies-only mode and bring-your-own-network for GKE (#39695)
* feat(terraform/gcp): add dependencies-only mode

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

* fix(terraform/gcp): review fixes for dependencies-only mode

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

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-05 12:22:52 -07:00
Yuneng Jiang
5051e6d44a
ci(e2e): include execution gate checks in code quality 2026-09-05 12:08:11 -07:00
Yuneng Jiang
0f59b6fb7a
ci(e2e): refine changed-test selection and runner lifecycle 2026-09-05 12:03:42 -07:00
Yuneng Jiang
431579dc16
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/mongodb-vector-store-e4ff63
# Conflicts:
#	.github/workflows/_test-unit-base.yml
#	litellm/litellm_core_utils/sensitive_data_masker.py
#	tests/test_litellm/litellm_core_utils/test_sensitive_data_masker.py
#	uv.lock
2026-09-04 14:00:10 -07:00
Yujong Lee
fae3d224eb Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_python_version_ci
# Conflicts:
#	basedpyright-code-budget.json
#	tests/sdk_function_trace/profiler.py
#	tests/sdk_function_trace/test_profiler.py
2026-09-04 09:01:13 -07:00
yujonglee
ee08c36fc0
refactor(tests): restructure rust python harness around strategy definitions (#39628)
* wip

* refactor(tests): move sdk function tracing into rust python harness

* dead code

* fix: handle harness keyboard interrupts

* refactor(tests): deduplicate rust python harness helpers

* fix(harness): expose validated strategy choices

* wip

* refactor(harness): let strategies own parity reports

* docs(harness): update strategy structure

* refactor(harness): localize strategy report views

* wip

* fix(harness): satisfy mapping runner type checks

* fix(harness): clarify trace parity output

* wip

* fix(harness): clarify unit mapping report

* fix(harness): finalize trace parity contracts

* refactor(harness): structure parity contracts

* feat: derive unit test mapping from traces

* feat(harness): map rstest test families

* feat(ocr): port Azure document intelligence tests

* feat(harness): enforce complete unit mappings

* feat(ocr): add reducto core transforms

* feat(harness): classify host-only unit tests

* fix(ocr): complete Rust provider plumbing

* fix(harness): reuse OCR parity workers
2026-09-03 21:15:01 -07:00
Mateo Wang
3a00e8020a
Merge pull request #39464 from BerriAI/litellm_containers_error_passthrough_pagination
fix(containers): pass upstream error status through and forward list pagination params
2026-09-03 14:36:44 -07:00
Mateo Wang
5e5cc54b28
Merge pull request #39496 from BerriAI/litellm_ui_build_check_image_boundary
ci(ui): run the UI build check through the image's ui-builder stage
2026-09-03 14:35:56 -07:00
mateo-berri
f986fc52f0 ci(rust): lint every gateway feature and keep one checks runbook
Clippy never links, so python-config's pyo3/auto-initialize needs no
libpython and the gateway clippy step can cover every feature at once.
The test step stays on --features server because cargo test does link
and this job installs no Python.

The check list existed in three places that had already drifted apart;
CLAUDE.md is now the only copy and the other two point at it.
2026-09-02 22:16:40 -07:00
mateo-berri
06e60e08d2 ci(ui): run the UI build check through the image's ui-builder stage
The build-ui check compiled the dashboard from a full checkout, so any
import reaching above ui/litellm-dashboard/ resolved there and only broke
inside the images, where the stage copies the dashboard tree alone.
Building the stage itself puts the check on the same file boundary the
shipped images use.
2026-09-02 22:09:55 -07:00
mateo-berri
cf958c0e6f ci(rust): build and test the ai-gateway server feature
litellm-ai-gateway's server feature is off by default and nothing in the workspace turns it on, so the workspace clippy and test steps never compiled src/auth, src/routes, src/state, src/realtime or the gateway binary. 43 tests ran instead of 57.

Adds the two steps CLAUDE.md already documents as the local gate, and fixes the three collapsible_if violations that had accumulated behind the flag.
2026-09-02 21:56:23 -07:00
mateo-berri
cd296814be Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_containers_error_passthrough_pagination 2026-09-02 21:26:40 -07:00
mateo-berri
425c8d37fc fix(containers): page upstream until a non-admin container list fills its limit
Forwarding limit to OpenAI made the ownership filter cut the page down after
the fact, so a key that owned an older container got an empty first page and
its cursor never moved. Non-admin lists now walk upstream pages of 100 until
they have enough owned containers (or five pages), trim to the requested
limit, and report first_id, last_id and has_more off what the caller keeps.

Also assigns tests/test_litellm/proxy/container_endpoints to a CI shard.
2026-09-02 21:26:34 -07:00
mateo-berri
85961201e7 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_python_version_ci 2026-09-02 18:30:13 -07:00
devin-ai-integration[bot]
92edcb90db
fix: keep litellm importable on Python 3.10 and guard 3.11-only typing imports in CI (#39448)
* ci: guard against Python 3.10-incompatible typing imports

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

* ci: address Python 3.10 typing guard review

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

* fix(ci): honor version-guard direction and scan litellm-proxy-extras in py310 typing check

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

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-02 18:27:19 -07:00
mateo-berri
748075be4f Merge origin/litellm_internal_staging into litellm_python_version_ci 2026-09-02 18:21:48 -07:00
Yuneng Jiang
a3ee6b2566
ci(e2e): fail a pass whose every collected test was skipped 2026-09-02 17:50:35 -07:00
Yuneng Jiang
1af9c229fa
ci(e2e): leave the managed-files opt-in file to its own lane instead of failing on an empty collection 2026-09-02 17:29:56 -07:00
yujonglee
62e318de8e
fix(python-bridge): harden sync and async route boundaries (#39332) 2026-09-02 16:26:35 -07:00
yujonglee
198906495f
refactor(python-bridge): split routes and add shared function tracing (#39031)
* refactor(python-bridge): split non-streaming bridge modules

* refactor(python-bridge): bring shared function tracing into route layer

* feat(dev): list Python route functions and call sites

* feat(dev): list Rust route functions and call sites

* docs(dev): record OCR parity gaps across Python and Rust

* feat(dev): list executed SDK calls with runtime tracing

* feat(dev): report Python vs Rust SDK pipeline steps in one CLI

* feat(dev): side-by-side pipeline step report in compare CLI

* fix(dev): drop invalid Final annotations in compare cell loop

* feat(dev): blue python-only and yellow rust-only steps in compare CLI

* feat(dev): vertical layout with section spacing in compare CLI

* fix(dev): validate SDK trace stages across sync and async routes

* refactor(rust): align SDK route call structure with Python

* refactor(python-bridge): share sync and async route call wrappers

* refactor(dev): split compare CLI into fixtures, runtime, and report modules

* fix(ci): run SDK trace tests and satisfy test lint
2026-09-02 16:26:35 -07:00
Yuneng Jiang
c62643f0cf
ci(e2e): drop trailing newlines from fetched secret values before writing the env 2026-09-02 15:39:21 -07:00
Yuneng Jiang
743f94f82a
ci(e2e): fail when nothing collects and ignore lanes with their own checks in the smoke trigger 2026-09-02 14:59:15 -07:00
Yuneng Jiang
b348ed7f09
ci(e2e): only tail stack logs when the stack actually booted 2026-09-02 14:56:52 -07:00
Yuneng Jiang
4a646dd9a0
ci(e2e): run a PR's changed e2e tests three times behind a human-approved environment
Adds a required-check candidate that selects the tests/e2e test files a PR added or
modified, boots a stage-mirror stack on the runner (migrations, backend, two gateway
processes behind nginx, Postgres, Jaeger, TLS cluster Valkey), and runs those files
three times with retries off. The run job sits behind the e2e-changed GitHub
environment, so a reviewer approves each run before the OIDC token that reads the
provider keys from AWS Secrets Manager exists. Supersedes #34981
2026-09-02 14:53:40 -07:00
Yujong Lee
77d6aedf0a fix: address cross-version CI failures 2026-09-02 14:17:19 -07:00
Yujong Lee
8010f267e2 ci: keep Python compatibility coverage in unit workflow 2026-09-02 13:42:22 -07:00
Yujong Lee
0313dcea61 ci: test Python 3.10 through 3.14 compatibility 2026-09-02 13:38:28 -07:00
Yuneng Jiang
3d0223b661 ci: install the mongodb extra for the unit test shard that runs the provider
tests/test_litellm/llms/mongodb imports pymongo's exception classes to check the
error translation against the real hierarchy, and the shard that runs it
(tests/test_litellm/llms, per test-unit.yml) synced --extra google, proxy,
semantic-router and saml but not mongodb, so 24 of 109 tests would have errored
with ModuleNotFoundError on the first CI run. CircleCI hid this because it syncs
--all-groups --all-extras.

  uv export --frozen ... --extra saml                  -> no pymongo
  uv export --frozen ... --extra saml --extra mongodb  -> pymongo==4.17.0

Also close the two gaps a mutation run found in the suite: nothing asserted that
a short request timeout shortens server selection as well as connect, and the
existing code 13 case carried "not authorized", which the message markers match
too, so it could not tell whether the code was still being checked. 28 of 28
mutants now die.
2026-09-02 12:51:19 -07:00
Yujong Lee
90eadac409
test(build): keep wheel checks outside package 2026-09-02 12:16:25 -07:00
Yujong Lee
ce0c85ea69
refactor(rust): colocate native wheel contract checks 2026-09-02 12:16:25 -07:00
Yujong Lee
38150dfc2c
fix(ci): pin workflow toolchain dependencies 2026-09-02 12:16:25 -07:00
Yujong Lee
ef9a207ed5
fix(ci): harden release wheel reporting 2026-09-02 12:16:25 -07:00
Yujong Lee
9dc9cd325c
fix(ci): isolate release wheel reporting permissions 2026-09-02 12:16:25 -07:00
Yujong Lee
2f362cfec2
fix(ci): preserve release wheel contract parity 2026-09-02 12:16:25 -07:00
Yujong Lee
25987cb961
test(build): validate release wheel contracts 2026-09-02 12:16:25 -07:00
devin-ai-integration[bot]
93219a9257
fix(docker): install bedrock-realtime extra in monolith proxy images (#39223)
The Dockerfile, docker/Dockerfile.non_root and docker/Dockerfile.database uv sync stages never passed --extra bedrock-realtime, so aws-sdk-bedrock-runtime was absent from the image venv and Bedrock Nova Sonic /v1/realtime sessions failed with 'Missing aws_sdk_bedrock_runtime'. gateway/Dockerfile already had the extra (PR #34426).

Adds a static check over every uv sync in the proxy Dockerfiles and an image-level import probe that the image-scan workflow runs against the built root, non-root and gateway images.

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-01 17:51:55 -07:00
Kolade Fajimi
d1320404fe
fix(redis): coerce env var string types and fix param discovery through decorator wrappers (#30644)
* fix(redis): coerce env var string types and fix param discovery through decorator wrappers

inspect.getfullargspec doesn't work on redis.Redis/redis.RedisCluster because
their __init__ is wrapped by @deprecated_args, which replaces the explicit
signature with *args/**kwargs internally. getfullargspec returns an empty arg
list, so _get_redis_kwargs and _get_redis_cluster_kwargs silently dropped every
real constructor parameter not in their hand-picked include_args set --
cluster_error_retry_attempts and connection_error_retry_attempts among them, so
an operator's configured retry bound never reached the Redis Cluster client and
it fell back to redis-py's own default instead.

Rebased onto litellm_internal_staging, which had independently added
_init_arg_names (MRO-walking, inspect.unwrap-based) for the same class of bug
in _get_redis_url_kwargs. Reused that pattern (as _unwrapped_init_args, without
the MRO walk: redis.Redis/RedisCluster declare every real parameter directly on
their own __init__, and MRO-walking breaks the tests here that mock the class
with autospec=True, since inspect.getmro needs a real __mro__) rather than
introducing a second, differently-shaped fix for the same problem.
_get_redis_cluster_kwargs now also honors its own client argument instead of
ignoring it, so the async cluster client's own extra constructor kwargs
(cluster_error_retry_attempts, connection_error_retry_attempts,
decode_responses, ...) are no longer filtered out by introspecting the sync
class regardless of which client is actually built.

Also fixes environment variables and Helm --set values always arriving as
strings: redis-py 8.x changed health_check_interval's arithmetic to require a
real number, so a stringified value raised TypeError on every Redis operation
instead of connecting. _coerce_redis_kwargs_types coerces to each parameter's
declared type at the end of _get_redis_client_logic, with an explicit type
table for max_connections/socket_timeout/socket_connect_timeout since redis-py
8.x changed the timeout defaults from None to int 5, which would otherwise
make a fractional value fail int() and get dropped.

Co-authored-by: mangabits <1457532+mangabits@users.noreply.github.com>

* ci: verify redis-py client version compatibility across a version matrix

* test(redis): assert an async-only cluster kwarg every matrix version declares

connection_error_retry_attempts is on the async cluster constructor in redis-py
5.x only; 6.0 removed it in favor of retry. The 6.4.0, 7.4.1 and 8.0.1 legs were
failing on that missing parameter name rather than on the behavior under test,
while the allow-list itself was doing the right thing on all four versions.

decode_responses is async-cluster-only on every version the matrix covers, so it
stands in for the same property: the sync cluster class takes it through **kwargs
and never names it in its signature. Reverting _get_redis_cluster_kwargs to ignore
its client argument still fails both tests on 5.3.1 and 8.0.1.

test_async_cluster_passes_async_only_kwargs now builds the real async cluster
client and reads connection_kwargs off it, so it no longer needs a patched class
factory; the constructor does no I/O. The retry-attempts test keeps its patch,
since redis-py >= 6 stores no cluster_error_retry_attempts attribute on the built
client and the constructor call is the only place the forwarded value shows up.

The _get_redis_cluster_kwargs docstring cited the same two parameters as its
examples of async-only kwargs, which is what made the test look reasonable;
cluster_error_retry_attempts is on both classes and connection_error_retry_attempts
is gone from 6.0 on, so it now names decode_responses instead.

* test(redis): drop internal patches from the kwarg coercion tests

The test-quality gate flagged the new patch() calls on litellm internals these
tests added. Three of them faked litellm._redis.inspect.signature with a MagicMock
to hand _coerce_redis_kwargs_types a synthetic parameter; that function already
takes a client argument, so they pass stub functions instead, matching the
_redis_signature_8x idiom the file uses elsewhere. The fourth patched
_redis_kwargs_from_environment to {} to prove _get_redis_client_logic raises
without a host or url, which clearing the real env keys through
_get_redis_env_kwarg_mapping does without pinning the test to that call.

Both files now sit one TQ008 below the merge base rather than six above it.

* fix(redis): keep the sync client construction inside the basedpyright budget

_get_redis_client_logic now returns dict[str, object] rather than an untyped
dict, which is the honest type for operator-supplied config, but it turns the
33 reportUnknownArgumentType errors at redis.Redis(**redis_kwargs) into 33
reportArgumentType errors plus one reportCallIssue, both over their budget.
No static type fits: redis-py's constructor declares 40-odd differently typed
parameters and the values arrive from config and env, so the allow-list and
coercion above are derived from that same signature and redis-py validates each
value itself at runtime.

The two suppressions name their exact rule and carry that reason. The file ends
up 42 basedpyright errors below the merge base, with reportArgumentType and
reportCallIssue back at the base counts of 3 and 0.

* fix(redis): coerce cluster-only and None-default bool kwargs

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

---------

Co-authored-by: mangabits <1457532+mangabits@users.noreply.github.com>
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-31 20:51:31 -07:00
Mateo Wang
d60e77ae8c
Merge pull request #38819 from BerriAI/litellm_fix_gemini_tts_response_format
fix(speech): stop forwarding response_format as a chat param for Gemini TTS
2026-08-31 15:56:18 -07:00
mateo-berri
b34064fe30 ci: add tests/test_litellm/endpoints to the misc unit-test shard 2026-08-29 15:21:39 -07:00
mateo-berri
539bc8ef92 fix(ci): close only identical-title duplicates, dry-run the sweep, reopen on reply
The merged detector's 0.6 flag threshold had become the close bar, and 6 of the 7 real flagged pairs at 85% or more were not duplicates. The sweep now closes only when an older open issue has the identical normalized title, measures the grace period from the latest bot notice, and leaves the issue open when anyone replies or gives the notice a thumbs down.

A reporter cannot reopen an issue the bot closed, so a reporter comment after the automatic close reopens it, drops the duplicate label, and asks for a human look. Manual dispatch defaults to a dry run and takes a grace_period_days input, the runner supplies the repository, the dead python closer is gone, and the decision core has bun tests on a PR-triggered job.
2026-08-29 17:30:01 -04:00
mubashir1osmani
f4542d9605 fix(ci): pin the Bun runtime instead of tracking latest
The setup step ran `bun-version: latest`, carried over from the upstream layout,
and the step after it holds an issues: write token. A compromised Bun release
would have executed privileged in that job and could rewrite or close issues.

Pinned to 1.4.0, the release the passing runs already resolved to. setup-bun
takes no checksum input, so pinning the action by sha and the runtime by exact
version is as far as this can be hardened without hand-rolling the download.
2026-08-29 17:29:56 -04:00
mubashir1osmani
3ea11b64e6 refactor(ci): run the duplicate sweep as a Bun TypeScript script
Moves the sweep out of inline workflow JavaScript and into scripts/, following
the layout anthropics/claude-code uses for the same job: a checked-out repo, a
sha-pinned setup-bun step, and `bun run scripts/auto-close-duplicates.ts`.

The script mirrors that repo's file shape, keeping the same request helper,
interfaces, per-issue debug logging, and top-level catch, so the two read the
same way side by side.

Two things stay deliberately different. Candidates come from the notice marker's
digits-only field rather than a regex over the comment prose, because titles are
attacker-controlled and are interpolated into that same comment. The label is
also added on its own endpoint instead of alongside the state change, since
sending labels with a PATCH replaces every label already on the issue.
2026-08-29 17:29:56 -04:00
mubashir1osmani
af340c0240 fix(ci): read duplicate candidates from the marker, not the notice prose
The notice interpolates each candidate's title, and the sweep scanned the whole
comment for issue references and took the lowest. Titles are attacker-controlled,
so filing a candidate titled "... see #1" redirected the closure: any later report
matching that candidate would be closed as a duplicate of #1 instead.

The detector now emits the candidate numbers as a digits-only field inside the
marker, built from the API's number field, and the sweep reads only that. Prose is
never parsed, so nothing a reporter can type reaches the target selection.
2026-08-29 17:29:55 -04:00
mubashir1osmani
e2ffb6b01c feat(ci): close duplicate issues after a 3-day grace period
Duplicate detection already labelled and commented on new issues, and then
closed them outright at 0.85 title similarity. That gave the reporter no chance
to push back, and a title-similarity match is not strong enough evidence to
close on its own.

Detection now only flags. A new daily sweep closes a flagged issue three days
later, and only if nobody engaged with the flag. Replying to it, thumbs-downing
it, or applying an opt-out label all keep the issue open. The notice says all of
that up front, so the reporter knows what happens and how to stop it.

The two workflows hand off through an HTML marker in the comment body rather
than its prose, so rewording the notice cannot silently break the sweep. The
sweep lists by label instead of walking the whole backlog: 1663 open issues
against 23 carrying the label meant a comments request each, which would burn
the Actions token's hourly budget for a handful of matches.

Candidates are taken as the lowest issue number, not the first one listed. The
detector orders by score rather than age, so the first candidate can be newer
than the issue being closed, and folding an original report into a later one is
backwards. An issue whose only candidates are newer is skipped.

Closures use state_reason=duplicate rather than not_planned, which reads as
"see the other issue" instead of "we are not doing this".

Also drops {{html_url}} from the notice. The detection action only exposes
number, title and accuracy, so that placeholder had been rendering empty and
every "similar issue" link in the comment pointed nowhere.
2026-08-29 17:29:55 -04:00