Skip to content

Preserve real finish_reason for tool calls truncated by length#2184

Open
mlpy0 wants to merge 1 commit into
exo-explore:mainfrom
mlpy0:fix/toolcall-truncation-finish-reason
Open

Preserve real finish_reason for tool calls truncated by length#2184
mlpy0 wants to merge 1 commit into
exo-explore:mainfrom
mlpy0:fix/toolcall-truncation-finish-reason

Conversation

@mlpy0

@mlpy0 mlpy0 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Problem

When a model stops generating mid-tool-call — e.g. it hits max_tokens before emitting the tool call's closing tag — parse_tool_calls overwrites the model's real finish_reason (length/stop) with "error". Downstream, map_responses_to_chunks turns any finish_reason == "error" into an ErrorChunk, which the chat-completions adapter serializes as {"error": {"type": "InternalServerError", "code": 500}}.

So a routine, out-of-budget tool call is reported to clients as an internal server error rather than a truncated completion. Clients that treat that as an upstream/service failure (retrying, marking the endpoint unhealthy, failing over) then escalate a normal truncation into an apparent outage.

Fix

In the "tool call parsing interrupted" branch, keep the model's real finish_reason and yield the accumulated partial tool-call text. A truncated tool call now streams back as a standard finish_reason: "length" completion (partial text as content), so clients get normal OpenAI truncation semantics instead of an error event.

The separate failed-parse branch (complete tags that fail to parse into a tool call) is unchanged and still surfaces as an error.

Test

Adds test_truncated_tool_call_preserves_finish_reason for an unclosed tool call cut off by the stop reason; the existing test_failed_parse_yields_text (genuine parse failure → error) still passes.

A tool call cut off by the model's stop reason (length/stop) before its
closing tag was relabeled finish_reason="error", which downstream
serializes as an InternalServerError error chunk — turning an
out-of-budget completion into a server error.

Keep the model's real finish_reason and yield the accumulated partial
tool-call text instead, so clients receive standard truncation semantics
(finish_reason="length") rather than an error event. Add a regression
test for the truncated-tool-call path; the existing failed-parse path
(complete tags that fail to parse) still yields an error as before.
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.

1 participant