[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
350
infrastructure/ai/contextCompaction.test.ts
Normal file
350
infrastructure/ai/contextCompaction.test.ts
Normal file
@@ -0,0 +1,350 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import type { ModelMessage } from "ai";
|
||||
|
||||
import {
|
||||
buildCompactedMessages,
|
||||
estimateModelMessagesTokens,
|
||||
estimateUnknownTokens,
|
||||
findSafeChatMessageCompactionSplitIndex,
|
||||
findSafeCompactionSplitIndex,
|
||||
formatMessagesForCompaction,
|
||||
prepareContextCompaction,
|
||||
resolveContextWindow,
|
||||
shouldCompactContext,
|
||||
} from "./contextCompaction.ts";
|
||||
|
||||
test("shouldCompactContext waits until the prompt approaches the context window", () => {
|
||||
assert.equal(shouldCompactContext({ promptTokens: 70, contextWindow: 100, thresholdRatio: 0.85 }), false);
|
||||
assert.equal(shouldCompactContext({ promptTokens: 85, contextWindow: 100, thresholdRatio: 0.85 }), true);
|
||||
});
|
||||
|
||||
test("shouldCompactContext uses dynamic threshold when ratio omitted", () => {
|
||||
assert.equal(shouldCompactContext({ promptTokens: 70, contextWindow: 100 }), true);
|
||||
});
|
||||
|
||||
test("findSafeCompactionSplitIndex keeps recent messages intact", () => {
|
||||
const messages: ModelMessage[] = [
|
||||
{ role: "user", content: "old 1" },
|
||||
{ role: "assistant", content: "old 2" },
|
||||
{ role: "user", content: "recent 1" },
|
||||
{ role: "assistant", content: "recent 2" },
|
||||
];
|
||||
|
||||
assert.equal(findSafeCompactionSplitIndex(messages, 2), 2);
|
||||
});
|
||||
|
||||
test("findSafeCompactionSplitIndex avoids orphaning a tool result", () => {
|
||||
const messages: ModelMessage[] = [
|
||||
{ role: "user", content: "old" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call-1",
|
||||
toolName: "run_command",
|
||||
input: { command: "pwd" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolCallId: "call-1",
|
||||
toolName: "run_command",
|
||||
output: { type: "text", value: "/tmp" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: "user", content: "recent" },
|
||||
{ role: "assistant", content: "answer" },
|
||||
];
|
||||
|
||||
assert.equal(findSafeCompactionSplitIndex(messages, 3), 1);
|
||||
});
|
||||
|
||||
test("findSafeChatMessageCompactionSplitIndex does not cut tool call/result pairs", () => {
|
||||
const messages = [
|
||||
{ role: "user" },
|
||||
{ role: "assistant", toolCalls: [{ id: "c1" }] },
|
||||
{ role: "tool" },
|
||||
{ role: "user" },
|
||||
{ role: "assistant" },
|
||||
{ role: "user" },
|
||||
{ role: "assistant" },
|
||||
{ role: "user" },
|
||||
{ role: "assistant" },
|
||||
{ role: "user" },
|
||||
{ role: "assistant" },
|
||||
{ role: "user" },
|
||||
{ role: "assistant", toolCalls: [{ id: "c2" }] },
|
||||
{ role: "tool" },
|
||||
];
|
||||
// protect last 3 would land inside the final tool pair; pull boundary before assistant toolCalls
|
||||
assert.equal(findSafeChatMessageCompactionSplitIndex(messages, 3), 11);
|
||||
assert.equal(findSafeChatMessageCompactionSplitIndex(messages.slice(0, 8), 10), 0);
|
||||
});
|
||||
|
||||
test("buildCompactedMessages places the summary before recent messages", () => {
|
||||
const recentMessages: ModelMessage[] = [
|
||||
{ role: "user", content: "what next?" },
|
||||
];
|
||||
|
||||
const compacted = buildCompactedMessages({
|
||||
summary: "Earlier work is summarized here.",
|
||||
recentMessages,
|
||||
});
|
||||
|
||||
assert.deepEqual(compacted, [
|
||||
{
|
||||
role: "user",
|
||||
content: "[Previous conversation summary]\n\nEarlier work is summarized here.\n\n[Continue with the recent messages below.]",
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: "I understand the previous conversation summary and will continue from the recent messages.",
|
||||
},
|
||||
{ role: "user", content: "what next?" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("prepareContextCompaction summarizes old messages and returns compacted context", async () => {
|
||||
const messages: ModelMessage[] = [
|
||||
{ role: "user", content: "old ".repeat(40) },
|
||||
{ role: "assistant", content: "older ".repeat(40) },
|
||||
{ role: "user", content: "recent question" },
|
||||
{ role: "assistant", content: "recent answer" },
|
||||
];
|
||||
|
||||
const result = await prepareContextCompaction({
|
||||
messages,
|
||||
contextWindow: 100,
|
||||
protectRecentMessages: 2,
|
||||
summarize: async (messagesToSummarize) => {
|
||||
assert.deepEqual(messagesToSummarize, messages.slice(0, 2));
|
||||
return "Summary of old messages.";
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.didCompact, true);
|
||||
assert.equal(result.summary, "Summary of old messages.");
|
||||
assert.deepEqual(result.messages.slice(-2), messages.slice(-2));
|
||||
});
|
||||
|
||||
test("prepareContextCompaction includes reserved request tokens in the compaction check", async () => {
|
||||
const messages: ModelMessage[] = [
|
||||
{ role: "user", content: "short prompt" },
|
||||
{ role: "assistant", content: "short answer" },
|
||||
{ role: "user", content: "recent question" },
|
||||
];
|
||||
|
||||
const result = await prepareContextCompaction({
|
||||
messages,
|
||||
contextWindow: 40,
|
||||
reservedTokens: estimateUnknownTokens("large system prompt ".repeat(20)),
|
||||
protectRecentMessages: 1,
|
||||
summarize: async (messagesToSummarize) => {
|
||||
assert.deepEqual(messagesToSummarize, messages.slice(0, 2));
|
||||
return "System prompt forced compaction.";
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.didCompact, true);
|
||||
assert.equal(result.summary, "System prompt forced compaction.");
|
||||
});
|
||||
|
||||
test("prepareContextCompaction summarizes older tool results instead of dropping them first", async () => {
|
||||
const messages: ModelMessage[] = [
|
||||
{ role: "user", content: "check disk usage" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call-1",
|
||||
toolName: "run_command",
|
||||
input: { command: "df -h" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolCallId: "call-1",
|
||||
toolName: "run_command",
|
||||
output: { type: "text", value: "/dev/disk1 81% full" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: "assistant", content: "Disk is 81% full." },
|
||||
{ role: "user", content: "old follow-up ".repeat(80) },
|
||||
{ role: "assistant", content: "old answer ".repeat(80) },
|
||||
{ role: "user", content: "recent question" },
|
||||
{ role: "assistant", content: "recent answer" },
|
||||
];
|
||||
|
||||
const result = await prepareContextCompaction({
|
||||
messages,
|
||||
contextWindow: 120,
|
||||
protectRecentMessages: 2,
|
||||
summarize: async (messagesToSummarize) => {
|
||||
assert.match(formatMessagesForCompaction(messagesToSummarize), /81% full/);
|
||||
return "Earlier disk check showed /dev/disk1 was 81% full.";
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.didCompact, true);
|
||||
assert.match(result.messages[0]?.content as string, /81% full/);
|
||||
});
|
||||
|
||||
test("formatMessagesForCompaction redacts image and file payloads", () => {
|
||||
const imagePayload = "iVBORw0KGgo".repeat(200);
|
||||
const filePayload = "JVBERi0xLjQK".repeat(200);
|
||||
const formatted = formatMessagesForCompaction([
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "text", text: "Please inspect these attachments." },
|
||||
{
|
||||
type: "image",
|
||||
image: imagePayload,
|
||||
mediaType: "image/png",
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: filePayload,
|
||||
filename: "report.pdf",
|
||||
mediaType: "application/pdf",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
assert.match(formatted, /Please inspect these attachments/);
|
||||
assert.match(formatted, /redacted image payload/);
|
||||
assert.match(formatted, /mediaType=image\/png/);
|
||||
assert.match(formatted, /redacted file payload/);
|
||||
assert.match(formatted, /filename=report\.pdf/);
|
||||
assert.doesNotMatch(formatted, new RegExp(imagePayload.slice(0, 40)));
|
||||
assert.doesNotMatch(formatted, new RegExp(filePayload.slice(0, 40)));
|
||||
});
|
||||
|
||||
test("formatMessagesForCompaction keeps non-attachment data fields", () => {
|
||||
const formatted = formatMessagesForCompaction([
|
||||
{
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolCallId: "call-1",
|
||||
toolName: "read_json",
|
||||
output: {
|
||||
type: "json",
|
||||
value: {
|
||||
data: {
|
||||
host: "prod-1",
|
||||
status: "healthy",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
assert.match(formatted, /prod-1/);
|
||||
assert.match(formatted, /healthy/);
|
||||
assert.doesNotMatch(formatted, /redacted data payload/);
|
||||
});
|
||||
|
||||
test("formatMessagesForCompaction omits encrypted provider continuation metadata", () => {
|
||||
const ciphertext = "gAAAA".repeat(20_000);
|
||||
const formatted = formatMessagesForCompaction([
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "reasoning",
|
||||
text: "Checked the deployment state.",
|
||||
providerOptions: {
|
||||
openai: {
|
||||
itemId: "rs_secret",
|
||||
reasoningEncryptedContent: ciphertext,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call-1",
|
||||
toolName: "run_command",
|
||||
input: { command: "docker service ls" },
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
assert.match(formatted, /Checked the deployment state/);
|
||||
assert.match(formatted, /docker service ls/);
|
||||
assert.doesNotMatch(formatted, /reasoningEncryptedContent/);
|
||||
assert.doesNotMatch(formatted, /rs_secret/);
|
||||
assert.doesNotMatch(formatted, new RegExp(ciphertext.slice(0, 40)));
|
||||
assert.ok(formatted.length < 2_000);
|
||||
});
|
||||
|
||||
test("estimateModelMessagesTokens counts multimodal and tool content", () => {
|
||||
const tokens = estimateModelMessagesTokens([
|
||||
{ role: "user", content: [{ type: "text", text: "hello world" }] },
|
||||
{
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolCallId: "call-1",
|
||||
toolName: "run_command",
|
||||
output: { type: "text", value: "result text" },
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
assert.ok(tokens >= 5);
|
||||
});
|
||||
|
||||
test("resolveContextWindow prefers manual override, then fetched model metadata, then default", () => {
|
||||
assert.equal(
|
||||
resolveContextWindow({
|
||||
provider: {
|
||||
contextWindow: 262144,
|
||||
modelContextWindows: { "qwen/test": 131072 },
|
||||
},
|
||||
modelId: "qwen/test",
|
||||
defaultContextWindow: 128000,
|
||||
}),
|
||||
262144,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
resolveContextWindow({
|
||||
provider: {
|
||||
modelContextWindows: { "qwen/test": 131072 },
|
||||
},
|
||||
modelId: "qwen/test",
|
||||
defaultContextWindow: 128000,
|
||||
}),
|
||||
131072,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
resolveContextWindow({
|
||||
provider: {},
|
||||
modelId: "unknown",
|
||||
defaultContextWindow: 128000,
|
||||
}),
|
||||
128000,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user