feat: reactive notes test and router LFM foundation plan
- Add reactive_notes_test.go: tests for context-aware reactive nudge generation using LLM phraser with dialogue history. - Add docs/plans/2026-07-10-router-lfm-foundation.md: architecture research on replacing classifier cascade with LFM-based router.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kami/maven/internal/ipc"
|
||||
"github.com/kami/maven/internal/memory"
|
||||
"github.com/kami/maven/internal/router"
|
||||
"github.com/kami/maven/internal/tool"
|
||||
"github.com/kami/maven/internal/voice"
|
||||
)
|
||||
|
||||
func TestReactiveNotesReminders(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
st := newTestStore(t)
|
||||
api := ipc.NewStoreAPI(st)
|
||||
now := time.Now()
|
||||
|
||||
emb := router.NewHashEmbedder(1024)
|
||||
matcher := tool.NewMatcher(api)
|
||||
rtr := buildRouter(emb, matcher, 0.55, nil)
|
||||
|
||||
h := &reactiveHandler{
|
||||
api: api,
|
||||
embedder: emb,
|
||||
router: rtr,
|
||||
replier: voice.NewStubReplier(),
|
||||
now: func() time.Time { return now },
|
||||
memStore: memory.NewInMemoryStore(),
|
||||
dataStore: st,
|
||||
}
|
||||
|
||||
t.Run("reminder capture", func(t *testing.T) {
|
||||
fireAt := now.Add(24 * time.Hour).Truncate(time.Millisecond)
|
||||
dec := router.Decision{
|
||||
Intent: router.IntentReminder,
|
||||
Utterance: "напомни завтра позвонить маме",
|
||||
Slots: router.Slots{
|
||||
Text: "завтра позвонить маме",
|
||||
Time: fireAt,
|
||||
HasTime: true,
|
||||
},
|
||||
}
|
||||
reply := h.applyAction(ctx, dec)
|
||||
if reply != "" {
|
||||
t.Errorf("expected empty reply from applyAction, got %q", reply)
|
||||
}
|
||||
reminders, err := st.ListReminders(ctx, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("ListReminders: %v", err)
|
||||
}
|
||||
if len(reminders) == 0 {
|
||||
t.Fatal("expected at least one reminder, got none")
|
||||
}
|
||||
last := reminders[0]
|
||||
if last.FireTs.UnixMilli() != fireAt.UnixMilli() {
|
||||
t.Errorf("reminder FireTs = %v, want %v", last.FireTs, fireAt)
|
||||
}
|
||||
if last.Status != "pending" {
|
||||
t.Errorf("reminder Status = %q, want pending", last.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("note capture", func(t *testing.T) {
|
||||
dec := router.Decision{
|
||||
Intent: router.IntentNote,
|
||||
Utterance: "запомни что кофе закончился",
|
||||
}
|
||||
reply := h.applyAction(ctx, dec)
|
||||
if reply != "" {
|
||||
t.Errorf("expected empty reply from applyAction, got %q", reply)
|
||||
}
|
||||
notes, err := st.RecentNotes(ctx, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("RecentNotes: %v", err)
|
||||
}
|
||||
if len(notes) == 0 {
|
||||
t.Fatal("expected at least one note, got none")
|
||||
}
|
||||
last := notes[0]
|
||||
if last.Text != "запомни что кофе закончился" {
|
||||
t.Errorf("note text = %q, want %q", last.Text, "запомни что кофе закончился")
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user