fix(backend): report tool_use stop_reason when tool calls finish with stop
Summary
On /v1/messages, a response that contains tool calls but whose upstream finish_reason is stop (or missing) is returned to the Anthropic client with stop_reason: "end_turn". Clients such as Claude Code treat that as a finished turn and never run the tools. Some OpenAI-compatible providers finish tool-calling turns this way. This is the same class of mismatch as the finish/tool_calls mapping fixed in the other adapters; the Google adapter already handles it with hasToolCalls.
Root cause
toAnthropicStopReason(finish_reason) is called in chatCompletionsResponseToMessages and in the streaming transformer without looking at whether tool calls were emitted.
Fix
toAnthropicStopReason takes a hasToolCalls flag. When tool calls are present and the finish reason is stop or absent, it returns tool_use. Other reasons (length, content_filter, ...) are unchanged, so a truncated turn still reports max_tokens. Non-streaming passes whether a tool_use block was built; streaming passes whether any tool call was seen. A patch changeset is included.
Test
Two new cases in anthropic-messages-adapter.spec.ts (non-streaming and streaming). Before the fix they fail; after it the suite passes (61 tests).
This change was prepared with the assistance of an AI coding agent (Claude) and reviewed before submission.
Summary by cubic
Fixes /v1/messages reporting stop_reason: "end_turn" when an upstream provider returns tool calls with a stop (or missing) finish reason, so clients like Claude Code run the tools instead of ending the turn.
Bug Fixes
toAnthropicStopReasonnow returnstool_usewhen tool calls are present with astopor missing reason; other reasons likelengthstill map as before.- Applies to both non-streaming and streaming responses.
- Adds coverage for both paths and a patch changeset.