204 lines
5.9 KiB
Go
204 lines
5.9 KiB
Go
package providers
|
|
|
|
import "testing"
|
|
|
|
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) {
|
|
// Explicit value in caps takes priority
|
|
caps := ModelCapabilities{MaxOutputTokens: 32000}
|
|
got := ResolveMaxOutput("whatever-model", caps)
|
|
if got != 32000 {
|
|
t.Errorf("got %d, want 32000", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveMaxOutput_FromKnownTable(t *testing.T) {
|
|
// No value in caps, falls to known table
|
|
caps := ModelCapabilities{}
|
|
got := ResolveMaxOutput("claude-opus-4-20250514", caps)
|
|
if got != 32000 {
|
|
t.Errorf("got %d, want 32000", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveMaxOutput_FromContext(t *testing.T) {
|
|
// Unknown model but has context window
|
|
caps := ModelCapabilities{MaxContext: 32768}
|
|
got := ResolveMaxOutput("unknown-model-abc", caps)
|
|
// 32768 / 8 = 4096
|
|
if got != 4096 {
|
|
t.Errorf("got %d, want 4096 (derived from context/8)", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveMaxOutput_ContextClamp(t *testing.T) {
|
|
// Very large context → clamp derived output to 16384
|
|
caps := ModelCapabilities{MaxContext: 1048576}
|
|
got := ResolveMaxOutput("unknown-large-model", caps)
|
|
if got != 16384 {
|
|
t.Errorf("got %d, want 16384 (clamped)", got)
|
|
}
|
|
|
|
// Very small context → clamp derived output to 2048
|
|
caps = 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) {
|
|
// Totally unknown, no context
|
|
caps := ModelCapabilities{}
|
|
got := ResolveMaxOutput("mystery-model", caps)
|
|
if got != 4096 {
|
|
t.Errorf("got %d, want 4096 (last resort)", got)
|
|
}
|
|
}
|
|
|
|
func TestMergeCapabilities(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 := ModelCapabilities{
|
|
ToolCalling: true,
|
|
MaxOutputTokens: 16384,
|
|
}
|
|
merged := MergeCapabilities(providerCaps, "claude-sonnet-4-20250514")
|
|
|
|
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)
|
|
}
|
|
// Vision should be filled from known table (claude-sonnet-4 has it)
|
|
if !merged.Vision {
|
|
t.Error("vision should be filled from known table")
|
|
}
|
|
// MaxContext should be filled from known table
|
|
if merged.MaxContext == 0 {
|
|
t.Error("max_context should be filled from known table")
|
|
}
|
|
}
|
|
|
|
func TestMergeCapabilities_ProviderFalseWins(t *testing.T) {
|
|
// Provider explicitly reports vision:false — should NOT be overridden by known table
|
|
providerCaps := ModelCapabilities{
|
|
ToolCalling: true,
|
|
Vision: false, // provider says no vision
|
|
MaxOutputTokens: 8192,
|
|
MaxContext: 65536,
|
|
}
|
|
// Even though gpt-4o has vision in known table, provider caps are authoritative
|
|
// Because HasProviderData() is true, the caller passes these as authoritative
|
|
merged := MergeCapabilities(providerCaps, "some-unknown-model")
|
|
|
|
if merged.Vision {
|
|
t.Error("vision should remain false — provider is authoritative")
|
|
}
|
|
}
|
|
|
|
func TestMergeCapabilities_EmptyProvider(t *testing.T) {
|
|
// Empty provider caps — should fall through entirely to known table
|
|
merged := MergeCapabilities(ModelCapabilities{}, "gpt-4o")
|
|
|
|
if !merged.ToolCalling {
|
|
t.Error("should get tool_calling from known table when provider is empty")
|
|
}
|
|
if !merged.Vision {
|
|
t.Error("should get vision from known table when provider is empty")
|
|
}
|
|
}
|
|
|
|
func TestHasProviderData(t *testing.T) {
|
|
empty := ModelCapabilities{}
|
|
if empty.HasProviderData() {
|
|
t.Error("empty caps should not have provider data")
|
|
}
|
|
|
|
withTool := ModelCapabilities{ToolCalling: true}
|
|
if !withTool.HasProviderData() {
|
|
t.Error("caps with tool_calling should have provider data")
|
|
}
|
|
|
|
withContext := ModelCapabilities{MaxContext: 128000}
|
|
if !withContext.HasProviderData() {
|
|
t.Error("caps with max_context should have provider data")
|
|
}
|
|
}
|