82 lines
2.8 KiB
Go
82 lines
2.8 KiB
Go
package semantic
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/kami/maven/internal/router"
|
|
)
|
|
|
|
// ExperimentActVerbs is the act allowlist shared by the experiment's legacy
|
|
// baseline (the eval fixture and the slice-22 harness), the routed-heads
|
|
// harness, and the corpus fast-path derivation. One non-test list so the
|
|
// corpus derivation can never drift from what the measurement router accepts.
|
|
func ExperimentActVerbs() []string {
|
|
return []string{
|
|
"перезапусти", "перезагрузи", "выключи", "включи", "останови", "запусти",
|
|
"закрой", "открой", "сделай", "поставь",
|
|
"restart", "reboot", "stop", "start", "turn off", "turn on", "open", "close",
|
|
}
|
|
}
|
|
|
|
func experimentActMatcher() router.ActMatcher {
|
|
return router.DefaultActMatcher{Fns: ExperimentActVerbs()}
|
|
}
|
|
|
|
// FastPathOutcome is the derived fast-path classification for one surface.
|
|
// Matched mirrors TryFastPath; Grammar names the winning stage-0 grammar for
|
|
// attribution, or the empty string when nothing resolved.
|
|
type FastPathOutcome struct {
|
|
Matched bool
|
|
Grammar string
|
|
}
|
|
|
|
var (
|
|
fastPathOnce sync.Once
|
|
fastPathRouter *router.Router
|
|
fastPathGrammars []router.Grammar
|
|
)
|
|
|
|
// DeriveFastPath mirrors the production fast path for a surface: it runs
|
|
// TryFastPath over the daemon's ordered stage-0 grammar list with the
|
|
// experiment's act allowlist — the exact router the legacy baseline measures —
|
|
// and reports whether a grammar resolved the utterance. This is the
|
|
// authoritative derivation for corpus fast_path_resolved metadata. The
|
|
// classifier, threshold and LLM never run on the fast path, so they are not
|
|
// wired here.
|
|
func DeriveFastPath(text string) FastPathOutcome {
|
|
fastPathOnce.Do(func() {
|
|
acts := experimentActMatcher()
|
|
fastPathGrammars = router.StageZeroGrammars(acts)
|
|
fastPathRouter = router.New(router.Config{
|
|
Grammars: fastPathGrammars,
|
|
Extractor: router.Extractor{
|
|
Time: router.StubDateTimeParser{},
|
|
Acts: acts,
|
|
Facts: router.DefaultFactParser{},
|
|
},
|
|
})
|
|
})
|
|
res, err := fastPathRouter.TryFastPath(context.Background(), router.NormalizedInput{
|
|
Text: text,
|
|
MatchText: router.NormalizeMatchText(text),
|
|
}, time.Now())
|
|
if err != nil || !res.Matched {
|
|
return FastPathOutcome{}
|
|
}
|
|
// Attribute the winner by replaying TryFastPath's ordered first-accept
|
|
// walk, including the wake-stripped alternate. A matcher that matched the
|
|
// shape but declined the content falls through, exactly as the router does.
|
|
stripped, hadWake := router.StripWakeToken(text)
|
|
for _, g := range fastPathGrammars {
|
|
_, matched, ok := g.Evaluate(text)
|
|
if !matched && hadWake {
|
|
_, matched, ok = g.Evaluate(stripped)
|
|
}
|
|
if matched && ok {
|
|
return FastPathOutcome{Matched: true, Grammar: g.Name}
|
|
}
|
|
}
|
|
return FastPathOutcome{Matched: true}
|
|
} |