| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- 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
- }
|