feat(storage): add app settings and model-scoped collections

Why:
- Different Embedding Models must not share Qdrant collections; Admin needs persisted role defaults and Experiment provenance.

Changes:
- Scope collection names by model id; add app_settings, embedding provenance columns, and queries.expansion_tree.

Impact:
- Process/query write only Model Corpus collections; legacy unscoped names are read-only for Admin labeling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-10 14:12:45 +03:30
parent 5fd19c12d9
commit 0ff9d8dd21
2 changed files with 284 additions and 49 deletions

View File

@@ -62,6 +62,7 @@ CREATE TABLE IF NOT EXISTS queries (
question TEXT NOT NULL,
answer TEXT NOT NULL,
retrieved_chunks TEXT NOT NULL DEFAULT '[]',
expansion_tree TEXT NOT NULL DEFAULT '[]',
latency_breakdown TEXT NOT NULL DEFAULT '{}',
token_usage TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
@@ -76,17 +77,100 @@ CREATE TABLE IF NOT EXISTS experiments (
per_question TEXT NOT NULL DEFAULT '[]',
aggregate_metrics TEXT NOT NULL DEFAULT '{}',
strategies_used TEXT NOT NULL DEFAULT '[]',
embedding_model_id TEXT,
embedding_provider TEXT,
boundary_embedding_model_id TEXT,
created_at TEXT NOT NULL,
FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS app_settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
"""
def init_db() -> None:
"""Create tables if they don't exist."""
"""Create tables if they don't exist and apply light migrations."""
conn = _connect()
try:
conn.executescript(_SCHEMA)
_migrate_schema(conn)
conn.commit()
finally:
conn.close()
def _migrate_schema(conn: sqlite3.Connection) -> None:
"""Add Embedding Model provenance columns and backfill legacy experiments."""
cols = {
row[1]
for row in conn.execute("PRAGMA table_info(experiments)").fetchall()
}
if "embedding_model_id" not in cols:
conn.execute("ALTER TABLE experiments ADD COLUMN embedding_model_id TEXT")
if "embedding_provider" not in cols:
conn.execute("ALTER TABLE experiments ADD COLUMN embedding_provider TEXT")
if "boundary_embedding_model_id" not in cols:
conn.execute(
"ALTER TABLE experiments ADD COLUMN boundary_embedding_model_id TEXT"
)
dcols = {
row[1]
for row in conn.execute("PRAGMA table_info(documents)").fetchall()
}
if "last_corpus_embedding_model_id" not in dcols:
conn.execute(
"ALTER TABLE documents ADD COLUMN last_corpus_embedding_model_id TEXT"
)
if "last_boundary_embedding_model_id" not in dcols:
conn.execute(
"ALTER TABLE documents ADD COLUMN last_boundary_embedding_model_id TEXT"
)
qcols = {
row[1]
for row in conn.execute("PRAGMA table_info(queries)").fetchall()
}
if "expansion_tree" not in qcols:
conn.execute(
"ALTER TABLE queries ADD COLUMN expansion_tree TEXT NOT NULL DEFAULT '[]'"
)
# Legacy Experiments without provenance → historical OpenAI small (ADR-0019)
from src.chunking.embedding_models import LEGACY_CLOUD_MODEL_ID, Provider
conn.execute(
"""UPDATE experiments
SET embedding_model_id = ?, embedding_provider = ?
WHERE embedding_model_id IS NULL OR embedding_model_id = ''""",
(LEGACY_CLOUD_MODEL_ID, Provider.CLOUD.value),
)
def get_app_setting(key: str) -> str | None:
"""Read a persisted app setting value."""
conn = _connect()
try:
row = conn.execute(
"SELECT value FROM app_settings WHERE key = ?", (key,)
).fetchone()
return row["value"] if row else None
finally:
conn.close()
def set_app_setting(key: str, value: str) -> None:
"""Upsert a persisted app setting."""
conn = _connect()
try:
conn.execute(
"""INSERT INTO app_settings (key, value) VALUES (?, ?)
ON CONFLICT(key) DO UPDATE SET value = excluded.value""",
(key, value),
)
conn.commit()
finally:
conn.close()
@@ -157,6 +241,27 @@ def update_chunk_counts(doc_id: str, chunk_counts: dict[str, int]) -> None:
conn.close()
def update_process_embedding_provenance(
doc_id: str,
*,
corpus_model_id: str,
boundary_model_id: str | None = None,
) -> None:
"""Record Corpus/Boundary used on the last successful process run."""
conn = _connect()
try:
conn.execute(
"""UPDATE documents
SET last_corpus_embedding_model_id = ?,
last_boundary_embedding_model_id = ?
WHERE id = ?""",
(corpus_model_id, boundary_model_id, doc_id),
)
conn.commit()
finally:
conn.close()
def delete_document(doc_id: str) -> bool:
"""Delete a document and its cascaded queries/experiments."""
conn = _connect()
@@ -178,6 +283,7 @@ def save_query(
question: str,
answer: str,
retrieved_chunks: list[dict] | None = None,
expansion_tree: list[dict] | None = None,
latency_breakdown: dict[str, float] | None = None,
token_usage: dict[str, int] | None = None,
) -> dict[str, Any]:
@@ -188,10 +294,11 @@ def save_query(
conn.execute(
"""INSERT INTO queries
(id, document_id, strategy_name, question, answer,
retrieved_chunks, latency_breakdown, token_usage, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
retrieved_chunks, expansion_tree, latency_breakdown, token_usage, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(query_id, document_id, strategy_name, question, answer,
json.dumps(retrieved_chunks or []),
json.dumps(expansion_tree or []),
json.dumps(latency_breakdown or {}),
json.dumps(token_usage or {}),
_now()),
@@ -250,22 +357,36 @@ def save_experiment(
per_question: list[dict] | None = None,
aggregate_metrics: dict | None = None,
strategies_used: list[str] | None = None,
embedding_model_id: str | None = None,
embedding_provider: str | None = None,
boundary_embedding_model_id: str | None = None,
) -> dict[str, Any]:
"""Insert a benchmark experiment."""
"""Insert a benchmark experiment.
embedding_model_id is the Corpus Embedding Model (ADR-0024).
"""
from src.chunking.embedding_models import DEFAULT_CLOUD_MODEL_ID, Provider
experiment_id = experiment_id or _new_id()
model_id = embedding_model_id or DEFAULT_CLOUD_MODEL_ID
provider = embedding_provider or Provider.CLOUD.value
conn = _connect()
try:
conn.execute(
"""INSERT INTO experiments
(id, document_id, benchmark_config, questions, per_question,
aggregate_metrics, strategies_used, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
aggregate_metrics, strategies_used, embedding_model_id,
embedding_provider, boundary_embedding_model_id, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(experiment_id, document_id,
json.dumps(benchmark_config or {}),
json.dumps(questions or []),
json.dumps(per_question or []),
json.dumps(aggregate_metrics or {}),
json.dumps(strategies_used or []),
model_id,
provider,
boundary_embedding_model_id,
_now()),
)
conn.commit()
@@ -324,7 +445,7 @@ def delete_experiment(experiment_id: str) -> bool:
# ── Internal helpers ───────────────────────────────────────────────
_JSON_FIELDS = {"chunk_counts", "retrieved_chunks", "latency_breakdown",
_JSON_FIELDS = {"chunk_counts", "retrieved_chunks", "expansion_tree", "latency_breakdown",
"token_usage", "document_tree", "benchmark_config",
"questions", "per_question", "aggregate_metrics",
"strategies_used"}