mirror of
https://github.com/earendil-works/pi.git
synced 2026-06-18 15:54:04 +08:00
fix: trim custom message exclusion tests
This commit is contained in:
@@ -4,7 +4,6 @@ import { getModel } from "@earendil-works/pi-ai";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { prepareBranchEntries } from "../src/core/compaction/branch-summarization.ts";
|
||||
import {
|
||||
type CompactionSettings,
|
||||
calculateContextTokens,
|
||||
@@ -19,7 +18,6 @@ import {
|
||||
import {
|
||||
buildSessionContext,
|
||||
type CompactionEntry,
|
||||
type CustomMessageEntry,
|
||||
type ModelChangeEntry,
|
||||
migrateSessionEntries,
|
||||
parseSessionEntries,
|
||||
@@ -94,22 +92,6 @@ function createMessageEntry(message: AgentMessage): SessionMessageEntry {
|
||||
return entry;
|
||||
}
|
||||
|
||||
function createCustomMessageEntry(content: string, excludeFromContext?: boolean): CustomMessageEntry {
|
||||
const id = `test-id-${entryCounter++}`;
|
||||
const entry: CustomMessageEntry = {
|
||||
type: "custom_message",
|
||||
id,
|
||||
parentId: lastId,
|
||||
timestamp: new Date().toISOString(),
|
||||
customType: "status",
|
||||
content,
|
||||
display: true,
|
||||
excludeFromContext,
|
||||
};
|
||||
lastId = id;
|
||||
return entry;
|
||||
}
|
||||
|
||||
function createCompactionEntry(summary: string, firstKeptEntryId: string): CompactionEntry {
|
||||
const id = `test-id-${entryCounter++}`;
|
||||
const entry: CompactionEntry = {
|
||||
@@ -413,59 +395,6 @@ describe("buildSessionContext", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("prepareCompaction with custom messages", () => {
|
||||
it("should ignore excluded custom messages in compaction and branch-summary preparation", () => {
|
||||
const excludedCustom = createCustomMessageEntry("x".repeat(1000), true);
|
||||
const user = createMessageEntry(createUserMessage("keep"));
|
||||
const simplePreparation = prepareCompaction([excludedCustom, user], {
|
||||
enabled: true,
|
||||
reserveTokens: 0,
|
||||
keepRecentTokens: 1,
|
||||
});
|
||||
const branchPreparation = prepareBranchEntries([excludedCustom, user], 10);
|
||||
|
||||
expect(simplePreparation).toBeDefined();
|
||||
expect(simplePreparation!.tokensBefore).toBe(1);
|
||||
expect(simplePreparation!.messagesToSummarize).toEqual([]);
|
||||
expect(branchPreparation.messages.map((message) => message.role)).toEqual(["user"]);
|
||||
expect(branchPreparation.totalTokens).toBe(1);
|
||||
|
||||
resetEntryCounter();
|
||||
const splitUser = createMessageEntry(createUserMessage("inspect file"));
|
||||
const assistantWithToolCall = createMessageEntry({
|
||||
...createAssistantMessage("calling tool"),
|
||||
content: [{ type: "toolCall", id: "call-1", name: "read", arguments: { path: "file.ts" } }],
|
||||
});
|
||||
const splitExcludedCustom = createCustomMessageEntry("tool is running", true);
|
||||
const toolResult = createMessageEntry({
|
||||
role: "toolResult",
|
||||
toolCallId: "call-1",
|
||||
toolName: "read",
|
||||
content: [{ type: "text", text: "x".repeat(1000) }],
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
const assistantFinal = createMessageEntry(createAssistantMessage("done"));
|
||||
const splitPreparation = prepareCompaction(
|
||||
[splitUser, assistantWithToolCall, splitExcludedCustom, toolResult, assistantFinal],
|
||||
{
|
||||
enabled: true,
|
||||
reserveTokens: 0,
|
||||
keepRecentTokens: 1,
|
||||
},
|
||||
);
|
||||
|
||||
expect(splitPreparation).toBeDefined();
|
||||
expect(splitPreparation!.isSplitTurn).toBe(true);
|
||||
expect(splitPreparation!.firstKeptEntryId).toBe(assistantFinal.id);
|
||||
expect(splitPreparation!.turnPrefixMessages.map((message) => message.role)).toEqual([
|
||||
"user",
|
||||
"assistant",
|
||||
"toolResult",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("prepareCompaction with previous compaction", () => {
|
||||
it("should preserve kept messages across repeated compactions when they still fit", () => {
|
||||
const u1 = createMessageEntry(createUserMessage("user msg 1 (summarized by compaction1)"));
|
||||
|
||||
@@ -258,108 +258,6 @@ describe("AgentSession queue characterization", () => {
|
||||
expect(getAssistantTexts(harness)).toEqual(["", "original turn complete", "batched follow-up response"]);
|
||||
});
|
||||
|
||||
it("records excluded custom messages immediately without starting provider turns", async () => {
|
||||
const idleHarness = await createHarness();
|
||||
harnesses.push(idleHarness);
|
||||
let providerCalled = false;
|
||||
idleHarness.setResponses([
|
||||
() => {
|
||||
providerCalled = true;
|
||||
return fauxAssistantMessage("unexpected");
|
||||
},
|
||||
]);
|
||||
|
||||
await idleHarness.session.sendCustomMessage(
|
||||
{ customType: "status", content: "display only", display: true, details: {}, excludeFromContext: true },
|
||||
{ triggerTurn: true },
|
||||
);
|
||||
|
||||
expect(providerCalled).toBe(false);
|
||||
expect(idleHarness.session.messages[0]).toMatchObject({
|
||||
role: "custom",
|
||||
customType: "status",
|
||||
excludeFromContext: true,
|
||||
});
|
||||
expect(idleHarness.getPendingResponseCount()).toBe(1);
|
||||
|
||||
for (const deliverAs of ["steer", "followUp"] as const) {
|
||||
const waiting = await createWaitingHarness();
|
||||
const { harness, waitForToolStart, promptPromise, releaseToolExecution } = waiting;
|
||||
harnesses.push(harness);
|
||||
let providerCalledForQueuedMessage = false;
|
||||
harness.setResponses([
|
||||
fauxAssistantMessage(fauxToolCall("wait", {}), { stopReason: "toolUse" }),
|
||||
fauxAssistantMessage("done"),
|
||||
() => {
|
||||
providerCalledForQueuedMessage = true;
|
||||
return fauxAssistantMessage("unexpected queued turn");
|
||||
},
|
||||
]);
|
||||
|
||||
await waitForToolStart;
|
||||
await harness.session.sendCustomMessage(
|
||||
{
|
||||
customType: "status",
|
||||
content: `${deliverAs} display only`,
|
||||
display: true,
|
||||
details: {},
|
||||
excludeFromContext: true,
|
||||
},
|
||||
{ deliverAs },
|
||||
);
|
||||
const recordedBeforeRelease = harness.session.messages.some(
|
||||
(message) => message.role === "custom" && message.customType === "status",
|
||||
);
|
||||
releaseToolExecution();
|
||||
await promptPromise;
|
||||
|
||||
expect(recordedBeforeRelease).toBe(true);
|
||||
expect(
|
||||
harness.session.messages.filter((message) => message.role === "custom" && message.customType === "status"),
|
||||
).toHaveLength(1);
|
||||
expect(providerCalledForQueuedMessage).toBe(false);
|
||||
expect(harness.getPendingResponseCount()).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
it("persists excluded custom messages from message_end hooks after the triggering message", async () => {
|
||||
const harness = await createHarness({
|
||||
extensionFactories: [
|
||||
(pi) => {
|
||||
pi.on("message_end", (event) => {
|
||||
if (event.message.role !== "assistant") return;
|
||||
pi.sendMessage({
|
||||
customType: "status",
|
||||
content: "display only",
|
||||
display: true,
|
||||
details: {},
|
||||
excludeFromContext: true,
|
||||
});
|
||||
});
|
||||
},
|
||||
],
|
||||
});
|
||||
harnesses.push(harness);
|
||||
harness.setResponses([fauxAssistantMessage("reply")]);
|
||||
|
||||
await harness.session.prompt("hello");
|
||||
|
||||
const stateOrder = harness.session.messages.map((message) =>
|
||||
message.role === "custom" ? `custom:${message.customType}` : message.role,
|
||||
);
|
||||
const branchOrder = harness.sessionManager.getBranch().map((entry) => {
|
||||
if (entry.type === "message") {
|
||||
return entry.message.role;
|
||||
}
|
||||
if (entry.type === "custom_message") {
|
||||
return `custom:${entry.customType}`;
|
||||
}
|
||||
return entry.type;
|
||||
});
|
||||
expect(stateOrder).toEqual(["user", "assistant", "custom:status"]);
|
||||
expect(branchOrder).toEqual(["user", "assistant", "custom:status"]);
|
||||
});
|
||||
|
||||
it("queues custom messages with deliverAs steer while streaming", async () => {
|
||||
const waiting = await createWaitingHarness();
|
||||
const { harness, waitForToolStart, promptPromise, releaseToolExecution } = waiting;
|
||||
|
||||
@@ -72,6 +72,13 @@ describe("AgentSession retry and event characterization", () => {
|
||||
harness.session.messages.some((message) => message.role === "custom" && message.customType === "status"),
|
||||
).toBe(true);
|
||||
expect(harness.session.messages[harness.session.messages.length - 1]?.role).toBe("assistant");
|
||||
expect(
|
||||
harness.sessionManager.getBranch().map((entry) => {
|
||||
if (entry.type === "message") return entry.message.role;
|
||||
if (entry.type === "custom_message") return `custom:${entry.customType}`;
|
||||
return entry.type;
|
||||
}),
|
||||
).toEqual(["user", "assistant", "custom:status", "assistant"]);
|
||||
});
|
||||
|
||||
it("retries multiple transient failures and succeeds on the final attempt", async () => {
|
||||
|
||||
Reference in New Issue
Block a user