All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 39s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m16s
- Incremented version to 1.1 and updated the status to reflect the implementation of core features including `ai_prompts`, `prompt_resolver`, and the Superadmin HTTP API. - Documented the current API endpoints for managing AI prompts, including CRUD operations and preview functionality. - Introduced a new placeholder catalog and preview capabilities for the Superadmin interface. - Enhanced the backend with new functions for handling AI prompt templates and integrated them into the API. - Updated frontend components to include navigation and routing for the new Admin AI Prompts page. - Incremented application version to 0.8.158 and updated changelog to reflect these changes.
129 lines
4.2 KiB
Python
129 lines
4.2 KiB
Python
"""
|
|
Mustache-aehnliche Platzhalter {{schluessel}} fuer KI-Templates aus ai_prompts.
|
|
|
|
Kein Vereinsbezug — reine Textersetzung; Aufrufe aus exercise_ai und Admin-Vorschau.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from dataclasses import dataclass
|
|
from typing import Dict, List, Mapping
|
|
|
|
_PLACEHOLDER_RE = re.compile(r"\{\{\s*([a-zA-Z0-9_]+)\s*\}\}")
|
|
|
|
|
|
def _placeholder_pattern_for_key(key: str) -> re.Pattern[str]:
|
|
return re.compile(r"\{\{\s*" + re.escape(str(key).strip()) + r"\s*\}\}")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MustacheRenderResult:
|
|
"""Ergebnis von render_mustache_template."""
|
|
|
|
text: str
|
|
keys_in_template: List[str]
|
|
keys_substituted: List[str]
|
|
keys_missing_variables: List[str]
|
|
placeholders_remaining: List[str]
|
|
|
|
|
|
def extract_mustache_keys(template: str) -> List[str]:
|
|
"""Platzhalter-Namen in Vorkommensreihenfolge, ohne erstes Duplikat."""
|
|
seen: set[str] = set()
|
|
ordered: List[str] = []
|
|
for m in _PLACEHOLDER_RE.finditer(template or ""):
|
|
k = str(m.group(1) or "").strip()
|
|
if not k or k in seen:
|
|
continue
|
|
seen.add(k)
|
|
ordered.append(k)
|
|
return ordered
|
|
|
|
|
|
def render_mustache_template(template: str, variables: Mapping[str, str]) -> MustacheRenderResult:
|
|
"""
|
|
Ersetzt {{keys}} durch die passenden Strings.
|
|
Variablen, die fuer einen im Template genutzten Key fehlen, werden als Leerstring ersetzt.
|
|
|
|
Rueckgabe-liste keys_missing_variables: Keys, die im Template vorkommen, aber nicht als Map-Keys
|
|
uebergeben wurden (oder None-Wert entsprachen Leerung).
|
|
"""
|
|
tpl_in = template or ""
|
|
vars_norm: Dict[str, str] = {}
|
|
for k, v in variables.items():
|
|
vars_norm[str(k)] = "" if v is None else str(v)
|
|
keys_in = extract_mustache_keys(tpl_in)
|
|
missing_known: List[str] = []
|
|
out = tpl_in
|
|
substituted: List[str] = []
|
|
|
|
for key in keys_in:
|
|
pat = _placeholder_pattern_for_key(key)
|
|
repl = vars_norm.get(key)
|
|
if key not in vars_norm:
|
|
missing_known.append(key)
|
|
repl = ""
|
|
substituted.append(key)
|
|
out = pat.sub(repl, out)
|
|
|
|
still = extract_mustache_keys(out)
|
|
|
|
return MustacheRenderResult(
|
|
text=out,
|
|
keys_in_template=keys_in,
|
|
keys_substituted=substituted,
|
|
keys_missing_variables=missing_known,
|
|
placeholders_remaining=still,
|
|
)
|
|
|
|
|
|
def exercise_placeholder_catalog() -> dict:
|
|
"""
|
|
Statischer Platzhalter-Katalog fuer Uebungs-KI-Templates — deckt aktuelle Seeds ab.
|
|
(Erweiterung andere Kontexte: matrix/import folgen separat.)
|
|
"""
|
|
defs = [
|
|
{
|
|
"key": "exercise_title",
|
|
"placeholder": "{{exercise_title}}",
|
|
"description": "Titel der Uebung (oder Platzhalter, wenn leer).",
|
|
"used_by_slugs": ["exercise_summary", "exercise_skill_suggestions"],
|
|
},
|
|
{
|
|
"key": "exercise_focus_area",
|
|
"placeholder": "{{exercise_focus_area}}",
|
|
"description": "Fokuskontext (Text-Hinweis aus Formular, optional).",
|
|
"used_by_slugs": ["exercise_summary", "exercise_skill_suggestions"],
|
|
},
|
|
{
|
|
"key": "exercise_goal",
|
|
"placeholder": "{{exercise_goal}}",
|
|
"description": "Ziel aus dem Formular, als Plaintext ohne HTML-Zeichen.",
|
|
"used_by_slugs": ["exercise_summary", "exercise_skill_suggestions"],
|
|
},
|
|
{
|
|
"key": "exercise_execution",
|
|
"placeholder": "{{exercise_execution}}",
|
|
"description": "Durchfuehrung als Plaintext ohne HTML-Zeichen.",
|
|
"used_by_slugs": ["exercise_summary", "exercise_skill_suggestions"],
|
|
},
|
|
{
|
|
"key": "skills_catalog",
|
|
"placeholder": "{{skills_catalog}}",
|
|
"description": (
|
|
"Gewichtete, kontextbezogene Liste aus dem Skill-Katalog (retrieval_profiles). "
|
|
"Nur fuer exercise_skill_suggestions."
|
|
),
|
|
"used_by_slugs": ["exercise_skill_suggestions"],
|
|
},
|
|
]
|
|
return {"context": "exercise", "placeholders": defs}
|
|
|
|
|
|
__all__ = [
|
|
"MustacheRenderResult",
|
|
"exercise_placeholder_catalog",
|
|
"extract_mustache_keys",
|
|
"render_mustache_template",
|
|
]
|