Skip to content

State Stores

State stores persist the runtime AffectiveState snapshot across sessions. Pass one as the state_store= constructor argument of EmotionalMemory or AsyncEmotionalMemory — it is not a field on EmotionalMemoryConfig.

The AffectiveStateStore protocol (defined in interfaces.py) requires three methods: save(state), load() → AffectiveState | None, and clear().

SQLiteAffectiveStateStore uses the stdlib sqlite3 module (no extra). RedisAffectiveStateStore requires the [redis] extra.

See also: State for the AffectiveState data model, and Stores for MemoryStore backends.

In-Memory (default)

emotional_memory.state_stores.in_memory.InMemoryAffectiveStateStore()

Simple process-local state store.

The stored state is copied on save/load so callers cannot accidentally mutate the persisted snapshot by holding a shared reference.

Source code in src/emotional_memory/state_stores/in_memory.py
def __init__(self) -> None:
    self._state: AffectiveState | None = None

SQLite (persistent)

emotional_memory.state_stores.sqlite.SQLiteAffectiveStateStore(path: str | Path = ':memory:')

Persist the current affective state in a small SQLite table.

Unlike SQLiteStore, this backend does not require sqlite-vec and is safe to use in the base package for local persistence or replay harnesses.

Source code in src/emotional_memory/state_stores/sqlite.py
def __init__(self, path: str | Path = ":memory:") -> None:
    self._path = str(path)
    self._conn = sqlite3.connect(self._path, check_same_thread=False)
    self._conn.row_factory = sqlite3.Row
    if self._path != ":memory:":
        self._conn.execute("PRAGMA journal_mode=WAL")
    self._conn.execute(_CREATE_STATE)
    self._conn.commit()
    self._lock: threading.RLock = threading.RLock()

Redis (distributed)

Requires the [redis] extra: pip install 'emotional-memory[redis]'

emotional_memory.state_stores.redis.RedisAffectiveStateStore(url: str = 'redis://localhost:6379/0', *, key: str = 'emotional_memory:affective_state', client: Any | None = None, strict: bool = False)

Persist the current affective state in Redis.

The class lazily imports redis so the package remains importable without the optional dependency installed. Pass a preconfigured client in tests or advanced deployments to avoid URL-based construction.

Concurrency: save/load are independent GET/SET operations with no transaction — concurrent workers sharing one key are last-write-wins, and an interleaved read-modify-write can drop another worker's update. This is a documented limitation (docs/research/08_limitations.md, "Shared-backend multi-worker validation"); use one key per worker, or an external lock, if that matters for your deployment.

When strict=True, Redis errors propagate after logging instead of being swallowed (fail-open by default for resilience).

Source code in src/emotional_memory/state_stores/redis.py
def __init__(
    self,
    url: str = "redis://localhost:6379/0",
    *,
    key: str = "emotional_memory:affective_state",
    client: Any | None = None,
    strict: bool = False,
) -> None:
    self._url = url
    self._key = key
    self._strict = strict
    self._client = client if client is not None else self._create_client(url)