package main import ( "context" "log" "strconv" "github.com/kami/maven/internal/router" ) // actionNote handles router.IntentNote: embed the note, persist it, and // index it for recall. func (h *reactiveHandler) actionNote(ctx context.Context, dec router.Decision) string { // An utterance that explicitly files a task is work, not recall, and // belongs in the task store (Vikunja #130). Checked before the embedding // is paid for. Everything else is a note, exactly as before. if reply, ok := h.captureTaskFromNote(ctx, dec); ok { return reply } // embed the note text with the same model the classifier uses, persist // via CoreAPI (source=tap:voice). Semantic recall lives in `notes`, not // facts — no predicate reads it (spec's two-memory split). vec, err := router.EmbedPassage(ctx, h.embedder, dec.Utterance) if err != nil { log.Printf("voice: embed note: %v", err) return "не получилось сохранить заметку." } noteTs := h.now() noteID, err := h.api.WriteNote(ctx, noteTs, dec.Utterance, vec, "tap:voice") if err != nil { log.Printf("voice: write note: %v", err) return "не получилось сохранить заметку." } // Insert into long-term memory (best-effort, must not fail the note write). // text/ts in the meta make a Search hit self-describing (see bestRecall). if h.memStore != nil { if err := h.memStore.Insert(ctx, "note:"+strconv.FormatInt(noteID, 10), vec, map[string]string{ "source": "voice", "type": "note", "text": dec.Utterance, "ts": strconv.FormatInt(noteTs.Unix(), 10), }); err != nil { log.Printf("voice: memory insert: %v", err) } } return "" // replier phrases the "saved" reply }