test(coding-agent): seed auto-compaction queue fixture
This commit is contained in:
@@ -2,7 +2,12 @@ import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { Agent } from "@earendil-works/pi-agent-core";
|
import { Agent } from "@earendil-works/pi-agent-core";
|
||||||
import { type AssistantMessage, getModel } from "@earendil-works/pi-ai";
|
import {
|
||||||
|
type AssistantMessage,
|
||||||
|
createAssistantMessageEventStream,
|
||||||
|
fauxAssistantMessage,
|
||||||
|
getModel,
|
||||||
|
} from "@earendil-works/pi-ai";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { AgentSession } from "../src/core/agent-session.ts";
|
import { AgentSession } from "../src/core/agent-session.ts";
|
||||||
import { AuthStorage } from "../src/core/auth-storage.ts";
|
import { AuthStorage } from "../src/core/auth-storage.ts";
|
||||||
@@ -11,51 +16,10 @@ import { SessionManager } from "../src/core/session-manager.ts";
|
|||||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||||
import { createTestResourceLoader } from "./utilities.ts";
|
import { createTestResourceLoader } from "./utilities.ts";
|
||||||
|
|
||||||
vi.mock("../src/core/compaction/index.js", () => ({
|
|
||||||
calculateContextTokens: (usage: {
|
|
||||||
input: number;
|
|
||||||
output: number;
|
|
||||||
cacheRead: number;
|
|
||||||
cacheWrite: number;
|
|
||||||
totalTokens?: number;
|
|
||||||
}) => usage.totalTokens ?? usage.input + usage.output + usage.cacheRead + usage.cacheWrite,
|
|
||||||
collectEntriesForBranchSummary: () => ({ entries: [], commonAncestorId: null }),
|
|
||||||
compact: async () => ({
|
|
||||||
summary: "compacted",
|
|
||||||
firstKeptEntryId: "entry-1",
|
|
||||||
tokensBefore: 100,
|
|
||||||
details: {},
|
|
||||||
}),
|
|
||||||
estimateContextTokens: (
|
|
||||||
messages: Array<{
|
|
||||||
role: string;
|
|
||||||
usage?: { input: number; output: number; cacheRead: number; cacheWrite: number; totalTokens?: number };
|
|
||||||
stopReason?: string;
|
|
||||||
}>,
|
|
||||||
) => {
|
|
||||||
// Walk backwards to find last non-error, non-aborted assistant with usage
|
|
||||||
for (let i = messages.length - 1; i >= 0; i--) {
|
|
||||||
const msg = messages[i];
|
|
||||||
if (msg.role === "assistant" && msg.stopReason !== "error" && msg.stopReason !== "aborted" && msg.usage) {
|
|
||||||
const tokens =
|
|
||||||
msg.usage.totalTokens ?? msg.usage.input + msg.usage.output + msg.usage.cacheRead + msg.usage.cacheWrite;
|
|
||||||
return { tokens, usageTokens: tokens, trailingTokens: 0, lastUsageIndex: i };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { tokens: 0, usageTokens: 0, trailingTokens: 0, lastUsageIndex: null };
|
|
||||||
},
|
|
||||||
generateBranchSummary: async () => ({ summary: "", aborted: false, readFiles: [], modifiedFiles: [] }),
|
|
||||||
prepareCompaction: () => ({ dummy: true }),
|
|
||||||
shouldCompact: (
|
|
||||||
contextTokens: number,
|
|
||||||
contextWindow: number,
|
|
||||||
settings: { enabled: boolean; reserveTokens: number },
|
|
||||||
) => settings.enabled && contextTokens > contextWindow - settings.reserveTokens,
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("AgentSession auto-compaction queue resume", () => {
|
describe("AgentSession auto-compaction queue resume", () => {
|
||||||
let session: AgentSession;
|
let session: AgentSession;
|
||||||
let sessionManager: SessionManager;
|
let sessionManager: SessionManager;
|
||||||
|
let settingsManager: SettingsManager;
|
||||||
let tempDir: string;
|
let tempDir: string;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -73,7 +37,7 @@ describe("AgentSession auto-compaction queue resume", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
sessionManager = SessionManager.inMemory();
|
sessionManager = SessionManager.inMemory();
|
||||||
const settingsManager = SettingsManager.create(tempDir, tempDir);
|
settingsManager = SettingsManager.create(tempDir, tempDir);
|
||||||
const authStorage = AuthStorage.create(join(tempDir, "auth.json"));
|
const authStorage = AuthStorage.create(join(tempDir, "auth.json"));
|
||||||
authStorage.setRuntimeApiKey("anthropic", "test-key");
|
authStorage.setRuntimeApiKey("anthropic", "test-key");
|
||||||
const modelRegistry = ModelRegistry.create(authStorage, tempDir);
|
const modelRegistry = ModelRegistry.create(authStorage, tempDir);
|
||||||
@@ -98,6 +62,57 @@ describe("AgentSession auto-compaction queue resume", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should resume after threshold compaction when only agent-level queued messages exist", async () => {
|
it("should resume after threshold compaction when only agent-level queued messages exist", async () => {
|
||||||
|
settingsManager.applyOverrides({ compaction: { keepRecentTokens: 1 } });
|
||||||
|
const model = session.model!;
|
||||||
|
const now = Date.now();
|
||||||
|
sessionManager.appendMessage({
|
||||||
|
role: "user",
|
||||||
|
content: [{ type: "text", text: "message to compact" }],
|
||||||
|
timestamp: now - 1000,
|
||||||
|
});
|
||||||
|
sessionManager.appendMessage({
|
||||||
|
role: "assistant",
|
||||||
|
content: [{ type: "text", text: "assistant response to compact" }],
|
||||||
|
api: model.api,
|
||||||
|
provider: model.provider,
|
||||||
|
model: model.id,
|
||||||
|
usage: {
|
||||||
|
input: 100,
|
||||||
|
output: 0,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
totalTokens: 100,
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||||
|
},
|
||||||
|
stopReason: "stop",
|
||||||
|
timestamp: now - 500,
|
||||||
|
});
|
||||||
|
session.agent.state.messages = sessionManager.buildSessionContext().messages;
|
||||||
|
session.agent.streamFn = (summaryModel) => {
|
||||||
|
const stream = createAssistantMessageEventStream();
|
||||||
|
queueMicrotask(() => {
|
||||||
|
stream.push({
|
||||||
|
type: "done",
|
||||||
|
reason: "stop",
|
||||||
|
message: {
|
||||||
|
...fauxAssistantMessage("compacted"),
|
||||||
|
api: summaryModel.api,
|
||||||
|
provider: summaryModel.provider,
|
||||||
|
model: summaryModel.id,
|
||||||
|
usage: {
|
||||||
|
input: 10,
|
||||||
|
output: 0,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
totalTokens: 10,
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return stream;
|
||||||
|
};
|
||||||
|
|
||||||
session.agent.followUp({
|
session.agent.followUp({
|
||||||
role: "custom",
|
role: "custom",
|
||||||
customType: "test",
|
customType: "test",
|
||||||
|
|||||||
Reference in New Issue
Block a user