#!/bin/sh set -eu cd "$(dirname "$0")/.." smoke_project=${SMOKE_PROJECT:-"thothii-vector-smoke-$(date +%s)-$$"} keep_resources=${KEEP_SMOKE_RESOURCES:-0} secret_dir=$(mktemp -d) marker="local-vector-$smoke_project" case "$smoke_project" in thothii) echo "SMOKE_PROJECT=thothii is reserved for the operator stack" >&2 exit 2 ;; ""|*[!a-z0-9_-]*|[!a-z0-9]*) echo "invalid SMOKE_PROJECT: use lowercase letters, digits, hyphens, or underscores" >&2 exit 2 ;; esac for secret in bootstrap migrator reader writer; do password="smoke-${secret}-${smoke_project}" printf '%s' "$password" >"$secret_dir/$secret" chmod 0600 "$secret_dir/$secret" done export THT_VECTOR_BOOTSTRAP_PASSWORD_SECRET_FILE="$secret_dir/bootstrap" export THT_VECTOR_MIGRATOR_PASSWORD_SECRET_FILE="$secret_dir/migrator" export THT_VECTOR_READER_PASSWORD_SECRET_FILE="$secret_dir/reader" export THT_VECTOR_WRITER_PASSWORD_SECRET_FILE="$secret_dir/writer" export THT_VECTOR_READER_PASSWORD="smoke-reader-${smoke_project}" export THT_VECTOR_WRITER_PASSWORD="smoke-writer-${smoke_project}" compose() { docker compose --project-name "$smoke_project" --profile local-vector "$@" } cleanup() { if [ "$keep_resources" = "1" ]; then echo "Keeping smoke resources for project $smoke_project (KEEP_SMOKE_RESOURCES=1)." >&2 else compose down --volumes >/dev/null 2>&1 || true fi rm -rf "$secret_dir" } trap cleanup EXIT HUP INT TERM probe_vector() { compose exec -T core /opt/venv/bin/python - "$marker" <<'PY' import hashlib import os import sys from tht.adapters.vector.pgvector import PgVectorStore from tht.config import DatabaseConfig from tht.ports.vector import VectorWriteRecord from tht.vectorstore.records import VectorRecord marker = sys.argv[1] database = "thoth" host = "vector-db" def credential(role: str) -> DatabaseConfig: return DatabaseConfig( host=host, port=5432, database=database, schema="vectors", user=f"thoth_vector_{role}", password=os.environ[f"THT_VECTOR_{role.upper()}_PASSWORD"], ) store = PgVectorStore(credential("reader"), credential("writer"), expected_dimension=768) health = store.health() assert health.ok, health assert health.read_reachable is True and health.write_reachable is True, health record = VectorRecord( id=marker, kind="memory", ref=marker, title="Local vector persistence smoke", content=marker, metadata={"smoke": True}, ) embedding = [1.0] + [0.0] * 767 store.upsert( "memory", [VectorWriteRecord(record, embedding, hashlib.sha256(marker.encode()).hexdigest())], ) hits = store.search(["memory"], embedding, limit=1, kinds=["memory"]) assert hits and hits[0].id == marker, hits print(f"role health, upsert, and search passed for {marker}") PY } compose config --quiet services=$(compose config --services) printf '%s\n' "$services" | grep -qx vector-db printf '%s\n' "$services" | grep -qx vector-migrate compose up --build --wait vector-migrate core migration_status=$(compose run --rm --no-deps vector-migrate) printf '%s\n' "$migration_status" | grep -q '"pending": \[\]' probe_vector compose restart vector-db core compose up --wait vector-db core probe_vector echo "Local pgvector migration, least-privilege roles, search, and restart persistence passed."