Skip to content

Commit 4ff0d07

Browse files
authored
Migrate configurable compaction tests (#26732)
1 parent 2a571b3 commit 4ff0d07

1 file changed

Lines changed: 103 additions & 118 deletions

File tree

packages/opencode/test/session/compaction.test.ts

Lines changed: 103 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,14 @@ async function lastCompactionPart(sessionID: SessionID) {
232232
?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction")
233233
}
234234

235+
function readLastCompactionPart(sessionID: SessionID) {
236+
return SessionNs.Service.use((ssn) => ssn.messages({ sessionID })).pipe(
237+
Effect.map((messages) =>
238+
messages.at(-2)?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction"),
239+
),
240+
)
241+
}
242+
235243
function fake(
236244
input: Parameters<SessionProcessorModule.SessionProcessor.Interface["create"]>[0],
237245
result: "continue" | "compact",
@@ -300,6 +308,30 @@ const env = Layer.mergeAll(
300308

301309
const it = testEffect(env)
302310

311+
const processEnv = Layer.mergeAll(SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer)
312+
const itProcess = testEffect(processEnv)
313+
314+
function compactionProcessLayer(options?: {
315+
result?: "continue" | "compact"
316+
plugin?: Layer.Layer<Plugin.Service>
317+
provider?: ReturnType<typeof ProviderTest.fake>
318+
config?: Layer.Layer<Config.Service>
319+
}) {
320+
const bus = Bus.layer
321+
return SessionCompaction.layer.pipe(
322+
Layer.provideMerge(
323+
Layer.mergeAll(
324+
(options?.provider ?? wide()).layer,
325+
layer(options?.result ?? "continue"),
326+
Agent.defaultLayer,
327+
options?.plugin ?? Plugin.defaultLayer,
328+
bus,
329+
options?.config ?? Config.defaultLayer,
330+
),
331+
),
332+
)
333+
}
334+
303335
function llm() {
304336
const queue: Array<
305337
Stream.Stream<LLM.Event, unknown> | ((input: LLM.StreamInput) => Stream.Stream<LLM.Event, unknown>)
@@ -911,43 +943,33 @@ describe("session.compaction.process", () => {
911943
}),
912944
)
913945

914-
test("marks summary message as errored on compact result", async () => {
915-
await using tmp = await tmpdir()
916-
await WithInstance.provide({
917-
directory: tmp.path,
918-
fn: async () => {
919-
const session = await svc.create({})
920-
const msg = await user(session.id, "hello")
921-
const rt = runtime("compact", Plugin.defaultLayer, wide())
922-
try {
923-
const msgs = await svc.messages({ sessionID: session.id })
924-
const result = await rt.runPromise(
925-
SessionCompaction.Service.use((svc) =>
926-
svc.process({
927-
parentID: msg.id,
928-
messages: msgs,
929-
sessionID: session.id,
930-
auto: false,
931-
}),
932-
),
933-
)
946+
itProcess.instance(
947+
"marks summary message as errored on compact result",
948+
Effect.gen(function* () {
949+
const ssn = yield* SessionNs.Service
950+
const session = yield* ssn.create({})
951+
const msg = yield* createUserMessage(session.id, "hello")
952+
const msgs = yield* ssn.messages({ sessionID: session.id })
934953

935-
const summary = (await svc.messages({ sessionID: session.id })).find(
936-
(msg) => msg.info.role === "assistant" && msg.info.summary,
937-
)
954+
const result = yield* SessionCompaction.use.process({
955+
parentID: msg.id,
956+
messages: msgs,
957+
sessionID: session.id,
958+
auto: false,
959+
})
938960

939-
expect(result).toBe("stop")
940-
expect(summary?.info.role).toBe("assistant")
941-
if (summary?.info.role === "assistant") {
942-
expect(summary.info.finish).toBe("error")
943-
expect(JSON.stringify(summary.info.error)).toContain("Session too large to compact")
944-
}
945-
} finally {
946-
await rt.dispose()
947-
}
948-
},
949-
})
950-
})
961+
const summary = (yield* ssn.messages({ sessionID: session.id })).find(
962+
(msg) => msg.info.role === "assistant" && msg.info.summary,
963+
)
964+
965+
expect(result).toBe("stop")
966+
expect(summary?.info.role).toBe("assistant")
967+
if (summary?.info.role === "assistant") {
968+
expect(summary.info.finish).toBe("error")
969+
expect(JSON.stringify(summary.info.error)).toContain("Session too large to compact")
970+
}
971+
}).pipe(Effect.provide(compactionProcessLayer({ result: "compact" }))),
972+
)
951973

952974
it.instance(
953975
"adds synthetic continue prompt when auto is enabled",
@@ -980,94 +1002,57 @@ describe("session.compaction.process", () => {
9801002
}),
9811003
)
9821004

983-
test("persists tail_start_id for retained recent turns", async () => {
984-
await using tmp = await tmpdir()
985-
await WithInstance.provide({
986-
directory: tmp.path,
987-
fn: async () => {
988-
const session = await svc.create({})
989-
await user(session.id, "first")
990-
const keep = await user(session.id, "second")
991-
await user(session.id, "third")
992-
await SessionCompaction.create({
993-
sessionID: session.id,
994-
agent: "build",
995-
model: ref,
996-
auto: false,
997-
})
1005+
itProcess.instance(
1006+
"persists tail_start_id for retained recent turns",
1007+
Effect.gen(function* () {
1008+
const ssn = yield* SessionNs.Service
1009+
const session = yield* ssn.create({})
1010+
yield* createUserMessage(session.id, "first")
1011+
const keep = yield* createUserMessage(session.id, "second")
1012+
yield* createUserMessage(session.id, "third")
1013+
yield* SessionCompaction.use.create({ sessionID: session.id, agent: "build", model: ref, auto: false })
9981014

999-
const rt = runtime(
1000-
"continue",
1001-
Plugin.defaultLayer,
1002-
wide(),
1003-
cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }),
1004-
)
1005-
try {
1006-
const msgs = await svc.messages({ sessionID: session.id })
1007-
const parent = msgs.at(-1)?.info.id
1008-
expect(parent).toBeTruthy()
1009-
await rt.runPromise(
1010-
SessionCompaction.Service.use((svc) =>
1011-
svc.process({
1012-
parentID: parent!,
1013-
messages: msgs,
1014-
sessionID: session.id,
1015-
auto: false,
1016-
}),
1017-
),
1018-
)
1015+
const msgs = yield* ssn.messages({ sessionID: session.id })
1016+
const parent = msgs.at(-1)?.info.id
1017+
expect(parent).toBeTruthy()
1018+
yield* SessionCompaction.use.process({
1019+
parentID: parent!,
1020+
messages: msgs,
1021+
sessionID: session.id,
1022+
auto: false,
1023+
})
10191024

1020-
const part = await lastCompactionPart(session.id)
1021-
expect(part?.type).toBe("compaction")
1022-
expect(part?.tail_start_id).toBe(keep.id)
1023-
} finally {
1024-
await rt.dispose()
1025-
}
1026-
},
1027-
})
1028-
})
1025+
const part = yield* readLastCompactionPart(session.id)
1026+
expect(part?.type).toBe("compaction")
1027+
expect(part?.tail_start_id).toBe(keep.id)
1028+
}).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) }))),
1029+
)
10291030

1030-
test("shrinks retained tail to fit preserve token budget", async () => {
1031-
await using tmp = await tmpdir()
1032-
await WithInstance.provide({
1033-
directory: tmp.path,
1034-
fn: async () => {
1035-
const session = await svc.create({})
1036-
await user(session.id, "first")
1037-
await user(session.id, "x".repeat(2_000))
1038-
const keep = await user(session.id, "tiny")
1039-
await SessionCompaction.create({
1040-
sessionID: session.id,
1041-
agent: "build",
1042-
model: ref,
1043-
auto: false,
1044-
})
1031+
itProcess.instance(
1032+
"shrinks retained tail to fit preserve token budget",
1033+
Effect.gen(function* () {
1034+
const ssn = yield* SessionNs.Service
1035+
const session = yield* ssn.create({})
1036+
yield* createUserMessage(session.id, "first")
1037+
yield* createUserMessage(session.id, "x".repeat(2_000))
1038+
const keep = yield* createUserMessage(session.id, "tiny")
1039+
yield* SessionCompaction.use.create({ sessionID: session.id, agent: "build", model: ref, auto: false })
10451040

1046-
const rt = runtime("continue", Plugin.defaultLayer, wide(), cfg({ tail_turns: 2, preserve_recent_tokens: 100 }))
1047-
try {
1048-
const msgs = await svc.messages({ sessionID: session.id })
1049-
const parent = msgs.at(-1)?.info.id
1050-
expect(parent).toBeTruthy()
1051-
await rt.runPromise(
1052-
SessionCompaction.Service.use((svc) =>
1053-
svc.process({
1054-
parentID: parent!,
1055-
messages: msgs,
1056-
sessionID: session.id,
1057-
auto: false,
1058-
}),
1059-
),
1060-
)
1041+
const msgs = yield* ssn.messages({ sessionID: session.id })
1042+
const parent = msgs.at(-1)?.info.id
1043+
expect(parent).toBeTruthy()
1044+
yield* SessionCompaction.use.process({
1045+
parentID: parent!,
1046+
messages: msgs,
1047+
sessionID: session.id,
1048+
auto: false,
1049+
})
10611050

1062-
const part = await lastCompactionPart(session.id)
1063-
expect(part?.type).toBe("compaction")
1064-
expect(part?.tail_start_id).toBe(keep.id)
1065-
} finally {
1066-
await rt.dispose()
1067-
}
1068-
},
1069-
})
1070-
})
1051+
const part = yield* readLastCompactionPart(session.id)
1052+
expect(part?.type).toBe("compaction")
1053+
expect(part?.tail_start_id).toBe(keep.id)
1054+
}).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 100 }) }))),
1055+
)
10711056

10721057
test("falls back to full summary when even one recent turn exceeds preserve token budget", async () => {
10731058
await using tmp = await tmpdir({ git: true })

0 commit comments

Comments
 (0)