All checks were successful
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 35s
Test Suite / playwright-tests (push) Successful in 1m15s
- Introduced support for catalog exercise hints in various functions, including `_build_stage_ai_context`, `try_suggest_ai_stage_step`, and `build_gap_fill_goal_text`, improving the contextual information available for AI suggestions. - Added a new function `catalog_exercise_hints_block` to consolidate hints retrieval from the catalog context. - Updated the `PlanningIntentContext` to include catalog exercise hints, ensuring they are part of the API output. - Implemented tests to verify the inclusion of catalog exercise hints in goal text generation and rematch logic. - Incremented version numbers and updated changelog to reflect these enhancements.
141 lines
4.9 KiB
Python
141 lines
4.9 KiB
Python
"""
|
||
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 catalog_exercise_hints_block(
|
||
cur,
|
||
catalog: Optional[ProgressionPlanningCatalogContext] = None,
|
||
*,
|
||
slug: Optional[str] = None,
|
||
) -> str:
|
||
"""Kombinierte hints_on_exercise aus aktivem Katalog-Kontext (Gap-Fill / Intent)."""
|
||
if cur is None or catalog is None:
|
||
return ""
|
||
from catalog_prompt_slots import CATALOG_KINDS, placeholder_key
|
||
|
||
merged = merge_planning_prompt_variables(cur, {}, catalog=catalog, slug=slug)
|
||
lines: list[str] = []
|
||
for cfg in CATALOG_KINDS:
|
||
key = placeholder_key(cfg.kind, "hints_on_exercise")
|
||
text = (merged.get(key) or "").strip()
|
||
if text:
|
||
lines.append(f"{cfg.label_de}: {text}")
|
||
return "\n".join(lines)
|
||
|
||
|
||
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__ = [
|
||
"catalog_exercise_hints_block",
|
||
"merge_planning_prompt_variables",
|
||
"planning_prompt_placeholder_catalog",
|
||
]
|