fix(chat): always emit narrative summary, even when tool-round cap is hit
Surfaced by the live Path B test: AI fired 7 tool calls (fs.read, fs.edit, kill, dev_server.start, curl, dev_server.logs, ...) in a single turn, the loop exited at MAX_TOOL_ROUNDS, and the user saw only a tray of ✓ icons — no text reply. Two changes: 1. Bump MAX_TOOL_ROUNDS 6 → 12. Path B iteration chains routinely run long; 6 was tuned for Path A's much-shorter Coolify-orchestration sequences. 2. When the loop exits because of the cap (the last assistant turn was a tool call, not a finish), force one more no-tools Gemini call with an explicit "summarize the result, do NOT call tools" prompt. That gives the user a sentence or two of context instead of a wall of green checkmarks. Wrapped in try/catch so the stream still terminates cleanly if Gemini errors. Made-with: Cursor
This commit is contained in:
@@ -19,7 +19,12 @@ import { callGeminiChat, streamGeminiChat } from '@/lib/ai/gemini-chat';
|
||||
import { VIBN_TOOL_DEFINITIONS, executeMcpTool } from '@/lib/ai/vibn-tools';
|
||||
import type { ChatMessage, ToolCall } from '@/lib/ai/gemini-chat';
|
||||
|
||||
const MAX_TOOL_ROUNDS = 6;
|
||||
// Bumped from 6 to 12 because Path B chains (devcontainer.ensure →
|
||||
// fs.read → fs.edit → kill → start → curl → logs) routinely fire 7-10
|
||||
// tool calls in one user turn. When the cap IS hit, we still emit a
|
||||
// narrative summary instead of leaving the user staring at a tool tray
|
||||
// (see the no-tools follow-up call below).
|
||||
const MAX_TOOL_ROUNDS = 12;
|
||||
|
||||
let chatTablesReady = false;
|
||||
async function ensureChatTables() {
|
||||
@@ -286,6 +291,32 @@ export async function POST(request: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// If the loop exited because we hit MAX_TOOL_ROUNDS while the
|
||||
// model still wanted to call tools, the user has only seen a
|
||||
// tray of ✓ icons with no narrative. Force one final no-tools
|
||||
// call so we always end on a human-readable summary.
|
||||
const lastTurnHadTools =
|
||||
messages.length > 0 &&
|
||||
messages[messages.length - 1].role === 'tool';
|
||||
if (round >= MAX_TOOL_ROUNDS && lastTurnHadTools) {
|
||||
try {
|
||||
const summary = await callGeminiChat({
|
||||
systemPrompt:
|
||||
systemPrompt +
|
||||
'\n\nYou have just executed a chain of tool calls. Summarize the result for the user in 1-3 sentences. Do NOT call any more tools.',
|
||||
messages,
|
||||
tools: [],
|
||||
temperature: 0.3,
|
||||
});
|
||||
if (summary.text) {
|
||||
assistantText += summary.text;
|
||||
emit({ type: 'text', text: summary.text });
|
||||
}
|
||||
} catch {
|
||||
// Don't let a failed summary kill the stream.
|
||||
}
|
||||
}
|
||||
|
||||
// Persist final assistant message
|
||||
const finalMsg: ChatMessage = {
|
||||
role: 'assistant',
|
||||
|
||||
Reference in New Issue
Block a user