What happened?
When using use_chat_completions_api: true to bridge the Responses API to Chat Completions for providers that don't support /v1/responses (e.g., DeepSeek, Z.AI/GLM, MiniMax), two bugs in transform_responses_api_tools_to_chat_completion_tools cause failures:
Bug 1: Unsupported tool types forwarded as-is
When Codex or other clients send tools with type: "custom" (a valid Responses API tool type per the OpenAI SDK's CustomToolParam), the else branch in transform_responses_api_tools_to_chat_completion_tools passes them through unchanged:
# Current code (line ~1411)
else:
chat_completion_tools.append(
cast(Union[ChatCompletionToolParam, OpenAIMcpServerTool], tool)
)
Providers like DeepSeek reject this: tools[0].type: unknown variant 'custom', expected 'function'.
Bug 2: Function tool names lost during transformation
When clients send tools in Chat Completion format nested under the Responses API (which Codex does), the transformation only reads name from the top level of the tool dict:
# Current code (line ~1394)
"name": typed_tool.get("name") or "", # Returns "" when name is nested under "function"
The Responses API FunctionToolParam has name at the top level ({"type": "function", "name": "read"}), but Codex sends Chat Completion-style tools even in Responses API requests: {"type": "function", "function": {"name": "read", ...}}. The transformation ignores the nested function.name, resulting in tools[0].function.name: empty string.
Expected behavior
- Non-standard tool types (
custom, shell, etc.) should be dropped during Responses API → Chat Completions transformation, not forwarded.
- Function tool names should be read from both
tool.name (Responses API format) AND tool.function.name (Chat Completion format) to handle both styles.
Reproduction
# config.yaml
model_list:
- model_name: deepseek-v4-pro
litellm_params:
model: deepseek/deepseek-v4-pro
api_key: os.environ/DEEPSEEK_API_KEY
drop_params: true
use_chat_completions_api: true
curl -X POST http://localhost:4000/v1/responses \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-pro",
"input": "list files",
"max_output_tokens": 20,
"tools": [
{"type": "custom", "name": "shell", "description": "run shell"},
{"type": "function", "function": {"name": "read", "description": "read file", "parameters": {"type": "object", "properties": {"path": {"type": "string"}}}}}
]
}'
Results in:
BadRequestError: DeepseekException - Invalid 'tools[0].function.name': empty string
Proposed fix
In litellm/responses/litellm_completion_transformation/transformation.py, transform_responses_api_tools_to_chat_completion_tools:
# Fix 1: Drop unknown tool types instead of forwarding
else:
pass # Drop custom/shell/other unsupported types
# Fix 2: Read function name from both formats
elif tool.get("type") == "function":
typed_tool = cast(FunctionToolParam, tool)
_fn = tool.get("function") or {}
parameters = dict(_fn.get("parameters") or typed_tool.get("parameters") or {})
if not parameters or "type" not in parameters:
parameters["type"] = "object"
chat_completion_tool: Dict[str, Any] = {
"type": "function",
"function": {
"name": _fn.get("name") or typed_tool.get("name") or "",
"description": _fn.get("description") or typed_tool.get("description") or "",
"parameters": parameters,
"strict": _fn.get("strict") or typed_tool.get("strict") or False,
},
}
Why drop_params: true doesn't fix this
drop_params strips unsupported top-level request parameters. It does not traverse the tools array to filter tool types or fix tool field mapping.
LiteLLM version
v1.83.14
Related issues
What happened?
When using
use_chat_completions_api: trueto bridge the Responses API to Chat Completions for providers that don't support/v1/responses(e.g., DeepSeek, Z.AI/GLM, MiniMax), two bugs intransform_responses_api_tools_to_chat_completion_toolscause failures:Bug 1: Unsupported tool types forwarded as-is
When Codex or other clients send tools with
type: "custom"(a valid Responses API tool type per the OpenAI SDK'sCustomToolParam), theelsebranch intransform_responses_api_tools_to_chat_completion_toolspasses them through unchanged:Providers like DeepSeek reject this:
tools[0].type: unknown variant 'custom', expected 'function'.Bug 2: Function tool names lost during transformation
When clients send tools in Chat Completion format nested under the Responses API (which Codex does), the transformation only reads
namefrom the top level of the tool dict:The Responses API
FunctionToolParamhasnameat the top level ({"type": "function", "name": "read"}), but Codex sends Chat Completion-style tools even in Responses API requests:{"type": "function", "function": {"name": "read", ...}}. The transformation ignores the nestedfunction.name, resulting intools[0].function.name: empty string.Expected behavior
custom,shell, etc.) should be dropped during Responses API → Chat Completions transformation, not forwarded.tool.name(Responses API format) ANDtool.function.name(Chat Completion format) to handle both styles.Reproduction
Results in:
Proposed fix
In
litellm/responses/litellm_completion_transformation/transformation.py,transform_responses_api_tools_to_chat_completion_tools:Why
drop_params: truedoesn't fix thisdrop_paramsstrips unsupported top-level request parameters. It does not traverse thetoolsarray to filter tool types or fix tool field mapping.LiteLLM version
v1.83.14
Related issues