phase 1: add [models] tier config — additive, no callers migrate yet
Codifies the small-model-pipeline tiering (per project_small_model_pipeline_vision.md) in lakehouse.toml [models] section. Tier names map to actual model IDs; bumping a model means editing one line, not hunting through code. Tier philosophy: - local_* : on-box Ollama. Inner-loop hot path. Repeated calls. - cloud_* : Ollama Cloud (Pro plan). Larger context, fail-up tier. - frontier_* : OpenRouter / OpenCode. Rate-limited, billed per call. weak_models is the codified "local-hot-path eligible" list — phase 2 will migrate matrix.downgrade to read it instead of hardcoding. Defaults reflect 2026-04-29 architecture: qwen3.5:latest as local (stronger than qwen2.5, same JSON-clean property), kimi-k2.6 as cloud judge (kimi-k2:1t still upstream-broken), opus-4-7 + kimi-k2-0905 as frontier review/arch via OpenRouter, opencode/claude-opus-4-7 as frontier_free leveraging the OpenCode subscription. 3 new tests in internal/shared/config_test.go: - TestDefaultConfig_ModelsTier — locks tier defaults - TestModelsConfig_IsWeak — weak-bypass list - TestLoadConfig_ModelsTOMLRoundTrip — override semantics just verify PASS (g2 had one flake on first run — Ollama transfer truncation; clean on retry, unrelated to this change). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,84 @@ bucket = "custom-bucket"
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultConfig_ModelsTier(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
// Locks tier defaults so a refactor that drops a tier or renames
|
||||
// a default silently can't ship without a test edit.
|
||||
checks := []struct {
|
||||
tier, want string
|
||||
}{
|
||||
{"local_fast", "qwen3.5:latest"},
|
||||
{"local_embed", "nomic-embed-text"},
|
||||
{"local_judge", "qwen3.5:latest"},
|
||||
{"cloud_judge", "kimi-k2.6:cloud"},
|
||||
{"cloud_review", "qwen3-coder:480b"},
|
||||
{"frontier_review", "openrouter/anthropic/claude-opus-4-7"},
|
||||
{"frontier_free", "opencode/claude-opus-4-7"},
|
||||
}
|
||||
for _, c := range checks {
|
||||
if got := cfg.Models.Resolve(c.tier); got != c.want {
|
||||
t.Errorf("Models.Resolve(%q) = %q, want %q", c.tier, got, c.want)
|
||||
}
|
||||
}
|
||||
// Unknown tier returns "" — caller's responsibility to fall back.
|
||||
if got := cfg.Models.Resolve("nonexistent"); got != "" {
|
||||
t.Errorf("Models.Resolve(nonexistent) = %q, want empty string", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModelsConfig_IsWeak(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
// Default WeakModels set is the matrix.downgrade bypass list.
|
||||
if !cfg.Models.IsWeak("qwen3.5:latest") {
|
||||
t.Errorf("qwen3.5:latest should be weak (in default WeakModels)")
|
||||
}
|
||||
if !cfg.Models.IsWeak("qwen3:latest") {
|
||||
t.Errorf("qwen3:latest should be weak")
|
||||
}
|
||||
// Strong / cloud / frontier models are NOT weak.
|
||||
for _, m := range []string{
|
||||
"opencode/claude-opus-4-7",
|
||||
"openrouter/openai/gpt-5",
|
||||
"qwen3-coder:480b",
|
||||
"deepseek-v3.2",
|
||||
} {
|
||||
if cfg.Models.IsWeak(m) {
|
||||
t.Errorf("%s should NOT be weak", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig_ModelsTOMLRoundTrip(t *testing.T) {
|
||||
// Override one tier name; verify it loads, rest stay at defaults.
|
||||
dir := t.TempDir()
|
||||
cfgPath := filepath.Join(dir, "lakehouse.toml")
|
||||
body := `[models]
|
||||
local_judge = "custom-judge:latest"
|
||||
weak_models = ["custom-judge:latest", "qwen3:latest"]
|
||||
`
|
||||
if err := os.WriteFile(cfgPath, []byte(body), 0o644); err != nil {
|
||||
t.Fatalf("write config: %v", err)
|
||||
}
|
||||
cfg, err := LoadConfig(cfgPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig: %v", err)
|
||||
}
|
||||
if cfg.Models.LocalJudge != "custom-judge:latest" {
|
||||
t.Errorf("local_judge = %q, want custom-judge:latest", cfg.Models.LocalJudge)
|
||||
}
|
||||
if !cfg.Models.IsWeak("custom-judge:latest") {
|
||||
t.Errorf("custom-judge:latest should be weak after override")
|
||||
}
|
||||
// Unspecified tiers keep defaults — TOML decoder doesn't zero
|
||||
// fields it didn't see, BUT slices are replaced wholesale. The
|
||||
// weak_models override above is intentional so callers know
|
||||
// they're trading the full default list when they set this.
|
||||
if cfg.Models.LocalFast != "qwen3.5:latest" {
|
||||
t.Errorf("local_fast drifted to %q, want default qwen3.5:latest", cfg.Models.LocalFast)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig_InvalidTOML_ReturnsError(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cfgPath := filepath.Join(dir, "bad.toml")
|
||||
|
||||
Reference in New Issue
Block a user