Skip to content

State: sessions, memory & artifacts

YAAB separates three kinds of state, each with a low-level service (the storage protocol) and a high-level manager (scoped operations):

Concern What it holds Service Manager
Session conversation history + structured KV state SessionService SessionManager
Memory long-term, semantic (vector) recall MemoryService MemoryManager
Artifacts binary/file blobs, versioned ArtifactService ArtifactManager

Managers add (app_name, user_id, session_id) scoping; services are the swappable backends.

Sessions

from yaab import SessionManager
from yaab.sessions import SQLiteSessionService
from yaab.types import Role

sessions = SessionManager(SQLiteSessionService("sessions.db"))

s = await sessions.create_session(app_name="bank", user_id="alice", state={"tier": "gold"})
await sessions.append_text(s.id, Role.USER, "Hello")
await sessions.update_state(s.id, last_seen="2026-01-01")
ids = await sessions.list_sessions(app_name="bank", user_id="alice")

Pass a session_id to agent.run(...) to make a conversation durable and multi-turn — prior history is replayed automatically:

await agent.run("My name is Alice.", session_id=s.id)
await agent.run("What's my name?", session_id=s.id)   # remembers

Backends: InMemorySessionService (default), SQLiteSessionService, and your own (Postgres/Redis) implementing the SessionService protocol.

Memory (long-term, vector)

from yaab import MemoryManager
from yaab.memory import InMemoryVectorMemory
from yaab.memory.embedders import LiteLLMEmbedder

memory = MemoryManager(InMemoryVectorMemory(embedder=LiteLLMEmbedder("openai/text-embedding-3-small")))

await memory.add("Alice prefers email contact", app_name="bank", user_id="alice")
hits = await memory.search("how should we reach Alice?", app_name="bank", user_id="alice", k=3)
for record, score in hits:
    print(score, record.text)

Retrieval uses the Rust-accelerated cosine/top-k (yaab._core), with a pure-Python fallback. The default embedder is a deterministic hashing stub for offline use; swap in LiteLLMEmbedder (or any Callable[[str], list[float]]) for production.

Ingest a session into memory

session = await sessions.get_session(app_name="bank", user_id="alice", session_id=s.id)
await memory.add_session_to_memory(session, app_name="bank", user_id="alice")

Attach a MemoryService to a Runner to fold retrieved memories into the system prompt automatically:

from yaab import Runner

runner = Runner(memory_service=InMemoryVectorMemory())

When the backend is namespace-aware (a MemoryManager), the Runner threads the run's identity into the search as user_id, and a memory_app_name as app_name, so scoped memory is reachable from the Agent path — and stays isolated across users:

memory = MemoryManager()
await memory.add("Alice's deadline is March 15.", app_name="bank", user_id="alice")

runner = Runner(memory_service=memory, memory_app_name="bank")
await runner.run(agent, "When is my deadline?", identity="alice")  # recalls it
await runner.run(agent, "When is my deadline?", identity="bob")    # sees nothing

Without identity/memory_app_name the search targets the default namespace.

Artifacts (versioned blobs)

from yaab import ArtifactManager

artifacts = ArtifactManager()
v1 = await artifacts.save("report.pdf", pdf_bytes, mime_type="application/pdf", session_id=s.id)
v2 = await artifacts.save("report.pdf", new_bytes, session_id=s.id)   # version 2
latest = await artifacts.load("report.pdf", session_id=s.id)
first  = await artifacts.load("report.pdf", version=1, session_id=s.id)

All three managers are backend-agnostic — the in-memory defaults are for dev; production swaps the service while the manager API stays identical.