Skip to content

Commit 8c4c742

Browse files
committed
LangCacheWrapper: make distance_threshold scale explicit (normalized|redis) and convert accordingly\n\n- Add distance_scale option to LangCacheWrapper (default: normalized)\n- Convert distance_threshold to similarity_threshold based on scale\n- Convert returned similarity to vector_distance based on scale\n- No change to SemanticCache behavior; opened issue #407 to track threshold inconsistencies there
1 parent eba7e3f commit 8c4c742

File tree

1 file changed

+30
-12
lines changed

1 file changed

+30
-12
lines changed

redisvl/extensions/cache/llm/langcache.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
managed service via the langcache Python SDK.
55
"""
66

7-
from typing import Any, Dict, List, Optional
7+
from typing import Any, Dict, List, Literal, Optional
88

99
from redisvl.extensions.cache.llm.base import BaseLLMCache
1010
from redisvl.extensions.cache.llm.schema import CacheHit
1111
from redisvl.query.filter import FilterExpression
1212
from redisvl.utils.log import get_logger
13+
from redisvl.utils.utils import denorm_cosine_distance, norm_cosine_distance
1314

1415
logger = get_logger(__name__)
1516

@@ -62,6 +63,7 @@ def __init__(
6263
ttl: Optional[int] = None,
6364
use_exact_search: bool = True,
6465
use_semantic_search: bool = True,
66+
distance_scale: Literal["normalized", "redis"] = "normalized",
6567
**kwargs,
6668
):
6769
"""Initialize a LangCache wrapper.
@@ -74,12 +76,19 @@ def __init__(
7476
ttl (Optional[int]): Time-to-live for cache entries in seconds.
7577
use_exact_search (bool): Whether to use exact matching. Defaults to True.
7678
use_semantic_search (bool): Whether to use semantic search. Defaults to True.
79+
distance_scale (str): Threshold scale for distance_threshold:
80+
- "normalized": 0–1 semantic distance (lower is better)
81+
- "redis": Redis COSINE distance 0–2 (lower is better)
7782
**kwargs: Additional arguments (ignored for compatibility).
7883
7984
Raises:
8085
ImportError: If the langcache package is not installed.
8186
ValueError: If cache_id or api_key is not provided.
8287
"""
88+
if distance_scale not in {"normalized", "redis"}:
89+
raise ValueError("distance_scale must be 'normalized' or 'redis'")
90+
self._distance_scale = distance_scale
91+
8392
if not LANGCACHE_AVAILABLE:
8493
raise ImportError(
8594
"The langcache package is required to use LangCacheWrapper. "
@@ -128,10 +137,13 @@ def _convert_to_cache_hit(self, result: Dict[str, Any]) -> CacheHit:
128137
# Extract attributes (metadata) from the result
129138
attributes = result.get("attributes", {})
130139

131-
# LangCache returns similarity (0-1, higher is better)
132-
# Convert to distance (lower is better) for consistency
140+
# LangCache returns similarity in [0,1] (higher is better)
133141
similarity = result.get("similarity", 0.0)
134-
distance = 1.0 - similarity if similarity else 0.0
142+
# Convert to the configured distance scale (lower is better)
143+
if self._distance_scale == "redis":
144+
distance = denorm_cosine_distance(similarity) # -> [0,2]
145+
else:
146+
distance = 1.0 - similarity # normalized [0,1]
135147

136148
return CacheHit(
137149
entry_id=result.get("id", ""),
@@ -181,12 +193,15 @@ def check(
181193
if filter_expression is not None:
182194
logger.warning("LangCache does not support filter expressions")
183195

184-
# Convert distance_threshold to similarity_threshold
185-
# Distance: lower is better (0.0 = exact match)
186-
# Similarity: higher is better (1.0 = exact match)
196+
# Convert distance threshold to similarity threshold according to configured scale
187197
similarity_threshold = None
188198
if distance_threshold is not None:
189-
similarity_threshold = 1.0 - distance_threshold
199+
if self._distance_scale == "redis":
200+
similarity_threshold = norm_cosine_distance(
201+
distance_threshold
202+
) # [0,2] -> [0,1]
203+
else:
204+
similarity_threshold = 1.0 - float(distance_threshold) # [0,1] -> [0,1]
190205

191206
# Search using the LangCache client
192207
# The client itself is the context manager
@@ -256,12 +271,15 @@ async def acheck(
256271
if filter_expression is not None:
257272
logger.warning("LangCache does not support filter expressions")
258273

259-
# Convert distance_threshold to similarity_threshold
260-
# Distance: lower is better (0.0 = exact match)
261-
# Similarity: higher is better (1.0 = exact match)
274+
# Convert distance threshold to similarity threshold according to configured scale
262275
similarity_threshold = None
263276
if distance_threshold is not None:
264-
similarity_threshold = 1.0 - distance_threshold
277+
if self._distance_scale == "redis":
278+
similarity_threshold = norm_cosine_distance(
279+
distance_threshold
280+
) # [0,2] -> [0,1]
281+
else:
282+
similarity_threshold = 1.0 - float(distance_threshold) # [0,1] -> [0,1]
265283

266284
# Search using the LangCache client (async)
267285
# The client itself is the context manager

0 commit comments

Comments
 (0)