Commit graph

1884 commits

Author SHA1 Message Date
Yassin Kortam
66bc70365f
fix(caching): close evicted LLM clients so their connections are reclaimed (#35492)
An evicted client was left for the garbage collector, but every OpenAI/Azure
SDK client is a reference cycle, so nothing freed the client or its pooled TCP
connections until a generational sweep ran. Driving 2000 azure calls through
the official image with no forced collection, live clients and open sockets
climbed from 202 to 1361 while the cache stayed at its 200-entry bound, and RSS
grew 279 MB to 456 MB against a TLS upstream.

Closing on eviction is what caused the earlier 'Cannot send a request, as the
client has been closed' regression, so an evicted client litellm created is now
closed only once a grace window has passed, by which point any request that was
already holding it has finished. A client the caller supplied is never closed,
since litellm does not own its lifecycle.

Resolves LIT-4883
2026-08-03 13:28:38 -07:00
Yassin Kortam
14dd98cd5f
fix(bedrock): cache AssumeRole credentials per attributed identity (#35467)
The explicit AssumeRole branch of BaseAWSLLM.get_credentials returned without
touching the process-wide IAM cache, so every model request issued a fresh
sts:AssumeRole, and on ECS/EC2 an uncached sts:GetCallerIdentity ahead of it.
Route the whole role branch through _get_or_set_cached_credentials with the TTL
_auth_with_aws_role already computed and discarded. The cache key is the same
aws_* argument snapshot the other flows use, taken before the session-name
default is filled in, so each aws_session_name keeps its own STS session and no
attributed identity can be served another's credentials.

Credential fetches now single-flight behind striped locks. Without that, a burst
of concurrent misses on one key each issued their own STS call, which is the
same thundering herd the cache exists to prevent, moved to the miss window.
2026-08-01 13:54:42 -07:00
Mateo Wang
2ef84db550
Merge pull request #35004 from mgeorgaklis/fix/gemini-thought-signature-duplication
fix(gemini): do not send duplicate thoughtSignature copies to Gemini
2026-07-31 11:51:18 -07:00
Yassin Kortam
16507f1174
fix(aiohttp): dispose recycled client sessions deterministically (#33428)
* fix(aiohttp): dispose recycled client sessions deterministically

LiteLLMAiohttpTransport replaced its cached aiohttp.ClientSession on
loop-mismatch, loop-inspection failure, and "Session is closed" retry
without reliably closing the previous session:

- the close task from asyncio.create_task() was never referenced, so
  it could be garbage-collected before running;
- the (RuntimeError, AttributeError) fallback branch replaced the
  session without closing it at all;
- sessions bound to a closed event loop were abandoned to the GC
  ("rely on GC"), and sessions bound to a loop running in another
  thread were closed from the wrong loop.

Replaced sessions surfaced as intermittent "Unclosed client session" /
"Unclosed connector" errors from the event-loop exception handler at
GC time.

_close_recycled_session() now covers the three lifecycles a recycled
session can be in: same-loop closes keep a strong task reference until
completion; sessions owned by a loop running elsewhere are closed on
their own loop via run_coroutine_threadsafe; sessions whose loop is
gone are disposed synchronously through the connector teardown that
aiohttp's own finalizer uses, which releases pooled connections and
silences the finalizer warnings.

Fixes #24230

* fix(aiohttp): guard threadsafe close callback against cancelled futures

---------

Co-authored-by: Anmol Jaiswal <68013660+anmolg1997@users.noreply.github.com>
2026-07-31 16:48:08 +00:00
mateo-berri
3e3a35dabf test(pricing): cover gpt-5.6 cache-cost plumbing and bedrock_mantle responses billing 2026-07-30 21:57:20 -07:00
Mateo Wang
bf1a8fe403
Merge pull request #35270 from BerriAI/litellm_gpt_pricing_change
fix(pricing): correct gpt-5.6 prices for openai, bedrock, and flex long context
2026-07-30 21:46:46 -07:00
yucheng-berri
1018d18e6b
fix(anthropic): split mixed stream chunks by payload kind (#35289)
* fix(anthropic): split mixed reasoning stream chunks

* style: use builtin generic annotation

* fix(anthropic): split mixed stream chunks by payload kind

The mixed-chunk split cleared only the fields it knew about on each
deep-copied piece, so any other payload riding the chunk survived on
both pieces: tool_calls were emitted as two tool_use blocks with the
same id, thinking_blocks on the text piece emitted duplicated thinking
into a text block while dropping the answer text, and chunks whose
reasoning arrived only as thinking_blocks never split at all

Rebuild each piece's delta from scratch with exactly one payload kind
(reasoning, text, tool calls), ordered to match native Anthropic block
order. Fresh Delta construction keeps unset attributes deleted, which
matters because the translators branch on hasattr, and prevents future
Delta fields from riding along on every piece

* fix(anthropic): keep continuation and multi-choice chunks unsplit, emit signature-less thinking once

Adversarial verification against the merge-base found three shapes where
the payload-kind split changed behavior beyond its target: a mixed chunk
carrying a tool argument continuation was torn into a truncated block
plus a fabricated one, a multi-choice chunk lost its secondary choices'
payload, and a signature-less thinking_blocks piece inherited the
non-empty block start body so accumulators collected the thinking twice

Continuation and multi-choice chunks now pass through the splitter
untouched, matching the merge-base byte for byte, and signature-less
thinking_blocks pieces are normalized to reasoning_content so the block
start opens empty and the thinking text is emitted exactly once

---------

Co-authored-by: Napuh <naamanynadiemas@gmail.com>
2026-07-31 02:05:18 +00:00
Mateo Wang
c3da12161b
Merge pull request #35174 from BerriAI/litellm_fix_fireworks_kimi_output_limits
fix(fireworks_ai): correct Kimi K2.5/K2.6/K2.7 max output token limits
2026-07-30 17:17:11 -07:00
mubashir1osmani
6aea561319 fix(pricing): correct bedrock_mantle gpt-5.6 terra/luna prices after OpenAI's cut
AWS rolled out the 2026-07-30 GPT-5.6 price cut the same day, but the
bedrock_mantle entries still carried values derived from the pre-cut OpenAI
base, so Terra billed 1.25x and Luna 5x over the published rate.

Re-derive both from the AWS Bedrock pricing page, which prices in-region
inference at parity with OpenAI's data residency tier (1.1x base). Sol was
not cut and is unchanged.

Also drop tests/test_litellm/test_gpt_5_6_model_metadata.py; its Azure and
openai pricing assertions are covered by test_llm_cost_calc_utils.py.
2026-07-30 13:29:44 -07:00
Mateo Wang
4d54324515
Merge pull request #35188 from BerriAI/litellm_remove_dead_bedrock_invoke_path
refactor(bedrock): remove the dead BedrockLLM invoke code path
2026-07-29 21:21:25 -07:00
mateo-berri
a895249923 refactor(bedrock): remove the dead BedrockLLM invoke code path 2026-07-29 20:25:36 -07:00
Mateo Wang
47f1fb394e
Merge pull request #35172 from BerriAI/litellm_vertex_cache_skip_tool_final
fix(vertex_ai): skip context caching when the cached block ends on a model turn
2026-07-29 20:24:42 -07:00
mateo-berri
819dc7812a fix(vertex_ai): evaluate cached-block terminal turn after system extraction 2026-07-29 19:47:26 -07:00
Mateo Wang
9f9d72b50f
Merge pull request #34603 from ljogeiger/litellm_vertex_function_call_id
fix(vertex_ai): forward function_call id on Vertex Gemini 3+ tool turns
2026-07-29 18:51:54 -07:00
mateo
f9c5be8ebf fix(fireworks_ai): correct Kimi K2.5/K2.6/K2.7 max output token limits
Fireworks publishes a 262144-token context window for the Kimi K2.5, K2.6
and K2.7 models but caps generation well below that. Every fireworks_ai
Kimi K2.5/K2.6/K2.7 alias had max_output_tokens/max_tokens flattened to
262144 (equal to the context window), so the pre-call context-window check
admitted requests asking for a full 262144-token completion that Fireworks
rejects. Correct max_output_tokens/max_tokens to 32768 while keeping
max_input_tokens at 262144, and add a regression test pinning the limits
for all ten aliases.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-30 01:43:59 +00:00
mateo-berri
dbc0d23c1e fix(vertex_ai): skip context caching when the cached block ends on a model turn 2026-07-29 18:38:29 -07:00
Mateo Wang
6fe1e73699
Merge pull request #34847 from BerriAI/litellm_fix_vertex_batch_read_per_model_bucket
fix(vertex_ai): honor per-model gcs_bucket_name on managed-file read path
2026-07-29 18:10:47 -07:00
Napuh
2f7574d7c1
fix(anthropic-adapter): open the first content block with the real upstream type so reasoning-first streams start with thinking (#34433)
* fix(anthropic-adapter): open first content block with the real upstream type

* fix(anthropic): defer blank leading stream deltas
2026-07-28 21:51:59 -07:00
mgeorgaklis
987a8fcf48 fix(gemini): do not send duplicate thoughtSignature copies to Gemini
Gemini returns each thoughtSignature on exactly one part. LiteLLM
stores a function-call signature both message-level (thought_signatures)
and on the tool call itself, then re-attached it to BOTH the text part
and the function-call part when serializing history. gemini-3 and newer
models bill every replayed copy as the previous turn's full reasoning
token count, so long agentic sessions doubled their context growth and
hit the 1,048,576-token limit

Only attach a message-level signature to the text part when the same
signature is not already carried by a tool-call part:

- compare signature values instead of boolean presence so a distinct
  text-part signature is never dropped
- ignore the gemini-3 dummy-signature fallback during detection so
  replaying gemini-2.5 history to a newer model keeps the real text
  signature
- count signatures carried by server-side tool invocations so they are
  not re-attached to the text part

gemini-2.5 responses (signature on the text part, function call
unsigned) are unaffected: the text signature is preserved as before
2026-07-29 04:08:19 +00:00
tin-berri
32a4377acd
Merge pull request #34589 from BerriAI/litellm_lit4798_glm_stop_thinking
fix(anthropic-adapter): translate stop_sequences and disabled thinking for non-Claude targets
2026-07-28 17:42:27 -07:00
Yassin Kortam
caede1c5a0
fix(aiohttp): keep keep-alive connector config when a session is rebuilt (#34962) 2026-07-28 16:18:34 -07:00
shivam
5f50791e99 fix(vertex_ai): source managed-file read bucket + credentials from per-model litellm_params
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-27 23:33:01 +00:00
mateo-berri
8b08c31ebe test: cover volcengine responses and openai evals transformations
Exercises the streaming field-fill heuristics, model_construct fallbacks,
and the get/cancel/delete/list request and response transforms that had no
tests.
2026-07-27 12:57:21 -07:00
yucheng-berri
1776daa267
fix(bedrock): stop replaying expired Google OIDC tokens to STS on guardrail auth (#34637)
Cache web identity STS credentials in the shared IAM cache (restores the
pre-v1.85.0 behavior removed by #27125) and cap the Google OIDC token cache
TTL at the token's own exp claim minus a 60s margin, never caching an
already-expired token
2026-07-25 16:27:54 -07:00
ryan-crabbe-berri
998a372417
test: stop bedrock tool acompletion tests from making real network calls (#34644) 2026-07-25 19:01:22 +00:00
devin-ai-integration[bot]
96f58fac53
fix(router): don't cool down parent deployment on advisor sub-call failure (#33792)
* fix(router): don't cool down parent deployment on advisor sub-call failure

Advisor orchestration issues a sub-call to a different provider/credentials than the selected deployment. When that sub-call fails (e.g. a 401 because no advisor API key is configured), the exception propagates up and the router's deployment_callback_on_failure attributes it to the healthy parent deployment's model_info.id, cooling it down and rejecting unrelated callers to the same model group.

Tag advisor sub-call failures on the exception and skip cooldown for them in deployment_callback_on_failure. The exception is tagged rather than wrapped so its type is preserved and retry/fallback classification and the client-facing error are unchanged. Genuine executor/deployment failures are untagged and still cool down as before.

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

* refactor(router): tag advisor orchestration failures via provider-neutral util

Address review on LIT-4565: move the cooldown-exemption marker into
litellm/router_utils/cooldown_handlers.py so the router imports it at
module top instead of an in-function anthropic import, and extend the
exemption to AdvisorMaxIterationsError so a max-iterations orchestration
failure no longer cools down the healthy executor deployment.

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

---------

Co-authored-by: shivam <shivam@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-25 10:17:13 -07:00
Lukas Geiger
acd414f186 fix(vertex_ai): forward function_call id on Vertex Gemini 3+ tool turns
Vertex AI now accepts and returns `id` on functionCall and functionResponse parts for Gemini 3+ on the v1 endpoint, so the provider check added in #28324 is stale. It silently drops the id for every Vertex caller, which breaks strict tool-call matching

Gate the id on model version alone, which is what the code did before #28324 and what Google AI Studio already does. `_forward_gemini_function_call_id` no longer takes `custom_llm_provider`, and the decision is resolved once in `_gemini_convert_messages_with_history` and passed to both converters as a bool rather than re-derived independently in each. The context caching path is covered by the same change, since it already passes `model` and the gate needs nothing else

The `id` comments on `FunctionCall`, `FunctionResponse` and `HttpxFunctionCall` were also written by #28324 and asserted the opposite of current behaviour, so they are corrected here
2026-07-25 05:39:23 +00:00
Tin Chi Lo
5072590c27 fix(anthropic-adapter): dedupe reasoning_effort wrapping to close sibling gap
translate_thinking_for_model duplicated the same summary/auto_summary
wrapping logic as _translate_thinking_to_openai without the
disabled-thinking guard, so it could still wrap "none" into an
{effort, summary} dict when reasoning_auto_summary is enabled (caught
by Cursor Bugbot). Extract the wrapping rule into one shared
_apply_reasoning_summary_wrapping helper used by both call sites so
this invariant can't drift apart again.
2026-07-24 19:31:29 -07:00
Mateo Wang
7b019cf152
Merge pull request #34446 from BerriAI/litellm_fix_mantle_missing_usage
fix(bedrock-mantle): backfill usage on non-streaming /v1/messages responses
2026-07-24 19:05:40 -07:00
Tin Chi Lo
fed03a41d1 test(anthropic-adapter): cover empty stop_sequences edge case
Codecov flagged the empty-list early-return in
_translate_stop_sequences_to_openai as an uncovered line in the diff —
add a regression test asserting stop_sequences=[] does not set
new_kwargs["stop"].
2026-07-24 19:00:20 -07:00
Tin Chi Lo
9da21f38a9 fix(anthropic-adapter): keep disabled-thinking reasoning_effort a plain string
Guard against reasoning_auto_summary wrapping "none" into a dict when
thinking is disabled — there's no reasoning trace to summarize, and
non-Claude providers (e.g. Fireworks) expect reasoning_effort as a
plain string.
2026-07-24 18:48:07 -07:00
Tin Chi Lo
b3e27a0bc3 fix(anthropic-adapter): translate stop_sequences and disabled thinking for non-Claude targets
Claude Code's auto-mode classifier sends stop_sequences and thinking:
{type: disabled} on /v1/messages. The Anthropic adapter passed
stop_sequences through unchanged instead of mapping it to OpenAI's stop,
which Fireworks' OpenAI-compatible endpoint rejects with HTTP 400. It also
dropped disabled thinking instead of mapping it to reasoning_effort: none,
so the model spent its output budget on reasoning it was told to skip.

Resolves LIT-4798
2026-07-24 18:48:07 -07:00
yucheng-berri
76b0b10908
fix(guardrails): add /v1/messages support for Straiker plugin (#34548)
* fix(guardrails): add /v1/messages support for Straiker plugin

- Pass prepared response data to Anthropic Messages streaming post-call hooks (litellm/llms/anthropic/chat/guardrail_translation/handler.py)
- Normalize Straiker request, tool, finish-reason, and mode fields across Chat Completions, Messages, and Responses APIs

* fix(guardrails): gate cross-surface message resolution and cover streaming request data

Resolve request messages only for surfaces that have a mapped translation
handler. The unguarded fallback tried every registered handler in turn, which
raised AttributeError out of the guardrail's error handling on list-shaped
`input` bodies, and synthesized a chat message that was never sent for bodies
it happened to parse.

Prepare request data on the mid-stream Anthropic branch as well, matching the
terminal branch and the OpenAI handler, so guardrails that scan before
end-of-stream still receive identity metadata.

Read usage from Anthropic dict responses so non-streaming /v1/messages reports
token counts instead of null.

Add regression coverage for the streaming request data on both the terminal and
mid-stream branches; reverting either now fails.

---------

Co-authored-by: cs-mehta <chandra@straiker.ai>
2026-07-24 17:13:11 -07:00
Mateo Wang
cfb7edb54e
Merge pull request #34549 from BerriAI/litellm_fix_stream_options_responses_api
fix(responses): strip include_usage from stream_options instead of dropping the param
2026-07-24 17:12:26 -07:00
yuneng-jiang
7047a37f2f
Merge pull request #34475 from BerriAI/litellm_/test-coverage-mutation-analysis-e42223
test: remove tests that mutation analysis proved assert nothing
2026-07-24 16:23:34 -07:00
Yuneng Jiang
9f9714c209
test: remove five more zero-kill tests from test_http_handler
The http_handler pair only received its full mutation verdict after the
first removal batch landed; these five tests pass unchanged when every
function they execute is mutated and the owning file killed none of
their scored mutants. The ssl tests excluded from mutation scoring are
untouched.
2026-07-24 16:05:18 -07:00
yucheng-berri
61d32c9aac
fix: handle explicit outputInfo: null in Vertex AI batch response (#34473)
* fix: handle explicit outputInfo: null in Vertex AI batch response

Vertex AI can return HTTP 200 for a create_batch/get_batch call with an
explicit "outputInfo": null body (the output directory is assigned
asynchronously and may not be populated yet at response time).

_get_output_file_id_from_vertex_ai_batch_response did:

    response.get("outputInfo", OutputInfo()).get("gcsOutputDirectory", "")

dict.get(key, default) only substitutes default when the key is absent,
not when it is present but explicitly None, so this crashed with:

    AttributeError: 'NoneType' object has no attribute 'get'

surfaced to callers as an opaque openai.InternalServerError 500 from
litellm.create_batch()/retrieve_batch() for any Vertex AI batch job,
regardless of whether the job ultimately succeeds.

Fixed by guarding with `response.get("outputInfo") or OutputInfo()`,
matching the existing null-safe pattern already used by the sibling
_get_input_file_id_from_vertex_ai_batch_response for inputConfig. The
existing outputConfig fallback branch (a few lines below) already
handles this case correctly once it's reachable - it just never was.

Added 2 regression tests covering outputInfo: null with and without an
outputConfig fallback available.

* test: drop explanatory comment from regression test

---------

Co-authored-by: htourinho-clgx <htourinho@cotality.com>
2026-07-24 15:10:16 -07:00
shivam
a376f72400 fix(responses): stop treating stream_options as a Responses API param
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-24 20:53:05 +00:00
Yassin Kortam
f6a1050cbf
fix(vertex): incrementally parse accumulated Gemini stream JSON to prevent multi-value wedge (#34320)
The accumulated-JSON fallback ran json.loads over the whole buffer after every fragment and, on failure, kept the buffer without resetting it. A buffer that ever held more than one concatenated JSON value could never parse (json raises on trailing data), so it returned None on every subsequent chunk while growing without bound - an unrecoverable per-request CPU spin. Parse one value at a time from the front with raw_decode and keep the remainder, draining trailing values on later calls and at end of stream.
2026-07-24 11:07:02 -07:00
Yassin Kortam
692b22655e
fix(logging): stop scheduling sync failure_handler concurrently with async_failure_handler (#34306)
The async streaming error paths fired the sync failure_handler in a thread
and the async_failure_handler via create_task at the same time, so both
mutated the shared logging object concurrently and could crash pydantic-core.
Route failure logging through a single guarded dispatch_failure_handlers, so
the sync handler only runs after the async one completes.
2026-07-24 11:06:55 -07:00
Yuneng Jiang
4db6955451
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/test-coverage-mutation-analysis-e42223 2026-07-23 23:52:11 -07:00
Yuneng Jiang
58e87985e5
test: remove tests that mutation analysis proved assert nothing
25 test functions across three files pass unchanged when every function
they execute is mutated; the owning file killed zero of their scored
mutants. Four zero-kill tests tied to the fix in #31288 are kept for
rewrite instead of removal.
2026-07-23 23:52:09 -07:00
shivam
ee9f0db1cf fix(bedrock-mantle): backfill usage on non-streaming /v1/messages responses
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-23 23:37:40 +00:00
Mateo Wang
3bba3633c7
Merge pull request #34338 from BerriAI/litellm_lit_4313_sagemaker_chat_streaming_ttft
fix(sagemaker): forward stream events as they arrive to cut TTFT
2026-07-23 00:37:39 -07:00
Mateo Wang
86eee8bd05
Merge pull request #34319 from BerriAI/litellm_anthropic_output_format_remaining_keywords
fix(anthropic): strip all remaining output_format schema keywords rejected by Anthropic
2026-07-22 23:29:37 -07:00
mateo
5bc1df5e47 test(sagemaker): assert make_sync_call maps non-200 to SagemakerError
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-23 04:31:11 +00:00
mateo
a63884bc8d test(sagemaker): cover sync native streaming path via injectable make_sync_call
Extract the inline sync streaming post/decode into make_sync_call so it can be
exercised with an injected client, mirroring make_async_call, and add a sync
regression test that each token is forwarded after exactly one pulled frame.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-23 04:21:12 +00:00
mateo
27c91e6574 fix(sagemaker): forward native streaming events as they arrive to cut TTFT
Mirror the sagemaker_chat fix on the native sagemaker/ streaming path: the
sync and async completion handlers read the invocations-response-stream body
with iter_bytes(chunk_size=1024) / aiter_bytes(chunk_size=1024), so httpx
withholds bytes until 1024 accumulate and tokens arrive in gap-then-burst
waves. Drop the fixed chunk size so each decoded event is forwarded as its
bytes arrive.

Also add a boundary-agnostic decoder test proving frames reassemble correctly
regardless of where transport reads split the stream.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-23 04:03:25 +00:00
mateo-berri
16dad256d4 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_anthropic_output_format_remaining_keywords 2026-07-22 19:59:09 -07:00
mateo-berri
75c0e12dac fix(bedrock): let parallel_tool_calls-derived disable flag win over raw tool_choice value 2026-07-22 19:52:54 -07:00