"""Per-tenant code hooks. Most customers differ only in *data* (colours, logo, prompt, PDF template) and need no code here. For the few that need genuine behaviour changes, drop an `overrides.py` into their tenant folder defining a `Hooks(TenantHooks)` subclass that overrides only the methods it needs. Core stays untouched, so core updates keep flowing. Example tenants/acme/overrides.py: from core.hooks import TenantHooks class Hooks(TenantHooks): def build_prompt(self, base, ctx): return base + "\\nBevorzuge Fachterminologie aus der Orthopädie." def extra_routes(self, app): @app.get("/health/acme") def _h(): return {"ok": True} """ from __future__ import annotations import importlib.util from pathlib import Path from core.contract import HOOK_API class TenantHooks: """Default (no-op) behaviour. Override selectively per tenant.""" def build_prompt(self, base_prompt: str, ctx: dict) -> str: """Adjust the system prompt sent to the LLM. ctx carries the form fields.""" return base_prompt def transform_transcript(self, transcript: str, ctx: dict) -> str: """Post-process the transcript before summarisation/delivery.""" return transcript def pdf_context(self, context: dict) -> dict: """Last chance to enrich/alter the data passed to the PDF template.""" return context def extra_routes(self, app) -> None: """Register tenant-specific FastAPI routes.""" return None def load_hooks(tenant_dir: Path) -> TenantHooks: override = tenant_dir / "overrides.py" if not override.exists(): return TenantHooks() spec = importlib.util.spec_from_file_location(f"tenant_overrides_{tenant_dir.name}", override) module = importlib.util.module_from_spec(spec) assert spec and spec.loader spec.loader.exec_module(module) declared = getattr(module, "HOOK_API", HOOK_API) # fehlt die Deklaration, gilt die aktuelle Version if declared != HOOK_API: raise RuntimeError(f"{override}: HOOK_API={declared} wird von diesem Core nicht unterstützt (Core: {HOOK_API})") hooks_cls = getattr(module, "Hooks", None) if hooks_cls is None: return TenantHooks() return hooks_cls()