92d5fd580c
modelSeam builds an llm.Pair when a workstation is configured and hands it to the router and the replier. Both are the silent half of the degradation rule: the big model is only better there, and he is never told which model answered. No block, no probe, and the box behaves exactly as it did.
92 lines
2.9 KiB
Go
92 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/kami/maven/internal/config"
|
|
"github.com/kami/maven/internal/llm"
|
|
)
|
|
|
|
// No `workstation` block is the shipping deploy. The seam must then be the
|
|
// resident client itself, with nothing probing anything.
|
|
func TestModelSeamUnconfiguredIsResidentOnly(t *testing.T) {
|
|
resident := llm.New("http://127.0.0.1:1", time.Second)
|
|
hot, pair := modelSeam(&config.Config{}, resident)
|
|
if pair != nil {
|
|
t.Error("built a pair with no workstation configured")
|
|
}
|
|
if hot == nil {
|
|
t.Fatal("no seam at all, so the cascade would route with the classifier")
|
|
}
|
|
}
|
|
|
|
// A workstation with no resident model behind it has no floor, and a Pair with
|
|
// no floor is a configuration mistake rather than a degraded mode.
|
|
func TestModelSeamWithoutResidentIsNil(t *testing.T) {
|
|
cfg := &config.Config{Workstation: &config.WorkstationConfig{URL: "http://127.0.0.1:1"}}
|
|
cfg.Workstation.Health = strings.TrimRight(cfg.Workstation.URL, "/") + "/health"
|
|
hot, pair := modelSeam(cfg, nil)
|
|
if hot != nil || pair != nil {
|
|
t.Errorf("built a seam with no floor: hot=%v pair=%v", hot, pair)
|
|
}
|
|
}
|
|
|
|
// The configured case: the seam is the pair, and the pair notices a workstation
|
|
// that answers /health.
|
|
func TestModelSeamPrefersAnAnsweringWorkstation(t *testing.T) {
|
|
up := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer up.Close()
|
|
|
|
cfg := &config.Config{Workstation: &config.WorkstationConfig{
|
|
URL: up.URL,
|
|
Probe: config.Duration(10 * time.Millisecond),
|
|
}}
|
|
cfg.Workstation.Health = strings.TrimRight(cfg.Workstation.URL, "/") + "/health"
|
|
|
|
hot, pair := modelSeam(cfg, llm.New("http://127.0.0.1:1", time.Second))
|
|
if pair == nil || hot == nil {
|
|
t.Fatal("no pair built for a configured workstation")
|
|
}
|
|
defer pair.Stop()
|
|
|
|
deadline := time.Now().Add(2 * time.Second)
|
|
for !pair.Available() && time.Now().Before(deadline) {
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
if !pair.Available() {
|
|
t.Fatal("the pair never saw a workstation that answers /health")
|
|
}
|
|
}
|
|
|
|
// A card held by a CPT run answers 503, and that must read as unavailable
|
|
// rather than as an error a turn has to handle.
|
|
func TestModelSeamHeldCardIsUnavailable(t *testing.T) {
|
|
busy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "model not loaded", http.StatusServiceUnavailable)
|
|
}))
|
|
defer busy.Close()
|
|
|
|
cfg := &config.Config{Workstation: &config.WorkstationConfig{
|
|
URL: busy.URL,
|
|
Probe: config.Duration(10 * time.Millisecond),
|
|
}}
|
|
cfg.Workstation.Health = strings.TrimRight(cfg.Workstation.URL, "/") + "/health"
|
|
|
|
_, pair := modelSeam(cfg, llm.New("http://127.0.0.1:1", time.Second))
|
|
if pair == nil {
|
|
t.Fatal("no pair built for a configured workstation")
|
|
}
|
|
defer pair.Stop()
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
if pair.Available() {
|
|
t.Error("a 503 from the supervisor read as available")
|
|
}
|
|
}
|