diff --git a/cmd/mavend/actions.go b/cmd/mavend/actions.go index d262111..e576e20 100644 --- a/cmd/mavend/actions.go +++ b/cmd/mavend/actions.go @@ -40,6 +40,7 @@ import ( "context" "log" + "github.com/kami/maven/internal/phraser" "github.com/kami/maven/internal/router" ) @@ -65,7 +66,7 @@ func (h *reactiveHandler) actionChat(ctx context.Context, dec router.Decision) s log.Printf("voice: chat: %v", err) } if reply == "" { - return "поговорили." + return phraser.ChatFallback } return reply } diff --git a/internal/phraser/failure_test.go b/internal/phraser/failure_test.go index 38d8ba9..687bab8 100644 --- a/internal/phraser/failure_test.go +++ b/internal/phraser/failure_test.go @@ -9,7 +9,7 @@ import ( ) // A dead server must be distinguishable from bad phrasing. Both PhraseChat and -// PhraseQuery keep the turn alive with canned text — "поговорили.", "не знаю.", +// PhraseQuery keep the turn alive with canned text — ChatFallback, "не знаю.", // "вот что я нашла: …" — and every one of those is also a legitimate reply, so // the text alone cannot say which happened. The error is the only signal, and // before Vikunja #397 it was dropped: the talk scorer reported a full run with @@ -28,7 +28,7 @@ func TestPhrasingReportsTheFailureWithTheFallback(t *testing.T) { }{ {"chat", func() (string, error) { return p.PhraseChat(context.Background(), "как дела", nil) - }, "поговорили."}, + }, ChatFallback}, {"knowledge", func() (string, error) { return p.PhraseQuery(context.Background(), "кто написал войну и мир", nil) }, "не знаю."}, diff --git a/internal/phraser/llmphraser.go b/internal/phraser/llmphraser.go index 5fcc6b5..b019eac 100644 --- a/internal/phraser/llmphraser.go +++ b/internal/phraser/llmphraser.go @@ -483,7 +483,7 @@ func (p *LLMPhraser) PhraseQuery(ctx context.Context, utterance string, notes [] // PhraseChat uses the LLM to respond conversationally, building a multi-turn // message array from dialogue history + the current user utterance. On any LLM -// error it returns both "поговорили." and the error, on the same rule as +// error it returns both ChatFallback and the error, on the same rule as // PhraseQuery: the fallback keeps the turn alive, the error stays visible. func (p *LLMPhraser) PhraseChat(ctx context.Context, utterance string, history []dialogue.Turn) (string, error) { sys := chatSystemPrompt(p.cfg.ContextBlock) @@ -501,11 +501,11 @@ func (p *LLMPhraser) PhraseChat(ctx context.Context, utterance string, history [ resp, err := p.chatWithMessages(ctx, msgs, 768) if err != nil { - return "поговорили.", fmt.Errorf("phrase chat: %w", err) + return ChatFallback, fmt.Errorf("phrase chat: %w", err) } text, _, perr := parseResponseMood(resp) if perr != nil { - return "поговорили.", fmt.Errorf("phrase chat: %w", perr) + return ChatFallback, fmt.Errorf("phrase chat: %w", perr) } if text != "" { return text, nil diff --git a/internal/phraser/phraser.go b/internal/phraser/phraser.go index 79488d2..5918ac9 100644 --- a/internal/phraser/phraser.go +++ b/internal/phraser/phraser.go @@ -66,11 +66,17 @@ type Stub struct{} // NewStub builds the floor phraser. no config — the Stub is stateless. func NewStub() *Stub { return &Stub{} } +// ChatFallback — what she says on the chat path when the model gave her +// nothing to say. It replaced "поговорили.", which reads as a summary of a +// conversation that did not happen. Said out loud this one is an admission, +// which is what it is. +const ChatFallback = "даже не знаю, что сказать." + // PhraseChat returns a stub reply — the LLMPhraser replaces this with a // prompted response from the model. The history parameter is accepted but // ignored at the stub level (the production impl uses it for multi-turn). func (s *Stub) PhraseChat(_ context.Context, _ string, _ []dialogue.Turn) (string, error) { - return "поговорили.", nil + return ChatFallback, nil } // PhraseQuery returns a deterministic summary of the best matching notes.