Repository navigation
Conversation
The Responses to chat completions bridge flattens namespace tools to "<namespace>__<tool>". Codex app connector tools produce names like mcp__codex_apps__atlassian_rovo__legacy__atlassian_rovo_legacy_addcommenttojiraissue (84 characters), and Bedrock Converse and OpenAI chat completions reject function names over 64 characters, so the whole request fails with a 400 Names over the limit now keep a 55 character prefix plus an 8 character sha256 digest of the full name. The same helper builds outbound tools, replayed history, the collision check, and the reverse map, so tool calls still restore to their original namespace and name
… tool names Shortening from the end cut every Codex Rovo legacy tool down to the same 55 character prefix plus a digest, so a model could not tell them apart by name. Shorten the namespace instead and keep the tool name, falling back to truncating a tool name that is too long on its own
Merging this PR will not alter performance
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
VANDRANKI
left a comment
There was a problem hiding this comment.
Community review (does not clear the merge gate).
I read the full diff and ran flatten_namespace_tool_name and the Responses to chat tool transformation on Python 3.13 from this branch. I did not run an actual Bedrock or OpenAI call.
What I checked:
- Boundary: names up to 64 characters are returned unchanged (
mcp__codex_apps__atlassian_rovo__legacy__shortstays as is), so existing short tools keep their names. On main the namespacemcp__codex_apps__atlassian_rovo__legacyplus either of the twoatlassian_rovo_legacy_add...tool names in the PR test flattens to 84 characters (computed from thef"{namespace}__{tool_name}"format), over the 64 limit. - On this branch those two names become
mcp__codex_394c900c__atlassian_rovo_legacy_addcommenttojiraissueandmcp__codex_410c820b__...addworklogtojiraissue, both exactly 64 characters and distinct. - A 60-character tool name, a 63, 64 and 70-character tool name, and two namespaces sharing a long prefix with the same tool name all produced names of at most 64 characters and different digests where inputs differed (the fallback branch drops the namespace and keeps a truncated tool name plus digest).
- Every place that built
f"{namespace}__{tool_name}"now goes through the helper: outbound tools, replayedfunction_callhistory,_validate_namespace_name_collisions, andnamespace_tool_name_map. I grepped litellm/responses and litellm/llms/*/responses and found no other site. The collision check still fires on the flattened long name: a top-level function with that exact flattened name raisesValueError: Top-level function names conflict with flattened namespace tools. - tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py: 226 passed on the branch. The directory also contains test_google_ai_studio_responses_wire.py, which errors at collection in my environment; I did not look into it and it is not related to this diff.
Observations, not blockers:
- The namespace is cut to a short prefix for long names (
mcp__codexin the example), so the model sees less of the original namespace. The tool name stays readable, which the docstring says is the intent. tool_choicehandling (_transform_tool_choice) has no namespace awareness, before or after this change, so I did not test it with a flattened name.
The change looks correct to me.
TLDR
Problem this solves:
/v1/responsesbridged to chat completions fails with a 400 on long namespace tool namesmcp__codex_apps__atlassian_rovo__legacy__atlassian_rovo_legacy_addcommenttojiraissue(84 characters)How it solves it:
namespace__toolname the bridge sends or maps backmcp__codex_394c900c__atlassian_rovo_legacy_addcommenttojiraissuenamespaceandnameUser Flow
Before: a developer running Codex through a LiteLLM proxy on a Bedrock Converse model gets a 400 on every turn once an MCP server or app connector with a long name is loaded
wire_api = "responses") with a model served bybedrock/converseor any chat completions providermcp__<server>__<tool>longer than 64 characters, or Codex loads its app connectorsMember must have length less than or equal to 64naming the toolAfter: the same turn works and the model can call the long-named tool
wire_api = "responses") with a model served bybedrock/converseor any chat completions providermcp__<server>__<tool>longer than 64 characters, or Codex loads its app connectorsnamespaceandname, and Codex runs itPre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/unit/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or moreDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Shared setup. The proxy runs from this branch with
python litellm/proxy/proxy_cli.py --config config.yaml --port 4000. The upstream is an OpenAI compatible gateway serving GLM 5 from Bedrock Converse (real calls, the 64 character check comes from Bedrock), reached throughcustom_openai/so the request goes through the Responses to chat completions bridgebody.json(the streaming case is the same with"stream": true){ "model": "glm-5", "input": "Add the comment 'hi' to Jira issue ABC-1 using the tool.", "tools": [ { "type": "namespace", "name": "mcp__codex_apps__atlassian_rovo__legacy", "description": "Atlassian tools", "tools": [ { "type": "function", "name": "atlassian_rovo_legacy_addcommenttojiraissue", "description": "Add a comment to a Jira issue", "parameters": { "type": "object", "properties": {"issue": {"type": "string"}, "body": {"type": "string"}}, "required": ["issue", "body"] } } ] } ] }For the Codex case, a one tool stdio MCP server (FastMCP,
get_build_status(pipeline)returning a fixed string, annotated read only) is registered under a long server name, so Codex sendsmcp__celonis_internal_build_and_release_dashboard_server__get_build_status(74 characters). Codex 0.162.1 runs interactively in a terminal and is pointed at the proxy withThe prompt typed into Codex is
Use the build dashboard MCP tool to get the status of the ai-gateway pipeline and reply with the build number only.The Codex output below is a text capture of the terminal pane (tmux capture-pane)Before (79b93aa)
Non-streaming /v1/responses
curl -sS http://localhost:4000/v1/responses -H "Authorization: Bearer sk-local-qa" -H "Content-Type: application/json" -d @body.json -w '\nHTTP %{http_code}\n'Streaming /v1/responses
curl -sS -N http://localhost:4000/v1/responses -H "Authorization: Bearer sk-local-qa" -H "Content-Type: application/json" -d @body_stream.json -w '\nHTTP %{http_code}\n'Codex with a long-named MCP server
After (1f0d979)
Non-streaming /v1/responses
curl -sS http://localhost:4000/v1/responses -H "Authorization: Bearer sk-local-qa" -H "Content-Type: application/json" -d @body.json -w '\nHTTP %{http_code}\n'function_callitems ofoutputStreaming /v1/responses
curl -sS -N http://localhost:4000/v1/responses -H "Authorization: Bearer sk-local-qa" -H "Content-Type: application/json" -d @body_stream.json -w '\nHTTP %{http_code}\n'function_callitem of theresponse.output_item.doneeventCodex with a long-named MCP server
Unit test mutation check, as
tests/AGENTS.mdasks. With the helper reverted to a plainf"{namespace}__{tool_name}",test_long_namespace_tool_names_fit_limit_and_round_tripfails on the 64 character assertion. With a version that cuts the flattened name from the end, it fails on the assertion that the tool name survives.tests/unit/responses,tests/unit/llms/bedrock/responses,tests/unit/llms/openai/responsesandtests/unit/llms/bedrock_mantlegive 1474 passed at the merge base and 1475 passed here, with the same single failure on both sides (tests/unit/responses/test_dispatch.py::test_responses_with_retries_uses_the_dispatch_entrypoint, unrelated)Type
🐛 Bug Fix
Caveats (if any)
Low
Final Attestation