sqlspec 0.36.0__cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ac8f31065839703b4e70__mypyc.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/__init__.py +140 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +315 -0
- sqlspec/_typing.py +700 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_typing.py +82 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +1273 -0
- sqlspec/adapters/adbc/config.py +295 -0
- sqlspec/adapters/adbc/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/core.py +735 -0
- sqlspec/adapters/adbc/data_dictionary.py +334 -0
- sqlspec/adapters/adbc/driver.py +529 -0
- sqlspec/adapters/adbc/events/__init__.py +5 -0
- sqlspec/adapters/adbc/events/store.py +285 -0
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +502 -0
- sqlspec/adapters/adbc/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/type_converter.py +140 -0
- sqlspec/adapters/aiosqlite/__init__.py +25 -0
- sqlspec/adapters/aiosqlite/_typing.py +82 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +818 -0
- sqlspec/adapters/aiosqlite/config.py +334 -0
- sqlspec/adapters/aiosqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/aiosqlite/core.py +315 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +208 -0
- sqlspec/adapters/aiosqlite/driver.py +313 -0
- sqlspec/adapters/aiosqlite/events/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/events/store.py +20 -0
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +279 -0
- sqlspec/adapters/aiosqlite/pool.py +533 -0
- sqlspec/adapters/asyncmy/__init__.py +21 -0
- sqlspec/adapters/asyncmy/_typing.py +87 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +703 -0
- sqlspec/adapters/asyncmy/config.py +302 -0
- sqlspec/adapters/asyncmy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncmy/core.py +360 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +124 -0
- sqlspec/adapters/asyncmy/driver.py +383 -0
- sqlspec/adapters/asyncmy/events/__init__.py +5 -0
- sqlspec/adapters/asyncmy/events/store.py +104 -0
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +19 -0
- sqlspec/adapters/asyncpg/_typing.py +88 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +748 -0
- sqlspec/adapters/asyncpg/config.py +569 -0
- sqlspec/adapters/asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncpg/core.py +367 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +162 -0
- sqlspec/adapters/asyncpg/driver.py +487 -0
- sqlspec/adapters/asyncpg/events/__init__.py +6 -0
- sqlspec/adapters/asyncpg/events/backend.py +286 -0
- sqlspec/adapters/asyncpg/events/store.py +40 -0
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +251 -0
- sqlspec/adapters/bigquery/__init__.py +14 -0
- sqlspec/adapters/bigquery/_typing.py +86 -0
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +827 -0
- sqlspec/adapters/bigquery/config.py +353 -0
- sqlspec/adapters/bigquery/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/core.py +715 -0
- sqlspec/adapters/bigquery/data_dictionary.py +128 -0
- sqlspec/adapters/bigquery/driver.py +548 -0
- sqlspec/adapters/bigquery/events/__init__.py +5 -0
- sqlspec/adapters/bigquery/events/store.py +139 -0
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +325 -0
- sqlspec/adapters/bigquery/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/type_converter.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/__init__.py +24 -0
- sqlspec/adapters/cockroach_asyncpg/_typing.py +72 -0
- sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/adk/store.py +410 -0
- sqlspec/adapters/cockroach_asyncpg/config.py +238 -0
- sqlspec/adapters/cockroach_asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
- sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/driver.py +144 -0
- sqlspec/adapters/cockroach_asyncpg/events/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/events/store.py +20 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/store.py +142 -0
- sqlspec/adapters/cockroach_psycopg/__init__.py +38 -0
- sqlspec/adapters/cockroach_psycopg/_typing.py +129 -0
- sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
- sqlspec/adapters/cockroach_psycopg/adk/store.py +868 -0
- sqlspec/adapters/cockroach_psycopg/config.py +484 -0
- sqlspec/adapters/cockroach_psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_psycopg/core.py +63 -0
- sqlspec/adapters/cockroach_psycopg/data_dictionary.py +215 -0
- sqlspec/adapters/cockroach_psycopg/driver.py +284 -0
- sqlspec/adapters/cockroach_psycopg/events/__init__.py +6 -0
- sqlspec/adapters/cockroach_psycopg/events/store.py +34 -0
- sqlspec/adapters/cockroach_psycopg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_psycopg/litestar/store.py +325 -0
- sqlspec/adapters/duckdb/__init__.py +25 -0
- sqlspec/adapters/duckdb/_typing.py +81 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +850 -0
- sqlspec/adapters/duckdb/config.py +463 -0
- sqlspec/adapters/duckdb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/core.py +257 -0
- sqlspec/adapters/duckdb/data_dictionary.py +140 -0
- sqlspec/adapters/duckdb/driver.py +430 -0
- sqlspec/adapters/duckdb/events/__init__.py +5 -0
- sqlspec/adapters/duckdb/events/store.py +57 -0
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +330 -0
- sqlspec/adapters/duckdb/pool.py +293 -0
- sqlspec/adapters/duckdb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/type_converter.py +118 -0
- sqlspec/adapters/mock/__init__.py +72 -0
- sqlspec/adapters/mock/_typing.py +147 -0
- sqlspec/adapters/mock/config.py +483 -0
- sqlspec/adapters/mock/core.py +319 -0
- sqlspec/adapters/mock/data_dictionary.py +366 -0
- sqlspec/adapters/mock/driver.py +721 -0
- sqlspec/adapters/mysqlconnector/__init__.py +36 -0
- sqlspec/adapters/mysqlconnector/_typing.py +141 -0
- sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
- sqlspec/adapters/mysqlconnector/adk/store.py +1060 -0
- sqlspec/adapters/mysqlconnector/config.py +394 -0
- sqlspec/adapters/mysqlconnector/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/mysqlconnector/core.py +303 -0
- sqlspec/adapters/mysqlconnector/data_dictionary.py +235 -0
- sqlspec/adapters/mysqlconnector/driver.py +483 -0
- sqlspec/adapters/mysqlconnector/events/__init__.py +8 -0
- sqlspec/adapters/mysqlconnector/events/store.py +98 -0
- sqlspec/adapters/mysqlconnector/litestar/__init__.py +5 -0
- sqlspec/adapters/mysqlconnector/litestar/store.py +426 -0
- sqlspec/adapters/oracledb/__init__.py +60 -0
- sqlspec/adapters/oracledb/_numpy_handlers.py +141 -0
- sqlspec/adapters/oracledb/_typing.py +182 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +166 -0
- sqlspec/adapters/oracledb/adk/__init__.py +10 -0
- sqlspec/adapters/oracledb/adk/store.py +2369 -0
- sqlspec/adapters/oracledb/config.py +550 -0
- sqlspec/adapters/oracledb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/core.py +543 -0
- sqlspec/adapters/oracledb/data_dictionary.py +536 -0
- sqlspec/adapters/oracledb/driver.py +1229 -0
- sqlspec/adapters/oracledb/events/__init__.py +16 -0
- sqlspec/adapters/oracledb/events/backend.py +347 -0
- sqlspec/adapters/oracledb/events/store.py +420 -0
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +781 -0
- sqlspec/adapters/oracledb/migrations.py +535 -0
- sqlspec/adapters/oracledb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/type_converter.py +211 -0
- sqlspec/adapters/psqlpy/__init__.py +17 -0
- sqlspec/adapters/psqlpy/_typing.py +79 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +766 -0
- sqlspec/adapters/psqlpy/config.py +304 -0
- sqlspec/adapters/psqlpy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/core.py +480 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +126 -0
- sqlspec/adapters/psqlpy/driver.py +438 -0
- sqlspec/adapters/psqlpy/events/__init__.py +6 -0
- sqlspec/adapters/psqlpy/events/backend.py +310 -0
- sqlspec/adapters/psqlpy/events/store.py +20 -0
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +270 -0
- sqlspec/adapters/psqlpy/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/type_converter.py +113 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_typing.py +164 -0
- sqlspec/adapters/psycopg/adk/__init__.py +10 -0
- sqlspec/adapters/psycopg/adk/store.py +1387 -0
- sqlspec/adapters/psycopg/config.py +576 -0
- sqlspec/adapters/psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/core.py +450 -0
- sqlspec/adapters/psycopg/data_dictionary.py +289 -0
- sqlspec/adapters/psycopg/driver.py +975 -0
- sqlspec/adapters/psycopg/events/__init__.py +20 -0
- sqlspec/adapters/psycopg/events/backend.py +458 -0
- sqlspec/adapters/psycopg/events/store.py +42 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +552 -0
- sqlspec/adapters/psycopg/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/type_converter.py +93 -0
- sqlspec/adapters/pymysql/__init__.py +21 -0
- sqlspec/adapters/pymysql/_typing.py +71 -0
- sqlspec/adapters/pymysql/adk/__init__.py +5 -0
- sqlspec/adapters/pymysql/adk/store.py +540 -0
- sqlspec/adapters/pymysql/config.py +195 -0
- sqlspec/adapters/pymysql/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/pymysql/core.py +299 -0
- sqlspec/adapters/pymysql/data_dictionary.py +122 -0
- sqlspec/adapters/pymysql/driver.py +259 -0
- sqlspec/adapters/pymysql/events/__init__.py +5 -0
- sqlspec/adapters/pymysql/events/store.py +50 -0
- sqlspec/adapters/pymysql/litestar/__init__.py +5 -0
- sqlspec/adapters/pymysql/litestar/store.py +232 -0
- sqlspec/adapters/pymysql/pool.py +137 -0
- sqlspec/adapters/spanner/__init__.py +40 -0
- sqlspec/adapters/spanner/_typing.py +86 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +732 -0
- sqlspec/adapters/spanner/config.py +352 -0
- sqlspec/adapters/spanner/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/core.py +188 -0
- sqlspec/adapters/spanner/data_dictionary.py +120 -0
- sqlspec/adapters/spanner/dialect/__init__.py +6 -0
- sqlspec/adapters/spanner/dialect/_spangres.py +57 -0
- sqlspec/adapters/spanner/dialect/_spanner.py +130 -0
- sqlspec/adapters/spanner/driver.py +373 -0
- sqlspec/adapters/spanner/events/__init__.py +5 -0
- sqlspec/adapters/spanner/events/store.py +187 -0
- sqlspec/adapters/spanner/litestar/__init__.py +5 -0
- sqlspec/adapters/spanner/litestar/store.py +291 -0
- sqlspec/adapters/spanner/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/type_converter.py +331 -0
- sqlspec/adapters/sqlite/__init__.py +19 -0
- sqlspec/adapters/sqlite/_typing.py +80 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +958 -0
- sqlspec/adapters/sqlite/config.py +280 -0
- sqlspec/adapters/sqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/core.py +312 -0
- sqlspec/adapters/sqlite/data_dictionary.py +202 -0
- sqlspec/adapters/sqlite/driver.py +359 -0
- sqlspec/adapters/sqlite/events/__init__.py +5 -0
- sqlspec/adapters/sqlite/events/store.py +20 -0
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +316 -0
- sqlspec/adapters/sqlite/pool.py +198 -0
- sqlspec/adapters/sqlite/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/type_converter.py +114 -0
- sqlspec/base.py +747 -0
- sqlspec/builder/__init__.py +179 -0
- sqlspec/builder/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_base.py +1022 -0
- sqlspec/builder/_column.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_column.py +521 -0
- sqlspec/builder/_ddl.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_ddl.py +1642 -0
- sqlspec/builder/_delete.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_delete.py +95 -0
- sqlspec/builder/_dml.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_dml.py +365 -0
- sqlspec/builder/_explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_explain.py +579 -0
- sqlspec/builder/_expression_wrappers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_factory.py +1697 -0
- sqlspec/builder/_insert.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_insert.py +328 -0
- sqlspec/builder/_join.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_join.py +499 -0
- sqlspec/builder/_merge.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_merge.py +821 -0
- sqlspec/builder/_parsing_utils.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_parsing_utils.py +297 -0
- sqlspec/builder/_select.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_select.py +1660 -0
- sqlspec/builder/_temporal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_temporal.py +139 -0
- sqlspec/builder/_update.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_update.py +173 -0
- sqlspec/builder/_vector_expressions.py +267 -0
- sqlspec/cli.py +911 -0
- sqlspec/config.py +1755 -0
- sqlspec/core/__init__.py +374 -0
- sqlspec/core/_correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/_correlation.py +176 -0
- sqlspec/core/cache.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +1069 -0
- sqlspec/core/compiler.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +954 -0
- sqlspec/core/explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/explain.py +275 -0
- sqlspec/core/filters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +952 -0
- sqlspec/core/hashing.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +262 -0
- sqlspec/core/metrics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +71 -0
- sqlspec/core/parameters/_alignment.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_alignment.py +270 -0
- sqlspec/core/parameters/_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_converter.py +543 -0
- sqlspec/core/parameters/_processor.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_processor.py +505 -0
- sqlspec/core/parameters/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_registry.py +206 -0
- sqlspec/core/parameters/_transformers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_transformers.py +292 -0
- sqlspec/core/parameters/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_types.py +499 -0
- sqlspec/core/parameters/_validator.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_validator.py +180 -0
- sqlspec/core/pipeline.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/pipeline.py +319 -0
- sqlspec/core/query_modifiers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/query_modifiers.py +437 -0
- sqlspec/core/result/__init__.py +23 -0
- sqlspec/core/result/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_base.py +1121 -0
- sqlspec/core/result/_io.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_io.py +28 -0
- sqlspec/core/splitter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +966 -0
- sqlspec/core/stack.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/statement.py +1503 -0
- sqlspec/core/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/type_converter.py +339 -0
- sqlspec/data_dictionary/__init__.py +22 -0
- sqlspec/data_dictionary/_loader.py +123 -0
- sqlspec/data_dictionary/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_registry.py +74 -0
- sqlspec/data_dictionary/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_types.py +121 -0
- sqlspec/data_dictionary/dialects/__init__.py +21 -0
- sqlspec/data_dictionary/dialects/bigquery.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/bigquery.py +49 -0
- sqlspec/data_dictionary/dialects/cockroachdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/cockroachdb.py +43 -0
- sqlspec/data_dictionary/dialects/duckdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/duckdb.py +47 -0
- sqlspec/data_dictionary/dialects/mysql.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/mysql.py +42 -0
- sqlspec/data_dictionary/dialects/oracle.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/oracle.py +34 -0
- sqlspec/data_dictionary/dialects/postgres.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/postgres.py +46 -0
- sqlspec/data_dictionary/dialects/spanner.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/spanner.py +37 -0
- sqlspec/data_dictionary/dialects/sqlite.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/sqlite.py +42 -0
- sqlspec/data_dictionary/sql/.gitkeep +0 -0
- sqlspec/data_dictionary/sql/bigquery/columns.sql +23 -0
- sqlspec/data_dictionary/sql/bigquery/foreign_keys.sql +34 -0
- sqlspec/data_dictionary/sql/bigquery/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/bigquery/tables.sql +33 -0
- sqlspec/data_dictionary/sql/bigquery/version.sql +3 -0
- sqlspec/data_dictionary/sql/cockroachdb/columns.sql +34 -0
- sqlspec/data_dictionary/sql/cockroachdb/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/cockroachdb/indexes.sql +32 -0
- sqlspec/data_dictionary/sql/cockroachdb/tables.sql +44 -0
- sqlspec/data_dictionary/sql/cockroachdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/duckdb/columns.sql +23 -0
- sqlspec/data_dictionary/sql/duckdb/foreign_keys.sql +36 -0
- sqlspec/data_dictionary/sql/duckdb/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/duckdb/tables.sql +38 -0
- sqlspec/data_dictionary/sql/duckdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/mysql/columns.sql +23 -0
- sqlspec/data_dictionary/sql/mysql/foreign_keys.sql +28 -0
- sqlspec/data_dictionary/sql/mysql/indexes.sql +26 -0
- sqlspec/data_dictionary/sql/mysql/tables.sql +33 -0
- sqlspec/data_dictionary/sql/mysql/version.sql +3 -0
- sqlspec/data_dictionary/sql/oracle/columns.sql +23 -0
- sqlspec/data_dictionary/sql/oracle/foreign_keys.sql +48 -0
- sqlspec/data_dictionary/sql/oracle/indexes.sql +44 -0
- sqlspec/data_dictionary/sql/oracle/tables.sql +25 -0
- sqlspec/data_dictionary/sql/oracle/version.sql +20 -0
- sqlspec/data_dictionary/sql/postgres/columns.sql +34 -0
- sqlspec/data_dictionary/sql/postgres/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/postgres/indexes.sql +56 -0
- sqlspec/data_dictionary/sql/postgres/tables.sql +44 -0
- sqlspec/data_dictionary/sql/postgres/version.sql +3 -0
- sqlspec/data_dictionary/sql/spanner/columns.sql +23 -0
- sqlspec/data_dictionary/sql/spanner/foreign_keys.sql +70 -0
- sqlspec/data_dictionary/sql/spanner/indexes.sql +30 -0
- sqlspec/data_dictionary/sql/spanner/tables.sql +9 -0
- sqlspec/data_dictionary/sql/spanner/version.sql +3 -0
- sqlspec/data_dictionary/sql/sqlite/columns.sql +23 -0
- sqlspec/data_dictionary/sql/sqlite/foreign_keys.sql +22 -0
- sqlspec/data_dictionary/sql/sqlite/indexes.sql +7 -0
- sqlspec/data_dictionary/sql/sqlite/tables.sql +28 -0
- sqlspec/data_dictionary/sql/sqlite/version.sql +3 -0
- sqlspec/driver/__init__.py +32 -0
- sqlspec/driver/_async.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_async.py +1737 -0
- sqlspec/driver/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_common.py +1478 -0
- sqlspec/driver/_sql_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sql_helpers.py +148 -0
- sqlspec/driver/_storage_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_storage_helpers.py +144 -0
- sqlspec/driver/_sync.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sync.py +1710 -0
- sqlspec/exceptions.py +338 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +70 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/memory/__init__.py +69 -0
- sqlspec/extensions/adk/memory/_types.py +30 -0
- sqlspec/extensions/adk/memory/converters.py +149 -0
- sqlspec/extensions/adk/memory/service.py +217 -0
- sqlspec/extensions/adk/memory/store.py +569 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +246 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +225 -0
- sqlspec/extensions/adk/store.py +567 -0
- sqlspec/extensions/events/__init__.py +51 -0
- sqlspec/extensions/events/_channel.py +703 -0
- sqlspec/extensions/events/_hints.py +45 -0
- sqlspec/extensions/events/_models.py +23 -0
- sqlspec/extensions/events/_payload.py +69 -0
- sqlspec/extensions/events/_protocols.py +134 -0
- sqlspec/extensions/events/_queue.py +461 -0
- sqlspec/extensions/events/_store.py +209 -0
- sqlspec/extensions/events/migrations/0001_create_event_queue.py +59 -0
- sqlspec/extensions/events/migrations/__init__.py +3 -0
- sqlspec/extensions/fastapi/__init__.py +19 -0
- sqlspec/extensions/fastapi/extension.py +351 -0
- sqlspec/extensions/fastapi/providers.py +607 -0
- sqlspec/extensions/flask/__init__.py +37 -0
- sqlspec/extensions/flask/_state.py +76 -0
- sqlspec/extensions/flask/_utils.py +71 -0
- sqlspec/extensions/flask/extension.py +519 -0
- sqlspec/extensions/litestar/__init__.py +28 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/channels.py +165 -0
- sqlspec/extensions/litestar/cli.py +102 -0
- sqlspec/extensions/litestar/config.py +90 -0
- sqlspec/extensions/litestar/handlers.py +316 -0
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +671 -0
- sqlspec/extensions/litestar/providers.py +526 -0
- sqlspec/extensions/litestar/store.py +296 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +113 -0
- sqlspec/extensions/starlette/__init__.py +19 -0
- sqlspec/extensions/starlette/_state.py +30 -0
- sqlspec/extensions/starlette/_utils.py +96 -0
- sqlspec/extensions/starlette/extension.py +346 -0
- sqlspec/extensions/starlette/middleware.py +235 -0
- sqlspec/loader.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/loader.py +702 -0
- sqlspec/migrations/__init__.py +36 -0
- sqlspec/migrations/base.py +731 -0
- sqlspec/migrations/commands.py +1232 -0
- sqlspec/migrations/context.py +157 -0
- sqlspec/migrations/fix.py +204 -0
- sqlspec/migrations/loaders.py +443 -0
- sqlspec/migrations/runner.py +1172 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.py +611 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.py +207 -0
- sqlspec/migrations/version.py +446 -0
- sqlspec/observability/__init__.py +55 -0
- sqlspec/observability/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_common.py +77 -0
- sqlspec/observability/_config.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_config.py +348 -0
- sqlspec/observability/_diagnostics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_diagnostics.py +74 -0
- sqlspec/observability/_dispatcher.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_dispatcher.py +152 -0
- sqlspec/observability/_formatters/__init__.py +13 -0
- sqlspec/observability/_formatters/_aws.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_aws.py +102 -0
- sqlspec/observability/_formatters/_azure.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_azure.py +96 -0
- sqlspec/observability/_formatters/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_base.py +57 -0
- sqlspec/observability/_formatters/_gcp.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_gcp.py +131 -0
- sqlspec/observability/_formatting.py +58 -0
- sqlspec/observability/_observer.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_observer.py +357 -0
- sqlspec/observability/_runtime.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_runtime.py +420 -0
- sqlspec/observability/_sampling.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_sampling.py +188 -0
- sqlspec/observability/_spans.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_spans.py +161 -0
- sqlspec/protocols.py +916 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +48 -0
- sqlspec/storage/_utils.py +104 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.py +253 -0
- sqlspec/storage/backends/fsspec.py +529 -0
- sqlspec/storage/backends/local.py +441 -0
- sqlspec/storage/backends/obstore.py +916 -0
- sqlspec/storage/errors.py +104 -0
- sqlspec/storage/pipeline.py +582 -0
- sqlspec/storage/registry.py +301 -0
- sqlspec/typing.py +395 -0
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/arrow_helpers.py +318 -0
- sqlspec/utils/config_tools.py +332 -0
- sqlspec/utils/correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/correlation.py +134 -0
- sqlspec/utils/deprecation.py +190 -0
- sqlspec/utils/fixtures.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/fixtures.py +258 -0
- sqlspec/utils/logging.py +222 -0
- sqlspec/utils/module_loader.py +306 -0
- sqlspec/utils/portal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/portal.py +375 -0
- sqlspec/utils/schema.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/schema.py +485 -0
- sqlspec/utils/serializers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/serializers.py +408 -0
- sqlspec/utils/singleton.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +311 -0
- sqlspec/utils/text.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +108 -0
- sqlspec/utils/type_converters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_converters.py +128 -0
- sqlspec/utils/type_guards.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_guards.py +1360 -0
- sqlspec/utils/uuids.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/uuids.py +225 -0
- sqlspec-0.36.0.dist-info/METADATA +205 -0
- sqlspec-0.36.0.dist-info/RECORD +531 -0
- sqlspec-0.36.0.dist-info/WHEEL +7 -0
- sqlspec-0.36.0.dist-info/entry_points.txt +2 -0
- sqlspec-0.36.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
"""AsyncPG ADK store for Google Agent Development Kit session/event storage."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Final, cast
|
|
4
|
+
|
|
5
|
+
import asyncpg
|
|
6
|
+
|
|
7
|
+
from sqlspec.config import AsyncConfigT
|
|
8
|
+
from sqlspec.extensions.adk import BaseAsyncADKStore, EventRecord, SessionRecord
|
|
9
|
+
from sqlspec.extensions.adk.memory.store import BaseAsyncADKMemoryStore
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
|
|
14
|
+
from sqlspec.adapters.asyncpg.config import AsyncpgConfig
|
|
15
|
+
from sqlspec.extensions.adk import MemoryRecord
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = ("AsyncpgADKMemoryStore", "AsyncpgADKStore")
|
|
19
|
+
|
|
20
|
+
POSTGRES_TABLE_NOT_FOUND_ERROR: Final = "42P01"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AsyncpgADKStore(BaseAsyncADKStore[AsyncConfigT]):
|
|
24
|
+
"""PostgreSQL ADK store base class for all PostgreSQL drivers.
|
|
25
|
+
|
|
26
|
+
Implements session and event storage for Google Agent Development Kit
|
|
27
|
+
using PostgreSQL via any PostgreSQL driver (AsyncPG, Psycopg, Psqlpy).
|
|
28
|
+
All drivers share the same SQL dialect and parameter style ($1, $2, etc).
|
|
29
|
+
|
|
30
|
+
Provides:
|
|
31
|
+
- Session state management with JSONB storage and merge operations
|
|
32
|
+
- Event history tracking with BYTEA-serialized actions
|
|
33
|
+
- Microsecond-precision timestamps with TIMESTAMPTZ
|
|
34
|
+
- Foreign key constraints with cascade delete
|
|
35
|
+
- Efficient upserts using ON CONFLICT
|
|
36
|
+
- GIN indexes for JSONB queries
|
|
37
|
+
- HOT updates with FILLFACTOR 80
|
|
38
|
+
- Optional user FK column for multi-tenancy
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
config: PostgreSQL database config with extension_config["adk"] settings.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
from sqlspec.adapters.asyncpg import AsyncpgConfig
|
|
45
|
+
from sqlspec.adapters.asyncpg.adk import AsyncpgADKStore
|
|
46
|
+
|
|
47
|
+
config = AsyncpgConfig(
|
|
48
|
+
connection_config={"dsn": "postgresql://..."},
|
|
49
|
+
extension_config={
|
|
50
|
+
"adk": {
|
|
51
|
+
"session_table": "my_sessions",
|
|
52
|
+
"events_table": "my_events",
|
|
53
|
+
"owner_id_column": "tenant_id INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
store = AsyncpgADKStore(config)
|
|
58
|
+
await store.ensure_tables()
|
|
59
|
+
|
|
60
|
+
Notes:
|
|
61
|
+
- PostgreSQL JSONB type used for state (more efficient than JSON)
|
|
62
|
+
- AsyncPG automatically converts Python dicts to/from JSONB (no manual serialization)
|
|
63
|
+
- TIMESTAMPTZ provides timezone-aware microsecond precision
|
|
64
|
+
- State merging uses `state || $1::jsonb` operator for efficiency
|
|
65
|
+
- BYTEA for pre-serialized actions from Google ADK (not pickled here)
|
|
66
|
+
- GIN index on state for JSONB queries (partial index)
|
|
67
|
+
- FILLFACTOR 80 leaves space for HOT updates
|
|
68
|
+
- Generic over PostgresConfigT to support all PostgreSQL drivers
|
|
69
|
+
- Owner ID column enables multi-tenant isolation with referential integrity
|
|
70
|
+
- Configuration is read from config.extension_config["adk"]
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
__slots__ = ()
|
|
74
|
+
|
|
75
|
+
def __init__(self, config: AsyncConfigT) -> None:
|
|
76
|
+
"""Initialize AsyncPG ADK store.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
config: PostgreSQL database config.
|
|
80
|
+
|
|
81
|
+
Notes:
|
|
82
|
+
Configuration is read from config.extension_config["adk"]:
|
|
83
|
+
- session_table: Sessions table name (default: "adk_sessions")
|
|
84
|
+
- events_table: Events table name (default: "adk_events")
|
|
85
|
+
- owner_id_column: Optional owner FK column DDL (default: None)
|
|
86
|
+
"""
|
|
87
|
+
super().__init__(config)
|
|
88
|
+
|
|
89
|
+
async def _get_create_sessions_table_sql(self) -> str:
|
|
90
|
+
"""Get PostgreSQL CREATE TABLE SQL for sessions.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
SQL statement to create adk_sessions table with indexes.
|
|
94
|
+
|
|
95
|
+
Notes:
|
|
96
|
+
- VARCHAR(128) for IDs and names (sufficient for UUIDs and app names)
|
|
97
|
+
- JSONB type for state storage with default empty object
|
|
98
|
+
- TIMESTAMPTZ with microsecond precision
|
|
99
|
+
- FILLFACTOR 80 for HOT updates (reduces table bloat)
|
|
100
|
+
- Composite index on (app_name, user_id) for listing
|
|
101
|
+
- Index on update_time DESC for recent session queries
|
|
102
|
+
- Partial GIN index on state for JSONB queries (only non-empty)
|
|
103
|
+
- Optional owner ID column for multi-tenancy or owner references
|
|
104
|
+
"""
|
|
105
|
+
owner_id_line = ""
|
|
106
|
+
if self._owner_id_column_ddl:
|
|
107
|
+
owner_id_line = f",\n {self._owner_id_column_ddl}"
|
|
108
|
+
|
|
109
|
+
return f"""
|
|
110
|
+
CREATE TABLE IF NOT EXISTS {self._session_table} (
|
|
111
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
112
|
+
app_name VARCHAR(128) NOT NULL,
|
|
113
|
+
user_id VARCHAR(128) NOT NULL{owner_id_line},
|
|
114
|
+
state JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
|
115
|
+
create_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
116
|
+
update_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
117
|
+
) WITH (fillfactor = 80);
|
|
118
|
+
|
|
119
|
+
CREATE INDEX IF NOT EXISTS idx_{self._session_table}_app_user
|
|
120
|
+
ON {self._session_table}(app_name, user_id);
|
|
121
|
+
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_{self._session_table}_update_time
|
|
123
|
+
ON {self._session_table}(update_time DESC);
|
|
124
|
+
|
|
125
|
+
CREATE INDEX IF NOT EXISTS idx_{self._session_table}_state
|
|
126
|
+
ON {self._session_table} USING GIN (state)
|
|
127
|
+
WHERE state != '{{}}'::jsonb;
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
async def _get_create_events_table_sql(self) -> str:
|
|
131
|
+
"""Get PostgreSQL CREATE TABLE SQL for events.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
SQL statement to create adk_events table with indexes.
|
|
135
|
+
|
|
136
|
+
Notes:
|
|
137
|
+
- VARCHAR sizes: id(128), session_id(128), invocation_id(256), author(256),
|
|
138
|
+
branch(256), error_code(256), error_message(1024)
|
|
139
|
+
- BYTEA for pickled actions (no size limit)
|
|
140
|
+
- JSONB for content, grounding_metadata, custom_metadata, long_running_tool_ids_json
|
|
141
|
+
- BOOLEAN for partial, turn_complete, interrupted
|
|
142
|
+
- Foreign key to sessions with CASCADE delete
|
|
143
|
+
- Index on (session_id, timestamp ASC) for ordered event retrieval
|
|
144
|
+
"""
|
|
145
|
+
return f"""
|
|
146
|
+
CREATE TABLE IF NOT EXISTS {self._events_table} (
|
|
147
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
148
|
+
session_id VARCHAR(128) NOT NULL,
|
|
149
|
+
app_name VARCHAR(128) NOT NULL,
|
|
150
|
+
user_id VARCHAR(128) NOT NULL,
|
|
151
|
+
invocation_id VARCHAR(256),
|
|
152
|
+
author VARCHAR(256),
|
|
153
|
+
actions BYTEA,
|
|
154
|
+
long_running_tool_ids_json JSONB,
|
|
155
|
+
branch VARCHAR(256),
|
|
156
|
+
timestamp TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
157
|
+
content JSONB,
|
|
158
|
+
grounding_metadata JSONB,
|
|
159
|
+
custom_metadata JSONB,
|
|
160
|
+
partial BOOLEAN,
|
|
161
|
+
turn_complete BOOLEAN,
|
|
162
|
+
interrupted BOOLEAN,
|
|
163
|
+
error_code VARCHAR(256),
|
|
164
|
+
error_message VARCHAR(1024),
|
|
165
|
+
FOREIGN KEY (session_id) REFERENCES {self._session_table}(id) ON DELETE CASCADE
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
CREATE INDEX IF NOT EXISTS idx_{self._events_table}_session
|
|
169
|
+
ON {self._events_table}(session_id, timestamp ASC);
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
def _get_drop_tables_sql(self) -> "list[str]":
|
|
173
|
+
"""Get PostgreSQL DROP TABLE SQL statements.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
List of SQL statements to drop tables and indexes.
|
|
177
|
+
|
|
178
|
+
Notes:
|
|
179
|
+
Order matters: drop events table (child) before sessions (parent).
|
|
180
|
+
PostgreSQL automatically drops indexes when dropping tables.
|
|
181
|
+
"""
|
|
182
|
+
return [f"DROP TABLE IF EXISTS {self._events_table}", f"DROP TABLE IF EXISTS {self._session_table}"]
|
|
183
|
+
|
|
184
|
+
async def create_tables(self) -> None:
|
|
185
|
+
"""Create both sessions and events tables if they don't exist."""
|
|
186
|
+
async with self.config.provide_session() as driver:
|
|
187
|
+
await driver.execute_script(await self._get_create_sessions_table_sql())
|
|
188
|
+
await driver.execute_script(await self._get_create_events_table_sql())
|
|
189
|
+
|
|
190
|
+
async def create_session(
|
|
191
|
+
self, session_id: str, app_name: str, user_id: str, state: "dict[str, Any]", owner_id: "Any | None" = None
|
|
192
|
+
) -> SessionRecord:
|
|
193
|
+
"""Create a new session.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
session_id: Unique session identifier.
|
|
197
|
+
app_name: Application name.
|
|
198
|
+
user_id: User identifier.
|
|
199
|
+
state: Initial session state.
|
|
200
|
+
owner_id: Optional owner ID value for owner_id_column (if configured).
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Created session record.
|
|
204
|
+
|
|
205
|
+
Notes:
|
|
206
|
+
Uses CURRENT_TIMESTAMP for create_time and update_time.
|
|
207
|
+
State is passed as dict and asyncpg converts to JSONB automatically.
|
|
208
|
+
If owner_id_column is configured, owner_id value must be provided.
|
|
209
|
+
"""
|
|
210
|
+
async with self.config.provide_connection() as conn:
|
|
211
|
+
if self._owner_id_column_name:
|
|
212
|
+
sql = f"""
|
|
213
|
+
INSERT INTO {self._session_table}
|
|
214
|
+
(id, app_name, user_id, {self._owner_id_column_name}, state, create_time, update_time)
|
|
215
|
+
VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
216
|
+
"""
|
|
217
|
+
await conn.execute(sql, session_id, app_name, user_id, owner_id, state)
|
|
218
|
+
else:
|
|
219
|
+
sql = f"""
|
|
220
|
+
INSERT INTO {self._session_table} (id, app_name, user_id, state, create_time, update_time)
|
|
221
|
+
VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
222
|
+
"""
|
|
223
|
+
await conn.execute(sql, session_id, app_name, user_id, state)
|
|
224
|
+
|
|
225
|
+
return await self.get_session(session_id) # type: ignore[return-value]
|
|
226
|
+
|
|
227
|
+
async def get_session(self, session_id: str) -> "SessionRecord | None":
|
|
228
|
+
"""Get session by ID.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
session_id: Session identifier.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Session record or None if not found.
|
|
235
|
+
|
|
236
|
+
Notes:
|
|
237
|
+
PostgreSQL returns datetime objects for TIMESTAMPTZ columns.
|
|
238
|
+
JSONB is automatically parsed by asyncpg.
|
|
239
|
+
"""
|
|
240
|
+
sql = f"""
|
|
241
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
242
|
+
FROM {self._session_table}
|
|
243
|
+
WHERE id = $1
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
async with self.config.provide_connection() as conn:
|
|
248
|
+
row = await conn.fetchrow(sql, session_id)
|
|
249
|
+
|
|
250
|
+
if row is None:
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
return SessionRecord(
|
|
254
|
+
id=row["id"],
|
|
255
|
+
app_name=row["app_name"],
|
|
256
|
+
user_id=row["user_id"],
|
|
257
|
+
state=row["state"],
|
|
258
|
+
create_time=row["create_time"],
|
|
259
|
+
update_time=row["update_time"],
|
|
260
|
+
)
|
|
261
|
+
except asyncpg.exceptions.UndefinedTableError:
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
async def update_session_state(self, session_id: str, state: "dict[str, Any]") -> None:
|
|
265
|
+
"""Update session state.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
session_id: Session identifier.
|
|
269
|
+
state: New state dictionary (replaces existing state).
|
|
270
|
+
|
|
271
|
+
Notes:
|
|
272
|
+
This replaces the entire state dictionary.
|
|
273
|
+
Uses CURRENT_TIMESTAMP for update_time.
|
|
274
|
+
"""
|
|
275
|
+
sql = f"""
|
|
276
|
+
UPDATE {self._session_table}
|
|
277
|
+
SET state = $1, update_time = CURRENT_TIMESTAMP
|
|
278
|
+
WHERE id = $2
|
|
279
|
+
"""
|
|
280
|
+
|
|
281
|
+
async with self.config.provide_connection() as conn:
|
|
282
|
+
await conn.execute(sql, state, session_id)
|
|
283
|
+
|
|
284
|
+
async def delete_session(self, session_id: str) -> None:
|
|
285
|
+
"""Delete session and all associated events (cascade).
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
session_id: Session identifier.
|
|
289
|
+
|
|
290
|
+
Notes:
|
|
291
|
+
Foreign key constraint ensures events are cascade-deleted.
|
|
292
|
+
"""
|
|
293
|
+
sql = f"DELETE FROM {self._session_table} WHERE id = $1"
|
|
294
|
+
|
|
295
|
+
async with self.config.provide_connection() as conn:
|
|
296
|
+
await conn.execute(sql, session_id)
|
|
297
|
+
|
|
298
|
+
async def list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
|
|
299
|
+
"""List sessions for an app, optionally filtered by user.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
app_name: Application name.
|
|
303
|
+
user_id: User identifier. If None, lists all sessions for the app.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
List of session records ordered by update_time DESC.
|
|
307
|
+
|
|
308
|
+
Notes:
|
|
309
|
+
Uses composite index on (app_name, user_id) when user_id is provided.
|
|
310
|
+
"""
|
|
311
|
+
if user_id is None:
|
|
312
|
+
sql = f"""
|
|
313
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
314
|
+
FROM {self._session_table}
|
|
315
|
+
WHERE app_name = $1
|
|
316
|
+
ORDER BY update_time DESC
|
|
317
|
+
"""
|
|
318
|
+
params = [app_name]
|
|
319
|
+
else:
|
|
320
|
+
sql = f"""
|
|
321
|
+
SELECT id, app_name, user_id, state, create_time, update_time
|
|
322
|
+
FROM {self._session_table}
|
|
323
|
+
WHERE app_name = $1 AND user_id = $2
|
|
324
|
+
ORDER BY update_time DESC
|
|
325
|
+
"""
|
|
326
|
+
params = [app_name, user_id]
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
async with self.config.provide_connection() as conn:
|
|
330
|
+
rows = await conn.fetch(sql, *params)
|
|
331
|
+
|
|
332
|
+
return [
|
|
333
|
+
SessionRecord(
|
|
334
|
+
id=row["id"],
|
|
335
|
+
app_name=row["app_name"],
|
|
336
|
+
user_id=row["user_id"],
|
|
337
|
+
state=row["state"],
|
|
338
|
+
create_time=row["create_time"],
|
|
339
|
+
update_time=row["update_time"],
|
|
340
|
+
)
|
|
341
|
+
for row in rows
|
|
342
|
+
]
|
|
343
|
+
except asyncpg.exceptions.UndefinedTableError:
|
|
344
|
+
return []
|
|
345
|
+
|
|
346
|
+
async def append_event(self, event_record: EventRecord) -> None:
|
|
347
|
+
"""Append an event to a session.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
event_record: Event record to store.
|
|
351
|
+
|
|
352
|
+
Notes:
|
|
353
|
+
Uses CURRENT_TIMESTAMP for timestamp if not provided.
|
|
354
|
+
JSONB fields are passed as dicts and asyncpg converts automatically.
|
|
355
|
+
"""
|
|
356
|
+
content_json = event_record.get("content")
|
|
357
|
+
grounding_metadata_json = event_record.get("grounding_metadata")
|
|
358
|
+
custom_metadata_json = event_record.get("custom_metadata")
|
|
359
|
+
|
|
360
|
+
sql = f"""
|
|
361
|
+
INSERT INTO {self._events_table} (
|
|
362
|
+
id, session_id, app_name, user_id, invocation_id, author, actions,
|
|
363
|
+
long_running_tool_ids_json, branch, timestamp, content,
|
|
364
|
+
grounding_metadata, custom_metadata, partial, turn_complete,
|
|
365
|
+
interrupted, error_code, error_message
|
|
366
|
+
) VALUES (
|
|
367
|
+
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18
|
|
368
|
+
)
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
async with self.config.provide_connection() as conn:
|
|
372
|
+
await conn.execute(
|
|
373
|
+
sql,
|
|
374
|
+
event_record["id"],
|
|
375
|
+
event_record["session_id"],
|
|
376
|
+
event_record["app_name"],
|
|
377
|
+
event_record["user_id"],
|
|
378
|
+
event_record.get("invocation_id"),
|
|
379
|
+
event_record.get("author"),
|
|
380
|
+
event_record.get("actions"),
|
|
381
|
+
event_record.get("long_running_tool_ids_json"),
|
|
382
|
+
event_record.get("branch"),
|
|
383
|
+
event_record["timestamp"],
|
|
384
|
+
content_json,
|
|
385
|
+
grounding_metadata_json,
|
|
386
|
+
custom_metadata_json,
|
|
387
|
+
event_record.get("partial"),
|
|
388
|
+
event_record.get("turn_complete"),
|
|
389
|
+
event_record.get("interrupted"),
|
|
390
|
+
event_record.get("error_code"),
|
|
391
|
+
event_record.get("error_message"),
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
async def get_events(
|
|
395
|
+
self, session_id: str, after_timestamp: "datetime | None" = None, limit: "int | None" = None
|
|
396
|
+
) -> "list[EventRecord]":
|
|
397
|
+
"""Get events for a session.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
session_id: Session identifier.
|
|
401
|
+
after_timestamp: Only return events after this time.
|
|
402
|
+
limit: Maximum number of events to return.
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
List of event records ordered by timestamp ASC.
|
|
406
|
+
|
|
407
|
+
Notes:
|
|
408
|
+
Uses index on (session_id, timestamp ASC).
|
|
409
|
+
Parses JSONB fields and converts BYTEA actions to bytes.
|
|
410
|
+
"""
|
|
411
|
+
where_clauses = ["session_id = $1"]
|
|
412
|
+
params: list[Any] = [session_id]
|
|
413
|
+
|
|
414
|
+
if after_timestamp is not None:
|
|
415
|
+
where_clauses.append(f"timestamp > ${len(params) + 1}")
|
|
416
|
+
params.append(after_timestamp)
|
|
417
|
+
|
|
418
|
+
where_clause = " AND ".join(where_clauses)
|
|
419
|
+
limit_clause = f" LIMIT ${len(params) + 1}" if limit else ""
|
|
420
|
+
if limit:
|
|
421
|
+
params.append(limit)
|
|
422
|
+
|
|
423
|
+
sql = f"""
|
|
424
|
+
SELECT id, session_id, app_name, user_id, invocation_id, author, actions,
|
|
425
|
+
long_running_tool_ids_json, branch, timestamp, content,
|
|
426
|
+
grounding_metadata, custom_metadata, partial, turn_complete,
|
|
427
|
+
interrupted, error_code, error_message
|
|
428
|
+
FROM {self._events_table}
|
|
429
|
+
WHERE {where_clause}
|
|
430
|
+
ORDER BY timestamp ASC{limit_clause}
|
|
431
|
+
"""
|
|
432
|
+
|
|
433
|
+
try:
|
|
434
|
+
async with self.config.provide_connection() as conn:
|
|
435
|
+
rows = await conn.fetch(sql, *params)
|
|
436
|
+
|
|
437
|
+
return [
|
|
438
|
+
EventRecord(
|
|
439
|
+
id=row["id"],
|
|
440
|
+
session_id=row["session_id"],
|
|
441
|
+
app_name=row["app_name"],
|
|
442
|
+
user_id=row["user_id"],
|
|
443
|
+
invocation_id=row["invocation_id"],
|
|
444
|
+
author=row["author"],
|
|
445
|
+
actions=bytes(row["actions"]) if row["actions"] else b"",
|
|
446
|
+
long_running_tool_ids_json=row["long_running_tool_ids_json"],
|
|
447
|
+
branch=row["branch"],
|
|
448
|
+
timestamp=row["timestamp"],
|
|
449
|
+
content=row["content"],
|
|
450
|
+
grounding_metadata=row["grounding_metadata"],
|
|
451
|
+
custom_metadata=row["custom_metadata"],
|
|
452
|
+
partial=row["partial"],
|
|
453
|
+
turn_complete=row["turn_complete"],
|
|
454
|
+
interrupted=row["interrupted"],
|
|
455
|
+
error_code=row["error_code"],
|
|
456
|
+
error_message=row["error_message"],
|
|
457
|
+
)
|
|
458
|
+
for row in rows
|
|
459
|
+
]
|
|
460
|
+
except asyncpg.exceptions.UndefinedTableError:
|
|
461
|
+
return []
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
class AsyncpgADKMemoryStore(BaseAsyncADKMemoryStore["AsyncpgConfig"]):
|
|
465
|
+
"""PostgreSQL ADK memory store using asyncpg driver.
|
|
466
|
+
|
|
467
|
+
Implements memory entry storage for Google Agent Development Kit
|
|
468
|
+
using PostgreSQL via the asyncpg driver. Provides:
|
|
469
|
+
- Session memory storage with JSONB for content and metadata
|
|
470
|
+
- Full-text search using to_tsvector/to_tsquery (postgres_fts strategy)
|
|
471
|
+
- Simple ILIKE search fallback (simple strategy)
|
|
472
|
+
- TIMESTAMPTZ for precise timestamp storage
|
|
473
|
+
- Deduplication via event_id unique constraint
|
|
474
|
+
- Efficient upserts using ON CONFLICT DO NOTHING
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
config: AsyncpgConfig with extension_config["adk"] settings.
|
|
478
|
+
|
|
479
|
+
Example:
|
|
480
|
+
from sqlspec.adapters.asyncpg import AsyncpgConfig
|
|
481
|
+
from sqlspec.adapters.asyncpg.adk.store import AsyncpgADKMemoryStore
|
|
482
|
+
|
|
483
|
+
config = AsyncpgConfig(
|
|
484
|
+
connection_config={"dsn": "postgresql://..."},
|
|
485
|
+
extension_config={
|
|
486
|
+
"adk": {
|
|
487
|
+
"memory_table": "adk_memory_entries",
|
|
488
|
+
"memory_use_fts": True,
|
|
489
|
+
"memory_max_results": 20,
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
store = AsyncpgADKMemoryStore(config)
|
|
494
|
+
await store.ensure_tables()
|
|
495
|
+
|
|
496
|
+
Notes:
|
|
497
|
+
- JSONB type for content_json and metadata_json
|
|
498
|
+
- TIMESTAMPTZ with microsecond precision
|
|
499
|
+
- GIN index on content_text tsvector for FTS queries
|
|
500
|
+
- Composite index on (app_name, user_id) for filtering
|
|
501
|
+
- event_id UNIQUE constraint for deduplication
|
|
502
|
+
- Configuration is read from config.extension_config["adk"]
|
|
503
|
+
"""
|
|
504
|
+
|
|
505
|
+
__slots__ = ()
|
|
506
|
+
|
|
507
|
+
def __init__(self, config: "AsyncpgConfig") -> None:
|
|
508
|
+
"""Initialize AsyncPG ADK memory store.
|
|
509
|
+
|
|
510
|
+
Args:
|
|
511
|
+
config: AsyncpgConfig instance.
|
|
512
|
+
|
|
513
|
+
Notes:
|
|
514
|
+
Configuration is read from config.extension_config["adk"]:
|
|
515
|
+
- memory_table: Memory table name (default: "adk_memory_entries")
|
|
516
|
+
- memory_use_fts: Enable full-text search when supported (default: False)
|
|
517
|
+
- memory_max_results: Max search results (default: 20)
|
|
518
|
+
- owner_id_column: Optional owner FK column DDL (default: None)
|
|
519
|
+
- enable_memory: Whether memory is enabled (default: True)
|
|
520
|
+
"""
|
|
521
|
+
super().__init__(config)
|
|
522
|
+
|
|
523
|
+
async def _get_create_memory_table_sql(self) -> str:
|
|
524
|
+
"""Get PostgreSQL CREATE TABLE SQL for memory entries.
|
|
525
|
+
|
|
526
|
+
Returns:
|
|
527
|
+
SQL statement to create memory table with indexes.
|
|
528
|
+
|
|
529
|
+
Notes:
|
|
530
|
+
- VARCHAR(128) for IDs and names
|
|
531
|
+
- JSONB for content and metadata storage
|
|
532
|
+
- TIMESTAMPTZ with microsecond precision
|
|
533
|
+
- UNIQUE constraint on event_id for deduplication
|
|
534
|
+
- Composite index on (app_name, user_id, timestamp DESC)
|
|
535
|
+
- GIN index on content_text tsvector for FTS
|
|
536
|
+
- Optional owner ID column for multi-tenancy
|
|
537
|
+
"""
|
|
538
|
+
owner_id_line = ""
|
|
539
|
+
if self._owner_id_column_ddl:
|
|
540
|
+
owner_id_line = f",\n {self._owner_id_column_ddl}"
|
|
541
|
+
|
|
542
|
+
fts_index = ""
|
|
543
|
+
if self._use_fts:
|
|
544
|
+
fts_index = f"""
|
|
545
|
+
CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_fts
|
|
546
|
+
ON {self._memory_table} USING GIN (to_tsvector('english', content_text));
|
|
547
|
+
"""
|
|
548
|
+
|
|
549
|
+
return f"""
|
|
550
|
+
CREATE TABLE IF NOT EXISTS {self._memory_table} (
|
|
551
|
+
id VARCHAR(128) PRIMARY KEY,
|
|
552
|
+
session_id VARCHAR(128) NOT NULL,
|
|
553
|
+
app_name VARCHAR(128) NOT NULL,
|
|
554
|
+
user_id VARCHAR(128) NOT NULL,
|
|
555
|
+
event_id VARCHAR(128) NOT NULL UNIQUE,
|
|
556
|
+
author VARCHAR(256){owner_id_line},
|
|
557
|
+
timestamp TIMESTAMPTZ NOT NULL,
|
|
558
|
+
content_json JSONB NOT NULL,
|
|
559
|
+
content_text TEXT NOT NULL,
|
|
560
|
+
metadata_json JSONB,
|
|
561
|
+
inserted_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_app_user_time
|
|
565
|
+
ON {self._memory_table}(app_name, user_id, timestamp DESC);
|
|
566
|
+
|
|
567
|
+
CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_session
|
|
568
|
+
ON {self._memory_table}(session_id);
|
|
569
|
+
{fts_index}
|
|
570
|
+
"""
|
|
571
|
+
|
|
572
|
+
def _get_drop_memory_table_sql(self) -> "list[str]":
|
|
573
|
+
"""Get PostgreSQL DROP TABLE SQL statements.
|
|
574
|
+
|
|
575
|
+
Returns:
|
|
576
|
+
List of SQL statements to drop the memory table.
|
|
577
|
+
|
|
578
|
+
Notes:
|
|
579
|
+
PostgreSQL automatically drops indexes when dropping tables.
|
|
580
|
+
"""
|
|
581
|
+
return [f"DROP TABLE IF EXISTS {self._memory_table}"]
|
|
582
|
+
|
|
583
|
+
async def create_tables(self) -> None:
|
|
584
|
+
"""Create the memory table and indexes if they don't exist.
|
|
585
|
+
|
|
586
|
+
Skips table creation if memory store is disabled.
|
|
587
|
+
"""
|
|
588
|
+
if not self._enabled:
|
|
589
|
+
return
|
|
590
|
+
|
|
591
|
+
async with self._config.provide_session() as driver:
|
|
592
|
+
await driver.execute_script(await self._get_create_memory_table_sql())
|
|
593
|
+
|
|
594
|
+
async def insert_memory_entries(self, entries: "list[MemoryRecord]", owner_id: "object | None" = None) -> int:
|
|
595
|
+
"""Bulk insert memory entries with deduplication.
|
|
596
|
+
|
|
597
|
+
Uses UPSERT pattern (ON CONFLICT DO NOTHING) to skip duplicates
|
|
598
|
+
based on event_id unique constraint.
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
entries: List of memory records to insert.
|
|
602
|
+
owner_id: Optional owner ID value for owner_id_column (if configured).
|
|
603
|
+
|
|
604
|
+
Returns:
|
|
605
|
+
Number of entries actually inserted (excludes duplicates).
|
|
606
|
+
|
|
607
|
+
Raises:
|
|
608
|
+
RuntimeError: If memory store is disabled.
|
|
609
|
+
"""
|
|
610
|
+
if not self._enabled:
|
|
611
|
+
msg = "Memory store is disabled"
|
|
612
|
+
raise RuntimeError(msg)
|
|
613
|
+
|
|
614
|
+
if not entries:
|
|
615
|
+
return 0
|
|
616
|
+
|
|
617
|
+
inserted_count = 0
|
|
618
|
+
async with self._config.provide_connection() as conn:
|
|
619
|
+
for entry in entries:
|
|
620
|
+
if self._owner_id_column_name:
|
|
621
|
+
sql = f"""
|
|
622
|
+
INSERT INTO {self._memory_table}
|
|
623
|
+
(id, session_id, app_name, user_id, event_id, author,
|
|
624
|
+
{self._owner_id_column_name}, timestamp, content_json,
|
|
625
|
+
content_text, metadata_json, inserted_at)
|
|
626
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
627
|
+
ON CONFLICT (event_id) DO NOTHING
|
|
628
|
+
"""
|
|
629
|
+
result = await conn.execute(
|
|
630
|
+
sql,
|
|
631
|
+
entry["id"],
|
|
632
|
+
entry["session_id"],
|
|
633
|
+
entry["app_name"],
|
|
634
|
+
entry["user_id"],
|
|
635
|
+
entry["event_id"],
|
|
636
|
+
entry["author"],
|
|
637
|
+
owner_id,
|
|
638
|
+
entry["timestamp"],
|
|
639
|
+
entry["content_json"],
|
|
640
|
+
entry["content_text"],
|
|
641
|
+
entry["metadata_json"],
|
|
642
|
+
entry["inserted_at"],
|
|
643
|
+
)
|
|
644
|
+
else:
|
|
645
|
+
sql = f"""
|
|
646
|
+
INSERT INTO {self._memory_table}
|
|
647
|
+
(id, session_id, app_name, user_id, event_id, author,
|
|
648
|
+
timestamp, content_json, content_text, metadata_json, inserted_at)
|
|
649
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
650
|
+
ON CONFLICT (event_id) DO NOTHING
|
|
651
|
+
"""
|
|
652
|
+
result = await conn.execute(
|
|
653
|
+
sql,
|
|
654
|
+
entry["id"],
|
|
655
|
+
entry["session_id"],
|
|
656
|
+
entry["app_name"],
|
|
657
|
+
entry["user_id"],
|
|
658
|
+
entry["event_id"],
|
|
659
|
+
entry["author"],
|
|
660
|
+
entry["timestamp"],
|
|
661
|
+
entry["content_json"],
|
|
662
|
+
entry["content_text"],
|
|
663
|
+
entry["metadata_json"],
|
|
664
|
+
entry["inserted_at"],
|
|
665
|
+
)
|
|
666
|
+
try:
|
|
667
|
+
inserted_count += int(result.split(" ")[1])
|
|
668
|
+
except (IndexError, ValueError):
|
|
669
|
+
continue
|
|
670
|
+
|
|
671
|
+
return inserted_count
|
|
672
|
+
|
|
673
|
+
async def search_entries(
|
|
674
|
+
self, query: str, app_name: str, user_id: str, limit: "int | None" = None
|
|
675
|
+
) -> "list[MemoryRecord]":
|
|
676
|
+
"""Search memory entries by text query.
|
|
677
|
+
|
|
678
|
+
Uses the configured search strategy (simple ILIKE or FTS).
|
|
679
|
+
|
|
680
|
+
Args:
|
|
681
|
+
query: Text query to search for.
|
|
682
|
+
app_name: Application name to filter by.
|
|
683
|
+
user_id: User ID to filter by.
|
|
684
|
+
limit: Maximum number of results (defaults to max_results config).
|
|
685
|
+
|
|
686
|
+
Returns:
|
|
687
|
+
List of memory records.
|
|
688
|
+
"""
|
|
689
|
+
if not self._enabled:
|
|
690
|
+
msg = "Memory store is disabled"
|
|
691
|
+
raise RuntimeError(msg)
|
|
692
|
+
|
|
693
|
+
if not query:
|
|
694
|
+
return []
|
|
695
|
+
|
|
696
|
+
limit_value = limit or self._max_results
|
|
697
|
+
if self._use_fts:
|
|
698
|
+
sql = f"""
|
|
699
|
+
SELECT * FROM {self._memory_table}
|
|
700
|
+
WHERE app_name = $1 AND user_id = $2
|
|
701
|
+
AND to_tsvector('english', content_text) @@ plainto_tsquery('english', $3)
|
|
702
|
+
ORDER BY timestamp DESC
|
|
703
|
+
LIMIT $4
|
|
704
|
+
"""
|
|
705
|
+
params = (app_name, user_id, query, limit_value)
|
|
706
|
+
else:
|
|
707
|
+
sql = f"""
|
|
708
|
+
SELECT * FROM {self._memory_table}
|
|
709
|
+
WHERE app_name = $1 AND user_id = $2 AND content_text ILIKE $3
|
|
710
|
+
ORDER BY timestamp DESC
|
|
711
|
+
LIMIT $4
|
|
712
|
+
"""
|
|
713
|
+
params = (app_name, user_id, f"%{query}%", limit_value)
|
|
714
|
+
|
|
715
|
+
async with self._config.provide_connection() as conn:
|
|
716
|
+
rows = await conn.fetch(sql, *params)
|
|
717
|
+
return [cast("MemoryRecord", dict(row)) for row in rows]
|
|
718
|
+
|
|
719
|
+
async def delete_entries_by_session(self, session_id: str) -> int:
|
|
720
|
+
"""Delete all memory entries for a specific session."""
|
|
721
|
+
if not self._enabled:
|
|
722
|
+
msg = "Memory store is disabled"
|
|
723
|
+
raise RuntimeError(msg)
|
|
724
|
+
|
|
725
|
+
sql = f"DELETE FROM {self._memory_table} WHERE session_id = $1"
|
|
726
|
+
async with self._config.provide_connection() as conn:
|
|
727
|
+
result = await conn.execute(sql, session_id)
|
|
728
|
+
try:
|
|
729
|
+
return int(result.split(" ")[1])
|
|
730
|
+
except (IndexError, ValueError):
|
|
731
|
+
return 0
|
|
732
|
+
|
|
733
|
+
async def delete_entries_older_than(self, days: int) -> int:
|
|
734
|
+
"""Delete memory entries older than specified days."""
|
|
735
|
+
if not self._enabled:
|
|
736
|
+
msg = "Memory store is disabled"
|
|
737
|
+
raise RuntimeError(msg)
|
|
738
|
+
|
|
739
|
+
sql = f"""
|
|
740
|
+
DELETE FROM {self._memory_table}
|
|
741
|
+
WHERE inserted_at < (CURRENT_TIMESTAMP - ($1::int * INTERVAL '1 day'))
|
|
742
|
+
"""
|
|
743
|
+
async with self._config.provide_connection() as conn:
|
|
744
|
+
result = await conn.execute(sql, days)
|
|
745
|
+
try:
|
|
746
|
+
return int(result.split(" ")[1])
|
|
747
|
+
except (IndexError, ValueError):
|
|
748
|
+
return 0
|