Schema¶
ADK stores create tables for sessions, events, memory entries, and artifact
metadata. Table names are configurable via extension_config["adk"].
You can programmatically create the schema with create_tables() or
ensure_tables() on a store. For managed deployments, configure SQLSpec
migrations for the target database and run sqlspec upgrade instead.
Sessions Table¶
The sessions table stores agent session metadata and durable state.
Default name: adk_sessions
Column |
Type |
Description |
|---|---|---|
|
|
Primary key. UUID assigned by the service layer. |
|
|
Application identifier. |
|
|
User identifier. |
|
|
Durable session state (see Scoped State Semantics). |
|
|
When the session was created (UTC). |
|
|
Last state update time (UTC). |
An optional owner_id column can be added via owner_id_column in the ADK
config for multi-tenant deployments.
Events Table (EventRecord)¶
The events table uses full-event JSON storage: the entire ADK Event is
serialized into a single event_json column alongside a small set of indexed
scalar columns used for query filtering.
This design eliminates column drift with upstream ADK releases. New Event
fields are automatically captured in event_json without schema changes.
Default name: adk_events
Column |
Type |
Description |
|---|---|---|
|
|
Foreign key to the sessions table. |
|
|
ADK invocation identifier (indexed for filtering). |
|
|
Event author: |
|
|
Event timestamp (UTC, indexed for range queries). |
|
|
Full ADK Event serialized via |
Serialization and reconstruction:
Events are converted to records via event_to_record(), which calls
event.model_dump(exclude_none=True, mode="json") to produce the JSON blob.
Reconstruction is lossless: record_to_event() restores the full Event
via Event.model_validate().
from sqlspec.extensions.adk.converters import event_to_record, record_to_event
# Serialize: Event -> EventRecord
record = event_to_record(event=adk_event, session_id="sess_123")
# Reconstruct: EventRecord -> Event
restored_event = record_to_event(record)
Scoped State Semantics¶
ADK uses key prefixes to scope state visibility across sessions. SQLSpec respects these prefixes when persisting and loading state.
Prefix |
Scope |
Persisted |
Description |
|---|---|---|---|
|
Application |
Yes |
Shared across all sessions for the same |
|
User |
Yes |
Shared across all sessions for the same |
|
Runtime |
No |
Process-local state. Stripped before every write to storage. |
(no prefix) |
Session |
Yes |
Private to a single session. |
How scoped state is handled:
On
create_session(), the service stripstemp:keys before the initialINSERT.On
append_event(), the service callsfilter_temp_state()to produce a durable state snapshot, then callsappend_event_and_update_state()to atomically persist the event and the state update.On
get_session(), state is loaded from the database. Sincetemp:keys were never written, they are absent from the loaded state.
from sqlspec.extensions.adk.converters import filter_temp_state, split_scoped_state
state = {
"app:model_version": "v2",
"user:preferences": {"theme": "dark"},
"temp:scratch_pad": "...",
"conversation_turn": 5,
}
# Strip temp keys before persisting
durable = filter_temp_state(state)
# {"app:model_version": "v2", "user:preferences": {...}, "conversation_turn": 5}
# Split into scoped buckets
app_state, user_state, session_state = split_scoped_state(durable)
# app_state: {"app:model_version": "v2"}
# user_state: {"user:preferences": {"theme": "dark"}}
# session_state: {"conversation_turn": 5}
The append_event_and_update_state() Contract¶
This method is the authoritative durable write boundary for post-creation session mutations. It atomically:
Inserts the event record into the events table.
Updates the session’s durable state in the sessions table.
Both operations succeed together or fail together within a single database transaction.
# Called by SQLSpecSessionService.append_event() internally:
await store.append_event_and_update_state(
event_record=event_record,
session_id=session.id,
state=durable_state, # temp: keys already stripped
)
Why this matters:
Prevents state from advancing without the corresponding event being recorded.
Prevents orphaned events that reference a stale session state.
Ensures that on session reload, the state always reflects all persisted events.
Every backend store implements this as a single transaction (or equivalent atomic operation for the backend’s concurrency model).
Memory Table¶
The memory table stores long-term context entries that agents can search and reference across sessions.
Default name: adk_memory_entries
Column |
Type |
Description |
|---|---|---|
|
|
Primary key. |
|
|
Session that produced this memory. |
|
|
Application identifier. |
|
|
User identifier. |
|
|
Searchable text content (used by FTS). |
|
|
Structured content. |
|
|
When the entry was created. |
When memory_use_fts is enabled in the ADK config, backends create
full-text search indexes on content_text using the database’s native
FTS engine (tsvector, FTS5, InnoDB FT, etc.).
Artifact Metadata Table¶
The artifact table stores versioning metadata for binary artifacts. Content bytes are stored separately in object storage; this table tracks ownership, versioning, and canonical URIs.
Default name: adk_artifact_versions
Column |
Type |
Description |
|---|---|---|
|
|
Application identifier. |
|
|
User identifier. |
|
|
Session identifier. NULL for user-scoped artifacts. |
|
|
Artifact filename. |
|
|
Monotonically increasing version (starts at 0). |
|
|
MIME type of the artifact content. |
|
|
URI pointing to content in object storage. |
|
|
User-defined metadata. |
|
|
When this version was created. |
The composite key is (app_name, user_id, session_id, filename, version).
Table Name Configuration¶
All table names are configurable:
config = AsyncpgConfig(
connection_config={"dsn": "postgresql://..."},
extension_config={
"adk": {
"session_table": "my_sessions", # default: "adk_sessions"
"events_table": "my_events", # default: "adk_events"
"memory_table": "my_memory", # default: "adk_memory_entries"
"artifact_table": "my_artifacts", # default: "adk_artifact_versions"
}
},
)
Table names are validated on store initialization: they must start with a letter or underscore, contain only alphanumeric characters and underscores, and be at most 63 characters long.