Spaces:
Sleeping
Sleeping
| """Enricher subsystem — post-normalization enrichment pipeline. | |
| Enrichers transform a CanonicalDocument into a new CanonicalDocument | |
| with additional derived data. They produce new instances (models are frozen). | |
| Every enricher MUST: | |
| - Set provenance.evidence_type to 'derived' or 'inferred' | |
| - Update geometry.status if geometry was modified | |
| - Add warnings when appropriate | |
| - Respect the active DocumentPolicy | |
| """ | |
| from __future__ import annotations | |
| from abc import ABC, abstractmethod | |
| from typing import TYPE_CHECKING | |
| from src.app.policies.document_policy import DocumentPolicy | |
| if TYPE_CHECKING: | |
| from src.app.domain.models import CanonicalDocument | |
| class BaseEnricher(ABC): | |
| """Abstract base for enrichers.""" | |
| def name(self) -> str: | |
| """Enricher name used in audit trail.""" | |
| ... | |
| def enrich( | |
| self, doc: CanonicalDocument, policy: DocumentPolicy | |
| ) -> CanonicalDocument: | |
| """Apply enrichment, returning a new document.""" | |
| ... | |
| class EnricherPipeline: | |
| """Runs a configurable chain of enrichers.""" | |
| def __init__(self, enrichers: list[BaseEnricher] | None = None) -> None: | |
| self._enrichers = enrichers or [] | |
| def add(self, enricher: BaseEnricher) -> None: | |
| self._enrichers.append(enricher) | |
| def run( | |
| self, doc: CanonicalDocument, policy: DocumentPolicy | None = None | |
| ) -> CanonicalDocument: | |
| """Run all enrichers in order, threading the document through.""" | |
| if policy is None: | |
| policy = DocumentPolicy() | |
| applied: list[str] = list(doc.audit.enrichers_applied) | |
| current = doc | |
| for enricher in self._enrichers: | |
| current = enricher.enrich(current, policy) | |
| applied.append(enricher.name) | |
| # Update audit with enrichers applied | |
| new_audit = doc.audit.model_copy(update={"enrichers_applied": applied}) | |
| return current.model_copy(update={"audit": new_audit}) | |