Files
next-tjwater-agent/tests/chat/promptSuggestions.test.ts
T
jiang 6a49d2209e feat(agent): unify MCP tools and adopt Responses delivery
Move project and host queries into native MCP with host-owned OAuth/context,
while retaining memory, skills, actions and publication plugins. Add nearby-model
queries and explicit chat/task/learning model defaults.

Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped
session ownership, pending input edits and investigation communication.

Disable local-provider discovery: Server /health allowed native vLLM to probe
/v1/models on port 8000. A native before/after regression prevents recurrence.

Validation: 170 focused Bun tests; service/plugin typechecks; native discovery,
provider-discovery and input-inbox smoke checks; no paid model inference.
2026-10-19 18:46:58 +08:00

41 lines
3.1 KiB
TypeScript

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);
});
});