Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
767 changes: 348 additions & 419 deletions sqlspec/adapters/cockroach_psycopg/adk/store.py

Large diffs are not rendered by default.

444 changes: 201 additions & 243 deletions sqlspec/adapters/mysqlconnector/adk/store.py

Large diffs are not rendered by default.

2,415 changes: 1,131 additions & 1,284 deletions sqlspec/adapters/oracledb/adk/store.py

Large diffs are not rendered by default.

765 changes: 343 additions & 422 deletions sqlspec/adapters/psycopg/adk/store.py

Large diffs are not rendered by default.

575 changes: 242 additions & 333 deletions sqlspec/adapters/sqlite/adk/store.py

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions sqlspec/extensions/adk/_config_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ def _adk_session_store_config(config: _ADKConfigSource) -> _ADKSessionStoreConfi
"user_state_table": str(adk_config.get("user_state_table") or "adk_user_state"),
"metadata_table": str(adk_config.get("metadata_table") or "adk_internal_metadata"),
}
owner_id = adk_config.get("owner_id_column")
if owner_id is not None:
result["owner_id_column"] = cast("str", owner_id)
_apply_owner_id(result, adk_config)
return result


Expand All @@ -94,9 +92,7 @@ def _adk_memory_store_config(config: _ADKConfigSource) -> _ADKMemoryStoreConfig:
"use_fts": bool(adk_config.get("memory_use_fts", False)),
"max_results": int(max_results) if isinstance(max_results, int) else 20,
}
owner_id = adk_config.get("owner_id_column")
if owner_id is not None:
result["owner_id_column"] = cast("str", owner_id)
_apply_owner_id(result, adk_config)
return result


Expand All @@ -107,6 +103,13 @@ def _adk_artifact_store_config(config: _ADKConfigSource) -> _ADKArtifactStoreCon
return {"artifact_table": str(adk_config.get("artifact_table") or "adk_artifact")}


def _apply_owner_id(result: "_ADKSessionStoreConfig | _ADKMemoryStoreConfig", adk_config: dict[str, Any]) -> None:
"""Copy the configured owner column into normalized store settings."""
owner_id = adk_config.get("owner_id_column")
if owner_id is not None:
result["owner_id_column"] = cast("str", owner_id)


def _adk_store_path(config: Any, store_suffix: str) -> str:
"""Return the adapter-specific ADK store import path."""

Expand Down
25 changes: 13 additions & 12 deletions sqlspec/extensions/adk/artifact/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,7 @@ async def save_artifact(

# Write content first (fail-fast before metadata)
backend = self._registry.get(self._artifact_storage_uri)
if hasattr(backend, "write_bytes_async"):
await backend.write_bytes_async(content_path, content_bytes)
else:
backend.write_bytes_sync(content_path, content_bytes)
await _call_storage_backend(backend, "write_bytes_async", "write_bytes_sync", content_path, content_bytes)

# Insert metadata row
from datetime import datetime, timezone
Expand Down Expand Up @@ -335,10 +332,7 @@ async def load_artifact(
content_path = record["canonical_uri"].removeprefix(self._artifact_storage_uri + "/")

backend = self._registry.get(self._artifact_storage_uri)
if hasattr(backend, "read_bytes_async"):
content_bytes = await backend.read_bytes_async(content_path)
else:
content_bytes = backend.read_bytes_sync(content_path)
content_bytes = await _call_storage_backend(backend, "read_bytes_async", "read_bytes_sync", content_path)

log_with_context(
logger,
Expand Down Expand Up @@ -400,10 +394,7 @@ async def delete_artifact(
for record in deleted_records:
content_path = record["canonical_uri"].removeprefix(self._artifact_storage_uri + "/")
try:
if hasattr(backend, "delete_async"):
await backend.delete_async(content_path)
else:
backend.delete_sync(content_path)
await _call_storage_backend(backend, "delete_async", "delete_sync", content_path)
except Exception:
log_with_context(
logger,
Expand Down Expand Up @@ -488,3 +479,13 @@ async def get_artifact_version(
if record is None:
return None
return _record_to_artifact_version(record)


async def _call_storage_backend(
backend: Any, async_method_name: str, sync_method_name: str, *args: Any, **kwargs: Any
) -> Any:
"""Call the available async or sync storage-backend capability."""
async_method = getattr(backend, async_method_name, None)
if async_method is not None:
return await async_method(*args, **kwargs)
return getattr(backend, sync_method_name)(*args, **kwargs)
55 changes: 20 additions & 35 deletions sqlspec/extensions/adk/artifact/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,8 @@
logger = get_logger("sqlspec.extensions.adk.artifact.store")


class BaseAsyncADKArtifactStore(ABC, Generic[ConfigT]):
"""Base class for async SQLSpec-backed ADK artifact metadata stores.

Manages artifact version metadata in a SQL table. Content bytes are
stored externally via ``sqlspec/storage/`` backends and referenced
by canonical URI in each metadata row.

Subclasses must implement dialect-specific SQL queries.

Args:
config: SQLSpec database configuration with extension_config["adk"] settings.
"""
class _ADKArtifactStoreCommon(Generic[ConfigT]):
"""Shared non-async ADK store state and helpers."""

__slots__ = ("_artifact_table", "_config")

Expand All @@ -64,6 +54,22 @@ def artifact_table(self) -> str:
"""Return the artifact versions table name."""
return self._artifact_table


class BaseAsyncADKArtifactStore(_ADKArtifactStoreCommon[ConfigT], ABC):
"""Base class for async SQLSpec-backed ADK artifact metadata stores.

Manages artifact version metadata in a SQL table. Content bytes are
stored externally via ``sqlspec/storage/`` backends and referenced
by canonical URI in each metadata row.

Subclasses must implement dialect-specific SQL queries.

Args:
config: SQLSpec database configuration with extension_config["adk"] settings.
"""

__slots__ = ()

@abstractmethod
async def insert_artifact(self, record: "ArtifactRecord") -> None:
"""Insert an artifact version metadata row.
Expand Down Expand Up @@ -179,7 +185,7 @@ async def ensure_table(self) -> None:
)


class BaseSyncADKArtifactStore(ABC, Generic[ConfigT]):
class BaseSyncADKArtifactStore(_ADKArtifactStoreCommon[ConfigT], ABC):
"""Base class for sync SQLSpec-backed ADK artifact metadata stores.

Synchronous counterpart of :class:`BaseAsyncADKArtifactStore`.
Expand All @@ -188,28 +194,7 @@ class BaseSyncADKArtifactStore(ABC, Generic[ConfigT]):
config: SQLSpec database configuration with extension_config["adk"] settings.
"""

__slots__ = ("_artifact_table", "_config")

def __init__(self, config: ConfigT) -> None:
"""Initialize the sync ADK artifact store.

Args:
config: SQLSpec database configuration.
"""
self._config = config
store_config = _adk_artifact_store_config(self._config)
self._artifact_table: str = store_config["artifact_table"]
ensure_table_name(self._artifact_table)

@property
def config(self) -> ConfigT:
"""Return the database configuration."""
return self._config

@property
def artifact_table(self) -> str:
"""Return the artifact versions table name."""
return self._artifact_table
__slots__ = ()

@abstractmethod
def insert_artifact(self, record: "ArtifactRecord") -> None:
Expand Down
28 changes: 20 additions & 8 deletions sqlspec/extensions/adk/memory/service.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""SQLSpec-backed memory service for Google ADK."""

from typing import TYPE_CHECKING
import inspect
from typing import TYPE_CHECKING, Any, cast

from google.adk.memory.base_memory_service import BaseMemoryService, SearchMemoryResponse

Expand All @@ -10,9 +11,10 @@
session_to_memory_records,
)
from sqlspec.utils.logging import get_logger
from sqlspec.utils.sync_tools import async_

if TYPE_CHECKING:
from collections.abc import Mapping, Sequence
from collections.abc import Callable, Mapping, Sequence

from google.adk.events.event import Event
from google.adk.memory.memory_entry import MemoryEntry
Expand All @@ -39,7 +41,7 @@ class SQLSpecMemoryService(BaseMemoryService):
store: Database store implementation.
"""

def __init__(self, store: "BaseAsyncADKMemoryStore") -> None:
def __init__(self, store: "BaseAsyncADKMemoryStore | BaseSyncADKMemoryStore") -> None:
"""Initialize the memory service.

Args:
Expand All @@ -48,7 +50,7 @@ def __init__(self, store: "BaseAsyncADKMemoryStore") -> None:
self._store = store

@property
def store(self) -> "BaseAsyncADKMemoryStore":
def store(self) -> "BaseAsyncADKMemoryStore | BaseSyncADKMemoryStore":
"""Return the database store."""
return self._store

Expand All @@ -73,7 +75,7 @@ async def add_session_to_memory(self, session: "Session") -> None:
)
return

inserted_count = await self._store.insert_memory_entries(records)
inserted_count = await self._call_store("insert_memory_entries", records)
logger.debug(
"Stored %d memory entries for session %s (total events: %d)", inserted_count, session.id, len(records)
)
Expand Down Expand Up @@ -121,7 +123,7 @@ async def add_events_to_memory(
)
return

inserted_count = await self._store.insert_memory_entries(records)
inserted_count = await self._call_store("insert_memory_entries", records)
logger.debug(
"Stored %d memory entries from %d events (app=%s, user=%s)", inserted_count, len(records), app_name, user_id
)
Expand Down Expand Up @@ -161,7 +163,7 @@ async def add_memory(
logger.debug("No content to store for memories (app=%s, user=%s)", app_name, user_id)
return

inserted_count = await self._store.insert_memory_entries(records)
inserted_count = await self._call_store("insert_memory_entries", records)
logger.debug(
"Stored %d memory entries from %d memories (app=%s, user=%s)",
inserted_count,
Expand All @@ -183,14 +185,24 @@ async def search_memory(self, *, app_name: str, user_id: str, query: str) -> "Se
Returns:
SearchMemoryResponse with memories: List[MemoryEntry].
"""
records = await self._store.search_entries(query=query, app_name=app_name, user_id=user_id)
records = await self._call_store("search_entries", query=query, app_name=app_name, user_id=user_id)

memories = records_to_memory_entries(records)

logger.debug("Found %d memories for query '%s' (app=%s, user=%s)", len(memories), query[:50], app_name, user_id)

return SearchMemoryResponse(memories=memories)

async def _call_store(self, method_name: str, *args: Any, **kwargs: Any) -> Any:
"""Call an async store method or bridge a sync store method."""
method = getattr(self._store, method_name)
if inspect.iscoroutinefunction(method):
return await method(*args, **kwargs)
sync_method = method
if TYPE_CHECKING:
sync_method = cast("Callable[..., Any]", method)
return await async_(sync_method)(*args, **kwargs)


class SQLSpecSyncMemoryService:
"""Synchronous SQLSpec-backed memory service.
Expand Down
Loading
Loading