fix(workers-ai-provider): close reasoning block before tool calls and text#455
Merged
threepointone merged 2 commits intocloudflare:mainfrom Mar 25, 2026
Conversation
🦋 Changeset detectedLatest commit: e02cdd2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
… text When a model (e.g. Kimi K2.5) streams reasoning_content followed by tool_calls or text content, the reasoning block was never closed before the new content type began. This caused the AI SDK to misinterpret the stream structure, leading to silent failures where tool calls or text responses were lost. This matches the behavior already implemented in @ai-sdk/openai-compatible (vercel/ai), which properly emits reasoning-end before transitioning to tool-input-start or text-start events. The fix applies to both native and OpenAI-compatible response formats. Nulling reasoningId on transition prevents double-close in flush(). Fixes tool calling with reasoning models on Workers AI (observed with @cf/moonshotai/kimi-k2.5 where multi-step tool loops would produce reasoning-only responses with no visible text). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7650a6c to
e02cdd2
Compare
commit: |
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a reasoning model (e.g. Kimi K2.5 via
@cf/moonshotai/kimi-k2.5) streamsreasoning_contentfollowed bytool_callsor textcontent, the workers-ai-provider never closes the reasoning block before emitting the new content type. This causes the AI SDK to misinterpret the stream structure, leading to silent failures where tool call results or text responses are lost.Root Cause
In
streaming.ts, thegetMappedStream()transform emitsreasoning-startandreasoning-deltaevents but only emitsreasoning-endinflush()— after the entire stream completes. When the model transitions from reasoning to tool calls or text mid-stream, the reasoning block stays open, and downstream consumers (AI SDK'sstreamText) can't properly detect the content type transition.The Fix
Close the active reasoning block (
reasoning-end+ nullreasoningId) before emitting:text-start/text-delta) — both native and OpenAI formattool-input-start/tool-input-delta) — both native and OpenAI formatThis matches the behavior already implemented in
@ai-sdk/openai-compatible, which properly emitsreasoning-endbefore transitioning to tool or text events.Nulling
reasoningIdon transition prevents double-close inflush().Reproduction
Observed with
@cf/moonshotai/kimi-k2.5on Workers AI:reasoning_content(thinking about the request)tool_calls(first tool call works correctly withfinish_reason: "tool_calls")reasoning_contentagain, then tries another tool callreasoning_contentas raw tokens (<|tool_call_begin|>,<|tool_calls_section_end|>)finish_reason: "stop"with zero text content → user sees nothingThe structural fix ensures reasoning blocks are properly closed so the AI SDK can detect tool calls even when the model's output format is imperfect.
Related: vercel/ai#11409 (same Kimi K2 issue on AWS Bedrock)
Test Plan
flush()guard (if (reasoningId)) prevents double-close when already nulled🤖 Generated with Claude Code