@@ -5,6 +5,7 @@ import { AgentSession } from "../src/core/agent-session.ts";
|
||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||
import { SessionManager } from "../src/core/session-manager.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { getUsageCostBreakdown } from "../src/core/usage-totals.ts";
|
||||
import { createInMemoryModelRegistry, getModelRuntime } from "./model-runtime-test-utils.ts";
|
||||
import { createTestResourceLoader } from "./utilities.ts";
|
||||
|
||||
@@ -224,6 +225,31 @@ describe("AgentSession.getSessionStats", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("groups tool and summary usage separately from model-attributed usage", () => {
|
||||
const sessionManager = SessionManager.inMemory();
|
||||
const rootId = sessionManager.appendMessage(createUserMessage("hello", 1));
|
||||
sessionManager.appendMessage({
|
||||
...createAssistantMessage("response", 100, 2),
|
||||
usage: { ...createUsage(100), cost: { ...createUsage(100).cost, total: 0.5 } },
|
||||
});
|
||||
sessionManager.appendMessage(
|
||||
createToolResultMessage({ ...createUsage(100), cost: { ...createUsage(100).cost, total: 1 } }),
|
||||
);
|
||||
sessionManager.appendCompaction("summary", rootId, 100, undefined, false, {
|
||||
...createUsage(100),
|
||||
cost: { ...createUsage(100).cost, total: 2 },
|
||||
});
|
||||
sessionManager.branchWithSummary(null, "branch summary", undefined, false, {
|
||||
...createUsage(100),
|
||||
cost: { ...createUsage(100).cost, total: 3 },
|
||||
});
|
||||
|
||||
expect(getUsageCostBreakdown(sessionManager.getEntries())).toEqual([
|
||||
{ key: "Tools/summaries", cost: 6, tokens: 300 },
|
||||
{ key: `${model.provider}/${model.id}`, cost: 0.5, tokens: 100 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("ignores zero-usage messages when checking for post-compaction context usage", async () => {
|
||||
const { session, sessionManager } = await createSession();
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
||||
import type { AssistantMessage, Model } from "@earendil-works/pi-ai";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { type CompactionPreparation, compact, generateSummary } from "../src/core/compaction/index.ts";
|
||||
import {
|
||||
type CompactionPreparation,
|
||||
compact,
|
||||
generateSummary,
|
||||
generateSummaryWithUsage,
|
||||
} from "../src/core/compaction/index.ts";
|
||||
|
||||
const { completeSimpleMock } = vi.hoisted(() => ({
|
||||
completeSimpleMock: vi.fn(),
|
||||
@@ -57,7 +62,7 @@ describe("generateSummary reasoning options", () => {
|
||||
});
|
||||
|
||||
it("uses the provided thinking level for reasoning-capable models", async () => {
|
||||
const result = await generateSummary(
|
||||
const result = await generateSummaryWithUsage(
|
||||
messages,
|
||||
createModel(true),
|
||||
2000,
|
||||
@@ -79,6 +84,12 @@ describe("generateSummary reasoning options", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves the string result from generateSummary", async () => {
|
||||
await expect(generateSummary(messages, createModel(false), 2000, "test-key")).resolves.toBe(
|
||||
"## Goal\nTest summary",
|
||||
);
|
||||
});
|
||||
|
||||
it("does not set reasoning when thinking is off", async () => {
|
||||
await generateSummary(
|
||||
messages,
|
||||
|
||||
@@ -523,6 +523,52 @@ describe("createBranchedSession", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("preserves tool and summary usage across a file-backed reload", () => {
|
||||
const tempDir = join(tmpdir(), `session-usage-roundtrip-${Date.now()}`);
|
||||
mkdirSync(tempDir, { recursive: true });
|
||||
|
||||
try {
|
||||
const session = SessionManager.create(tempDir, tempDir);
|
||||
const rootId = session.appendMessage(userMsg("question"));
|
||||
session.appendMessage(assistantMsg("answer"));
|
||||
const usage = {
|
||||
input: 10,
|
||||
output: 20,
|
||||
cacheRead: 30,
|
||||
cacheWrite: 40,
|
||||
totalTokens: 100,
|
||||
cost: { input: 0.1, output: 0.2, cacheRead: 0.3, cacheWrite: 0.4, total: 1 },
|
||||
};
|
||||
session.appendMessage({
|
||||
role: "toolResult",
|
||||
toolCallId: "call-1",
|
||||
toolName: "nested-model",
|
||||
content: [{ type: "text", text: "result" }],
|
||||
isError: false,
|
||||
usage,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
session.appendCompaction("summary", rootId, 100, undefined, false, usage);
|
||||
session.branchWithSummary(rootId, "branch summary", undefined, false, usage);
|
||||
|
||||
const file = session.getSessionFile();
|
||||
expect(file).toBeDefined();
|
||||
const reopened = SessionManager.open(file!, tempDir);
|
||||
expect(reopened.getEntries()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ type: "compaction", usage }),
|
||||
expect.objectContaining({ type: "branch_summary", usage }),
|
||||
expect.objectContaining({
|
||||
type: "message",
|
||||
message: expect.objectContaining({ role: "toolResult", usage }),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
} finally {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("writes file immediately when forking from a point with assistant messages", () => {
|
||||
const tempDir = join(tmpdir(), `session-fork-with-assistant-${Date.now()}`);
|
||||
mkdirSync(tempDir, { recursive: true });
|
||||
|
||||
Reference in New Issue
Block a user