a4abcdefa3
embedder.heads_path is empty by default and deploy/mavend.json sets it. A missing or broken weights file logs and leaves the heads nil, because refusing to start over a routing accelerator would trade a working box for a better one. TestONNXRoutingHeads is the same cascade TestONNXBaseline scores with one arm added, so the two are directly comparable. It also checks the Go tokenizer against the Python one, since the heads were trained through transformers and are read through a hand-written tokenizer: a mismatch shows up here as a score below what Python measured on the same weights, and nowhere else. That is how the reversed word pieces were found. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013ptwopxyo3Z2kwFckHkLvN
51 lines
1.8 KiB
Go
51 lines
1.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/kami/maven/internal/llm"
|
|
"github.com/kami/maven/internal/router"
|
|
)
|
|
|
|
// The chat path must answer when llama-server is down (Vikunja #45 step 4).
|
|
// Both halves of a turn call the model — the router and the replier — and each
|
|
// has its own floor: the cascade falls to the classifier, the replier falls to
|
|
// the stub. This wires a client at a closed port so both floors are exercised
|
|
// by a dial error rather than by a stubbed error value.
|
|
func TestChatAnswersWithNoLlamaServer(t *testing.T) {
|
|
h, _, _ := newClarifyHandler(t)
|
|
dead := llm.New("http://127.0.0.1:1", 500*time.Millisecond)
|
|
emb := router.NewHashEmbedder(1024)
|
|
h.recall.embedder = emb
|
|
h.router = buildRouter(emb, h.matcher, 0.55, pickLLMRouter(true, dead), nil)
|
|
h.replier = newLLMReplier(dead, nil)
|
|
|
|
ctx := withDialogueID(context.Background(), dialogueIDFor(sourceText, "web"))
|
|
for _, utt := range []string{
|
|
"привет",
|
|
"запиши что кофе закончился",
|
|
"что у меня сегодня",
|
|
} {
|
|
reply := h.handleText(ctx, "web", utt)
|
|
if reply == "" {
|
|
t.Errorf("%q answered with nothing; a dead model must degrade to the stub", utt)
|
|
}
|
|
}
|
|
}
|
|
|
|
// daemonAPI.Chat reports an error only when the voice path was never wired.
|
|
// A turn that reaches handleText always carries text, which is what keeps
|
|
// mavweb's /api/chat off its error branch when the model is down.
|
|
func TestChatAPIErrsOnlyWhenUnwired(t *testing.T) {
|
|
d := &daemonAPI{}
|
|
if _, err := d.Chat(context.Background(), "web", "привет"); err == nil {
|
|
t.Fatal("an unwired daemon must say so")
|
|
}
|
|
d.chatFn = func(context.Context, string, string) string { return "" }
|
|
if _, err := d.Chat(context.Background(), "web", "привет"); err != nil {
|
|
t.Fatalf("a wired daemon must not error: %v", err)
|
|
}
|
|
}
|