-
Notifications
You must be signed in to change notification settings - Fork 138
Description
Description
When using Kimi K2.5 model via OpenRouter (or OpenRouter-compatible APIs like OpenCode), the finishReason field is undefined in the finish-step event, even when the model made tool calls. This causes agentic applications to incorrectly determine the conversation has ended.
Steps to Reproduce
- Configure the OpenRouter provider with Kimi K2.5 model (
moonshotai/kimi-k2.5) - Send a request that triggers tool calls
- Observe the
finish-stepevent
Expected Behavior
The finishReason should be "tool-calls" when the model response includes tool calls:
```json
{
"type": "finish-step",
"finishReason": "tool-calls"
}
```
Or "stop" when the response is complete text.
Actual Behavior
The finishReason is undefined:
```json
{
"type": "step_finish",
"part": {
"reason": "unknown",
"tokens": { "input": 0, "output": 0, "reasoning": 0 },
"model": {
"providerID": "opencode",
"requestedModelID": "kimi-k2.5-free",
"respondedModelID": "kimi-k2.5"
}
}
}
```
Impact
This breaks agentic loops that depend on finishReason to determine whether to continue. For example:
```typescript
// In an agentic loop
if (lastAssistant?.finish && lastAssistant.finish !== 'tool-calls') {
break; // Exit loop
}
```
When finishReason is undefined (converted to "unknown"), the loop incorrectly exits even though tool calls were made and the agent should continue working.
Environment
- @openrouter/ai-sdk-provider version: 1.5.4
- Model:
moonshotai/kimi-k2.5(also affectsopencode/kimi-k2.5-free) - Using streaming mode with tool calls
Possible Causes
- Kimi K2.5 API might not populate
finish_reasonin SSE chunks - The SDK might not be extracting it from the correct location in the response
Suggested Fix
Similar to issue #419, the SDK could infer finishReason when it's undefined:
```typescript
// In flush() or finish-step handling
if (finishReason === undefined) {
// If we had tool calls, finish reason should be tool-calls
if (hasToolCalls) {
finishReason = 'tool-calls';
}
// If we have usage data, assume it completed successfully
else if (openrouterUsage?.totalTokens > 0) {
finishReason = 'stop';
}
}
```
Related Issues
- Standard usage object empty when usage data is in providerMetadata #419 - Standard usage object empty (same root cause - data in wrong location)
- v6.0.0-alpha.1: Regressions in streamText tool call handling (finishReason, inputs, and message validation) #332 - v6.0.0-alpha.1 regressions in streamText tool call handling
Workaround
Applications can work around this by treating undefined/"unknown" finish reasons as incomplete when tool calls exist:
```typescript
const isComplete = finishReason !== undefined &&
finishReason !== 'unknown' &&
finishReason !== 'tool-calls';
```