Commit graph

46534 commits

Author SHA1 Message Date
mateo-berri
cd5cf84aac Merge branch 'litellm_internal_staging' into litellm_aws_partition_helper 2026-08-29 02:18:16 -07:00
Mateo Wang
3993829a21
Merge pull request #38748 from BerriAI/litellm_fix_gpt55_extra_body_test
test(responses): adapt temperature tests to the gpt-5 reasoning validation
2026-08-29 02:17:01 -07:00
mateo-berri
b0d485568b test(batches): pin the poller against retiring zero-count completed batches 2026-08-29 02:00:11 -07:00
mateo-berri
66947fcf7c test(responses): expect the bad-temperature 400 on a non-reasoning model 2026-08-29 01:58:15 -07:00
mateo-berri
8cf090b368 fix(router): arm the provider-scoped fallback pin only on resource-operating handlers 2026-08-29 01:46:33 -07:00
mateo-berri
7fbcd9c3ed fix(bedrock): treat partial record counts as unknown on batch retrieve 2026-08-29 01:40:07 -07:00
mateo-berri
81eb014e71 test(responses): use a reasoning-legal temperature in the gpt-5.5 extra_body merge test 2026-08-29 01:36:45 -07:00
mateo-berri
2968c246b9 test(responses): repair two tests broken by the gpt-5 temperature gate
PR #38593 stopped forwarding temperature to reasoning models, which left
test_extra_body_merges_with_request_data raising UnsupportedParamsError
and test_bad_request_bad_param_error no longer getting a rejection from
OpenAI because drop_params now eats the param. Both repairs are the same
hunks PR #38739 carries, so the branches merge clean in either order
2026-08-29 01:32:09 -07:00
mateo-berri
64eec53fd8 fix(guardrails): surface post-flush stream blocks as in-stream error frames and keep guardrail_information in spend logs
A guardrail block or failed scan that fires after SSE chunks have been
flushed can no longer set an HTTP status, so raising HTTPException there
silently truncated the stream. _emit_streaming_http_error now routes
post-flush failures through the endpoint translation's
build_stream_error_items, emitting the surface-correct error frame on
chat completions (data: {error}), /v1/messages (event: error), and
/v1/responses (ErrorEvent with the next sequence number). Pre-flush
blocks still raise with a real HTTP status.

Successful flags-on scans also logged metadata.guardrail_information as
null: the chat handler planted litellm_metadata on a route whose bucket
is metadata, flipping the bucket for every later write, and responses
streams fired their spend log before the eos scan ran. The chat handler
now merges user_api_key metadata through get_or_create_metadata_bucket,
and deferred stream-complete logging is armed for aresponses like it
already was for anthropic_messages.
2026-08-29 01:25:08 -07:00
mateo-berri
9da766a1f9 test: use a non-reasoning model for the responses bad-param e2e fixture 2026-08-29 01:22:49 -07:00
mateo-berri
ad8c1457d1 fix(aws): build every AWS endpoint and ARN from the region partition
Adds litellm/litellm_core_utils/aws_partition.py mapping a region to its
AWS partition (aws, aws-cn, aws-us-gov, and the iso partitions), its DNS
suffix, and its ARN prefix, and uses it at every AWS host and ARN build
site: bedrock (runtime, agent, agentcore, legacy client, batches, files,
realtime), sagemaker, polly, secrets manager, s3 log uploads, bedrock
passthrough routes, and rag ingestion. ARN detection now accepts
arn:aws-cn: and arn:aws-us-gov: prefixes.

STS region resolution now falls back to the configured aws_region_name
after the aws_sts_endpoint host and the AWS_REGION/AWS_DEFAULT_REGION env
vars, so cn and gov role assumption no longer silently signs against
us-west-2.

A partition sweep test walks every endpoint builder with cn regions and
asserts no amazonaws.com host or arn:aws: prefix comes out, plus an AST
guard that fails on any new f-string hardcoding either literal.
2026-08-29 01:21:59 -07:00
mateo-berri
1956903c28 refactor(vertex_ai): narrow dict return annotations in gemini transcribe config 2026-08-29 01:21:38 -07:00
mateo-berri
6301a520a6 test: pin reasoning effort none in the responses extra_body fixture 2026-08-29 01:07:24 -07:00
mateo-berri
5d34fb20ff fix(bedrock): map real batch record counts and guard zero-count retire 2026-08-29 01:05:36 -07:00
Devin AI
9bfb332904 refactor: replace fresh getattr/setattr and test type-ignores with typed access
Same-day debt cleanup on code that landed in the last 24 hours. No behavior change.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-29 07:59:27 +00:00
mateo-berri
8fcfaf519c test: drop duplicated callback assertion 2026-08-29 00:53:24 -07:00
mateo-berri
817b5f44f6 fix(anthropic_endpoints): serialize dict-detail HTTPExceptions on /v1/messages like sibling surfaces 2026-08-29 00:48:14 -07:00
mateo-berri
0b14897a7e fix(router): pin batch, file, and fine-tuning job ids to their owning model group on fallback 2026-08-29 00:47:34 -07:00
mateo-berri
1a26608769 feat(vertex_ai): route gemini transcribe models to generateContent on /v1/audio/transcriptions 2026-08-29 00:46:03 -07:00
mateo-berri
f58c3c0868 fix(proxy): fold litellm_metadata into metadata on chat routes so tag routing sees merged tags 2026-08-29 00:42:43 -07:00
mateo-berri
d7bd6ca614 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_aws_external_id_embed_sagemaker 2026-08-29 00:17:05 -07:00
mateo-berri
7eb757a49b fix(bedrock): route streamed responses-API output through the unified guardrail
Streamed /v1/responses returned 500 whenever a Bedrock post_call guardrail
was enabled: the hook fed responses-API events into stream_chunk_builder,
which only understands chat-completions chunks, and the wrapped KeyError
surfaced as litellm.APIError before any ApplyGuardrail scan ran.

Delegate responses-API routes to UnifiedLLMGuardrails, whose translation
layer scans the assembled response at end of stream and only then releases
the buffered events, so flagged content never reaches the client.
2026-08-29 00:01:02 -07:00
Mateo Wang
ae7e50f096
Merge pull request #38607 from BerriAI/litellm_fix_files_pre_call_hook
fix(proxy): trigger async_pre_call_hook on POST /v1/files uploads
2026-08-28 23:58:05 -07:00
mateo-berri
44a5d7a47a fix(aws): forward aws_external_id in bedrock embeddings and sagemaker credential loading 2026-08-28 18:25:14 -07:00
yuneng-jiang
4d7144160a
Merge pull request #38715 from BerriAI/litellm_/restrictedpython-security-update-c0aee7
chore(deps): raise RestrictedPython floor to 8.5
2026-08-28 18:20:57 -07:00
mateo-berri
e486e43d0f Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_bedrock_guardrail_stream_audit 2026-08-28 18:16:00 -07:00
yuneng-jiang
c80dc68445
Merge branch 'litellm_internal_staging' into litellm_/restrictedpython-security-update-c0aee7 2026-08-28 18:12:24 -07:00
Mateo Wang
fef5d3d0f9
Merge pull request #38713 from BerriAI/litellm_fix_messages_stream_guardrail_logging_race
fix(guardrails): record post_call scans on native /v1/messages streams
2026-08-28 17:44:55 -07:00
mateo-berri
a90ad5fe5c feat(bedrock): honor streaming buffer/sampling config for unbuffered post_call scans 2026-08-28 17:35:15 -07:00
devin-ai-integration[bot]
cbdaa3b153
fix(terraform): refresh jwt key mapping allowlist entries now that the resource is merged (#38720)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-28 17:22:22 -07:00
devin-ai-integration[bot]
f7fb3694f8
feat(terraform): coverage-enforcing CI gate against the latest OpenAPI spec (#38710)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-28 17:11:43 -07:00
devin-ai-integration[bot]
592518202c
feat(terraform): add litellm_jwt_key_mapping resource (#38714)
* feat(terraform): add litellm_jwt_key_mapping resource

Adds a Terraform resource for the proxy's JWT to virtual key mappings, so a
JWT client identified by a claim such as client_id, azp or sub maps to a
virtual key and inherits its models, budgets, rate limits and spend tracking.

Covers the four mapping endpoints: /jwt/key/mapping/new, /info, /update and
/delete. is_active is applied through a follow-up update because the create
endpoint always starts a mapping active, a dropped description is sent as an
empty string because the update endpoint ignores absent fields, changing the
mapped key rotates it in place, and changing the claim name or value forces
replacement since the update endpoint cannot change them.

* fix(terraform): revert key on failed jwt_key_mapping update

Classic SDKv2 persists a failed Update's diff-applied values to state
regardless of the error, so a rejected key rotation left the new key in
state while the proxy kept the old one and the next plan falsely converged.
Revert key via GetChange and resync description/is_active/computed fields
from a post-failure Read, since Read alone can't recover key (the proxy
never returns it).

Also drop the case-insensitive "mapping not found" body match: the proxy
raises 404 for all three not-found paths (info, update, delete), so
checking the status code alone is sufficient.

Clarify the docs: referencing a litellm_key resource's write-only key is
not a null-then-400 situation, it's a static "Missing required argument"
error at plan time, in every apply ordering.

* fix(terraform): stop leaving an active mapping behind on failed cleanup

Two issues flagged by review:

- Create has no way to ask the proxy for an inactive mapping, so an
  is_active=false mapping is briefly active while the follow-up
  deactivation runs. If that deactivation call itself fails, the mapping
  used to stay active and untracked. It's now deleted instead, closing
  the exposure rather than leaving it open indefinitely.
- On a failed update, only `key` was reverted before the recovery read.
  If that read also failed, description/is_active kept the rejected
  values, so a later plan could report false convergence. Now all three
  are reverted before the read runs.

Both come with regression tests, mutation-verified against the pre-fix
code.

* fix(deps): bump restrictedpython to 8.5 for GHSA-ffg3-p8fm-mjx2

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

* chore: retrigger ci

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

* fix(tests): stub anthropic judge credentials in funnel seeding test

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

* revert(deps): keep uv.lock unchanged to keep the PR terraform-only

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

---------

Co-authored-by: Fabrice Pont <fabrice.pont@doctolib.com>
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-28 17:10:44 -07:00
Mateo Wang
d42c71b7ff
Merge pull request #38695 from BerriAI/litellm_fix_together_sync_gh_token
fix(ci): fall back to github.token when the GH_TOKEN secret is unset in the Together sync workflow
2026-08-28 16:59:34 -07:00
yucheng-berri
470eb9620a
test(shadow_eval): configure the anthropic sdk judge in the funnel-seed test (#38717)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 16:59:22 -07:00
Shivam Rawat
3653e5893f
feat(terraform): resource and data source parity with the community provider (#38158)
* feat(terraform): close key/team schema gaps, fix team read envelope, add import support

* feat(terraform): add fallback resource/data source and key/team block resources

* feat(terraform): add access group and unified access group resources and data sources

* feat(terraform): add guardrail and prompt resources and data sources

* feat(terraform): add agent and search tool resources and data sources

* feat(terraform): add user and budget resources and data sources

* feat(terraform): add tag and project resources and data sources

* docs(terraform): changelog and readme for parity additions

* feat(terraform): add data sources for keys, teams, models, organizations, and mcp servers

* fix(terraform): key update 400 on empty budget_duration, key info envelope, config-supplied key

* fix(terraform): hash raw keys to SHA-256 tokens in key lookup URLs and block resource IDs
2026-08-28 16:55:40 -07:00
mateo-berri
307b471902 style(guardrails): drop narrative comments from the deferred logging path 2026-08-28 16:45:45 -07:00
Yuneng Jiang
f1bebb6fbd
chore(deps): raise RestrictedPython floor to 8.5
RestrictedPython 8.3 extended its protected-name validation to cover
positional-only parameters, so sandboxed source can no longer bind a
local named _getattr_, _getitem_, _write_ or _print_ that takes
precedence over the hooks the custom-code guardrail sandbox installs.
8.4 and 8.5 continue in the same direction with safer_getattr and the
Python 3.15 syntax audit.

The floor moves rather than the lock alone so downstream installs of
litellm[proxy] pick up the same behaviour.
2026-08-28 16:25:41 -07:00
yuneng-jiang
733d0b5af5
Merge pull request #38588 from BerriAI/litellm_/dark-mode-logo-strategy-7b99f2
feat(ui): make provider logos readable in dark mode
2026-08-28 16:25:11 -07:00
mateo-berri
1a5a856e3e fix(guardrails): defer native /v1/messages stream logging until post_call scans finish 2026-08-28 16:05:45 -07:00
Mateo Wang
4ebedf901a
Merge pull request #38696 from BerriAI/litellm_lit6376_dspy_streaming
fix(streaming): report response_cost and Anthropic citations from stream_chunk_builder
2026-08-28 15:31:23 -07:00
Mateo Wang
27c09248e4
Merge pull request #38593 from BerriAI/litellm_gpt5_default_reasoning_effort
fix(gpt-5): stop forwarding temperature and top_p to reasoning models that reject them
2026-08-28 15:20:41 -07:00
tin-berri
4e48d74455
feat(shadow_eval): measure both arms' cost so a job reports what the router would have saved (#38631)
The attempt row now prices the real arm (the payload's response_cost plus its own
routing classifier when it routed) beside the shadow arm (completion plus the
classifier cost the routing decision writes back), and flags turns litellm's
response cache served. A per-leg funnel table counts the eligible requests that
produced no row (lost the sampling dice, unjudgeable shape, concurrency shed),
so results can weigh judged rows against the traffic they stand for. Job results
gain per-slice and overall arm spends plus the coverage counts, the budget gates
charge the shadow arm's classifier spend against max_budget, and the dashboard
shows the measured cost comparison beside the win rate

Resolves LIT-6358

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 15:13:19 -07:00
Yassin Kortam
671f89d8bc
fix(proxy): reset a key's budget-window counters on spend reset (#38686)
* fix(proxy): reset a key's budget-window counters and broadcast the reset cross-pod

/key/{id}/reset_spend already reset the key's lifetime spend counter in
Redis, but a key with its own budget_limits (an extra time-windowed cap,
e.g. a daily budget layered on top of the lifetime max_budget) kept its
window counter untouched, so the key stayed 429'd on
"ExceededBudget: Key over <duration> budget" even after the admin action
reported spend back to $0.

Force-expire each window on reset: zero its Redis counter and restart the
window from now (window_start is derived as reset_at - budget_duration,
so reset_at must float to now + duration, not the next calendar boundary
get_budget_reset_time gives key creation - that boundary can still be
in the past relative to the spend that triggered the block).

Also close a second, narrower race: _delete_cache_key_object evicted the
cached key object only on the handling pod, so another pod could keep
serving the stale pre-reset object (and re-derive the pre-reset spend
counter via its own floor-marker cache) until its own TTL expired. It now
broadcasts the eviction, matching the pattern already used for team,
team-member, customer, and tag caches.

* fix(proxy): evict the cached key object after every reset_spend DB write

Greptile P1: eviction ran before the window-reset DB write committed, so a
request racing the reset could re-fetch and re-cache the pre-write row,
pinning that pod to the stale budget_limits for the rest of its own cache
TTL even after the write went through. Move the eviction to run last.

* test: pin real cache state and satisfy the test-quality gate

test_delete_cache_key_object_broadcasts_invalidation now asserts a real
UserApiKeyCache no longer holds the evicted entry, rather than only
inspecting a mock's call args. Suppress test-quality-ok on the
hash_token/_check_proxy_or_team_admin_for_key/_delete_cache_key_object/
publish_auth_cache_invalidation patches: none has an HTTP boundary to
fake, matching the pattern the file already uses for these same targets.

* fix(proxy): narrow budget_limits by the str branch, not the list branch

isinstance(x, list) in the else branch still leaves Sequence[object] | str
(a tuple satisfies Sequence without being a list), so json.loads() saw a
possible non-str argument. Check isinstance(x, str) instead, which narrows
each branch to exactly the type it needs.

* fix(proxy): persist advanced budget-window boundaries before zeroing counters

Greptile P1: publishing a zeroed window counter before the new reset_at
committed let a request racing the write compute window_start from the
stale boundary, re-sum the historical spend log rows the reset was
clearing, and put the counter right back above budget. Compute every
window's new boundary, persist all of them in one DB write, then zero
each window's Redis counter only once that write has landed.
2026-08-28 15:13:03 -07:00
tin-berri
42d278cfad
fix(router): tier-pinned reasoning_effort supersedes client effort carriers (#38698)
An auto-router tier pin injects reasoning_effort into request_kwargs, but
provider translations give a caller-supplied thinking, output_config.effort,
or reasoning carrier precedence over the reasoning_effort alias, so the pin
never reached the wire whenever the client expressed effort natively. Drop
the client's other encodings of the setting at the tier-param merge; a
client output_config keeps its non-effort fields
2026-08-28 14:53:33 -07:00
tin-berri
fb80ba7c98
fix(spend): remove the proxy-wide autorouter savings baseline override (#38700)
Every complexity router now derives and records its savings baseline from
its hardest configured tier, and the spend writer always prices against the
decision-recorded baseline model and deployment id. A leftover
litellm_settings.autorouter_savings_baseline_model key is inert
2026-08-28 14:53:27 -07:00
yucheng-berri
3fa4a4b72a
chore: drop stray learnings.md workflow note from CLAUDE.md (#38704)
PR #34940 (Lakera v2 guardrail) incidentally added a CLAUDE.md paragraph
about recording findings in litellm/learnings.md and per-skill learnings.md
files. It is unrelated to that feature, the referenced litellm/learnings.md
does not exist in the repo, and the skill paths it points at are agent-local
(.claude/skills/*), so the note has no effect in the shared repo. Revert it to
the pre-#34940 state.
2026-08-28 14:28:39 -07:00
tin-berri
8868aaa5db
feat(ui): run the Lite preset's medium and complex tiers at their documented efforts (#38482)
Lite ran Muse Spark 1.2 and Kimi K3 at whatever effort each provider happens to
default to. Set the ones their own docs name: Muse Spark 1.2 at xhigh, and Kimi
K3 at max, which is Kimi's own default and what the model map now declares for
that model.

Stacked on the map change, since without it kimi-k3 resolves to unknown levels
and the tier editor's capability-blind fallback list does not offer max.
2026-08-28 14:26:55 -07:00
tin-berri
e966369558
fix(shadow-eval): validate Anthropic SDK judge credentials (#38701)
* fix(shadow-eval): validate Anthropic SDK judge credentials

* test(shadow-eval): configure valid SDK judges
2026-08-28 14:24:46 -07:00
yucheng-berri
3e280b1be9
fix(router): scrub fallback stamp keys in place and strip them at the proxy boundary (#38690)
PR #38586 changed the fallback-stamp scrub in async_function_with_fallbacks to
rebind kwargs[sibling] to a scrubbed copy instead of popping in place. Every
other router bucket write mutates the caller's dict in place, and everything
below the router resolves the metadata bucket by key presence, so on a proxy
request that carries litellm_metadata the copy becomes a detached object: the
proxy's post_call guardrail write-backs land in request_data while the spend
row is built from the router's copy. Result: guardrail_information and the
guardrail cost silently drop from the spend row on any request that planted a
reserved key, and an SDK caller aliasing one dict as both buckets loses the
router stamps entirely.

Scrub in place again, and move the anti-spoof to the proxy boundary: strip
attempted_fallbacks and original_model_group from client-supplied metadata and
litellm_metadata in add_litellm_data_to_request, next to the pricing-field
strip, so proxy traffic never carries a reserved key and the in-place pop only
ever fires for an SDK caller that planted one. Keep #38586's hop-stamp ordering
fix (caller keys first, stamps appended) untouched.
2026-08-28 14:18:56 -07:00
tin-berri
1e4d358f3f
fix(router): drop a tier param the routed target cannot take (#38622)
* fix(router): drop a tier param the routed target cannot take

A complexity tier's litellm_params are an operator override applied to every request that tier
routes, and they were written into the request kwargs unconditionally. When the tier set a param
the target does not declare, get_optional_params raised UnsupportedParamsError before the request
left the proxy, so the whole tier answered 400. The bundled Lite preset sets reasoning_effort on
its complex tier, and four of the thirteen kimi-k3 map entries reject that param, so a router
built from a first-party template failed on every complex prompt

Filter the tier params at both store sites against what the group's deployments declare. The
candidate set is asked of the module that raises rather than derived from a second list, so
credentials, endpoint and transport controls are never at risk: base_url, timeout,
default_headers, organization and deployment_id are not chat completion params and never reach
that comparison. A param survives if any deployment could take it, since routing has not picked
one yet, and it survives an unresolvable provider or an empty group, since a best-effort filter
must not narrow what the request already did

The skip list _check_valid_arg applies before rejecting a param now has one owner both it and the
router read, so the two cannot drift

* fix(router): honor allowed_openai_params when gating tier params

* test(router): cover _declared_param_allowlist malformed declarations

* fix(router): never ask an authenticating provider whether it takes a tier param

Resolving github_copilot or chatgpt runs their OAuth device flow, so the
capability question _deployment_accepts_param asks would freeze the event
loop for minutes inside async_get_available_deployment. Promote
register_model's local skip set to
constants.PROVIDERS_THAT_AUTHENTICATE_ON_PROVIDER_INFO and fail open on
those providers before any lookup

* fix(utils): adopt a declared authenticating prefix instead of resolving it

The tier-param guard alone was not enough: the savings baseline and the
model-info funnels also resolve deployments during routing, and each
resolution of github_copilot or chatgpt runs their OAuth device flow.
declared_authenticating_provider gives every metadata funnel
(get_supported_openai_params, _get_potential_model_names,
_supports_factory, canonical_model) the resolver's answer by string, so
the whole routing path answers without authenticating. A through-test
drives async_get_available_deployment with a copilot deployment and
records that no copilot resolution happens
2026-08-28 14:17:49 -07:00