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
6 changes: 4 additions & 2 deletions docs/extensions/adk/adapters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ Import from the adapter's ``adk`` subpackage:
)

Artifact service base classes live under ``sqlspec.extensions.adk.artifact``.
They require a concrete metadata-store implementation and are not exported from
adapter ``adk`` packages.
Google Cloud Spanner also provides concrete artifact metadata stores
(:class:`~sqlspec.adapters.spanner.adk.SpannerSyncADKArtifactStore` and
:class:`~sqlspec.adapters.spanner.adk.SpannerAsyncADKArtifactStore`) in
``sqlspec.adapters.spanner.adk``.

Example
=======
Expand Down
24 changes: 23 additions & 1 deletion docs/extensions/adk/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ Artifact Stores
:show-inheritance:
:no-index:

.. autoclass:: sqlspec.adapters.spanner.adk.SpannerAsyncADKArtifactStore
:members:
:undoc-members:
:show-inheritance:
:no-index:

.. autoclass:: sqlspec.adapters.spanner.adk.SpannerSyncADKArtifactStore
:members:
:undoc-members:
:show-inheritance:
:no-index:

Session Listing & Ordering
==========================

Expand Down Expand Up @@ -210,7 +222,7 @@ Configuration
:show-inheritance:
:no-index:

Use these types to set up search on PostgreSQL:
Use these types to set up search and storage on PostgreSQL and Spanner:

.. autoclass:: sqlspec.adapters.asyncpg.adk.AsyncpgADKConfig
:members:
Expand All @@ -222,6 +234,16 @@ Use these types to set up search on PostgreSQL:
:show-inheritance:
:no-index:

.. autoclass:: sqlspec.adapters.spanner.adk.SpannerADKConfig
:members:
:show-inheritance:
:no-index:

.. autoclass:: sqlspec.adapters.spanner.adk.SpannerADKRetentionConfig
:members:
:show-inheritance:
:no-index:

Session Converters
==================

Expand Down
37 changes: 24 additions & 13 deletions docs/extensions/adk/backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,11 @@ Artifact Storage
----------------

The ADK artifact service API and base metadata-store contracts are available in
``sqlspec.extensions.adk.artifact``. Adapter-specific concrete artifact
metadata stores are not part of this support matrix; session/event and memory
support are the backend guarantees listed above.
``sqlspec.extensions.adk.artifact``. Google Cloud Spanner provides concrete
sync and async artifact metadata stores
(:class:`~sqlspec.adapters.spanner.adk.SpannerSyncADKArtifactStore` and
:class:`~sqlspec.adapters.spanner.adk.SpannerAsyncADKArtifactStore`) in
``sqlspec.adapters.spanner.adk``.

Backend Details
===============
Expand Down Expand Up @@ -330,18 +332,27 @@ Spanner
-------

Google Cloud Spanner provides globally distributed ADK storage in both native
async (``SpannerAsyncADKStore`` / ``SpannerAsyncADKMemoryStore``) and sync
(``SpannerSyncADKStore`` / ``SpannerSyncADKMemoryStore``) modes:
async (``SpannerAsyncADKStore``, ``SpannerAsyncADKMemoryStore``, and
``SpannerAsyncADKArtifactStore``) and sync (``SpannerSyncADKStore``,
``SpannerSyncADKMemoryStore``, and ``SpannerSyncADKArtifactStore``) modes:

- Cloud-managed, horizontally scalable.
- Cloud-managed, horizontally scalable with strong consistency across regions.
- Optional hash sharding via ``shard_count`` to reduce hot spots.
- Full-text search support for memory entries with ``TOKENIZE_FULLTEXT`` and
search indexes.
- Explicit table/index option passthrough for deployments that need
Spanner-specific DDL tuning.
- Native row-deletion TTL policies generated from ADK retention settings.
- Strong consistency across regions.
- Suitable for multi-region agent deployments.
- Atomic batched memory deduplication via ``WHERE event_id IN UNNEST(@event_ids)``
inside a single ``run_in_transaction`` call backed by ``idx_{table}_event_id``.
- Vector embeddings (``embedding ARRAY<FLOAT32>``) with optional ``CREATE VECTOR
INDEX ... STORING (...)`` and 4-mode ``search_entries`` retrieval: Hybrid
Reciprocal Rank Fusion (RRF, ``k=60``) combining ``APPROX_COSINE_DISTANCE`` and
``SEARCH()`` / ``SCORE()``, Vector-Only, Full-Text Search (ordered by
``SCORE(content_tokens, @query) DESC``), and Simple ``LIKE`` fallback.
- Optional Spanner Property Graph DDL overlay (``enable_memory_graph=True``)
defining ``CREATE OR REPLACE PROPERTY GRAPH`` over sessions and memories for
``GRAPH_TABLE`` Hybrid GraphRAG traversals.
- Concrete artifact metadata stores (``adk_artifact``) with composite primary key
``(app_name, user_id, session_id, filename, version)`` and automatic
user-scoped ``session_id=None`` normalization.
- Explicit table/index option passthrough and native ``ROW DELETION POLICY`` TTL
policies generated from ADK retention settings.

BigQuery (Analytics Replica)
----------------------------
Expand Down
39 changes: 27 additions & 12 deletions docs/extensions/adk/schema.rst
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,12 @@ pgvector Recall and Search Strategies
When querying memories via :meth:`~sqlspec.extensions.adk.SQLSpecMemoryService.search_memory`,
SQLSpec supports multiple retrieval modes depending on adapter capabilities and configuration:

1. **Semantic Vector Search (pgvector)**
1. **Semantic Vector Search (pgvector & Spanner)**
When an ``embedding`` vector is passed, PostgreSQL uses the pgvector cosine distance
operator (``embedding <=> :query_vector::vector``) to rank entries by semantic similarity:
operator (``embedding <=> :query_vector::vector``) and Google Cloud Spanner uses
``APPROX_COSINE_DISTANCE(embedding, @embedding, options => JSON '{"num_leaves_to_search": 50}')``
(or ``COSINE_DISTANCE(embedding, @embedding)`` when ``vector_index_enabled=False``)
to rank entries by semantic similarity:

.. code-block:: sql

Expand All @@ -323,21 +326,26 @@ SQLSpec supports multiple retrieval modes depending on adapter capabilities and
ORDER BY embedding <=> $3::float8[]::vector ASC, timestamp DESC
LIMIT $4

Vector indexes are configured via ``vector_index_type``:
On PostgreSQL, vector indexes are configured via ``vector_index_type``:
- ``"hnsw"`` (default): Hierarchical Navigable Small World graphs with ``vector_cosine_ops``.
- ``"ivfflat"``: Inverted file index with vector cosine distance.
- ``"scann"``: AlloyDB/ScaNN tree quantization index (tuned via ``scann_num_leaves`` and ``scann_quantizer``).

On Spanner, setting ``vector_index_enabled=True`` and ``vector_dimensions=768`` emits an
``ARRAY<FLOAT32>(vector_length=>768)`` column and a ``CREATE VECTOR INDEX ... STORING (...)``
index tuned via ``scann_tree_depth``, ``scann_num_leaves``, and ``vector_distance_type``.

2. **Full-Text Search (FTS)**
When ``memory_use_fts`` is ``True``, backends create native full-text indexes (GIN on
``to_tsvector('english', content_text)`` on PostgreSQL, FTS5 on SQLite, InnoDB FT on MySQL,
Oracle Text on Oracle, or ``TOKENIZE_FULLTEXT`` on Spanner). Queries execute using native
stemmed text matching.
Oracle Text on Oracle, or ``TOKENIZE_FULLTEXT`` with ``SCORE(content_tokens, @query) DESC``
ranking on Spanner). Queries execute using native stemmed text matching.

3. **Hybrid Search with Reciprocal Rank Fusion (RRF)**
When both a text ``query`` and a vector ``embedding`` are provided and ``enable_bm25=True``
(using PostgreSQL 17/18 with ``pg_textsearch`` or AlloyDB), SQLSpec executes a hybrid query
fusing dense vector search and sparse BM25 text rank using Reciprocal Rank Fusion:
When both a text ``query`` and a vector ``embedding`` are provided and hybrid search is enabled
(``enable_bm25=True`` on PostgreSQL 17/18 with ``pg_textsearch`` or AlloyDB, or
``enable_hybrid_search=True`` with ``memory_use_fts=True`` on Spanner), SQLSpec executes a hybrid
query fusing dense vector search and sparse lexical rank using Reciprocal Rank Fusion:

.. code-block:: sql

Expand Down Expand Up @@ -371,10 +379,15 @@ Indexes Created on Memory Table
- ``idx_{table}_app_scope_user_time``: composite index on ``(app_name, scope, user_id, timestamp DESC)``
- ``idx_{table}_scope``: composite index on ``(app_name, scope)``
- ``idx_{table}_session``: index on ``(session_id)``
- ``idx_{table}_event_id``: index on ``(event_id)`` (Spanner batched deduplication lookup)
- ``idx_{table}_fts``: GIN index on ``to_tsvector('english', content_text)`` (when ``memory_use_fts=True``)
- ``idx_{table}_bm25``: BM25 index on ``content_text`` (when ``enable_bm25=True``)
- ``idx_{table}_{index_type}``: HNSW, IVFFlat, or ScaNN vector index on ``embedding``

On Spanner, setting ``enable_memory_graph=True`` also creates a ``CREATE OR REPLACE PROPERTY GRAPH``
overlay connecting the sessions node table and memory entries edge table for ``GRAPH_TABLE``
queries.

.. _artifact-schema:

Artifact Metadata Table Contract
Expand All @@ -400,7 +413,7 @@ Default name: ``adk_artifact``
- User identifier.
* - ``session_id``
- ``VARCHAR`` / ``TEXT`` (nullable)
- Session identifier. NULL for user-scoped artifacts.
- Session identifier. NULL for user-scoped artifacts (persisted as ``""`` in Spanner's ``NOT NULL`` primary key and restored to ``None`` on read).
* - ``filename``
- ``VARCHAR`` / ``TEXT``
- Artifact filename.
Expand Down Expand Up @@ -428,9 +441,11 @@ Table and Extension Configuration
All table names and extension settings are configured through ``extension_config["adk"]``:

``sqlspec.config.ADKConfig`` describes shared settings. Use
``sqlspec.adapters.asyncpg.adk.AsyncpgADKConfig`` or
``sqlspec.adapters.psycopg.adk.PsycopgADKConfig`` when typing PostgreSQL vector,
BM25, or ScaNN options. These options are rejected by other adapters.
``sqlspec.adapters.asyncpg.adk.AsyncpgADKConfig``,
``sqlspec.adapters.psycopg.adk.PsycopgADKConfig``, or
``sqlspec.adapters.spanner.adk.SpannerADKConfig`` when typing backend-specific
vector, BM25, ScaNN, or Property Graph options. These options are rejected by
other adapters.

.. code-block:: python

Expand Down
8 changes: 8 additions & 0 deletions docs/reference/adapters/spanner.rst
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ Extensions
:members:
:show-inheritance:

.. autoclass:: sqlspec.adapters.spanner.adk.SpannerSyncADKArtifactStore
:members:
:show-inheritance:

.. autoclass:: sqlspec.adapters.spanner.adk.SpannerAsyncADKArtifactStore
:members:
:show-inheritance:

.. autoclass:: sqlspec.adapters.spanner.litestar.SpannerSyncStore
:members:
:show-inheritance:
Expand Down
61 changes: 61 additions & 0 deletions docs/reference/dialects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,67 @@ Spanner
:show-inheritance:
:no-index:

The ``spanner`` (GoogleSQL) and ``spangres`` (PostgreSQL-interface) dialects
support Spanner-specific DDL and analytical SQL:

* **Table Hierarchy & TTL**: ``INTERLEAVE IN [PARENT] <parent> [ON DELETE CASCADE | NO ACTION]`` and bidirectional transpilation between ``ROW DELETION POLICY (OLDER_THAN(col, INTERVAL n DAY))`` (``spanner``) and ``TTL INTERVAL 'n days' ON col`` (``spangres``).
* **Property Graph**: ``CREATE [OR REPLACE] PROPERTY GRAPH [IF NOT EXISTS] ... NODE TABLES (...) [EDGE TABLES (...)]``, ``DROP PROPERTY GRAPH [IF EXISTS]``, and ``GRAPH_TABLE(<graph> MATCH <pattern> [WHERE <expr>] COLUMNS (...))``.
* **Vector Search**: ``FLOAT32`` and ``ARRAY<FLOAT32>(vector_length => N)`` columns, ``CREATE VECTOR INDEX [IF NOT EXISTS] ... [STORING (...)] WHERE col IS NOT NULL OPTIONS (...)``, ``DROP VECTOR INDEX [IF EXISTS]``, and distance functions (``COSINE_DISTANCE``, ``APPROX_COSINE_DISTANCE``, ``EUCLIDEAN_DISTANCE``, ``DOT_PRODUCT``).
* **Full-Text Search**: ``TOKENLIST`` generated columns (including ``AS (TOKENIZE_FULLTEXT(...)) HIDDEN``), ``CREATE SEARCH INDEX``, ``DROP SEARCH INDEX [IF EXISTS]``, ``SEARCH()``, ``SEARCH_SUBSTRING()``, ``SCORE()``, ``SCORE_NGRAMS()``, and ``SNIPPET()``.
* **Sequences, Returning & Hints**: ``CREATE/ALTER SEQUENCE``, ``GET_NEXT_SEQUENCE_VALUE(SEQUENCE seq)``, ``THEN RETURN [WITH ACTION]``, and ``@{FORCE_INDEX=...}`` / ``/*@ ... */`` hints.

.. autoclass:: sqlspec.dialects.spanner.SpannerPropertyGraph
:members:
:show-inheritance:
:no-index:

.. autoclass:: sqlspec.dialects.spanner.SpannerGraphTable
:members:
:show-inheritance:
:no-index:

.. autofunction:: sqlspec.dialects.spanner.graph_table
:no-index:

.. autofunction:: sqlspec.dialects.spanner.approx_cosine_distance
:no-index:

.. autofunction:: sqlspec.dialects.spanner.cosine_distance
:no-index:

.. autofunction:: sqlspec.dialects.spanner.euclidean_distance
:no-index:

.. autofunction:: sqlspec.dialects.spanner.dot_product
:no-index:

.. autofunction:: sqlspec.dialects.spanner.tokenize_fulltext
:no-index:

.. autofunction:: sqlspec.dialects.spanner.tokenize_substring
:no-index:

.. autofunction:: sqlspec.dialects.spanner.tokenize_ngrams
:no-index:

.. autofunction:: sqlspec.dialects.spanner.search
:no-index:

.. autofunction:: sqlspec.dialects.spanner.search_substring
:no-index:

.. autofunction:: sqlspec.dialects.spanner.score
:no-index:

.. autofunction:: sqlspec.dialects.spanner.score_ngrams
:no-index:

.. autofunction:: sqlspec.dialects.spanner.snippet
:no-index:

.. autofunction:: sqlspec.dialects.spanner.get_next_sequence_value
:no-index:

Expression Types
================

Expand Down
8 changes: 8 additions & 0 deletions docs/reference/extensions/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,14 @@ that the backend cannot honor raises ``ImproperConfigurationError``.
* Oracle Database accepts ``compression``, ``partitioning``, ``in_memory``,
and ``table_options``. See :ref:`oracledb-extension-storage-options` for how
SQLSpec handles optional database capabilities.
* Google Cloud Spanner accepts ``shard_count`` (adds a generated ``shard_id``
prefix to the primary key and covering index to prevent split hotspotting)
and ``retention_seconds`` (generates a native ``ROW DELETION POLICY
(OLDER_THAN(acknowledged_at, INTERVAL N DAY))`` on GoogleSQL or ``TTL
INTERVAL 'N days' ON acknowledged_at`` on ``spangres`` instead of running a
synchronous ``DELETE`` on every ``ack()``). Spanner queues also poll with
lock-free read-only snapshots (``transaction=False``) and execute DML writes
inside ``run_in_transaction`` for automatic ``Aborted`` conflict retries.

CockroachDB deliberately does not expose its session-table row TTL for durable
queues. Queue acknowledgement and retention have different semantics, so
Expand Down
3 changes: 3 additions & 0 deletions sqlspec/adapters/spanner/adk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Spanner ADK store exports."""

from sqlspec.adapters.spanner.adk.artifact_store import SpannerAsyncADKArtifactStore, SpannerSyncADKArtifactStore
from sqlspec.adapters.spanner.adk.store import (
SpannerADKConfig,
SpannerADKRetentionConfig,
Expand All @@ -12,8 +13,10 @@
__all__ = (
"SpannerADKConfig",
"SpannerADKRetentionConfig",
"SpannerAsyncADKArtifactStore",
"SpannerAsyncADKMemoryStore",
"SpannerAsyncADKStore",
"SpannerSyncADKArtifactStore",
"SpannerSyncADKMemoryStore",
"SpannerSyncADKStore",
)
Loading
Loading