Reviewed PR details and related session.
UnderstandIdentified API differences between OpenRouter and Vercel.
UnderstandAnalyzed tool call token leakage issue in Vercel session.
UnderstandYou are given one or more GitHub PR URLs: https://github.com/badlogic/pi-mono/pull/987 pull inot a local branch please
For each PR URL, do the following in order:
packages/*/CHANGELOG.md files. Report whether an entry exists. If missing, state that a changelog entry is required before merge and that you will add it if the user decides to merge. Follow the changelog format rules in AGENTS.md. Verify:
### Breaking Changes, ### Added, ### Fixed, etc.)Fixed foo ([#123](https://github.com/badlogic/pi-mono/pull/123) by [@user](https://github.com/user))### Breaking Changes, not just ### FixedOutput format per PR: PR: Changelog:
If no issues are found, say so under Bad and Ugly.
continue
The user wants me to review a GitHub PR. Let me start by fetching the PR details.
>gh pr view 987 --repo badlogic/pi-mono --json title,body,comments,commits,files,author,state,labels,headRefName,baseRefName>gh pr diff 987 --repo badlogic/pi-monoNow let me read the current main branch versions of the files being modified to compare against the diff.
Let me read the relevant source files to understand the full context:
Let me now check if there are any linked issues and check the changelog format. The PR already has a changelog entry. Let me also check the README and docs to see if any documentation updates are needed.
Looking at the PR:
reasoning_content for OpenRouterreasoning_content when reasoning is enabled but missing for tool callsThe changelog entry in the PR diff is:
### Fixed
- Fixed OpenRouter Kimi K2.5 tool-call replay by mapping reasoning fields to `reasoning_content` and ensuring it is present when reasoning is enabled.This entry doesn't include the PR link and author attribution as required for external contributions. Let me check if any documentation or README changes are needed.
Let me check if there are any documentation updates needed and review the PR comments more carefully:
>ls /Users/badlogic/workspaces/pi-mono/packages/coding-agent/docs/Let me now pull the PR into a local branch as the user requested.
Now let me pull the PR into a local branch:
>cd /Users/badlogic/workspaces/pi-mono && git fetch origin && git checkout -b fix/kimi-pr-987 && gh pr checkout 987 --detach && git checkout -b fix/kimi-pr-987-workI've pulled the PR into a local branch. Let me provide the structured review.
Looking at the changes:
openai-completions.ts changes:
reasoningEnabled detection from options?.reasoningEffortreasoningEnabled to convertMessagesreasoningEnabled parameter and isOpenRouter detectionreasoning_contentreasoning_content when reasoning is enabled and assistant has tool callsCHANGELOG.md: Entry exists but missing PR link and author attribution
models.generated.ts: This is auto-generated, so the changes (adding Kimi K2.5, removing some models) are fine
Let me provide the review.
PR: https://github.com/badlogic/pi-mono/pull/987
packages/ai/CHANGELOG.md under ### FixedFixed OpenRouter Kimi K2.5 tool-call replay by mapping reasoning fields to reasoning_content and ensuring it is present when reasoning is enabled.Fixed OpenRouter Kimi K2.5 tool-call replay by mapping reasoning fields to \reasoning_content` and ensuring it is present when reasoning is enabled (#987 by @banteg)`reasoning_content specifically, not reasoning or reasoning_textreasoning_content: "" when missing prevents 400 errors without changing behavior for working conversationsreasoningEnabled && isOpenRoutermodel.provider === "openrouter" won't work for Vercel AI Gateway or other proxies that route to Kimi. Checking model slug would be more robust (though this is an enhancement, not blocking)reasoning_details with type: "reasoning.text" but pi-ai only stores reasoning.encrypted type. The PR's empty string fix stops the error but doesn't preserve actual reasoning. This is a valid observation, though the current fix is simpler and sufficient for the error casereasoningEnabled boolean to convertMessages() based on options.reasoningEffortmodel.provider === "openrouter"reasoning, reasoning_text, reasoning_content) to reasoning_content for OpenRouterreasoning_content: "" to assistant tool-call messages when reasoning is enabled, OpenRouter, and the field is missingreasoning_content"npm run check passes (per PR description)The PR is now in local branch fix/kimi-pr-987-work. If you want to merge, I'll need to fix the changelog attribution first.
/Users/badlogic/.pi/agent/sessions/--Users-badlogic-workspaces-pi-mono--/2026-01-29T02-05-25-874Z_bb1f1612-2f68-4d2b-a37e-f97536a9d087.jsonl
any idea if we use @packages/ai/src/providers/openai-completions.ts for vercel? this is a session where the model eventually spits out a thinking trace that includes a tool call
The user is asking if the openai-completions.ts provider is used for Vercel AI Gateway, and they're referencing a session file. Let me check the session file and also look at how Vercel AI Gateway is configured to understand which API it uses.
>cd /Users/badlogic/workspaces/pi-mono && rg "vercel-ai-gateway" packages/ai/src --type ts -lLooking at the session file, I can see the model is moonshotai/kimi-k2.5 with provider vercel-ai-gateway and API anthropic-messages.
So the answer is: No, Vercel AI Gateway with Kimi K2.5 uses anthropic-messages API, not openai-completions.
The session shows:
"api":"anthropic-messages","provider":"vercel-ai-gateway","model":"moonshotai/kimi-k2.5"Looking at the last message, the assistant's thinking block contains a malformed tool call section that leaked into the thinking output:
<|tool_calls_section_begin|> <|tool_call_begin|> functions.bash:22 <|tool_call_argument_begin|> {\"command\": \"cat packages/*/CHANGELOG.md | grep -c \\\"^### \\\" && cat packages/*/CHANGELOG.md | grep -c \\\"^## \\\\[\\\" | head -5\"} <|tool_call_end|>...This is Kimi's internal format leaking through. The thinking trace contains what should have been tool calls, but they got emitted as thinking content instead.
So the PR fix for openai-completions.ts won't help Vercel AI Gateway because Vercel uses anthropic-messages API for Kimi, not openai-completions.
Let me check how Vercel models are configured:
Looking at the session, Vercel AI Gateway with Kimi K2.5 uses anthropic-messages API, not openai-completions:
"api":"anthropic-messages","provider":"vercel-ai-gateway","model":"moonshotai/kimi-k2.5"So the PR fix won't help Vercel because it only modifies openai-completions.ts.
The last message in that session shows the problem: Kimi's internal tool call format leaked into the thinking trace:
<|tool_calls_section_begin|> <|tool_call_begin|> functions.bash:22 <|tool_call_argument_begin|> {"command": "..."} <|tool_call_end|>...This is the model outputting what should be tool calls as raw text inside the thinking block. The stop reason is "stop" (not "toolUse"), so the tool calls were never properly parsed.
Let me check how Vercel models are configured:
>cd /Users/badlogic/workspaces/pi-mono && rg "kimi-k2.5" packages/ai/src/models.generated.ts -A 15>cd /Users/badlogic/workspaces/pi-mono && rg "vercel-ai-gateway" packages/ai/src/providers/ -lInteresting. So:
openai-completions APIanthropic-messages APIThe PR only fixes the openai-completions provider. Vercel routes through anthropic-messages, which is the Anthropic provider code.
The problem in the session is that Kimi is outputting its internal tool call format (<|tool_calls_section_begin|>) as text inside thinking blocks instead of proper tool calls. This is a model-level issue or an API format mismatch.
The PR's approach of ensuring reasoning_content is present wouldn't help Vercel since Vercel uses the Anthropic API format, not OpenAI format.
For Vercel, the issue is different: Kimi's thinking mode seems to be producing malformed output where tool calls get emitted as raw text in the thinking trace. This could be:
Let me check if there's any Kimi-specific handling in the anthropic provider.
So the picture is:
openai-completions API → PR fix applies hereanthropic-messages API → PR fix does NOT applyThe session you shared shows Kimi outputting its internal tool call format (<|tool_calls_section_begin|>) as raw text inside the thinking block instead of proper tool calls. This is a different bug that the PR doesn't address.
Let me check the Anthropic provider to see if there's anything relevant:
>cd /Users/badlogic/workspaces/pi-mono && rg "reasoning|thinking" packages/ai/src/providers/anthropic.ts -n | head -40Okay, so the Anthropic provider has proper thinking handling. But Kimi isn't Anthropic, and when Vercel routes Kimi through the anthropic-messages API format, Kimi apparently emits its internal tool call markers as raw text.
This is a Vercel/Kimi-specific problem, not something the PR addresses. The PR is specifically for OpenRouter's Kimi handling via the openai-completions API.
The session you showed me demonstrates that Kimi's thinking mode with tools doesn't work properly on Vercel AI Gateway. The model outputs <|tool_calls_section_begin|> tokens instead of proper API-formatted tool calls.
Summary for the user:
So the PR is narrowly scoped to OpenRouter, and the Vercel issue is a separate bug (possibly in Vercel's API translation layer or Kimi's compatibility with the Anthropic message format).
The Anthropic provider has proper thinking handling for Claude, but Kimi isn't Claude. When Vercel routes Kimi through anthropic-messages API format, Kimi appears to emit its internal control tokens (<|tool_calls_section_begin|>, <|tool_call_begin|>, etc.) as raw text instead of proper tool calls.
Summary:
openai-completions APIanthropic-messages API for Kimi, and the model outputs its internal tool call format as literal text in thinking tracesThe Vercel issue is likely:
This aligns with badlogic's comment on the PR: "It craps itself on Vercel as well" and "OpenRouter is clown shit, HF is meh, rest is fine". The "rest is fine" probably refers to providers that route directly to Moonshot (like OpenCode Zen), not Vercel.
Should we still merge the PR for the OpenRouter fix, or wait for broader Kimi support to stabilize?
### Fixed
- Fixed OpenRouter Kimi K2.5 tool-call replay by mapping reasoning fields to `reasoning_content` and ensuring it is present when reasoning is enabled."api":"anthropic-messages","provider":"vercel-ai-gateway","model":"moonshotai/kimi-k2.5"<|tool_calls_section_begin|> <|tool_call_begin|> functions.bash:22 <|tool_call_argument_begin|> {\"command\": \"cat packages/*/CHANGELOG.md | grep -c \\\"^### \\\" && cat packages/*/CHANGELOG.md | grep -c \\\"^## \\\\[\\\" | head -5\"} <|tool_call_end|>..."api":"anthropic-messages","provider":"vercel-ai-gateway","model":"moonshotai/kimi-k2.5"<|tool_calls_section_begin|> <|tool_call_begin|> functions.bash:22 <|tool_call_argument_begin|> {"command": "..."} <|tool_call_end|>...