State scoping & the AG-UI layer¶
Prefix-scoped state¶
A session's state is session-scoped by default. Real apps also need values that
outlive one session, span the whole app, or must never be persisted. YAAB uses
a key-prefix convention (app:, user:, temp:), via the State object:
| Prefix | Scope | Persisted? |
|---|---|---|
app:<key> |
shared across all users & sessions | yes (app store) |
user:<key> |
shared across one user's sessions | yes (user store) |
temp:<key> |
current run only | no |
<key> |
this session | yes (session store) |
from yaab import SessionManager
mgr = SessionManager()
s = await mgr.create_session(app_name="bank", user_id="alice")
state = await mgr.resolve_state(s.id, app_name="bank", user_id="alice")
state["app:region"] = "eu" # global to the app
state["user:tier"] = "gold" # all of alice's sessions
state["draft"] = "..." # only this session
state["temp:otp_ok"] = True # ephemeral, never written
await mgr.save_state(s.id, state) # persists everything except temp:
A second session for alice sees app: and user: keys; a different user sees
app: but not alice's user: state. State is a MutableMapping, so it behaves
like a dict (in, len, iteration, del) while routing each key to the right
backing store. state.persisted() returns the durable subset.
AG-UI compatibility middleware¶
AG-UI is the emerging protocol (popularized by CopilotKit) for connecting agent backends to chat/coagent frontends over a standard streamed event schema. YAAB ships a middleware that translates its native event stream into AG-UI events — so any AG-UI frontend drives a YAAB agent with no custom glue. It's a translation layer, not a dependency.
Stream AG-UI events¶
from yaab.agui import run_agui
async for event in run_agui(agent, "Summarize the Q3 report"):
print(event["type"], event) # RUN_STARTED, TEXT_MESSAGE_CONTENT, TOOL_CALL_START, ...
YAAB events map to the AG-UI vocabulary:
| YAAB event | AG-UI event(s) |
|---|---|
| run start | RUN_STARTED |
MODEL_DELTA (token) |
TEXT_MESSAGE_START / TEXT_MESSAGE_CONTENT / TEXT_MESSAGE_END |
MODEL_DELTA (reasoning) |
THINKING_TEXT_MESSAGE_CONTENT |
TOOL_CALL |
TOOL_CALL_START / TOOL_CALL_ARGS / TOOL_CALL_END |
TOOL_RESULT |
TOOL_CALL_RESULT |
FINAL_OUTPUT |
text message (if not already streamed) |
| run end | RUN_FINISHED |
| error | RUN_ERROR |
Serve over SSE¶
from yaab.agui import agui_sse_app
app = agui_sse_app(agent) # POST /agui → SSE stream of AG-UI events
# uvicorn module:app
The endpoint accepts either a simple {"prompt": "..."} body or an AG-UI run
input {"threadId": ..., "messages": [{"role": "user", "content": "..."}]}. Auth
is pluggable via yaab.auth (same schemes as serving).