Skip to content

[Bug]: Responses API → Chat Completions bridge drops tool names and forwards unsupported tool types (custom, shell) #27276

Description

@TopherMayor

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

  1. Non-standard tool types (custom, shell, etc.) should be dropped during Responses API → Chat Completions transformation, not forwarded.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions