09-agentic-workflow-sys / src /persistence.py
FlyingNunchucks's picture
Sync from GitHub via hub-sync
152be26 verified
Raw History Blame Contribute Delete
3.02 kB
"""Persistence adapters for durable workflow checkpoints.
The workflow runtime depends on a small StateStore contract rather than a specific
database. InMemoryStateStore keeps tests deterministic, while SQLiteStateStore
provides a zero-infrastructure durable store for the portfolio demo.
"""
from __future__ import annotations
import sqlite3
from pathlib import Path
from typing import Protocol
from src.schemas import WorkflowRun
class StateStoreError(RuntimeError):
"""Base error for workflow state-store operations."""
class RunNotFoundError(StateStoreError):
"""Raised when a requested workflow run does not exist."""
class StateStore(Protocol):
"""Minimal persistence contract used by the workflow runtime."""
def save(self, run: WorkflowRun) -> None:
"""Persist the latest complete checkpoint for a workflow run."""
def load(self, run_id: str) -> WorkflowRun:
"""Load a detached workflow run snapshot by identifier."""
class InMemoryStateStore:
"""Deterministic state store for tests and local execution."""
def __init__(self) -> None:
self._runs: dict[str, str] = {}
def save(self, run: WorkflowRun) -> None:
self._runs[run.run_id] = run.model_dump_json()
def load(self, run_id: str) -> WorkflowRun:
try:
payload = self._runs[run_id]
except KeyError as exc:
raise RunNotFoundError(f"workflow run '{run_id}' was not found") from exc
return WorkflowRun.model_validate_json(payload)
class SQLiteStateStore:
"""SQLite-backed state store using one JSON snapshot per workflow run."""
def __init__(self, database_path: str | Path) -> None:
self.database_path = str(database_path)
self._initialize()
def _connect(self) -> sqlite3.Connection:
return sqlite3.connect(self.database_path)
def _initialize(self) -> None:
with self._connect() as connection:
connection.execute(
"""
CREATE TABLE IF NOT EXISTS workflow_runs (
run_id TEXT PRIMARY KEY,
payload TEXT NOT NULL
)
"""
)
def save(self, run: WorkflowRun) -> None:
payload = run.model_dump_json()
with self._connect() as connection:
connection.execute(
"""
INSERT INTO workflow_runs (run_id, payload)
VALUES (?, ?)
ON CONFLICT(run_id) DO UPDATE SET payload = excluded.payload
""",
(run.run_id, payload),
)
def load(self, run_id: str) -> WorkflowRun:
with self._connect() as connection:
row = connection.execute(
"SELECT payload FROM workflow_runs WHERE run_id = ?",
(run_id,),
).fetchone()
if row is None:
raise RunNotFoundError(f"workflow run '{run_id}' was not found")
return WorkflowRun.model_validate_json(row[0])