"""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 collections.abc import Iterable from pathlib import Path from typing import TYPE_CHECKING from tht.ports.evidence import ( AcquiredDocument, EvidenceSource, EvidenceSourceError, EvidenceSourceErrorCategory, SourceObject, ) if TYPE_CHECKING: from tht.corpus.store import CorpusStore from tht.decisions import DecisionRecord from tht.search.evidence import ActiveEvidenceSearcher from tht.session.models import SchemaLinking def discover(source: EvidenceSource) -> Iterable[SourceObject]: """Discover source objects without changing source-defined laziness or ordering.""" return source.discover() def acquire(source: EvidenceSource, item: SourceObject) -> AcquiredDocument: """Acquire one discovered object, preserving the source's classified failures.""" return source.acquire(item) 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_retrieval_entries", "discover", "project_session", "resolve_citation", "validate_corpus_workspace", ]