fix(compaction): adjust instructions and structure to be more clear to smaller models like dsv4 flash (#42045)

Co-authored-by: akenra <37288280+akenra@users.noreply.github.com>
This commit is contained in:
Aiden Cline
2026-08-12 14:36:51 -05:00
committed by GitHub
parent 39fb919a05
commit dab2637217
8 changed files with 207 additions and 52 deletions
@@ -4,12 +4,32 @@ import { SessionCompaction } from "@opencode-ai/core/session/compaction"
test("compaction prompt preserves detailed work state and relevant files", () => {
const prompt = SessionCompaction.buildPrompt({ context: ["conversation history"] })
expect(prompt).toStartWith(
"Here is the conversation so far:\n\n<conversation>\nconversation history\n</conversation>",
)
expect(prompt.indexOf("</conversation>")).toBeLessThan(prompt.indexOf("Create a new anchored summary"))
expect(prompt).toContain("conversation history in the <conversation> tags above")
expect(prompt).toContain("## Work State\n### Completed")
expect(prompt).toContain("### Active")
expect(prompt).toContain("### Blocked")
expect(prompt).toContain("## Relevant Files")
})
test("compaction prompt gives update instructions for a prior summary", () => {
const prompt = SessionCompaction.buildPrompt({
context: ["new conversation"],
previousSummary: "existing summary",
})
expect(prompt.indexOf("<conversation>")).toBeLessThan(prompt.indexOf("<prior-summary>"))
expect(prompt.indexOf("</prior-summary>")).toBeLessThan(prompt.indexOf("The <prior-summary> summarizes"))
expect(prompt).toContain(
"Carry forward objectives, constraints, user directives, decisions, and parallel workstreams from the <prior-summary>",
)
expect(prompt).toContain('Move completed work from "Active" to "Completed".')
expect(prompt).toContain('Update "Objective" and "Next Move" to reflect the current work state.')
})
test("compaction describes tool media without embedding base64", () => {
const base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"
const serialized = SessionCompaction.serializeToolContent([
+62 -1
View File
@@ -1135,7 +1135,7 @@ describe("SessionRunnerLLM", () => {
expect(requests).toHaveLength(2)
expect(userTexts(requests[0])[0]).toContain(
"<previous-summary>\n## Objective\n- Preserve the task\n</previous-summary>",
"<prior-summary>\n## Objective\n- Preserve the task\n</prior-summary>",
)
expect(userTexts(requests[0])[0]).toContain("Recent exact request")
expect((yield* (yield* SessionStore.Service).context(sessionID))[0]).toMatchObject({
@@ -1145,6 +1145,67 @@ describe("SessionRunnerLLM", () => {
}),
)
it.effect("retains only complete serialized messages during compaction", () =>
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
const earlier = `EARLIER_BOUNDARY ${"a".repeat(3_000)} EARLIER_END`
const recent = `RECENT_BOUNDARY ${"b".repeat(3_000)} RECENT_END`
response = fragmentFixture("text", "text-earlier", ["Earlier answer"]).completeEvents
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: earlier }), resume: false })
yield* session.resume(sessionID)
currentModel = compactModel
requests.length = 0
responses = [
fragmentFixture("text", "text-summary", ["## Objective\n- Preserve the task"]).completeEvents,
fragmentFixture("text", "text-final", ["Continued"]).completeEvents,
]
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: recent }), resume: false })
yield* session.resume(sessionID)
expect(requests).toHaveLength(2)
const summary = userTexts(requests[0])[0]
const continuation = userTexts(requests[1])[0]
expect(summary.match(/EARLIER_BOUNDARY/g)).toHaveLength(1)
expect(summary).toContain(`EARLIER_BOUNDARY ${"a".repeat(3_000)} EARLIER_END`)
expect(summary).not.toContain("RECENT_BOUNDARY")
expect(continuation).not.toContain("EARLIER_BOUNDARY")
expect(continuation).not.toContain("EARLIER_END")
expect(continuation).toContain("<recent-context>\n[Assistant]: Earlier answer")
expect(continuation).toContain(`RECENT_BOUNDARY ${"b".repeat(3_000)} RECENT_END`)
}),
)
it.effect("summarizes an oversized newest message without retaining a fragment", () =>
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
response = fragmentFixture("text", "text-earlier", ["Earlier answer"]).completeEvents
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Earlier question" }), resume: false })
yield* session.resume(sessionID)
const oversized = `OVERSIZED_BOUNDARY ${"x".repeat(4_500)} OVERSIZED_END`
currentModel = compactModel
requests.length = 0
responses = [
fragmentFixture("text", "text-summary", ["## Objective\n- Preserve the task"]).completeEvents,
fragmentFixture("text", "text-final", ["Continued"]).completeEvents,
]
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: oversized }), resume: false })
yield* session.resume(sessionID)
expect(requests).toHaveLength(2)
const summary = userTexts(requests[0])[0]
const continuation = userTexts(requests[1])[0]
expect(summary.match(/OVERSIZED_BOUNDARY/g)).toHaveLength(1)
expect(summary).toContain(oversized)
expect(continuation).not.toContain("OVERSIZED_BOUNDARY")
expect(continuation).not.toContain("OVERSIZED_END")
expect(continuation).toContain("<recent-context>\n\n</recent-context>")
}),
)
it.effect("forces one compaction and retries after provider context overflow", () =>
Effect.gen(function* () {
const session = yield* setupOverflowRecovery