"""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_mvp # noqa: F401 import placeholder_system # noqa: F401 registers system keys import privacy_placeholders from db import get_db, row_to_dict from entitlements import EntitlementError, check_feature_access, increment_feature_usage from placeholders import PlaceholderError, resolve_template from privacy_gateway import GatewayRequest, PrivacyGatewayError, complete from journal_reconstruct import parse_dialogue_line class EngineError(Exception): def __init__(self, code: str, message: str, status_code: int = 400, diagnostics: dict | None = None): super().__init__(message) self.code = code self.message = message self.status_code = status_code self.diagnostics = diagnostics or {} def load_active_prompt(slug: str) -> dict: with get_db() as conn: row = row_to_dict( conn.execute( "SELECT * FROM ai_prompts WHERE slug = ? AND active = 1", (slug,), ).fetchone() ) if not row: raise EngineError("prompt_missing", f"Prompt fehlt oder ist inaktiv: {slug}", 404) return row 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 user_source_text(dialogue_context: str) -> str: parts = [] for line in (dialogue_context or "").splitlines(): parsed = parse_dialogue_line(line) if parsed and parsed["role"] == "user": parts.append(parsed["body"]) elif line.startswith("user:"): parts.append(line[5:].lstrip()) return "\n".join(parts) def execute_prompt( prompt: dict, profile_id: str, purpose: str, data_class: str, context: dict[str, Any] | None = None, *, max_tokens: int | None = None, disable_context_compression: bool = False, budget=None, diagnostics: 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: ctx = context or {} source_text = preview["rendered"] if purpose == "profile_review" and ctx.get("source_text"): source_text = "\n".join( part for part in (ctx.get("source_text"), ctx.get("review_package"), source_text) if part ) elif not (source_text or "").strip(): source_text = user_source_text(ctx.get("dialogue_context") or "") result = complete( GatewayRequest( prompt_id=prompt["id"], purpose=purpose, data_class=data_class, profile_id=profile_id, payload={ "rendered": preview["rendered"], "source_text": source_text, "privacy_tokens": preview["privacy_tokens"], "prompt_slug": prompt.get("slug"), "prompt_revision": prompt.get("seed_revision") or "", "max_tokens": max_tokens, "disable_context_compression": disable_context_compression, "budget": budget, "diagnostics": diagnostics or {}, }, ) ) except PrivacyGatewayError as exc: raise EngineError(exc.code, exc.message, exc.status_code, getattr(exc, "diagnostics", None)) from exc increment_feature_usage(profile_id, feature_id) return { **preview, "llm": True, "content": result.content, "provider": result.provider, "trace": result.trace, "diagnostics": result.diagnostics, "local_identities": result.local_identities, }