package semanticcache import ( "strconv" "testing" bifrost "github.com/maximhq/bifrost/core" "github.com/maximhq/bifrost/core/schemas" ) // TestConversationHistoryThresholdBasic tests basic conversation history threshold functionality func TestConversationHistoryThresholdBasic(t *testing.T) { // Test with threshold of 2 messages setup := CreateTestSetupWithConversationThreshold(t, 2) defer setup.Cleanup() ctx := CreateContextWithCacheKey("test-conversation-threshold-basic") // Test 1: Conversation with exactly 2 messages (should cache) conversation1 := BuildConversationHistory("", []string{"Hello", "Hi there!"}, ) request1 := CreateConversationRequest(conversation1, 0.7, 50) t.Log("Testing conversation with exactly 2 messages (at threshold)...") response1, err1 := setup.Client.ChatCompletionRequest(ctx, request1) if err1 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) // Fresh request WaitForCache(setup.Plugin) // Verify it was cached response2, err2 := setup.Client.ChatCompletionRequest(ctx, request1) if err2 != nil { if err2.Error != nil { t.Fatalf("Second request failed: %v", err2.Error.Message) } else { t.Fatalf("Second request failed: %v", err2) } } AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct") // Should be cached // Test 2: Conversation with 3 messages (exceeds threshold, should NOT cache) conversation2 := BuildConversationHistory("", []string{"Hello", "Hi there!"}, []string{"How are you?", "I'm doing well!"}, ) messages2 := AddUserMessage(conversation2, "What's the weather?") request2 := CreateConversationRequest(messages2, 0.7, 50) // 5 messages total > 2 t.Log("Testing conversation with 5 messages (exceeds threshold)...") response3, err3 := setup.Client.ChatCompletionRequest(ctx, request2) if err3 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response3}) // Should not cache WaitForCache(setup.Plugin) // Verify it was NOT cached t.Log("Verifying conversation exceeding threshold was not cached...") response4, err4 := setup.Client.ChatCompletionRequest(ctx, request2) if err4 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response4}) // Should still be fresh (not cached) t.Log("✅ Conversation history threshold works correctly") } // TestConversationHistoryThresholdWithSystemPrompt tests threshold with system messages func TestConversationHistoryThresholdWithSystemPrompt(t *testing.T) { // Test with threshold of 3, ExcludeSystemPrompt = false setup := CreateTestSetupWithConversationThreshold(t, 3) defer setup.Cleanup() ctx := CreateContextWithCacheKey("test-threshold-system-prompt") // System prompt + 2 user/assistant pairs = 5 messages total > 3 conversation := BuildConversationHistory( "You are a helpful assistant", // System message (counts toward threshold) []string{"Hello", "Hi there!"}, []string{"How are you?", "I'm doing well!"}, ) request := CreateConversationRequest(conversation, 0.7, 50) t.Log("Testing conversation with system prompt (5 total messages > 3 threshold)...") response1, err1 := setup.Client.ChatCompletionRequest(ctx, request) if err1 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) // Should not cache (exceeds threshold) WaitForCache(setup.Plugin) // Verify not cached response2, err2 := setup.Client.ChatCompletionRequest(ctx, request) if err2 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}) // Should not be cached t.Log("✅ Conversation threshold correctly counts system messages") } // TestConversationHistoryThresholdWithExcludeSystemPrompt tests interaction between threshold and exclude system prompt func TestConversationHistoryThresholdWithExcludeSystemPrompt(t *testing.T) { // Create setup with both threshold=3 and ExcludeSystemPrompt=true setup := CreateTestSetupWithThresholdAndExcludeSystem(t, 3, true) defer setup.Cleanup() ctx := CreateContextWithCacheKey("test-threshold-exclude-system") // Create conversation with exactly 3 non-system messages to test threshold boundary // System + 1.5 user/assistant pairs = 4 messages total // With ExcludeSystemPrompt=true, should only count 3 non-system messages for threshold conversation := BuildConversationHistory( "You are helpful", // System (excluded from count) []string{"Hello", "Hi"}, // User + Assistant = 2 messages []string{"Thanks", ""}, // User only = 1 message (no assistant response) ) // No slicing needed; BuildConversationHistory skips empty assistant entries. request := CreateConversationRequest(conversation, 0.7, 50) // 3 non-system messages exactly t.Log("Testing threshold with ExcludeSystemPrompt=true (3 non-system messages = at threshold)...") // Test logic: // - Total messages: 4 (1 system + 3 others) // - With ExcludeSystemPrompt=true: counts as 3 non-system messages // - Threshold is 3, so 3 <= 3 should allow caching response1, err1 := setup.Client.ChatCompletionRequest(ctx, request) if err1 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) // Fresh request, should not hit cache WaitForCache(setup.Plugin) // Second request should hit cache (3 non-system messages <= 3 threshold) response2, err2 := setup.Client.ChatCompletionRequest(ctx, request) if err2 != nil { if err2.Error != nil { t.Fatalf("Second request failed: %v", err2.Error.Message) } else { t.Fatalf("Second request failed: %v", err2) } } AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct") // Should cache since 3 <= 3 after excluding system t.Log("✅ Conversation threshold respects ExcludeSystemPrompt setting") } // TestConversationHistoryThresholdDifferentValues tests different threshold values func TestConversationHistoryThresholdDifferentValues(t *testing.T) { testCases := []struct { name string threshold int messages int shouldCache bool }{ {"Threshold 1, 1 message", 1, 1, true}, {"Threshold 1, 2 messages", 1, 2, false}, {"Threshold 5, 4 messages", 5, 4, true}, {"Threshold 5, 6 messages", 5, 6, false}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { setup := CreateTestSetupWithConversationThreshold(t, tc.threshold) defer setup.Cleanup() ctx := CreateContextWithCacheKey("test-threshold-" + tc.name) // Build conversation with specified number of messages var conversation []schemas.ChatMessage for i := 0; i < tc.messages; i++ { role := schemas.ChatMessageRoleUser if i%2 == 1 { role = schemas.ChatMessageRoleAssistant } message := schemas.ChatMessage{ Role: role, Content: &schemas.ChatMessageContent{ ContentStr: bifrost.Ptr("Message " + strconv.Itoa(i+1)), }, } conversation = append(conversation, message) } request := CreateConversationRequest(conversation, 0.7, 50) response1, err1 := setup.Client.ChatCompletionRequest(ctx, request) if err1 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) // Always fresh first time WaitForCache(setup.Plugin) response2, err2 := setup.Client.ChatCompletionRequest(ctx, request) if err2 != nil { return // Test will be skipped by retry function } if tc.shouldCache { AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct") } else { AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}) } }) } t.Log("✅ Different conversation threshold values work correctly") } // TestExcludeSystemPromptBasic tests basic ExcludeSystemPrompt functionality func TestExcludeSystemPromptBasic(t *testing.T) { // Test with ExcludeSystemPrompt = true setup := CreateTestSetupWithExcludeSystemPrompt(t, true) defer setup.Cleanup() ctx := CreateContextWithCacheKey("test-exclude-system-basic") // Create two conversations with different system prompts but same user/assistant messages conversation1 := BuildConversationHistory( "You are a helpful assistant", []string{"What is AI?", "AI is artificial intelligence."}, ) conversation2 := BuildConversationHistory( "You are a technical expert", // Different system prompt []string{"What is AI?", "AI is artificial intelligence."}, // Same user/assistant ) request1 := CreateConversationRequest(conversation1, 0.7, 50) request2 := CreateConversationRequest(conversation2, 0.7, 50) t.Log("Caching conversation with system prompt 1...") response1, err1 := setup.Client.ChatCompletionRequest(ctx, request1) if err1 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) WaitForCache(setup.Plugin) t.Log("Testing conversation with different system prompt (should hit cache due to ExcludeSystemPrompt=true)...") 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) } } // Should hit cache because system prompts are excluded from cache key AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct") t.Log("✅ ExcludeSystemPrompt=true correctly ignores system prompts in cache keys") } // TestExcludeSystemPromptComparison tests ExcludeSystemPrompt true vs false func TestExcludeSystemPromptComparison(t *testing.T) { // Test 1: ExcludeSystemPrompt = false (default) setup1 := CreateTestSetupWithExcludeSystemPrompt(t, false) defer setup1.Cleanup() ctx1 := CreateContextWithCacheKey("test-exclude-system-false") conversation1 := BuildConversationHistory( "You are helpful", []string{"Hello", "Hi there!"}, ) conversation2 := BuildConversationHistory( "You are an expert", // Different system prompt []string{"Hello", "Hi there!"}, // Same user/assistant ) request1 := CreateConversationRequest(conversation1, 0.7, 50) request2 := CreateConversationRequest(conversation2, 0.7, 50) t.Log("Testing ExcludeSystemPrompt=false...") response1, err1 := setup1.Client.ChatCompletionRequest(ctx1, request1) if err1 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) WaitForCache(setup1.Plugin) response2, err2 := setup1.Client.ChatCompletionRequest(ctx1, 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) } } // Should NOT hit direct cache, but might hit semantic cache due to similar content if response2.ExtraFields.CacheDebug != nil && response2.ExtraFields.CacheDebug.CacheHit { if response2.ExtraFields.CacheDebug.HitType != nil && *response2.ExtraFields.CacheDebug.HitType == "semantic" { t.Log("✅ Found semantic cache match (expected with similar content)") } else { t.Error("❌ Unexpected direct cache hit with different system prompts") } } else { t.Log("✅ No cache hit (system prompts create different cache keys)") } // Test 2: ExcludeSystemPrompt = true setup2 := CreateTestSetupWithExcludeSystemPrompt(t, true) defer setup2.Cleanup() ctx2 := CreateContextWithCacheKey("test-exclude-system-true") t.Log("Testing ExcludeSystemPrompt=true...") response3, err3 := setup2.Client.ChatCompletionRequest(ctx2, request1) if err3 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response3}) WaitForCache(setup2.Plugin) response4, err4 := setup2.Client.ChatCompletionRequest(ctx2, request2) if err4 != nil { t.Fatalf("Fourth request failed: %v", err4) } // Should hit cache because system prompts are excluded from cache key AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response4}, "direct") t.Log("✅ ExcludeSystemPrompt true vs false comparison works correctly") } // TestExcludeSystemPromptWithMultipleSystemMessages tests behavior with multiple system messages func TestExcludeSystemPromptWithMultipleSystemMessages(t *testing.T) { setup := CreateTestSetupWithExcludeSystemPrompt(t, true) defer setup.Cleanup() ctx := CreateContextWithCacheKey("test-multiple-system-messages") // Manually create conversation with multiple system messages conversation1 := []schemas.ChatMessage{ { Role: schemas.ChatMessageRoleSystem, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("You are helpful")}, }, { Role: schemas.ChatMessageRoleSystem, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("Be concise")}, }, { Role: schemas.ChatMessageRoleUser, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("Hello")}, }, { Role: schemas.ChatMessageRoleAssistant, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("Hi!")}, }, } conversation2 := []schemas.ChatMessage{ { Role: schemas.ChatMessageRoleSystem, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("You are an expert")}, }, { Role: schemas.ChatMessageRoleSystem, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("Be detailed")}, }, { Role: schemas.ChatMessageRoleUser, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("Hello")}, }, { Role: schemas.ChatMessageRoleAssistant, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("Hi!")}, }, } request1 := CreateConversationRequest(conversation1, 0.7, 50) request2 := CreateConversationRequest(conversation2, 0.7, 50) t.Log("Caching conversation with multiple system messages...") response1, err1 := setup.Client.ChatCompletionRequest(ctx, request1) if err1 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) WaitForCache(setup.Plugin) t.Log("Testing conversation with different multiple system messages...") 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) } } // Should hit cache because all system messages are excluded AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct") t.Log("✅ ExcludeSystemPrompt works with multiple system messages") } // TestExcludeSystemPromptWithNoSystemMessages tests behavior when there are no system messages func TestExcludeSystemPromptWithNoSystemMessages(t *testing.T) { setup := CreateTestSetupWithExcludeSystemPrompt(t, true) defer setup.Cleanup() ctx := CreateContextWithCacheKey("test-no-system-messages") // Conversation with no system messages conversation := []schemas.ChatMessage{ { Role: schemas.ChatMessageRoleUser, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("Hello")}, }, { Role: schemas.ChatMessageRoleAssistant, Content: &schemas.ChatMessageContent{ContentStr: bifrost.Ptr("Hi there!")}, }, } request := CreateConversationRequest(conversation, 0.7, 50) t.Log("Testing conversation with no system messages...") response1, err1 := setup.Client.ChatCompletionRequest(ctx, request) if err1 != nil { return // Test will be skipped by retry function } AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) WaitForCache(setup.Plugin) // Should cache normally response2, err2 := setup.Client.ChatCompletionRequest(ctx, request) if err2 != nil { if err2.Error != nil { t.Fatalf("Second request failed: %v", err2.Error.Message) } else { t.Fatalf("Second request failed: %v", err2) } } AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct") t.Log("✅ ExcludeSystemPrompt works correctly when no system messages present") }