Merge pull request #26221 from BerriAI/litellm_responses_strip_custom_tool_call_namespace

feat(responses): strip custom_tool_call namespace for all providers
This commit is contained in:
yuneng-jiang 2026-04-24 09:42:55 -07:00 • committed by GitHub
commit 4e3feda952
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 130 additions and 1 deletions

View file

@ -1,6 +1,6 @@
import types
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, cast
import httpx
@ -289,3 +289,32 @@ class BaseResponsesAPIConfig(ABC):
#########################################################
########## END COMPACT RESPONSE API TRANSFORMATION ######
#########################################################
@staticmethod
def strip_custom_tool_call_namespace_from_responses_input(
input: Union[str, ResponseInputParam],
) -> Union[str, ResponseInputParam]:
"""
Remove ``namespace`` from ``custom_tool_call`` input items.
"""
if not isinstance(input, list):
return input
out: List[Any] = []
for item in input:
if isinstance(item, dict) and item.get("type") == "custom_tool_call":
out.append({k: v for k, v in item.items() if k != "namespace"})
else:
out.append(item)
return cast(ResponseInputParam, out)
@staticmethod
def normalize_responses_api_request_dict(data: Dict[str, Any]) -> Dict[str, Any]:
"""Apply provider-agnostic fixes to an outbound Responses API request dict."""
if not isinstance(data, dict) or "input" not in data:
return data
return {
**data,
"input": BaseResponsesAPIConfig.strip_custom_tool_call_namespace_from_responses_input(
data["input"]
),
}

View file

@ -2213,6 +2213,7 @@ class BaseLLMHTTPHandler:
litellm_params=litellm_params,
headers=headers,
)
data = BaseResponsesAPIConfig.normalize_responses_api_request_dict(data)
if extra_body:
data.update(extra_body)
@ -2358,6 +2359,7 @@ class BaseLLMHTTPHandler:
litellm_params=litellm_params,
headers=headers,
)
data = BaseResponsesAPIConfig.normalize_responses_api_request_dict(data)
if extra_body:
data.update(extra_body)
@ -3897,6 +3899,7 @@ class BaseLLMHTTPHandler:
litellm_params=litellm_params,
headers=headers,
)
data = BaseResponsesAPIConfig.normalize_responses_api_request_dict(data)
## LOGGING
logging_obj.pre_call(
@ -3979,6 +3982,7 @@ class BaseLLMHTTPHandler:
litellm_params=litellm_params,
headers=headers,
)
data = BaseResponsesAPIConfig.normalize_responses_api_request_dict(data)
## LOGGING
logging_obj.pre_call(

View file

@ -11,6 +11,7 @@ sys.path.insert(
) # Adds the parent directory to the system path
import litellm
from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig
from litellm.llms.azure.responses.transformation import AzureOpenAIResponsesAPIConfig
from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig
from litellm.types.llms.openai import (
@ -542,6 +543,57 @@ class TestOpenAIResponsesAPIConfig:
assert result.output_index == 0
assert result.content_index == 0
def test_base_strip_custom_tool_call_namespace_all_providers(self):
"""Base helper strips ``namespace`` from custom_tool_call for every provider path."""
inp = [
{"type": "function_call", "call_id": "a", "name": "f", "namespace": "keep"},
{"type": "custom_tool_call", "call_id": "b", "name": "c", "namespace": "drop"},
]
out = BaseResponsesAPIConfig.strip_custom_tool_call_namespace_from_responses_input(
inp
)
assert out[0]["namespace"] == "keep"
assert "namespace" not in out[1]
body = {"model": "x", "input": inp}
norm = BaseResponsesAPIConfig.normalize_responses_api_request_dict(body)
assert norm["input"][0]["namespace"] == "keep"
assert "namespace" not in norm["input"][1]
def test_openai_transform_then_normalize_strips_custom_tool_call_namespace(self):
"""``transform_responses_api_request`` leaves input as validated; HTTP layer ``normalize_*`` strips."""
input_items = [
{
"type": "function_call",
"call_id": "c1",
"name": "t",
"arguments": "{}",
"namespace": "my_tools",
},
{
"type": "custom_tool_call",
"call_id": "c2",
"name": "agent",
"input": "x",
"namespace": "None",
"status": "completed",
},
]
body = self.config.transform_responses_api_request(
model=self.model,
input=input_items,
response_api_optional_request_params={},
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert body["input"][0].get("namespace") == "my_tools"
assert body["input"][1].get("namespace") == "None"
norm = BaseResponsesAPIConfig.normalize_responses_api_request_dict(body)
assert norm["input"][0].get("namespace") == "my_tools"
assert norm["input"][1]["type"] == "custom_tool_call"
assert "namespace" not in norm["input"][1]
class TestAzureResponsesAPIConfig:
def setup_method(self):
@ -583,6 +635,50 @@ class TestAzureResponsesAPIConfig:
== "https://litellm8397336933.openai.azure.com/openai/responses?api-version=2025-01-01"
)
def test_azure_transform_then_normalize_strips_custom_tool_call_namespace(self):
"""Same as OpenAI path: ``normalize_responses_api_request_dict`` strips custom_tool_call only."""
input_items = [
{
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "Hi"}],
},
{
"type": "custom_tool_call",
"call_id": "call_1",
"input": "do thing",
"name": "my_tool",
"id": "ctc_1",
"namespace": "None",
"status": "completed",
},
{
"type": "function_call",
"call_id": "call_2",
"name": "get_weather",
"arguments": "{}",
"id": "fc_1",
"namespace": "tools",
"status": "completed",
},
]
body = self.config.transform_responses_api_request(
model=self.model,
input=input_items,
response_api_optional_request_params={},
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert body["input"][1].get("namespace") == "None"
assert body["input"][2].get("namespace") == "tools"
norm = BaseResponsesAPIConfig.normalize_responses_api_request_dict(body)
assert norm["input"][1]["type"] == "custom_tool_call"
assert "namespace" not in norm["input"][1]
assert norm["input"][2]["type"] == "function_call"
assert norm["input"][2].get("namespace") == "tools"
assert norm["input"][2]["name"] == "get_weather"
class TestTransformListInputItemsRequest:
"""Test suite for transform_list_input_items_request function"""