ba33a677f8
Three defects in how the counted profile is read back. The counting unit was the key the LLM invented. There is no allowlist and no normalization behind it, so "я выпил воду" and "попил воды" landed as different keys, split one habit into two, and dropped both below the two-day threshold. Keys now go through an alias table in behavior_ru.json before they are counted. An unglossed key is quoted rather than recited as a verb, because "обычно ты выпил_воды около 09:00" is not a sentence. The typical time was a median of minutes since midnight, which is wrong for anything that straddles midnight. Bedtimes of 23:40, 23:50, 00:10 and 00:20 gave 12:00, on the one activity most likely to cross the boundary. It is now a circular median, and when the observations span more than half the clock she names the habit without a time instead of inventing one. The rest is wording. Profile.Since was computed and never spoken, so "обычно" was an unfalsifiable claim; the overall read-back now says over how many days of records it holds. The no-data weekday answer said "у меня пока нет ничего постоянного" about a question concerning him. And "quiet" was a bare prefix in the non-behavioural list, so any future self-fact key starting with those five letters would have been dropped. Found in review of #59.
56 lines
1.8 KiB
Go
56 lines
1.8 KiB
Go
package memory
|
|
|
|
import (
|
|
_ "embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// The Russian read-back vocabulary lives in behavior_ru.json, not in Go source.
|
|
//
|
|
// It is data, not logic: a weekday name and a verb gloss are wording choices
|
|
// that change independently of anything the counter does, and having them as
|
|
// literals scattered through behavior.go meant every phrasing tweak was a code
|
|
// diff. go:embed keeps the single-binary deploy — the JSON is compiled in, so
|
|
// there is no file to install alongside mavend and no way for the two to drift.
|
|
//
|
|
// Parsed once at init. A malformed file is a panic, deliberately: it can only
|
|
// happen if the embedded asset is broken at build time, and a daemon that comes
|
|
// up with an empty vocabulary would answer with bare fact keys.
|
|
|
|
//go:embed behavior_ru.json
|
|
var behaviorRUJSON []byte
|
|
|
|
type behaviorRU struct {
|
|
Weekdays []string `json:"weekdays"`
|
|
Activities map[string]string `json:"activities"`
|
|
KeyAliases map[string][]string `json:"key_aliases"`
|
|
}
|
|
|
|
var (
|
|
// weekdayRU — dative plural, indexed by time.Weekday.
|
|
weekdayRU []string
|
|
// activityRU — canonical fact key to second-person-singular verb phrase.
|
|
activityRU map[string]string
|
|
// canonicalKey — observed fact key to the canonical key it counts as.
|
|
canonicalKey map[string]string
|
|
)
|
|
|
|
func init() {
|
|
var v behaviorRU
|
|
if err := json.Unmarshal(behaviorRUJSON, &v); err != nil {
|
|
panic(fmt.Sprintf("memory: behavior_ru.json: %v", err))
|
|
}
|
|
if len(v.Weekdays) != 7 {
|
|
panic(fmt.Sprintf("memory: behavior_ru.json: want 7 weekdays, got %d", len(v.Weekdays)))
|
|
}
|
|
weekdayRU, activityRU = v.Weekdays, v.Activities
|
|
canonicalKey = make(map[string]string)
|
|
for canonical, variants := range v.KeyAliases {
|
|
canonicalKey[canonical] = canonical
|
|
for _, variant := range variants {
|
|
canonicalKey[variant] = canonical
|
|
}
|
|
}
|
|
}
|