Compare commits

...
4 changed files with 104 additions and 8 deletions
@@ -115,6 +115,20 @@ class AsyncPostgresStore(AsyncBatchedBaseStore, BasePostgresStore[_ainternal.Con
If you provide a TTL configuration, you must explicitly call `start_ttl_sweeper()` to begin
the background task that removes expired items. Call `stop_ttl_sweeper()` to properly
clean up resources when you're done with the store.
Note:
The core data operations -- `put`/`aput`, `get`/`aget`, `search`/`asearch`,
`delete`/`adelete`, and `list_namespaces`/`alist_namespaces` -- are inherited
from `langgraph.store.base.BaseStore`, which is the canonical reference for
their arguments and behavior. (This is LangGraph's `BaseStore`, distinct from
`langchain_core.stores.BaseStore`.) Key semantics:
- `put`/`aput` `value` must be a `dict` of JSON-serializable data. `None` is
not a valid stored value: a top-level `None` is reserved as the delete
signal (prefer `delete`/`adelete`). To store a marker or "empty" entry,
use an empty dict (`{}`), not `None`.
- `list_namespaces`/`alist_namespaces` match `prefix`/`suffix` exactly and
case-sensitively; there is no case-insensitive matching option.
"""
__slots__ = (
@@ -713,6 +713,20 @@ class PostgresStore(BaseStore, BasePostgresStore[_pg_internal.Conn]):
the background thread that removes expired items. Call `stop_ttl_sweeper()` to properly
clean up resources when you're done with the store.
Note:
The core data operations -- `put`/`aput`, `get`/`aget`, `search`/`asearch`,
`delete`/`adelete`, and `list_namespaces`/`alist_namespaces` -- are inherited
from `langgraph.store.base.BaseStore`, which is the canonical reference for
their arguments and behavior. (This is LangGraph's `BaseStore`, distinct from
`langchain_core.stores.BaseStore`.) Key semantics:
- `put`/`aput` `value` must be a `dict` of JSON-serializable data. `None` is
not a valid stored value: a top-level `None` is reserved as the delete
signal (prefer `delete`/`adelete`). To store a marker or "empty" entry,
use an empty dict (`{}`), not `None`.
- `list_namespaces`/`alist_namespaces` match `prefix`/`suffix` exactly and
case-sensitively; there is no case-insensitive matching option.
"""
__slots__ = (
@@ -861,8 +861,11 @@ class BaseStore(ABC):
Example: `("documents", "user123")`
key: Unique identifier within the namespace. Together with namespace forms
the complete path to the item.
value: Dictionary containing the item's data. Must contain string keys
and JSON-serializable values.
value: Dictionary containing the item's data. Must be a `dict` with
string keys and JSON-serializable values. `None` is not a valid
stored value: a top-level `None` is reserved as the delete signal
(prefer `delete()`). To store a marker or "empty" entry, use an
empty dict (`{}`), not `None`.
index: Controls how the item's fields are indexed for search:
- None (default): Use `fields` you configured when creating the store (if any)
@@ -895,6 +898,13 @@ class BaseStore(ABC):
store.put(("docs",), "report", {"memory": "Will likes ai"})
```
Store a set/list-like marker entry. Use an empty dict, not `None`
(which would delete the item):
```python
store.put(("seen",), "user123", {})
```
Do not index item for semantic search. Still accessible through `get()`
and `search()` operations but won't have a vector representation.
@@ -950,8 +960,10 @@ class BaseStore(ABC):
find specific collections, or navigate the namespace hierarchy.
Args:
prefix: Filter namespaces that start with this path.
suffix: Filter namespaces that end with this path.
prefix: Filter namespaces that start with this path. Matched exactly and
case-sensitively.
suffix: Filter namespaces that end with this path. Matched exactly and
case-sensitively.
max_depth: Return namespaces up to this depth in the hierarchy.
Namespaces deeper than this level will be truncated.
limit: Maximum number of namespaces to return.
@@ -961,6 +973,11 @@ class BaseStore(ABC):
A list of namespace tuples that match the criteria. Each tuple represents a
full namespace path up to `max_depth`.
Note:
Namespace matching is case-sensitive. There is no case-insensitive
matching option. If you need case-insensitive lookups, normalize the
namespace casing when you write items (e.g. lowercase each label).
???+ example "Examples":
Setting `max_depth=3`. Given the namespaces:
@@ -1114,8 +1131,11 @@ class BaseStore(ABC):
Example: `("documents", "user123")`
key: Unique identifier within the namespace. Together with namespace forms
the complete path to the item.
value: Dictionary containing the item's data. Must contain string keys
and JSON-serializable values.
value: Dictionary containing the item's data. Must be a `dict` with
string keys and JSON-serializable values. `None` is not a valid
stored value: a top-level `None` is reserved as the delete signal
(prefer `adelete()`). To store a marker or "empty" entry, use an
empty dict (`{}`), not `None`.
index: Controls how the item's fields are indexed for search:
- None (default): Use `fields` you configured when creating the store (if any)
@@ -1211,8 +1231,10 @@ class BaseStore(ABC):
find specific collections, or navigate the namespace hierarchy.
Args:
prefix: Filter namespaces that start with this path.
suffix: Filter namespaces that end with this path.
prefix: Filter namespaces that start with this path. Matched exactly and
case-sensitively.
suffix: Filter namespaces that end with this path. Matched exactly and
case-sensitively.
max_depth: Return namespaces up to this depth in the hierarchy.
Namespaces deeper than this level will be truncated to this depth.
limit: Maximum number of namespaces to return.
@@ -1222,6 +1244,11 @@ class BaseStore(ABC):
A list of namespace tuples that match the criteria. Each tuple represents a
full namespace path up to `max_depth`.
Note:
Namespace matching is case-sensitive. There is no case-insensitive
matching option. If you need case-insensitive lookups, normalize the
namespace casing when you write items (e.g. lowercase each label).
???+ example "Examples"
Setting `max_depth=3` with existing namespaces:
@@ -86,6 +86,10 @@ class AsyncBatchedBaseStore(BaseStore):
*,
refresh_ttl: bool | None = None,
) -> Item | None:
"""Asynchronously retrieve a single item.
See `BaseStore.aget` for the full description of arguments and behavior.
"""
self._ensure_task()
fut = self._loop.create_future()
self._aqueue.put_nowait(
@@ -111,6 +115,10 @@ class AsyncBatchedBaseStore(BaseStore):
offset: int = 0,
refresh_ttl: bool | None = None,
) -> list[SearchItem]:
"""Asynchronously search for items within a namespace prefix.
See `BaseStore.asearch` for the full description of arguments and behavior.
"""
self._ensure_task()
fut = self._loop.create_future()
self._aqueue.put_nowait(
@@ -137,6 +145,12 @@ class AsyncBatchedBaseStore(BaseStore):
*,
ttl: float | None | NotProvided = NOT_PROVIDED,
) -> None:
"""Asynchronously store or update an item.
`value` must be a `dict`; a top-level `None` is reserved as the delete
signal (prefer `adelete`). See `BaseStore.aput` for the full description
of arguments and behavior.
"""
self._ensure_task()
_validate_namespace(namespace)
fut = self._loop.create_future()
@@ -155,6 +169,10 @@ class AsyncBatchedBaseStore(BaseStore):
namespace: tuple[str, ...],
key: str,
) -> None:
"""Asynchronously delete an item.
See `BaseStore.adelete` for the full description of arguments and behavior.
"""
self._ensure_task()
fut = self._loop.create_future()
self._aqueue.put_nowait((fut, PutOp(namespace, key, None)))
@@ -169,6 +187,11 @@ class AsyncBatchedBaseStore(BaseStore):
limit: int = 100,
offset: int = 0,
) -> list[tuple[str, ...]]:
"""Asynchronously list and filter namespaces in the store.
Namespace matching is case-sensitive. See `BaseStore.alist_namespaces`
for the full description of arguments and behavior.
"""
self._ensure_task()
fut = self._loop.create_future()
match_conditions = []
@@ -188,6 +211,10 @@ class AsyncBatchedBaseStore(BaseStore):
@_check_loop
def batch(self, ops: Iterable[Op]) -> list[Result]:
"""Execute multiple operations synchronously in a single batch.
See `BaseStore.batch` for the full description of arguments and behavior.
"""
return asyncio.run_coroutine_threadsafe(self.abatch(ops), self._loop).result()
@_check_loop
@@ -198,6 +225,10 @@ class AsyncBatchedBaseStore(BaseStore):
*,
refresh_ttl: bool | None = None,
) -> Item | None:
"""Retrieve a single item.
See `BaseStore.get` for the full description of arguments and behavior.
"""
return asyncio.run_coroutine_threadsafe(
self.aget(namespace, key=key, refresh_ttl=refresh_ttl), self._loop
).result()
@@ -214,6 +245,10 @@ class AsyncBatchedBaseStore(BaseStore):
offset: int = 0,
refresh_ttl: bool | None = None,
) -> list[SearchItem]:
"""Search for items within a namespace prefix.
See `BaseStore.search` for the full description of arguments and behavior.
"""
return asyncio.run_coroutine_threadsafe(
self.asearch(
namespace_prefix,
@@ -236,6 +271,12 @@ class AsyncBatchedBaseStore(BaseStore):
*,
ttl: float | None | NotProvided = NOT_PROVIDED,
) -> None:
"""Store or update an item.
`value` must be a `dict`; a top-level `None` is reserved as the delete
signal (prefer `delete`). See `BaseStore.put` for the full description of
arguments and behavior.
"""
_validate_namespace(namespace)
asyncio.run_coroutine_threadsafe(
self.aput(