mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-21 10:31:23 +00:00
core[minor],langchain[patch],community[patch]: Move storage interfaces to core (#20750)
* Move storage interface to core * Move in memory and file system implementation to core
This commit is contained in:
@@ -9,11 +9,15 @@ import warnings
|
||||
from typing import Any
|
||||
|
||||
from langchain_core._api import LangChainDeprecationWarning
|
||||
from langchain_core.stores import (
|
||||
InMemoryByteStore,
|
||||
InMemoryStore,
|
||||
InvalidKeyException,
|
||||
)
|
||||
|
||||
from langchain.storage._lc_store import create_kv_docstore, create_lc_store
|
||||
from langchain.storage.encoder_backed import EncoderBackedStore
|
||||
from langchain.storage.file_system import LocalFileStore
|
||||
from langchain.storage.in_memory import InMemoryByteStore, InMemoryStore
|
||||
from langchain.utils.interactive_env import is_interactive_env
|
||||
|
||||
|
||||
@@ -36,12 +40,13 @@ def __getattr__(name: str) -> Any:
|
||||
|
||||
__all__ = [
|
||||
"EncoderBackedStore",
|
||||
"InMemoryStore",
|
||||
"InMemoryByteStore",
|
||||
"LocalFileStore",
|
||||
"RedisStore",
|
||||
"create_lc_store",
|
||||
"create_kv_docstore",
|
||||
"LocalFileStore",
|
||||
"InMemoryStore",
|
||||
"InvalidKeyException",
|
||||
"InMemoryByteStore",
|
||||
"UpstashRedisByteStore",
|
||||
"UpstashRedisStore",
|
||||
]
|
||||
|
@@ -1,3 +1,3 @@
|
||||
from langchain_community.storage.exceptions import InvalidKeyException
|
||||
from langchain_core.stores import InvalidKeyException
|
||||
|
||||
__all__ = ["InvalidKeyException"]
|
||||
|
@@ -3,150 +3,10 @@
|
||||
This is a simple implementation of the BaseStore using a dictionary that is useful
|
||||
primarily for unit testing purposes.
|
||||
"""
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Dict,
|
||||
Generic,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
)
|
||||
from langchain_core.stores import InMemoryBaseStore, InMemoryByteStore, InMemoryStore
|
||||
|
||||
from langchain_core.stores import BaseStore
|
||||
|
||||
V = TypeVar("V")
|
||||
|
||||
|
||||
class InMemoryBaseStore(BaseStore[str, V], Generic[V]):
|
||||
"""In-memory implementation of the BaseStore using a dictionary.
|
||||
|
||||
Attributes:
|
||||
store (Dict[str, Any]): The underlying dictionary that stores
|
||||
the key-value pairs.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from langchain.storage import InMemoryStore
|
||||
|
||||
store = InMemoryStore()
|
||||
store.mset([('key1', 'value1'), ('key2', 'value2')])
|
||||
store.mget(['key1', 'key2'])
|
||||
# ['value1', 'value2']
|
||||
store.mdelete(['key1'])
|
||||
list(store.yield_keys())
|
||||
# ['key2']
|
||||
list(store.yield_keys(prefix='k'))
|
||||
# ['key2']
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize an empty store."""
|
||||
self.store: Dict[str, V] = {}
|
||||
|
||||
def mget(self, keys: Sequence[str]) -> List[Optional[V]]:
|
||||
"""Get the values associated with the given keys.
|
||||
|
||||
Args:
|
||||
keys (Sequence[str]): A sequence of keys.
|
||||
|
||||
Returns:
|
||||
A sequence of optional values associated with the keys.
|
||||
If a key is not found, the corresponding value will be None.
|
||||
"""
|
||||
return [self.store.get(key) for key in keys]
|
||||
|
||||
async def amget(self, keys: Sequence[str]) -> List[Optional[V]]:
|
||||
"""Get the values associated with the given keys.
|
||||
|
||||
Args:
|
||||
keys (Sequence[str]): A sequence of keys.
|
||||
|
||||
Returns:
|
||||
A sequence of optional values associated with the keys.
|
||||
If a key is not found, the corresponding value will be None.
|
||||
"""
|
||||
return self.mget(keys)
|
||||
|
||||
def mset(self, key_value_pairs: Sequence[Tuple[str, V]]) -> None:
|
||||
"""Set the values for the given keys.
|
||||
|
||||
Args:
|
||||
key_value_pairs (Sequence[Tuple[str, V]]): A sequence of key-value pairs.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
for key, value in key_value_pairs:
|
||||
self.store[key] = value
|
||||
|
||||
async def amset(self, key_value_pairs: Sequence[Tuple[str, V]]) -> None:
|
||||
"""Set the values for the given keys.
|
||||
|
||||
Args:
|
||||
key_value_pairs (Sequence[Tuple[str, V]]): A sequence of key-value pairs.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
return self.mset(key_value_pairs)
|
||||
|
||||
def mdelete(self, keys: Sequence[str]) -> None:
|
||||
"""Delete the given keys and their associated values.
|
||||
|
||||
Args:
|
||||
keys (Sequence[str]): A sequence of keys to delete.
|
||||
"""
|
||||
for key in keys:
|
||||
if key in self.store:
|
||||
del self.store[key]
|
||||
|
||||
async def amdelete(self, keys: Sequence[str]) -> None:
|
||||
"""Delete the given keys and their associated values.
|
||||
|
||||
Args:
|
||||
keys (Sequence[str]): A sequence of keys to delete.
|
||||
"""
|
||||
self.mdelete(keys)
|
||||
|
||||
def yield_keys(self, prefix: Optional[str] = None) -> Iterator[str]:
|
||||
"""Get an iterator over keys that match the given prefix.
|
||||
|
||||
Args:
|
||||
prefix (str, optional): The prefix to match. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Iterator[str]: An iterator over keys that match the given prefix.
|
||||
"""
|
||||
if prefix is None:
|
||||
yield from self.store.keys()
|
||||
else:
|
||||
for key in self.store.keys():
|
||||
if key.startswith(prefix):
|
||||
yield key
|
||||
|
||||
async def ayield_keys(self, prefix: Optional[str] = None) -> AsyncIterator[str]:
|
||||
"""Get an async iterator over keys that match the given prefix.
|
||||
|
||||
Args:
|
||||
prefix (str, optional): The prefix to match. Defaults to None.
|
||||
|
||||
Returns:
|
||||
AsyncIterator[str]: An async iterator over keys that match the given prefix.
|
||||
"""
|
||||
if prefix is None:
|
||||
for key in self.store.keys():
|
||||
yield key
|
||||
else:
|
||||
for key in self.store.keys():
|
||||
if key.startswith(prefix):
|
||||
yield key
|
||||
|
||||
|
||||
InMemoryStore = InMemoryBaseStore[Any]
|
||||
InMemoryByteStore = InMemoryBaseStore[bytes]
|
||||
__all__ = [
|
||||
"InMemoryStore",
|
||||
"InMemoryBaseStore",
|
||||
"InMemoryByteStore",
|
||||
]
|
||||
|
@@ -3,8 +3,8 @@ import tempfile
|
||||
from typing import Generator
|
||||
|
||||
import pytest
|
||||
from langchain_core.stores import InvalidKeyException
|
||||
|
||||
from langchain.storage.exceptions import InvalidKeyException
|
||||
from langchain.storage.file_system import LocalFileStore
|
||||
|
||||
|
||||
|
@@ -7,6 +7,7 @@ EXPECTED_ALL = [
|
||||
"InMemoryByteStore",
|
||||
"LocalFileStore",
|
||||
"RedisStore",
|
||||
"InvalidKeyException",
|
||||
"create_lc_store",
|
||||
"create_kv_docstore",
|
||||
"UpstashRedisByteStore",
|
||||
|
@@ -1,95 +0,0 @@
|
||||
from langchain.storage.in_memory import InMemoryStore
|
||||
|
||||
|
||||
def test_mget() -> None:
|
||||
store = InMemoryStore()
|
||||
store.mset([("key1", "value1"), ("key2", "value2")])
|
||||
|
||||
values = store.mget(["key1", "key2"])
|
||||
assert values == ["value1", "value2"]
|
||||
|
||||
# Test non-existent key
|
||||
non_existent_value = store.mget(["key3"])
|
||||
assert non_existent_value == [None]
|
||||
|
||||
|
||||
async def test_amget() -> None:
|
||||
store = InMemoryStore()
|
||||
await store.amset([("key1", "value1"), ("key2", "value2")])
|
||||
|
||||
values = await store.amget(["key1", "key2"])
|
||||
assert values == ["value1", "value2"]
|
||||
|
||||
# Test non-existent key
|
||||
non_existent_value = await store.amget(["key3"])
|
||||
assert non_existent_value == [None]
|
||||
|
||||
|
||||
def test_mset() -> None:
|
||||
store = InMemoryStore()
|
||||
store.mset([("key1", "value1"), ("key2", "value2")])
|
||||
|
||||
values = store.mget(["key1", "key2"])
|
||||
assert values == ["value1", "value2"]
|
||||
|
||||
|
||||
async def test_amset() -> None:
|
||||
store = InMemoryStore()
|
||||
await store.amset([("key1", "value1"), ("key2", "value2")])
|
||||
|
||||
values = await store.amget(["key1", "key2"])
|
||||
assert values == ["value1", "value2"]
|
||||
|
||||
|
||||
def test_mdelete() -> None:
|
||||
store = InMemoryStore()
|
||||
store.mset([("key1", "value1"), ("key2", "value2")])
|
||||
|
||||
store.mdelete(["key1"])
|
||||
|
||||
values = store.mget(["key1", "key2"])
|
||||
assert values == [None, "value2"]
|
||||
|
||||
# Test deleting non-existent key
|
||||
store.mdelete(["key3"]) # No error should be raised
|
||||
|
||||
|
||||
async def test_amdelete() -> None:
|
||||
store = InMemoryStore()
|
||||
await store.amset([("key1", "value1"), ("key2", "value2")])
|
||||
|
||||
await store.amdelete(["key1"])
|
||||
|
||||
values = await store.amget(["key1", "key2"])
|
||||
assert values == [None, "value2"]
|
||||
|
||||
# Test deleting non-existent key
|
||||
await store.amdelete(["key3"]) # No error should be raised
|
||||
|
||||
|
||||
def test_yield_keys() -> None:
|
||||
store = InMemoryStore()
|
||||
store.mset([("key1", "value1"), ("key2", "value2"), ("key3", "value3")])
|
||||
|
||||
keys = list(store.yield_keys())
|
||||
assert set(keys) == {"key1", "key2", "key3"}
|
||||
|
||||
keys_with_prefix = list(store.yield_keys(prefix="key"))
|
||||
assert set(keys_with_prefix) == {"key1", "key2", "key3"}
|
||||
|
||||
keys_with_invalid_prefix = list(store.yield_keys(prefix="x"))
|
||||
assert keys_with_invalid_prefix == []
|
||||
|
||||
|
||||
async def test_ayield_keys() -> None:
|
||||
store = InMemoryStore()
|
||||
await store.amset([("key1", "value1"), ("key2", "value2"), ("key3", "value3")])
|
||||
|
||||
keys = [key async for key in store.ayield_keys()]
|
||||
assert set(keys) == {"key1", "key2", "key3"}
|
||||
|
||||
keys_with_prefix = [key async for key in store.ayield_keys(prefix="key")]
|
||||
assert set(keys_with_prefix) == {"key1", "key2", "key3"}
|
||||
|
||||
keys_with_invalid_prefix = [key async for key in store.ayield_keys(prefix="x")]
|
||||
assert keys_with_invalid_prefix == []
|
Reference in New Issue
Block a user