fix: thread AbortSignal through LLM calls so stop_goal cancels immediately
The agent loop checked signal.aborted only at the top of each iteration, but the LLM fetch() call (which takes seconds) never received the signal. Now the signal is passed to fetch() and checked after LLM errors and before the inter-step sleep, so aborting takes effect mid-step.
This commit is contained in:
@@ -22,7 +22,8 @@ export interface LLMProvider {
|
|||||||
getAction(
|
getAction(
|
||||||
systemPrompt: string,
|
systemPrompt: string,
|
||||||
userPrompt: string,
|
userPrompt: string,
|
||||||
imageBase64?: string
|
imageBase64?: string,
|
||||||
|
signal?: AbortSignal
|
||||||
): Promise<string>;
|
): Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +382,8 @@ export function getLlmProvider(config: LLMConfig): LLMProvider {
|
|||||||
async getAction(
|
async getAction(
|
||||||
systemPrompt: string,
|
systemPrompt: string,
|
||||||
userPrompt: string,
|
userPrompt: string,
|
||||||
imageBase64?: string
|
imageBase64?: string,
|
||||||
|
signal?: AbortSignal
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const messages: Array<{ role: string; content: unknown }> = [
|
const messages: Array<{ role: string; content: unknown }> = [
|
||||||
{ role: "system", content: systemPrompt },
|
{ role: "system", content: systemPrompt },
|
||||||
@@ -418,6 +420,7 @@ export function getLlmProvider(config: LLMConfig): LLMProvider {
|
|||||||
max_tokens: 1024,
|
max_tokens: 1024,
|
||||||
response_format: { type: "json_object" },
|
response_format: { type: "json_object" },
|
||||||
}),
|
}),
|
||||||
|
signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
@@ -489,9 +489,11 @@ export async function runAgentLoop(
|
|||||||
rawResponse = await llm.getAction(
|
rawResponse = await llm.getAction(
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
userPrompt,
|
userPrompt,
|
||||||
useScreenshot ? screenshot : undefined
|
useScreenshot ? screenshot : undefined,
|
||||||
|
signal
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (signal?.aborted) break;
|
||||||
console.error(
|
console.error(
|
||||||
`[Agent ${sessionId}] LLM error at step ${step + 1}: ${(err as Error).message}`
|
`[Agent ${sessionId}] LLM error at step ${step + 1}: ${(err as Error).message}`
|
||||||
);
|
);
|
||||||
@@ -510,7 +512,8 @@ export async function runAgentLoop(
|
|||||||
rawResponse = await llm.getAction(
|
rawResponse = await llm.getAction(
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
userPrompt + "\n\nIMPORTANT: Your previous response was not valid JSON. You MUST respond with ONLY a valid JSON object.",
|
userPrompt + "\n\nIMPORTANT: Your previous response was not valid JSON. You MUST respond with ONLY a valid JSON object.",
|
||||||
useScreenshot ? screenshot : undefined
|
useScreenshot ? screenshot : undefined,
|
||||||
|
signal
|
||||||
);
|
);
|
||||||
parsed = parseJsonResponse(rawResponse);
|
parsed = parseJsonResponse(rawResponse);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -634,6 +637,7 @@ export async function runAgentLoop(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── 10. Brief pause for UI to settle ────────────────────
|
// ── 10. Brief pause for UI to settle ────────────────────
|
||||||
|
if (signal?.aborted) break;
|
||||||
await new Promise((r) => setTimeout(r, 500));
|
await new Promise((r) => setTimeout(r, 500));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user