mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-20 02:17:24 +00:00
fix: persist mid-loop compaction to stop re-compaction loop and revive episodic
The v3 pipeline compacts session history mid-loop (prune_stage + final-request guard) but only mutates the run's message buffer, never the session store. Each turn reloads full history and re-compacts from scratch: message_tokens climb 129k->156k across turns while every turn compacts back down to ~60k. The lossy compaction differs per run, degrading the agent. The same missing persistence stalls episodic memory: the cumulative compaction count never advances, so the episodic worker's idempotency key (sessionKey:count) is pinned and every cycle after the first is skipped. Observed on live traffic: 8 run.completed since deploy, 0 new episodic. Fixes, all reusing existing machinery (no new store methods, no migrations): - Bug A: emitSessionCompleted reads cumulative GetCompactionCount (matching the legacy v2 path) instead of the per-run counter that resets to 0. - Bug B/anti-loop: finalize passes state.Prune.MidLoopCompacted into maybeSummarize; under pressure it lowers the trigger to a unit-aligned threshold (compactionInputCap - overhead, same MaxRequestShare the guard uses) so the compaction is PERSISTED via the existing TruncateHistory + IncrementCompaction path. Defensive floor prevents over-compaction on pathological config; tool-result-only bloat still skips (history-only). - Bug C: SourceID embeds the count (sessionKey:count) so the eventbus dedup key advances per compaction cycle instead of swallowing rapid same-session turns within the 5m TTL. Tests: episodic compaction, maybe_summarize pressure, request budget. go build (PG + sqliteonly), go vet, go test -race all green.
This commit is contained in:
1 parent
9957a94868
commit
5ca433b8ac
69 files changed
+4110
-371
No files matched your search
@@ -25,8 +25,9 @@ func TestCompactMessagesInPlace_UsesDefaultTimeout(t *testing.T) {
|
||||
capturingProvider: capturingProvider{response: "Summary of conversation."},
|
||||
}
|
||||
loop := &Loop{
|
||||
provider: provider,
|
||||
model: "claude-3-5-sonnet",
|
||||
provider: provider,
|
||||
model: "claude-3-5-sonnet",
|
||||
contextWindow: 200_000,
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
@@ -43,8 +44,9 @@ func TestCompactMessagesInPlace_UsesConfiguredTimeout(t *testing.T) {
|
||||
capturingProvider: capturingProvider{response: "Summary of conversation."},
|
||||
}
|
||||
loop := &Loop{
|
||||
provider: provider,
|
||||
model: "claude-3-5-sonnet",
|
||||
provider: provider,
|
||||
model: "claude-3-5-sonnet",
|
||||
contextWindow: 200_000,
|
||||
compactionCfg: &config.CompactionConfig{
|
||||
TimeoutSeconds: 45,
|
||||
},
|
||||
@@ -64,8 +66,9 @@ func TestCompactMessagesInPlace_NonPositiveTimeoutFallsBackToDefault(t *testing.
|
||||
capturingProvider: capturingProvider{response: "Summary of conversation."},
|
||||
}
|
||||
loop := &Loop{
|
||||
provider: provider,
|
||||
model: "claude-3-5-sonnet",
|
||||
provider: provider,
|
||||
model: "claude-3-5-sonnet",
|
||||
contextWindow: 200_000,
|
||||
compactionCfg: &config.CompactionConfig{
|
||||
TimeoutSeconds: -1,
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user