litellm/litellm
ryan-crabbe-berri 96c008f420
ci: fail on new unbounded SQL IN lists and add a Prisma chunking helper (#42629)
* ci: warn on SQL IN lists with no written bound

Postgres caps a prepared statement at 32,767 bind parameters and a
membership filter binds one per value, so an IN list built from table
data breaks once the table outgrows the cap. That is how the budget reset
job froze every due budget (LIT-7535, #40564).

check_unbounded_in_lists.py reports every Prisma "in" / "not_in" filter
whose value has no fixed size and every raw SQL literal that splices a
list in after "IN (", unless the line carries "# bounded-ok: <reason>".
It only warns for now: the output is the inventory for RCA action item
AI-1, and it exits 0.

* ci: decide a constant IN list by its module binding, not its casing

An ALL_CAPS name imported or filled at runtime is as unbounded as any
other, so a name now passes only when the module binds it once to a
value of fixed size. Adds Final to the locals a loop does not forbid.

* ci: only a frozen module value makes an IN list constant

A module list bound once could still grow through append or extend, so
a name now counts as fixed only when it is bound to a tuple, frozenset
or constant. Trims the module docstring to what a reader needs.

* ci: chunk Prisma IN lists with a shared helper and fail on new unbounded ones

Add litellm.repositories.bounded_in: find_many_in, count_in, update_many_in
and delete_many_in split a deduplicated value list into 5,000-value chunks,
AND each chunk with the caller's where, run them in order (a transaction
handle works) and combine the results. Writes take a required atomicity
argument, and a where that already filters the chunked field is refused.

check_unbounded_in_lists.py now fails CI on any finding missing from
unbounded_in_baseline.txt and on any stale baseline entry, so the baseline
only shrinks. Entries are keyed by path, enclosing scope, kind, field and
occurrence, not line numbers. The helper module is exempt, a constant
spread into a frozen tuple counts as fixed, and messages point at the
helper for "in" and at an array parameter for "not_in" and raw SQL.

A real-Postgres integration test shows a raw 40,000-value filter rejected
for too many bind variables while the helpers handle it.

* refactor: rename bounded_in to chunked_in and let callers pick a chunk size

The helper module is litellm.repositories.chunked_in, and its unit and
integration tests, the checker's exemption path and its finding messages
follow the new name. The `# bounded-ok` marker is unchanged.

find_many_in, count_in, update_many_in and delete_many_in take a
keyword-only chunk_size, defaulting to IN_LIST_CHUNK_SIZE (5,000). A value
below 1 or above MAX_IN_LIST_CHUNK_SIZE (30,000) raises ValueError before
any query, which leaves the rest of the filter headroom under Postgres's
32,767 bind-parameter cap.

* refactor: flatten chunked_in's stacked comprehensions with chain.from_iterable

LIT014 (#42650) caps a comprehension at one for and one if clause. The four nested walks in the helper now chain their iterables instead, with the same order and results.

* refactor: recover user details with find_many_in, sending chunks as lists

_details_for_user_ids reads users through find_many_in instead of a raw
"in" filter, so its lookup stays under the bind-parameter cap for any
number of recovered keys. Up to 5,000 ids it still sends one find_many
with the same where dict, and a PrismaError from any chunk is still
logged and treated as no details.

The helper now sends each chunk as a list, so a chunked filter equals
the dict a hand-written call would send and a migrated call site's
existing assertions keep passing.

The site's baseline entry is gone.

* ci: skip functional TypedDict field maps in the unbounded IN list check

The dict passed as the field map of TypedDict("Name", {...}), or as its fields= keyword, names fields: an "in" or "notIn" key there is a type, not a filter. Only that dict is skipped, for TypedDict, typing.TypedDict and typing_extensions.TypedDict; a filter nested in a field value or passed to any other call is still reported. The two types/proxy/management_endpoints/team_endpoints.py entries leave the baseline, which is now 156.

* fix: refuse an update_many_in whose data writes the chunked field

Chunks run one after another, so an update that sets the chunked field can move a row into a later chunk, which updates it again and counts it twice: values ["old", "new"] with chunk_size=1 and data={"id": "new"} does exactly that. update_many_in now raises ChunkedFieldWriteError before any query when data has the chunked field as a top-level key, in any form, including Prisma operators such as {"set": ...}.

* docs: cut the unbounded IN list checker's docstring to what it flags and how to clear it

It now says what is reported, the three ways to clear a finding, and how the baseline and --update-baseline work, in 11 lines. The per-shape detail lives in the tests.

* ci: key an unbounded IN list finding by its filtered expression too

A baseline key of path, scope, kind, field and occurrence let a PR delete
a baselined filter and add a different unbounded one on the same field in
the same function, and the new one took over the old key. The key now
also carries the filtered expression's source, whitespace-normalized
(the Prisma value, or a raw-SQL `IN (...)` slot), so that swap reads as
one new and one stale entry and fails the run. The same expression
re-added in the same function is still the same finding.

Every baseline entry is rewritten in the new form; the 156 findings are
unchanged, and only occurrence indexes renumber where one field had
several different expressions.
2026-09-26 13:40:44 -07:00
..
a2a_protocol Merge remote-tracking branch 'origin/main' into litellm_decrease_anys_opus5_r5 2026-09-21 12:57:38 -07:00
anthropic_interface fix(proxy): send a real error event when a /v1/messages stream fails (#41826) 2026-09-25 15:36:35 -07:00
assistants
batch_completion
batches refactor: daily fresh tech debt cleanup, rolling PR (2026-09-25) (#43151) 2026-09-26 01:32:55 -07:00
caching refactor(anthropic): rename experimental_pass_through to pass_through (#43329) 2026-09-26 13:00:50 -07:00
chat_completions feat(rust-bridge): add cache and secret migration foundations (#42328) 2026-09-22 03:41:04 +00:00
completion_extras feat(lint): add LIT013 flagging *-ok suppressions that suppress nothing and remove the 240 stale ones (#42793) 2026-09-23 17:50:09 -07:00
compression merge: main into litellm_headroom_protect_cached_prefix 2026-09-15 04:12:28 +00:00
containers test: finish the non-proxy half of tests/test_litellm (#43281) 2026-09-25 22:43:41 -07:00
embeddings feat(embeddings): add native dispatch foundation (#42799) 2026-09-23 21:11:09 +00:00
endpoints/speech/speech_to_completion_bridge
evals
experimental_mcp_client feat(mcp): configure protocol versions and capability discovery (#43169) 2026-09-25 13:13:52 -07:00
files feat(xai): add native xAI batches and files support (#42812) 2026-09-25 15:35:20 -07:00
fine_tuning
google_genai fix(google_genai): drop non-object tool parameters instead of forwarding them 2026-09-19 19:19:33 -07:00
images Merge branch 'main' into litellm_add_edenai_provider 2026-09-21 19:34:49 +00:00
integrations refactor(anthropic): rename experimental_pass_through to pass_through (#43329) 2026-09-26 13:00:50 -07:00
interactions feat(lint): add LIT013 flagging *-ok suppressions that suppress nothing and remove the 240 stale ones (#42793) 2026-09-23 17:50:09 -07:00
litellm_core_utils feat(sail): add Sail as a provider with service_tier mapped to its completion window (#42840) 2026-09-26 12:57:48 -07:00
llms refactor(anthropic): rename experimental_pass_through to pass_through (#43329) 2026-09-26 13:00:50 -07:00
messages refactor(anthropic): rename experimental_pass_through to pass_through (#43329) 2026-09-26 13:00:50 -07:00
models fix(vector_stores): keep config-defined vector stores listed and read-only (#42574) 2026-09-23 04:02:16 +00:00
ocr refactor(ocr): remove the Python OCR execution path and require the Rust route (#43081) 2026-09-24 18:18:50 -07:00
passthrough fix(responses): fall back on pre-output stream drops, fail truncated streams, honor request_timeout (#43133) 2026-09-25 14:32:41 -07:00
proxy ci: fail on new unbounded SQL IN lists and add a Prisma chunking helper (#42629) 2026-09-26 13:40:44 -07:00
proxy_auth
rag fix(types): read upstream headers through a typed helper 2026-09-21 13:16:58 -07:00
realtime_api refactor(types): replace Any with proven types in 13 files (#42937) 2026-09-24 09:02:02 -07:00
repositories ci: fail on new unbounded SQL IN lists and add a Prisma chunking helper (#42629) 2026-09-26 13:40:44 -07:00
rerank_api feat(lint): add LIT013 flagging *-ok suppressions that suppress nothing and remove the 240 stale ones (#42793) 2026-09-23 17:50:09 -07:00
responses fix(responses): run stream failure and success hooks on the iterating loop instead of blocking it (#43270) 2026-09-26 12:22:22 -07:00
router_strategy fix(router): parse the classifier verdict out of surrounding prose instead of falling to the default tier (#43215) 2026-09-25 16:18:51 -07:00
router_utils fix(router): match provider-prefixed fallback keys for bare model groups served by wildcard deployments (#43062) 2026-09-24 18:08:56 -07:00
rust_bridge refactor(anthropic): rename experimental_pass_through to pass_through (#43329) 2026-09-26 13:00:50 -07:00
sandbox
search
secret_managers fix(ci): stop stale CI reds, keep unit tests off the host env, retry CyberArk policy conflicts (#43294) 2026-09-26 09:25:13 -07:00
skills
types feat(sail): add Sail as a provider with service_tier mapped to its completion window (#42840) 2026-09-26 12:57:48 -07:00
vector_store_files
vector_stores fix(vector_stores): keep config-defined vector stores listed and read-only (#42574) 2026-09-23 04:02:16 +00:00
videos
__init__.py refactor(anthropic): rename experimental_pass_through to pass_through (#43329) 2026-09-26 13:00:50 -07:00
_internal_context.py fix(otel): detach post-response service spans by request phase, name redis spans by operation (#43237) 2026-09-26 10:10:12 -07:00
_lazy_imports.py feat(tokenizer): preserve Python defaults with opt-in Rust dispatch (#42174) 2026-09-22 04:41:11 +00:00
_lazy_imports_registry.py refactor(anthropic): rename experimental_pass_through to pass_through (#43329) 2026-09-26 13:00:50 -07:00
_logging.py feat(lint): add LIT013 flagging *-ok suppressions that suppress nothing and remove the 240 stale ones (#42793) 2026-09-23 17:50:09 -07:00
_redis.py fix(redis): authenticate sync clusters with IAM credential providers (#40204) 2026-09-23 17:23:55 -05:00
_redis_credential_provider.py fix(redis): accept every truthy flag and sign serverless ElastiCache caches 2026-09-10 10:13:03 -04:00
_service_logger.py fix(otel): detach post-response service spans by request phase, name redis spans by operation (#43237) 2026-09-26 10:10:12 -07:00
_uuid.py
_version.py
anthropic_beta_headers_config.json feat(router): native compact-to-fit across conversation APIs (#42074) 2026-09-21 22:52:29 -07:00
anthropic_beta_headers_manager.py fix: drop a blank anthropic-beta header before it reaches the provider 2026-09-19 20:13:59 -07:00
blog_posts.json
budget_manager.py
constants.py feat(sail): add Sail as a provider with service_tier mapped to its completion window (#42840) 2026-09-26 12:57:48 -07:00
cost.json
cost_calculator.py fix(cost): apply a deployment's pricing override to realtime sessions (#43114) 2026-09-24 21:03:59 -07:00
exceptions.py fix(spend): return 400 from /spend/calculate for a model with no pricing row (#42497) 2026-09-22 15:44:00 -07:00
main.py feat(providers): add Nadir intelligent-router provider (nadir/auto) (#33227) 2026-09-24 22:03:30 -07:00
model_prices_and_context_window_backup.json feat(sail): add Sail as a provider with service_tier mapped to its completion window (#42840) 2026-09-26 12:57:48 -07:00
policy_templates_backup.json
provider_endpoints_support_backup.json Merge pull request #41101 from hMED22/litellm_add_edenai_provider 2026-09-21 16:16:28 -05:00
py.typed
router.py refactor(anthropic): rename experimental_pass_through to pass_through (#43329) 2026-09-26 13:00:50 -07:00
scheduler.py
setup_wizard.py feat(anthropic): add Claude Opus 5.5 (#42489) 2026-09-22 09:52:35 -07:00
timeout.py
utils.py feat(sail): add Sail as a provider with service_tier mapped to its completion window (#42840) 2026-09-26 12:57:48 -07:00