diff --git a/src/modules/aix/server/dispatch/chatGenerate/parsers/openai.parser.ts b/src/modules/aix/server/dispatch/chatGenerate/parsers/openai.parser.ts index 19fb4ee11..ab92b298b 100644 --- a/src/modules/aix/server/dispatch/chatGenerate/parsers/openai.parser.ts +++ b/src/modules/aix/server/dispatch/chatGenerate/parsers/openai.parser.ts @@ -206,9 +206,12 @@ export function createOpenAIChatCompletionsChunkParser(): ChatGenerateParseFunct for (const reasoningDetail of delta.reasoning_details) { // Extract text from reasoning blocks based on type - if (reasoningDetail.type === 'reasoning.text' && typeof reasoningDetail.text === 'string') { - pt.appendReasoningText(reasoningDetail.text); - deltaHasReasoning = true; + if (reasoningDetail.type === 'reasoning.text') { + if (typeof reasoningDetail.text === 'string') { + pt.appendReasoningText(reasoningDetail.text); + deltaHasReasoning = true; + } + // else: empty reasoning chunk, e.g. "{ type: 'reasoning.text' }", skip } // Summaries can also be shown as reasoning else if (reasoningDetail.type === 'reasoning.summary' && typeof reasoningDetail.summary === 'string') { @@ -474,8 +477,10 @@ export function createOpenAIChatCompletionsParserNS(): ChatGenerateParseFunction // [OpenRouter, 2025-01-20] Handle structured reasoning_details if (Array.isArray(message.reasoning_details)) { for (const reasoningDetail of message.reasoning_details) { - if (reasoningDetail.type === 'reasoning.text' && typeof reasoningDetail.text === 'string') { - pt.appendReasoningText(reasoningDetail.text); + if (reasoningDetail.type === 'reasoning.text') { + if (typeof reasoningDetail.text === 'string') + pt.appendReasoningText(reasoningDetail.text); + // else: empty reasoning chunk, e.g. "{ type: 'reasoning.text' }", skip } else if (reasoningDetail.type === 'reasoning.summary' && typeof reasoningDetail.summary === 'string') { // pt.appendReasoningText(`[Summary] ${reasoningDetail.summary}`); pt.appendReasoningText(reasoningDetail.summary); diff --git a/src/modules/aix/server/dispatch/wiretypes/openai.wiretypes.ts b/src/modules/aix/server/dispatch/wiretypes/openai.wiretypes.ts index 6c35a39f0..f2f2d95e7 100644 --- a/src/modules/aix/server/dispatch/wiretypes/openai.wiretypes.ts +++ b/src/modules/aix/server/dispatch/wiretypes/openai.wiretypes.ts @@ -115,8 +115,8 @@ export namespace OpenAIWire_ContentParts { z.enum(['reasoning.summary', 'reasoning.text', 'reasoning.encrypted']), z.string(), ]), - text: z.string().optional(), // Actual reasoning text (for 'text' type) - summary: z.string().optional(), // Summary of reasoning (for 'summary' type) + text: z.string().nullish(), // Actual reasoning text (for 'text' type) + summary: z.string().nullish(), // Summary of reasoning (for 'summary' type) // we don't use these for now: // signature: z.string().nullable().optional(), // Signature verification (for 'text' type) // // 'encrypted' type has 'data' field - indicates reasoning happened but not returned