Skip to content

Commit aa86fb7

Browse files
committed
refactor compaction tail selection
1 parent 6f5a3d3 commit aa86fb7

1 file changed

Lines changed: 45 additions & 22 deletions

File tree

packages/opencode/src/session/compaction.ts

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ export namespace SessionCompaction {
3939
const DEFAULT_TAIL_TURNS = 2
4040
const MIN_TAIL_TOKENS = 2_000
4141
const MAX_TAIL_TOKENS = 8_000
42+
type Turn = {
43+
start: number
44+
end: number
45+
id: MessageID
46+
}
4247

4348
function usable(input: { cfg: Config.Info; model: Provider.Model }) {
4449
const reserved =
@@ -55,6 +60,24 @@ export namespace SessionCompaction {
5560
)
5661
}
5762

63+
function turns(messages: MessageV2.WithParts[]) {
64+
const result: Turn[] = []
65+
for (let i = 0; i < messages.length; i++) {
66+
const msg = messages[i]
67+
if (msg.info.role !== "user") continue
68+
if (msg.parts.some((part) => part.type === "compaction")) continue
69+
result.push({
70+
start: i,
71+
end: messages.length,
72+
id: msg.info.id,
73+
})
74+
}
75+
for (let i = 0; i < result.length - 1; i++) {
76+
result[i].end = result[i + 1].start
77+
}
78+
return result
79+
}
80+
5881
export interface Interface {
5982
readonly isOverflow: (input: {
6083
tokens: MessageV2.Assistant["tokens"]
@@ -123,36 +146,36 @@ export namespace SessionCompaction {
123146
const limit = input.cfg.compaction?.tail_turns ?? DEFAULT_TAIL_TURNS
124147
if (limit <= 0) return { head: input.messages, tail_start_id: undefined }
125148
const budget = tailBudget({ cfg: input.cfg, model: input.model })
126-
const turns = input.messages.flatMap((msg, idx) =>
127-
msg.info.role === "user" && !msg.parts.some((part) => part.type === "compaction") ? [idx] : [],
149+
const all = turns(input.messages)
150+
if (!all.length) return { head: input.messages, tail_start_id: undefined }
151+
const recent = all.slice(-limit)
152+
const sizes = yield* Effect.forEach(
153+
recent,
154+
(turn) =>
155+
estimate({
156+
messages: input.messages.slice(turn.start, turn.end),
157+
model: input.model,
158+
}),
159+
{ concurrency: 1 },
128160
)
129-
if (!turns.length) return { head: input.messages, tail_start_id: undefined }
161+
if (sizes.at(-1)! > budget) {
162+
log.info("tail fallback", { budget, size: sizes.at(-1) })
163+
return { head: input.messages, tail_start_id: undefined }
164+
}
130165

131166
let total = 0
132-
let start = input.messages.length
133-
let kept = 0
134-
135-
for (let i = turns.length - 1; i >= 0 && kept < limit; i--) {
136-
const idx = turns[i]
137-
const end = i + 1 < turns.length ? turns[i + 1] : input.messages.length
138-
const size = yield* estimate({
139-
messages: input.messages.slice(idx, end),
140-
model: input.model,
141-
})
142-
if (kept === 0 && size > budget) {
143-
log.info("tail fallback", { budget, size })
144-
return { head: input.messages, tail_start_id: undefined }
145-
}
167+
let keep: Turn | undefined
168+
for (let i = recent.length - 1; i >= 0; i--) {
169+
const size = sizes[i]
146170
if (total + size > budget) break
147171
total += size
148-
start = idx
149-
kept++
172+
keep = recent[i]
150173
}
151174

152-
if (kept === 0 || start === 0) return { head: input.messages, tail_start_id: undefined }
175+
if (!keep || keep.start === 0) return { head: input.messages, tail_start_id: undefined }
153176
return {
154-
head: input.messages.slice(0, start),
155-
tail_start_id: input.messages[start]?.info.id,
177+
head: input.messages.slice(0, keep.start),
178+
tail_start_id: keep.id,
156179
}
157180
})
158181

0 commit comments

Comments
 (0)