Commit graph

751 commits

Author SHA1 Message Date
Yuneng Jiang
0dfc1eec78
ci: drop the duplicate proxy_unit_tests letter-shard workflow
tests/proxy_unit_tests/ runs twice on every pull request. The nine
alphabetical shards in test-unit-proxy-legacy.yml cover the same
directory as the twelve semantic shards in test-unit-proxy-db.yml,
and all nine are required checks, so each PR pays for the directory
twice before it can merge.

The semantic shards are a strict superset. Expanding both matrices
against the working tree, the legacy globs collect 58 files while the
semantic shards name all 59: test_model_response_typing is a directory
and matches none of the test_[a-z]*.py patterns, so the legacy lane has
silently skipped it. The semantic workflow also carries its own
assert-shard-coverage guard, which fails if a file under that directory
is not assigned to a shard, so a new file cannot drop out of CI once the
alphabetical fallback is gone.

Verified with .github/scripts/assert_ci_coverage.py: 2380 test files
have a runner both before and after the deletion. Removing
test-unit-proxy-db.yml as well takes the same guard red with 58
orphaned files, which confirms the guard is live and that the semantic
shards, not the legacy ones, are what hold the coverage.

The nine bare contexts this workflow published (auth-and-jwt,
key-generation, proxy-config, proxy-server, proxy-server-extras,
proxy-token-counter, proxy-response-and-misc, proxy-user-auth-and-spend,
proxy-utils) still need pruning from the guard-internal-staging ruleset,
which needs admin rights and is not part of this change
2026-08-13 19:04:21 -07:00
Yassin Kortam
5bc523a09f
feat(terraform/aws): make VPC, Aurora, and Redis optional (#36676)
Adds vpc_id/public_subnet_ids/private_subnet_ids to deploy into existing
networking, plus create_database/database_url and create_redis/redis_url to
use existing data stores or none at all. Defaults keep today's module-owned
behavior.

A container reads a secret by ARN, which gives Terraform no edge to the
_version that writes the value. The managed-Aurora path hid that behind the
cluster create; the bring-your-own path has nothing slow in between, so the
migration, the bootstrap, and both services now depend on the versions
explicitly.

Supplied private subnets must cover two AZs whenever Aurora or ElastiCache is
module-created, since both subnet groups require it, and a check block warns
when a Redis-less stack can run more than one gateway process, because per-key
rate limits are counted per process without Redis.
2026-08-12 15:27:13 -07:00
Yuneng Jiang
584a8a0554
ci: drop deleted files from the proxy-server-core shard
The proxy-server-core matrix entry named test_proxy_server_caching.py and
test_proxy_server_langfuse.py by path. This PR deletes both, so pytest exited 5
with "no tests collected" and the whole shard failed without running the four
files that do exist.

assert-shard-coverage did not catch it because it only checks one direction:
every file under tests/proxy_unit_tests/ must appear in some shard. It never
checks that every path a shard names still exists, so a stale entry passes.

After this change no shard names a missing path and no file is left without a
shard. The shard collects 85 tests.
2026-08-12 11:21:39 -07:00
mateo-berri
e9156afdd0 ci: retry transient network fetch failures in lint workflow 2026-08-11 11:34:26 -07:00
mateo-berri
812abcc7f5 fix(triage): track and credit feature dead-end evidence in the verdict 2026-08-11 10:57:46 -07:00
mateo-berri
e59da40750 fix(triage): ask for dead-end evidence in feature request recovery comments 2026-08-11 10:43:36 -07:00
mateo
d4468ba63a chore: say real $ instead of $$$ in the feature request attempt rules
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 10:40:57 -07:00
mateo
cf1766be1b chore: drop the feature request attempt attestation and tighten its wording
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 10:40:57 -07:00
mateo
1d9500066e fix: move feature request guidance out of prefilled values so required means filled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 10:40:57 -07:00
mateo
38a1bec5c2 fix(triage): require evidence of the dead-end in feature requests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 10:40:57 -07:00
mateo
6f7050c05f docs: make an unfilled feature request attempt visibly empty and attested
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 10:40:57 -07:00
mateo
471bb834ba docs: rename the feature request flow lists to before/after this feature
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 10:40:57 -07:00
mateo
c8f3854833 docs: require a user flow and a stuck-at proof in feature requests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 10:40:57 -07:00
mateo
ad1ff19195 chore: say real $ instead of $$$ in the bug report proof rules
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 16:31:19 +00:00
mateo
10d4213453 chore: drop the bug report proof attestation and tighten its wording
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 16:25:52 +00:00
mateo
5643a59aa4 fix: move bug report guidance out of prefilled values so required means filled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 02:58:21 +00:00
mateo
7a17735473 fix(triage): treat an unfilled bug report scaffold as missing proof
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 02:56:13 +00:00
mateo
f8a5d6a6a1 docs: make an unfilled bug report proof visibly empty and attested
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 02:49:57 +00:00
mateo
550682d5f8 docs: redact only sensitive env vars in bug report proof
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 02:36:11 +00:00
mateo
7653532e89 docs: spell out secret redaction in the bug report proof
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 02:35:44 +00:00
mateo
7e8faf9267 docs: ask bug reports for the config and version behind the proof
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 02:33:10 +00:00
mateo
f096e4c10b docs: require a user flow and live-proxy proof in bug reports
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-11 02:27:07 +00:00
Yuneng Jiang
729ec315e2
refactor(ui): make illegal DataTable prop combinations unrepresentable
DataTable accepted any mix of its 40-odd props and rejected the incoherent
combinations at runtime, from a validator that threw during the first render.
A caller only found out it had wired server sorting without a `sorting` prop
when the page blew up in front of them.

Split the public prop type into mode-keyed unions instead, so the compiler
rejects those combinations at the call site. `validateDataTableConfig` and
`DataTableConfigError` go away; the component body reads an unchanged flat
`DataTableResolvedProps`, which every union member is assignable to, so there
is no narrowing inside it.

All 44 existing call sites typecheck against the new union unchanged, which
`next build` covers. That build only typechecks the app module graph, so the
prop type itself needed a gate of its own: `npm run test:types` runs vitest's
typecheck mode over `*.test-d.tsx`, and the unit workflow now runs it. The
four guards deleted from `DataTable.test.tsx` come back there as compile-time
assertions, and loosening the union back to the flat shape fails all five.
2026-08-10 15:19:39 -07:00
Mateo Wang
9bca9dfbb1
Merge pull request #36417 from BerriAI/litellm_cache_prisma_ci_binaries
ci: cache Prisma CLI and engine binaries, split test timeout from setup
2026-08-10 12:26:26 -07:00
mateo-berri
c22a749f70
fix(ci): drop unsupported arithmetic from the job timeout expression
GitHub expressions have no arithmetic operators, so
`${{ inputs.timeout-minutes + inputs.setup-timeout-minutes }}` was not a value
but a startup failure. The proxy-db workflow died before creating any job on
both prior commits, which posts no check run at all: the entire suite stopped
running while the PR's checks stayed green.

Pass the job backstop in as `job-timeout-minutes` instead of computing it, and
size it as the test budget plus the 30 minutes of setup ceilings plus 5 minutes
of runner overhead the job clock charges but no step owns.

check_workflow_startup_safety.py makes this class of mistake visible before
merge, since CI cannot report it: it rejects arithmetic inside an expression
and checks every caller of the reusable workflow keeps a job budget large
enough that the deadline cannot preempt pytest inside its own budget.
2026-08-10 17:13:16 +00:00
devin-ai-integration[bot]
b485ddc6bb
docs: replace the Changes PR template section with Caveats (#36423)
Co-authored-by: mateo <mateo@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-10 17:05:47 +00:00
mateo-berri
efd98bb1b4
fix(ci): bound setup steps so pytest always gets its full budget
The summed job deadline alone did not protect the test budget. Setup that
overran its allowance still ate into pytest's window, which is the same
failure this change set out to remove, just with more headroom.

Every step before pytest now carries its own ceiling, and their sum is the
`setup-timeout-minutes` default. Setup can no longer overrun into the test
budget without failing its own step first, and a slow setup step now reports
as a red step naming itself rather than a cancelled shard whose tests passed.

Model the workflow YAML the guard reads with Pydantic instead of bare dicts,
so the shapes it depends on are validated once at the boundary. A workflow
that does not parse is now reported as a finding rather than a traceback.
2026-08-10 16:22:07 +00:00
mateo-berri
6a83a84f31
ci: cache Prisma CLI and engine binaries, split test timeout from setup
`prisma generate` runs `npm install prisma@<version>` whenever the
prisma-client-py binary cache directory has no CLI entrypoint, pulling ~85 MB
of query and schema engines over the network. Every workflow pointed
PRISMA_BINARY_CACHE_DIR at `${{ runner.temp }}/prisma-cache`, which GitHub
wipes and recreates per job, so that cache was empty on every job of every
run and the download was never avoidable.

The download is normally a few seconds and occasionally minutes. On one
proxy-db run it took 5m18s on a single shard against 3.8s on its eleven
siblings, which pushed the job past its 15 minute timeout and cancelled a
shard whose tests were at 99% and all passing.

Leave PRISMA_BINARY_CACHE_DIR unset so the binaries land in the
prisma-client-py default, which is already keyed by prisma and engine
version, and restore both that path and the @prisma/engines staging cache
through a shared composite action.

Job timeouts also counted setup against the test budget. `timeout-minutes`
now bounds the pytest step, with a separate allowance for checkout,
dependency install, and client generation, so slow setup shows up as a slow
job instead of a cancelled test run.

check_prisma_binary_cache.py guards all three invariants: no workflow
reintroduces the override, every job that generates the client restores the
cache, and the version the action greps out of uv.lock still resolves.
2026-08-10 16:10:54 +00:00
Mateo Wang
1d0cba7f7c
Merge pull request #35551 from BerriAI/devin_ai_require_managed_files_read_paths_35530 2026-08-08 10:22:18 -07:00
mateo-berri
b01eacd67c ci: run the new fine-tuning and vector store file test dirs 2026-08-08 01:36:30 -07:00
mateo
ede84eee15 ci: give the remaining pull_request workflows a concurrency group
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-08 03:25:38 +00:00
mateo
4306c4ad70 ci: detect relevant changes from git instead of the paginated files API
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-07 21:07:27 +00:00
mateo
ecd5ad49f7 ci: always run the UI API types sync check so it can be required
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-07 19:24:46 +00:00
Mateo Wang
bf2def7eb5
docs(pr-template): add a User Flow section with authoring instructions (#36162)
Adds a User Flow section right below the TLDR so every PR describes the same end user doing the same task before and after the change, plus comment instructions and a worked example so contributors can write it without any local tooling.
2026-08-07 11:32:30 -07:00
cursor[bot]
d30e933e33
ci: wire credential_endpoints tests into the proxy endpoints job (#36187)
PR #36166 added tests/test_litellm/proxy/credential_endpoints/test_endpoints.py
but no CI job invokes it, so the CI Coverage guard failed on
litellm_internal_staging. Add the directory to the proxy-endpoints
job's test-path list so pytest actually runs the new tests and the
coverage assertion is satisfied.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Krrish Dholakia <krrish-berri-2@users.noreply.github.com>
2026-08-07 11:12:08 -07:00
Yassin Kortam
6ba744b340
test(docker): gate the componentized gateway and backend images on an arbitrary-uid offline boot (#36136) 2026-08-07 09:57:37 -07:00
mateo-berri
e824510765 fix(lint): generate the prisma client into the gate-owned venv
prisma resolves its prisma-client-py generator through a plain /bin/sh PATH
lookup, never through the interpreter that ran prisma generate, so the gate's
generate step landed the client in whatever venv the caller had on PATH: the
owned env never received one, every gate run regenerated, the caller's venv
was mutated instead, and any invocation without a venv on PATH (the rewritten
publisher workflow) failed outright

The generate now runs with the target interpreter's bin directory pinned to
the front of the child PATH. The prisma schema joins the environment
fingerprint so clientless counts recorded before this commit can never be
compared against clientful ones, a cold provision announces itself on stderr
instead of sitting silent for two minutes, and the CI gate step reuses the
job's prisma binary cache
2026-08-06 01:54:26 -07:00
mateo-berri
fa47c47020 fix(lint): measure the basedpyright budget gate in a gate-owned venv
The gate previously measured whatever environment the caller happened to
have. Locally that is the fat bootstrap venv (--extra proxy pulls in
fastapi-sso, whose type info flips a reportUnnecessaryIsInstance
diagnostic in ui_sso.py), while CI's publisher venv only has the
proxy-dev and e2e-dev groups, so identical trees measured 866 locally vs
865 in CI and every local gate run breached by a phantom +1

scripts/type_check_gate.py now provisions .venv-typecheck itself: a
frozen uv sync of the canonical proxy-dev and e2e-dev groups, the
interpreter pinned to pyrightconfig.json's pythonVersion, plus the
generated Prisma client. Every measurement pass is pinned to that env
with --pythonpath, because basedpyright auto-detects a .venv in the
project root and that auto-detection beats both PATH order and
VIRTUAL_ENV, so the CLI flag is the only pin that actually works. The
dependency-group set is folded into the environment fingerprint, so
artifacts or caches recorded under a different group set never match
and the gate falls back to computing base counts locally instead of
comparing mismatched environments

The publisher workflow drops its own install and prisma steps and lets
the script build the measurement env, and the node heap for the
full-tree pass drops from 12GB to 8GB (peak RSS measured at 5.4GB)
2026-08-05 21:33:24 -07:00
Mateo Wang
eabcafc1df
perf(pre-commit): fetch basedpyright base counts from CI artifacts (#35970) 2026-08-05 17:21:16 -07:00
Yassin Kortam
7a5d6a0548
ci: fail the build when a test file or Dockerfile is invoked by no job (#35991)
The absence of a CI signal is indistinguishable from a passing one, and
that shape has now produced several independent holes: whole test
directories no job runs, and shipped images no job builds. Nothing was
watching for either, so each was found by accident.

assert_ci_coverage.py enumerates every test_*.py under tests/ and every
Dockerfile in the repo, then credits only what the workflows and the
CircleCI config actually invoke. Paths filters and lint steps that merely
name a directory do not count as coverage, because crediting a mention is
the same mistake one level up. Anything neither invoked nor listed in
.github/ci-coverage-allowlist.yml with a written reason fails the job.

The guard found 275 uncovered test files and 6 unbuilt Dockerfiles. Fixed
here: the root Dockerfile, the primary published image, now gets an
image-scan leg that builds it and runs the offline migration check against
it, and 8 tests/test_litellm subdirectories join the shards that already
enumerate their siblings. Everything else is allowlisted per file, so a
new file cannot inherit an exemption, and the remaining decisions are
tracked rather than invisible.

The job reports but is not in branch protection, so it does not block
merges; promoting it is a separate change once the allowlist has survived
contact with a few pull requests.
2026-08-05 14:52:48 -07:00
Yassin Kortam
50c6a1f344
fix(ci): run every helm test suite, not just the first one per file (#35993)
* fix(ci): run every helm test suite, not just the first one per file

helm-unittest gained support for multiple suites in one test file in
v0.5.0; CI and the Makefile both pinned v0.4.4, the last release that
decodes a single YAML document per file. Any suite after a `---`
separator was parsed away and its assertions never ran, while the
summary still reported a clean pass.

Upgrading the pin to v0.8.2, the newest release that installs under the
pinned helm 3.11.1, brings the litellm-helm chart from 11 suites / 90
tests to 14 suites / 93 tests with no change to any test file. All the
recovered tests pass.

The run step now compares the number of declared `suite:` documents
against the number of suites the runner reports, so the same class of
silent skip fails the job loudly instead of passing quietly. The
Makefile target upgrades a stale local plugin instead of swallowing the
"already installed" error and leaving the developer on an old version.

* ci: install helm-unittest from a pinned, checksum-verified artifact

`helm plugin install <git url>` clones the plugin repo and executes its
install hook, which downloads the release tarball itself. The old
integrity step then checked the cloned repo's HEAD, which happens after
the hook has already run and never covers the binary that was actually
downloaded.

The plugin now comes from a full pinned release URL, verified against
the SHA-256 the project publishes in its helm-unittest-checksum.sha
sidecar, before anything is unpacked or run. Nothing remote executes
ahead of the check, and a re-published release asset fails the job
instead of installing silently.
2026-08-05 14:52:18 -07:00
Mateo Wang
6d604d27a6
fix(ci): make every remaining CI checkout shallow (#35997)
* fix(ci): make every remaining CI checkout shallow

PR #35982 only covered the lint and budget-ratchet jobs, so secret-scan
kept spending minutes fetching every branch inside its 5 minute timeout
and PRs kept getting cancelled. The UI lint and UI unit jobs carried the
same fetch-depth 0 checkout

secret-scan now checks out at depth 1, runs the hardcoded-secret pytest
without building the project environment, and lets the ggshield step
deepen history itself when a key is configured. UI lint resolves the
merge base through the API instead of local history. UI unit tests
compute the changed files the same way and feed them to vitest related,
because vitest --changed does a three-dot diff that silently selects
zero tests on a shallow clone

The daily branch creation workflows also did full checkouts, then
failed every run since persist-credentials: false left git push with no
credentials. They now create the ref through the GitHub API without a
checkout at all

* fix(ci): feed deleted UI files into vitest related selection

vitest --changed fed git's full change list to the related filter,
deletions included, so a deletion-only dashboard PR still selected the
tests importing the removed files. Keep that behavior by dropping the
diff filter and existence guard; vitest resolves nonexistent paths fine
and --passWithNoTests covers the nothing-related case
2026-08-05 14:32:17 -07:00
Yassin Kortam
de00655363
fix(migrations): keep the toolchain heal from raising on an unreadable nodeenv cache (#35986)
heal_incomplete_nodeenv_cache() stats a $HOME-derived path with a bare
Path.is_dir(). pathlib only swallows ENOENT-shaped errnos, so a cache
directory the process cannot search raises PermissionError instead of
answering False. Images bake that cache under the build user's home, whose
mode is 0700, so a container started under any other uid dies there before
the Prisma CLI is ever invoked, and the migration never runs.

Tolerate OSError while inspecting the cache, matching the guard
nodeenv_cache_dir() already carries, so an unreachable cache means there is
nothing to heal rather than a crash. This restores the never-raises
contract ensure_prisma_toolchain() documents.
2026-08-05 13:38:53 -07:00
Mateo Wang
7c9c9ffbc7
fix(ci): fetch only head and merge-base in lint jobs instead of every branch (#35982) 2026-08-05 13:00:00 -07:00
Mateo Wang
7900e1fc79
Merge pull request #35251 from BerriAI/litellm_fix_codecov_oidc_flake 2026-08-05 09:26:43 -07:00
mateo-berri
22a1c30603 fix(lint): move the basedpyright heap flag into the type check gate
The 12 GB NODE_OPTIONS setting lived only in the Makefile export and the
CI env line, so any hand-run gate pipeline forgot it and node OOMed at
the ~4 GB default after 80 seconds, with || true feeding the gate empty
output. The gate now spawns basedpyright itself for both the head and
base passes, appends the heap flag last so it wins node's last-flag-wins
resolution while preserving other caller flags, and fails loudly on
crash exit codes instead of reading them as zero errors.
2026-08-04 17:59:56 -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
Yassin Kortam
72c8888563
fix(docker): bake prisma offline in the componentized migrations image (#35485)
The migrations image ran `prisma migrate deploy` against a bake anchored in
$HOME with no node in the runtime stage, so prisma-client-py fell through to
nodeenv and tried to download a Node runtime on first start. In an
egress-restricted cluster that fails outright, and under an arbitrary uid the
uid-specific cache path is unreadable, so the job never applies a migration.

Move the bake to /opt/prisma with world-readable modes, install node in the
runtime stage, and pin PRISMA_BINARY_CACHE_DIR / PRISMA_CLI_PATH /
PRISMA_OFFLINE_MODE so the migration entrypoint runs the cached CLI directly.
This is the same treatment the root, non_root and database images already
carry.

Resolves LIT-4727
2026-08-01 14:12:31 -07:00
Yuneng Jiang
37ca659f84
ci: run unit tests on pushes to main and litellm_internal_staging
The unit workflows trigger on pull_request only, so a commit that
actually lands on a gated branch ends up with no unit-test check runs at
all. The commit status API reports success for those commits, which a
release gate reads as "nothing failed" rather than "never tested". PR
checks also only ever ran against the merge preview, not the commit that
landed, so two branches that are each green can still land broken
together

Add a push trigger on the two gated branches to the twelve unit
workflows and to the code-quality workflow, so every landed commit gets
check runs addressable by its SHA

test-linting.yml is deliberately left on pull_request only; six of its
steps gate on a diff against github.event.pull_request.base.sha, which
is empty outside a pull request, and a "what did this branch add" check
has no meaning on a merge commit

Also key the concurrency group on github.sha and restrict
cancel-in-progress to pull_request. The previous group was stable across
pushes to a branch, so consecutive merges would cancel the in-flight run
for the earlier commit and leave that SHA without a result, which is the
same blind spot this change is meant to close
2026-08-01 11:58:49 -07:00
tin-berri
b408b1d6dc
fix(guardrails/headroom): stop compressing the turn the model must act on (#35294)
The Headroom guardrail sent every message to /v1/compress, including the
system prompt and the user's current instruction. On an agentic /v1/messages
request the live turn is the largest compressible blob, so it came back as a
hash marker; the model then called headroom_retrieve and got its own
instruction returned in a tool_result block, which reads as data it fetched
rather than a request to act on, so it described the content instead of doing
the work.

litellm already owns the policy for what a compressor may never rewrite:
get_protected_indices covers the system rows, the last user row and the last
assistant row, and compress() expands it over whole tool exchanges. Headroom
now consults it (promoted from a private name and given tests) and expands it
the same way, so the trailing tool result cannot come back as a marker
standing in for the result of the call the model just made. Protected rows are
withheld from the payload rather than pinned afterwards, so their tokens are
not reported as savings that are never applied; the write-back discards a
compressed system prompt outright, so that saving never existed. The cost is
that a query-aware service no longer sees the newest user message.

A response whose row count differs from what was sent can no longer be
interleaved with the withheld rows, so it goes through the configured fail
policy instead of being adopted. Fail-open now returns the caller's own inputs
object: translation handlers detect a rewrite by identity, so a rebuilt copy
sent an unchanged request through the Anthropic write-back for nothing.

That write-back rebuilt the request with one anthropic_messages_pt call, which
merges every run of consecutive user/tool rows, so a tool_result turn and the
user turn after it arrived fused. Converting a row at a time would separate
them but breaks tool pairing: with modify_params on, an assistant row whose
results are converted separately reads as an orphaned tool call and the
sanitizer answers it with a synthetic "tool execution skipped" result while
dropping the real one. Conversion is now grouped by tool_call_id ownership,
which satisfies both, and the same grouping decides which rows headroom
protects, so the two agree by construction.

The CCR follow-up also dropped any text the model wrote alongside its tool
call, and echoed tool calls it had no results for. Both are fixed by reusing
compresr's extraction helper, now shared instead of duplicated.

Resolves LIT-5018
2026-07-30 18:53:31 -07:00