From a79ca41190c769e1bee21560e3921be8d697b238 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 14 Mar 2026 03:59:55 +0100 Subject: [PATCH] fix(ai): handle unknown finish_reason in openai-completions gracefully Map unknown finish_reason values (e.g. "end" from Ollama/LM Studio) to "stop" instead of throwing, since assistant content is already produced. fixes #2142 --- packages/ai/CHANGELOG.md | 4 ++++ packages/ai/src/providers/openai-completions.ts | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 27fa6f25..92e61fd0 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Handle `finish_reason: "end"` from Ollama/LM Studio by mapping it to `"stop"` instead of throwing ([#2142](https://github.com/badlogic/pi-mono/issues/2142)) + ## [0.58.0] - 2026-03-14 ### Added diff --git a/packages/ai/src/providers/openai-completions.ts b/packages/ai/src/providers/openai-completions.ts index beca1132..d208089e 100644 --- a/packages/ai/src/providers/openai-completions.ts +++ b/packages/ai/src/providers/openai-completions.ts @@ -730,10 +730,11 @@ function parseChunkUsage( return usage; } -function mapStopReason(reason: ChatCompletionChunk.Choice["finish_reason"]): StopReason { +function mapStopReason(reason: ChatCompletionChunk.Choice["finish_reason"] | string): StopReason { if (reason === null) return "stop"; switch (reason) { case "stop": + case "end": return "stop"; case "length": return "length"; @@ -743,7 +744,7 @@ function mapStopReason(reason: ChatCompletionChunk.Choice["finish_reason"]): Sto case "content_filter": return "error"; default: { - const _exhaustive: never = reason; + const _exhaustive: never = reason as never; throw new Error(`Unhandled stop reason: ${_exhaustive}`); } }