a Russian act reaches a tool, and the seeds stop disagreeing (V-633)
Three tangled defects, fixed together because each one hid the others. DefaultActMatcher matched an exact English prefix and internal/tool.Matcher delegated straight to it, so no Russian utterance could reach a tool: 55 of the 69 lines in models/seeds/act.txt routed to IntentAct and fell to proposeGap. Tools now carry spoken aliases from deploy/mavend.json, matched as exact leading tokens, longest phrase first. Config data, not a stem pattern in code. The comment claiming "the production matcher is fuzzy" was false and is gone. Seven lines were exact duplicates inside models/seeds/query.txt, each one a second identical vector double-weighting its region. "как дела у сервера" carried both a query and a system label. It leaves system.txt, because replySystem's stats arm answers "системная статистика пока не подключена." and always did. The mode inventory records that shape as act.tool.hoststats rather than a system mode. Fixture unchanged at 69/91, and it cannot see any of this: no host-stat case and no Russian act in it. TestActMatcherAliases is the coverage. docs/evals/2026-08-06-russian-acts-reach-tools.md has the numbers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117tgnmbgZpHVV3XSNw8Qua
This commit is contained in:
+15
-1
@@ -176,7 +176,7 @@ func wireVoice(cfg *config.Config, coreAPI ipc.CoreAPI, phr phraser.Phraser, mem
|
||||
// The LAN scanner (Vikunja #257): a read, bounded to the configured
|
||||
// subnets and rate-limited. Off unless the `netscan` block is enabled.
|
||||
w.netscan = wireNetScan(cfg, coreAPI)
|
||||
matcher := tool.NewMatcher(coreAPI)
|
||||
matcher := tool.NewMatcher(coreAPI).WithAliases(toolAliases(cfg.Voice.Tools))
|
||||
|
||||
// ----- weather provider (Open-Meteo when configured, Stub otherwise) -----
|
||||
var weatherProvider weather.Provider
|
||||
@@ -515,6 +515,20 @@ func loadSeedFile(c *router.Classifier, intent router.Intent) (int, error) {
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// toolAliases collects the spoken phrases per tool name. Without them the act
|
||||
// matcher only ever matched the English tool name, so no Russian utterance could
|
||||
// reach a tool and every homelab act fell to proposeGap (V-633).
|
||||
func toolAliases(tools []config.ToolConfig) map[string][]string {
|
||||
out := make(map[string][]string, len(tools))
|
||||
for _, tc := range tools {
|
||||
if tc.Name == "" || len(tc.Aliases) == 0 {
|
||||
continue
|
||||
}
|
||||
out[tc.Name] = tc.Aliases
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// seedTools upserts the config-declared tools into the store as enabled. Editing
|
||||
// mavend.json is a human act, so a config tool is enabled by definition; this
|
||||
// makes the declarative config the reproducible bootstrap while the store stays
|
||||
|
||||
Reference in New Issue
Block a user