Commit graph

8121 commits

Author SHA1 Message Date
mateo-berri
2a1c21b72d fix(router): keep acreate_file fallbacks inside the requested model group
A file uploaded through Router.acreate_file lands in the account of the
deployment that stored it, so a cross-group fallback silently stores the
file with the wrong provider and every later batch or fine-tuning call
against the returned id permanently fails. Extend the provider-scoped
fallback pin that already covers input_file_id and training_file to file
creation, so the original provider error surfaces instead.
2026-08-18 19:46:45 -07:00
mateo-berri
138c77023a fix: accept bool thinking param instead of crashing with AttributeError
litellm.completion(thinking=True) crashed pre-network in is_thinking_enabled
with a retryable APIConnectionError ('bool' object has no attribute 'get'),
so the router burned retries on a deterministic failure and proxy clients got
a traceback instead of a usable response.

validate_and_fix_thinking_param now coerces thinking=True to the enabled dict
with the default medium budget and drops thinking=False, and the remaining
dict-assuming thinking accessors (base config, bedrock converse, deepseek)
guard with isinstance so raw bools can never crash a transform.
2026-08-18 19:44:31 -07:00
yuneng-jiang
7ac764970b
fix(proxy): return no rows when the aggregated activity entity filter is empty (#37414)
The aggregated daily-activity queries build their WHERE clause as raw SQL, and
an empty entity list rendered as `"team_id" IN ()`, which Postgres rejects with
`syntax error at or near ")"`. Callers reach that state normally: a caller
without admin view and no explicit team_ids has its scope resolved to the teams
it belongs to, so anyone belonging to no teams, an org admin for instance, sent
an empty list and got a 500 back from /team/daily/activity/aggregated.

The paginated endpoint hands the same empty list to Prisma, which renders
`in: []` and matches nothing, so it kept returning 200 with an empty result set.
Emit FALSE for the empty case so the raw-SQL path lands on the same answer,
mirroring what the api_key filter a few lines below already does.

The fix covers both aggregated queries at once because they share one WHERE
clause builder.
2026-08-18 19:37:32 -07:00
mateo-berri
818886cfa1 fix(bedrock): raise non-retryable BadRequestError on unconvertible tool calls 2026-08-18 19:24:18 -07:00
mateo-berri
fe8c353435 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_bedrock_malformed_toolcall_18667 2026-08-18 19:16:41 -07:00
Mateo Wang
807e1da4af
Merge pull request #32448 from ChenluJi/feat/tinyfish-search-headers-and-extras
feat(tinyfish): surface response headers + top-level response extras
2026-08-18 18:53:02 -07:00
mateo-berri
490079e7df test: cover nested cache_creation_input_tokens in responses bridge and spend logs 2026-08-18 18:38:30 -07:00
tin-berri
f6eaca9069
fix(mcp): serve token-forwarding servers when oauth discovery fails (#37399)
true_passthrough and oauth_delegate forward the caller's own bearer and mint
nothing, so their sessions consume no discovered OAuth endpoint. The discovery
completeness gate still failed them closed with a 503 raised before the upstream
was ever contacted, which the tools/list fan-out swallowed into HTTP 200 with an
empty tool list. Any upstream that publishes no RFC 9728 metadata, including
every OpenAPI-backed internal API, was permanently unusable.

A failed discovery is now fatal only to servers whose OAuth flow the gateway
runs itself. Discovery still runs for the forwarding modes, so /authorize,
/token and /register keep resolving their endpoints, and each keeps its own 400
when they are missing.

MCPServer.is_client_forwarded_token now owns the mode pair that five call sites
spelled inline, one of which had already named it is_client_forwarded_mode.
2026-08-19 01:32:41 +00:00
Mateo Wang
704cc41f28
Merge pull request #37388 from BerriAI/litellm_lit_5718_mcp_tool_bound_to_server
fix(mcp): bind tool existence check to the selected server
2026-08-18 18:22:00 -07:00
ryan-crabbe-berri
4493c826e7
fix(logging): close three secret-leak paths in verbose logging (#37391)
* fix(logging): close three secret-leak paths in verbose logging

The AWS credential pattern was the only key-name matcher in secret_redaction
that skipped optional quotes, so quoted dict-repr values leaked. Fold the three
AWS key names into the shared key-name alternation instead.

SecretRedactionFilter only scrubs str record attributes, so a dict/list/set
passed through extra={...} reached the formatter unredacted. Redact at the
formatter boundary so no value shape can bypass it.

log_raw_request_response wrote the request curl command to metadata["raw_request"]
unredacted, returned an unmasked raw_request_api_base, and fell back to dumping
model_call_details whenever api_base was empty.

* Update litellm/_logging.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(logging): redact JSON log values without breaking the document

JsonFormatter redacted the serialized JSON, so a secret-named member
collapsed from '"api_key": "sk-..."' to a bare REDACTED token and the
line stopped parsing as JSON.

Redact before serialization instead: safe_dumps takes an optional
value_transform hook (default None, so all other callers are unchanged)
and redact_structured_value collapses only the value, leaving the key
and surrounding structure intact.

JsonFormatter now emits "api_key": "REDACTED" where the formatter unit
test expected the already-masked "sk**********". That test bypasses
SecretRedactionFilter, which in production collapses the pair before any
formatter runs, so the assertion is updated to match real behavior.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-08-19 01:03:34 +00:00
ryan-crabbe-berri
869a8cd984
fix(vector_stores): stop leaking stored credentials in direct search debug logs (#37373)
* fix(vector_stores): stop leaking stored credentials in direct search debug logs

Direct vector store providers (RESP datastores like Valkey) have no HTTP
request to echo, so both search handlers called `logging_obj.pre_call` with
no `api_base`. The logging helper treats an empty `api_base` as "nothing to
render" and falls back to `str(self.model_call_details)`, which carries the
resolved `litellm_params`: the stored `valkey_password` and the embedding
config's `api_key` among them.

The stdout logger's regex redaction hid this, but `pre_call` also writes the
same string to `litellm_params["metadata"]["raw_request"]`, which ships
unredacted to every logging callback (Langfuse, OTel, etc.).

Pass a synthetic `<provider>://<vector_store_id>` endpoint plus an explicit
`request_str` so the debug output describes the call instead of dumping call
details, and fold the duplicated sync/async blocks into one helper so the
sanitized descriptor cannot drift between them.

* fix(vector_stores): type direct search query as Sequence[str]

The new helper's list[str] annotation pushed LIT001 over its
type-discipline ceiling. Sequence is the read-only shape the helper
actually needs, and list[str] still satisfies it at both call sites.
2026-08-18 17:46:59 -07:00
mateo-berri
9018a95037 test(mcp): build fixture mapping state without in-place mutation 2026-08-18 17:35:40 -07:00
devin-ai-integration[bot]
3f15dc3287
fix(mcp): attach per-user BYOK credential when listing tools for non-oauth2 auth types (#34787)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-18 17:10:00 -07:00
yuneng-jiang
c33b3a32a6
feat(bedrock): add a config toggle to disable agent-runtime pass-through (#37386)
* feat(bedrock): add a config toggle to disable agent-runtime pass-through

The /bedrock pass-through dispatches agents, knowledge bases, flows, rerank,
retrieveAndGenerate, generateQuery and optimize-prompt to bedrock-agent-runtime,
so an operator who only wants to expose model invoke and converse has no way to
narrow that surface

Adds general_settings.disable_bedrock_agent_runtime_passthrough. When set, those
routes are rejected with a 403 before credentials are fetched or the request is
signed. Plain bedrock-runtime model pass-through is unaffected, and the setting
defaults to off, so existing deployments behave exactly as before

The branch is inverted to an early return for the non-agent-runtime case so the
toggle can reject outright instead of falling through to model extraction, which
would surface a confusing 400 about an unparseable model

* style(bedrock): drop redundant docstrings from the agent-runtime toggle
2026-08-18 17:05:40 -07:00
Mateo Wang
55777d0e80
Merge pull request #35110 from shivijain2323/feature/bedrock-mantle-quota-project-itr1
feat(proxy): add project-level ITPM and OTPM quotas
2026-08-18 16:54:33 -07:00
Mateo Wang
054aefce0d
Merge pull request #37387 from BerriAI/litellm_guardrail_usage_requeue
fix(guardrails): requeue usage rollup rows dropped after retry exhaustion
2026-08-18 16:42:06 -07:00
mateo-berri
a30e1f6e3d fix(mcp): bind tool existence check to the selected server 2026-08-18 16:41:40 -07:00
Mateo Wang
589f6859d8
Merge pull request #37356 from BerriAI/litellm_fix_v1_messages_double_content_block_stop
test(anthropic): pin one content_block_stop per tool_use block on the Responses adapter
2026-08-18 16:38:13 -07:00
yassin
17b72d5089 fix(search): send MCP-Protocol-Version on AgentCore gateway calls
Some checks failed
Terraform Modules / fmt, validate, test (aws) (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-08-18 23:26:41 +00:00
Yassin Kortam
2cf88d9a37
fix(proxy): send SSE keepalives on assistants runs and A2A streams (#37368)
Both surfaces wrote zero bytes for the whole time-to-first-token, so an
intermediary with an idle read timeout drops a healthy connection before the
first token. They reached neither keepalive engine, which is what #37322 left
open.

The streaming assistants run spends that wait inside the awaited call that
produces its response, since create_response buffers the first chunk, so it
takes the same open_sse_before_first_byte seam the native routes use. The A2A
route only contacts the upstream agent once its body iterator is first pulled,
so nothing is awaited before the response exists and the gap has to be filled
from inside the stream instead; wrap_sse_stream_with_keepalive_pings already
does that and now takes the filler as a parameter, so A2A gets an SSE comment
its JSON-RPC clients discard rather than Anthropic's ping event.

Off until an operator sets litellm_settings.sse_keepalive_ping_interval_seconds.
2026-08-18 16:22:05 -07:00
yucheng-berri
55ec491d03
fix(otel): bound and shut down credential-scoped tracer providers (#36591)
* fix(otel): bound and shut down credential-scoped tracer providers

Each credential-scoped TracerProvider owns a BatchSpanProcessor worker thread that
only stops on shutdown, and the v1 cache holding them was an unbounded, unsynchronized
dict that never shut anything down. Every distinct team/key credential set therefore
added a thread for the life of the process, and concurrent first-requests for the same
credential set orphaned duplicate providers outright.

Make the cache a lock-guarded bounded LRU that shuts down whatever it drops, matching
the v2 TenantTracerCache. Providers wrapping a caller-supplied SpanExporter instance
share that exporter with the logger's own provider, so they are dropped without
shutdown; those use SimpleSpanProcessor and own no thread.

* fix(otel): reclaim dropped providers on a dedicated executor

Sustained credential churn queues one blocking shutdown per eviction, so using the
shared logging executor let an unreachable tenant endpoint stall unrelated logging
work behind the OTLP retry budget. Give provider shutdown its own bounded pool; its
threads spawn lazily, so a proxy that never evicts still pays nothing.

* fix(otel): decide provider shutdown from the victim, not the evicting request

Both dynamic entry points share one provider cache, so it can hold providers of
mixed exporter ownership. Reading the ownership flag from the evicting request
therefore stopped a shared caller-supplied exporter in one direction, silencing
telemetry process-wide, and leaked a BatchSpanProcessor thread in the other.

Cache ownership alongside the provider so the drop decision reads the victim's
own flag.

* fix(otel): honor the widened header mapping type instead of dict only

Widening the header parameter to Mapping left the isinstance check on dict, so a
non-dict Mapping silently returned no headers at all, which for the OTLP path means
an unauthenticated exporter and no traces with nothing raised. The dict branch also
returned the caller's own object, and dropping the defensive copy at the call site
let that alias reach a long-lived exporter. Match on Mapping and copy.

* fix(otel): do not give a provider we may never stop an interpreter-exit hook

Every TracerProvider registers an atexit hook by default, and that hook holds a strong
reference. Providers wrapping a caller-supplied exporter are dropped without shutdown,
so they stayed pinned for the life of the process and then stopped the shared exporter
at exit. Tie shutdown_on_exit to ownership: those providers use SimpleSpanProcessor and
buffer nothing, so they lose no flush, while providers that own their exporter keep the
hook and their exit flush.

Also stop the victim the eviction test leaves behind, and trim the added comments.
2026-08-18 16:21:58 -07:00
mateo-berri
47f3cf804e fix(router): honor request-level tag filtering in pre-routing strategy selection
Key and team router_settings set enable_tag_filtering on the request kwargs,
and get_deployments_for_tag already treats that as authoritative, but
_select_pre_routing_strategy only consulted the router-wide flag, so tagged
auto-router markers still captured untagged requests from keys that enabled
filtering. The e2e auto-router module now enables tag filtering through
key-level router_settings instead of flipping /config/update module-wide,
which was denying concurrently running tagged requests from other suites on
the shared per-build CI proxy.
2026-08-18 16:19:43 -07:00
Devin AI
4a43b50800 test: add Final annotations to LIT-5757 regression test variables
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-18 23:19:38 +00:00
mateo-berri
42ddc5c535 fix(proxy): estimate image message tokens without fetching the image url 2026-08-18 16:18:23 -07:00
mateo-berri
c435c25da2 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_pr35110_itpm_otpm
# Conflicts:
#	type-discipline-budget.json
2026-08-18 16:11:12 -07:00
Mateo Wang
2903d3a02e
Merge pull request #37380 from BerriAI/litellm_cap_guardrail_usage_window
fix(guardrails): cap the date window accepted by /guardrails/usage endpoints
2026-08-18 16:01:51 -07:00
mateo-berri
5513fd032d fix(guardrails): requeue usage rollup rows dropped after retry exhaustion 2026-08-18 15:51:56 -07:00
mateo-berri
eb3ed6cf39 fix(guardrails): reject non-canonical date formats in usage windows 2026-08-18 15:46:09 -07:00
Mateo Wang
6c4059aacc
Merge pull request #37367 from BerriAI/litellm_lit_5527_semantic_cache_embedding_truncation
fix(caching): truncate semantic cache embedding input, send extra_body top-level
2026-08-18 15:45:30 -07:00
mateo-berri
3c34c34459 fix(proxy): guard candidate-count and batch cap coercion against float overflow 2026-08-18 15:42:02 -07:00
Devin AI
645b87fae1 fix(types): map nested prompt_tokens_details.cache_creation_input_tokens to cache_write_tokens
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-18 22:40:27 +00:00
mateo-berri
c9bfb7f0ab fix(guardrails): cap the date window accepted by /guardrails/usage endpoints 2026-08-18 15:37:57 -07:00
Mateo Wang
e75b4b1c2a
Merge pull request #37362 from BerriAI/litellm_lit_5651_bedrock_guardrail_cost
feat(guardrails): count bedrock guardrail cost against spend and budgets
2026-08-18 15:27:43 -07:00
Mateo Wang
6b7adf011e
Merge pull request #37355 from BerriAI/litellm_ultrafast_service_tier_cost
fix(cost_calculator): recognize the ultrafast service tier in cost calculation
2026-08-18 15:25:53 -07:00
mateo-berri
3894455c99 test(caching): annotate new semantic cache and hosted_vllm test helpers 2026-08-18 15:20:13 -07:00
mateo-berri
69ea1c6599 fix(proxy): coerce batch candidate counts like the live limiter path 2026-08-18 15:04:18 -07:00
mateo-berri
ef2c30227a fix(caching): truncate semantic cache embedding input, send extra_body top-level 2026-08-18 15:03:51 -07:00
mateo-berri
b849d073e0 fix(guardrails): bill completed chunks when a later chunk fails terminally
A terminal HTTP failure partway through chunking now logs the summed usage
and cost of the ApplyGuardrail calls AWS already billed, mirroring the
blocked-chunk path.
2026-08-18 15:02:11 -07:00
yucheng-berri
0b82b087fd
feat(team-callbacks): add DELETE /team/{team_id}/callback/{callback_name} (#37331)
Removes one named callback from a team and leaves the team's other callbacks
registered and firing. Before this, the only removal route was
POST /team/{team_id}/disable_logging, which clears every callback at once, so a
tenant sharing a team could not deregister its own integration

The handler filters metadata["logging"], keeps the survivors encrypted, refreshes
the cached team so the removal applies to keys that are already live, and emits a
redacted audit row, matching what the add and disable routes do

Resolves LIT-5161
2026-08-18 14:56:45 -07:00
yassin
cf2e50077c Merge branch 'litellm_internal_staging' into devin_ai_agentcore_search
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-18 21:53:22 +00:00
mateo-berri
354b0c3a45 fix(guardrails): bill all chunks on mid-chunking block, strip client guardrail cost metadata, add cost map schema keys
A blocked chunk now logs the summed usage and cost of every ApplyGuardrail
call AWS billed for the logical request, not just the blocking chunk.
Client-supplied metadata.standard_logging_guardrail_information is stripped
at the proxy boundary so callers cannot forge (even negative) guardrail
cost into spend, and guardrail_information_cost ignores negative or
non-finite entry costs as defense in depth. The cost map schema test now
allows guardrail_cost_per_unit and the guardrail mode.
2026-08-18 14:52:23 -07:00
mateo-berri
e9355a7fe9 fix(proxy): hand the embeddings failure hook the post-setup request data 2026-08-18 14:49:42 -07:00
ryan-crabbe-berri
28266d90e7
feat(vector_stores): add Valkey as a managed vector store provider (#37002)
* feat(vector_stores): add Valkey as a managed vector store provider

Adds a valkey provider for managed vector stores, searchable via the
valkey-search module over RESP. Introduces BaseDirectVectorStoreConfig
for datastores that execute searches directly instead of building an
HTTP request, and refactors the valkey semantic cache to share the new
connection URL helper. Registered in the provider enum, router params,
proxy config registry, Admin UI Add Vector Store modal, and provider
endpoint support matrix.

* fix(vector_stores): join list queries and bound valkey socket timeouts

Review feedback: multi-string queries are now space-joined like every
other embedding-based provider instead of dropping all but the first,
and the request timeout is threaded through the direct vector store
interface into bounded socket_connect_timeout / socket_timeout values
on both redis clients so an unreachable Valkey host cannot pin proxy
workers until the OS TCP timeout.

* chore(ui): regenerate schema.d.ts for valkey vector store fields

* docs(ui): make the Valkey vector store setup note and field tooltips explicit

* feat(ui): pick the Valkey embedding model from the proxy's models like Milvus

* fix(ui): number the setup steps in the vector store provider alerts
2026-08-18 21:45:22 +00:00
mateo-berri
72960d10e9 fix(proxy): address review findings on project ITPM/OTPM quotas
- scale batch output-token reservations by the row's n / best_of candidate count
- parse client-supplied output caps defensively instead of 500ing on unparseable values
- exclude project IO descriptors from the first should_rate_limit pass when TPM
  reservation is disabled so their buckets are not double-charged
2026-08-18 14:45:05 -07:00
Yassin Kortam
3fe0201d40
fix(proxy): let org admins view their organization's usage (#37235)
An internal user who administers an organization saw an empty
Organization Usage dashboard and had to be promoted to proxy admin to
see any of it.

Two independent gates were closed on them. The route layer rejected
GET /organization/daily/activity with 401 before the handler ran, since
the route belonged to no list a non-proxy-admin can reach, and the
handler's own org-admin scoping was therefore dead code. In the
dashboard, viewOrganizationUsage was granted by session role alone, and
an org admin's session role is internal_user, so the Organization Usage
option never rendered and its data fetch stayed disabled.

The route now sits in self_managed_routes, where the handler restricts
results to organizations the caller is ORG_ADMIN of and 403s on any
other org, and viewOrganizationUsage joins the existing per-capability
org-admin allowance that already covers viewDeletedTeams.

A caller who administers no organization resolves to an empty id list
rather than to None, so the organization-alias lookup is scoped by that
same list instead of reading the whole table.

The Usage page falls back to the global view when org-admin membership
is revoked while it is open, so the selector never keeps a value it no
longer offers.
2026-08-18 14:44:36 -07:00
Yassin Kortam
1857f5d04b
fix(proxy): send SSE keepalives while a slow upstream is still silent (#37322)
A model with a long time-to-first-token leaves the proxy's response completely
idle, so any hop with an idle read timeout (AWS ALB and nginx both default to
60s) drops a connection that is perfectly healthy and would have delivered its
tokens shortly after.

The keepalive engines LiteLLM already ships wrap the response object, so they
fill a gap once the upstream has answered and then gone quiet. They cannot fill
the gap before it answers at all, and that is where the whole wait is spent:
measured against api.openai.com/v1/chat/completions with gpt-5.6 at
reasoning_effort high, the response headers and the first body byte both arrive
at 37.90s. Nothing has entered the ASGI response phase by then.

The upstream call is now raced against the keepalive interval, and when it
loses, the SSE response is opened immediately and ": ping" comments, which every
conformant SSE client ignores, fill the wire until the real response is ready to
be replayed onto it. One seam per funnel: base_process_llm_request covers every
native route, create_pass_through_route covers every passthrough route.

Committing the status line that early is the cost. A failure discovered after
the first ping reaches the client as an SSE error frame under a 200 rather than
as an HTTP error status, and LiteLLM's own x-litellm-* response headers are not
yet known. keepalive_ping_has_fired already documents the same trade-off for the
existing engines. Both are why this stays off until an operator sets
litellm_settings.sse_keepalive_ping_interval_seconds.

Separately, the passthrough relay reached neither engine even for mid-stream
gaps, which is the shape of #32491 and #24929, so the relayed bytes get the same
treatment, gated on the upstream declaring text/event-stream and only emitted
between complete frames so a binary transport (AWS event streams on /bedrock)
and a stall halfway through a frame are both left alone.

Fixes #34819
2026-08-18 14:43:01 -07:00
Yassin Kortam
49b72e14da
fix(anthropic): emit tool_use content_block_start without awaiting the next chunk (#37310)
On /v1/messages, AnthropicStreamWrapper synthesizes the content_block_start for
the first content block, queues it, then hits a bare `continue` when that same
upstream chunk's translated delta is empty. The queue is only drained at the top
of the next __next__ / __anext__, so the queued content_block_start waits for a
further upstream chunk to arrive.

An empty delta on the opening chunk is the normal tool-call shape: Bedrock
Converse's contentBlockStart carries the tool id and name with no arguments, and
OpenAI-format streams send arguments: "" on the chunk that names the function.
So a client learns a tool call started one upstream event late, and when the
provider delivers argument fragments as a trailing burst it sees nothing at all
after message_start for the whole generation.

Flush the queued event before continuing, in both the sync and async paths. The
sibling block-transition path already returns from the queue, so only the
first-block-open case changed.
2026-08-18 14:42:42 -07:00
mateo-berri
3a4d3a01af fix(proxy): only estimate failed-request input tokens for call types whose input is countable 2026-08-18 14:36:57 -07:00
Mateo Wang
5d1401342a
Merge pull request #33195 from Sujithr07/fix/33184-store-prompt-cache-key
fix(main): forward store and prompt_cache_key params on chat completions
2026-08-18 14:22:00 -07:00
mateo-berri
803113c63a fix(proxy): estimate failed-request input tokens on /v1/messages and count system prompts
The Anthropic messages endpoint's exception handler passed the raw
request body dict to the failure hook, but request setup had already
replaced the processor's dict with one carrying the logging object, so
failure rows for /v1/messages never lifted recovered or estimated usage.
Pass the processor's dict instead.

The input-side estimate only counted the messages list, missing the
Anthropic top-level system prompt (string or text-block list) and the
Responses API instructions field, which live in optional_params. Count
them too.
2026-08-18 14:21:47 -07:00