mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-16 19:00:38 +09:00
114 lines
3.3 KiB
TypeScript
114 lines
3.3 KiB
TypeScript
|
|
import { describe, expect, it } from "vitest";
|
||
|
|
import { parsePiJsonl, isPiUnknownSessionError } from "./parse.js";
|
||
|
|
|
||
|
|
describe("parsePiJsonl", () => {
|
||
|
|
it("parses agent lifecycle and messages", () => {
|
||
|
|
const stdout = [
|
||
|
|
JSON.stringify({ type: "agent_start" }),
|
||
|
|
JSON.stringify({
|
||
|
|
type: "turn_end",
|
||
|
|
message: {
|
||
|
|
role: "assistant",
|
||
|
|
content: [{ type: "text", text: "Hello from Pi" }],
|
||
|
|
},
|
||
|
|
}),
|
||
|
|
JSON.stringify({ type: "agent_end", messages: [] }),
|
||
|
|
].join("\n");
|
||
|
|
|
||
|
|
const parsed = parsePiJsonl(stdout);
|
||
|
|
expect(parsed.messages).toContain("Hello from Pi");
|
||
|
|
expect(parsed.finalMessage).toBe("Hello from Pi");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("parses streaming text deltas", () => {
|
||
|
|
const stdout = [
|
||
|
|
JSON.stringify({
|
||
|
|
type: "message_update",
|
||
|
|
assistantMessageEvent: { type: "text_delta", delta: "Hello " },
|
||
|
|
}),
|
||
|
|
JSON.stringify({
|
||
|
|
type: "message_update",
|
||
|
|
assistantMessageEvent: { type: "text_delta", delta: "World" },
|
||
|
|
}),
|
||
|
|
JSON.stringify({
|
||
|
|
type: "turn_end",
|
||
|
|
message: {
|
||
|
|
role: "assistant",
|
||
|
|
content: "Hello World",
|
||
|
|
},
|
||
|
|
}),
|
||
|
|
].join("\n");
|
||
|
|
|
||
|
|
const parsed = parsePiJsonl(stdout);
|
||
|
|
expect(parsed.messages).toContain("Hello World");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("parses tool execution", () => {
|
||
|
|
const stdout = [
|
||
|
|
JSON.stringify({
|
||
|
|
type: "tool_execution_start",
|
||
|
|
toolCallId: "tool_1",
|
||
|
|
toolName: "read",
|
||
|
|
args: { path: "/tmp/test.txt" },
|
||
|
|
}),
|
||
|
|
JSON.stringify({
|
||
|
|
type: "tool_execution_end",
|
||
|
|
toolCallId: "tool_1",
|
||
|
|
toolName: "read",
|
||
|
|
result: "file contents",
|
||
|
|
isError: false,
|
||
|
|
}),
|
||
|
|
JSON.stringify({
|
||
|
|
type: "turn_end",
|
||
|
|
message: { role: "assistant", content: "Done" },
|
||
|
|
toolResults: [
|
||
|
|
{
|
||
|
|
toolCallId: "tool_1",
|
||
|
|
content: "file contents",
|
||
|
|
isError: false,
|
||
|
|
},
|
||
|
|
],
|
||
|
|
}),
|
||
|
|
].join("\n");
|
||
|
|
|
||
|
|
const parsed = parsePiJsonl(stdout);
|
||
|
|
expect(parsed.toolCalls).toHaveLength(1);
|
||
|
|
expect(parsed.toolCalls[0].toolName).toBe("read");
|
||
|
|
expect(parsed.toolCalls[0].result).toBe("file contents");
|
||
|
|
expect(parsed.toolCalls[0].isError).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("handles errors in tool execution", () => {
|
||
|
|
const stdout = [
|
||
|
|
JSON.stringify({
|
||
|
|
type: "tool_execution_start",
|
||
|
|
toolCallId: "tool_1",
|
||
|
|
toolName: "read",
|
||
|
|
args: { path: "/missing.txt" },
|
||
|
|
}),
|
||
|
|
JSON.stringify({
|
||
|
|
type: "tool_execution_end",
|
||
|
|
toolCallId: "tool_1",
|
||
|
|
toolName: "read",
|
||
|
|
result: "File not found",
|
||
|
|
isError: true,
|
||
|
|
}),
|
||
|
|
].join("\n");
|
||
|
|
|
||
|
|
const parsed = parsePiJsonl(stdout);
|
||
|
|
expect(parsed.toolCalls).toHaveLength(1);
|
||
|
|
expect(parsed.toolCalls[0].isError).toBe(true);
|
||
|
|
expect(parsed.toolCalls[0].result).toBe("File not found");
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("isPiUnknownSessionError", () => {
|
||
|
|
it("detects unknown session errors", () => {
|
||
|
|
expect(isPiUnknownSessionError("session not found: s_123", "")).toBe(true);
|
||
|
|
expect(isPiUnknownSessionError("", "unknown session id")).toBe(true);
|
||
|
|
expect(isPiUnknownSessionError("", "no session available")).toBe(true);
|
||
|
|
expect(isPiUnknownSessionError("all good", "")).toBe(false);
|
||
|
|
expect(isPiUnknownSessionError("working fine", "no errors")).toBe(false);
|
||
|
|
});
|
||
|
|
});
|