| package semanticcache |
|
|
| import ( |
| "context" |
| "strings" |
| "testing" |
|
|
| bifrost "github.com/maximhq/bifrost/core" |
| "github.com/maximhq/bifrost/core/schemas" |
| ) |
|
|
| |
| func TestParameterVariations(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| basePrompt := "What is the capital of France?" |
|
|
| tests := []struct { |
| name string |
| request1 *schemas.BifrostChatRequest |
| request2 *schemas.BifrostChatRequest |
| shouldCache bool |
| }{ |
| { |
| name: "Same Parameters", |
| request1: CreateBasicChatRequest(basePrompt, 0.5, 50), |
| request2: CreateBasicChatRequest(basePrompt, 0.5, 50), |
| shouldCache: true, |
| }, |
| { |
| name: "Different Temperature", |
| request1: CreateBasicChatRequest(basePrompt, 0.1, 50), |
| request2: CreateBasicChatRequest(basePrompt, 0.9, 50), |
| shouldCache: false, |
| }, |
| { |
| name: "Different MaxTokens", |
| request1: CreateBasicChatRequest(basePrompt, 0.5, 50), |
| request2: CreateBasicChatRequest(basePrompt, 0.5, 200), |
| shouldCache: false, |
| }, |
| } |
|
|
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| |
| ctx := CreateContextWithCacheKey("param-variations-test") |
|
|
| |
| clearTestKeysWithStore(t, setup.Store) |
|
|
| |
| _, err1 := setup.Client.ChatCompletionRequest(ctx, tt.request1) |
| if err1 != nil { |
| return |
| } |
|
|
| WaitForCache(setup.Plugin) |
|
|
| |
| response2, err2 := setup.Client.ChatCompletionRequest(ctx, tt.request2) |
| if err2 != nil { |
| if err2.Error != nil { |
| t.Fatalf("Second request failed: %v", err2.Error.Message) |
| } else { |
| t.Fatalf("Second request failed: %v", err2) |
| } |
| } |
|
|
| |
| if tt.shouldCache { |
| AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, string(CacheTypeDirect)) |
| } else { |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}) |
| } |
| }) |
| } |
| } |
|
|
| |
| func TestToolVariations(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| ctx := CreateContextWithCacheKey("tool-variations-test") |
|
|
| |
| baseRequest := &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("What's the weather like today?"), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(100), |
| Temperature: bifrost.Ptr(0.5), |
| }, |
| } |
|
|
| |
| requestWithTools := &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("What's the weather like today?"), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(100), |
| Temperature: bifrost.Ptr(0.5), |
| Tools: []schemas.ChatTool{ |
| { |
| Type: schemas.ChatToolTypeFunction, |
| Function: &schemas.ChatToolFunction{ |
| Name: "get_weather", |
| Description: bifrost.Ptr("Get the current weather"), |
| Parameters: &schemas.ToolFunctionParameters{ |
| Type: "object", |
| Properties: schemas.NewOrderedMapFromPairs( |
| schemas.KV("location", map[string]interface{}{ |
| "type": "string", |
| "description": "The city and state", |
| }), |
| ), |
| }, |
| Strict: bifrost.Ptr(false), |
| }, |
| }, |
| }, |
| }, |
| } |
|
|
| |
| requestWithDifferentTools := &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("What's the weather like today?"), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(100), |
| Temperature: bifrost.Ptr(0.5), |
| Tools: []schemas.ChatTool{ |
| { |
| Type: schemas.ChatToolTypeFunction, |
| Function: &schemas.ChatToolFunction{ |
| Name: "get_current_weather", |
| Description: bifrost.Ptr("Get current weather information"), |
| Parameters: &schemas.ToolFunctionParameters{ |
| Type: "object", |
| Properties: schemas.NewOrderedMapFromPairs( |
| schemas.KV("city", map[string]interface{}{ |
| "type": "string", |
| "description": "The city name", |
| }), |
| ), |
| }, |
| Strict: bifrost.Ptr(false), |
| }, |
| }, |
| }, |
| }, |
| } |
|
|
| |
| t.Log("Making request without tools...") |
| _, err1 := setup.Client.ChatCompletionRequest(ctx, baseRequest) |
| if err1 != nil { |
| t.Fatalf("Request without tools failed: %v", err1) |
| } |
|
|
| WaitForCache(setup.Plugin) |
|
|
| |
| t.Log("Making request with tools...") |
| response2, err2 := setup.Client.ChatCompletionRequest(ctx, requestWithTools) |
| if err2 != nil { |
| return |
| } |
|
|
| AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}) |
|
|
| WaitForCache(setup.Plugin) |
|
|
| |
| t.Log("Making same request with tools again...") |
| response3, err3 := setup.Client.ChatCompletionRequest(ctx, requestWithTools) |
| if err3 != nil { |
| t.Fatalf("Second request with tools failed: %v", err3) |
| } |
|
|
| AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response3}, "") |
|
|
| |
| t.Log("Making request with different tools...") |
| response4, err4 := setup.Client.ChatCompletionRequest(ctx, requestWithDifferentTools) |
| if err4 != nil { |
| return |
| } |
|
|
| AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response4}) |
|
|
| t.Log("✅ Tool variations test completed!") |
| } |
|
|
| |
| func TestContentVariations(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| tests := []struct { |
| name string |
| request *schemas.BifrostChatRequest |
| }{ |
| { |
| name: "Image URL Content", |
| request: &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentBlocks: []schemas.ChatContentBlock{ |
| { |
| Type: schemas.ChatContentBlockTypeText, |
| Text: bifrost.Ptr("Analyze this image"), |
| }, |
| { |
| Type: schemas.ChatContentBlockTypeImage, |
| ImageURLStruct: &schemas.ChatInputImage{ |
| URL: "https://pub-cdead89c2f004d8f963fd34010c479d0.r2.dev/Gfp-wisconsin-madison-the-nature-boardwalk.jpg", |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(200), |
| Temperature: bifrost.Ptr(0.3), |
| }, |
| }, |
| }, |
| { |
| name: "Multiple Images", |
| request: &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentBlocks: []schemas.ChatContentBlock{ |
| { |
| Type: schemas.ChatContentBlockTypeText, |
| Text: bifrost.Ptr("Compare these images"), |
| }, |
| { |
| Type: schemas.ChatContentBlockTypeImage, |
| ImageURLStruct: &schemas.ChatInputImage{ |
| URL: "https://pub-cdead89c2f004d8f963fd34010c479d0.r2.dev/Gfp-wisconsin-madison-the-nature-boardwalk.jpg", |
| }, |
| }, |
| { |
| Type: schemas.ChatContentBlockTypeImage, |
| ImageURLStruct: &schemas.ChatInputImage{ |
| URL: "https://upload.wikimedia.org/wikipedia/commons/b/b5/Scenery_.jpg", |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(200), |
| Temperature: bifrost.Ptr(0.3), |
| }, |
| }, |
| }, |
| { |
| name: "Very Long Content", |
| request: &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr(strings.Repeat("This is a very long prompt. ", 100)), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(50), |
| Temperature: bifrost.Ptr(0.2), |
| }, |
| }, |
| }, |
| { |
| name: "Multi-turn Conversation", |
| request: &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("What is AI?"), |
| }, |
| }, |
| { |
| Role: schemas.ChatMessageRoleAssistant, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("AI stands for Artificial Intelligence..."), |
| }, |
| }, |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("Can you give me examples?"), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(150), |
| Temperature: bifrost.Ptr(0.5), |
| }, |
| }, |
| }, |
| } |
|
|
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| t.Logf("Testing content variation: %s", tt.name) |
|
|
| |
| ctx := CreateContextWithCacheKey("content-variations-test") |
|
|
| |
| _, err1 := setup.Client.ChatCompletionRequest(ctx, tt.request) |
| if err1 != nil { |
| t.Logf("⚠️ First %s request failed: %v", tt.name, err1) |
| return |
| } |
|
|
| WaitForCache(setup.Plugin) |
|
|
| |
| response2, err2 := setup.Client.ChatCompletionRequest(ctx, tt.request) |
| if err2 != nil { |
| t.Fatalf("Second %s request failed: %v", tt.name, err2) |
| } |
|
|
| |
| AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, string(CacheTypeDirect)) |
| t.Logf("✅ %s content variation successful", tt.name) |
| }) |
| } |
| } |
|
|
| |
| func TestBoundaryParameterValues(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| tests := []struct { |
| name string |
| request *schemas.BifrostChatRequest |
| }{ |
| { |
| name: "Maximum Parameter Values", |
| request: &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("Test max parameters"), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(4096), |
| PresencePenalty: bifrost.Ptr(2.0), |
| FrequencyPenalty: bifrost.Ptr(2.0), |
| Temperature: bifrost.Ptr(2.0), |
| TopP: bifrost.Ptr(1.0), |
| }, |
| }, |
| }, |
| { |
| name: "Minimum Parameter Values", |
| request: &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("Test min parameters"), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(1), |
| PresencePenalty: bifrost.Ptr(-2.0), |
| FrequencyPenalty: bifrost.Ptr(-2.0), |
| Temperature: bifrost.Ptr(0.0), |
| TopP: bifrost.Ptr(0.01), |
| }, |
| }, |
| }, |
| { |
| name: "Edge Case Parameters", |
| request: &schemas.BifrostChatRequest{ |
| Provider: schemas.OpenAI, |
| Model: "gpt-4o-mini", |
| Input: []schemas.ChatMessage{ |
| { |
| Role: schemas.ChatMessageRoleUser, |
| Content: &schemas.ChatMessageContent{ |
| ContentStr: bifrost.Ptr("Test edge case parameters"), |
| }, |
| }, |
| }, |
| Params: &schemas.ChatParameters{ |
| MaxCompletionTokens: bifrost.Ptr(1), |
| User: bifrost.Ptr("test-user-id-12345"), |
| Temperature: bifrost.Ptr(0.0), |
| TopP: bifrost.Ptr(0.1), |
| }, |
| }, |
| }, |
| } |
|
|
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| t.Logf("Testing boundary parameters: %s", tt.name) |
|
|
| |
| ctx := CreateContextWithCacheKey("boundary-params-test") |
|
|
| _, err := setup.Client.ChatCompletionRequest(ctx, tt.request) |
| if err != nil { |
| t.Logf("⚠️ %s request failed (may be expected): %v", tt.name, err) |
| } else { |
| t.Logf("✅ %s handled gracefully", tt.name) |
| } |
| }) |
| } |
| } |
|
|
| |
| func TestSemanticSimilarityEdgeCases(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| setup.Config.Threshold = 0.9 |
|
|
| |
| similarTests := []struct { |
| prompt1 string |
| prompt2 string |
| shouldMatch bool |
| description string |
| }{ |
| { |
| prompt1: "What is machine learning?", |
| prompt2: "Can you explain machine learning?", |
| shouldMatch: true, |
| description: "Similar questions about ML", |
| }, |
| { |
| prompt1: "How does AI work?", |
| prompt2: "Explain artificial intelligence", |
| shouldMatch: true, |
| description: "AI-related questions", |
| }, |
| { |
| prompt1: "What is the weather today?", |
| prompt2: "What do you know about bifrost?", |
| shouldMatch: false, |
| description: "Completely different topics", |
| }, |
| { |
| prompt1: "Hello, how are you?", |
| prompt2: "Hi, how are you doing?", |
| shouldMatch: true, |
| description: "Similar greetings", |
| }, |
| } |
|
|
| for i, test := range similarTests { |
| t.Run(test.description, func(t *testing.T) { |
| |
| ctx := CreateContextWithCacheKey("semantic-edge-test") |
|
|
| |
| clearTestKeysWithStore(t, setup.Store) |
|
|
| |
| request1 := CreateBasicChatRequest(test.prompt1, 0.1, 50) |
| _, err1 := setup.Client.ChatCompletionRequest(ctx, request1) |
| if err1 != nil { |
| return |
| } |
|
|
| |
| WaitForCache(setup.Plugin) |
|
|
| |
| request2 := CreateBasicChatRequest(test.prompt2, 0.1, 50) |
| response2, err2 := setup.Client.ChatCompletionRequest(ctx, request2) |
| if err2 != nil { |
| if err2.Error != nil { |
| t.Fatalf("Second request failed: %v", err2.Error.Message) |
| } else { |
| t.Fatalf("Second request failed: %v", err2) |
| } |
| } |
|
|
| var cacheThresholdFloat float64 |
| var cacheSimilarityFloat float64 |
|
|
| |
| semanticMatch := false |
| if response2.ExtraFields.CacheDebug != nil && response2.ExtraFields.CacheDebug.CacheHit { |
| if response2.ExtraFields.CacheDebug.HitType != nil && *response2.ExtraFields.CacheDebug.HitType == string(CacheTypeSemantic) { |
| semanticMatch = true |
|
|
| if response2.ExtraFields.CacheDebug.Threshold != nil { |
| cacheThresholdFloat = *response2.ExtraFields.CacheDebug.Threshold |
| } |
| if response2.ExtraFields.CacheDebug.Similarity != nil { |
| cacheSimilarityFloat = *response2.ExtraFields.CacheDebug.Similarity |
| } |
| } |
| } |
|
|
| if test.shouldMatch { |
| if semanticMatch { |
| t.Logf("✅ Test %d: Semantic match found as expected for '%s'", i+1, test.description) |
| } else { |
| t.Logf("ℹ️ Test %d: No semantic match found for '%s', check with threshold: %f and found similarity: %f", i+1, test.description, cacheThresholdFloat, cacheSimilarityFloat) |
| } |
| } else { |
| if semanticMatch { |
| t.Errorf("❌ Test %d: Unexpected semantic match for different topics: '%s', check with threshold: %f and found similarity: %f", i+1, test.description, cacheThresholdFloat, cacheSimilarityFloat) |
| } else { |
| t.Logf("✅ Test %d: Correctly no semantic match for different topics: '%s'", i+1, test.description) |
| } |
| } |
| }) |
| } |
| } |
|
|
| |
| func TestErrorHandlingEdgeCases(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| testRequest := CreateBasicChatRequest("Test error handling scenarios", 0.5, 50) |
|
|
| |
| t.Run("Request without cache key", func(t *testing.T) { |
| ctxNoKey := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline) |
|
|
| response, err := setup.Client.ChatCompletionRequest(ctxNoKey, testRequest) |
| if err != nil { |
| t.Errorf("Request without cache key failed: %v", err) |
| return |
| } |
|
|
| |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response}) |
| t.Log("✅ Request without cache key correctly bypassed cache") |
| }) |
|
|
| |
| t.Run("Request with invalid cache key type", func(t *testing.T) { |
| |
| validCtx := CreateContextWithCacheKey("error-handling-test") |
| _, err := setup.Client.ChatCompletionRequest(validCtx, testRequest) |
| if err != nil { |
| t.Fatalf("First request with valid cache key failed: %v", err) |
| } |
|
|
| WaitForCache(setup.Plugin) |
|
|
| |
| ctxInvalidKey := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline).WithValue(CacheKey, 12345) |
|
|
| response, err := setup.Client.ChatCompletionRequest(ctxInvalidKey, testRequest) |
| if err != nil { |
| t.Errorf("Request with invalid cache key type failed: %v", err) |
| return |
| } |
|
|
| |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response}) |
| t.Log("✅ Request with invalid cache key type correctly bypassed cache") |
| }) |
|
|
| t.Log("✅ Error handling edge cases completed!") |
| } |
|
|