feat(storage): enhance storage package with async provider and docs

Add to packages/storage/:
- README.md and README_zh.md - comprehensive documentation
- store/persistence/async_provider.py - async persistence provider

Update repositories:
- contracts/thread_meta.py - add new contract method
- db/thread_meta.py - implement new method
- factory.py - update factory logic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rayhpeng
2026-04-22 11:29:42 +08:00
parent e3e00af51d
commit b5e18f5b47
6 changed files with 1072 additions and 1 deletions
@@ -0,0 +1,76 @@
from __future__ import annotations
import asyncio
from contextlib import asynccontextmanager
from typing import AsyncIterator
from sqlalchemy import URL
from store.config.app_config import get_app_config
@asynccontextmanager
async def make_checkpointer() -> AsyncIterator[object]:
"""Create a LangGraph checkpointer from the unified storage config."""
storage = get_app_config().storage
if storage.driver == "sqlite":
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
conn_str = storage.sqlite_storage_path
await asyncio.to_thread(_ensure_sqlite_parent_dir, conn_str)
async with AsyncSqliteSaver.from_conn_string(conn_str) as saver:
await saver.setup()
yield saver
return
if storage.driver == "postgres":
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
async with AsyncPostgresSaver.from_conn_string(_create_postgres_url(storage)) as saver:
await saver.setup()
yield saver
return
if storage.driver == "mysql":
from langgraph.checkpoint.mysql.aio import AIOMySQLSaver
async with AIOMySQLSaver.from_conn_string(_create_mysql_url(storage)) as saver:
await saver.setup()
yield saver
return
raise ValueError(f"Unsupported storage driver for checkpointer: {storage.driver}")
def _ensure_sqlite_parent_dir(connection_string: str) -> None:
from pathlib import Path
if connection_string == ":memory:":
return
Path(connection_string).expanduser().parent.mkdir(parents=True, exist_ok=True)
def _create_postgres_url(storage) -> URL:
return URL.create(
drivername="postgresql+asyncpg",
username=storage.username,
password=storage.password,
host=storage.host,
port=storage.port,
database=storage.db_name or "deerflow",
)
def _create_mysql_url(storage) -> URL:
return URL.create(
drivername="mysql+aiomysql",
username=storage.username,
password=storage.password,
host=storage.host,
port=storage.port,
database=storage.db_name or "deerflow",
)
__all__ = ["make_checkpointer"]
@@ -39,6 +39,7 @@ class ThreadMetaRepositoryProtocol(Protocol):
self,
thread_id: str,
*,
assistant_id: str | None = None,
display_name: str | None = None,
status: str | None = None,
metadata: dict[str, Any] | None = None,
@@ -49,11 +49,14 @@ class DbThreadMetaRepository(ThreadMetaRepositoryProtocol):
self,
thread_id: str,
*,
assistant_id: str | None = None,
display_name: str | None = None,
status: str | None = None,
metadata: dict[str, Any] | None = None,
) -> None:
values: dict = {}
if assistant_id is not None:
values["assistant_id"] = assistant_id
if display_name is not None:
values["display_name"] = display_name
if status is not None:
@@ -6,7 +6,12 @@ from store.repositories import (
RunRepositoryProtocol,
ThreadMetaRepositoryProtocol,
)
from store.repositories.db import DbFeedbackRepository, DbRunEventRepository, DbRunRepository, DbThreadMetaRepository
from store.repositories.db import (
DbFeedbackRepository,
DbRunEventRepository,
DbRunRepository,
DbThreadMetaRepository,
)
def build_thread_meta_repository(session: AsyncSession) -> ThreadMetaRepositoryProtocol: