Commit graph

231 commits

Author SHA1 Message Date
Mateo Wang
80250807db
Merge pull request #38808 from BerriAI/litellm_headroom_ccr_streaming_responses
fix(headroom): resolve CCR retrieval on streaming /v1/responses
2026-09-03 13:13:18 -07:00
Mateo Wang
1e2d6abc18
Merge pull request #39614 from BerriAI/litellm_fix_stream_usage_default_openai_hosts
fix(openai): default stream usage on PrivateLink and regional api.openai.com hosts
2026-09-03 13:13:00 -07:00
mateo-berri
1e75668a25 fix(openai): default stream usage on PrivateLink and regional api.openai.com hosts 2026-09-03 12:37:17 -07:00
mateo-berri
ec2e35b679 fix(image_gen): keep the provider's echoed size, quality, and output_format on gpt-image responses 2026-09-03 11:07:08 -07:00
mateo-berri
4d3c1998af fix(image_gen): report the requested output_format on gpt-image responses 2026-09-03 10:50:23 -07:00
Mateo Wang
4f7b20ec10
fix(guardrails): skip streaming guardrail rounds that re-scan cleared output (#39386)
* fix(guardrails): skip streaming guardrail rounds that re-scan cleared output

Streaming guardrails scanned the finished answer twice at end of stream
whenever the chunk count landed on a multiple of the sampling rate, ran
sampled rounds whose payload was identical to the previous one, and on
/v1/messages could scan an empty text before the first content chunk.
Every redundant round is a paid guardrail provider call.

Each endpoint handler now exposes a scan key describing what a round
would hand to apply_guardrail (the text so far, plus tool calls once the
stream has ended), and the unified streaming hook skips a sampled or
end-of-stream round whose key equals the last scanned one or carries
nothing to scan yet. Rounds that carry tool calls are never skipped.

* test(guardrails): expect one end-of-stream scan when the terminal chunk is sampled

Update sampled cadence expectations and use tuple-backed scan state

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-02 18:25:28 -07:00
yassin
6d5a3ab42c merge: litellm_internal_staging into litellm_headroom_ccr_streaming_responses
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-03 00:24:17 +00:00
mateo-berri
7cbc74399d fix(responses): accept pydantic tool objects returned by guardrails 2026-09-02 15:24:43 -07:00
mateo-berri
5561b8438c fix(responses): keep a namespace's non-function members when every function member is dropped 2026-09-02 14:18:16 -07:00
mateo-berri
dc12e4c2b4 fix(responses): match guardrail tools by ordinal in one pass
Sort the chat-tool keys once and number duplicates with groupby instead of
rescanning every preceding key per position, so the guardrail merge stays
O(n log n) on client-supplied tool lists. Drop the comment that restated the
unsupported-tool warning in the Responses-to-chat transformation.
2026-09-02 12:57:01 -07:00
mateo-berri
d7ee215c57 fix(responses): keep namespace tools intact when a guardrail returns them unchanged
Any pre_call guardrail on /v1/responses flattened Codex namespace tools
into ns__member functions and wrote the flattened list back to the
request, so the model called mcp__server__tool with no namespace and
Codex rejected the call as unsupported.

The handler now keeps the client's original tools, hands the guardrail a
deep copy of the flattened ones, and rebuilds data["tools"] by matching
the guardrail's output to the originals by type and name. Unchanged
tools go back as the original objects, a dropped or edited namespace
member changes only that member, and tools the guardrail injects are
still appended.

Fixes #39183
2026-09-02 11:32:04 -07:00
mateo-berri
4a5d0b8163 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_headroom_ccr_streaming_responses
# Conflicts:
#	litellm/llms/openai/responses/guardrail_translation/handler.py
#	tests/test_litellm/llms/openai/responses/test_openai_responses_guardrail_handler.py
2026-09-01 22:25:59 -07:00
mateo-berri
8c7fe00d80 fix: compare stream event types by equality so typed completed events keep their usage 2026-09-01 17:37:07 -07:00
mateo-berri
a38dfecd96 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_stream_modify_response_chunks 2026-09-01 14:49:06 -07:00
Mateo Wang
deb67ce6e2 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_bedrock_buffered_responses_stream
# Conflicts:
#	tests/test_litellm/proxy/guardrails/guardrail_hooks/test_bedrock_guardrails.py
2026-09-01 14:11:29 -07:00
Mateo Wang
286a754999
Merge pull request #38839 from BerriAI/litellm_fix_chat_anyof_tool_schema
fix(openai): flatten top-level tool schema combinators on chat completions
2026-09-01 13:25:03 -07:00
Mateo Wang
8ae072b501
Merge pull request #39147 from BerriAI/litellm_openai_drop_toolless_tool_choice
fix(openai): drop tool_choice when request has no tools on chat completions
2026-09-01 12:50:38 -07:00
mateo-berri
4567fc784c fix: close non-message open items as incomplete when a stream is blocked 2026-09-01 12:45:23 -07:00
mateo-berri
4c7dd0b522 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_stream_modify_response_chunks
# Conflicts:
#	type-discipline-budget.json
2026-09-01 12:36:48 -07:00
mateo-berri
c01ef712a2 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_headroom_ccr_streaming_responses
# Conflicts:
#	tests/test_litellm/responses/litellm_completion_transformation/test_streaming_iterator_transformation.py
2026-09-01 11:27:03 -07:00
mateo-berri
93a03a9ffd fix(openai): drop tool_choice when request has no tools on chat completions 2026-09-01 11:16:56 -07:00
mateo-berri
ab2c9aed0f fix(responses): normalize tool call id shapes across the anthropic bridge and openai replay
The chat-completions bridge emitted Responses output items whose item ids
were raw Anthropic tool ids (toolu_/srvtoolu_), which OpenAI rejects on
replay with "Expected an ID that begins with 'fc'", breaking router
fallback conversations from gpt-5 to claude models.

Four fixes, composable and independently useful:
- emission: bridge output items get fc_/ctc_-prefixed item ids while
  call_id stays raw so tool_result pairing keeps working (streaming and
  non-streaming share the same helpers)
- openai replay: request transformation drops tool call item ids that do
  not match OpenAI's own shapes instead of forwarding them, gated to
  OpenAI and Azure, since the API accepts the items with no id at all
- anthropic replay: a replayed srvtoolu_ call whose paired server tool
  result is unavailable degrades to a plain client tool_use instead of a
  dangling server_tool_use that 400s the client's tool_result
- tool-only turns no longer emit a message output item with output_text
  text null, matching native OpenAI output
2026-09-01 11:12:24 -07:00
mateo-berri
0a9676bd4f fix(openai): scope unknown-model reasoning_effort forwarding to the plain openai provider 2026-08-31 21:42:31 -07:00
mateo-berri
a03378f6d1 fix(openai): forward reasoning_effort for unknown model aliases instead of failing closed 2026-08-31 21:14:54 -07:00
mateo-berri
38825cf9c6 fix(guardrails): match Responses stream event types by value so enum-typed events close the open item 2026-08-31 17:19:55 -07:00
mateo-berri
78b57fb427 fix(guardrails): withhold chat finish chunk in end_of_stream_only mode and close open Responses items before a mid-stream block 2026-08-31 17:05:45 -07:00
mateo-berri
158220f151 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_stream_modify_response_chunks 2026-08-31 16:20:29 -07:00
mateo-berri
7edf5b36cf fix(guardrails): deliver modify_response block as valid SSE on streaming chat and Responses
A guardrail modify_response verdict on a streaming request only produced a
proper replacement on /v1/messages: the chat completions and Responses API
translations had no build_block_sse_chunks, so the ModifyResponseException
re-raised and surfaced as an in-stream 500 error frame (or a whole-request
500 in buffered mode) instead of the documented 200 replacement.

Implement build_block_sse_chunks for both OpenAI translations: chat emits a
content delta plus a finish_reason content_filter chunk with real usage;
Responses emits the typed event sequence (standalone via
build_synthetic_response_events pre-stream, or an output-item continuation
under the in-progress response id mid-stream) ending in response.completed.
2026-08-31 16:01:39 -07:00
Mateo Wang
b518be45fb
Merge pull request #38997 from BerriAI/litellm_add_responses_input_tokens_endpoint
feat(proxy): add /v1/responses/input_tokens token counting endpoint
2026-08-31 14:30:44 -07:00
mateo-berri
c9908ffabb fix(responses): count input_file tokens instead of silently dropping the file
The Responses-to-chat transform dropped the filename OpenAI requires next to
file_data, so a request carrying an inline PDF counted 13 tokens instead of 36
and a real completion through the chat bridge got a 400.
2026-08-31 13:17:43 -07:00
mateo-berri
fe90c6f6fc fix(count_tokens): keep assistant turns on the provider counting API
Assistant list content was forwarded to /v1/responses/input_tokens as chat
`text` blocks, which the Responses API rejects (it accepts only output_text
and refusal inside an assistant turn). The 400 sent the whole request to the
local tokenizer, so any conversation with an assistant turn silently lost
provider-exact counting, including the image counting added in 73ab647b1c.

Assistant content now collapses to the plain string the Responses API counts
identically, and image parts are kept to user turns where they are legal.
2026-08-31 12:59:40 -07:00
Cursor Agent
ae83444a3e
fix(openai): treat empty api_key as unset for WIF resolution 2026-08-31 19:55:29 +00:00
mateo-berri
e7dc0213bd fix(openai): treat empty api key values as unset for workload identity 2026-08-31 12:52:55 -07:00
mateo-berri
73ab647b1c fix(count_tokens): preserve image inputs when counting Responses API tokens
The chat-to-Responses reverse transform kept only text blocks, so an image
input was dropped before the count went to OpenAI. A 256x256 image request
counted 13 tokens instead of 268.
2026-08-31 12:43:17 -07:00
mateo-berri
ef72e7b37d fix(openai): require https for workload identity api_base targets 2026-08-31 12:32:00 -07:00
mateo-berri
72adeda9ce fix(openai): scope workload identity to the openai provider and env-resolved base/key 2026-08-31 12:15:52 -07:00
mateo-berri
ae945f4fa3 feat(openai): support workload identity federation (OIDC token exchange) 2026-08-31 11:54:04 -07:00
mateo-berri
db1e0717f9 fix(guardrail_translation): assemble responses stream text from delta events for terminal-failure scans 2026-08-30 12:52:03 -07:00
mateo-berri
4261198b2f Merge branch 'litellm_internal_staging' into litellm_headroom_ccr_streaming_responses 2026-08-30 12:47:33 -07:00
mateo-berri
574010a2ce fix(responses): make guardrail input provenance O(n) and guard non-list structured_messages
_input_item_provenance converted every input prefix, so an n-item request paid
for n+1 full conversions. It now converts each item once, glues consecutive
function_call items (plus their trailing-assistant context) into units so the
transform's tool_call merging is reproduced inside the unit conversion, and
verifies the unit concatenation against one full conversion, bailing to the
full-conversion fallback on any mismatch. Messages from multi-item units are
tainted, which keeps parallel tool calls patchable exactly like the old prefix
pass while unpredicted merges fall back safely.

A guardrail handing back a non-list structured_messages payload (the
HiddenLayer v2 evaluation dict) previously fell through the length-mismatch
fallback and 500ed converting the dict's keys as messages. The write-back is
now skipped for non-list payloads, restoring the previous no-write-back
behavior on the Responses surface.

Also refreshes the compresr texts-mirror docstring, which still claimed the
Responses translation cannot round-trip structured_messages.
2026-08-29 22:04:26 -07:00
mateo-berri
1c4674441c docs(openai): trim tool-flattening docstrings to upstream facts 2026-08-29 16:43:05 -07:00
mateo-berri
6bd3699d43 fix(responses): keep guardrailed input items and bridge stream usage intact
- _write_back_structured_messages now patches only the rewritten rows back
  into the original input items, so reasoning items (encrypted_content),
  function_call ids, and web_search_call items survive a guardrail rewrite
  verbatim; rewrites that cannot be row-mapped fall back to the previous
  full conversion
- the responses bridge stream snapshot restores usage hidden in
  _hidden_params when stream_options is unset, so converted fake streams
  report real input_tokens instead of 0
2026-08-29 16:39:14 -07:00
mateo-berri
855f56fa94 fix(openai): flatten top-level tool schema combinators on chat completions 2026-08-29 16:25:28 -07:00
mateo-berri
9448293903 fix(openai): flatten tool schema unions only for models whose validator rejects them
GPT-5 and later accept a top-level anyOf natively and call tools better with it intact, so the flattening now runs only for the gpt-4, gpt-3.5, chatgpt-4o, o1, o3, and o4 families. Non-dict tool entries pass through untouched, a typeless root that carries properties counts as an object, and the bounded $ref walker is listed in the recursion detector allowlist.
2026-08-29 14:38:08 -07:00
mateo-berri
429ad06972 fix(guardrails): write structured_messages rewrites back into /v1/responses input
Message-rewriting guardrails such as Headroom return their rewrite in
structured_messages and leave texts untouched. The responses guardrail
translation only mapped texts back, so compression never reached the
upstream request on /v1/responses while the retrieve tool still got
injected. Convert the returned messages back to Responses input (plus
instructions) the way the chat and Anthropic handlers already do, and
keep developer messages as input_text in the chat-to-responses bridge.

Resolves LIT-6494
2026-08-29 14:23:57 -07:00
mateo-berri
9b8ad46f37 fix(openai): flatten top-level anyOf/oneOf/allOf in Responses API tool schemas
OpenAI's function-calling validator rejects tool parameters carrying
oneOf/anyOf/allOf/enum/const/not at the top level, while the ChatGPT
backend Codex talks to natively accepts them, so an MCP tool declaring a
top-level union 400s through the proxy. Merge the branches into the
object schema for OpenAI itself only, walking the namespace-nested tools
current Codex builds send, on both /v1/responses and /v1/responses/compact
2026-08-29 13:11:04 -07:00
Tin Chi Lo
e5c3df2da2 fix(gpt-5): resolve temperature support from the model's default reasoning effort
A gpt-5 model accepts a non-default temperature only while its effective reasoning
effort resolves to "none". litellm had no representation of the effort a model applies
when the request omits reasoning_effort, so it substituted supports_none_reasoning_effort,
which is a different fact. Every model that supports "none" without defaulting to it
therefore had temperature forwarded and rejected upstream, and because the carve-out
returned before the drop_params branch, drop_params: true could not save it.

Declare the fact instead. A new cost-map key, default_reasoning_effort, states the effort
the provider applies when the request omits one, and one shared predicate resolves the
effective effort from it: an explicit reasoning_effort wins, otherwise the declared
default, otherwise the catalogue decides.

That last step matters because the cost map is fetched from the published branch at import
time, so it can be OLDER than the code reading it. On such a map every model looks
undeclared, and reading that as "reasoning is active" would strip temperature from the 39
gpt-5.1/5.2/5.4 entries that accept it, a regression caused by data lag rather than by
anything about the model. So an absent declaration is only meaningful once the catalogue
carries the key at all; a map that predates the feature keeps the answer litellm gave
before it existed, and the conservative answer applies from the moment the data lands.

The top_p/logprobs/top_logprobs gate carried the same assumption spelled differently and
now shares the predicate, as does the Responses API, which reimplemented the rule and is
what the default /v1/messages bridge routes openai models through. Azure normalises its
routing names in one resolver that every capability lookup goes through, which replaces
its bespoke per-lookup rewrite.

Declared on the 37 gpt-5.1/5.2/5.4 entries measured to accept temperature=0 today, so
their behaviour is unchanged. The 23 gpt-5.5/5.6 entries that reject it stay undeclared
and are fixed once the catalogue carries the key.

Resolves LIT-3797
Resolves LIT-5028
2026-08-27 18:46:18 -07:00
mateo-berri
edde2e50ef fix(openai): drop tool_reference parts from tool messages at the chat boundary
OpenAI's chat completions API rejects tool_reference content parts in
role tool messages, so a mixed text plus reference tool result carried
through the Anthropic adapter turned a previously working request into
a 400 on chat-routed OpenAI and Azure deployments. Strip the reference
parts there, keeping a reference-only result as an empty-text tool
message so the preceding tool_call stays answered, mirroring the
Responses bridge skip.
2026-08-26 23:43:41 -07:00
mateo-berri
7dc5a1682d Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_reasoning_effort_capability_v2 2026-08-25 09:28:55 -07:00
mateo-berri
7d0df4a062 fix(videos): forward uploaded source file on /v1/videos/edits to the provider
The video edit endpoint parsed the multipart body but dropped the uploaded
source video, only normalizing it to an id. When a raw file is uploaded it now
flows through videos.main -> the http handler -> the provider transform, which
emits multipart/form-data with the source video as a file part, matching the
official OpenAI SDK's videos.edit wire format. Edit-by-id still egresses JSON.
2026-08-24 15:34:21 -07:00