Skip to content

chore(release): backport #40989 to stable/1.101.x and cut 1.101.6 - #45212

Merged
yuneng-berri merged 3 commits into
stable/1.101.xfrom
litellm_backport_1_101_x_bp40989
Oct 8, 2026
Merged

yuneng-berri merged 3 commits into
stable/1.101.xfrom
litellm_backport_1_101_x_bp40989

Conversation

@yuneng-berri

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Codex CLI tools never reach bridged models on stable/1.101.x
  • Codex sends tools inside an additional_tools input item, which the bridge drops
  • Codex's shell tool, a custom tool nested in a namespace, is skipped too

How it solves it:

User Flow

Before: a developer pointing Codex CLI at a bridged model on 1.101.x never gets a tool call back, so Codex cannot run a shell command

  1. The proxy admin adds a bridged deployment (for example Bedrock Converse) and starts the proxy
  2. The developer points ~/.codex/config.toml at https://litellm-domain/v1 with wire_api = "responses", opens codex, and asks it to run ls
  3. Codex sends POST https://litellm-domain/v1/responses with its exec shell tool nested in one {"type": "additional_tools", ...} input item and no top-level tools
  4. The proxy answers 200 with only reasoning and message output items
  5. Codex replies that it has no shell access and nothing runs

After: the same session comes back with a shell tool call, so Codex runs the command

  1. The proxy admin adds the same bridged deployment and starts the proxy
  2. The developer points ~/.codex/config.toml at the same URL, opens codex, and asks it to run ls
  3. Codex sends the same POST https://litellm-domain/v1/responses
  4. The proxy answers 200 with a custom_tool_call output item named exec whose input is the shell command
  5. Codex runs ls locally and reports the files

Relevant issues

Backports #40989 onto stable/1.101.x and cuts 1.101.6. The line's tip 1.101.5 is already published on DockerHub and GHCR and tagged, so this PR bumps the version

#40989 merged to main as a two-parent merge commit, so it is picked with -m 1 against its first parent. It has shipped since v1.103.0. Later main commits touching the same files are lint, typing and perf refactors only, so no follow-up is included

What is included

Adaptation notes

#40989 is adapted by context only: every added and removed line matches the upstream change byte for byte in all 13 files, and the line counts are identical (560 additions, 104 deletions). Three files needed conflict resolution because this line is missing code that main had near the hunks

In streaming_iterator.py, main has a web_search_call handling block next to the removed tool_name, tool_namespace = ... line. That block comes from a different change that is not on this line, so it was not pulled in. Only the removed line was dropped, as upstream does

In test_litellm_completion_responses.py, a plain three-way merge would have pulled in six reasoning-summary tests that belong to other changes. The line's file was restored and only the PR's own hunks were added, so the added lines are identical to upstream (3 new tests plus one extra entry in an existing test's tool list)

bedrock_mantle/responses/transformation.py merged cleanly, and its changed lines are identical to upstream

Known noise on this line

These tests fail on the untouched line tip and fail the same way after these commits:

  • tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_transformation.py::TestBedrockMantlePricing::test_gpt_oss_20b_pricing
  • tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py::test_output_config_format_forwarded_for_bedrock_chat_invoke_request
  • tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py::test_output_config_format_translated_to_native_output_config_converse
  • tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py::test_translate_response_format_json_schema_still_injects_tool

Screenshots / Proof of Fix

Deep verification (gauntlet-mini, live-proxy replay) in progress; this body will be updated with the evidence

The targeted set (tests/test_litellm/responses, tests/test_litellm/llms/bedrock_mantle, tests/test_litellm/llms/openai/responses) goes from 1039 passed and 1 failed to 1053 passed and the same 1 failed. 10 of the pick's own tests fail when its source changes are reverted on this line and pass with them. A wider set covering the callers (completion_extras, integrations/test_custom_guardrail.py, the Purview guardrail, proxy/response_api_endpoints, llms/bedrock) has the same 3 failures before and after

Type

🐛 Bug Fix

mateo-berri and others added 3 commits October 7, 2026 17:29
…_additional_tools

fix(responses): hoist Codex additional_tools input items into the chat bridge tools

(cherry picked from commit 1ce66e9)
@yuneng-berri
yuneng-berri requested a review from a team October 8, 2026 00:32
@greptile-apps

greptile-apps Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 2/5

[High risk] Refactors tool handling across responses API and streaming.

The PR should not merge while tools can bypass policy checks and matching tool names can return the wrong call type

Findings

  1. P1 Security Blocked tools bypass checks ▶
  2. P1 Matching tool names become ambiguous ▶

Summary

Backports support for Codex tools carried inside additional_tools and prepares release 1.101.6

  • Shares tool hoisting between the chat bridge and Bedrock Mantle
  • Converts nested custom tools into chat functions and restores custom calls in responses
  • Adds tests for hoisting, streaming, and guardrail tool merging

Reviews (2) · Last reviewed commit: "chore: refresh uv.lock for 1.101.6" · Reviewed by Greptile

Comment on lines +41 to +44
hoisted: Final = hoist_additional_tools(input, responses_api_request.get("tools"))
bridged_input: Final = hoisted.input
bridged_request: Final[ResponsesAPIOptionalRequestParams] = (
{**responses_api_request, "tools": list(hoisted.tools)} if hoisted.hoisted else responses_api_request

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Blocked tools bypass checks

With tool_policy configured for pre_call and exec blocked, a caller can put exec inside an additional_tools input item. The Responses guardrail checks only top-level tools. This bridge then forwards the unchecked tool, and the completion hook skips guardrails already marked as run. Hoist before guardrails and forward their edited tools.

How this was verified: The request path checks top-level tools before hoisting, then skips the already-run guardrail when calling completion

Comment on lines +1784 to +1785
if nested and tool_type == "custom":
return convert_custom_tool_to_function_tool({**namespace_tool, "description": description})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Matching tool names become ambiguous

A top-level function exec and a nested custom tool exec now both become chat functions named exec. _validate_namespace_name_collisions ignores custom members. Both response paths then classify calls to the existing function as custom_tool_call, returning the wrong call type and arguments. Reject these collisions or preserve distinct names throughout conversion

@yuneng-berri
yuneng-berri marked this pull request as draft October 8, 2026 00:47
| BaseResponsesAPIStreamingIterator
| Coroutine[object, object, ResponsesAPIResponse | BaseResponsesAPIStreamingIterator]
):
hoisted: Final = hoist_additional_tools(input, responses_api_request.get("tools"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: Late tool hoisting bypasses key and team allowlists

On a Responses-to-Chat-Completions deployment, a caller whose key/team allowed_tools permits only read can place a write function in an input item of type additional_tools, alongside a normal message, and have it forwarded to the provider. check_tools_allowlist() runs during authentication and its Responses extractor only reads top-level tools, so this newly supported declaration is never authorized; hoist before that check, or reapply it to the merged tools using the authenticated key/team before calling completion/acompletion.

@veria-ai

veria-ai Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

PR overview

This PR backports #40989 to stable/1.101.x and prepares release 1.101.6. The touched Responses-to-Chat-Completions handler adds support for merging tool declarations from additional_tools input items into the tools forwarded to the provider.

One security issue remains open, with none addressed so far. On Responses-to-Chat-Completions deployments, callers can bypass key and team tool allowlists by supplying tool declarations through additional_tools, which are merged after authorization. This allows disallowed tool definitions, including write functions, to reach the provider, though execution of those functions is not established by the finding.

Open issues (1)

Fixed/addressed: 0 · PR risk: 7/10

@yuneng-berri
yuneng-berri marked this pull request as ready for review October 8, 2026 01:20
@yuneng-berri
yuneng-berri enabled auto-merge October 8, 2026 01:22
@yuneng-berri
yuneng-berri merged commit a501c70 into stable/1.101.x Oct 8, 2026
6 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_backport_1_101_x_bp40989 branch October 8, 2026 01:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants