diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index 6678f21d68..f3ef13e2f0 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -137,15 +137,19 @@ export abstract class BaseOpenAiCompatibleProvider ) } + // Safely access delta with fallback const delta = chunk.choices?.[0]?.delta - if (delta?.content) { - for (const processedChunk of matcher.update(delta.content)) { - yield processedChunk - } - } - + // Only process if delta exists if (delta) { + // Process content + if (delta.content) { + for (const processedChunk of matcher.update(delta.content)) { + yield processedChunk + } + } + + // Process reasoning content for (const key of ["reasoning_content", "reasoning"] as const) { if (key in delta) { const reasoning_content = ((delta as any)[key] as string | undefined) || "" @@ -155,21 +159,26 @@ export abstract class BaseOpenAiCompatibleProvider break } } - } - // Emit raw tool call chunks - NativeToolCallParser handles state management - if (delta?.tool_calls) { - for (const toolCall of delta.tool_calls) { - yield { - type: "tool_call_partial", - index: toolCall.index, - id: toolCall.id, - name: toolCall.function?.name, - arguments: toolCall.function?.arguments, + // Emit raw tool call chunks - NativeToolCallParser handles state management + // Ensure tool_calls is properly handled even if structure varies + if (delta.tool_calls && Array.isArray(delta.tool_calls)) { + for (const toolCall of delta.tool_calls) { + // Validate toolCall structure before emitting + if (toolCall && typeof toolCall.index === "number") { + yield { + type: "tool_call_partial", + index: toolCall.index, + id: toolCall.id, + name: toolCall.function?.name, + arguments: toolCall.function?.arguments, + } + } } } } + // Process usage information if (chunk.usage) { lastUsage = chunk.usage } diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 2a2065edd6..097d9fcf6a 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -211,14 +211,18 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl } } - if (delta.tool_calls) { + // Emit raw tool call chunks with validation + if (delta.tool_calls && Array.isArray(delta.tool_calls)) { for (const toolCall of delta.tool_calls) { - yield { - type: "tool_call_partial", - index: toolCall.index, - id: toolCall.id, - name: toolCall.function?.name, - arguments: toolCall.function?.arguments, + // Validate toolCall structure before emitting + if (toolCall && typeof toolCall.index === "number") { + yield { + type: "tool_call_partial", + index: toolCall.index, + id: toolCall.id, + name: toolCall.function?.name, + arguments: toolCall.function?.arguments, + } } } } @@ -455,14 +459,17 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl } // Emit raw tool call chunks - NativeToolCallParser handles state management - if (delta.tool_calls) { + if (delta.tool_calls && Array.isArray(delta.tool_calls)) { for (const toolCall of delta.tool_calls) { - yield { - type: "tool_call_partial", - index: toolCall.index, - id: toolCall.id, - name: toolCall.function?.name, - arguments: toolCall.function?.arguments, + // Validate toolCall structure before emitting + if (toolCall && typeof toolCall.index === "number") { + yield { + type: "tool_call_partial", + index: toolCall.index, + id: toolCall.id, + name: toolCall.function?.name, + arguments: toolCall.function?.arguments, + } } } }