import { describe, expect, it } from "vitest"; import { DEFAULT_SYSTEM_PROMPT, RECENCY_HINT } from "../lib/constants"; import { buildMessages, composeSystemPrompt } from "../lib/history"; import type { ChatTurn } from "../lib/types"; function turn(prompt: string, response: string): ChatTurn { return { id: prompt, prompt, response, thinking: "", model: "m", createdAt: 0, toolCalls: [] }; } describe("buildMessages", () => { it("opens with the system prompt and closes with the new question", () => { const messages = buildMessages("SYSTEM", [], "hello"); expect(messages).toEqual([ { role: "system", content: "SYSTEM" }, { role: "user", content: "hello" }, ]); }); it("replays completed turns in order", () => { const messages = buildMessages("SYS", [turn("q1", "a1"), turn("q2", "a2")], "q3"); expect(messages.map((message) => message.role)).toEqual([ "system", "user", "assistant", "user", "assistant", "user", ]); expect(messages[5]).toEqual({ role: "user", content: "q3" }); }); it("skips turns that never produced an answer", () => { // A cancelled or failed turn would otherwise be replayed to the model as an // empty assistant message, which some models treat as a hard stop. const messages = buildMessages("SYS", [turn("q1", ""), turn("q2", "a2")], "q3"); expect(messages).toHaveLength(4); expect(messages[1]).toEqual({ role: "user", content: "q2" }); }); it("does not replay tool messages from earlier turns", () => { const withTools: ChatTurn = { ...turn("q1", "a1"), toolCalls: [{ name: "web_search", arguments: {}, result: "a huge page", isError: false }], }; const messages = buildMessages("SYS", [withTools], "q2"); expect(messages.some((message) => message.role === "tool")).toBe(false); expect(JSON.stringify(messages)).not.toContain("a huge page"); }); }); describe("composeSystemPrompt", () => { it("uses the default prompt when the user has not set one", () => { expect(composeSystemPrompt("", false)).toBe(DEFAULT_SYSTEM_PROMPT); }); it("prefers the user's prompt when set", () => { expect(composeSystemPrompt("Be terse.", false)).toBe("Be terse."); }); it("appends the recency hint only when web tools are on", () => { expect(composeSystemPrompt("Be terse.", true)).toBe(`Be terse.\n\n${RECENCY_HINT}`); expect(composeSystemPrompt("Be terse.", false)).not.toContain(RECENCY_HINT); }); });