import { describe, expect, it } from "bun:test"; import { createPromptSuggestionService, parseGeneratedSuggestions, promptSuggestionRequestSchema } from "../../src/chat/prompt-suggestions.js"; import type { LocalAgentContext } from "../../src/context/localContext.js"; const activity = { id: "alert:pressure", kind: "alert" as const, title: "城南压力告警", occurred_at: "2026-10-09T07:00:00Z", origin: "backend" as const }; const context = { actorKey: "actor-a", projectKey: "project-a", projectId: "project-a", userId: "user-a", network: "supply", traceId: "trace-a" } satisfies LocalAgentContext; const output = JSON.stringify({ suggestions: [{ question: "核对城南压力告警的监测证据与影响范围", activity_id: activity.id }] }); describe("Agent prompt suggestions", () => { it("accepts only bounded activity snapshots and suggestions referencing supplied records", () => { expect(promptSuggestionRequestSchema.safeParse({ activities: Array.from({ length: 13 }, (_, i) => ({ ...activity, id: String(i) })) }).success).toBe(false); expect(promptSuggestionRequestSchema.safeParse({ activities: [activity, activity] }).success).toBe(false); expect(parseGeneratedSuggestions(`\`\`\`json\n${output}\n\`\`\``, [activity])).toHaveLength(1); expect(() => parseGeneratedSuggestions(output.replace(activity.id, "other-project-alert"), [activity])).toThrow("unknown activity ID"); expect(() => parseGeneratedSuggestions(JSON.stringify({ suggestions: [JSON.parse(output).suggestions[0], JSON.parse(output).suggestions[0]] }), [activity])).toThrow("duplicate suggestion"); }); it("coalesces inference for the same actor, project, model and activity without sharing other scopes", async () => { const calls: string[] = []; const generate = createPromptSuggestionService({ generateText: async (prompt) => { calls.push(prompt); return output; } }); const request = { activities: [activity] }; const [a, b] = await Promise.all([generate(context, request, "test/model"), generate(context, request, "test/model")]); expect(a).toEqual(b); expect(calls).toHaveLength(1); expect(calls[0]).toContain(activity.title); await generate({ ...context, actorKey: "actor-b" }, request, "test/model"); await generate({ ...context, projectKey: "project-b" }, request, "test/model"); await generate(context, request, "test/other-model"); await generate(context, { activities: [{ ...activity, title: "城北压力告警" }] }, "test/model"); expect(calls).toHaveLength(5); }); it("does not cache malformed model output and supports generic questions without inventing activity references", async () => { let calls = 0; const generate = createPromptSuggestionService({ generateText: async () => ++calls === 1 ? "invalid JSON" : '{"suggestions":[{"question":"分析供水管网需要优先核对的监测指标","activity_id":null}]}' }); await expect(generate(context, { activities: [] }, "test/model")).rejects.toThrow(); expect((await generate(context, { activities: [] }, "test/model")).suggestions[0].activity_id).toBeNull(); expect(calls).toBe(2); }); });