refactor compaction tail selection

This commit is contained in:
Shoubhit Dash 2026-04-10 09:36:39 +05:30
parent 6f5a3d30fd
commit aa86fb75ad

View file

@ -39,6 +39,11 @@ export namespace SessionCompaction {
const DEFAULT_TAIL_TURNS = 2
const MIN_TAIL_TOKENS = 2_000
const MAX_TAIL_TOKENS = 8_000
type Turn = {
start: number
end: number
id: MessageID
}
function usable(input: { cfg: Config.Info; model: Provider.Model }) {
const reserved =
@ -55,6 +60,24 @@ export namespace SessionCompaction {
)
}
function turns(messages: MessageV2.WithParts[]) {
const result: Turn[] = []
for (let i = 0; i < messages.length; i++) {
const msg = messages[i]
if (msg.info.role !== "user") continue
if (msg.parts.some((part) => part.type === "compaction")) continue
result.push({
start: i,
end: messages.length,
id: msg.info.id,
})
}
for (let i = 0; i < result.length - 1; i++) {
result[i].end = result[i + 1].start
}
return result
}
export interface Interface {
readonly isOverflow: (input: {
tokens: MessageV2.Assistant["tokens"]
@ -123,36 +146,36 @@ export namespace SessionCompaction {
const limit = input.cfg.compaction?.tail_turns ?? DEFAULT_TAIL_TURNS
if (limit <= 0) return { head: input.messages, tail_start_id: undefined }
const budget = tailBudget({ cfg: input.cfg, model: input.model })
const turns = input.messages.flatMap((msg, idx) =>
msg.info.role === "user" && !msg.parts.some((part) => part.type === "compaction") ? [idx] : [],
const all = turns(input.messages)
if (!all.length) return { head: input.messages, tail_start_id: undefined }
const recent = all.slice(-limit)
const sizes = yield* Effect.forEach(
recent,
(turn) =>
estimate({
messages: input.messages.slice(turn.start, turn.end),
model: input.model,
}),
{ concurrency: 1 },
)
if (!turns.length) return { head: input.messages, tail_start_id: undefined }
let total = 0
let start = input.messages.length
let kept = 0
for (let i = turns.length - 1; i >= 0 && kept < limit; i--) {
const idx = turns[i]
const end = i + 1 < turns.length ? turns[i + 1] : input.messages.length
const size = yield* estimate({
messages: input.messages.slice(idx, end),
model: input.model,
})
if (kept === 0 && size > budget) {
log.info("tail fallback", { budget, size })
return { head: input.messages, tail_start_id: undefined }
}
if (total + size > budget) break
total += size
start = idx
kept++
if (sizes.at(-1)! > budget) {
log.info("tail fallback", { budget, size: sizes.at(-1) })
return { head: input.messages, tail_start_id: undefined }
}
if (kept === 0 || start === 0) return { head: input.messages, tail_start_id: undefined }
let total = 0
let keep: Turn | undefined
for (let i = recent.length - 1; i >= 0; i--) {
const size = sizes[i]
if (total + size > budget) break
total += size
keep = recent[i]
}
if (!keep || keep.start === 0) return { head: input.messages, tail_start_id: undefined }
return {
head: input.messages.slice(0, start),
tail_start_id: input.messages[start]?.info.id,
head: input.messages.slice(0, keep.start),
tail_start_id: keep.id,
}
})