142 lines
6.1 KiB
Python
142 lines
6.1 KiB
Python
"""Central construction of deployment-specific adapters."""
|
|
|
|
from tht.adapters.dwh import PostgresDwhAdapter, ThothRestDwhAdapter
|
|
from tht.adapters.evidence import FilesystemEvidenceSource, HttpManifestEvidenceSource
|
|
from tht.adapters.evidence.s3 import S3EvidenceSource
|
|
from tht.adapters.vector import PgVectorStore, ThothHttpVectorStore
|
|
from tht.config import Config, ConfigError
|
|
from tht.db.connection import make_engine
|
|
from tht.ports.dwh import DwhAdapter
|
|
from tht.ports.vector import VectorStore
|
|
from tht.vectorstore.rest_client import VectorRestClient
|
|
|
|
|
|
def build_dwh(cfg: Config) -> DwhAdapter:
|
|
"""Build the DWH adapter selected by the validated workspace resource."""
|
|
resource = cfg.dwh
|
|
match resource.type:
|
|
case "postgres_direct":
|
|
return PostgresDwhAdapter(
|
|
resource.connection,
|
|
statement_timeout_ms=cfg.execution.statement_timeout_ms,
|
|
)
|
|
case "thoth_rest":
|
|
return ThothRestDwhAdapter(resource.database, resource.endpoint)
|
|
case other: # pragma: no cover - Pydantic's discriminator rejects this first.
|
|
raise ConfigError(f"Adapter DWH non supportato: {other}")
|
|
|
|
|
|
def build_vector_store(cfg: Config, *, require_write: bool = False) -> VectorStore:
|
|
"""Build the vector adapter, optionally requiring an HTTP writer credential."""
|
|
resource = cfg.vectors
|
|
if resource is None:
|
|
raise ConfigError("Risorsa vectors non configurata")
|
|
|
|
match resource.type:
|
|
case "pgvector_direct":
|
|
reader = resource.reader or resource.connection
|
|
if require_write and resource.writer is None:
|
|
raise ConfigError("Vector writer non configurato per pgvector_direct")
|
|
return PgVectorStore(
|
|
reader,
|
|
resource.writer,
|
|
expected_dimension=cfg.embeddings.dim if cfg.embeddings is not None else None,
|
|
)
|
|
case "thoth_vector_http":
|
|
if require_write and resource.writer is None:
|
|
raise ConfigError("Vector writer non configurato")
|
|
return ThothHttpVectorStore(
|
|
VectorRestClient(resource.reader) if resource.reader is not None else None,
|
|
VectorRestClient(resource.writer) if resource.writer is not None else None,
|
|
expected_dimension=cfg.embeddings.dim if cfg.embeddings is not None else None,
|
|
)
|
|
case other: # pragma: no cover - Pydantic's discriminator rejects this first.
|
|
raise ConfigError(f"Adapter vector non supportato: {other}")
|
|
|
|
|
|
def build_vector_loader(cfg: Config, collection: str):
|
|
"""Compatibility construction for legacy collection sync commands."""
|
|
resource = cfg.vectors
|
|
if resource is None:
|
|
raise ConfigError("Risorsa vectors non configurata")
|
|
if cfg.embeddings is None:
|
|
raise ConfigError("Embeddings non configurati")
|
|
|
|
if (
|
|
resource.type == "thoth_vector_http"
|
|
and resource.writer is not None
|
|
and (cfg.profile == "workstation" or resource.direct is None)
|
|
):
|
|
from tht.vectorstore.rest_writer import RestVectorWriter
|
|
|
|
return RestVectorWriter(VectorRestClient(resource.writer), table=collection)
|
|
|
|
connection = (
|
|
resource.writer or resource.connection
|
|
if resource.type == "pgvector_direct"
|
|
else resource.direct
|
|
)
|
|
if connection is None:
|
|
raise ConfigError("Vector writer non configurato")
|
|
from tht.vectorstore.store import VectorStore as TableVectorStore
|
|
|
|
return TableVectorStore(
|
|
make_engine(connection),
|
|
schema=connection.db_schema,
|
|
table=collection,
|
|
dim=cfg.embeddings.dim,
|
|
)
|
|
|
|
|
|
def build_evidence_sources(cfg: Config):
|
|
"""Build configured Evidence sources, including the legacy curated filesystem tree."""
|
|
evidence = cfg.evidence
|
|
if evidence is None:
|
|
return []
|
|
sources = []
|
|
if evidence.source_root is not None:
|
|
sources.append(FilesystemEvidenceSource(evidence.source_root / evidence.evidence_dir))
|
|
for resource in evidence.sources:
|
|
match resource.type:
|
|
case "filesystem":
|
|
sources.append(
|
|
FilesystemEvidenceSource(
|
|
resource.root,
|
|
patterns=resource.patterns,
|
|
max_bytes=resource.max_bytes,
|
|
)
|
|
)
|
|
case "http":
|
|
sources.append(
|
|
HttpManifestEvidenceSource(
|
|
[url.get_secret_value() for url in resource.urls],
|
|
connect_timeout=resource.connect_timeout,
|
|
read_timeout=resource.read_timeout,
|
|
max_bytes=resource.max_bytes,
|
|
max_redirects=resource.max_redirects,
|
|
allow_private_hosts=resource.allow_private_hosts,
|
|
max_cache_bytes=resource.max_cache_bytes,
|
|
)
|
|
)
|
|
case "s3":
|
|
def secret(value):
|
|
return value.get_secret_value() if value is not None else None
|
|
|
|
sources.append(S3EvidenceSource(
|
|
bucket=resource.bucket, prefix=resource.prefix,
|
|
endpoint_url=resource.endpoint_url, region=resource.region,
|
|
access_key=secret(resource.access_key), secret_key=secret(resource.secret_key),
|
|
session_token=secret(resource.session_token),
|
|
trusted_endpoint=resource.trusted_endpoint,
|
|
allow_private_endpoint=resource.allow_private_endpoint,
|
|
allow_insecure_endpoint=resource.allow_insecure_endpoint,
|
|
max_bytes=resource.max_bytes, max_objects=resource.max_objects,
|
|
max_pages=resource.max_pages, page_size=resource.page_size,
|
|
))
|
|
case other: # pragma: no cover - Pydantic rejects unsupported discriminators.
|
|
raise ConfigError(f"Adapter evidence non supportato: {other}")
|
|
return sources
|
|
|
|
|
|
__all__ = ["build_dwh", "build_evidence_sources", "build_vector_loader", "build_vector_store"]
|