Google ADK Integration¶
sqlspec.extensions.adk integrates SQLSpec with Google Agent Development Kit (google-adk >= 2.10.0), providing persistent database-backed implementations of ADK's BaseSessionService (SQLSpecSessionService), BaseMemoryService (SQLSpecMemoryService), and BaseArtifactService (SQLSpecArtifactService) across 19 database adapters.
Installation¶
Architecture and Store Services¶
| ADK Service | SQLSpec Class | Adapter Store Base | Responsibility |
|---|---|---|---|
| Session & Event Store | SQLSpecSessionService |
BaseAsyncADKStore / BaseSyncADKStore |
Persists conversation sessions, structured state (app:, user:, session state), and ordered Event records with optimistic revision checks and per-session concurrency locks. |
| Long-Term Memory Store | SQLSpecMemoryService |
BaseAsyncADKMemoryStore / BaseSyncADKMemoryStore |
Indexes conversation turns and tool outputs into adk_memory_entries with substring (simple), full-text (fts), vector (vector), and Reciprocal Rank Fusion (hybrid) search. |
| Artifact Store | SQLSpecArtifactService |
BaseAsyncADKArtifactStore / BaseSyncADKArtifactStore |
Stores versioned binary and text artifacts scoped by (app_name, user_id, session_id, filename, version) with user:-prefixed cross-session sharing and artifact:// resolution. |
Configuring extension_config["adk"]¶
Configure table names, full-text indexing, vector dimensions, and optional owner foreign keys on any adapter via extension_config["adk"] (ADKConfig):
"""Configure Google ADK session, event, and memory table names on an ADBC adapter."""
from sqlspec.adapters.adbc import AdbcConfig
__all__ = ("main",)
def main() -> AdbcConfig:
"""Attach ADK store table settings via extension_config on AdbcConfig."""
adk_config = {
"session_table": "adk_session",
"events_table": "adk_event",
"memory_table": "adk_memory",
"memory_use_fts": True,
}
config = AdbcConfig(
connection_config={"driver_name": "gizmosql", "gizmosql_backend": "duckdb"},
extension_config={"adk": adk_config},
)
adk_settings = config.extension_config.get("adk")
assert isinstance(adk_settings, dict)
assert adk_settings.get("memory_use_fts") is True
return config
if __name__ == "__main__":
print(main())
Key ADKConfig Options¶
| Key | Type | Default | Description |
|---|---|---|---|
session_table |
str |
"adk_sessions" |
Table storing session metadata and serialized state JSON. |
events_table |
str |
"adk_events" |
Table storing ordered conversation events linked to session_table. |
memory_table |
str |
"adk_memory_entries" |
Table storing searchable long-term memory entries. |
artifact_table |
str |
"adk_artifacts" |
Table storing versioned binary/text agent artifacts. |
memory_use_fts |
bool |
False |
Enables native full-text search indexing (FTS5 on SQLite, tsvector/GIN or ParadeDB BM25 on PostgreSQL, CONTAINS on Oracle Text, FULLTEXT on MySQL/MSSQL, SEARCH on Spanner). |
memory_use_vector |
bool |
False |
Adds vector embedding storage and index DDL (pgvector, Oracle 26ai VECTOR, Spanner ARRAY<FLOAT32>, DuckDB FLOAT[N]). |
memory_vector_dimensions |
int |
768 |
Embedding vector dimensionality when memory_use_vector=True. |
owner_id_column |
str | None |
None |
Optional DDL definition (for example, "tenant_id UUID NOT NULL REFERENCES tenants(id)") linking sessions to an application table. |
Persisting Sessions and Events¶
Instantiate the adapter's *ADKStore (such as AiosqliteADKStore, AsyncpgADKStore, OracleAsyncADKStore, or SpannerAsyncADKStore), call await store.ensure_tables(), and pass the store to SQLSpecSessionService(store) or call the store directly:
"""Persist and reload Google ADK session events using AiosqliteADKStore."""
import asyncio
from datetime import UTC, datetime
from google.adk.events.event import Event
from google.genai import types
from sqlspec.adapters.aiosqlite import AiosqliteConfig
from sqlspec.adapters.aiosqlite.adk import AiosqliteADKStore
from sqlspec.extensions.adk import StoredEvent
from sqlspec.extensions.adk.converters import event_to_record
__all__ = ("main",)
async def main() -> list[StoredEvent]:
"""Create an ADK session, append a user event, and fetch stored events."""
config = AiosqliteConfig(connection_config={"database": ":memory:"})
try:
store = AiosqliteADKStore(config)
await store.ensure_tables()
session = await store.create_session(
session_id="session_1", app_name="docs", user_id="user_1", state={"mode": "demo"}
)
event = Event(
id="evt_1",
invocation_id="inv_1",
author="user",
content=types.Content(parts=[types.Part(text="Hello")]),
timestamp=datetime.now(UTC).timestamp(),
)
event_record = event_to_record(event, session["app_name"], session["user_id"], session["id"])
await store.append_event(event_record)
events = await store.get_events(session["app_name"], session["user_id"], session["id"])
assert len(events) == 1
return events
finally:
await config.close_pool()
if __name__ == "__main__":
print(len(asyncio.run(main())))
When wiring an ADK Runner, pass SQLSpecSessionService(store), SQLSpecMemoryService(memory_store), and SQLSpecArtifactService(artifact_store) inside an async context manager (async with SQLSpecSessionService(store) as session_service:) so session locks and resources close cleanly on shutdown.
Full-Text, Vector, and Hybrid RRF Memory Search¶
ADKMemoryStore supports four search modes across engines:
- Simple (
simple): Portable case-insensitive substring matching across all 19 ADK adapters. - Full-Text (
fts): Native lexical ranking (FTS5onsqlite/aiosqlite,tsvectoror ParadeDB@@@BM25 on PostgreSQL, Oracle TextCONTAINSwith"AND","OR","ACCUM", and"NEAR"operators,MATCH ... AGAINSTon MySQL, andSEARCH/SCOREon Spanner). - Vector (
vector): Cosine or inner-product similarity search onasyncpg,psycopg,psqlpy,adbc(pgvector),oracledb(Oracle 26ai HNSWFETCH APPROX FIRSTor exact top-K),spanner(APPROX_COSINE_DISTANCE), andduckdb(array_cosine_similarity). - Hybrid RRF (
hybrid): Single-round-trip Reciprocal Rank Fusion (1 / (k + rank)) combining vector distance and full-text rank in one SQL CTE on PostgreSQL (asyncpg,psycopg,psqlpy,adbc), Oracle 26ai (oracledb), Cloud Spanner (spanner), and DuckDB (duckdb).
"""Index and query tool-generated memories with full-text search in AiosqliteADKMemoryStore."""
import asyncio
from datetime import UTC, datetime
from sqlspec.adapters.aiosqlite import AiosqliteConfig
from sqlspec.adapters.aiosqlite.adk import AiosqliteADKMemoryStore
from sqlspec.extensions.adk import StoredMemory
__all__ = ("main",)
async def main() -> int:
"""Insert a tool invocation memory entry and search it via FTS."""
config = AiosqliteConfig(
connection_config={"database": ":memory:"}, extension_config={"adk": {"memory_use_fts": True}}
)
try:
store = AiosqliteADKMemoryStore(config)
await store.ensure_tables()
record: StoredMemory = {
"id": "mem_1",
"session_id": "session_1",
"app_name": "docs",
"user_id": "user_1",
"scope": "user",
"event_id": "evt_1",
"author": "tool",
"timestamp": datetime.now(UTC),
"content_json": {"tool": "search", "query": "sqlspec"},
"content_text": "tool:search query=sqlspec",
"metadata_json": None,
"inserted_at": datetime.now(UTC),
"embedding": None,
}
await store.insert_memory_entries([record])
results = await store.search_entries(query="sqlspec", app_name="docs", user_id="user_1")
assert len(results) == 1
return len(results)
finally:
await config.close_pool()
if __name__ == "__main__":
print(asyncio.run(main()))
Schema Migrations and CLI Maintenance¶
Instead of calling ensure_tables() at runtime in production, enable "adk" in migration_config={"enable_extensions": ["adk"]} so sqlspec upgrade tracks ADK schema revisions alongside your application migrations.
Use the built-in sqlspec adk memory CLI commands to inspect, clean up, or verify memory stores:
sqlspec --config myapp.db.spec adk memory stats
sqlspec --config myapp.db.spec adk memory cleanup --older-than-days 90
Recap¶
- Configure ADK tables, FTS, and vector search in
extension_config["adk"]. - Pair
SQLSpecSessionService,SQLSpecMemoryService, andSQLSpecArtifactServicewith your adapter'sadkstore classes. - See the Database Capability Matrix for per-adapter ADK vector/FTS support and the ADK Extension Reference for full API signatures.