From 78349bb22bc69415ffc10f7c9901635b8e8ab852 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 5 May 2026 17:09:32 +0000 Subject: [PATCH] fix: non-streaming tool calls now feed results back to AI for final answer Previously tool calls in non-streaming path returned raw tool output as the response. Now executes tool, sends results back to model for a synthesized answer. Fixes the 'silent after streaming fallback' bug. --- src/bot/index.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/bot/index.js b/src/bot/index.js index 67326f47..3b0cb1a5 100644 --- a/src/bot/index.js +++ b/src/bot/index.js @@ -390,20 +390,28 @@ export async function initBot(config, api, tools, skills, agents) { const msg = choice.message; if (msg.tool_calls?.length) { - const parts = []; + // Execute all tool calls, feed results back to AI for final answer + const toolMessages = [{ role: 'assistant', tool_calls: msg.tool_calls }]; for (const tc of msg.tool_calls) { const fn = tc.function; try { const handler = toolHandlers[fn.name]; - if (!handler) { parts.push(`❌ Unknown tool: ${fn.name}`); continue; } + if (!handler) { + toolMessages.push({ role: 'tool', tool_call_id: tc.id, content: `❌ Unknown tool: ${fn.name}` }); + continue; + } const args = JSON.parse(fn.arguments); + logger.info(`🔧 ${fn.name}(${fn.arguments?.slice(0, 80)})`); const result = await handler(args); - parts.push(`${result}`); + toolMessages.push({ role: 'tool', tool_call_id: tc.id, content: String(result).slice(0, 4000) }); } catch (e) { - parts.push(`❌ Tool ${fn.name} error: ${e.message}`); + toolMessages.push({ role: 'tool', tool_call_id: tc.id, content: `❌ Error: ${e.message}` }); } } - return parts.join('\n\n'); + // Ask AI to produce final text answer using tool results + const followUp = [...body.messages, ...toolMessages, { role: 'user', content: 'Based on the tool results above, provide your final answer.' }]; + const followRes = await api.client.post('/chat/completions', { ...body, messages: followUp, tools: [] }); + return followRes.data.choices?.[0]?.message?.content || '✅ Done.'; } return msg.content || '✅ Done.'; } catch (error) {