fe489dff6d
attentionq.go, repair.go and internal/router/complaint.go carry the last
hand-written Russian patterns of the V-522 sweep, and they live on task/467.
internal/lexicon, internal/morph and cmd/mavend/topics.go live here. One of
the two had to move.
Four conflicts, and one of them is a real collision rather than a mechanical
one. Both branches wrote the narrative stage 0 rule. This side had
NarrativeQueryGrammars, plural, with the rest-of-day rule beside it and the
verb alternation built from the lexicon; task/467 had NarrativeQueryGrammar,
singular, which extracts the topic into Slots.Text, refuses a bare "расскажи",
and excludes the shapes that are chat ("расскажи о себе", "историю на ночь").
Resolved by keeping this side's container and this side's lexicon-built
pattern, and taking every behaviour only the other side had: the topic slot,
the empty-topic refusal, chatNarrativeTopics, and its wiring position after
TaskCaptureGrammar so "запиши" still beats "расскажи".
The rest: queryFeeds keeps task/467's conditional claim (V-474 supersedes the
unconditional one), rank.go keeps Spoken and drops pluralTasksRU because
say.CountWord is the one copy of Russian count agreement, and vendor/ was
re-vendored — the merged modules.txt claimed replaces for nexus and praxis
that neither go.mod has.
Routing fixture 58/82, unchanged from both sides.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
45 lines
1.5 KiB
Go
45 lines
1.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/kami/maven/internal/phraser"
|
|
"github.com/kami/maven/internal/router"
|
|
"github.com/kami/maven/internal/voice"
|
|
)
|
|
|
|
// llmReplier is the daemon-side wiring around phraser.Replier: it owns the
|
|
// deterministic floor, and nothing else. The phrasing itself, the prompt and the
|
|
// output parsing live in internal/phraser so the eval can score them (#396).
|
|
type llmReplier struct {
|
|
p *phraser.Replier
|
|
stub *voice.StubReplier
|
|
}
|
|
|
|
func newLLMReplier(c phraser.Completer, block func() string) *llmReplier {
|
|
return &llmReplier{p: phraser.NewReplier(c, block), stub: voice.NewStubReplier()}
|
|
}
|
|
|
|
// Reply never fails: a clarify, a model error and an unusable generation all
|
|
// answer from the stub, which is what keeps a turn from breaking on the model.
|
|
func (r *llmReplier) Reply(d router.Decision) string {
|
|
if d.Clarify {
|
|
// The deck, not the stub's single sentence: a clarify she cannot turn
|
|
// into a question is the line he hears most often when she misses him,
|
|
// and it used to be the same words every time (Vikunja #457). Still no
|
|
// model call — this text has to be right every time, and it is not worth
|
|
// a generation to say something this small.
|
|
return clarifyMissedLine(d)
|
|
}
|
|
out, err := r.p.PhraseReply(context.Background(), d)
|
|
if err != nil || out == "" {
|
|
return r.stub.Reply(d)
|
|
}
|
|
// The persona checks, on the live path (personaguard.go). A reply that
|
|
// leaks reasoning or calls him "вы" is worse than a flat one.
|
|
if _, ok := guardSpoken("reply", out); !ok {
|
|
return r.stub.Reply(d)
|
|
}
|
|
return out
|
|
}
|