"""Single prompt execution entry. Templates live in the DB; LLM egress only via Privacy Gateway.""" from __future__ import annotations from typing import Any import placeholder_system # noqa: F401 registers system keys import privacy_placeholders from entitlements import EntitlementError, check_feature_access from placeholders import PlaceholderError, resolve_template from privacy_gateway import GatewayRequest, PrivacyGatewayError, complete class EngineError(Exception): def __init__(self, code: str, message: str, status_code: int = 400): super().__init__(message) self.code = code self.message = message self.status_code = status_code def preview_prompt(prompt: dict, context: dict[str, Any] | None = None) -> dict: template = prompt.get("template") or "" if prompt.get("prompt_type") != "base": raise EngineError("prompt_type_not_ready", "Pipeline- und Workflow-Typen sind vorbereitet, aber noch ohne Executor-Stufen.") try: rendered = resolve_template(template, context or {}) privacy_tokens = privacy_placeholders.validate_tokens(template) except PlaceholderError as exc: raise EngineError(exc.code, exc.message) from exc return { "prompt_id": prompt["id"], "slug": prompt["slug"], "rendered": rendered, "privacy_tokens": privacy_tokens, "llm": False, } def execute_prompt(prompt: dict, profile_id: str, purpose: str, data_class: str, context: dict[str, Any] | None = None) -> dict: preview = preview_prompt(prompt, context) feature_id = prompt.get("required_feature") or "ai_calls" try: check_feature_access(profile_id, feature_id) except EntitlementError as exc: raise EngineError(exc.code, exc.message, exc.status_code) from exc try: complete( GatewayRequest( prompt_id=prompt["id"], purpose=purpose, data_class=data_class, payload={"template_chars": len(preview["rendered"]), "privacy_tokens": preview["privacy_tokens"]}, ) ) except PrivacyGatewayError as exc: raise EngineError(exc.code, exc.message, exc.status_code) from exc raise EngineError("provider_missing", "Gateway erlaubte einen Aufruf ohne konfigurierten Provider", 500)