Async State Store

Non-blocking key-value state persistence with the AsyncStateStore protocol

Async state stores provide non-blocking key-value and hash persistence so native-async backends never block the event loop.

python
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    instructions="Remember the user's preferences across turns.",
)
agent.start("What's my preferred language?")

The user asks a follow-up; an async state store fetches cached preferences without blocking other agents on the same loop.

Diagram

Async State Flow

AsyncStateStore

StateStore

🤖 Agent

🔄 PraisonAIDB

📊 instanceof?

⚡ await store.get

🔄 run_sync

💾 Backend

Quick Start

  1. Subclass AsyncStateStore

    Implement a native-async backend by inheriting from AsyncStateStore:

    python
    from praisonai.persistence import AsyncStateStore
    from typing import Any, Dict, List, Optional
    
    class MyAsyncStateStore(AsyncStateStore):
        async def get(self, key: str) -> Optional[Any]:
            ...
    
        async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
            ...
    
        async def delete(self, key: str) -> bool:
            ...
    
        async def exists(self, key: str) -> bool:
            ...
    
        async def keys(self, pattern: str = "*") -> List[str]:
            ...
    
        async def ttl(self, key: str) -> Optional[int]:
            ...
    
        async def expire(self, key: str, ttl: int) -> bool:
            ...
    
        async def hget(self, key: str, field: str) -> Optional[Any]:
            ...
    
        async def hset(self, key: str, field: str, value: Any) -> None:
            ...
    
        async def hgetall(self, key: str) -> Dict[str, Any]:
            ...
    
        async def hdel(self, key: str, *fields: str) -> int:
            ...
    
        async def close(self) -> None:
            ...
  2. Wire It Into an Agent

    Pass the store through the persistence config so the agent dispatches async calls natively:

    python
    from praisonaiagents import Agent
    
    store = MyAsyncStateStore()
    
    agent = Agent(
        name="Assistant",
        instructions="Remember the user's preferences across turns.",
        state_store=store,
    )
    agent.start("What's my preferred language?")

How It Works

A sync caller inside a running loop dispatches to the async store through PraisonAIDB._dispatch_async.

Diagram
BackendAsyncStateStorePraisonAIDBCallerBackendAsyncStateStorePraisonAIDBCallerget(“prefs:lang”)isinstance(store, AsyncStateStore)?await store.get(“prefs:lang”)fetch valuevalueOptional[Any]value
ComponentRole
CallerAgent or application requesting state
PraisonAIDBRoutes the call based on store type via isinstance()
AsyncStateStoreHandles native-async key-value / hash operations
BackendRedis, MongoDB, or custom storage

Abstract Methods

MethodSignatureReturns
getasync def get(self, key: str)Optional[Any]
setasync def set(self, key: str, value: Any, ttl: Optional[int] = None)None
deleteasync def delete(self, key: str)bool
existsasync def exists(self, key: str)bool
keysasync def keys(self, pattern: str = "*")List[str]
ttlasync def ttl(self, key: str)Optional[int]
expireasync def expire(self, key: str, ttl: int)bool
hgetasync def hget(self, key: str, field: str)Optional[Any]
hsetasync def hset(self, key: str, field: str, value: Any)None
hgetallasync def hgetall(self, key: str)Dict[str, Any]
hdelasync def hdel(self, key: str, *fields: str)int
closeasync def close(self)None

Concrete helpers — get_json(key), set_json(key, value, ttl=None), __aenter__, __aexit__ — ship on the ABC and need no override.


Configuration Options

Persistence API Reference

Complete method signatures for AsyncStateStore and sibling stores


Common Patterns

Custom Async Backend Inheritance

Subclass AsyncStateStore (not StateStore) when your backend is natively async:

python
from praisonai.persistence import AsyncStateStore

class RedisAsyncStateStore(AsyncStateStore):
    def __init__(self, client):
        self._client = client

    async def get(self, key):
        return await self._client.get(key)

    async def set(self, key, value, ttl=None):
        await self._client.set(key, value, ex=ttl)

    async def close(self):
        await self._client.aclose()
    # ... implement the remaining abstract methods

async with Context Manager

The ABC defines __aenter__ / __aexit__, so async with closes the store automatically:

python
async with MyAsyncStateStore() as store:
    await store.set("prefs:lang", "en", ttl=3600)
    lang = await store.get("prefs:lang")

get_json / set_json Helpers

The concrete helpers serialize to JSON on write. On read, a stored str is parsed with json.loads; any non-str value is returned as-is:

python
async with MyAsyncStateStore() as store:
    await store.set_json("prefs", {"lang": "en", "theme": "dark"})
    prefs = await store.get_json("prefs")  # {"lang": "en", "theme": "dark"}

Best Practices

Subclass AsyncStateStore for Native-Async Backends

Inherit the async ABC so isinstance() dispatch awaits your methods directly:

python
# ✅ Good - native async dispatch
class MyStore(AsyncStateStore):
    async def get(self, key): ...

# ❌ Bad - sync ABC forces run_sync shims
class MyStore(StateStore):
    def get(self, key): ...
Always Close the Store

Use async with or await store.close() to release connections:

python
# ✅ Good - automatic cleanup
async with MyAsyncStateStore() as store:
    await store.get("key")

# ❌ Bad - leaked connections
store = MyAsyncStateStore()
await store.get("key")
Don't Mix Sync and Async Methods

Keep a single store async end-to-end:

python
# ✅ Good - pure async
async with store:
    await store.set("key", "value")

# ❌ Bad - calling an async method without await
store.set("key", "value")  # returns an un-awaited coroutine

Related