From d29c0ca389c9e6f01e663dc30c38744487d0fdd0 Mon Sep 17 00:00:00 2001 From: ztimson Date: Fri, 18 Sep 2026 02:15:02 -0400 Subject: [PATCH] Fixed opanai early termination from tool calls --- package.json | 2 +- src/open-ai.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 29ff173..bdc4341 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ztimson/ai-utils", - "version": "1.6.8", + "version": "1.6.9", "description": "AI Utility library", "author": "Zak Timson", "license": "MIT", diff --git a/src/open-ai.ts b/src/open-ai.ts index 97baeb6..c507b96 100644 --- a/src/open-ai.ts +++ b/src/open-ai.ts @@ -98,11 +98,12 @@ export class OpenAi extends LLMProvider { throw err; }); - let usage: any, msg: any = {content: '', tool_calls: []}; + let usage: any, finishReason: string | undefined, msg: any = {content: '', tool_calls: []}; if(options.stream) { for await (const chunk of resp) { if(controller.signal.aborted) break; if(chunk.usage) usage = chunk.usage; + if(chunk.choices[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason; if(chunk.choices[0]?.delta?.content) { msg.content += chunk.choices[0].delta.content; options.stream({text: chunk.choices[0].delta.content}); @@ -126,11 +127,16 @@ export class OpenAi extends LLMProvider { } } else { usage = resp.usage; + finishReason = resp.choices[0].finish_reason; msg = resp.choices[0].message; } const duration = Date.now() - callStart; const tps = usage?.completion_tokens && duration > 0 ? usage.completion_tokens / (duration / 1000) : 0; + if(finishReason === 'length') { + console.warn('[OpenAi] Response truncated: max_completion_tokens reached before finish_reason=stop'); + } + const toolCalls = msg.tool_calls || []; if(toolCalls.length && !controller.signal.aborted) { if(msg.content?.trim()) history.push({role: 'assistant', content: msg.content.trim(), timestamp: Date.now(), duration, tps}); @@ -147,7 +153,7 @@ export class OpenAi extends LLMProvider { if(!tool) { entry.error = 'Tool not found'; return; } try { const toolStream = options.stream && ((chunk: any) => { - if(chunk.done) { terminal = true; return; } + if(chunk.done) return; options.stream!(chunk); }); const result = await tool.fn(entry.args, toolStream, this.ai, tc.id);