feat: implement memory and evidence administration with guided repairs
Publish documentation / publish (push) Successful in 1m27s
Publish documentation / publish (push) Successful in 1m27s
Add PostgreSQL-backed memory, editable evidence with source review and activation, and human-approved archive repairs across the harness, API, and UI. Include migrations, deployment support, regression coverage, and validation documentation. Refresh permissions from validated session roles so existing administrator logins can access newly deployed archive management features.
This commit is contained in:
@@ -50,6 +50,10 @@ def _evaluation_workspace_root(cfg) -> Path:
|
||||
|
||||
def _requires_candidate_evaluation(cfg) -> bool:
|
||||
evidence = cfg.evidence
|
||||
if evidence and evidence.local_archive_root and (
|
||||
evidence.local_archive_root / "evidence/.local/state.yaml"
|
||||
).exists():
|
||||
return False
|
||||
if evidence is None or evidence.schema_version != 2:
|
||||
return False
|
||||
return evidence.source_root is not None or any(
|
||||
@@ -224,7 +228,8 @@ def _parse_dwh_steps(value: str) -> tuple[str, ...]:
|
||||
return steps
|
||||
|
||||
|
||||
def run_from_config(config: Path, *, dry_run: bool = False, resume: str | None = None):
|
||||
def run_from_config(config: Path, *, dry_run: bool = False, resume: str | None = None,
|
||||
local_snapshot: Path | None = None):
|
||||
from tht.adapters.factory import build_vector_store
|
||||
from tht.cli.schema_cmd import _load_config_or_exit
|
||||
from tht.cli.vector_cmd import make_embedder
|
||||
@@ -239,8 +244,11 @@ def run_from_config(config: Path, *, dry_run: bool = False, resume: str | None =
|
||||
corpus_root = cfg.paths.artifacts.parent / "corpus"
|
||||
vector_store = build_vector_store(cfg, require_write=True)
|
||||
embedder = make_embedder(cfg.embeddings)
|
||||
from tht.evidence.adapters import FilesystemEvidenceSource
|
||||
sources = [FilesystemEvidenceSource(local_snapshot, patterns=("curated/**/*.md",))] \
|
||||
if local_snapshot is not None else build_sources(cfg.evidence)
|
||||
pipeline = build_preprocessing_pipeline(
|
||||
store=CorpusStore(corpus_root), sources=build_sources(cfg.evidence),
|
||||
store=CorpusStore(corpus_root), sources=sources,
|
||||
embedder=embedder,
|
||||
vector_store=vector_store,
|
||||
embedding_id=cfg.embeddings.id or f"ollama/{cfg.embeddings.model}",
|
||||
@@ -376,9 +384,17 @@ def evidence_cmd(
|
||||
dry_run: bool = typer.Option(False, "--dry-run"),
|
||||
resume: str | None = typer.Option(None, "--resume"),
|
||||
json_output: bool = typer.Option(False, "--json"),
|
||||
consolidate: bool = typer.Option(False, "--consolidate"),
|
||||
) -> None:
|
||||
if action is not None and action != "gc":
|
||||
raise typer.BadParameter("only the optional 'gc' action is supported")
|
||||
if consolidate and (dry_run or resume is not None or action is not None):
|
||||
message = "Consolidation cannot be combined with dry-run, resume or gc"
|
||||
if json_output:
|
||||
typer.echo(json.dumps({"status": "failed", "code": "invalid_consolidation", "error": message}))
|
||||
else:
|
||||
typer.secho(message, fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(code=2)
|
||||
if action == "gc":
|
||||
try:
|
||||
payload = gc_from_config(config, dry_run=dry_run)
|
||||
@@ -406,21 +422,29 @@ def evidence_cmd(
|
||||
typer.secho("ERRORE: resume requires a preprocessing run id", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(code=2)
|
||||
try:
|
||||
result = run_from_config(config, dry_run=dry_run, resume=resume)
|
||||
except Exception: # noqa: BLE001
|
||||
if consolidate:
|
||||
from tht.evidence.administration import consolidate_from_config
|
||||
result = consolidate_from_config(config)
|
||||
else:
|
||||
result = run_from_config(config, dry_run=dry_run, resume=resume)
|
||||
except Exception as error: # noqa: BLE001
|
||||
from tht.evidence.administration import ConsolidationError
|
||||
detail = str(error) if isinstance(error, ConsolidationError) else "preprocessing failed"
|
||||
payload = {"status": "failed"}
|
||||
if isinstance(error, ConsolidationError):
|
||||
payload["saved"] = error.saved
|
||||
if json_output:
|
||||
typer.echo(json.dumps(
|
||||
_evidence_json_payload(
|
||||
cfg,
|
||||
payload,
|
||||
code="preprocessing_failed",
|
||||
error="preprocessing failed",
|
||||
error=detail,
|
||||
),
|
||||
sort_keys=True,
|
||||
))
|
||||
else:
|
||||
typer.secho("ERRORE: preprocessing failed", fg=typer.colors.RED, err=True)
|
||||
typer.secho(f"ERRORE: {detail}", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(code=1) from None
|
||||
payload = result.model_dump(mode="json")
|
||||
if payload.get("status") != "succeeded":
|
||||
|
||||
Reference in New Issue
Block a user