feat(chat): add summary budget discipline for compaction#652
feat(chat): add summary budget discipline for compaction#652Cstewart-HC wants to merge 2 commits intomoltis-org:mainfrom
Conversation
Enforce hard budget on LLM-generated compaction summaries via new compress_summary() function: - Max 1,200 characters total - Max 24 lines - Max 160 characters per line - Case-insensitive line deduplication - Priority-based line dropping: headers > bullets > plain text - Omission notice appended when lines are dropped Applied to both compact() method and standalone compact_session() function. Includes 8 unit tests covering all budget enforcement paths.
Greptile SummaryThis PR adds Confidence Score: 4/5Safe to merge after addressing the previously flagged off-by-one and notice-undercount bugs; the two new findings here are low-impact. The three issues from prior review threads (off-by-one in fold budget, omission notice undercount for dropped headers, blank-line dedup) remain open and affect correctness of the budget enforcement in real inputs. The two new findings (doc comment vs implementation mismatch and stale notice estimate) are P2 but the doc mismatch weakens the test contract. Score is 4 pending resolution of at least the previously flagged P1-level issues. crates/chat/src/lib.rs — the compress_summary function, specifically the fold-based total_len calculations and edge-case char_budget path Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A([compress_summary input]) --> B{text.trim is empty?}
B -- yes --> Z([return empty string])
B -- no --> C[Step 1: deduplicate lines case-insensitive, keep first]
C --> D[Step 2: truncate lines > 160 chars at char boundary]
D --> E{len <= 24 lines AND <= 1200 chars?}
E -- yes --> F([return joined as-is])
E -- no --> G[Partition into headers / bullets / others]
G --> H[candidates = bullets ++ others, headers always kept]
H --> I{all candidates fit with headers?}
I -- yes --> J([return headers ++ candidates])
I -- no --> K[Drop loop: drop from tail of candidates until line_count + notice <= 24 AND total chars <= 1200]
K -- fits --> L([return headers ++ kept ++ notice])
K -- never fits --> M[Edge case: headers alone too long, keep headers until char/line budget, append notice with full drop count]
M --> N([return kept headers ++ notice])
Reviews (2): Last reviewed commit: "fix(chat): correct budget accounting bug..." | Re-trigger Greptile |
Fix three Greptile review findings from PR moltis-org#652: 1. Off-by-one in fold-based character budget: fold adds l.len() + 1 per element but join("\n") produces N-1 separators, overcounting by 1 byte and dropping an extra line at the budget boundary. Added .saturating_sub(1) to both fold sites. 2. Omission notice undercounts when headers are also truncated: all_dropped_count only counted candidates, not headers skipped in the edge-case loop. Track header_drop_count separately and build the notice after the loop with the combined total. Changed break to continue so shorter headers later in the list still get a chance to fit. 3. Empty lines bypass deduplication: key.is_empty() short-circuited seen.insert(), allowing unlimited blank lines through. Removed the short-circuit so blank lines are deduplicated like any other line.
|
@greptileai new commit landed to address issues. |
Summary
Enforce hard budget on LLM-generated compaction summaries via new
compress_summary()function incrates/chat/src/lib.rs.Without this, auto-compact can produce arbitrarily long summaries that overflow the context window on the next turn — defeating the purpose of compaction.
Changes
crates/chat/src/lib.rs(+269 lines) — newcompress_summary()function:compact()method and standalonecompact_session()functionTesting
Also verified:
cargo check,cargo clippy,just format-check— all clean.Checklist
.unwrap()/.expect(), iterators over loops,#[must_use]on pure fns)