""" KI-Prompt Jobs: validierter Kontext (Pydantic) + Zugriff auf gemeinsame Resolver. Importiert exercise_ai nur fuer Platzhalter-Builder — Router importieren dieses Modul oder exercise_ai, nicht umgekehrt von exercise_ai nach ai_prompt_job (Zirkel vermeiden). """ from __future__ import annotations from typing import Dict, List, Optional, Tuple from pydantic import BaseModel, Field from exercise_ai import build_exercise_placeholder_variables class ExerciseFormAiFocusRow(BaseModel): """Fokusbereich fuer Skill-Retrieval-Kontext (wie ExerciseAiFocusCtx / Admin-Preview).""" focus_area_id: int = Field(..., ge=1) is_primary: Optional[bool] = False class ExerciseFormAiPromptContext(BaseModel): """ Eingabe fuer Uebungsbezogene Prompts (Kurzfassung / Skill-JSON-Vorschlag). Entspricht fachlich dem Preview-Body unter /api/admin/ai-prompts/*/preview. """ title: Optional[str] = "" goal: Optional[str] = None execution: Optional[str] = None focus_hint: Optional[str] = None focus_areas_context: Optional[List[ExerciseFormAiFocusRow]] = None def focus_area_tuples(self) -> Optional[List[Tuple[int, bool]]]: if not self.focus_areas_context: return None return [(int(x.focus_area_id), bool(x.is_primary)) for x in self.focus_areas_context] def resolve_exercise_form_variables(cur, slug: str, ctx: ExerciseFormAiPromptContext) -> Dict[str, str]: """Baut die Mustache-Map fuer exercise_summary / exercise_skill_suggestions.""" return build_exercise_placeholder_variables( cur, slug=slug, title=(ctx.title or "").strip(), goal=ctx.goal, execution=ctx.execution, focus_area_hint=ctx.focus_hint, focus_areas_context=ctx.focus_area_tuples(), ) __all__ = [ "ExerciseFormAiFocusRow", "ExerciseFormAiPromptContext", "resolve_exercise_form_variables", ]