Skip to content

Commit 5787ddd

Browse files
author
Nitin Kanukolanu
committed
Simplify version validation
1 parent 552376f commit 5787ddd

File tree

11 files changed

+60
-228
lines changed

11 files changed

+60
-228
lines changed

docs/user_guide/01_getting_started.ipynb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@
761761
],
762762
"metadata": {
763763
"kernelspec": {
764-
"display_name": ".venv",
764+
"display_name": "Python 3 (ipykernel)",
765765
"language": "python",
766766
"name": "python3"
767767
},
@@ -775,10 +775,9 @@
775775
"name": "python",
776776
"nbconvert_exporter": "python",
777777
"pygments_lexer": "ipython3",
778-
"version": "3.13.2"
779-
},
780-
"orig_nbformat": 4
778+
"version": "3.12.6"
779+
}
781780
},
782781
"nbformat": 4,
783-
"nbformat_minor": 2
782+
"nbformat_minor": 4
784783
}

docs/user_guide/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ User guides provide helpful resources for using RedisVL and its different compon
2222
07_message_history
2323
08_semantic_router
2424
```
25+
# TODO: Nitin Add the new user guide for SVS-VAMANA

redisvl/exceptions.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,17 @@ class RedisModuleVersionError(RedisVLError):
3737
"""Error when Redis or module versions are incompatible with requested features."""
3838

3939
@classmethod
40-
def for_svs_vamana(cls, capabilities, min_redis_version: str):
40+
def for_svs_vamana(cls, min_redis_version: str):
4141
"""Create error for unsupported SVS-VAMANA.
4242
4343
Args:
44-
capabilities: VectorSupport instance with version info
4544
min_redis_version: Minimum required Redis version
4645
4746
Returns:
4847
RedisModuleVersionError with formatted message
4948
"""
5049
message = (
5150
f"SVS-VAMANA requires Redis >= {min_redis_version} with RediSearch >= 2.8.10. "
52-
f"Current: Redis {capabilities.redis_version}, "
53-
f"RediSearch {capabilities.search_version_str}, "
54-
f"SearchLight {capabilities.searchlight_version_str}. "
5551
f"Options: 1) Upgrade Redis Stack, "
5652
f"2) Use algorithm='hnsw' or 'flat', "
5753
f"3) Remove compression parameters"

redisvl/index/index.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@
8383
from redisvl.query.filter import FilterExpression
8484
from redisvl.redis.connection import (
8585
RedisConnectionFactory,
86-
check_vector_capabilities,
87-
check_vector_capabilities_async,
8886
convert_index_info_to_schema,
87+
supports_svs,
88+
supports_svs_async,
8989
)
9090
from redisvl.redis.constants import SVS_MIN_REDIS_VERSION
9191
from redisvl.schema import IndexSchema, StorageType
@@ -552,10 +552,8 @@ def _check_svs_support(self) -> None:
552552
Raises:
553553
RedisModuleVersionError: If SVS-VAMANA requirements are not met.
554554
"""
555-
caps = check_vector_capabilities(self._redis_client)
556-
557-
if not caps.svs_vamana_supported:
558-
raise RedisModuleVersionError.for_svs_vamana(caps, SVS_MIN_REDIS_VERSION)
555+
if not supports_svs(self._redis_client):
556+
raise RedisModuleVersionError.for_svs_vamana(SVS_MIN_REDIS_VERSION)
559557

560558
def create(self, overwrite: bool = False, drop: bool = False) -> None:
561559
"""Create an index in Redis with the current schema and properties.
@@ -1335,10 +1333,8 @@ async def _check_svs_support_async(self) -> None:
13351333
RedisModuleVersionError: If SVS-VAMANA requirements are not met.
13361334
"""
13371335
client = await self._get_client()
1338-
caps = await check_vector_capabilities_async(client)
1339-
1340-
if not caps.svs_vamana_supported:
1341-
raise RedisModuleVersionError.for_svs_vamana(caps, SVS_MIN_REDIS_VERSION)
1336+
if not await supports_svs_async(client):
1337+
raise RedisModuleVersionError.for_svs_vamana(SVS_MIN_REDIS_VERSION)
13421338

13431339
async def create(self, overwrite: bool = False, drop: bool = False) -> None:
13441340
"""Asynchronously create an index in Redis with the current schema

redisvl/redis/connection.py

Lines changed: 30 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from redisvl.redis.constants import (
2020
REDIS_URL_ENV_VAR,
2121
SVS_MIN_REDIS_VERSION,
22-
SVS_REQUIRED_MODULES,
22+
SVS_MIN_SEARCH_VERSION,
2323
)
2424
from redisvl.redis.utils import convert_bytes, is_cluster_url
2525
from redisvl.types import AsyncRedisClient, RedisClient, SyncRedisClient
@@ -72,16 +72,16 @@ def _strip_cluster_from_url_and_kwargs(
7272
return cleaned_url, cleaned_kwargs
7373

7474

75-
def compare_versions(version1: str, version2: str):
75+
def is_version_gte(version1: str, version2: str) -> bool:
7676
"""
77-
Compare two Redis version strings numerically.
77+
Check if version1 >= version2.
7878
7979
Parameters:
80-
version1 (str): The first version string (e.g., "7.2.4").
81-
version2 (str): The second version string (e.g., "6.2.1").
80+
version1 (str): The first version string (e.g., "7.2.4").
81+
version2 (str): The second version string (e.g., "6.2.1").
8282
8383
Returns:
84-
int: -1 if version1 < version2, 0 if version1 == version2, 1 if version1 > version2.
84+
bool: True if version1 >= version2, False otherwise.
8585
"""
8686
v1_parts = list(map(int, version1.split(".")))
8787
v2_parts = list(map(int, version2.split(".")))
@@ -106,44 +106,14 @@ def unpack_redis_modules(module_list: List[Dict[str, Any]]) -> Dict[str, Any]:
106106
return {module["name"]: module["ver"] for module in module_list}
107107

108108

109-
@dataclass
110-
class VectorSupport:
111-
"""Redis server capabilities for vector operations."""
112-
113-
redis_version: str
114-
search_version: int
115-
searchlight_version: int
116-
svs_vamana_supported: bool
117-
118-
@property
119-
def search_version_str(self) -> str:
120-
"""Format search module version as string."""
121-
return format_module_version(self.search_version)
122-
123-
@property
124-
def searchlight_version_str(self) -> str:
125-
"""Format searchlight module version as string."""
126-
return format_module_version(self.searchlight_version)
127-
128-
129-
def format_module_version(version: int) -> str:
130-
"""Format module version from integer (20810) to string (2.8.10)."""
131-
if version == 0:
132-
return "not installed"
133-
major = version // 10000
134-
minor = (version % 10000) // 100
135-
patch = version % 100
136-
return f"{major}.{minor}.{patch}"
137-
138-
139-
def check_vector_capabilities(client: SyncRedisClient) -> VectorSupport:
140-
"""Check Redis server capabilities for vector features.
109+
def supports_svs(client: SyncRedisClient) -> bool:
110+
"""Check if Redis server supports SVS-VAMANA.
141111
142112
Args:
143113
client: Sync Redis client instance
144114
145115
Returns:
146-
VectorSupport with version info and supported features
116+
True if SVS-VAMANA is supported, False otherwise
147117
"""
148118
info = client.info("server") # type: ignore[union-attr]
149119
redis_version = info.get("redis_version", "0.0.0") # type: ignore[union-attr]
@@ -153,25 +123,26 @@ def check_vector_capabilities(client: SyncRedisClient) -> VectorSupport:
153123
searchlight_ver = modules.get("searchlight", 0)
154124

155125
# Check if SVS-VAMANA requirements are met
156-
redis_ok = compare_versions(redis_version, SVS_MIN_REDIS_VERSION)
157-
modules_ok = search_ver >= 20810 or searchlight_ver >= 20810
158-
159-
return VectorSupport(
160-
redis_version=redis_version,
161-
search_version=search_ver,
162-
searchlight_version=searchlight_ver,
163-
svs_vamana_supported=redis_ok and modules_ok,
126+
redis_ok = is_version_gte(redis_version, SVS_MIN_REDIS_VERSION)
127+
128+
# Check either search or searchlight module (only one is typically installed)
129+
# RediSearch is the open-source module, SearchLight is the enterprise version
130+
modules_ok = (
131+
search_ver >= SVS_MIN_SEARCH_VERSION
132+
or searchlight_ver >= SVS_MIN_SEARCH_VERSION
164133
)
165134

135+
return redis_ok and modules_ok
136+
166137

167-
async def check_vector_capabilities_async(client: AsyncRedisClient) -> VectorSupport:
168-
"""Async version of check_vector_capabilities.
138+
async def supports_svs_async(client: AsyncRedisClient) -> bool:
139+
"""Async version of _supports_svs.
169140
170141
Args:
171142
client: Async Redis client instance
172143
173144
Returns:
174-
VectorSupport with version info and supported features
145+
True if SVS-VAMANA is supported, False otherwise
175146
"""
176147
info = await client.info("server") # type: ignore[union-attr]
177148
redis_version = info.get("redis_version", "0.0.0") # type: ignore[union-attr]
@@ -181,16 +152,17 @@ async def check_vector_capabilities_async(client: AsyncRedisClient) -> VectorSup
181152
searchlight_ver = modules.get("searchlight", 0)
182153

183154
# Check if SVS-VAMANA requirements are met
184-
redis_ok = compare_versions(redis_version, SVS_MIN_REDIS_VERSION)
185-
modules_ok = search_ver >= 20810 or searchlight_ver >= 20810
186-
187-
return VectorSupport(
188-
redis_version=redis_version,
189-
search_version=search_ver,
190-
searchlight_version=searchlight_ver,
191-
svs_vamana_supported=redis_ok and modules_ok,
155+
redis_ok = is_version_gte(redis_version, SVS_MIN_REDIS_VERSION)
156+
157+
# Check either search or searchlight module (only one is typically installed)
158+
# RediSearch is the open-source module, SearchLight is the enterprise version
159+
modules_ok = (
160+
search_ver >= SVS_MIN_SEARCH_VERSION
161+
or searchlight_ver >= SVS_MIN_SEARCH_VERSION
192162
)
193163

164+
return redis_ok and modules_ok
165+
194166

195167
def get_address_from_env() -> str:
196168
"""Get Redis URL from environment variable."""

redisvl/redis/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
# Minimum Redis version for SVS-VAMANA
1414
SVS_MIN_REDIS_VERSION = "8.2.0"
15+
# Minimum search module version for SVS-VAMANA (2.8.10)
16+
SVS_MIN_SEARCH_VERSION = 20810
1517

1618
# default tag separator
1719
REDIS_TAG_SEPARATOR = ","

redisvl/utils/migration.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
1111

1212
from redisvl.exceptions import RedisModuleVersionError
13-
from redisvl.redis.connection import check_vector_capabilities
13+
from redisvl.redis.connection import supports_svs
14+
from redisvl.redis.constants import SVS_MIN_REDIS_VERSION
1415
from redisvl.schema import IndexSchema
1516
from redisvl.utils.compression import CompressionAdvisor
1617
from redisvl.utils.log import get_logger
@@ -101,11 +102,8 @@ def progress(current, total):
101102
from redisvl.query.filter import FilterExpression
102103

103104
# Check SVS-VAMANA support
104-
caps = check_vector_capabilities(old_index._redis_client)
105-
if not caps.svs_vamana_supported:
106-
raise RedisModuleVersionError.for_svs_vamana(
107-
caps.redis_version, caps.search_version
108-
)
105+
if not supports_svs(old_index._redis_client):
106+
raise RedisModuleVersionError.for_svs_vamana(SVS_MIN_REDIS_VERSION)
109107

110108
# Find vector fields in the old schema
111109
vector_fields = [
@@ -294,15 +292,12 @@ async def progress(current, total):
294292
from redisvl.index import AsyncSearchIndex
295293
from redisvl.query import FilterQuery
296294
from redisvl.query.filter import FilterExpression
297-
from redisvl.redis.connection import check_vector_capabilities_async
295+
from redisvl.redis.connection import supports_svs_async
298296

299297
# Check SVS-VAMANA support
300298
client = await old_index._get_client()
301-
caps = await check_vector_capabilities_async(client)
302-
if not caps.svs_vamana_supported:
303-
raise RedisModuleVersionError.for_svs_vamana(
304-
caps.redis_version, caps.search_version
305-
)
299+
if not await supports_svs_async(client):
300+
raise RedisModuleVersionError.for_svs_vamana(SVS_MIN_REDIS_VERSION)
306301

307302
# Find vector fields
308303
vector_fields = [

tests/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from testcontainers.compose import DockerCompose
88

99
from redisvl.index.index import AsyncSearchIndex, SearchIndex
10-
from redisvl.redis.connection import RedisConnectionFactory, compare_versions
10+
from redisvl.redis.connection import RedisConnectionFactory, is_version_gte
1111
from redisvl.redis.utils import array_to_buffer
1212
from redisvl.utils.vectorize import HFTextVectorizer
1313

@@ -609,7 +609,7 @@ def skip_if_redis_version_below(client, min_version: str, message: str = None):
609609
message: Custom skip message
610610
"""
611611
redis_version = get_redis_version(client)
612-
if not compare_versions(redis_version, min_version):
612+
if not is_version_gte(redis_version, min_version):
613613
skip_msg = message or f"Redis version {redis_version} < {min_version} required"
614614
pytest.skip(skip_msg)
615615

@@ -626,7 +626,7 @@ async def skip_if_redis_version_below_async(
626626
message: Custom skip message
627627
"""
628628
redis_version = await get_redis_version_async(client)
629-
if not compare_versions(redis_version, min_version):
629+
if not is_version_gte(redis_version, min_version):
630630
skip_msg = message or f"Redis version {redis_version} < {min_version} required"
631631
pytest.skip(skip_msg)
632632

tests/integration/test_semantic_router.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
Route,
1212
RoutingConfig,
1313
)
14-
from redisvl.redis.connection import compare_versions
14+
from redisvl.redis.connection import is_version_gte
1515
from tests.conftest import skip_if_no_redisearch, skip_if_redis_version_below
1616

1717

@@ -159,7 +159,7 @@ def test_add_route(semantic_router):
159159
assert "political speech" in route.references
160160

161161
redis_version = semantic_router._index.client.info()["redis_version"]
162-
if compare_versions(redis_version, "7.0.0"):
162+
if is_version_gte(redis_version, "7.0.0"):
163163
match = semantic_router("political speech")
164164
print(match, flush=True)
165165
assert match is not None

tests/integration/test_svs_integration.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from redisvl.exceptions import RedisModuleVersionError
2626
from redisvl.index import SearchIndex
2727
from redisvl.query import VectorQuery
28-
from redisvl.redis.connection import check_vector_capabilities
28+
from redisvl.redis.connection import supports_svs
2929
from redisvl.redis.utils import array_to_buffer
3030
from redisvl.schema import IndexSchema
3131
from redisvl.utils import CompressionAdvisor
@@ -138,13 +138,10 @@ class TestSVSCapabilityDetection:
138138

139139
def test_check_svs_capabilities(self, client):
140140
"""Test that SVS-VAMANA is supported on the test Redis instance."""
141-
caps = check_vector_capabilities(client)
142-
143141
# These tests require Redis 8.2+ with RediSearch 2.8.10+
144-
assert caps.svs_vamana_supported is True, (
145-
f"SVS-VAMANA not supported. "
146-
f"Redis: {caps.redis_version}, "
147-
f"RediSearch: {caps.search_version}"
142+
assert supports_svs(client) is True, (
143+
"SVS-VAMANA not supported. "
144+
"Requires Redis >= 8.2.0 with RediSearch >= 2.8.10"
148145
)
149146

150147

0 commit comments

Comments
 (0)