| package semanticcache |
|
|
| import ( |
| "testing" |
| "time" |
|
|
| "github.com/maximhq/bifrost/core/schemas" |
| ) |
|
|
| |
| func TestResponsesAPIBasicFunctionality(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| ctx := CreateContextWithCacheKey("test-responses-basic") |
|
|
| |
| testRequest := CreateBasicResponsesRequest( |
| "What is Bifrost? Answer in one short sentence.", |
| 0.7, |
| 500, |
| ) |
|
|
| t.Log("Making first Responses API request (should go to OpenAI and be cached)...") |
|
|
| |
| start1 := time.Now() |
| response1, err1 := setup.Client.ResponsesRequest(ctx, testRequest) |
| duration1 := time.Since(start1) |
|
|
| if err1 != nil { |
| return |
| } |
|
|
| if response1 == nil || len(response1.Output) == 0 { |
| t.Fatal("First Responses response is invalid") |
| } |
|
|
| t.Logf("First request completed in %v", duration1) |
| t.Logf("Response contains %d output messages", len(response1.Output)) |
| if c := response1.Output[0].Content; c != nil && c.ContentStr != nil { |
| t.Logf("Response: %s", *c.ContentStr) |
| } else if c != nil && len(c.ContentBlocks) > 0 && c.ContentBlocks[0].Text != nil { |
| t.Logf("Response: %s", *c.ContentBlocks[0].Text) |
| } else { |
| t.Log("Response: <no text>") |
| } |
|
|
| |
| WaitForCache(setup.Plugin) |
|
|
| t.Log("Making second identical Responses API request (should be served from cache)...") |
|
|
| |
| start2 := time.Now() |
| response2, err2 := setup.Client.ResponsesRequest(ctx, testRequest) |
| duration2 := time.Since(start2) |
|
|
| if err2 != nil { |
| t.Fatalf("Second Responses request failed: %v", err2) |
| } |
|
|
| if response2 == nil || len(response2.Output) == 0 { |
| t.Fatal("Second Responses response is invalid") |
| } |
| if response2.Output[0].Content.ContentStr != nil { |
| t.Logf("Response: %s", *response2.Output[0].Content.ContentStr) |
| } else { |
| t.Logf("Response: %v", *response2.Output[0].Content.ContentBlocks[0].Text) |
| } |
|
|
| t.Logf("Second request completed in %v", duration2) |
|
|
| |
| AssertCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response2}, string(CacheTypeDirect)) |
|
|
| |
| t.Logf("Performance Summary:") |
| t.Logf("First request (OpenAI): %v", duration1) |
| t.Logf("Second request (Cache): %v", duration2) |
|
|
| if duration2 >= duration1 { |
| t.Log("⚠️ Cache doesn't seem faster, but this could be due to test environment") |
| } |
|
|
| |
| if response2.ExtraFields.Provider != testRequest.Provider { |
| t.Errorf("Provider mismatch in cached response: expected %s, got %s", |
| testRequest.Provider, response2.ExtraFields.Provider) |
| } |
|
|
| t.Log("✅ Basic Responses API semantic caching test completed successfully!") |
| } |
|
|
| |
| func TestResponsesAPIDifferentParameters(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| ctx := CreateContextWithCacheKey("test-responses-params") |
| basePrompt := "Explain quantum computing" |
|
|
| tests := []struct { |
| name string |
| request1 *schemas.BifrostResponsesRequest |
| request2 *schemas.BifrostResponsesRequest |
| shouldCache bool |
| }{ |
| { |
| name: "Identical Requests", |
| request1: CreateBasicResponsesRequest(basePrompt, 0.5, 500), |
| request2: CreateBasicResponsesRequest(basePrompt, 0.5, 500), |
| shouldCache: true, |
| }, |
| { |
| name: "Different Temperature", |
| request1: CreateBasicResponsesRequest(basePrompt, 0.1, 500), |
| request2: CreateBasicResponsesRequest(basePrompt, 0.9, 500), |
| shouldCache: false, |
| }, |
| { |
| name: "Different MaxOutputTokens", |
| request1: CreateBasicResponsesRequest(basePrompt, 0.5, 500), |
| request2: CreateBasicResponsesRequest(basePrompt, 0.5, 200), |
| shouldCache: false, |
| }, |
| { |
| name: "Different Instructions", |
| request1: CreateResponsesRequestWithInstructions(basePrompt, "Be concise", 0.5, 500), |
| request2: CreateResponsesRequestWithInstructions(basePrompt, "Be detailed", 0.5, 500), |
| shouldCache: false, |
| }, |
| } |
|
|
| for _, tt := range tests { |
| t.Run(tt.name, func(t *testing.T) { |
| |
| clearTestKeysWithStore(t, setup.Store) |
|
|
| |
| _, err1 := setup.Client.ResponsesRequest(ctx, tt.request1) |
| if err1 != nil { |
| return |
| } |
|
|
| WaitForCache(setup.Plugin) |
|
|
| |
| response2, err2 := setup.Client.ResponsesRequest(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{ResponsesResponse: response2}, "direct") |
| t.Log("✓ Parameters match: cache hit as expected") |
| } else { |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response2}) |
| t.Log("✓ Parameters differ: no cache hit as expected") |
| } |
| }) |
| } |
| } |
|
|
| |
| func TestResponsesAPISemanticMatching(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| ctx := CreateContextWithCacheKeyAndType("test-responses-semantic", CacheTypeSemantic) |
|
|
| |
| originalRequest := CreateBasicResponsesRequest("What is machine learning?", 0.5, 500) |
| t.Log("Making first Responses request with original text...") |
| response1, err1 := setup.Client.ResponsesRequest(ctx, originalRequest) |
| if err1 != nil { |
| return |
| } |
|
|
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response1}) |
| WaitForCache(setup.Plugin) |
|
|
| |
| semanticRequest := CreateBasicResponsesRequest("Can you explain machine learning concepts?", 0.5, 500) |
| t.Log("Making semantically similar Responses request...") |
| response2, err2 := setup.Client.ResponsesRequest(ctx, semanticRequest) |
| 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{ResponsesResponse: response2}, "semantic") |
| t.Log("✓ Semantic cache hit with similar content") |
| } |
|
|
| |
| func TestResponsesAPIWithInstructions(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| ctx := CreateContextWithCacheKey("test-responses-instructions") |
|
|
| |
| request1 := CreateResponsesRequestWithInstructions( |
| "Explain artificial intelligence", |
| "You are a helpful assistant. Be concise and accurate.", |
| 0.7, |
| 500, |
| ) |
|
|
| t.Log("Making first Responses request with instructions...") |
| response1, err1 := setup.Client.ResponsesRequest(ctx, request1) |
| if err1 != nil { |
| return |
| } |
|
|
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response1}) |
| WaitForCache(setup.Plugin) |
|
|
| |
| request2 := CreateResponsesRequestWithInstructions( |
| "Explain artificial intelligence", |
| "You are a helpful assistant. Be concise and accurate.", |
| 0.7, |
| 500, |
| ) |
|
|
| t.Log("Making second identical Responses request with instructions...") |
| response2, err2 := setup.Client.ResponsesRequest(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) |
| } |
| } |
|
|
| |
| AssertCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response2}, "direct") |
| t.Log("✓ Responses API with instructions cached correctly") |
| } |
|
|
| |
| func TestResponsesAPICacheExpiration(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| |
| shortTTL := 5 * time.Second |
| ctx := CreateContextWithCacheKeyAndTTL("test-responses-ttl", shortTTL) |
|
|
| responsesRequest := CreateBasicResponsesRequest("TTL test for Responses API", 0.5, 500) |
|
|
| t.Log("Making first Responses request with short TTL...") |
| response1, err1 := setup.Client.ResponsesRequest(ctx, responsesRequest) |
| if err1 != nil { |
| return |
| } |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response1}) |
|
|
| WaitForCache(setup.Plugin) |
|
|
| t.Log("Making second Responses request before TTL expiration...") |
| response2, err2 := setup.Client.ResponsesRequest(ctx, responsesRequest) |
| 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{ResponsesResponse: response2}, "direct") |
|
|
| t.Logf("Waiting for TTL expiration (%v)...", shortTTL) |
| time.Sleep(shortTTL + 2*time.Second) |
|
|
| t.Log("Making third Responses request after TTL expiration...") |
| response3, err3 := setup.Client.ResponsesRequest(ctx, responsesRequest) |
| if err3 != nil { |
| return |
| } |
| |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response3}) |
|
|
| t.Log("✅ Responses API requests properly handle TTL expiration") |
| } |
|
|
| |
| func TestResponsesAPIWithoutCacheKey(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| |
| ctx := CreateContextWithCacheKey("") |
|
|
| responsesRequest := CreateBasicResponsesRequest("Test Responses without cache key", 0.5, 500) |
|
|
| t.Log("Making Responses request without cache key...") |
|
|
| response, err := setup.Client.ResponsesRequest(ctx, responsesRequest) |
| if err != nil { |
| return |
| } |
|
|
| |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response}) |
|
|
| t.Log("✅ Responses requests without cache key are properly not cached") |
| } |
|
|
| |
| func TestResponsesAPINoStoreFlag(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| responsesRequest := CreateBasicResponsesRequest("Test no-store with Responses API", 0.7, 500) |
| ctx := CreateContextWithCacheKeyAndNoStore("test-no-store-responses", true) |
|
|
| t.Log("Testing no-store with Responses API...") |
| response1, err1 := setup.Client.ResponsesRequest(ctx, responsesRequest) |
| if err1 != nil { |
| return |
| } |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response1}) |
|
|
| WaitForCache(setup.Plugin) |
|
|
| |
| response2, err2 := setup.Client.ResponsesRequest(ctx, responsesRequest) |
| if err2 != nil { |
| return |
| } |
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response2}) |
|
|
| t.Log("✅ Responses API no-store flag working correctly") |
| } |
|
|
| |
| func TestResponsesAPIStreaming(t *testing.T) { |
| t.Log("Responses streaming not supported yet") |
|
|
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| ctx := CreateContextWithCacheKey("test-responses-streaming") |
| prompt := "Explain the basics of quantum computing in simple terms" |
|
|
| |
| t.Log("Making non-streaming Responses request...") |
| nonStreamRequest := CreateBasicResponsesRequest(prompt, 0.5, 500) |
| _, err1 := setup.Client.ResponsesRequest(ctx, nonStreamRequest) |
| if err1 != nil { |
| return |
| } |
|
|
| WaitForCache(setup.Plugin) |
|
|
| |
| t.Log("Making streaming Responses request with same prompt...") |
| streamRequest := CreateStreamingResponsesRequest(prompt, 0.5, 500) |
| stream, err2 := setup.Client.ResponsesStreamRequest(ctx, streamRequest) |
| if err2 != nil { |
| t.Fatalf("Streaming Responses request failed: %v", err2) |
| } |
|
|
| var streamResponses []schemas.BifrostResponsesStreamResponse |
| for streamMsg := range stream { |
| if streamMsg.BifrostError != nil { |
| t.Fatalf("Error in Responses stream: %v", streamMsg.BifrostError) |
| } |
| if streamMsg.BifrostResponsesStreamResponse != nil { |
| streamResponses = append(streamResponses, *streamMsg.BifrostResponsesStreamResponse) |
| } |
| } |
|
|
| if len(streamResponses) == 0 { |
| t.Fatal("No streaming responses received") |
| } |
|
|
| |
| cacheHitFound := false |
| for _, resp := range streamResponses { |
| if resp.ExtraFields.CacheDebug != nil && resp.ExtraFields.CacheDebug.CacheHit { |
| cacheHitFound = true |
| break |
| } |
| } |
|
|
| if !cacheHitFound { |
| t.Log("⚠️ No cache hit detected in streaming responses - this could be expected behavior") |
| } else { |
| t.Log("✓ Cache hit detected in streaming Responses API") |
| } |
|
|
| t.Log("✅ Streaming Responses API test completed") |
| } |
|
|
| |
| func TestResponsesAPIComplexParameters(t *testing.T) { |
| setup := NewTestSetup(t) |
| defer setup.Cleanup() |
|
|
| ctx := CreateContextWithCacheKey("test-responses-complex-params") |
|
|
| |
| request := CreateBasicResponsesRequest("Test complex parameters", 0.8, 500) |
| request.Params.TopP = PtrFloat64(0.9) |
| request.Params.Background = &[]bool{true}[0] |
| request.Params.ParallelToolCalls = &[]bool{false}[0] |
| request.Params.ServiceTier = &[]string{"default"}[0] |
| request.Params.Store = &[]bool{true}[0] |
|
|
| t.Log("Making first Responses request with complex parameters...") |
| response1, err1 := setup.Client.ResponsesRequest(ctx, request) |
| if err1 != nil { |
| return |
| } |
|
|
| AssertNoCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response1}) |
| WaitForCache(setup.Plugin) |
|
|
| |
| request2 := CreateBasicResponsesRequest("Test complex parameters", 0.8, 500) |
| request2.Params.TopP = PtrFloat64(0.9) |
| request2.Params.Background = &[]bool{true}[0] |
| request2.Params.ParallelToolCalls = &[]bool{false}[0] |
| request2.Params.ServiceTier = &[]string{"default"}[0] |
| request2.Params.Store = &[]bool{true}[0] |
|
|
| t.Log("Making second identical Responses request with complex parameters...") |
| response2, err2 := setup.Client.ResponsesRequest(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) |
| } |
| } |
|
|
| |
| AssertCacheHit(t, &schemas.BifrostResponse{ResponsesResponse: response2}, "direct") |
| t.Log("✓ Responses API with complex parameters cached correctly") |
| } |
|
|