Commit graph

821 commits

Author SHA1 Message Date
ryan-crabbe-berri
539ac2bc01 ci(issue-classifier): name every workflow, script and job after the issue it works on
Everything this stack adds now carries issue in its file name, workflow name
and job id, so one search finds all of it:

  ls .github/workflows/issue_*
  grep -ril issue scripts .github/prompts .github/*.json

Renames: label_sync.yml -> issue_label_sync.yml, label_claude_code.yml ->
issue_label_claude_code.yml, .github/labels.json -> .github/issue-labels.json,
scripts/sync-labels(.test).ts -> scripts/sync-issue-labels(.test).ts. Job ids
now match the script they run: classify-issue-tests, classify-issue,
label-issue, sync-issue-labels-tests, sync-issue-labels, label-claude-code
2026-09-18 11:37:55 -07:00
ryan-crabbe-berri
333ed01878 ci(issue-classifier): give the classifier and label jobs unique check-run names
code-quality's job name collision check flagged classify (also in
duplicate_issue_check.yml) and label (also in label_claude_code.yml)
2026-09-18 11:37:55 -07:00
ryan-crabbe-berri
e37bd7c60b fix(issue-classifier): queue runs per issue and judge edits by live labels
An edit during the first run used to cancel it, and the edited run then
skipped because the webhook payload had no needs:template yet, so a
well-formed issue edited within the first minute was never labelled.
Runs for one issue now queue, and the script decides an edited event
against the live labels: a domain label means leave it alone, a gated
issue is re-run, and an unlabelled issue is re-run for its first hour
2026-09-18 11:37:55 -07:00
ryan-crabbe-berri
9c0a840122 ci(issue-classifier): parse only form headings and keep the claude code label
Split the issue body only on headings the two forms actually emit, keep the
first value when a heading repeats, cap each field on its own so a long config
cannot push the repro out of the model's view, and only treat comments from
github-actions[bot] as the template notice. The claude code keyword label the
deleted component labeler used to add gets its own small workflow.
2026-09-18 11:37:55 -07:00
ryan-crabbe-berri
294a9e15de ci: classify new issues into domain, provider, kind, priority and lift labels
Every issue opened from now on is gated on the template headings, sent once
through the LiteLLM proxy with a strict JSON schema, and labelled from the
manifest in .github/labels.json. Old-template issues are not touched. The bug
template shrinks to Description, Config, LiteLLM Version and Steps to Repro,
both templates gain a domain dropdown, and the labelers that keyed off the old
component dropdown go away.
2026-09-18 11:37:55 -07:00
ryan-crabbe-berri
30035f817b
Merge pull request #40935 from BerriAI/litellm_codex_duplicate_issue_check
ci: replace the title-similarity duplicate bot with a Codex semantic check
2026-09-18 11:37:51 -07:00
kerry
d2ac51893b test: keep the pinning-test removal free of unrelated reformatting
Regenerated every touched file from origin/main applying only the B1 test deletions and the unused import and helper cleanup they leave behind, without running the formatter across untouched code. CI only checks ruff format under litellm/, so the earlier reflows of test files were pure diff noise for reviewers

Also drops the tests/local_testing/test_prompt_caching.py entry from the caching-local shard in test-unit.yml since that file is deleted

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 04:27:28 +00:00
Mateo Wang
98b3564a5b
Merge pull request #41660 from BerriAI/litellm_remove_commented_out_proxy_tests
chore(tests): remove fully commented-out proxy test files and their CI entries
2026-09-17 17:56:02 -07:00
mateo
13d20036cf chore(tests): remove fully commented-out proxy test files and their CI entries
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 20:08:08 +00:00
mateo
cb4d4e9bfd test: drop test_deployed_proxy_keygen.py and its workflow entry
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 20:06:22 +00:00
Yujong Lee
b26935416a Merge remote-tracking branch 'github/main' into litellm_rust_bridge_declarative_route_catalog
# Conflicts:
#	tests/e2e/access_control/test_model_access_group_e2e.py
2026-09-17 11:08:08 -07:00
kerry
9e84d8a9c0 fix(ci): drop the pull_request_review trigger so the auto-merge workflow only runs from main
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 00:11:31 +00:00
Yujong Lee
23c059faba ci: assign chat_completions and messages test dirs to the misc shard
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 23:05:55 +00: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
Yujong Lee
48df3d5a48 ci(rust): install nextest via pinned taiki-e/install-action
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 19:54:46 +00:00
Yujong Lee
3f15dcd96b ci(rust): fold fmt into the clippy job
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 19:47:48 +00:00
Yujong Lee
b70ddc2fd8 ci(rust): split rust jobs, use nextest and Swatinem/rust-cache
Split the Rust workflow into fmt, clippy, nextest and wheel jobs so they run in parallel, replace manual actions/cache with Swatinem/rust-cache, and install a pinned checksum-verified cargo-nextest. Make two python-bridge tests self-contained so they pass when nextest runs each test in its own process.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 19:14:32 +00:00
Yujong Lee
fc93c9ccba ci(rust): drop removed bedrock-auth feature steps
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 18:20:33 +00:00
Yujong Lee
96baeb8b04 refactor(rust): remove gateway, config, router, realtime, and trace-parity infrastructure
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 16:00:07 +00:00
Yuneng Jiang
064d5d61da
ci(image-scan): ignore zlib CVE-2026-85091 until Wolfi ships the fix
Wolfi's security database names zlib 1.3.3-r0 as the fix for CVE-2026-85091,
but the newest zlib published to the Wolfi apk repo is 1.3.2-r7. Every
wolfi-base digest, including the current latest, still reports the CVE, so no
base image bump or apk upgrade can clear it and image-scan fails on every PR
touching a Dockerfile or the lockfile, and on the nightly schedule.

Ignore that CVE and its GHSA alias for the zlib apk package only, so a fixable
High in anything else still fails the job.
2026-09-15 19:23:21 -07:00
Yuneng Jiang
a1d216b7d7
fix(ci): test checked-out model pricing in unit jobs 2026-09-14 21:00:00 -07:00
yuneng-jiang
15bd8b0e4a
Merge pull request #40892 from BerriAI/litellm_jwt_management_callers
test: bind management E2E callers and isolate JWT actors
2026-09-14 20:30:52 -07:00
Devin AI
0679d799d4 ci: exercise the lite CLI on the Python 3.10 import smoke job
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-14 07:07:06 +00:00
ryan-crabbe-berri
155d982821 ci(duplicate-check): require DUPLICATE_CHECK_MODEL instead of defaulting to gpt-5.6
The baked-in default meant a repo that never set the variable silently
got the most expensive candidate. Cost per issue spans roughly 20x
across the models this can run on, so the workflow now fails with a
clear message rather than picking one.
2026-09-13 16:17:45 -07:00
yuneng-jiang
daa2b0248a
Merge pull request #40172 from BerriAI/litellm_remove_main_guard
ci: remove main branch source guard
2026-09-12 21:10:34 -07:00
ryan-crabbe-berri
009d6f364b ci(duplicate-check): move the flag step into a tested bun script
A verdict is now dropped when it names a pull request, the issue itself,
or a newer issue, and the label goes on before the comment so a failed
comment leaves no marker and the rerun finishes the job. The flag logic
lives in scripts/flag-duplicate-issue.ts next to the sweep it feeds,
sharing normalizeTitle and the marker format, with bun tests that run on
pull requests touching it
2026-09-12 20:10:30 -07:00
ryan-crabbe-berri
82f20793eb ci: replace the title-similarity duplicate bot with a Codex semantic check
The old check_duplicate_issues.yml matched on title wording, so it missed the
same bug reported in different words. Over one full week of new issues (167,
5 to 12 Sep) it flagged 2, both wrong, while hand review found 11 real
duplicates that nothing caught.

The new workflow fetches the issue through the API into a file, runs
openai/codex-action with a fixed prompt and an output schema, and lets Codex
search the tracker with gh. At a 0.95 confidence gate it would have posted 12
comments that week, 9 naming a real duplicate. It reuses the same marker
comment and potential-duplicate label as before so auto-close-duplicates.yml
keeps working unchanged, and warns about the auto-close only when the titles
actually match.

Traffic goes through LiteLLM: the key is a virtual key and the endpoint is
the proxy's /v1/responses. Comments and labels stay off until the
DUPLICATE_CHECK_ENABLED repo variable is set.
2026-09-12 19:41:07 -07:00
Yuneng Jiang
88de192dcf
test: bind management E2E callers and isolate JWT actors 2026-09-12 13:29:04 -07:00
Yuneng Jiang
ec64005ff7
ci: run the JWT stack startup checks 2026-09-11 17:17:03 -07:00
Yuneng Jiang
a706dbbb7d
chore: merge current staging into JWT E2E infrastructure 2026-09-11 17:13:52 -07:00
Yuneng Jiang
77f406dc00
test(e2e): start persistent Keycloak in the changed-test runner 2026-09-11 17:09:35 -07:00
kerry-berri
8e4f2abb40
Merge pull request #40482 from BerriAI/litellm_e2e_redis_timeout
test(load): add a Redis timeout chaos load test
2026-09-11 16:58:52 -07:00
kerry
ffc16a4b0e test(e2e): restore the E2E_REDIS_CHAOS opt-in for the redis chaos test
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (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
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 22:46:31 +00:00
Kerry Lu
d4e083348c revert: drop the create-release.yml gating and E2E_REDIS_CHAOS opt-in
create-release.yml is back to calling the chaos test through no mechanism at
all; it never called it. Also drops the E2E_REDIS_CHAOS opt-in gate itself:
the redis_chaos marker still exists for -m selection and is still excluded
from the per-PR selector by path (tests/e2e/(ui|claude_code|load)/), but the
test no longer needs an env var to run once its file is targeted.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 15:16:00 -07:00
Kerry Lu
7d8f2c9ad3 ci(e2e): drop the weekly cron for the Redis chaos test
Now that create-release.yml gates stable and RC releases on this test directly,
the weekly schedule is redundant: every release gets a run against its own
commit instead of whatever happened to be on the default branch that Saturday.
workflow_dispatch stays for manual runs.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 15:00:57 -07:00
Kerry Lu
39324dc960 ci(release): rename redis-chaos-gate to redis-chaos-check
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 14:59:35 -07:00
Kerry Lu
445b45f261 ci(release): rename the tag-classification job to run-stable-release-checks
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 14:55:59 -07:00
Kerry Lu
7ebf60f33e ci(release): rename the tag-classification job to classify-if-stable-release
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 14:49:59 -07:00
Kerry Lu
cdf8a8cab8 ci(release): rename the tag-classification job to prepare
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 14:47:57 -07:00
Kerry Lu
9d91aff249 ci(release): rename the tag-decision job to stable-release-gate
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 14:43:47 -07:00
Kerry Lu
a8d0171800 ci: gate stable and RC releases on the Redis chaos load test
The chaos test only ran on a weekly cron, so a release could be cut from a
commit it had never covered. Making it callable lets create-release.yml run it
against the exact commit being tagged and refuse to tag if it fails.

Dev, nightly, alpha and beta tags skip the gate: they are cut far more often
than stable and RC tags, and the weekly schedule already covers the default
branch. Input validation moves into the gate job so a malformed tag or SHA
fails before spending a multi-minute chaos run.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 14:38:10 -07:00
Kerry Lu
66980bbb87 test(e2e): address Redis chaos PR review, add log-bytes budget
Pad the locust payload to tens of KB so per-request bookkeeping cost scales with
body size instead of hiding behind a 40-byte prompt. Turn on
use_redis_transaction_buffer in the chaos config and JSON_LOGS in the workflow so
the spend buffer, pod lock, and JSON-encoded breaker tracebacks are all part of
the measured chaos cost. Add a log-bytes-per-request budget alongside latency,
RSS, and CPU, reading the proxy's log file size at each phase split; its ceiling
is uncalibrated since no chaos run has measured it yet.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-11 12:40:25 -07:00
Mateo Wang
2c78e28c24
Merge pull request #39523 from BerriAI/litellm_fix_ai_gateway_image_build
fix(ai-gateway): build the release image again and cover it in CI
2026-09-11 11:46:49 -07:00
Kerry Lu
1699f2d6dc Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_e2e_redis_timeout
# Conflicts:
#	uv.lock
2026-09-11 11:38:22 -07:00
Kerry Lu
33ec56ed75 test(e2e): rewrite the Redis timeout test as a locust chaos load test
The sequential version sent one request at a time, so a Redis outage never
reached the concurrency where the failed-tracking alert body actually grows.
This drives the proxy with locust against one model group of three mock
deployments, two failing at order 1 and one serving at order 2, so every
request spends its retries on the failing pair and lands on the serving
deployment through the order-based fallback. Two phases, a healthy baseline
and a CLIENT PAUSE WRITE window, and every request must succeed in both.

Latency, RSS and CPU are reported as p50/p90/p99 per phase rather than
asserted on: RSS and CPU come from psutil on the proxy's process tree, since
a multi-worker proxy serves /metrics from the prometheus multiprocess
collector and that drops the process collector's series. Thresholds stay open
until weekly runs give real baselines.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-10 21:55:23 -07:00
devin-ai-integration[bot]
ae01882535
feat(proxy): offload spend tracking to a pod-local collector sidecar (#40545)
* feat(proxy): offload spend tracking to a pod-local spend worker sidecar

py-spy on the gateway showed the post-response _PROXY_track_cost_callback,
spend-log and DBSpendUpdateWriter work running on the inference workers'
event loop, so a DB or Redis stall backed up the request path.

When LITELLM_SPEND_WORKER_ENABLED=true, _ProxyDBLogger serializes one compact
typed SpendEvent per success and hands it to a SpendEventProducer that ships
it over a unix socket (default) or loopback-only TCP to a sidecar started as
`python -m gateway.spend_worker`. The sidecar runs the unchanged
_ProxyDBLogger pipeline against the pod's PgBouncer (pooled_database_url).
When the sidecar is unreachable, the buffer is full, or the gateway shuts
down with events still queued or in flight, the producer applies
LITELLM_SPEND_WORKER_ON_UNAVAILABLE (fallback in-process, or drop). The
sidecar half-closes producers on SIGTERM and drains, the producer treats
EOF as unavailable, and the gateway flushes buffered spend counters on
shutdown. The sidecar honors LITELLM_LOG so its writes are visible in its
own process log.

Helm: both charts gain an opt-in spend-worker sidecar container sharing an
emptyDir socket dir, and the componentized chart's HPA uses a
ContainerResource CPU metric scoped to the gateway container so sidecar
CPU does not drive inference scaling.

* feat(terraform): opt-in spend-worker sidecar for the AWS and GCP gateway stacks

Adds spend_worker_* inputs to both modules. On ECS Fargate the sidecar is a second, non-essential container in the gateway task; on Cloud Run it is a second container in the gateway service. Both listen on loopback TCP, share the gateway's DB/Redis/secret env, and set LITELLM_JOB_ROLE=spend_worker. Disabled by default. Plan-only tests cover both, and the terraform CI workflow now runs the gcp module too

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

* test(proxy): retrieve a completed batch in the in-process spend path test

The base now defers cost tracking for batches that are still in flight, so an in_progress batch never reaches update_database

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

* refactor(proxy): rename the spend worker sidecar to collector

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

* fix(proxy): run the collector from the installed litellm package and finish in-flight fallbacks on shutdown

The sidecar command becomes python -m litellm.proxy.collector so the classic image, whose runtime
stage copies only the installed package, can run it. The module now assembles DATABASE_URL and the
pod-local pgbouncer URL itself, replacing gateway/collector.py

The componentized collector sidecar inherits gateway.volumeMounts so custom CA mounts reach it.
SpendEventProducer shields an in-progress fallback from the writer task cancellation so close()
no longer loses an event already handed to the in-process pipeline

Helpers used across modules (address_argument, should_store_prompts_and_responses_in_spend_logs,
flush_spend_counters_on_shutdown) become public so the change adds no reportPrivateUsage errors

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

* ci(terraform): drop the gcp job duplicated by the aws/gcp matrix

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

* fix(collector): keep metrics env off the classic sidecar and reject shared loopback ports

The classic chart no longer hands PROMETHEUS_METRICS_PORT and the billing metrics env to the collector container, and gives it the same /.npm scratch mount as the proxy on a read-only root. AWS and GCP now refuse a plan where the spend collector and the metrics sidecar bind the same loopback port. A regression test drives a sidecar crash mid-stream on asyncio and uvloop and checks no event is billed by both the sidecar and the in-process fallback; the producer docstring spells out why a failed drain() cannot double count

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

* style(proxy): format pooled_database_url after the pgbouncer rebase

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

* fix(proxy): keep the cache-hit preset key and survive dead producers on collector drain

Cache hits updated the logging object after the early return, so the offloaded spend event carried
preset_cache_key=None and the collector re-hashed reconstructed kwargs. Also guard write_eof() against
producer transports uvloop already closed so one dead connection cannot abort the drain

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

* fix(terraform): keep the gcp collector port off the metrics sidecar health port

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

* fix(proxy): collector connects to Postgres directly under IAM or Entra token auth

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

* fix(proxy): mark the collector's DATABASE_URL as pooled when it uses the pod's pgbouncer

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-10 17:14:13 -07:00
yujonglee
61b0def867
test(ocr): isolate native Rust bridge contracts (#40410)
* test(rust): add retained callback suite as expected failures

* fix(tests): narrow retained callback xfails

* test(rust): clarify retained callback contracts

* test(ocr): clarify retained Rust contracts

* test(ocr): restore guardrail contracts

* test(ocr): require Rust file input parity

* test(ocr): isolate native bridge contracts

* fix(ci): repair Rust dispatch and OSV checks

* test(ocr): assert explicit backend dispatch
2026-09-10 16:51:37 -07:00
Kerry Lu
be7dce30a3 ci(e2e): pin the Redis timeout workflow's Postgres image by digest
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-10 15:16:24 -07:00
devin-ai-integration[bot]
d98522b6f6
feat(proxy): share database connections across workers with an in-container pgbouncer (#39683)
* feat(proxy): share database connections across workers with an in-container pgbouncer

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

* fix(proxy): parse pgbouncer options iteratively to satisfy the recursion gate

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

* fix(proxy): refuse pgbouncer with token db auth and retry failed pooler restarts

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

* fix(proxy): build pgbouncer 1.25.2 from a pinned source archive and verify pooler replacements

The public Wolfi repository only carries pgbouncer 1.24.1-r3, which the image
scan rejects (CVE-2026-6664, CVE-2026-6665, CVE-2026-6666, CVE-2025-12819).
All three images now compile the checksummed 1.25.2 release in a builder stage.

The supervisor now waits for a replacement pooler to listen before treating it
as recovered, ends and retries one that never does, and takes the same lock for
stop() and spawn so no replacement can be started after shutdown began.

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

* fix(proxy): refuse to start pgbouncer on a loopback port another process already owns

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

* fix(proxy): count pgbouncer ready only once its own unix socket answers, not any listener on the port

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

* fix(proxy): refuse pgbouncer older than 1.19, whose unix socket cannot vouch for the tcp port

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

* feat(helm,terraform): expose the in-container pgbouncer pool for the componentized gateway

Add database.connectionPool to helm/litellm and gateway_connection_pool_* to
terraform/litellm/aws so the componentized gateway can receive the
LITELLM_PGBOUNCER_* env the classic image already honours. Both reject the
pool under IAM or Entra token auth at render/plan time: the pooler holds one
static database password for the life of the pod or task.

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

* feat(gateway): launch the componentized gateway image through a pgbouncer-aware supervisor (#40592)

The componentized gateway image started uvicorn directly, so the in-container
PgBouncer never ran for it: every worker opened its own Prisma pool to the
database. It also passed no keep-alive timeout, so behind a load balancer with
a 60s idle timeout uvicorn's 5s default closed idle connections first and the
balancer returned 502s on scale-out

gateway.launch assembles DATABASE_URL, starts PgBouncer once per pod when
LITELLM_PGBOUNCER_ENABLED is set, hands the workers the loopback URL and then
runs uvicorn on gateway.main:app with KEEPALIVE_TIMEOUT as --timeout-keep-alive.
The image builds PgBouncer 1.25.2 from a checksummed tarball, copies the
compiled Rust extension into the /app source tree it imports from (it was only
in site-packages, which PYTHONPATH=/app shadows) and asserts the native bridge
loads. The app user is added to stats_users so operators can read the PgBouncer
console with the application credentials

The supervisor returns the pooled URL instead of writing into the mapping it
was handed, a database user whose name PgBouncer would split into several
stats_users entries is refused before the config is written, and the launcher
tests drive main() with an injected serve callable

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

* docs(terraform): describe the gateway.launch pooler entrypoint in the aws module README

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

* fix(proxy): run pgbouncer exit hooks only in the parent and copy the CA into the runtime dir

Gunicorn workers inherit the parent's atexit table, so a recycled worker (max_requests) stopped the shared pooler and removed its runtime dir, then hung in the inherited Popen lock. The hooks now no-op unless os.getpid() is the process that started PgBouncer

A verified TLS upstream named the operator's CA bundle directly, which is often a 0600 root-owned file that nobody (the user PgBouncer drops to) cannot read, so every server connection failed with "failed to load CA". The bundle is copied into the runtime dir next to the ini and chowned with it

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

* feat(terraform): run the gateway through gateway.launch and add the gcp connection-pool variables

Cloud Run and ECS overrode the image command with uvicorn gateway.main:app, which skips the supervisor that starts the in-container PgBouncer, so LITELLM_PGBOUNCER_ENABLED was inert on both stacks. Both now exec python -m gateway.launch (under ddtrace-run when USE_DDTRACE is set), and the gcp module gains gateway_connection_pool_enabled / gateway_pool_max_db_connections / gateway_pool_max_client_conn wired to the gateway service only

The test_launch password_env fixture now restores DATABASE_URL even when it was unset: monkeypatch.delenv records nothing for an absent var, so main() left postgresql://...@db.internal in the xdist worker's environ and the key-rotation e2e test in the same proxy-infra shard stopped skipping and tried to reach db.internal

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

* fix(pgbouncer): keep channel_binding and gssencmode off the loopback URL

Prisma would demand TLS channel binding from a pooler that only speaks
plain TCP on 127.0.0.1. Also pass the request the marketplace test
started needing after #40518 landed on top of #40496

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-10 22:14:37 +00:00
Mateo Wang
218b3280d1
Merge pull request #39272 from BerriAI/litellm_fix_e2e_lint_pathspec
ci(lint): gate top-level tests/e2e and litellm files in the diff-scoped lint steps
2026-09-10 10:13:56 -07:00