feat: P3 effective configuration, memory root, and revision-scoped records

This commit is contained in:
2026-08-12 16:01:25 +02:00
parent beaba548c1
commit e23e526966
21 changed files with 1108 additions and 44 deletions
+72
View File
@@ -1,3 +1,4 @@
import hashlib
import json
import os
import re
@@ -16,6 +17,74 @@ from tht.ports.evidence import canonical_provenance_uri
_ENV_RE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")
def _canonical_fingerprint(value: str) -> str:
return "sha256:" + hashlib.sha256(value.encode("utf-8")).hexdigest()
def canonical_effective_config_document(cfg) -> dict:
"""Non-secret effective DWH/preprocessing configuration (versioned, P3).
Mirrors backend/src/workspaces/effective-config.ts: only fields that determine whether a
prepared DWH generation is reusable. Deliberately excludes session_storage, runtime_identity,
evidence, memory, search, execution, and every credential value.
"""
dwh: dict = {"engine": "postgres"}
dwh_cfg = getattr(cfg, "dwh", None)
if dwh_cfg is None:
raise ConfigError("DWH configuration is unavailable; cannot canonicalize effective config")
transport = getattr(dwh_cfg, "type", None)
if transport == "postgres_direct":
conn = dwh_cfg.connection
dwh["database"] = conn.database
dwh["schema"] = getattr(conn, "db_schema", None) or getattr(conn, "schema", None) or conn.database
dwh["transport"] = "postgres_direct"
dwh["host"] = conn.host
dwh["port"] = conn.port
dwh["user"] = conn.user
elif transport == "thoth_rest":
dwh["database"] = dwh_cfg.database.database
dwh["schema"] = dwh_cfg.database.db_schema
dwh["transport"] = "rest_api"
dwh["baseUrl"] = dwh_cfg.endpoint.base_url
else:
raise ConfigError("unsupported DWH transport in canonical effective config")
vectors = getattr(cfg, "vectors", None)
collection = getattr(vectors, "collection", None) if vectors is not None else None
if not collection:
raise ConfigError("vector configuration is unavailable; cannot canonicalize effective config")
embeddings = getattr(cfg, "embeddings", None)
model = getattr(embeddings, "model", None) if embeddings is not None else None
embed_dim = getattr(embeddings, "dim", None) if embeddings is not None else None
if not model or not embed_dim:
raise ConfigError("embedding configuration is unavailable; cannot canonicalize effective config")
return {
"schemaVersion": 1,
"dwh": dwh,
"vector": {"collection": collection, "dimensions": 1024, "distance": "cosine"},
"embedding": {"model": model, "dimensions": int(embed_dim)},
"roots": {
"artifacts": str(getattr(cfg.paths, "artifacts", Path("artifacts"))),
"indexes": str(getattr(cfg.paths, "indexes", Path("indexes"))),
},
}
def canonical_effective_config_json(cfg) -> str:
return json.dumps(canonical_effective_config_document(cfg), separators=(",", ":"), ensure_ascii=False)
def effective_config_identity(workspace_id: str, cfg) -> str:
digest = hashlib.sha256(canonical_effective_config_json(cfg).encode("utf-8")).hexdigest()
return f"workspace://{workspace_id}@v1:{digest}"
def effective_config_fingerprint(cfg) -> str:
return _canonical_fingerprint(canonical_effective_config_json(cfg))
def effective_config_input_fingerprint(workspace_id: str, cfg) -> str:
return _canonical_fingerprint(effective_config_identity(workspace_id, cfg))
class ConfigError(Exception):
"""Errore di configurazione, con messaggio leggibile per l'utente."""
@@ -252,6 +321,9 @@ class PathsConfig(BaseModel):
artifacts: Path = Path("artifacts")
indexes: Path = Path("indexes")
sessions: Path = Path("sessions")
# Explicit workspace-global memory root (P3). When absent, legacy `artifacts/memory` is used
# only through the documented migration path.
memory: Path | None = None
class RuntimeIdentityConfig(BaseModel):