Skip to content

Commit d76a97e

Browse files
committed
fix: guard against non-string content delta and thinking blocks
Mistral (and potentially other providers) may send thinking content as an object in choice.delta.content rather than a string. Without a type guard, the object is coerced to "[object Object]" when appended to the text buffer. Two fixes: 1. openai-transport-stream.ts: add typeof === "string" check on choice.delta.content before appending to the text buffer 2. message-extract.ts: handle object-form thinking blocks where the text lives in a .text property instead of being a plain string Fixes #68309
1 parent 9a93ea9 commit d76a97e

2 files changed

Lines changed: 15 additions & 5 deletions

File tree

src/agents/openai-transport-stream.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1182,7 +1182,7 @@ async function processOpenAICompletionsStream(
11821182
if (!choice.delta) {
11831183
continue;
11841184
}
1185-
if (choice.delta.content) {
1185+
if (typeof choice.delta.content === "string" && choice.delta.content) {
11861186
if (currentBlock?.type === "toolCall") {
11871187
queuePostToolCallDelta({ kind: "text", text: choice.delta.content });
11881188
} else {

ui/src/ui/chat/message-extract.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,20 @@ export function extractThinking(message: unknown): string | null {
4848
if (Array.isArray(content)) {
4949
for (const p of content) {
5050
const item = p as Record<string, unknown>;
51-
if (item.type === "thinking" && typeof item.thinking === "string") {
52-
const cleaned = item.thinking.trim();
53-
if (cleaned) {
54-
parts.push(cleaned);
51+
if (item.type === "thinking") {
52+
const thinkingValue =
53+
typeof item.thinking === "string"
54+
? item.thinking
55+
: typeof item.thinking === "object" &&
56+
item.thinking !== null &&
57+
typeof (item.thinking as Record<string, unknown>).text === "string"
58+
? ((item.thinking as Record<string, unknown>).text as string)
59+
: null;
60+
if (thinkingValue) {
61+
const cleaned = thinkingValue.trim();
62+
if (cleaned) {
63+
parts.push(cleaned);
64+
}
5565
}
5666
}
5767
}

0 commit comments

Comments
 (0)