mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
fix(responses-bridge): flatten namespace tools without recursion
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
434c04e7bc
commit
82df8a9489
3 changed files with 34 additions and 6 deletions
|
|
@ -5,7 +5,9 @@ Completions providers.
|
|||
A namespace tool is a grouping container: it carries no callable schema of its own and
|
||||
holds its callable tools under ``tools``. Chat Completions has no equivalent container,
|
||||
so the bridge replaces each namespace with the tools it contains, which then go through
|
||||
the same conversion as any top level tool.
|
||||
the same conversion as any top level tool. Namespaces are flat in practice, and a
|
||||
namespace that somehow contains another one keeps the inner container, which the
|
||||
conversion then drops as an unsupported type.
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
|
@ -19,9 +21,7 @@ ResponsesAPITool: TypeAlias = FunctionToolParam | OpenAIMcpServerTool
|
|||
|
||||
|
||||
def flatten_namespace_tools(tools: Sequence[ResponsesAPITool]) -> tuple[ResponsesAPITool, ...]:
|
||||
"""Replace every namespace tool with its nested tools, recursively."""
|
||||
"""Replace every namespace tool with the tools it contains."""
|
||||
return tuple(
|
||||
nested
|
||||
for tool in tools
|
||||
for nested in (flatten_namespace_tools(tool.get("tools") or ()) if tool.get("type") == "namespace" else (tool,))
|
||||
nested for tool in tools for nested in (tool.get("tools") or () if tool.get("type") == "namespace" else (tool,))
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1324,7 +1324,7 @@ class LiteLLMCompletionResponsesConfig:
|
|||
chat_completion_tools.append(converted)
|
||||
else:
|
||||
_tool_type = tool.get("type")
|
||||
if _tool_type in ("computer_use", "image_generation", "shell"):
|
||||
if _tool_type in ("computer_use", "image_generation", "namespace", "shell"):
|
||||
# Drop unsupported Responses-API-only tool types that have no
|
||||
# Chat Completions equivalent. Passing them through verbatim
|
||||
# causes providers to reject the request with "'function' is a
|
||||
|
|
|
|||
|
|
@ -1633,6 +1633,34 @@ class TestToolTransformation:
|
|||
"read_file",
|
||||
]
|
||||
|
||||
def test_transform_nested_namespace_container_is_dropped(self):
|
||||
"""A namespace inside a namespace is not something clients send, but its inner
|
||||
container must never reach the provider as a tool without a function schema."""
|
||||
tools = [
|
||||
{
|
||||
"type": "namespace",
|
||||
"name": "outer",
|
||||
"tools": [
|
||||
{"type": "function", "name": "outer_tool"},
|
||||
{
|
||||
"type": "namespace",
|
||||
"name": "inner",
|
||||
"tools": [{"type": "function", "name": "inner_tool"}],
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
(
|
||||
result_tools,
|
||||
_,
|
||||
) = LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools(
|
||||
tools=tools
|
||||
)
|
||||
|
||||
assert [tool["function"]["name"] for tool in result_tools] == ["outer_tool"]
|
||||
assert not any(tool.get("type") == "namespace" for tool in result_tools)
|
||||
|
||||
def test_bedrock_anthropic_drops_derived_web_search_options(self):
|
||||
"""
|
||||
Regression for LIT-3858: a Responses web_search tool becomes a derived
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue