feat: add context compaction to automatically summarize old messages (#compaction) (#276)

When input tokens exceed a configurable threshold after a conversation round,
the system asynchronously compacts older messages into a summary. Cascading
compactions reference prior summaries via <prior_context> tags to maintain
conversational continuity without duplicating content.

- Add bot_history_message_compacts table and compact_id on messages
- Add compaction_enabled, compaction_threshold, compaction_model_id to bots
- Implement compaction service (internal/compaction) with LLM summarization
- Integrate into conversation flow: replace compacted messages with summaries
  wrapped in <summary> tags during context loading
- Add REST API endpoints (GET/DELETE /bots/:bot_id/compaction/logs)
- Add frontend Compaction tab with settings and log viewer
- Wire compaction service into both dev (cmd/agent) and prod (cmd/memoh) entry points
- Update test mocks to include new GetBotByID columns
This commit is contained in:
Acbox Liu
2026-03-22 14:26:00 +08:00
committed by GitHub
parent 91e5e44509
commit de62f94315
40 changed files with 2375 additions and 197 deletions
+10 -2
View File
@@ -482,7 +482,7 @@ func toMessageFromSinceBySessionRow(row sqlc.ListMessagesSinceBySessionRow) Mess
}
func toMessageFromActiveSinceRow(row sqlc.ListActiveMessagesSinceRow) Message {
return toMessageFields(
m := toMessageFields(
row.ID,
row.BotID,
row.SessionID,
@@ -499,10 +499,14 @@ func toMessageFromActiveSinceRow(row sqlc.ListActiveMessagesSinceRow) Message {
row.Usage,
row.CreatedAt,
)
if row.CompactID.Valid {
m.CompactID = row.CompactID.String()
}
return m
}
func toMessageFromActiveSinceBySessionRow(row sqlc.ListActiveMessagesSinceBySessionRow) Message {
return toMessageFields(
m := toMessageFields(
row.ID,
row.BotID,
row.SessionID,
@@ -519,6 +523,10 @@ func toMessageFromActiveSinceBySessionRow(row sqlc.ListActiveMessagesSinceBySess
row.Usage,
row.CreatedAt,
)
if row.CompactID.Valid {
m.CompactID = row.CompactID.String()
}
return m
}
func toMessageFromLatestRow(row sqlc.ListMessagesLatestRow) Message {
+1
View File
@@ -36,6 +36,7 @@ type Message struct {
Metadata map[string]any `json:"metadata,omitempty"`
Usage json.RawMessage `json:"usage,omitempty"`
Assets []MessageAsset `json:"assets,omitempty"`
CompactID string `json:"compact_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
}