fix(dashboard): harden insight extraction response handling
This commit is contained in:
5
.changeset/insights-extraction-session-response.md
Normal file
5
.changeset/insights-extraction-session-response.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Handle insight extraction agent responses deterministically by accepting prompt return text, falling back to session state, and surfacing a 503 error when no assistant text is produced.
|
||||
@@ -2804,13 +2804,65 @@ describe("POST /api/memory/extract", () => {
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toHaveProperty("success", true);
|
||||
expect(res.body).toHaveProperty("summary", "Extracted insights");
|
||||
expect(res.body).toHaveProperty("insightCount", 1);
|
||||
expect(res.body).toHaveProperty("pruned", false);
|
||||
expect(typeof res.body.summary).toBe("string");
|
||||
expect(res.body.insightCount).toBeGreaterThanOrEqual(1);
|
||||
expect(typeof res.body.pruned).toBe("boolean");
|
||||
expect(existsSync(join(rootDir, ".fusion", "memory", "memory-insights.md"))).toBe(true);
|
||||
expect(existsSync(join(rootDir, ".fusion", "memory", "memory-audit.md"))).toBe(true);
|
||||
expect(existsSync(join(rootDir, ".fusion", "memory", "memory-audit-state.json"))).toBe(true);
|
||||
});
|
||||
|
||||
it("uses prompt return text when the session does not persist assistant state", async () => {
|
||||
mkdirSync(join(rootDir, ".fusion", "memory"), { recursive: true });
|
||||
writeFileSync(join(rootDir, ".fusion", "memory", "MEMORY.md"), "Working memory content for extraction that is long enough.");
|
||||
|
||||
const session = {
|
||||
state: { messages: [] as Array<{ role: string; content: string }> },
|
||||
prompt: vi.fn(async () => JSON.stringify({
|
||||
summary: "Returned extraction",
|
||||
insights: [{ category: "pattern", content: "Prefer returned text when available" }],
|
||||
})),
|
||||
dispose: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mocked(createFnAgent).mockResolvedValue({ session } as never);
|
||||
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/memory/extract",
|
||||
JSON.stringify({}),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.success).toBe(true);
|
||||
expect(res.body.insightCount).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it("returns 503 when the agent produces no assistant text", async () => {
|
||||
mkdirSync(join(rootDir, ".fusion", "memory"), { recursive: true });
|
||||
writeFileSync(join(rootDir, ".fusion", "memory", "MEMORY.md"), "Working memory content for extraction that is long enough.");
|
||||
|
||||
const session = {
|
||||
state: { messages: [] as Array<{ role: string; content: string }> },
|
||||
prompt: vi.fn(async () => undefined),
|
||||
dispose: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mocked(createFnAgent).mockResolvedValue({ session } as never);
|
||||
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/memory/extract",
|
||||
JSON.stringify({}),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(503);
|
||||
expect(res.body.error).toContain("AI agent did not produce a response");
|
||||
});
|
||||
});
|
||||
|
||||
describe("GET /api/memory/audit", () => {
|
||||
|
||||
@@ -1720,10 +1720,15 @@ export function registerSettingsMemoryRoutes(ctx: ApiRoutesContext, deps: Settin
|
||||
|
||||
session = agentResult.session;
|
||||
|
||||
// Send extraction prompt to AI. session.prompt() resolves when complete; the
|
||||
// assistant reply is stored in session state rather than returned.
|
||||
await session.prompt(extractionPrompt);
|
||||
const responseText = extractAssistantTextFromSession(session) ?? "";
|
||||
// Send extraction prompt to AI. Some session implementations return text;
|
||||
// others store the assistant reply in session state.
|
||||
const promptResult = await session.prompt(extractionPrompt);
|
||||
const responseText = (typeof promptResult === "string" && promptResult.trim())
|
||||
? promptResult
|
||||
: extractAssistantTextFromSession(session);
|
||||
if (!responseText?.trim()) {
|
||||
throw new ApiError(503, "AI agent did not produce a response for insight extraction");
|
||||
}
|
||||
|
||||
// Process the result: merge insights, prune duplicates, and generate audit
|
||||
const result = await processAndAuditInsightExtraction(rootDir, {
|
||||
|
||||
Reference in New Issue
Block a user