fix(engine): treat finish_reason=repeat as soft stop
Moonshot/Kimi returns a non-standard finish_reason of "repeat" when its server-side repetition detector trips. pi-ai surfaces this as a fatal session state error, killing the agent heartbeat. Filter that case in promptSessionAndCheck so the truncated turn is logged and the heartbeat continues on the next tick. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
.changeset/engine-finish-reason-repeat-soft-stop.md
Normal file
5
.changeset/engine-finish-reason-repeat-soft-stop.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Treat OpenAI-compatible `finish_reason: repeat` (raised by Moonshot/Kimi when its server-side repetition detector trips) as a soft stop in the engine heartbeat instead of a fatal error, so agent runs survive the truncation and can continue on the next tick.
|
||||
@@ -167,6 +167,16 @@ async function promptSessionAndCheck(session: AgentSession, prompt: string, opti
|
||||
piLog.warn(`pi state error — failed to inspect transcript: ${inspectErr instanceof Error ? inspectErr.message : String(inspectErr)}`);
|
||||
}
|
||||
}
|
||||
// Some OpenAI-compatible providers (notably Moonshot/Kimi) end generation
|
||||
// with a non-standard `finish_reason: repeat` when their server-side
|
||||
// repetition detector trips. pi-ai surfaces this as a fatal state error,
|
||||
// but for our purposes the assistant turn is already complete — treat it
|
||||
// as a soft stop so the heartbeat keeps running.
|
||||
if (/Provider finish_reason:\s*repeat\b/i.test(stateError)) {
|
||||
piLog.warn(`pi state error — treating provider finish_reason=repeat as soft stop: ${stateError}`);
|
||||
clearSessionStateError(session);
|
||||
return;
|
||||
}
|
||||
throw new Error(stateError);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user