package workflow import ( "encoding/json" "testing" ) func TestIsAnthropicModel(t *testing.T) { tests := []struct { model string expected bool }{ {"claude-3-opus-20240229", true}, {"claude-3-sonnet", true}, {"claude-3-haiku", true}, {"anthropic/claude-3-opus", true}, {"Claude-3-Opus", true}, {"gpt-4", false}, {"gpt-4-turbo", false}, {"gemini-pro", false}, {"mistral-large", false}, } for _, tc := range tests { t.Run(tc.model, func(t *testing.T) { if got := isAnthropicModel(tc.model); got != tc.expected { t.Errorf("isAnthropicModel(%q) = %v, want %v", tc.model, got, tc.expected) } }) } } func TestBuildRequestModelOverride(t *testing.T) { t.Run("ConfigModelOverridesParamModel", func(t *testing.T) { adapter := NewOpenAIAdapter(OpenAIConfig{Model: "claude-3-opus"}) params := map[string]interface{}{ "model": "gpt-4", "messages": []interface{}{}, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.Model != "claude-3-opus" { t.Errorf("Expected model 'claude-3-opus', got %q", req.Model) } }) t.Run("NoOverrideUsesParamModel", func(t *testing.T) { adapter := NewOpenAIAdapter(OpenAIConfig{}) params := map[string]interface{}{ "model": "gpt-4", "messages": []interface{}{}, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.Model != "gpt-4" { t.Errorf("Expected model 'gpt-4', got %q", req.Model) } }) } func TestBuildRequestCacheControlOverride(t *testing.T) { boolPtr := func(b bool) *bool { return &b } t.Run("ConfigCacheControlOverridesParam", func(t *testing.T) { adapter := NewOpenAIAdapter(OpenAIConfig{CacheControl: boolPtr(true)}) params := map[string]interface{}{ "model": "claude-3-opus-20240229", "cache_control": false, "messages": []interface{}{ map[string]interface{}{"role": "system", "content": "You are helpful."}, }, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.Messages[0].CacheControl == nil { t.Fatal("Expected cache_control on system message when config override is true") } }) t.Run("ConfigCacheControlDisablesParam", func(t *testing.T) { adapter := NewOpenAIAdapter(OpenAIConfig{CacheControl: boolPtr(false)}) params := map[string]interface{}{ "model": "claude-3-opus-20240229", "cache_control": true, "messages": []interface{}{ map[string]interface{}{"role": "system", "content": "You are helpful."}, }, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.Messages[0].CacheControl != nil { t.Error("Expected no cache_control when config override is false") } }) t.Run("NilConfigFallsToParam", func(t *testing.T) { adapter := NewOpenAIAdapter(OpenAIConfig{}) params := map[string]interface{}{ "model": "claude-3-opus-20240229", "cache_control": true, "messages": []interface{}{ map[string]interface{}{"role": "system", "content": "You are helpful."}, }, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.Messages[0].CacheControl == nil { t.Fatal("Expected cache_control from param when config is nil") } }) } func TestBuildRequestAPIKey(t *testing.T) { t.Run("RequestAPIKeySetInBody", func(t *testing.T) { adapter := NewOpenAIAdapter(OpenAIConfig{RequestAPIKey: "sk-user-key-123"}) params := map[string]interface{}{ "model": "gpt-4", "messages": []interface{}{}, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.APIKey != "sk-user-key-123" { t.Errorf("Expected api_key 'sk-user-key-123', got %q", req.APIKey) } // Verify JSON output includes api_key body, _ := json.Marshal(req) if !contains(string(body), `"api_key":"sk-user-key-123"`) { t.Errorf("Expected api_key in JSON body, got: %s", string(body)) } }) t.Run("NoRequestAPIKeyOmittedFromBody", func(t *testing.T) { adapter := NewOpenAIAdapter(OpenAIConfig{}) params := map[string]interface{}{ "model": "gpt-4", "messages": []interface{}{}, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.APIKey != "" { t.Errorf("Expected empty api_key, got %q", req.APIKey) } // Verify JSON output does NOT include api_key body, _ := json.Marshal(req) if contains(string(body), `"api_key"`) { t.Errorf("Expected no api_key in JSON body, got: %s", string(body)) } }) } func TestBuildRequestCacheControl(t *testing.T) { adapter := NewOpenAIAdapter(OpenAIConfig{}) t.Run("AnthropicWithCacheControl", func(t *testing.T) { params := map[string]interface{}{ "model": "claude-3-opus-20240229", "cache_control": true, "messages": []interface{}{ map[string]interface{}{"role": "system", "content": "You are helpful."}, map[string]interface{}{"role": "user", "content": "Hello"}, }, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } // System message should have cache_control if req.Messages[0].CacheControl == nil { t.Fatal("Expected cache_control on system message") } if req.Messages[0].CacheControl.Type != "ephemeral" { t.Errorf("Expected cache_control type 'ephemeral', got %q", req.Messages[0].CacheControl.Type) } // User message should NOT have cache_control if req.Messages[1].CacheControl != nil { t.Error("Expected no cache_control on user message") } // Verify JSON output includes cache_control body, _ := json.Marshal(req) if !contains(string(body), `"cache_control":{"type":"ephemeral"}`) { t.Errorf("Expected cache_control in JSON, got: %s", string(body)) } }) t.Run("AnthropicWithoutCacheControl", func(t *testing.T) { params := map[string]interface{}{ "model": "claude-3-opus-20240229", "messages": []interface{}{ map[string]interface{}{"role": "system", "content": "You are helpful."}, }, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.Messages[0].CacheControl != nil { t.Error("Expected no cache_control when option not set") } }) t.Run("NonAnthropicWithCacheControl", func(t *testing.T) { params := map[string]interface{}{ "model": "gpt-4", "cache_control": true, "messages": []interface{}{ map[string]interface{}{"role": "system", "content": "You are helpful."}, }, } req, err := adapter.buildRequest(params) if err != nil { t.Fatalf("buildRequest failed: %v", err) } if req.Messages[0].CacheControl != nil { t.Error("Expected no cache_control for non-Anthropic model") } }) } func contains(s, substr string) bool { return len(s) >= len(substr) && searchString(s, substr) } func searchString(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false }