fix: preserve tool_use stop reason for Responses function calls - #173
Conversation
Code Review Roast 🔥Verdict: No Issues Found | Recommendation: Merge Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything. 📊 Overall: Like finding a unicorn in production — I didn't think clean PRs existed anymore, but here we are. Files Reviewed (5 files)
Previous Review Summaries (3 snapshots, latest commit 8657cb2)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 8657cb2)Verdict: No Issues Found | Recommendation: Merge Oh wait, this PR actually fixed the issues. I need to sit down. I had my flamethrower warmed up and everything. 📊 Overall: Like finding a unicorn in production — I didn't think clean PRs existed anymore, but here we are. Files Reviewed (3 files)
Previous review (commit 27595d5)Verdict: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)
🏆 Best part: The streaming path now correctly buffers and normalizes tool arguments, and the 💀 Worst part: 📊 Overall: This PR built a solid boat for the streaming river but left a hole in the non-streaming hull. The HTML-escaping bug in Files Reviewed (7 files)
Fix these issues in Kilo Cloud Previous review (commit 7e6dcc9)Verdict: No Issues Found | Recommendation: Merge Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything. 📊 Overall: Like finding a unicorn in production — I didn't think clean PRs existed anymore, but here we are. Files Reviewed (2 files)
Reviewed by free · Input: 0 · Output: 0 · Cached: 0 Review guidance: REVIEW.md from base branch |
|
Same hardcoded "end_turn" at internal/transformer/response.go:225 as normalized_bridge.go:272 had. It is still reachable through the fallback in messages.go:1486. Can you apply the same hasToolCall logic here so both Responses paths return "tool_use"? I am hitting this early stop with muse-spark-1.3-contributor. A tool call comes back and Claude stops instead of running it. This looks like the right fix to me. |
|
Applied in Some context from my side, and I'd like to keep this as an open conversation. Before your comment I had already tried several different approaches specifically for That is also why I would rather check the reachability question with you than assume this closes your case. In the current code, So two things that would help:
Happy to dig into it together. |
TransformResponsesResponse converted a Responses function_call into a tool_use block but always reported stop_reason "end_turn", so a client that read the turn as finished skipped the tool call. Mirror the hasToolCall logic already used by ResponsesToNormalized so both Responses paths report "tool_use". Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Update — the fix is now actually in this branch, not just described: What changed: Verification:
@alecchen the line number you gave was exact — |
normalizeToolArguments is documented as repairing whitespace around JSON
object keys, but it decoded into any and re-encoded, which rewrote the
values as well:
{"id":1234567890123456789} -> {"id":1234567890123456800}
{"price":1.0} -> {"price":1}
{"html":"<b>bold</b>"} -> the < and > inside the value are escaped
Integers above 2^53 lose precision, which silently corrupts a tool
argument that carries a large ID or byte count. Decode with UseNumber so
number literals survive, and disable HTML escaping on the encoder so
string values are re-emitted untouched.
A Decoder also accepts the trailing content that json.Unmarshal rejected
({"a":1}{"b":2}), which would silently keep only the first value; that
input is now returned unchanged, as before.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…path TransformResponsesResponse passed output.Arguments straight through, unlike ResponsesToNormalized, so the key-whitespace repair added for Responses tool calls did not cover the non-streaming fallback path. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Both items are addressed, in
|
|
Thanks @hinha, checked at head - Agree on reachability. I read I also tried forwarding If it comes back after this patch I will grab one Muse turn of raw SSE ( |
|
Thank you all for working on this. I am going through it and will revert shortly! |
|
LGTM |
Summary
Fix Claude Code tool-call compatibility for OpenCode Go Responses models.
This PR addresses two related response-conversion problems:
function_calloutput was converted to Anthropictool_usecontent while retainingstop_reason: "end_turn". The proxy now emitsstop_reason: "tool_use"when a function call is present."description ". Claude Code Plan Mode rejects these asInvalid tool parametersbecause the expected schema key isdescription. The proxy now normalizes whitespace around JSON object keys in both non-streaming and streaming Responses tool calls before sending them to Claude Code.The second issue was reproduced from a Claude Code 2.1.263 transcript:
The malformed input was:
{"label":"Repo + Wiki + Ask","description ":"..."}Validation
GOTOOLCHAIN=local /usr/local/go/bin/go test -count=1 ./...GOTOOLCHAIN=local /usr/local/go/bin/go build ./...git diff --checkCloses #172
Source branch:
hinha:fix/responses-tool-use-stop-reasonLatest commit:
29cf114