Skip to content

fix(router): invert IP vector distance before comparing against a similarity threshold - #2716

Open
drivebyer wants to merge 6 commits into
mainfrom
fix/semantic-cache-inverted-hit-criterion
Open

fix(router): invert IP vector distance before comparing against a similarity threshold#2716
drivebyer wants to merge 6 commits into
mainfrom
fix/semantic-cache-inverted-hit-criterion

Conversation

@drivebyer

@drivebyer drivebyer commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Closes #2715

Purpose

RediSearch and valkey-search return vector_distance as a distance for all three metrics. For IP it is defined as d = 1 - u·v, so smaller means closer (Redis docs, valkey FT.CREATE). Four copies of the distance-to-similarity conversion returned that value unchanged for IP, while their callers keep results on similarity >= threshold. The comparison ran backwards: an identical query gives d = 0 and scored 0.0, an unrelated one gives d ≈ 1 and scored ≈ 1.0.

Three paths were affected: the semantic cache (Redis and Valkey), agentic memory retrieval, and vector-store/RAG retrieval.

The conversion now lives in pkg/utils/valkey, whose package doc already covers all three components, and the four call sites use it. The Redis backend also gains the uppercase metric-type normalization the other three already had, so the index that gets built and the formula that reads its distances back cannot disagree. COSINE and L2 already ordered correctly and are unchanged.

Module: Router

Test Plan

make agent-lint CHANGED_FILES="<the changed files>"
SKIP_VALKEY_TESTS=false VALKEY_HOST=localhost VALKEY_PORT=6380 make test-semantic-router

Three unit tests asserted the passthrough and are replaced by one shared table plus TestDistanceToSimilarityIsDecreasing, which fails for any branch where a larger distance scores as more similar. The Valkey integration tests for IP and L2 were written as if hit { assert.NotNil(...) } and passed either way. They now require the hit and assert the returned body. CI runs them with SKIP_VALKEY_TESTS: false, which is the regression gate for this change. No E2E case is added: every e2e/config/*.yaml uses backend_type: memory, and the integration tests cover this instead.

Test Result

  • make agent-lint: exit 0
  • Unit and Valkey integration tests pass against a live Valkey, re-run after merging main, which brought in partitionedKNNQuery from [Router][Dashboard] Add first-class Mixture-of-Models recipes #2741
  • Falsification: reverting the IP branch to return distance fails TestDistanceToSimilarityIsDecreasing/IP and TestValkeyCacheIntegration_IPMetricType with similarity=0.0000, threshold=0.5

Known gaps: TestValkeyCacheIntegration_UpdateWithResponse still shares the doc: keyspace and can miss on a dirty Valkey (newIsolatedValkeyCache added here is available if someone converts it later). An unset metric_type still builds a COSINE index read back as 1-d; unchanged here and deferred to the COSINE-scale discussion.

@drivebyer
drivebyer requested review from Xunzhuo and rootfs as code owners July 28, 2026 13:49
@netlify

netlify Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploy Preview for vllm-semantic-router ready!

Name Link
🔨 Latest commit 1359208
🔍 Latest deploy log https://app.netlify.com/projects/vllm-semantic-router/deploys/6a72be115f78b30008e90ea1
😎 Deploy Preview https://deploy-preview-2716--vllm-semantic-router.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

👥 vLLM Semantic Team Notification

The following members have been identified for the changed files in this PR and have been automatically assigned when their GitHub accounts are assignable in this repository:

📁 src/semantic-router

Owners: @FAUST-BENCHOU, @shraderdm, @drivebyer, @ramkrishs, @WUKUNTAI-0211, @AayushSaini101, @siloteemu
Files changed:

  • src/semantic-router/pkg/cache/redis_cache.go
  • src/semantic-router/pkg/cache/redis_cache_test.go
  • src/semantic-router/pkg/cache/valkey_cache.go
  • src/semantic-router/pkg/cache/valkey_cache_helpers.go
  • src/semantic-router/pkg/cache/valkey_cache_integration_test.go
  • src/semantic-router/pkg/cache/valkey_cache_test.go
  • src/semantic-router/pkg/memory/valkey_store_helpers.go
  • src/semantic-router/pkg/memory/valkey_store_test.go
  • src/semantic-router/pkg/routerreplay/recorder.go
  • src/semantic-router/pkg/utils/valkey/distance.go
  • src/semantic-router/pkg/utils/valkey/distance_test.go
  • src/semantic-router/pkg/vectorstore/valkey_backend_parse.go
  • src/semantic-router/pkg/vectorstore/valkey_backend_unit_test.go

vLLM Semantic Router

🎉 Thanks for your contributions!

This comment was automatically generated based on the OWNER files in the repository.

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

✅ Supply Chain Security Report — All Clear

Scanner Status Findings
AST Codebase Scan (Py, Go, JS/TS, Rust) 31 finding(s) — MEDIUM: 24 · LOW: 7
AST PR Diff Scan No issues detected
Regex Fallback Scan No issues detected

Scanned at 2026-08-05T02:46:25.302Z · View full workflow logs

@github-actions

Copy link
Copy Markdown
Contributor

Performance Benchmark Results

Component benchmarks completed with no regressions beyond thresholds.

Summary

  • Classification benchmarks: ✅
  • Decision engine benchmarks: ✅
  • Cache benchmarks: ✅
  • Looper family benchmarks: ✅
  • Regression gate: ✅ no regressions beyond thresholds

Details

See attached benchmark artifacts for detailed results and profiles.


Performance testing powered by vLLM Semantic Router

…ity threshold

RediSearch and valkey-search report vector_distance as a distance for all
three metrics: IP is defined as d = 1 - u*v, so smaller means closer. Four
copies of the conversion returned that value unchanged for IP while their
callers kept the result on `similarity >= threshold`, which runs the
comparison backwards. An identical query yields d = 0 and scored 0.0, below
every threshold, while an unrelated one yields d ~ 1 and scored ~1.0. The
semantic cache never reused a cached response, agentic memory never recalled
a stored memory, and the vector store dropped exact-match chunks.

Collapse the four copies into pkg/utils/valkey, whose package doc already
covers the vector store, semantic cache and agentic memory, and fix IP there
once. Normalize the Redis metric type at construction as the other three
backends already do, so the index that gets built and the formula used to
read its distances back cannot disagree.

COSINE and L2 are unaffected: their branches already inverted the ordering.

Three unit tests asserted the passthrough and are replaced by a shared table
plus a monotonicity guard that fails for any branch where a larger distance
scores as more similar. The Valkey integration tests for the IP and L2
metrics were written as `if hit { assert.NotNil(...) }`, passing whether or
not the cache hit; they now require the hit. Tests that assert which entry
came back get their own index, because the shared "doc:" keyspace is never
cleaned up and a TopK=1 search ranks against every document earlier tests
left behind.

Closes #2715

Signed-off-by: drivebyer <wuyangmuc@gmail.com>
@drivebyer
drivebyer force-pushed the fix/semantic-cache-inverted-hit-criterion branch from 40c3276 to 88eae76 Compare July 28, 2026 14:19
@drivebyer drivebyer changed the title [Router] invert IP vector distance before comparing against a similarity threshold fix(router): invert IP vector distance before comparing against a similarity threshold Aug 1, 2026
@drivebyer drivebyer added the bug Something isn't working label Aug 3, 2026
…corder

Deduplicate the request/response body capture blocks in AddRecord into a
single helper, keeping the semantics introduced in #2749 unchanged:
capture off drops the body, oversized bodies are truncated, and the
caller's truncated flag is preserved within the limit.

Follow-up to a post-merge review suggestion on #2749.

Signed-off-by: drivebyer <wuyangmuc@gmail.com>
@drivebyer
drivebyer force-pushed the fix/semantic-cache-inverted-hit-criterion branch from d92143a to 17c7fd8 Compare August 4, 2026 14:46
@mergify mergify Bot added the queued label Aug 5, 2026
@mergify

mergify Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-08-05 03:29 UTC · Rule: default · triggered by rule queue approved pull requests
  • 🟠 Checks running · in-place · dashboard
  • ⏳ Merge · ETA: 2026-08-05 05:42 UTC 🚀
Waiting for
  • any of:
    • check-skipped = test-and-build
    • check-success = test-and-build
  • any of: [🛡 GitHub branch protection]
    • check-neutral = Run pre-commit hooks check file lint
    • check-skipped = Run pre-commit hooks check file lint
    • check-success = Run pre-commit hooks check file lint
  • any of: [🛡 GitHub branch protection]
    • check-neutral = test-and-build
    • check-skipped = test-and-build
    • check-success = test-and-build
All merge conditions
Waiting for
  • any of:
    • check-skipped = test-and-build
    • check-success = test-and-build
  • any of: [🛡 GitHub branch protection]
    • check-neutral = Run pre-commit hooks check file lint
    • check-skipped = Run pre-commit hooks check file lint
    • check-success = Run pre-commit hooks check file lint
  • any of: [🛡 GitHub branch protection]
    • check-neutral = test-and-build
    • check-skipped = test-and-build
    • check-success = test-and-build
All queue conditions
  • any of [🔀 queue conditions]:
    • all of [📌 queue conditions of queue rule default]:
      • any of:
        • check-skipped = test-and-build
        • check-success = test-and-build
      • any of [🛡 GitHub branch protection]:
        • check-neutral = Run pre-commit hooks check file lint
        • check-skipped = Run pre-commit hooks check file lint
        • check-success = Run pre-commit hooks check file lint
      • any of [🛡 GitHub branch protection]:
        • check-neutral = test-and-build
        • check-skipped = test-and-build
        • check-success = test-and-build
      • #review-threads-unresolved = 0 [🛡 GitHub branch protection]
      • base = main
      • github-review-approved [🛡 GitHub branch protection]
      • any of:
        • check-skipped = Lint
        • check-success = Lint
      • any of:
        • check-skipped = Unit Tests
        • check-success = Unit Tests
      • any of:
        • check-skipped = Verify Manifests
        • check-success = Verify Manifests
      • any of:
        • check-skipped = Validate OLM Bundle
        • check-success = Validate OLM Bundle
  • -closed [📌 queue requirement]
  • -conflict [📌 queue requirement]
  • -draft [📌 queue requirement]
  • any of [📌 queue -> configuration change requirements]:
    • -mergify-configuration-changed
    • check-success = Configuration changed
  • any of [📌 queue requirement]:
    • check-neutral = Mergify Merge Protections
    • check-skipped = Mergify Merge Protections
    • check-success = Mergify Merge Protections

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working queued

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Inverted cache-hit criterion: IP vector distance is treated as a similarity

7 participants