Skip to content

Commit 644f846

Browse files
handle old versions of redis
1 parent eda1379 commit 644f846

File tree

4 files changed

+84
-41
lines changed

4 files changed

+84
-41
lines changed

redisvl/extensions/router/semantic.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pydantic.v1 import BaseModel, Field, PrivateAttr
66
from redis import Redis
77
from redis.commands.search.aggregation import AggregateRequest, AggregateResult, Reducer
8+
from redis.exceptions import ResponseError
89

910
from redisvl.extensions.router.schema import (
1011
DistanceAggregationMethod,
@@ -275,10 +276,19 @@ def _classify(
275276
aggregate_request = self._build_aggregate_request(
276277
vector_range_query, aggregation_method, max_k=1
277278
)
278-
route_matches: AggregateResult = self._index.client.ft( # type: ignore
279-
self._index.name
280-
).aggregate(aggregate_request, vector_range_query.params)
281-
return [self._process_route(route_match) for route_match in route_matches.rows]
279+
try:
280+
route_matches: AggregateResult = self._index.client.ft( # type: ignore
281+
self._index.name
282+
).aggregate(aggregate_request, vector_range_query.params)
283+
return [
284+
self._process_route(route_match) for route_match in route_matches.rows
285+
]
286+
except ResponseError as e:
287+
if "VSS is not yet supported on FT.AGGREGATE" in str(e):
288+
raise RuntimeError(
289+
"Semantic routing is only available on Redis version 7.x.x or greater"
290+
)
291+
raise e
282292

283293
def _classify_many(
284294
self,
@@ -307,10 +317,19 @@ def _classify_many(
307317
aggregate_request = self._build_aggregate_request(
308318
vector_range_query, aggregation_method, max_k
309319
)
310-
route_matches: AggregateResult = self._index.client.ft( # type: ignore
311-
self._index.name
312-
).aggregate(aggregate_request, vector_range_query.params)
313-
return [self._process_route(route_match) for route_match in route_matches.rows]
320+
try:
321+
route_matches: AggregateResult = self._index.client.ft( # type: ignore
322+
self._index.name
323+
).aggregate(aggregate_request, vector_range_query.params)
324+
return [
325+
self._process_route(route_match) for route_match in route_matches.rows
326+
]
327+
except ResponseError as e:
328+
if "VSS is not yet supported on FT.AGGREGATE" in str(e):
329+
raise RuntimeError(
330+
"Semantic routing is only available on Redis version 7.x.x or greater"
331+
)
332+
raise e
314333

315334
def _pass_threshold(self, route_match: Optional[RouteMatch]) -> bool:
316335
"""Check if a route match passes the distance threshold.

redisvl/redis/connection.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,35 @@
1818
from redisvl.version import __version__
1919

2020

21+
def compare_versions(version1, version2):
22+
"""
23+
Compare two Redis version strings numerically.
24+
25+
Parameters:
26+
version1 (str): The first version string (e.g., "7.2.4").
27+
version2 (str): The second version string (e.g., "6.2.1").
28+
29+
Returns:
30+
int: -1 if version1 < version2, 0 if version1 == version2, 1 if version1 > version2.
31+
"""
32+
v1_parts = list(map(int, version1.split(".")))
33+
v2_parts = list(map(int, version2.split(".")))
34+
35+
for v1, v2 in zip(v1_parts, v2_parts):
36+
if v1 < v2:
37+
return False
38+
elif v1 > v2:
39+
return True
40+
41+
# If the versions are equal so far, compare the lengths of the version parts
42+
if len(v1_parts) < len(v2_parts):
43+
return False
44+
elif len(v1_parts) > len(v2_parts):
45+
return True
46+
47+
return True
48+
49+
2150
def unpack_redis_modules(module_list: List[Dict[str, Any]]) -> Dict[str, Any]:
2251
"""Unpack a list of Redis modules pulled from the MODULES LIST command."""
2352
return {module["name"]: module["ver"] for module in module_list}

tests/integration/test_connection.py

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from redisvl.redis.connection import (
99
RedisConnectionFactory,
10+
compare_versions,
1011
convert_index_info_to_schema,
1112
get_address_from_env,
1213
unpack_redis_modules,
@@ -18,35 +19,6 @@
1819
EXPECTED_LIB_NAME = f"redis-py(redisvl_v{__version__})"
1920

2021

21-
def compare_versions(version1, version2):
22-
"""
23-
Compare two Redis version strings numerically.
24-
25-
Parameters:
26-
version1 (str): The first version string (e.g., "7.2.4").
27-
version2 (str): The second version string (e.g., "6.2.1").
28-
29-
Returns:
30-
int: -1 if version1 < version2, 0 if version1 == version2, 1 if version1 > version2.
31-
"""
32-
v1_parts = list(map(int, version1.split(".")))
33-
v2_parts = list(map(int, version2.split(".")))
34-
35-
for v1, v2 in zip(v1_parts, v2_parts):
36-
if v1 < v2:
37-
return False
38-
elif v1 > v2:
39-
return True
40-
41-
# If the versions are equal so far, compare the lengths of the version parts
42-
if len(v1_parts) < len(v2_parts):
43-
return False
44-
elif len(v1_parts) > len(v2_parts):
45-
return True
46-
47-
return True
48-
49-
5022
def test_get_address_from_env(redis_url):
5123
assert get_address_from_env() == redis_url
5224

tests/integration/test_semantic_router.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from redisvl.extensions.router import SemanticRouter
44
from redisvl.extensions.router.schema import Route, RoutingConfig
5+
from redisvl.redis.connection import compare_versions
56

67

78
@pytest.fixture
@@ -65,18 +66,30 @@ def test_get_non_existing_route(semantic_router):
6566

6667

6768
def test_single_query(semantic_router):
69+
redis_version = semantic_router._index.client.info()["redis_version"]
70+
if not compare_versions(redis_version, "7.0.0"):
71+
pytest.skip("Not using a late enough version of Redis")
72+
6873
match = semantic_router("hello")
6974
assert match.route is not None
7075
assert match.route.name == "greeting"
7176
assert match.distance <= semantic_router.route_thresholds["greeting"]
7277

7378

7479
def test_single_query_no_match(semantic_router):
80+
redis_version = semantic_router._index.client.info()["redis_version"]
81+
if not compare_versions(redis_version, "7.0.0"):
82+
pytest.skip("Not using a late enough version of Redis")
83+
7584
match = semantic_router("unknown_phrase")
7685
assert match.route is None
7786

7887

7988
def test_multiple_query(semantic_router):
89+
redis_version = semantic_router._index.client.info()["redis_version"]
90+
if not compare_versions(redis_version, "7.0.0"):
91+
pytest.skip("Not using a late enough version of Redis")
92+
8093
matches = semantic_router.route_many("hello", max_k=2)
8194
assert len(matches) > 0
8295
assert matches[0].route.name == "greeting"
@@ -90,13 +103,21 @@ def test_update_routing_config(semantic_router):
90103

91104

92105
def test_vector_query(semantic_router):
106+
redis_version = semantic_router._index.client.info()["redis_version"]
107+
if not compare_versions(redis_version, "7.0.0"):
108+
pytest.skip("Not using a late enough version of Redis")
109+
93110
vector = semantic_router.vectorizer.embed("goodbye")
94111
match = semantic_router(vector=vector)
95112
assert match.route is not None
96113
assert match.route.name == "farewell"
97114

98115

99116
def test_vector_query_no_match(semantic_router):
117+
redis_version = semantic_router._index.client.info()["redis_version"]
118+
if not compare_versions(redis_version, "7.0.0"):
119+
pytest.skip("Not using a late enough version of Redis")
120+
100121
vector = [
101122
0.0
102123
] * semantic_router.vectorizer.dims # Random vector unlikely to match any route
@@ -123,7 +144,9 @@ def test_additional_route(semantic_router):
123144
assert route.name == "politics"
124145
assert "political speech" in route.references
125146

126-
match = semantic_router("political speech")
127-
print(match, flush=True)
128-
assert match is not None
129-
assert match.route.name == "politics"
147+
redis_version = semantic_router._index.client.info()["redis_version"]
148+
if compare_versions(redis_version, "7.0.0"):
149+
match = semantic_router("political speech")
150+
print(match, flush=True)
151+
assert match is not None
152+
assert match.route.name == "politics"

0 commit comments

Comments
 (0)