"""Prüfer-Router — method-agnostischer Dispatch. control → sensor_classification (verification_method + decision_method) → Checker. Ein neues Modul liefert nur ControlSpecs; der Router wählt den Prüfer. Damit wird der „Embedding findet, Claude entscheidet"-Pfad EIN gemeinsamer CONTENT/LLM-Prüfer statt Cookie-Sonderlogik. Nicht-gebaute Prüfer (PLAYWRIGHT/AUDIT/SCANNER/REGEX- FIELD) → present=None (fail-safe: Aufrufer behält sein deterministisches Ergebnis). """ from __future__ import annotations from typing import Any, Optional from .base import CheckResult, ControlSpec, DecisionMethod, DocContext from .embedding_checker import EmbeddingChecker from .llm_checker import LLMChecker from .reference_checker import ReferenceChecker _LLM = LLMChecker() _EMB = EmbeddingChecker() _REF = ReferenceChecker() # decision_method → Checker. Fehlende Mechanismen bewusst None (noch nicht gebaut). _BY_DECISION: dict[str, Any] = { DecisionMethod.LLM: _LLM, DecisionMethod.EMBEDDING: _EMB, DecisionMethod.LINK_RESOLVER: _REF, } async def route_and_check(ctrl: ControlSpec, doc: DocContext) -> CheckResult: checker = _BY_DECISION.get((ctrl.decision_method or "").upper()) if checker is None: return CheckResult(present=None, source=f"no_checker:{ctrl.decision_method}") return await checker.check(ctrl, doc) def build_spec( control_id: str, sensor_classification: Optional[dict[str, Any]], *, label: str = "", criteria: Optional[list] = None, question: str = "", patterns: Optional[list[str]] = None, embed_threshold: Optional[float] = None, ) -> ControlSpec: """Baut ein ControlSpec aus der GESPEICHERTEN sensor_classification (canonical_controls.generation_metadata.sensor_classification) + den Control-Kriterien. CONTENT/LLM → judge='haiku' (validierter Sufficiency- Judge; Default für Sufficiency lt. Entscheidung 2026-06-22).""" sc = sensor_classification or {} vm = (sc.get("verification_method") or "").upper() dm = (sc.get("decision_method") or "").upper() extra: dict[str, Any] = {} if vm == "CONTENT" and dm == "LLM": extra["judge"] = "haiku" return ControlSpec( control_id=control_id, verification_method=vm, decision_method=dm, label=label, paraphrases=[str(c) for c in (criteria or []) if c], question=question, patterns=patterns or [], embed_threshold=embed_threshold, extra=extra, )