Migrate configurable compaction tests (#26732)

This commit is contained in:
Kit Langton
2026-05-10 19:18:02 -04:00
committed by GitHub
parent 2a571b3cee
commit 4ff0d07b1d

View File

@@ -232,6 +232,14 @@ async function lastCompactionPart(sessionID: SessionID) {
?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction") ?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction")
} }
function readLastCompactionPart(sessionID: SessionID) {
return SessionNs.Service.use((ssn) => ssn.messages({ sessionID })).pipe(
Effect.map((messages) =>
messages.at(-2)?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction"),
),
)
}
function fake( function fake(
input: Parameters<SessionProcessorModule.SessionProcessor.Interface["create"]>[0], input: Parameters<SessionProcessorModule.SessionProcessor.Interface["create"]>[0],
result: "continue" | "compact", result: "continue" | "compact",
@@ -300,6 +308,30 @@ const env = Layer.mergeAll(
const it = testEffect(env) const it = testEffect(env)
const processEnv = Layer.mergeAll(SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer)
const itProcess = testEffect(processEnv)
function compactionProcessLayer(options?: {
result?: "continue" | "compact"
plugin?: Layer.Layer<Plugin.Service>
provider?: ReturnType<typeof ProviderTest.fake>
config?: Layer.Layer<Config.Service>
}) {
const bus = Bus.layer
return SessionCompaction.layer.pipe(
Layer.provideMerge(
Layer.mergeAll(
(options?.provider ?? wide()).layer,
layer(options?.result ?? "continue"),
Agent.defaultLayer,
options?.plugin ?? Plugin.defaultLayer,
bus,
options?.config ?? Config.defaultLayer,
),
),
)
}
function llm() { function llm() {
const queue: Array< const queue: Array<
Stream.Stream<LLM.Event, unknown> | ((input: LLM.StreamInput) => Stream.Stream<LLM.Event, unknown>) Stream.Stream<LLM.Event, unknown> | ((input: LLM.StreamInput) => Stream.Stream<LLM.Event, unknown>)
@@ -911,43 +943,33 @@ describe("session.compaction.process", () => {
}), }),
) )
test("marks summary message as errored on compact result", async () => { itProcess.instance(
await using tmp = await tmpdir() "marks summary message as errored on compact result",
await WithInstance.provide({ Effect.gen(function* () {
directory: tmp.path, const ssn = yield* SessionNs.Service
fn: async () => { const session = yield* ssn.create({})
const session = await svc.create({}) const msg = yield* createUserMessage(session.id, "hello")
const msg = await user(session.id, "hello") const msgs = yield* ssn.messages({ sessionID: session.id })
const rt = runtime("compact", Plugin.defaultLayer, wide())
try {
const msgs = await svc.messages({ sessionID: session.id })
const result = await rt.runPromise(
SessionCompaction.Service.use((svc) =>
svc.process({
parentID: msg.id,
messages: msgs,
sessionID: session.id,
auto: false,
}),
),
)
const summary = (await svc.messages({ sessionID: session.id })).find( const result = yield* SessionCompaction.use.process({
(msg) => msg.info.role === "assistant" && msg.info.summary, parentID: msg.id,
) messages: msgs,
sessionID: session.id,
auto: false,
})
expect(result).toBe("stop") const summary = (yield* ssn.messages({ sessionID: session.id })).find(
expect(summary?.info.role).toBe("assistant") (msg) => msg.info.role === "assistant" && msg.info.summary,
if (summary?.info.role === "assistant") { )
expect(summary.info.finish).toBe("error")
expect(JSON.stringify(summary.info.error)).toContain("Session too large to compact") expect(result).toBe("stop")
} expect(summary?.info.role).toBe("assistant")
} finally { if (summary?.info.role === "assistant") {
await rt.dispose() expect(summary.info.finish).toBe("error")
} expect(JSON.stringify(summary.info.error)).toContain("Session too large to compact")
}, }
}) }).pipe(Effect.provide(compactionProcessLayer({ result: "compact" }))),
}) )
it.instance( it.instance(
"adds synthetic continue prompt when auto is enabled", "adds synthetic continue prompt when auto is enabled",
@@ -980,94 +1002,57 @@ describe("session.compaction.process", () => {
}), }),
) )
test("persists tail_start_id for retained recent turns", async () => { itProcess.instance(
await using tmp = await tmpdir() "persists tail_start_id for retained recent turns",
await WithInstance.provide({ Effect.gen(function* () {
directory: tmp.path, const ssn = yield* SessionNs.Service
fn: async () => { const session = yield* ssn.create({})
const session = await svc.create({}) yield* createUserMessage(session.id, "first")
await user(session.id, "first") const keep = yield* createUserMessage(session.id, "second")
const keep = await user(session.id, "second") yield* createUserMessage(session.id, "third")
await user(session.id, "third") yield* SessionCompaction.use.create({ sessionID: session.id, agent: "build", model: ref, auto: false })
await SessionCompaction.create({
sessionID: session.id,
agent: "build",
model: ref,
auto: false,
})
const rt = runtime( const msgs = yield* ssn.messages({ sessionID: session.id })
"continue", const parent = msgs.at(-1)?.info.id
Plugin.defaultLayer, expect(parent).toBeTruthy()
wide(), yield* SessionCompaction.use.process({
cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }), parentID: parent!,
) messages: msgs,
try { sessionID: session.id,
const msgs = await svc.messages({ sessionID: session.id }) auto: false,
const parent = msgs.at(-1)?.info.id })
expect(parent).toBeTruthy()
await rt.runPromise(
SessionCompaction.Service.use((svc) =>
svc.process({
parentID: parent!,
messages: msgs,
sessionID: session.id,
auto: false,
}),
),
)
const part = await lastCompactionPart(session.id) const part = yield* readLastCompactionPart(session.id)
expect(part?.type).toBe("compaction") expect(part?.type).toBe("compaction")
expect(part?.tail_start_id).toBe(keep.id) expect(part?.tail_start_id).toBe(keep.id)
} finally { }).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) }))),
await rt.dispose() )
}
},
})
})
test("shrinks retained tail to fit preserve token budget", async () => { itProcess.instance(
await using tmp = await tmpdir() "shrinks retained tail to fit preserve token budget",
await WithInstance.provide({ Effect.gen(function* () {
directory: tmp.path, const ssn = yield* SessionNs.Service
fn: async () => { const session = yield* ssn.create({})
const session = await svc.create({}) yield* createUserMessage(session.id, "first")
await user(session.id, "first") yield* createUserMessage(session.id, "x".repeat(2_000))
await user(session.id, "x".repeat(2_000)) const keep = yield* createUserMessage(session.id, "tiny")
const keep = await user(session.id, "tiny") yield* SessionCompaction.use.create({ sessionID: session.id, agent: "build", model: ref, auto: false })
await SessionCompaction.create({
sessionID: session.id,
agent: "build",
model: ref,
auto: false,
})
const rt = runtime("continue", Plugin.defaultLayer, wide(), cfg({ tail_turns: 2, preserve_recent_tokens: 100 })) const msgs = yield* ssn.messages({ sessionID: session.id })
try { const parent = msgs.at(-1)?.info.id
const msgs = await svc.messages({ sessionID: session.id }) expect(parent).toBeTruthy()
const parent = msgs.at(-1)?.info.id yield* SessionCompaction.use.process({
expect(parent).toBeTruthy() parentID: parent!,
await rt.runPromise( messages: msgs,
SessionCompaction.Service.use((svc) => sessionID: session.id,
svc.process({ auto: false,
parentID: parent!, })
messages: msgs,
sessionID: session.id,
auto: false,
}),
),
)
const part = await lastCompactionPart(session.id) const part = yield* readLastCompactionPart(session.id)
expect(part?.type).toBe("compaction") expect(part?.type).toBe("compaction")
expect(part?.tail_start_id).toBe(keep.id) expect(part?.tail_start_id).toBe(keep.id)
} finally { }).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 100 }) }))),
await rt.dispose() )
}
},
})
})
test("falls back to full summary when even one recent turn exceeds preserve token budget", async () => { test("falls back to full summary when even one recent turn exceeds preserve token budget", async () => {
await using tmp = await tmpdir({ git: true }) await using tmp = await tmpdir({ git: true })