shinkan-jinkendo/backend/planning_prompt_variables.py
Lars 3181cc126d
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
Enhance Planning AI Context with Catalog Exercise Hints
- 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.
2026-06-19 12:35:30 +02:00

141 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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",
]