Skip to content

Commit 14285ed

Browse files
committed
fix(classification): resolve keyword matching failures in E2E tests (#713)
Fixes two critical bugs causing keyword routing E2E test failures: 1. **Config merge bug**: Embedded struct assignment in reconciler didn't copy IntelligentRouting fields correctly. Changed to explicit field-by-field copy to ensure keyword rules are properly loaded from CRDs. 2. **Cache hit headers bug**: Cache responses used ImmediateResponse which bypassed normal header processing, causing VSR decision headers to be missing. Added vsrDecisionName parameter to CreateCacheHitResponse() to include x-vsr-selected-decision header in cached responses. **Test Results:** - keyword-routing: 16.67% -> 100% - rule-condition-logic: 33.33% -> 83.33% (remaining failure is unrelated) Fixes #713 Signed-off-by: Srinivas A <56465971+srini-abhiram@users.noreply.github.com>
1 parent c3ce62e commit 14285ed

File tree

4 files changed

+48
-20
lines changed

4 files changed

+48
-20
lines changed

src/semantic-router/pkg/extproc/req_filter_cache.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ func (r *OpenAIRouter) handleCaching(ctx *RequestContext, categoryName string) (
6060
} else if found {
6161
// Mark this request as a cache hit
6262
ctx.VSRCacheHit = true
63+
64+
// Set VSR decision context even for cache hits so headers are populated
65+
// The categoryName passed here is the decision name from classification
66+
if categoryName != "" {
67+
ctx.VSRSelectedDecisionName = categoryName
68+
}
69+
6370
// Log cache hit
6471
logging.LogEvent("cache_hit", map[string]interface{}{
6572
"request_id": ctx.RequestID,
@@ -69,7 +76,7 @@ func (r *OpenAIRouter) handleCaching(ctx *RequestContext, categoryName string) (
6976
"threshold": threshold,
7077
})
7178
// Return immediate response from cache
72-
response := http.CreateCacheHitResponse(cachedResponse, ctx.ExpectStreamingResponse)
79+
response := http.CreateCacheHitResponse(cachedResponse, ctx.ExpectStreamingResponse, categoryName)
7380
ctx.TraceContext = spanCtx
7481
return response, true
7582
}

src/semantic-router/pkg/k8s/reconciler.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,15 @@ func (r *Reconciler) validateAndUpdate(ctx context.Context, pool *v1alpha1.Intel
259259
// Create new config by merging with static config
260260
newConfig := *r.staticConfig
261261
newConfig.BackendModels = *backendModels
262-
newConfig.IntelligentRouting = *intelligentRouting
262+
263+
// Copy IntelligentRouting fields explicitly (since it's embedded with ,inline in YAML)
264+
// Assigning the whole struct doesn't work correctly with embedded structs
265+
newConfig.KeywordRules = intelligentRouting.KeywordRules
266+
newConfig.EmbeddingRules = intelligentRouting.EmbeddingRules
267+
newConfig.Categories = intelligentRouting.Categories
268+
newConfig.Decisions = intelligentRouting.Decisions
269+
newConfig.Strategy = intelligentRouting.Strategy
270+
newConfig.ReasoningConfig = intelligentRouting.ReasoningConfig
263271

264272
// Call update callback
265273
if r.onConfigUpdate != nil {

src/semantic-router/pkg/utils/http/response.go

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ func CreateJailbreakViolationResponse(jailbreakType string, confidence float32,
233233
}
234234

235235
// CreateCacheHitResponse creates an immediate response from cache
236-
func CreateCacheHitResponse(cachedResponse []byte, isStreaming bool) *ext_proc.ProcessingResponse {
236+
func CreateCacheHitResponse(cachedResponse []byte, isStreaming bool, vsrDecisionName string) *ext_proc.ProcessingResponse {
237237
var responseBody []byte
238238
var contentType string
239239

@@ -283,25 +283,38 @@ func CreateCacheHitResponse(cachedResponse []byte, isStreaming bool) *ext_proc.P
283283
responseBody = cachedResponse
284284
}
285285

286+
// Build headers including VSR decision headers for cache hits
287+
setHeaders := []*core.HeaderValueOption{
288+
{
289+
Header: &core.HeaderValue{
290+
Key: "content-type",
291+
RawValue: []byte(contentType),
292+
},
293+
},
294+
{
295+
Header: &core.HeaderValue{
296+
Key: headers.VSRCacheHit,
297+
RawValue: []byte("true"),
298+
},
299+
},
300+
}
301+
302+
// Add VSR decision header if provided
303+
if vsrDecisionName != "" {
304+
setHeaders = append(setHeaders, &core.HeaderValueOption{
305+
Header: &core.HeaderValue{
306+
Key: headers.VSRSelectedDecision,
307+
RawValue: []byte(vsrDecisionName),
308+
},
309+
})
310+
}
311+
286312
immediateResponse := &ext_proc.ImmediateResponse{
287313
Status: &typev3.HttpStatus{
288314
Code: typev3.StatusCode_OK,
289315
},
290316
Headers: &ext_proc.HeaderMutation{
291-
SetHeaders: []*core.HeaderValueOption{
292-
{
293-
Header: &core.HeaderValue{
294-
Key: "content-type",
295-
RawValue: []byte(contentType),
296-
},
297-
},
298-
{
299-
Header: &core.HeaderValue{
300-
Key: headers.VSRCacheHit,
301-
RawValue: []byte("true"),
302-
},
303-
},
304-
},
317+
SetHeaders: setHeaders,
305318
},
306319
Body: responseBody,
307320
}

src/semantic-router/pkg/utils/http/response_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestCreateCacheHitResponse_NonStreaming(t *testing.T) {
3838
}
3939

4040
// Test non-streaming response
41-
response := CreateCacheHitResponse(cachedResponse, false)
41+
response := CreateCacheHitResponse(cachedResponse, false, "test_decision")
4242

4343
// Verify response structure
4444
if response == nil {
@@ -121,7 +121,7 @@ func TestCreateCacheHitResponse_Streaming(t *testing.T) {
121121
}
122122

123123
// Test streaming response
124-
response := CreateCacheHitResponse(cachedResponse, true)
124+
response := CreateCacheHitResponse(cachedResponse, true, "test_decision")
125125

126126
// Verify response structure
127127
if response == nil {
@@ -226,7 +226,7 @@ func TestCreateCacheHitResponse_StreamingWithInvalidJSON(t *testing.T) {
226226
// Test with invalid JSON
227227
invalidJSON := []byte("invalid json")
228228

229-
response := CreateCacheHitResponse(invalidJSON, true)
229+
response := CreateCacheHitResponse(invalidJSON, true, "")
230230

231231
// Verify response structure
232232
if response == nil {

0 commit comments

Comments
 (0)