diff --git a/cmd/olliesrv/internal/agent/history.go b/cmd/olliesrv/internal/agent/history.go index 1c2d24a..39cbb09 100644 --- a/cmd/olliesrv/internal/agent/history.go +++ b/cmd/olliesrv/internal/agent/history.go @@ -413,6 +413,8 @@ func (s *History) estimateTokens() int { return chars / 4 } +const maxColdSummariesPerCall = 8 + // stripCold summarizes large tool-result messages outside the hot tail // using a single batched LLM call. Messages in the last hotTailSize slots // are left verbatim. @@ -455,6 +457,9 @@ func (s *History) stripCold(ctx context.Context, b backend.Backend) (backend.Usa continue } items = append(items, item{idx: i, orig: m.Content, messageID: id, hash: toolSummaryHash(*m)}) + if len(items) >= maxColdSummariesPerCall { + break + } } } } diff --git a/cmd/olliesrv/internal/agent/history_incremental_test.go b/cmd/olliesrv/internal/agent/history_incremental_test.go new file mode 100644 index 0000000..a20f2ef --- /dev/null +++ b/cmd/olliesrv/internal/agent/history_incremental_test.go @@ -0,0 +1,48 @@ +package agent + +import ( + "context" + "strings" + "testing" + + "ollie/cmd/olliesrv/internal/backend" +) + +func TestStripColdLimitsEachBatch(t *testing.T) { + b := &batchSummaryBackend{} + h := newHistory("test") + for i := 0; i < hotTailSize+maxColdSummariesPerCall+1; i++ { + m := backend.Message{Role: "user", Content: "hot"} + if i < maxColdSummariesPerCall+1 { + m = backend.Message{Role: "tool", ToolCallID: "tc-" + string(rune('0'+i)), Content: strings.Repeat("result ", 40)} + } + h.messages = append(h.messages, m) + } + + if _, _ = h.stripCold(context.Background(), b); b.calls != 1 { + t.Fatalf("first stripCold calls = %d, want 1", b.calls) + } + if b.items != maxColdSummariesPerCall { + t.Fatalf("first batch items = %d, want %d", b.items, maxColdSummariesPerCall) + } +} + +type batchSummaryBackend struct { + calls int + items int +} + +func (b *batchSummaryBackend) ChatStream(_ context.Context, messages []backend.Message, _ []backend.Tool, _ backend.GenerationParams) (<-chan backend.StreamEvent, error) { + b.calls++ + b.items = strings.Count(messages[0].Content, "id=tool:") + out := make(chan backend.StreamEvent, 1) + out <- backend.StreamEvent{Content: `{}`, Done: true} + close(out) + return out, nil +} +func (b *batchSummaryBackend) Name() string { return "test" } +func (b *batchSummaryBackend) DefaultModel() string { return "test" } +func (b *batchSummaryBackend) Model() string { return "test" } +func (b *batchSummaryBackend) SetModel(string) {} +func (b *batchSummaryBackend) ContextLength(context.Context) int { return 128000 } +func (b *batchSummaryBackend) Models(context.Context) []string { return []string{"test"} }