Skip to content

fix: preserve tool_calls and tool results in Chat→Responses API conversion#50

Merged
CaddyGlow merged 4 commits intoCaddyGlow:mainfrom
chaizhenhua:fix/tool-result-conversion
Mar 31, 2026
Merged

fix: preserve tool_calls and tool results in Chat→Responses API conversion#50
CaddyGlow merged 4 commits intoCaddyGlow:mainfrom
chaizhenhua:fix/tool-result-conversion

Conversation

@chaizhenhua
Copy link
Copy Markdown
Contributor

Summary

  • Fix Chat Completions → Responses API request conversion to include full message history (assistant tool_calls + tool results), not just the last user message
  • Fix Claude API adapter to convert tool_calls and tool_call_id fields when forwarding to Anthropic

Problem

When a client sends a Chat Completions request with tool call history:

{"messages": [
  {"role": "user", "content": "What is the weather?"},
  {"role": "assistant", "tool_calls": [{"id": "fc_xxx", "function": {"name": "get_weather", "arguments": "{}"}}]},
  {"role": "tool", "tool_call_id": "fc_xxx", "content": "{\"temp\": 70}"}
]}

CCProxy's _build_responses_payload_from_chat_request only extracted the last user message as input, dropping the assistant's tool_calls and the tool result entirely. The upstream LLM never saw the tool result, so it re-called the same tool in an infinite loop.

Fix

1. openai_to_openai/requests.py_build_responses_payload_from_chat_request

Convert ALL non-system messages to Responses API input items:

  • user{"type": "message", "role": "user", ...}
  • assistant with text → {"type": "message", "role": "assistant", ...}
  • assistant with tool_calls{"type": "function_call", "call_id": ..., "name": ..., "arguments": ...} per call
  • tool{"type": "function_call_output", "call_id": ..., "output": ...}

2. claude_api/adapter.py_convert_openai_to_anthropic

Add handling for:

  • Assistant messages with tool_calls → Anthropic tool_use blocks
  • Tool role messages with tool_call_id → Anthropic tool_result blocks

Test

curl http://localhost:8000/codex/v1/chat/completions \
  -H "Authorization: Bearer sk-ccproxy" \
  -d '{"model":"gpt-5.4","messages":[
    {"role":"user","content":"Weather in Tokyo?"},
    {"role":"assistant","content":null,"tool_calls":[{"id":"fc_xxx","type":"function","function":{"name":"get_weather","arguments":"{\"location\":\"Tokyo\"}"}}]},
    {"role":"tool","tool_call_id":"fc_xxx","content":"{\"temp\":70,\"condition\":\"Sunny\"}"}
  ],"tools":[{"type":"function","function":{"name":"get_weather","description":"Get weather","parameters":{"type":"object","properties":{"location":{"type":"string"}}}}}]}'

Before: LLM re-calls get_weather (tool result was dropped)
After: LLM summarizes "It's 70°F and sunny in Tokyo"

@chaizhenhua chaizhenhua changed the title fix: preserve tool_calls and tool results in Chat→Responses API conversion#1 fix: preserve tool_calls and tool results in Chat→Responses API conversion Mar 29, 2026
- Reformat payload builders for cleaner multiline structures
- Simplify assistant content checks in request conversion
- Emit streaming tool deltas with `ToolCallChunk` metadata
- Reuse shared parsing for message content and tool arguments
@CaddyGlow CaddyGlow merged commit 39dc5f8 into CaddyGlow:main Mar 31, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants