from typing import Any, Iterator, List, Optional, Sequence, Tuple, cast
from langchain_core._api.deprecation import deprecated
from langchain_core.stores import BaseStore, ByteStore
class _UpstashRedisStore(BaseStore[str, str]):
"""使用Upstash Redis作为基础存储的BaseStore实现。"""
def __init__(
self,
*,
client: Any = None,
url: Optional[str] = None,
token: Optional[str] = None,
ttl: Optional[int] = None,
namespace: Optional[str] = None,
) -> None:
"""使用HTTP API初始化UpstashRedisStore。
必须提供Upstash Redis客户端或一个URL。
参数:
client:一个Upstash Redis实例
url:UPSTASH_REDIS_REST_URL
token:UPSTASH_REDIS_REST_TOKEN
ttl:如果提供,键的过期时间(以秒为单位),
如果为None,则键永远不会过期
namespace:如果提供,所有键将以此命名空间为前缀。
"""
try:
from upstash_redis import Redis
except ImportError as e:
raise ImportError(
"UpstashRedisStore requires the upstash_redis library to be installed. "
"pip install upstash_redis"
) from e
if client and url:
raise ValueError(
"Either an Upstash Redis client or a url must be provided, not both."
)
if client:
if not isinstance(client, Redis):
raise TypeError(
f"Expected Upstash Redis client, got {type(client).__name__}."
)
_client = client
else:
if not url or not token:
raise ValueError(
"Either an Upstash Redis client or url and token must be provided."
)
_client = Redis(url=url, token=token)
self.client = _client
if not isinstance(ttl, int) and ttl is not None:
raise TypeError(f"Expected int or None, got {type(ttl)} instead.")
self.ttl = ttl
self.namespace = namespace
def _get_prefixed_key(self, key: str) -> str:
"""获取带有命名空间前缀的键。
参数:
key(str):原始键。
返回:
str:带有命名空间前缀的键。
"""
delimiter = "/"
if self.namespace:
return f"{self.namespace}{delimiter}{key}"
return key
def mget(self, keys: Sequence[str]) -> List[Optional[str]]:
"""获取与给定键相关联的值。"""
keys = [self._get_prefixed_key(key) for key in keys]
return cast(
List[Optional[str]],
self.client.mget(*keys),
)
def mset(self, key_value_pairs: Sequence[Tuple[str, str]]) -> None:
"""设置给定的键值对。"""
for key, value in key_value_pairs:
self.client.set(self._get_prefixed_key(key), value, ex=self.ttl)
def mdelete(self, keys: Sequence[str]) -> None:
"""删除给定的键。"""
_keys = [self._get_prefixed_key(key) for key in keys]
self.client.delete(*_keys)
def yield_keys(self, *, prefix: Optional[str] = None) -> Iterator[str]:
"""在存储中生成密钥。"""
if prefix:
pattern = self._get_prefixed_key(prefix)
else:
pattern = self._get_prefixed_key("*")
cursor, keys = self.client.scan(0, match=pattern)
for key in keys:
if self.namespace:
relative_key = key[len(self.namespace) + 1 :]
yield relative_key
else:
yield key
while cursor != 0:
cursor, keys = self.client.scan(cursor, match=pattern)
for key in keys:
if self.namespace:
relative_key = key[len(self.namespace) + 1 :]
yield relative_key
else:
yield key
[docs]@deprecated("0.0.1", alternative="UpstashRedisByteStore")
class UpstashRedisStore(_UpstashRedisStore):
"""使用Upstash Redis作为底层存储来存储字符串的BaseStore实现。
已弃用,推荐使用更通用的UpstashRedisByteStore。"""
[docs]class UpstashRedisByteStore(ByteStore):
"""使用Upstash Redis作为底层存储来实现BaseStore。"""
[docs] def __init__(
self,
*,
client: Any = None,
url: Optional[str] = None,
token: Optional[str] = None,
ttl: Optional[int] = None,
namespace: Optional[str] = None,
) -> None:
self.underlying_store = _UpstashRedisStore(
client=client, url=url, token=token, ttl=ttl, namespace=namespace
)
[docs] def mget(self, keys: Sequence[str]) -> List[Optional[bytes]]:
"""获取与给定键相关联的值。"""
return [
value.encode("utf-8") if value is not None else None
for value in self.underlying_store.mget(keys)
]
[docs] def mset(self, key_value_pairs: Sequence[Tuple[str, bytes]]) -> None:
"""设置给定的键值对。"""
self.underlying_store.mset(
[
(k, v.decode("utf-8")) if v is not None else None
for k, v in key_value_pairs
]
)
[docs] def mdelete(self, keys: Sequence[str]) -> None:
"""删除给定的键。"""
self.underlying_store.mdelete(keys)
[docs] def yield_keys(self, *, prefix: Optional[str] = None) -> Iterator[str]:
"""在存储中生成密钥。"""
yield from self.underlying_store.yield_keys(prefix=prefix)