""" Zentrale Mustache-Variablen für Planungs-KI-Prompts. Orchestratoren bauen domänenspezifische Basis-Variablen; dieses Modul merged erweiterbare Provider (Katalog-Slots, später weitere Kontexte). """ from __future__ import annotations from typing import Any, Callable, Dict, Mapping, Optional from catalog_prompt_slots import all_placeholder_keys, empty_catalog_variables from planning_catalog_context import ProgressionPlanningCatalogContext PlanningPromptVariableProvider = Callable[..., Dict[str, str]] def _catalog_slot_variables( *, cur, catalog: Optional[ProgressionPlanningCatalogContext] = None, slug: Optional[str] = None, **_: Any, ) -> Dict[str, str]: if cur is None or catalog is None: return empty_catalog_variables() from catalog_prompt_slots import resolve_catalog_prompt_variables resolved = resolve_catalog_prompt_variables(cur, catalog, slug=slug) return {k: str(resolved.get(k) or "") for k in all_placeholder_keys()} _PLANNING_PROMPT_VARIABLE_PROVIDERS: tuple[PlanningPromptVariableProvider, ...] = ( _catalog_slot_variables, ) def merge_planning_prompt_variables( cur, base_variables: Mapping[str, str], *, catalog: Optional[ProgressionPlanningCatalogContext] = None, slug: Optional[str] = None, ) -> Dict[str, str]: """Merged Basis-Variablen mit allen registrierten Planungs-Providern.""" out = {str(k): "" if v is None else str(v) for k, v in base_variables.items()} ctx: Dict[str, Any] = {"cur": cur, "catalog": catalog, "slug": slug} for provider in _PLANNING_PROMPT_VARIABLE_PROVIDERS: out.update(provider(**ctx)) return out def planning_prompt_placeholder_catalog() -> dict: """Platzhalter-Katalog für Admin — Slot-Typ × Dimension + Aggregat.""" from catalog_prompt_slots import CATALOG_KINDS, SLOT_KEYS, placeholder_key slot_labels = { "description": "Allgemeine Beschreibung", "hints_on_progression": "Hinweise Progressionsgraph / Stufen", "hints_on_exercise": "Hinweise Übungsanlage / Gap-Fill", "hints_on_path_qa": "Bewertungsmaßstäbe Pfad-QS", "anti_patterns": "Anti-Patterns (Fehlbewertung vermeiden)", "rematch_guard": "Rematch-Guard (primär Code, optional Prompt)", } kind_labels = {c.kind: c.label_de for c in CATALOG_KINDS} slugs_common = [ "planning_exercise_path_qa", "planning_progression_roadmap", "planning_progression_stage_spec", "planning_progression_goal_analysis", "planning_progression_start_target", ] defs = [] for cfg in CATALOG_KINDS: for slot in SLOT_KEYS: key = placeholder_key(cfg.kind, slot) defs.append( { "key": key, "placeholder": "{{" + key + "}}", "description": ( f"{kind_labels.get(cfg.kind, cfg.kind)} — " f"{slot_labels.get(slot, slot)} (aktiver Eintrag aus planning_catalog_context)." ), "used_by_slugs": slugs_common, } ) defs.extend( [ { "key": "catalog_guidance_block", "placeholder": "{{catalog_guidance_block}}", "description": "Aggregierter Markdown-Block aus aktiven Slots (slug-spezifisches Profil).", "used_by_slugs": slugs_common, }, { "key": "catalog_context_json", "placeholder": "{{catalog_context_json}}", "description": "Audit-JSON der gewählten Katalog-Einträge und befüllten Slots.", "used_by_slugs": slugs_common[:3], }, { "key": "has_catalog_guidance", "placeholder": "{{has_catalog_guidance}}", "description": "„true“ wenn mindestens ein LLM-Slot gesetzt; sonst leer.", "used_by_slugs": slugs_common[:3], }, ] ) return {"context": "planning", "placeholders": defs} __all__ = [ "merge_planning_prompt_variables", "planning_prompt_placeholder_catalog", ]