import type { ToolResultMessage } from "@earendil-works/pi-ai"; import { describe, expect, test } from "vitest"; import { convertToLlm } from "../src/core/messages.js"; function toolResult(overrides: Partial = {}): ToolResultMessage { return { role: "toolResult", toolCallId: "tool-1", toolName: "bash", content: [{ type: "text", text: "file contents" }], isError: false, timestamp: 0, ...overrides, }; } describe("convertToLlm tool duration annotation", () => { test("appends the measured duration to the last text block", () => { const [converted] = convertToLlm([toolResult({ durationMs: 3400 })]); if (converted?.role !== "toolResult") { throw new Error("expected toolResult"); } expect(converted.content).toEqual([{ type: "text", text: "file contents\n\n[Took 3.4s]" }]); }); test("does not mutate the stored message or annotate results without a duration", () => { const stored = toolResult(); const [converted] = convertToLlm([stored]); if (converted?.role !== "toolResult") { throw new Error("expected toolResult"); } expect(converted).toBe(stored); expect(converted).not.toHaveProperty("durationMs"); }); test("formats sub-second and long durations", () => { const [fast, slow] = convertToLlm([ toolResult({ toolCallId: "a", durationMs: 420 }), toolResult({ toolCallId: "b", durationMs: 65_000 }), ]) as ToolResultMessage[]; expect(fast.content).toEqual([{ type: "text", text: "file contents\n\n[Took 420ms]" }]); expect(slow.content).toEqual([{ type: "text", text: "file contents\n\n[Took 1m 05s]" }]); }); test("adds a text block to image-only results", () => { const [converted] = convertToLlm([ toolResult({ content: [{ type: "image", data: "AAAA", mimeType: "image/png" }], durationMs: 800 }), ]) as ToolResultMessage[]; expect(converted.content).toEqual([ { type: "image", data: "AAAA", mimeType: "image/png" }, { type: "text", text: "[Took 800ms]" }, ]); }); });