"""In-memory feature registry with startup sync to PostgreSQL.""" from __future__ import annotations from dataclasses import dataclass from db import get_connection VALID_CONTEXT_KINDS = frozenset( { "kairo.tenant_context", "kairo.actor_context", "kairo.debug_context", } ) VALID_EXECUTION_MODES = frozenset({"single", "pipeline", "workflow"}) @dataclass(frozen=True) class FeatureRegistration: key: str module: str name: str description: str _REGISTRY: dict[str, FeatureRegistration] = {} def register_feature(registration: FeatureRegistration) -> None: if not registration.key or not registration.key.strip(): raise ValueError("Feature key is required") if not registration.module or not registration.module.strip(): raise ValueError("Feature module is required") if not registration.name or not registration.name.strip(): raise ValueError("Feature name is required") if not registration.description or not registration.description.strip(): raise ValueError("Feature description is required") if registration.key in _REGISTRY: raise ValueError(f"Feature already registered: {registration.key}") _REGISTRY[registration.key] = registration def get_registered_features() -> tuple[FeatureRegistration, ...]: return tuple(_REGISTRY.values()) def clear_feature_registry_for_tests() -> None: _REGISTRY.clear() def sync_features_to_db() -> int: if not _REGISTRY: print("[feature_registry] Keine Features registriert — Sync übersprungen") return 0 conn = get_connection() try: with conn.cursor() as cur: for reg in _REGISTRY.values(): cur.execute( """ INSERT INTO features (feature_key, module, name, description, is_active) VALUES (%s, %s, %s, %s, TRUE) ON CONFLICT (feature_key) DO UPDATE SET module = EXCLUDED.module, name = EXCLUDED.name, description = EXCLUDED.description, is_active = TRUE, updated_at = NOW() """, (reg.key, reg.module, reg.name, reg.description), ) conn.commit() print(f"[feature_registry] Sync OK — {len(_REGISTRY)} Feature(s)") return 0 except Exception as exc: conn.rollback() print(f"[feature_registry] Sync FAIL: {exc}") return 1 finally: conn.close() def load_active_features() -> dict[str, dict]: conn = get_connection() try: with conn.cursor() as cur: cur.execute( """ SELECT feature_key, module, name, description FROM features WHERE is_active = TRUE ORDER BY feature_key """ ) return { row[0]: { "enabled": True, "module": row[1], "name": row[2], "description": row[3], } for row in cur.fetchall() } finally: conn.close()