Files
bifrost/plugins/semanticcache/plugin_conversation_config_test.go
Beyhan Oğur 880f412e2c first commit
2026-04-26 21:52:23 +03:00

455 lines
16 KiB
Go

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")
}