6a5121657a
Daemon side of Decision B: parse {"response","mood"} across the 4 consumers
(replier, nudges, reminders, chat), fall back to legacy formats. Drop the
LLM router — the classifier handles routing; replier/phraser share one
llm.Client (timeout 20s->60s). llm.Client reads reasoning_content when
content is empty (thinking models).
Docs: TTS piper-student plan (OmniVoice teacher -> piper student, from
scratch, phoneme-first). CLAUDE.md training guide.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
2.2 KiB
Go
67 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/kami/maven/internal/llm"
|
|
"github.com/kami/maven/internal/router"
|
|
"github.com/kami/maven/internal/voice"
|
|
)
|
|
|
|
type mockCompleter struct{ out string; err error }
|
|
|
|
func (m mockCompleter) Complete(_ context.Context, _ llm.Req) (string, error) { return m.out, m.err }
|
|
|
|
func TestLLMReplierReturnsLLMReply(t *testing.T) {
|
|
r := newLLMReplier(mockCompleter{out: `{"response":"записала, кофе закончился","mood":"neutral"}`})
|
|
got := r.Reply(router.Decision{Intent: router.IntentNote, Slots: router.Slots{Text: "кофе закончился"}})
|
|
if got != "записала, кофе закончился" {
|
|
t.Errorf("got %q, want %q", got, "записала, кофе закончился")
|
|
}
|
|
}
|
|
|
|
func TestLLMReplierFallsBackToPlainText(t *testing.T) {
|
|
r := newLLMReplier(mockCompleter{out: "записала, кофе закончился"})
|
|
got := r.Reply(router.Decision{Intent: router.IntentNote, Slots: router.Slots{Text: "кофе закончился"}})
|
|
if got != "записала, кофе закончился" {
|
|
t.Errorf("got %q, want %q", got, "записала, кофе закончился")
|
|
}
|
|
}
|
|
|
|
func TestLLMReplierFallsBackToStubOnError(t *testing.T) {
|
|
r := newLLMReplier(mockCompleter{err: errTestLLMDown})
|
|
noteDec := router.Decision{Intent: router.IntentNote}
|
|
got := r.Reply(noteDec)
|
|
want := voice.NewStubReplier().Reply(noteDec)
|
|
if got != want {
|
|
t.Errorf("on llm error: got %q, want stub %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestLLMReplierFallsBackToStubOnEmpty(t *testing.T) {
|
|
r := newLLMReplier(mockCompleter{out: ""})
|
|
noteDec := router.Decision{Intent: router.IntentNote}
|
|
got := r.Reply(noteDec)
|
|
want := voice.NewStubReplier().Reply(noteDec)
|
|
if got != want {
|
|
t.Errorf("on empty llm: got %q, want stub %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestLLMReplierClarifyUsesStub(t *testing.T) {
|
|
r := newLLMReplier(mockCompleter{out: "я всё поняла"})
|
|
clarifyDec := router.Decision{Clarify: true}
|
|
got := r.Reply(clarifyDec)
|
|
want := voice.NewStubReplier().Reply(clarifyDec)
|
|
if got != want {
|
|
t.Errorf("on clarify: got %q, want stub %q", got, want)
|
|
}
|
|
}
|
|
|
|
var errTestLLMDown = errTest("llm down")
|
|
|
|
type errTest string
|
|
|
|
func (e errTest) Error() string { return string(e) }
|