harness: config layer + model routing v0 (tier map, no heuristics)
harness.toml loading via a stdlib-only TOML subset parser (tables, bare
keys, strings/ints/bools, multi-line arrays; anything richer fails loudly).
Secrets are refs only (env:/file:/literal:, bw: reserved) and are redacted
from every error path. Model routing v0: static class -> tier alias ->
concrete model map per the DESIGN model-selection layer; requests carry the
resolved concrete model and unknown classes fail hard so routing stays
auditable. Table-driven tests cover the parser, validation, key refs, and
routing decisions.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// Package models implements MODEL ROUTING v0: a static, config-only map from
|
||||
// task class -> tier alias -> concrete proxy model. There is deliberately no
|
||||
// heuristic code: the tier map in harness.toml is the whole contract, and an
|
||||
// unknown class is a hard error so routing stays auditable.
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Router resolves task classes to concrete model names.
|
||||
type Router struct {
|
||||
tiers map[string]string // tier alias -> concrete model
|
||||
classes map[string]string // task class -> tier alias
|
||||
defaultTier string
|
||||
}
|
||||
|
||||
// Decision records one routing outcome for the REPORT trail.
|
||||
type Decision struct {
|
||||
Class string
|
||||
Tier string
|
||||
Model string
|
||||
}
|
||||
|
||||
// NewRouter validates the config maps and returns a Router.
|
||||
func NewRouter(tiers, classes map[string]string, defaultTier string) (*Router, error) {
|
||||
if len(tiers) == 0 {
|
||||
return nil, fmt.Errorf("models: tier map is empty")
|
||||
}
|
||||
if _, ok := tiers[defaultTier]; !ok {
|
||||
return nil, fmt.Errorf("models: default tier %q has no model entry", defaultTier)
|
||||
}
|
||||
for class, tier := range classes {
|
||||
if _, ok := tiers[tier]; !ok {
|
||||
return nil, fmt.Errorf("models: class %q points at unknown tier %q", class, tier)
|
||||
}
|
||||
}
|
||||
return &Router{tiers: tiers, classes: classes, defaultTier: defaultTier}, nil
|
||||
}
|
||||
|
||||
// Resolve maps a task class to its tier and concrete model. An empty class
|
||||
// falls back to the default tier; an unknown class is an error.
|
||||
func (r *Router) Resolve(class string) (Decision, error) {
|
||||
tier := r.defaultTier
|
||||
if class != "" {
|
||||
t, ok := r.classes[class]
|
||||
if !ok {
|
||||
return Decision{}, fmt.Errorf("models: unknown task class %q (add it to [models.classes] in harness.toml)", class)
|
||||
}
|
||||
tier = t
|
||||
}
|
||||
model, ok := r.tiers[tier]
|
||||
if !ok {
|
||||
return Decision{}, fmt.Errorf("models: tier %q has no model entry", tier)
|
||||
}
|
||||
return Decision{Class: class, Tier: tier, Model: model}, nil
|
||||
}
|
||||
|
||||
// Tiers returns the sorted tier aliases known to the router.
|
||||
func (r *Router) Tiers() []string {
|
||||
out := make([]string, 0, len(r.tiers))
|
||||
for k := range r.tiers {
|
||||
out = append(out, k)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testRouter(t *testing.T) *Router {
|
||||
t.Helper()
|
||||
tiers := map[string]string{
|
||||
"mopac-study": "glm-4.7-flash",
|
||||
"mopac-code": "glm-5.2",
|
||||
"mopac-review": "glm-5-turbo",
|
||||
"mopac-primary": "glm-5.3",
|
||||
"mopac-vision": "glm-4.6v",
|
||||
}
|
||||
classes := map[string]string{
|
||||
"study": "mopac-study",
|
||||
"read": "mopac-study",
|
||||
"code": "mopac-code",
|
||||
"review": "mopac-review",
|
||||
"primary": "mopac-primary",
|
||||
}
|
||||
r, err := NewRouter(tiers, classes, "mopac-primary")
|
||||
if err != nil {
|
||||
t.Fatalf("NewRouter: %v", err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
cases := []struct {
|
||||
class string
|
||||
tier string
|
||||
model string
|
||||
}{
|
||||
{"study", "mopac-study", "glm-4.7-flash"},
|
||||
{"read", "mopac-study", "glm-4.7-flash"},
|
||||
{"code", "mopac-code", "glm-5.2"},
|
||||
{"review", "mopac-review", "glm-5-turbo"},
|
||||
{"primary", "mopac-primary", "glm-5.3"},
|
||||
{"", "mopac-primary", "glm-5.3"}, // no class -> default tier
|
||||
}
|
||||
r := testRouter(t)
|
||||
for _, tc := range cases {
|
||||
d, err := r.Resolve(tc.class)
|
||||
if err != nil {
|
||||
t.Fatalf("Resolve(%q): %v", tc.class, err)
|
||||
}
|
||||
if d.Tier != tc.tier || d.Model != tc.model {
|
||||
t.Errorf("Resolve(%q) = %s/%s, want %s/%s", tc.class, d.Tier, d.Model, tc.tier, tc.model)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveUnknownClassIsError(t *testing.T) {
|
||||
r := testRouter(t)
|
||||
_, err := r.Resolve("urgent")
|
||||
if err == nil {
|
||||
t.Fatal("unknown class must error (routing stays auditable)")
|
||||
}
|
||||
if !strings.Contains(err.Error(), `[models.classes]`) {
|
||||
t.Errorf("error should point at the config section: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRouterValidation(t *testing.T) {
|
||||
tiers := map[string]string{"mopac-primary": "glm-5.3"}
|
||||
if _, err := NewRouter(tiers, nil, "mopac-missing"); err == nil || !strings.Contains(err.Error(), "default tier") {
|
||||
t.Errorf("missing default tier: err=%v", err)
|
||||
}
|
||||
if _, err := NewRouter(tiers, map[string]string{"x": "mopac-nope"}, "mopac-primary"); err == nil || !strings.Contains(err.Error(), "unknown tier") {
|
||||
t.Errorf("class to unknown tier: err=%v", err)
|
||||
}
|
||||
if _, err := NewRouter(nil, nil, "mopac-primary"); err == nil {
|
||||
t.Errorf("empty tiers must error")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user