Add /topics and /drop-topic commands#4941
Draft
kimjune01 wants to merge 19 commits intoAider-AI:mainfrom
Draft
Conversation
…ry-summarizer) Port 4 modules from standalone implementation (145 existing tests): - context_window.py: Forest (union-find clusters) + ContextWindow (hot/cold zones) - embedding_service.py: Pure Python TF-IDF embedder (no new dependencies) - cluster_summarizer.py: Per-cluster summarization via model cascade - chat_summary_uf.py: ChatSummaryUF(ChatSummary) drop-in subclass Integration: --chat-history-summarizer union-find flag in args.py, conditional construction in main.py. Default unchanged (recursive). Backend fixes applied during port: - Stable root ordering via _root_order list (deterministic render output) - Weighted centroid averaging in union() (prevents cluster identity distortion) Safety: mandatory fallback to recursive if output exceeds budget, stale-safety preserved via _fed_count mechanism, same tokenizer and model cascade. 49 new tests covering 12 areas. 523 existing tests unaffected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8 tests with realistic 3-topic conversation (path traversal, Windows drive letters, file descriptor leak): cluster formation, output format, cold+hot render, distinct topic clustering, recursive fallback, stale rebuild, system message filtering, deterministic rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix hot tail mismatch: track fed message indices so hot_count maps back to correct original messages even with system/tool messages interspersed - Fix unbounded _hot growth: trim graduated entries after each append - Fix empty embedding drop in union(): preserve non-empty side - Remove unused Forest.is_dirty() and Forest.dirty_inputs() - Remove list-vector branches from _cosine_similarity and union() (all embeddings are sparse dicts from TFIDFEmbedder) - Add tests: mixed-role preservation (2), memory bounds (2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- resolve_dirty() failure now falls back to recursive instead of crashing - Remove _maybe_evict() and evict_at parameter — dead code since _maybe_graduate() already keeps hot zone at <= graduate_at - Update all tests to remove evict_at references Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codex (GPT-5.4) independent implementation of PR 2 from spec. 67 tests passing, 565 total, zero regressions. Co-Authored-By: codex (GPT-5.4) <noreply@openai.com>
This was referenced Mar 19, 2026
Author
Demo output (mock model, simulated 40-message session)Five distinct topics: database migrations, JWT auth, React components, CI/CD, payment processing. To try it yourself: |
Non-root members' _content and _embedding are only needed before merge (for dirty input collection and centroid computation). After union(), only the root's centroid and summary matter. Without cleanup, _content grows unbounded in long sessions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Control-loop bug: summarization triggers on tokens (too_big) but graduation triggers on message count (graduate_at=26). Token budget fires first, no cold clusters exist, falls back to recursive every time. Union-find path was unreachable in real usage. Fix: when summarize() runs with no cold clusters and >4 hot messages, force_graduate() moves the oldest half to the cold forest. This breaks the deadlock and lets clusters form before rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… union() Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
…ic interpretation Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Co-authored-by: aider (claude-sonnet-4-5) <aider@aider.chat>
Token-aware graduation: keep only as many hot messages as fit in 25% of max_tokens. Replaces fixed graduate_at=26 which caused a control-loop deadlock (too_big fires on tokens, graduation on message count — never aligned). Integration tests use real model tokenizer (litellm) with mock LLM calls. Tests: cold clusters form, result fits budget, /topics shows clusters, /drop-topic removes them, repeated cycles work, low budget falls back. These tests would have caught both the control-loop bug and the budget overflow that were only discovered during manual testing. Removes debug logging from chat_summary_uf.py and base_coder.py. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #4940. Review after that PR merges.
Why two PRs
PR 1 (#4940) adds the backend — an opt-in union-find summarizer that maintains topic clusters. It changes nothing by default and can be evaluated on its own merits: quality-equivalent output, mandatory fallback to recursive, 57 tests.
This PR adds the user-facing commands that make the structured backend useful:
/topics— see what the model remembers, with token counts/drop-topic N— remove one stale topic without wiping everythingSplitting lets the maintainer evaluate the data structure separately from the UX surface. The backend is the risky part; the commands are straightforward.
What it does
/drop-topicupdatesdone_messagesimmediately (same pattern as/clear). Both commands refuse during background summarization. Without--chat-history-summarizer union-find, they show a guidance message.Addresses #3607 (selective history control), #2219 (see/edit context), #948 (token breakdown with actions).
Test plan
done_messagesexcludes dropped content → verify forest rebuild🤖 Generated with Claude Code and Codex