Skip to content

Commit 3b907a5

Browse files
initial refactor of caching extensions
1 parent ab3e711 commit 3b907a5

File tree

15 files changed

+1932
-947
lines changed

15 files changed

+1932
-947
lines changed

redisvl/extensions/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""
2+
Redis Vector Library Extensions
3+
4+
These extensions provide additional functionality on top of the
5+
core RedisVL functionality.
6+
"""
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""
2+
Redis Vector Library Cache Extensions
3+
4+
This module provides caching functionality for Redis Vector Library,
5+
including both embedding caches and LLM response caches.
6+
"""
7+
8+
from redisvl.extensions.cache.base import BaseCache
9+
from redisvl.extensions.cache.embeddings import EmbeddingsCache
10+
from redisvl.extensions.cache.llm import SemanticCache
11+
12+
__all__ = ["BaseCache", "EmbeddingsCache", "SemanticCache"]

redisvl/extensions/cache/base.py

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
"""Base cache interface for RedisVL.
2+
3+
This module defines the abstract base cache interface that is implemented by
4+
specific cache types such as LLM caches and embedding caches.
5+
"""
6+
7+
from typing import Any, Dict, Optional
8+
9+
from redis import Redis
10+
from redis.asyncio import Redis as AsyncRedis
11+
12+
13+
class BaseCache:
14+
"""Base abstract cache interface for all RedisVL caches.
15+
16+
This class defines common functionality shared by all cache implementations,
17+
including TTL management, connection handling, and basic cache operations.
18+
"""
19+
20+
def __init__(
21+
self,
22+
name: str,
23+
ttl: Optional[int] = None,
24+
redis_client: Optional[Redis] = None,
25+
redis_url: str = "redis://localhost:6379",
26+
connection_kwargs: Dict[str, Any] = {},
27+
):
28+
"""Initialize a base cache.
29+
30+
Args:
31+
name (str): The name of the cache.
32+
ttl (Optional[int], optional): The time-to-live for records cached
33+
in Redis. Defaults to None.
34+
redis_client (Optional[Redis], optional): A redis client connection instance.
35+
Defaults to None.
36+
redis_url (str, optional): The redis url. Defaults to redis://localhost:6379.
37+
connection_kwargs (Dict[str, Any]): The connection arguments
38+
for the redis client. Defaults to empty {}.
39+
"""
40+
self.name = name
41+
self._ttl: Optional[int] = None
42+
self.set_ttl(ttl)
43+
44+
self.redis_kwargs = {
45+
"redis_client": redis_client,
46+
"redis_url": redis_url,
47+
"connection_kwargs": connection_kwargs,
48+
}
49+
50+
if redis_client:
51+
self._owns_redis_client = False
52+
self._redis_client = redis_client
53+
else:
54+
self._owns_redis_client = True
55+
self._redis_client = None
56+
57+
def _get_prefix(self) -> str:
58+
"""Get the key prefix for Redis keys.
59+
60+
Returns:
61+
str: The prefix to use for Redis keys.
62+
"""
63+
return f"{self.name}:"
64+
65+
def _make_key(self, entry_id: str) -> str:
66+
"""Generate a full Redis key for the given entry ID.
67+
68+
Args:
69+
entry_id (str): The unique entry ID.
70+
71+
Returns:
72+
str: The full Redis key including prefix.
73+
"""
74+
return f"{self._get_prefix()}{entry_id}"
75+
76+
@property
77+
def ttl(self) -> Optional[int]:
78+
"""The default TTL, in seconds, for entries in the cache."""
79+
return self._ttl
80+
81+
def set_ttl(self, ttl: Optional[int] = None) -> None:
82+
"""Set the default TTL, in seconds, for entries in the cache.
83+
84+
Args:
85+
ttl (Optional[int], optional): The optional time-to-live expiration
86+
for the cache, in seconds.
87+
88+
Raises:
89+
ValueError: If the time-to-live value is not an integer.
90+
"""
91+
if ttl:
92+
if not isinstance(ttl, int):
93+
raise ValueError(f"TTL must be an integer value, got {ttl}")
94+
self._ttl = int(ttl)
95+
else:
96+
self._ttl = None
97+
98+
def _get_redis_client(self) -> Redis:
99+
"""Get or create a Redis client.
100+
101+
Returns:
102+
Redis: A Redis client instance.
103+
"""
104+
if self._redis_client is None:
105+
self._redis_client = Redis.from_url(
106+
self.redis_kwargs["redis_url"], **self.redis_kwargs["connection_kwargs"]
107+
)
108+
return self._redis_client
109+
110+
async def _get_async_redis_client(self) -> AsyncRedis:
111+
"""Get or create an async Redis client.
112+
113+
Returns:
114+
AsyncRedis: An async Redis client instance.
115+
"""
116+
if not hasattr(self, "_async_redis_client") or self._async_redis_client is None:
117+
self._async_redis_client = AsyncRedis.from_url(
118+
self.redis_kwargs["redis_url"], **self.redis_kwargs["connection_kwargs"]
119+
)
120+
return self._async_redis_client
121+
122+
def expire(self, key: str, ttl: Optional[int] = None) -> None:
123+
"""Set or refresh the expiration time for a key in the cache.
124+
125+
Args:
126+
key (str): The Redis key to set the expiration on.
127+
ttl (Optional[int], optional): The time-to-live in seconds. If None,
128+
uses the default TTL configured for this cache instance.
129+
Defaults to None.
130+
131+
Note:
132+
If neither the provided TTL nor the default TTL is set (both are None),
133+
this method will have no effect.
134+
"""
135+
_ttl = ttl if ttl is not None else self._ttl
136+
if _ttl:
137+
client = self._get_redis_client()
138+
client.expire(key, _ttl)
139+
140+
async def aexpire(self, key: str, ttl: Optional[int] = None) -> None:
141+
"""Asynchronously set or refresh the expiration time for a key in the cache.
142+
143+
Args:
144+
key (str): The Redis key to set the expiration on.
145+
ttl (Optional[int], optional): The time-to-live in seconds. If None,
146+
uses the default TTL configured for this cache instance.
147+
Defaults to None.
148+
149+
Note:
150+
If neither the provided TTL nor the default TTL is set (both are None),
151+
this method will have no effect.
152+
"""
153+
_ttl = ttl if ttl is not None else self._ttl
154+
if _ttl:
155+
client = await self._get_async_redis_client()
156+
await client.expire(key, _ttl)
157+
158+
def clear(self) -> None:
159+
"""Clear the cache of all keys."""
160+
client = self._get_redis_client()
161+
prefix = self._get_prefix()
162+
163+
# Scan for all keys with our prefix
164+
cursor = "0"
165+
while cursor != 0:
166+
cursor, keys = client.scan(cursor=cursor, match=f"{prefix}*", count=100)
167+
if keys:
168+
client.delete(*keys)
169+
170+
async def aclear(self) -> None:
171+
"""Async clear the cache of all keys."""
172+
client = await self._get_async_redis_client()
173+
prefix = self._get_prefix()
174+
175+
# Scan for all keys with our prefix
176+
cursor = "0"
177+
while cursor != 0:
178+
cursor, keys = await client.scan(
179+
cursor=cursor, match=f"{prefix}*", count=100
180+
)
181+
if keys:
182+
await client.delete(*keys)
183+
184+
def disconnect(self) -> None:
185+
"""Disconnect from Redis."""
186+
if self._owns_redis_client is False:
187+
return
188+
189+
if self._redis_client:
190+
self._redis_client.close()
191+
self._redis_client = None
192+
193+
if hasattr(self, "_async_redis_client") and self._async_redis_client:
194+
self._async_redis_client.close()
195+
self._async_redis_client = None
196+
197+
async def adisconnect(self) -> None:
198+
"""Async disconnect from Redis."""
199+
if self._owns_redis_client is False:
200+
return
201+
202+
if self._redis_client:
203+
self._redis_client.close()
204+
self._redis_client = None
205+
206+
if hasattr(self, "_async_redis_client") and self._async_redis_client:
207+
await self._async_redis_client.aclose()
208+
self._async_redis_client = None
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""
2+
Redis Vector Library - Embeddings Cache Extensions
3+
4+
This module provides embedding caching functionality for RedisVL.
5+
"""
6+
7+
from redisvl.extensions.cache.embeddings.base import BaseEmbeddingsCache
8+
from redisvl.extensions.cache.embeddings.embeddings import EmbeddingsCache
9+
from redisvl.extensions.cache.embeddings.schema import CacheEntry
10+
11+
__all__ = ["BaseEmbeddingsCache", "EmbeddingsCache", "CacheEntry"]

0 commit comments

Comments
 (0)