186 lines
6.0 KiB
Python
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",
|
|
]
|