Files
ThothII/harness/tht/evidence/__init__.py
T

186 lines
6.0 KiB
Python

"""Cohesive public entrypoint for Evidence domain capabilities.
The implementation is introduced beside the legacy module layout so production callers can
migrate one path at a time. These functions deliberately preserve the existing objects, ordering,
and exceptions; they do not define a cross-domain service protocol.
"""
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
from tht.evidence.acquisition import acquire, discover
from tht.evidence.contracts import (
AcquiredDocument,
EvidenceSource,
EvidenceSourceError,
EvidenceSourceErrorCategory,
SourceObject,
canonical_provenance_uri,
normalize_aware_datetime,
validate_namespaced_value,
validate_safe_metadata,
)
if TYPE_CHECKING:
from tht.config import EvidenceSourcesConfig
from tht.corpus.chunk import ChunkPolicy
from tht.corpus.pipeline import CorpusPipeline
from tht.corpus.store import CorpusStore
from tht.decisions import DecisionRecord
from tht.evidence.preprocessing import EvidenceEmbedder
from tht.ports.vector import VectorStore
from tht.search.evidence import ActiveEvidenceSearcher
from tht.session.models import SchemaLinking
def build_sources(evidence: "EvidenceSourcesConfig | None") -> list[EvidenceSource]:
"""Build configured Evidence source adapters in the existing deterministic order."""
from tht.evidence.sources import build_sources as build_configured_sources
return build_configured_sources(evidence)
def build_preprocessing_pipeline(
*,
store: "CorpusStore",
sources: list[EvidenceSource],
embedder: "EvidenceEmbedder",
vector_store: "VectorStore",
embedding_model: str,
embedding_dimensions: int,
chunk_policy: "ChunkPolicy",
pipeline_version: str,
retain_published_generations: int = 3,
workspace_id: str | None = None,
) -> "CorpusPipeline":
"""Construct the Evidence preprocessing use case from core-owned infrastructure."""
from tht.evidence.preprocessing import build_preprocessing_pipeline as build_pipeline
return build_pipeline(
store=store,
sources=sources,
embedder=embedder,
vector_store=vector_store,
embedding_model=embedding_model,
embedding_dimensions=embedding_dimensions,
chunk_policy=chunk_policy,
pipeline_version=pipeline_version,
retain_published_generations=retain_published_generations,
workspace_id=workspace_id,
)
def active_searcher(
cfg,
delegate,
*,
workspace_id: str | None = None,
) -> ActiveEvidenceSearcher:
"""Bind vector search to the atomically ACTIVE Evidence corpus generation."""
from tht.search.evidence import active_searcher as legacy_active_searcher
return legacy_active_searcher(cfg, delegate, workspace_id=workspace_id)
def validate_corpus_workspace(cfg, workspace_id: str) -> None:
"""Validate persisted Evidence corpus ownership before runtime retrieval setup."""
from tht.search.evidence import validate_corpus_workspace as legacy_validate
legacy_validate(cfg, workspace_id)
def build_retrieval_entries(results, *, excerpt_chars: int) -> list[dict]:
"""Project ordered Evidence search hits into the existing retrieval-pack shape."""
return [
{
"title": result.label,
"status": result.status,
"excerpt": result.content[:excerpt_chars],
}
for result in results
]
def resolve_citation(
store: "CorpusStore",
evidence_id: str,
*,
materialized_root: Path | None = None,
) -> str:
"""Resolve an Evidence identifier to its immutable ACTIVE materialization."""
from tht.search.evidence import resolve_evidence_file
return resolve_evidence_file(
store,
evidence_id,
materialized_root=materialized_root,
)
def _resolve_session_citation(evidence_root: Path, evidence_id: str) -> str:
# New deployments resolve only immutable materialized files from ACTIVE. Keep the
# curated-tree fallback for sessions created before a canonical corpus exists.
corpus_root = evidence_root.parent.parent / "corpus"
if corpus_root.exists():
from tht.corpus.store import CorpusStore
return resolve_citation(
CorpusStore(corpus_root),
evidence_id,
materialized_root=evidence_root.parent / ".materialized-evidence",
)
for match in evidence_root.rglob(f"{evidence_id}.md"):
return str(match)
return ""
def project_session(
decisions: list["DecisionRecord"],
linking: "SchemaLinking",
evidence_root: Path,
) -> list[dict]:
"""Project cited and reviewed Evidence into the existing session artifact shape."""
entries: dict[str, dict] = {}
for candidate in linking.candidates:
for evidence_id in candidate.evidence:
entries.setdefault(evidence_id, {
"id": evidence_id,
"file": _resolve_session_citation(evidence_root, evidence_id),
"esito": "usata",
"decision_seq": candidate.decision_seq,
})
for decision in decisions:
if decision.type not in ("evidence_accepted", "evidence_rejected"):
continue
entries[decision.subject] = {
"id": decision.subject,
"file": _resolve_session_citation(evidence_root, decision.subject),
"esito": "accettata" if decision.type == "evidence_accepted" else "scartata",
"decision_seq": decision.seq,
}
return list(entries.values())
__all__ = [
"AcquiredDocument",
"EvidenceSource",
"EvidenceSourceError",
"EvidenceSourceErrorCategory",
"SourceObject",
"acquire",
"active_searcher",
"build_preprocessing_pipeline",
"build_retrieval_entries",
"build_sources",
"canonical_provenance_uri",
"discover",
"normalize_aware_datetime",
"project_session",
"resolve_citation",
"validate_corpus_workspace",
"validate_namespaced_value",
"validate_safe_metadata",
]