package main import ( "context" "math" "os" "path/filepath" "strings" "testing" "time" "github.com/kami/maven/internal/router" ) func TestPersonalBoundaryLinearHeadSeparatesSemanticDirections(t *testing.T) { personal := [][]float32{{1, 0}, {0.9, 0.1}, {0.8, -0.1}} world := [][]float32{{-1, 0}, {-0.9, 0.1}, {-0.8, -0.1}} head, ok := trainPersonalBoundaryLinearHead(personal, world) if !ok { t.Fatal("valid training vectors were rejected") } b := personalBoundary{personal: personal, world: world, head: head, loaded: true} for _, tc := range []struct { vector []float32 personal bool }{ {vector: []float32{0.75, 0.2}, personal: true}, {vector: []float32{-0.75, 0.2}, personal: false}, } { personalScore, worldScore, ok := b.score(tc.vector) if !ok { t.Fatal("loaded boundary did not score") } if got := personalScore > worldScore; got != tc.personal { t.Fatalf("vector %v classified personal=%v (scores %.4f/%.4f), want %v", tc.vector, got, personalScore, worldScore, tc.personal) } if math.Abs(personalScore+worldScore-1) > 1e-12 { t.Fatalf("scores %.8f and %.8f are not complementary probabilities", personalScore, worldScore) } } } func TestPersonalBoundaryTrainingBalancesClasses(t *testing.T) { personal := [][]float32{{1, 0}, {0.8, 0.2}} world := [][]float32{{-1, 0}} oneWorld, ok := trainPersonalBoundaryLinearHead(personal, world) if !ok { t.Fatal("valid training vectors were rejected") } repeatedWorld := make([][]float32, 12) for i := range repeatedWorld { repeatedWorld[i] = world[0] } twelveWorld, ok := trainPersonalBoundaryLinearHead(personal, repeatedWorld) if !ok { t.Fatal("valid repeated training vectors were rejected") } if math.Abs(oneWorld.bias-twelveWorld.bias) > 1e-10 { t.Fatalf("duplicating one class moved bias from %.12f to %.12f", oneWorld.bias, twelveWorld.bias) } for i := range oneWorld.weights { if math.Abs(oneWorld.weights[i]-twelveWorld.weights[i]) > 1e-10 { t.Fatalf("duplicating one class moved weight %d from %.12f to %.12f", i, oneWorld.weights[i], twelveWorld.weights[i]) } } } func TestPersonalBoundaryTrainingRejectsMixedDimensions(t *testing.T) { if _, ok := trainPersonalBoundaryLinearHead( [][]float32{{1, 0}}, [][]float32{{-1, 0, 0}}, ); ok { t.Fatal("mixed embedding dimensions were accepted") } } // The corpus is grouped by sentence shape in personalboundary.go. This test // leaves one entire shape out of training at a time, then requires the linear // head to classify the omitted examples from the semantics learned from the // other shapes. It is ordinary deterministic CI: the small axis vectors stand // in for frozen embedding directions, so the test proves the training code // generalises across groups rather than memorising one row at a time. func TestPersonalBoundaryLinearHeadLeaveOneShapeOut(t *testing.T) { type example struct { vector []float32 shape int want bool } const shapeCount = 6 examples := make([]example, 0, shapeCount*4) for shape := 0; shape < shapeCount; shape++ { for variant := 0; variant < 2; variant++ { personal := make([]float32, shapeCount+1) world := make([]float32, shapeCount+1) personal[0], world[0] = 1, -1 personal[shape+1] = float32(0.1 * float64(variant+1)) world[shape+1] = float32(-0.1 * float64(variant+1)) examples = append(examples, example{vector: personal, shape: shape, want: true}, example{vector: world, shape: shape, want: false}, ) } } for omitted := 0; omitted < shapeCount; omitted++ { var personal, world [][]float32 for _, example := range examples { if example.shape == omitted { continue } if example.want { personal = append(personal, example.vector) } else { world = append(world, example.vector) } } head, ok := trainPersonalBoundaryLinearHead(personal, world) if !ok { t.Fatalf("fold %d rejected valid vectors", omitted) } for _, example := range examples { if example.shape != omitted { continue } if got := head.logit(example.vector) > 0; got != example.want { t.Errorf("fold %d classified %v as personal=%v, want %v", omitted, example.vector, got, example.want) } } } } func TestPersonalBoundaryTrainingCorpusIsIndependent(t *testing.T) { // The strict stratified fixture already enforces this for its 72 rows. The // historical regression table lives here, so protect it here too: a future // seed addition must not copy a regression sentence into training. training := make(map[string]bool, len(personalSeeds)+len(worldSeeds)) for _, seed := range append(append([]string(nil), personalSeeds...), worldSeeds...) { training[normalizePersonalBoundaryTraining(seed)] = true } for _, regression := range []string{ "что я говорил про бэкапы?", "что я сказал вчера про отпуск", "я писал что-нибудь про сервер", "я упоминал про конференцию?", "что я отмечал по поводу переезда", "я рассказывал тебе про новую работу?", "во сколько у меня встреча", "когда мой следующий отпуск", "what did i say about backups", "did i tell you about the doctor", "как я говорил, почему небо синее", "как уже я говорил, какая столица франции", "почему трава зелёная", "столица франции", "как мне сварить борщ", "что мне посмотреть вечером", "я хочу узнать про рим", "кто такой гагарин", "how do i boil an egg", "во сколько закат сегодня", "когда сегодня заканчивается концерт", "во сколько завтра открывается аптека", "какой сегодня праздник", "что интересного произошло сегодня в мире", "кто выиграл вчера матч", "расскажи про эверест", "расскажи про войну 1812 года", "объясни что такое инфляция", "я рассказывал тебе про байкал?", } { if training[normalizePersonalBoundaryTraining(regression)] { t.Errorf("regression utterance leaked into training: %q", regression) } } } func TestPersonalBoundaryFrozenHeadDecodes(t *testing.T) { head, ok := frozenPersonalBoundaryHead() if !ok { t.Fatal("frozen head did not decode") } if len(head.weights) != 384 { t.Fatalf("frozen head has %d weights, want 384", len(head.weights)) } } func TestPersonalBoundaryHashFloorFitsAndScores(t *testing.T) { b := &personalBoundary{} embedder := router.NewHashEmbedder(1024) query, err := router.EmbedQuery(context.Background(), embedder, "когда моя встреча") if err != nil { t.Fatal(err) } b.load(context.Background(), embedder) if _, _, ok := b.score(query); !ok { t.Fatal("hash-floor boundary declined to score") } if len(b.head.weights) != 1024 { t.Fatalf("hash-floor boundary has %d weights, want 1024", len(b.head.weights)) } } // BenchmarkPersonalBoundaryHashFloorFitAndScore keeps startup cost measurable // without making ambient CI load a correctness condition. In particular, // -race and coverage instrumentation both multiply the cost of this numeric // training loop; the functional test above is the deterministic gate. func BenchmarkPersonalBoundaryHashFloorFitAndScore(b *testing.B) { embedder := router.NewHashEmbedder(1024) query, err := router.EmbedQuery(context.Background(), embedder, "когда моя встреча") if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { boundary := &personalBoundary{} boundary.load(context.Background(), embedder) if _, _, ok := boundary.score(query); !ok { b.Fatal("hash-floor boundary declined to score") } } } func normalizePersonalBoundaryTraining(value string) string { return strings.Join(strings.Fields(strings.ToLower(value)), " ") } // A handler with no embedder never loads the seeds, so the boundary falls back // to the possession markers. That is the offline floor and it must keep working // — an embedder that fails to load must not open the boundary. func TestBoundaryFallsBackToMarkersWithNoEmbedder(t *testing.T) { h := personalHandler() if !h.isPersonalTurn(context.Background(), &queryTurn{ dec: router.Decision{Utterance: "во сколько у меня встреча"}, }) { t.Error("no embedder: a possession question must still be personal") } if h.isPersonalTurn(context.Background(), &queryTurn{ dec: router.Decision{Utterance: "почему небо синее"}, }) { t.Error("no embedder: a world question must still pass") } } // TestONNXPersonalBoundary — the number that matters, scored against the // embedder homesrv actually runs. Opt-in via MAVEN_ONNX_LIB, exactly like // TestONNXRecall in internal/memory/recalleval. // // Every case here is held out: none of these strings is a seed. The #495 // regression is the first row — "что я говорил про бэкапы?" reached SearXNG and // was answered from a Habr article, and no possession word appears in it. func TestONNXPersonalBoundary(t *testing.T) { lib := os.Getenv("MAVEN_ONNX_LIB") if lib == "" { t.Skip("MAVEN_ONNX_LIB unset — see AGENTS.md § Embedder model for intent routing") } dir := filepath.Join("../..", "models/embedder/multilingual-e5-small") emb, err := router.NewONNXEmbedder(filepath.Join(dir, "model_quantized.onnx"), filepath.Join(dir, "tokenizer.json"), lib) if err != nil { t.Skipf("onnx embedder unavailable: %v", err) } defer emb.Close() cases := []struct { utterance string personal bool }{ {"что я говорил про бэкапы?", true}, {"что я сказал вчера про отпуск", true}, {"я писал что-нибудь про сервер", true}, {"я упоминал про конференцию?", true}, {"что я отмечал по поводу переезда", true}, {"я рассказывал тебе про новую работу?", true}, {"во сколько у меня встреча", true}, {"когда мой следующий отпуск", true}, {"what did i say about backups", true}, {"did i tell you about the doctor", true}, {"как я говорил, почему небо синее", false}, {"как уже я говорил, какая столица франции", false}, {"почему трава зелёная", false}, {"столица франции", false}, {"как мне сварить борщ", false}, {"что мне посмотреть вечером", false}, {"я хочу узнать про рим", false}, {"кто такой гагарин", false}, {"how do i boil an egg", false}, // Asking when a public thing happens (Vikunja #553). "во сколько закат // сегодня" was answered "не знаю — не нашла у тебя такой записи", // because the frame lived only on the personal side. The pair above it // is the control: "во сколько у меня встреча" is the same frame about // something that IS his, and it has to stay personal. {"во сколько закат сегодня", false}, {"когда сегодня заканчивается концерт", false}, {"во сколько завтра открывается аптека", false}, // The "какой сегодня X" frame. These clear the weather topic after the // V-553 seeds and were then refused here, which is the same defect one // source further down the chain. {"какой сегодня праздник", false}, {"что интересного произошло сегодня в мире", false}, {"кто выиграл вчера матч", false}, // The narrative shape, held out from the seeds above (Vikunja #554). // The control is the row after them: the same verb about his own words // is still his. {"расскажи про эверест", false}, {"расскажи про войну 1812 года", false}, {"объясни что такое инфляция", false}, {"я рассказывал тебе про байкал?", true}, } h := &reactiveHandler{recall: recallWiring{embedder: emb}} ctx := context.Background() wrong := 0 for _, c := range cases { vec, err := router.EmbedQuery(ctx, emb, c.utterance) if err != nil { t.Fatalf("embed %q: %v", c.utterance, err) } turn := &queryTurn{dec: router.Decision{Utterance: c.utterance}, vec: vec} got := h.isPersonalTurn(ctx, turn) p, w, ok := h.recall.boundary.score(vec) if !ok { t.Fatal("seeds did not load with a working embedder") } if got != c.personal { wrong++ t.Errorf("%q: personal=%v want %v (personal %.4f world %.4f)", c.utterance, got, c.personal, p, w) } t.Logf("personal=%-5v personal %.4f world %.4f delta %+.4f %s", got, p, w, p-w, c.utterance) } t.Logf("personal boundary: %d/%d held-out utterances correct", len(cases)-wrong, len(cases)) } func TestONNXPersonalBoundaryFourFold(t *testing.T) { lib := os.Getenv("MAVEN_ONNX_LIB") if lib == "" { t.Skip("MAVEN_ONNX_LIB unset — see AGENTS.md § Embedder model for intent routing") } dir := filepath.Join("../..", "models/embedder/multilingual-e5-small") emb, err := router.NewONNXEmbedder(filepath.Join(dir, "model_quantized.onnx"), filepath.Join(dir, "tokenizer.json"), lib) if err != nil { t.Skipf("onnx embedder unavailable: %v", err) } defer emb.Close() ctx := context.Background() embedAll := func(values []string) [][]float32 { vectors := make([][]float32, len(values)) for i, value := range values { vector, err := router.EmbedQuery(ctx, emb, value) if err != nil { t.Fatalf("embed %q: %v", value, err) } vectors[i] = vector } return vectors } personalVectors := embedAll(personalSeeds) worldVectors := embedAll(worldSeeds) type group struct { name string personalStart, personalCount int worldStart, worldCount int } groups := []group{ {name: "remembered_speech", personalStart: 8, personalCount: 8, worldStart: 20, worldCount: 8}, {name: "possession", personalStart: 16, personalCount: 10, worldStart: 28, worldCount: 12}, {name: "narrative", personalStart: 26, personalCount: 8, worldStart: 40, worldCount: 8}, {name: "first_person_preamble", personalStart: 34, personalCount: 8, worldStart: 48, worldCount: 8}, {name: "advice_current_info", personalStart: 42, personalCount: 8, worldStart: 56, worldCount: 8}, {name: "public_proper_nouns", personalStart: 50, personalCount: 10, worldStart: 64, worldCount: 8}, } const foldCount = 4 aggregateCorrect, aggregateTotal := 0, 0 for omittedFold := 0; omittedFold < foldCount; omittedFold++ { trainingPersonal := append([][]float32(nil), personalVectors[:8]...) trainingWorld := append([][]float32(nil), worldVectors[:20]...) var heldPersonal, heldWorld [][]float32 partition := func(vectors [][]float32, start, count int, training, held *[][]float32) { for relative, vector := range vectors[start : start+count] { if relative%foldCount == omittedFold { *held = append(*held, vector) } else { *training = append(*training, vector) } } } for _, group := range groups { partition(personalVectors, group.personalStart, group.personalCount, &trainingPersonal, &heldPersonal) partition(worldVectors, group.worldStart, group.worldCount, &trainingWorld, &heldWorld) } head, ok := trainPersonalBoundaryLinearHead( trainingPersonal, trainingWorld, ) if !ok { t.Fatalf("fold %d: valid training fold rejected", omittedFold) } correct, total := 0, 0 for _, vector := range heldPersonal { total++ if head.logit(vector) > 0 { correct++ } } for _, vector := range heldWorld { total++ if head.logit(vector) <= 0 { correct++ } } t.Logf("fold %d: %d/%d held-out training examples", omittedFold+1, correct, total) aggregateCorrect += correct aggregateTotal += total } t.Logf("four-fold aggregate: %d/%d", aggregateCorrect, aggregateTotal) if aggregateCorrect < 99 { t.Errorf("four-fold aggregate %d/%d, want at least 99/104", aggregateCorrect, aggregateTotal) } } func TestONNXPersonalBoundarySemanticGroupHoldout(t *testing.T) { lib := os.Getenv("MAVEN_ONNX_LIB") if lib == "" { t.Skip("MAVEN_ONNX_LIB unset — see AGENTS.md § Embedder model for intent routing") } dir := filepath.Join("../..", "models/embedder/multilingual-e5-small") emb, err := router.NewONNXEmbedder(filepath.Join(dir, "model_quantized.onnx"), filepath.Join(dir, "tokenizer.json"), lib) if err != nil { t.Skipf("onnx embedder unavailable: %v", err) } defer emb.Close() ctx := context.Background() embedAll := func(values []string) [][]float32 { vectors := make([][]float32, len(values)) for i, value := range values { vector, err := router.EmbedQuery(ctx, emb, value) if err != nil { t.Fatalf("embed %q: %v", value, err) } vectors[i] = vector } return vectors } personalVectors := embedAll(personalSeeds) worldVectors := embedAll(worldSeeds) type group struct { name string personalStart, personalCount int worldStart, worldCount int } groups := []group{ {name: "remembered_speech", personalStart: 8, personalCount: 8, worldStart: 20, worldCount: 8}, {name: "possession", personalStart: 16, personalCount: 10, worldStart: 28, worldCount: 12}, {name: "narrative", personalStart: 26, personalCount: 8, worldStart: 40, worldCount: 8}, {name: "first_person_preamble", personalStart: 34, personalCount: 8, worldStart: 48, worldCount: 8}, {name: "advice_current_info", personalStart: 42, personalCount: 8, worldStart: 56, worldCount: 8}, {name: "public_proper_nouns", personalStart: 50, personalCount: 10, worldStart: 64, worldCount: 8}, } aggregateCorrect, aggregateTotal := 0, 0 for _, omitted := range groups { excluding := func(vectors [][]float32, start, count int) [][]float32 { result := make([][]float32, 0, len(vectors)-count) result = append(result, vectors[:start]...) return append(result, vectors[start+count:]...) } head, ok := trainPersonalBoundaryLinearHead( excluding(personalVectors, omitted.personalStart, omitted.personalCount), excluding(worldVectors, omitted.worldStart, omitted.worldCount), ) if !ok { t.Fatalf("%s: valid training fold rejected", omitted.name) } correct, total := 0, 0 for _, vector := range personalVectors[omitted.personalStart : omitted.personalStart+omitted.personalCount] { total++ if head.logit(vector) > 0 { correct++ } } for _, vector := range worldVectors[omitted.worldStart : omitted.worldStart+omitted.worldCount] { total++ if head.logit(vector) <= 0 { correct++ } } t.Logf("leave %-21s out: %d/%d", omitted.name, correct, total) aggregateCorrect += correct aggregateTotal += total // Whole-shape holdout is an honest diagnostic, not a 100% release gate: // some shapes (notably private-vs-general possession) define a distinct // semantic ambiguity. The separately authored challenge set remains the // strict generalisation gate. } if aggregateCorrect < 92 { t.Errorf("whole-shape aggregate %d/%d, want at least 92/104", aggregateCorrect, aggregateTotal) } } // This challenge set was originally authored after the six-shape training // corpus and the 72-case matrix were frozen. Its sole miss then informed the // regularisation comparison, so it is now a strict regression gate rather than // independent evidence. It remains outside the production corpus. func TestONNXPersonalBoundaryChallenge(t *testing.T) { lib := os.Getenv("MAVEN_ONNX_LIB") if lib == "" { t.Skip("MAVEN_ONNX_LIB unset — see AGENTS.md § Embedder model for intent routing") } dir := filepath.Join("../..", "models/embedder/multilingual-e5-small") emb, err := router.NewONNXEmbedder(filepath.Join(dir, "model_quantized.onnx"), filepath.Join(dir, "tokenizer.json"), lib) if err != nil { t.Skipf("onnx embedder unavailable: %v", err) } defer emb.Close() cases := []struct { utterance string personal bool }{ {"какой пароль приложения я задал для почтового клиента?", true}, {"на каком порту я решил поднять тестовый сервис?", true}, {"какую причину я указал, когда отменил бронь?", true}, {"где в гараже я сложил зимние шины?", true}, {"какой сериал я бросил после второго сезона?", true}, {"о чём мы договорились с Олегом на прошлой неделе?", true}, {"почему мой монитор мерцает при частоте 144 герца?", false}, {"подойдёт ли кабель Thunderbolt 3 к разъёму USB4?", false}, {"как вывести запах дыма из моей куртки?", false}, {"что означают кольца на флаге Олимпиады?", false}, {"почему после дождя на асфальте видна радуга?", false}, {"какой формат файлов поддерживает Kindle Paperwhite?", false}, {"which SSH key did I install on the build server?", true}, {"what spending limit did I set for the travel card?", true}, {"where did I store the spare apartment fob?", true}, {"which objection did I raise during the design review?", true}, {"what route did I plan for the Sunday hike?", true}, {"when did I promise Maya I would send the draft?", true}, {"why does my mechanical keyboard sometimes chatter?", false}, {"can my USB-C charger safely power a Steam Deck?", false}, {"how do I stop condensation inside my camera lens?", false}, {"what caused the Tacoma Narrows Bridge to collapse?", false}, {"why are some auroras red instead of green?", false}, {"which codecs does the current Firefox release support?", false}, } b := &personalBoundary{} b.load(context.Background(), emb) correct := 0 minimumMargin := math.Inf(1) for _, testCase := range cases { vector, err := router.EmbedQuery(context.Background(), emb, testCase.utterance) if err != nil { t.Fatalf("embed %q: %v", testCase.utterance, err) } personal, world, ok := b.score(vector) if !ok { t.Fatal("loaded boundary declined to score") } got := personal > world signedMargin := personal - world if !testCase.personal { signedMargin = -signedMargin } if signedMargin < minimumMargin { minimumMargin = signedMargin } if got == testCase.personal { correct++ } else { t.Logf("miss %q: personal=%v want %v (%.4f/%.4f)", testCase.utterance, got, testCase.personal, personal, world) } } t.Logf("regularisation challenge: %d/%d, minimum signed margin %+.4f", correct, len(cases), minimumMargin) if correct != len(cases) { t.Errorf("regularisation challenge %d/%d, want every case correct", correct, len(cases)) } } // TestONNXPersonalBoundaryPostRetuneChallenge was authored only after the L2 // coefficient and frozen head had been selected using corpus cross-validation. // It deliberately returns to private configuration, commitments and stored // choices with new objects, and contrasts them with public technical facts, // compatibility and maintenance. No result from this table may be used to // tune the current head; a miss is evidence for the next independently // evaluated model revision. func TestONNXPersonalBoundaryPostRetuneChallenge(t *testing.T) { lib := os.Getenv("MAVEN_ONNX_LIB") if lib == "" { t.Skip("MAVEN_ONNX_LIB unset — see AGENTS.md § Embedder model for intent routing") } dir := filepath.Join("../..", "models/embedder/multilingual-e5-small") emb, err := router.NewONNXEmbedder(filepath.Join(dir, "model_quantized.onnx"), filepath.Join(dir, "tokenizer.json"), lib) if err != nil { t.Skipf("onnx embedder unavailable: %v", err) } defer emb.Close() cases := []struct { utterance string personal bool }{ {"какое имя я выбрал для гостевой сети Wi-Fi?", true}, {"на какой день я перенёс техосмотр машины?", true}, {"какую сумму мы с Мариной согласовали за ремонт кухни?", true}, {"где я сохранил резервные коды от GitHub?", true}, {"какой из макетов визитки я одобрил?", true}, {"что я решил делать со страховкой перед поездкой?", true}, {"какой диапазон частот использует Wi-Fi 6E?", false}, {"почему OLED-экраны со временем выгорают?", false}, {"можно ли подключить монитор DisplayPort к Thunderbolt 4?", false}, {"чем безопасно чистить замшевые ботинки?", false}, {"когда появился протокол WebSocket?", false}, {"почему соль ускоряет таяние льда?", false}, {"which hostname did I assign to the home NAS?", true}, {"what date did I move the annual checkup to?", true}, {"where did I save the recovery phrase for the hardware wallet?", true}, {"which catering quote did we accept for the party?", true}, {"what did I decide about renewing the domain?", true}, {"which paint sample did I approve for the hallway?", true}, {"does Wi-Fi 7 work with older wireless clients?", false}, {"why can an SSD slow down when it is nearly full?", false}, {"how should suede shoes be cleaned?", false}, {"when was the WebSocket protocol standardized?", false}, {"what does a hardware-wallet recovery phrase do?", false}, {"why does road salt damage concrete?", false}, } b := &personalBoundary{} b.load(context.Background(), emb) correct := 0 minimumMargin := math.Inf(1) for _, testCase := range cases { vector, err := router.EmbedQuery(context.Background(), emb, testCase.utterance) if err != nil { t.Fatalf("embed %q: %v", testCase.utterance, err) } personal, world, ok := b.score(vector) if !ok { t.Fatal("loaded boundary declined to score") } got := personal > world signedMargin := personal - world if !testCase.personal { signedMargin = -signedMargin } if signedMargin < minimumMargin { minimumMargin = signedMargin } if got == testCase.personal { correct++ } else { t.Logf("miss %q: personal=%v want %v (%.4f/%.4f)", testCase.utterance, got, testCase.personal, personal, world) } } t.Logf("post-retune challenge: %d/%d, minimum signed margin %+.4f", correct, len(cases), minimumMargin) if correct != len(cases) { t.Errorf("post-retune challenge %d/%d, want every case correct", correct, len(cases)) } } func TestONNXPersonalBoundaryLatency(t *testing.T) { lib := os.Getenv("MAVEN_ONNX_LIB") if lib == "" { t.Skip("MAVEN_ONNX_LIB unset — see AGENTS.md § Embedder model for intent routing") } dir := filepath.Join("../..", "models/embedder/multilingual-e5-small") emb, err := router.NewONNXEmbedder(filepath.Join(dir, "model_quantized.onnx"), filepath.Join(dir, "tokenizer.json"), lib) if err != nil { t.Skipf("onnx embedder unavailable: %v", err) } defer emb.Close() ctx := context.Background() query, err := router.EmbedQuery(ctx, emb, "что я решил насчёт переезда?") if err != nil { t.Fatal(err) } b := &personalBoundary{} coldStart := time.Now() b.load(ctx, emb) if _, _, ok := b.score(query); !ok { t.Fatal("loaded boundary declined to score") } cold := time.Since(coldStart) const iterations = 100000 steadyStart := time.Now() for i := 0; i < iterations; i++ { if _, _, ok := b.score(query); !ok { t.Fatal("loaded boundary declined to score") } } steady := time.Since(steadyStart) / iterations t.Logf("boundary cold load+train+score: %s; steady score: %s/op", cold, steady) // This is a user-visible first-turn path. Keep a generous ceiling to avoid // noisy CI while making an accidental per-turn training/load regression // unmistakable. if cold > 5*time.Second { t.Errorf("cold boundary load %s exceeds 5s local usability ceiling", cold) } if steady > 100*time.Microsecond { t.Errorf("steady boundary score %s exceeds 100µs ceiling", steady) } } func TestONNXPersonalBoundaryFrozenHeadMatchesCorpusFit(t *testing.T) { lib := os.Getenv("MAVEN_ONNX_LIB") if lib == "" { t.Skip("MAVEN_ONNX_LIB unset — see AGENTS.md § Embedder model for intent routing") } dir := filepath.Join("../..", "models/embedder/multilingual-e5-small") emb, err := router.NewONNXEmbedder(filepath.Join(dir, "model_quantized.onnx"), filepath.Join(dir, "tokenizer.json"), lib) if err != nil { t.Skipf("onnx embedder unavailable: %v", err) } defer emb.Close() ctx := context.Background() embedAll := func(values []string) [][]float32 { vectors := make([][]float32, len(values)) for i, value := range values { vector, err := router.EmbedQuery(ctx, emb, value) if err != nil { t.Fatalf("embed %q: %v", value, err) } vectors[i] = vector } return vectors } fitted, ok := trainPersonalBoundaryLinearHead(embedAll(personalSeeds), embedAll(worldSeeds)) if !ok { t.Fatal("corpus fit failed") } frozen, ok := frozenPersonalBoundaryHead() if !ok { t.Fatal("frozen head did not decode") } if math.Abs(fitted.bias-frozen.bias) > 1e-9 { t.Fatalf("frozen bias %.12f != fitted %.12f", frozen.bias, fitted.bias) } for i := range fitted.weights { if math.Abs(fitted.weights[i]-frozen.weights[i]) > 5e-7 { t.Fatalf("frozen weight %d %.12f != fitted %.12f", i, frozen.weights[i], fitted.weights[i]) } } }