Skip to content

Commit 3ff3255

Browse files
hkiratclaude
andcommitted
fix: speed up setup agent loop and detect frontend port correctly
- Execute tool calls in parallel instead of sequentially - Add 2-minute timeout on LLM calls to prevent hanging - Reduce DB writes (single persist per iteration, not per tool call) - Setup prompt now explicitly detects frontend port for virtual browser - Session prompt clarifies devServerPort is the frontend port Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent db18cfa commit 3ff3255

1 file changed

Lines changed: 41 additions & 25 deletions

File tree

apps/server/src/services/setup.service.ts

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,30 @@ let setupTokensOut = 0;
164164

165165
async function callLLM(messages: any[]): Promise<any> {
166166
const { url, key, model } = getLLMConfig();
167-
const res = await fetch(url, {
168-
method: "POST",
169-
headers: {
170-
"Content-Type": "application/json",
171-
Authorization: `Bearer ${key}`,
172-
"X-Title": "Vendi",
173-
},
174-
body: JSON.stringify({ model, messages, tools, max_tokens: 4096, parallel_tool_calls: true }),
175-
});
176-
if (!res.ok) throw new Error(`LLM error ${res.status}: ${await res.text()}`);
177-
const json = await res.json();
178-
const usage = json?.usage;
179-
if (usage) {
180-
setupTokensIn += usage.prompt_tokens || 0;
181-
setupTokensOut += usage.completion_tokens || 0;
167+
const controller = new AbortController();
168+
const timeout = setTimeout(() => controller.abort(), 120_000); // 2 min timeout
169+
try {
170+
const res = await fetch(url, {
171+
method: "POST",
172+
headers: {
173+
"Content-Type": "application/json",
174+
Authorization: `Bearer ${key}`,
175+
"X-Title": "Vendi",
176+
},
177+
body: JSON.stringify({ model, messages, tools, max_tokens: 4096, parallel_tool_calls: true }),
178+
signal: controller.signal,
179+
});
180+
if (!res.ok) throw new Error(`LLM error ${res.status}: ${await res.text()}`);
181+
const json = await res.json();
182+
const usage = json?.usage;
183+
if (usage) {
184+
setupTokensIn += usage.prompt_tokens || 0;
185+
setupTokensOut += usage.completion_tokens || 0;
186+
}
187+
return json;
188+
} finally {
189+
clearTimeout(timeout);
182190
}
183-
return json;
184191
}
185192

186193
// ── DB persistence helpers ──────────────────────────────────────────────────
@@ -260,15 +267,24 @@ async function runAgentLoop(ctx: LoopContext): Promise<string> {
260267
ctx.llmMessages.push(msg);
261268

262269
if (msg.tool_calls && msg.tool_calls.length > 0) {
263-
for (const tc of msg.tool_calls) {
264-
const { name, arguments: argsStr } = tc.function;
265-
const statusMap: Record<string, string> = { read_file: "Reading files...", list_files: "Browsing project...", search_code: "Searching code...", run_command: "Running commands..." };
266-
267-
await persistState(ctx.projectId, { status: statusMap[name] || "Working..." });
270+
const toolNames = msg.tool_calls.map((tc: any) => tc.function.name);
271+
const statusMsg = toolNames.length > 1
272+
? `Running ${toolNames.length} tools in parallel...`
273+
: { read_file: "Reading files...", list_files: "Browsing project...", search_code: "Searching code...", run_command: "Running commands..." }[toolNames[0]] || "Working...";
274+
await persistState(ctx.projectId, { status: statusMsg });
275+
276+
// Execute all tool calls in parallel
277+
const results = await Promise.all(
278+
msg.tool_calls.map(async (tc: any) => {
279+
const { name, arguments: argsStr } = tc.function;
280+
let args: Record<string, string>;
281+
try { args = JSON.parse(argsStr); } catch { args = {}; }
282+
const result = await executeTool(ctx.sandbox, name, args);
283+
return { tc, name, args, result };
284+
})
285+
);
268286

269-
let args: Record<string, string>;
270-
try { args = JSON.parse(argsStr); } catch { args = {}; }
271-
const result = await executeTool(ctx.sandbox, name, args);
287+
for (const { tc, name, args, result } of results) {
272288
ctx.toolCalls.push({
273289
id: crypto.randomUUID(),
274290
name,
@@ -279,7 +295,7 @@ async function runAgentLoop(ctx: LoopContext): Promise<string> {
279295
ctx.llmMessages.push({ role: "tool", tool_call_id: tc.id, content: result });
280296
}
281297

282-
// Persist after each LLM iteration (batch tool calls)
298+
// Persist once after all tool calls complete
283299
await persistState(ctx.projectId, {
284300
toolCalls: ctx.toolCalls,
285301
llmHistory: ctx.llmMessages,

0 commit comments

Comments
 (0)