normalize summary cache keys
This commit is contained in:
parent
adf7242774
commit
60c4541fa4
|
|
@ -101,12 +101,33 @@ func toolMessageID(m backend.Message) string {
|
|||
return "tool:content:" + hex.EncodeToString(h[:])
|
||||
}
|
||||
|
||||
const coldSummaryCacheVersion = "v2"
|
||||
|
||||
func toolSummaryHash(m backend.Message) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(m.ToolCallID))
|
||||
h.Write([]byte{0})
|
||||
h.Write([]byte(m.Content))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
h := sha256.Sum256([]byte(coldSummaryCacheVersion + "\x00" + normalizeSummaryCacheContent(m.Content)))
|
||||
return hex.EncodeToString(h[:])
|
||||
}
|
||||
|
||||
func normalizeSummaryCacheContent(content string) string {
|
||||
if looksStructured(content) {
|
||||
return content
|
||||
}
|
||||
lines := strings.Split(strings.ReplaceAll(content, "\r\n", "\n"), "\n")
|
||||
normalized := make([]string, 0, len(lines))
|
||||
blank := false
|
||||
for _, line := range lines {
|
||||
line = strings.TrimRight(line, " \t")
|
||||
if strings.TrimSpace(line) == "" {
|
||||
if blank {
|
||||
continue
|
||||
}
|
||||
blank = true
|
||||
} else {
|
||||
blank = false
|
||||
}
|
||||
normalized = append(normalized, line)
|
||||
}
|
||||
return strings.TrimSpace(strings.Join(normalized, "\n"))
|
||||
}
|
||||
|
||||
func (s *History) history() []backend.Message {
|
||||
|
|
|
|||
|
|
@ -77,6 +77,27 @@ func TestStripColdReusesSummaryCache(t *testing.T) {
|
|||
t.Fatalf("cached summary was not applied: %#v", h.messages[0])
|
||||
}
|
||||
}
|
||||
func TestStripColdReusesSummaryCacheAcrossToolCalls(t *testing.T) {
|
||||
b := &summaryBackend{}
|
||||
h := newHistory("test")
|
||||
for i := 0; i < 9; i++ {
|
||||
m := backend.Message{Role: "user", Content: "hot"}
|
||||
if i == 0 {
|
||||
m = backend.Message{Role: "tool", ToolCallID: "tc-0", Content: strings.Repeat("same result ", 30)}
|
||||
}
|
||||
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)
|
||||
}
|
||||
h.messages[0] = backend.Message{Role: "tool", ToolCallID: "tc-0", Content: strings.Repeat("same result ", 30)}
|
||||
if _, _ = h.stripCold(context.Background(), b); b.calls != 1 {
|
||||
t.Fatalf("different-call cache calls = %d, want 1", b.calls)
|
||||
}
|
||||
if !h.messages[0].SummaryApplied || h.messages[0].Content != "summary" {
|
||||
t.Fatalf("cached summary was not applied: %#v", h.messages[0])
|
||||
}
|
||||
}
|
||||
func TestStripColdSkipsAlreadySummarizedAfterRestore(t *testing.T) {
|
||||
b := &summaryBackend{}
|
||||
h := RestoreHistoryFromMessages([]backend.Message{
|
||||
|
|
@ -99,11 +120,17 @@ type summaryBackend struct{ calls int }
|
|||
|
||||
func (b *summaryBackend) ChatStream(_ context.Context, messages []backend.Message, _ []backend.Tool, _ backend.GenerationParams) (<-chan backend.StreamEvent, error) {
|
||||
b.calls++
|
||||
if len(messages) != 1 || !strings.Contains(messages[0].Content, "id=tool:tc-0:") {
|
||||
if len(messages) != 1 || !strings.Contains(messages[0].Content, "id=tool:") {
|
||||
return nil, fmt.Errorf("summary request did not use stable message ID")
|
||||
}
|
||||
start := strings.Index(messages[0].Content, "id=") + len("id=")
|
||||
end := strings.Index(messages[0].Content[start:], ":\n")
|
||||
if end < 0 {
|
||||
return nil, fmt.Errorf("summary request has malformed message ID")
|
||||
}
|
||||
id := messages[0].Content[start : start+end]
|
||||
out := make(chan backend.StreamEvent, 1)
|
||||
out <- backend.StreamEvent{Content: `{"tool:tc-0":"summary"}`, Done: true}
|
||||
out <- backend.StreamEvent{Content: fmt.Sprintf(`{"%s":"summary"}`, id), Done: true}
|
||||
close(out)
|
||||
return out, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"ollie/cmd/olliesrv/internal/backend"
|
||||
)
|
||||
|
||||
func messageWithContent(callID, content string) backend.Message {
|
||||
return backend.Message{Role: "tool", ToolCallID: callID, Content: content}
|
||||
}
|
||||
|
||||
func TestNormalizeSummaryCacheContentPlainText(t *testing.T) {
|
||||
got := normalizeSummaryCacheContent(" first line \r\n\r\n\nsecond line \t\n\n")
|
||||
if got != "first line\n\nsecond line" {
|
||||
t.Fatalf("normalized content = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeSummaryCacheContentPreservesStructuredContent(t *testing.T) {
|
||||
content := "{\r\n \"key\": \"value\"\r\n}\r\n"
|
||||
if got := normalizeSummaryCacheContent(content); got != content {
|
||||
t.Fatalf("structured content changed: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToolSummaryHashIgnoresToolCallID(t *testing.T) {
|
||||
first := toolSummaryHash(messageWithContent("first-call", "same result\n"))
|
||||
second := toolSummaryHash(messageWithContent("second-call", "same result"))
|
||||
if first != second {
|
||||
t.Fatalf("hashes differ for identical content: %q != %q", first, second)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToolSummaryHashSeparatesContent(t *testing.T) {
|
||||
first := toolSummaryHash(messageWithContent("call", "first result"))
|
||||
second := toolSummaryHash(messageWithContent("call", "second result"))
|
||||
if first == second {
|
||||
t.Fatal("different content produced the same hash")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue