package capabilities import ( "testing" "git.gobha.me/xcaliber/chat-switchboard/models" ) func TestLookupKnownModel_ExactMatch(t *testing.T) { caps, ok := LookupKnownModel("gpt-4o") if !ok { t.Fatal("expected gpt-4o to be found") } if caps.MaxOutputTokens != 16384 { t.Errorf("gpt-4o max output: got %d, want 16384", caps.MaxOutputTokens) } if !caps.ToolCalling { t.Error("gpt-4o should support tool calling") } if !caps.Vision { t.Error("gpt-4o should support vision") } } func TestLookupKnownModel_PrefixMatch(t *testing.T) { caps, ok := LookupKnownModel("claude-sonnet-4-20250514") if !ok { t.Fatal("expected claude-sonnet-4-20250514 to match prefix claude-sonnet-4") } if caps.MaxOutputTokens != 16000 { t.Errorf("claude-sonnet-4 max output: got %d, want 16000", caps.MaxOutputTokens) } } func TestLookupKnownModel_ProviderPrefix(t *testing.T) { caps, ok := LookupKnownModel("anthropic/claude-sonnet-4-20250514") if !ok { t.Fatal("expected provider-prefixed model to match") } if caps.MaxOutputTokens != 16000 { t.Errorf("got %d, want 16000", caps.MaxOutputTokens) } } func TestLookupKnownModel_NotFound(t *testing.T) { _, ok := LookupKnownModel("totally-unknown-model-xyz") if ok { t.Error("expected unknown model to not be found") } } func TestInferCapabilities_ToolCalling(t *testing.T) { tests := []struct { model string want bool }{ {"llama-3.1-70b-instruct", true}, {"mistral-7b", true}, {"qwen-2.5-72b", true}, {"phi-3-mini", true}, {"random-smallmodel", false}, } for _, tt := range tests { caps := InferCapabilities(tt.model) if caps.ToolCalling != tt.want { t.Errorf("InferCapabilities(%q).ToolCalling = %v, want %v", tt.model, caps.ToolCalling, tt.want) } } } func TestInferCapabilities_Vision(t *testing.T) { caps := InferCapabilities("llava-v1.6-34b") if !caps.Vision { t.Error("llava should infer vision capability") } } func TestInferCapabilities_Reasoning(t *testing.T) { caps := InferCapabilities("deepseek-r1-distill-llama-70b") if !caps.Reasoning { t.Error("deepseek-r1 should infer reasoning capability") } } func TestResolveMaxOutput_FromCaps(t *testing.T) { caps := models.ModelCapabilities{MaxOutputTokens: 32000} got := ResolveMaxOutput("whatever-model", caps) if got != 32000 { t.Errorf("got %d, want 32000", got) } } func TestResolveMaxOutput_FromKnownTable(t *testing.T) { caps := models.ModelCapabilities{} got := ResolveMaxOutput("claude-opus-4-20250514", caps) if got != 32000 { t.Errorf("got %d, want 32000", got) } } func TestResolveMaxOutput_FromContext(t *testing.T) { caps := models.ModelCapabilities{MaxContext: 32768} got := ResolveMaxOutput("unknown-model-abc", caps) if got != 4096 { t.Errorf("got %d, want 4096 (derived from context/8)", got) } } func TestResolveMaxOutput_ContextClamp(t *testing.T) { caps := models.ModelCapabilities{MaxContext: 1048576} got := ResolveMaxOutput("unknown-large-model", caps) if got != 16384 { t.Errorf("got %d, want 16384 (clamped)", got) } caps = models.ModelCapabilities{MaxContext: 4096} got = ResolveMaxOutput("unknown-tiny-model", caps) if got != 2048 { t.Errorf("got %d, want 2048 (clamped)", got) } } func TestResolveMaxOutput_LastResort(t *testing.T) { caps := models.ModelCapabilities{} got := ResolveMaxOutput("mystery-model", caps) if got != 4096 { t.Errorf("got %d, want 4096 (last resort)", got) } } func TestResolveIntrinsic(t *testing.T) { // Provider reports tool_calling and max_output — these should be authoritative. // Known table for claude-sonnet-4 has vision, thinking, etc — should fill gaps. providerCaps := models.ModelCapabilities{ ToolCalling: true, MaxOutputTokens: 16384, } merged := ResolveIntrinsic("claude-sonnet-4-20250514", &providerCaps) if !merged.ToolCalling { t.Error("tool_calling should be preserved from provider") } if merged.MaxOutputTokens != 16384 { t.Errorf("max_output should be 16384 from provider, got %d", merged.MaxOutputTokens) } if !merged.Vision { t.Error("vision should be filled from known table") } if merged.MaxContext == 0 { t.Error("max_context should be filled from known table") } } func TestResolveIntrinsic_ProviderFalseWins(t *testing.T) { providerCaps := models.ModelCapabilities{ ToolCalling: true, Vision: false, MaxOutputTokens: 8192, MaxContext: 65536, } merged := ResolveIntrinsic("some-unknown-model", &providerCaps) if merged.Vision { t.Error("vision should remain false — provider is authoritative") } } func TestResolveIntrinsic_NilProvider(t *testing.T) { // Nil provider caps — should fall through entirely to known table merged := ResolveIntrinsic("gpt-4o", nil) if !merged.ToolCalling { t.Error("should get tool_calling from known table when provider is nil") } if !merged.Vision { t.Error("should get vision from known table when provider is nil") } } func TestHasProviderData(t *testing.T) { empty := models.ModelCapabilities{} if empty.HasProviderData() { t.Error("empty caps should not have provider data") } withTool := models.ModelCapabilities{ToolCalling: true} if !withTool.HasProviderData() { t.Error("caps with tool_calling should have provider data") } withContext := models.ModelCapabilities{MaxContext: 128000} if !withContext.HasProviderData() { t.Error("caps with max_context should have provider data") } }