feat(llm): index_state.fingerprint + index_docket_state migrations (refs #615)

This commit is contained in:
kert
2026-09-08 13:28:57 -04:00
parent 8453b8a1f9
commit 3bc7c28077
2 changed files with 37 additions and 1 deletions

View File

@@ -21,10 +21,27 @@ CREATE TABLE IF NOT EXISTS index_state (
content_hash TEXT NOT NULL, content_hash TEXT NOT NULL,
chunk_count INTEGER NOT NULL DEFAULT 0, chunk_count INTEGER NOT NULL DEFAULT 0,
indexed_at TIMESTAMPTZ NOT NULL DEFAULT now(), indexed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
fingerprint TEXT NOT NULL DEFAULT '',
PRIMARY KEY (item_key, collection) PRIMARY KEY (item_key, collection)
) )
""" """
# Existing databases predate the column.
INDEX_STATE_ALTER = "ALTER TABLE index_state ADD COLUMN IF NOT EXISTS fingerprint TEXT NOT NULL DEFAULT ''"
# A sealed docket that was fully indexed under a given seal: the iterator
# does not even list its items until the seal changes (unseal/reseal
# writes a new sealed_at in bib, which no longer matches).
INDEX_DOCKET_STATE_DDL = """
CREATE TABLE IF NOT EXISTS index_docket_state (
collection TEXT NOT NULL,
docket TEXT NOT NULL,
sealed_at TEXT NOT NULL,
indexed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (collection, docket)
)
"""
# langchain-postgres creates langchain_pg_embedding with an untyped vector # langchain-postgres creates langchain_pg_embedding with an untyped vector
# column; HNSW needs a typed one. ALTER is a no-op when already typed. # column; HNSW needs a typed one. ALTER is a no-op when already typed.
_HNSW_DDL = [ _HNSW_DDL = [
@@ -35,9 +52,11 @@ _HNSW_DDL = [
def migrate(engine: Engine) -> None: def migrate(engine: Engine) -> None:
"""Create llm-owned tables. Safe to run on every start.""" """Create llm-owned tables/columns. Safe to run on every start."""
with engine.begin() as conn: with engine.begin() as conn:
conn.execute(text(INDEX_STATE_DDL)) conn.execute(text(INDEX_STATE_DDL))
conn.execute(text(INDEX_STATE_ALTER))
conn.execute(text(INDEX_DOCKET_STATE_DDL))
def ensure_hnsw(engine: Engine, dim: int) -> None: def ensure_hnsw(engine: Engine, dim: int) -> None:

View File

@@ -24,3 +24,20 @@ class TestDdl:
executed = " ".join(str(call.args[0]) for call in conn.execute.call_args_list) executed = " ".join(str(call.args[0]) for call in conn.execute.call_args_list)
assert "vector(768)" in executed assert "vector(768)" in executed
assert "USING hnsw" in executed assert "USING hnsw" in executed
def test_fingerprint_column_and_docket_state(self):
assert "fingerprint" in migrate.INDEX_STATE_DDL
assert "ADD COLUMN IF NOT EXISTS fingerprint" in migrate.INDEX_STATE_ALTER
assert (
"CREATE TABLE IF NOT EXISTS index_docket_state"
in migrate.INDEX_DOCKET_STATE_DDL
)
assert "PRIMARY KEY (collection, docket)" in migrate.INDEX_DOCKET_STATE_DDL
def test_migrate_runs_alter_and_docket_state(self):
engine = MagicMock()
conn = engine.begin.return_value.__enter__.return_value
migrate.migrate(engine)
executed = " ".join(str(call.args[0]) for call in conn.execute.call_args_list)
assert "ADD COLUMN IF NOT EXISTS fingerprint" in executed
assert "index_docket_state" in executed