# core/store.py from __future__ import annotations import json import sqlite3 from typing import Protocol class StateStore(Protocol): async def get(self, key: str) -> dict | None: ... async def set(self, key: str, value: dict) -> None: ... async def delete(self, key: str) -> None: ... async def keys(self, prefix: str) -> list[str]: ... class InMemoryStore: def __init__(self) -> None: self._data: dict[str, dict] = {} async def get(self, key: str) -> dict | None: return self._data.get(key) async def set(self, key: str, value: dict) -> None: self._data[key] = value async def delete(self, key: str) -> None: self._data.pop(key, None) async def keys(self, prefix: str) -> list[str]: return [k for k in self._data if k.startswith(prefix)] class SQLiteStore: def __init__(self, db_path: str) -> None: self._db_path = db_path self._init_db() def _init_db(self) -> None: conn = sqlite3.connect(self._db_path) conn.execute( "CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)" ) conn.commit() conn.close() async def get(self, key: str) -> dict | None: conn = sqlite3.connect(self._db_path) row = conn.execute("SELECT value FROM kv WHERE key = ?", (key,)).fetchone() conn.close() return json.loads(row[0]) if row else None async def set(self, key: str, value: dict) -> None: conn = sqlite3.connect(self._db_path) conn.execute( "INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)", (key, json.dumps(value, default=str)), ) conn.commit() conn.close() async def delete(self, key: str) -> None: conn = sqlite3.connect(self._db_path) conn.execute("DELETE FROM kv WHERE key = ?", (key,)) conn.commit() conn.close() async def keys(self, prefix: str) -> list[str]: conn = sqlite3.connect(self._db_path) rows = conn.execute( "SELECT key FROM kv WHERE key LIKE ?", (prefix + "%",) ).fetchall() conn.close() return [row[0] for row in rows]