Merge branch 'fix/g09' into fix/integrated

This commit is contained in:
kami
2026-08-01 14:22:24 +04:00
31 changed files with 1431 additions and 159 deletions
+7 -1
View File
@@ -64,11 +64,17 @@ func (m *MemoryStore) Insert(ctx context.Context, id string, vec []float32, meta
// Search returns the topK nearest rows by cosine similarity. A full scan; see
// the type doc for why that's fine at this scale.
//
// Rows under memory.NonRecallPrefix are excluded in SQL. They are speaker
// voiceprints sharing this table, and note recall must not rank them; see that
// constant for why the previous arrangement only appeared to do this.
func (m *MemoryStore) Search(ctx context.Context, vec []float32, topK int) ([]memory.Result, error) {
if topK <= 0 {
topK = 10
}
rows, err := m.db.QueryContext(ctx, `SELECT id, vec, meta FROM memory_vectors`)
rows, err := m.db.QueryContext(ctx,
`SELECT id, vec, meta FROM memory_vectors WHERE id NOT LIKE ? ESCAPE '\'`,
escapeLike(memory.NonRecallPrefix)+"%")
if err != nil {
return nil, fmt.Errorf("memory: scan: %w", err)
}
+38
View File
@@ -3,7 +3,10 @@ package store
import (
"context"
"path/filepath"
"strings"
"testing"
"github.com/kami/maven/internal/memory"
)
func newMemTestStore(t *testing.T) *Store {
@@ -101,3 +104,38 @@ func TestMemoryStorePersistsAcrossReopen(t *testing.T) {
t.Fatalf("memory did not survive reopen: %v", got)
}
}
// A voiceprint sharing the vector table must never rank as a note hit. The
// dimensions match here on purpose: what used to hide these rows was cosine
// returning 0 on a width mismatch, which is a property of two model choices and
// not of the store.
func TestMemoryStoreSearchSkipsVoiceprints(t *testing.T) {
ctx := context.Background()
m := newMemTestStore(t).VectorMemory()
if err := m.Insert(ctx, "note:1", []float32{0, 1, 0}, map[string]string{"text": "заметка"}); err != nil {
t.Fatal(err)
}
if err := m.Insert(ctx, memory.NonRecallPrefix+"kami", []float32{1, 0, 0}, map[string]string{"name": "Ками"}); err != nil {
t.Fatal(err)
}
got, err := m.Search(ctx, []float32{1, 0, 0}, 10)
if err != nil {
t.Fatalf("Search: %v", err)
}
for _, r := range got {
if strings.HasPrefix(r.ID, memory.NonRecallPrefix) {
t.Fatalf("recall returned a voiceprint: %+v", r)
}
}
if len(got) != 1 || got[0].ID != "note:1" {
t.Fatalf("Search = %+v, want just the note", got)
}
// The row is still there for the speaker code that owns it.
recs, err := m.ByPrefix(ctx, memory.NonRecallPrefix)
if err != nil || len(recs) != 1 {
t.Fatalf("ByPrefix = %+v, %v; want the voiceprint", recs, err)
}
}