Skip to content

fix(responses): insert synthetic tool messages for missing function_call_output items - #2051

Open
ThinkPeace wants to merge 1 commit into
looplj:unstablefrom
ThinkPeace:fix/responses-missing-tool-call-output
Open

fix(responses): insert synthetic tool messages for missing function_call_output items#2051
ThinkPeace wants to merge 1 commit into
looplj:unstablefrom
ThinkPeace:fix/responses-missing-tool-call-output

Conversation

@ThinkPeace

Copy link
Copy Markdown

Summary

When converting Responses API input items to Chat Completions messages, function_call items without matching function_call_output items produce assistant messages with tool_calls but no tool responses. Downstream providers reject such incomplete tool call cycles.

Fix

Add fixMissingToolCallOutputs — a post-processing step in convertInputToMessages that:

  1. Collects all tool_call_ids that already have responses
  2. Inserts synthetic empty role: "tool" messages for any tool call lacking a response

Changes

  • llm/transformer/openai/responses/inbound.go: Add fixMissingToolCallOutputs, called at end of convertInputToMessages
  • llm/transformer/openai/responses/inbound_test.go: Add 9 test cases

All existing tests in transformer/openai/responses/ continue to pass.

Closes #2049

🤖 Generated with Claude Code

@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR repairs incomplete tool-call cycles before forwarding requests. The main changes are:

  • Inserts empty tool responses for unanswered function calls.
  • Matches responses within each assistant turn so reused IDs remain independent.
  • Adds missing function-tool definitions during request processing.
  • Covers complete, partial, missing, terminal, and repeated-ID cases.

Confidence Score: 5/5

This looks safe to merge.

  • Reused tool-call IDs are now matched independently for each assistant turn.
  • A second middleware application does not add duplicate synthetic responses.
  • No blocking issue was found in the updated code.

Important Files Changed

Filename Overview
llm/transformer/openai/responses/inbound.go Adds turn-local matching and synthetic responses for unanswered tool calls.
llm/pipeline/cc/fix_tool_calls.go Adds middleware for missing tool responses and function definitions.
internal/server/orchestrator/orchestrator.go Registers the new request middleware in the orchestrator.
llm/transformer/openai/responses/inbound_test.go Adds coverage for the repaired tool-call matching behavior.

Reviews (3): Last reviewed commit: "fix(pipeline): universal fix for missing..." | Re-trigger Greptile

Comment thread llm/transformer/openai/responses/inbound.go Outdated
@ThinkPeace
ThinkPeace force-pushed the fix/responses-missing-tool-call-output branch from d1675d2 to 459675e Compare July 21, 2026 17:55
@ThinkPeace

Copy link
Copy Markdown
Author

Good catch! Fixed in 459675e.

The updated algorithm does per-assistant-message independent checking instead of using a global responded map:

  1. For each assistant message, collect its tool_call_ids into a missing set
  2. Look ahead at immediately-following role: "tool" messages (stop at first non-tool), removing covered IDs from missing
  3. Insert synthetic tool messages for any remaining uncovered IDs

This means [assistant(x), tool(x), assistant(x), user] correctly gets a synthetic tool(x) inserted after the second assistant turn — the first turn's tool(x) is before the second assistant, not after it, so it does not cover the second call.

Added a dedicated test case: "same ID across two assistant turns — each checked independently" that validates this exact scenario.

…n tool definitions

Add FixMissingToolCalls pipeline middleware that runs on every request to:

1. Insert synthetic tool messages for assistant tool_calls that lack a
   corresponding tool response (prevents 'tool_calls must be followed by
   tool messages' errors from downstream providers)

2. Add dummy tool definitions for any function name referenced in
   conversation history tool_calls that is missing from the request's
   tools array (prevents 'tool call id X is not found' errors when
   Codex-specific tools like exec_command appear in history)

The FixMissingToolCallOutputs function in the responses transformer
is exported and each assistant message is checked independently so
repeated tool-call IDs across turns are handled correctly.

Fixes looplj#2049

Co-Authored-By: Claude <noreply@anthropic.com>
@ThinkPeace
ThinkPeace force-pushed the fix/responses-missing-tool-call-output branch from 459675e to ab4318c Compare July 22, 2026 18:04
@ThinkPeace

Copy link
Copy Markdown
Author

Updated with the full fix. The original approach only covered the /v1/responses path. Testing revealed the /v1/chat/completions path also needs the fix.

Changes since last review

  1. Exported FixMissingToolCallOutputs so it can be called from other packages
  2. Added llm/pipeline/cc/fix_tool_calls.go — a pipeline middleware that runs on ALL request paths (both /v1/responses and /v1/chat/completions)
  3. Registered middleware in orchestrator as a global middleware
  4. Added tool definition fix: the middleware now also ensures every tool name referenced in conversation history has a matching entry in the tools array (prevents tool call id X is not found errors when Codex-specific tools like exec_command appear in history but not in the client's tool definitions)
  5. 8 test cases covering the exported function

Middleware does two things on every request:

  1. Calls FixMissingToolCallOutputs to insert synthetic tool messages for any assistant tool_calls lacking responses
  2. Scans conversation history for tool names not in request.Tools and adds minimal dummy definitions

@looplj

looplj commented Jul 23, 2026

Copy link
Copy Markdown
Owner

这是客户端问题吧

MrLiuGangQiang added a commit to MrLiuGangQiang/axonhub that referenced this pull request Jul 28, 2026
MrLiuGangQiang added a commit to MrLiuGangQiang/axonhub that referenced this pull request Jul 28, 2026
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.

fix: Responses API inbound converter should handle missing function_call_output items

2 participants