update
This commit is contained in:
@@ -719,9 +719,11 @@ async def _generate_onepager(
|
|||||||
if project:
|
if project:
|
||||||
source = _prompt("OnePager-Quelle-Projekt", project=project)
|
source = _prompt("OnePager-Quelle-Projekt", project=project)
|
||||||
recherche_template = "OnePager-Recherche-Projekt"
|
recherche_template = "OnePager-Recherche-Projekt"
|
||||||
|
recherche_check_template = "OnePager-Recherche-Check-Projekt"
|
||||||
else:
|
else:
|
||||||
source = _prompt("OnePager-Quelle-Thema", topic=topic)
|
source = _prompt("OnePager-Quelle-Thema", topic=topic)
|
||||||
recherche_template = "OnePager-Recherche"
|
recherche_template = "OnePager-Recherche"
|
||||||
|
recherche_check_template = "OnePager-Recherche-Check"
|
||||||
|
|
||||||
def recherche_payload(result=None):
|
def recherche_payload(result=None):
|
||||||
if not recherche_path.exists():
|
if not recherche_path.exists():
|
||||||
@@ -752,7 +754,7 @@ async def _generate_onepager(
|
|||||||
await _set_step(guide_id, 1, "Prüfe Recherche…")
|
await _set_step(guide_id, 1, "Prüfe Recherche…")
|
||||||
slots = [{
|
slots = [{
|
||||||
"key": f"{guide_id}-recherche-check",
|
"key": f"{guide_id}-recherche-check",
|
||||||
"prompt": _prompt("OnePager-Recherche-Check", topic=topic, recherche=recherche, out_path=recherche_check_path),
|
"prompt": _prompt(recherche_check_template, topic=topic, recherche=recherche, out_path=recherche_check_path),
|
||||||
"role": "fast", "capabilities": "files",
|
"role": "fast", "capabilities": "files",
|
||||||
"payload": (lambda result: _probleme_schema(_json_datei(recherche_check_path))),
|
"payload": (lambda result: _probleme_schema(_json_datei(recherche_check_path))),
|
||||||
}]
|
}]
|
||||||
|
|||||||
@@ -46,17 +46,16 @@ async def get_topics():
|
|||||||
return db_topics + sorted(derived - set(db_topics))
|
return db_topics + sorted(derived - set(db_topics))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/stats")
|
# Lernschulden-Regel: höchstens 5 erstellte, aber nicht absolvierte Guides.
|
||||||
async def get_stats():
|
# Darüber sind nur Neu-Generierungen bereits erstellter Guides erlaubt.
|
||||||
"""Tracker: Themen-Anzahl + pro Format erstellt/absolviert (alle Kapitel abgehakt)."""
|
MAX_OFFENE_GUIDES = 5
|
||||||
guides = await list_guides()
|
|
||||||
themen = set(await db_list_topics()) | {g["topic"] for g in guides} | set(bausteine_topics())
|
|
||||||
if PROJECTS_DIR.is_dir():
|
|
||||||
themen |= {e.name for e in PROJECTS_DIR.iterdir() if e.is_dir()}
|
|
||||||
|
|
||||||
|
|
||||||
|
async def _formate_stats() -> dict:
|
||||||
|
"""Pro Format erstellt/absolviert (alle Kapitel abgehakt) — pro Thema zählt nur der neueste fertige Guide."""
|
||||||
|
guides = await list_guides()
|
||||||
formate = {}
|
formate = {}
|
||||||
for fmt in ("MiniGuide", "Guide", "FullGuide"):
|
for fmt in ("MiniGuide", "Guide", "FullGuide"):
|
||||||
# Pro Thema zählt nur der neueste fertige Guide (Altläufe teilen die Content-Datei)
|
|
||||||
neueste: dict[str, dict] = {}
|
neueste: dict[str, dict] = {}
|
||||||
for g in guides:
|
for g in guides:
|
||||||
if g["format"] == fmt and g["status"] == "done":
|
if g["format"] == fmt and g["status"] == "done":
|
||||||
@@ -75,7 +74,17 @@ async def get_stats():
|
|||||||
if titles and titles <= set(await list_progress(g["id"])):
|
if titles and titles <= set(await list_progress(g["id"])):
|
||||||
absolviert += 1
|
absolviert += 1
|
||||||
formate[fmt] = {"erstellt": len(neueste), "absolviert": absolviert}
|
formate[fmt] = {"erstellt": len(neueste), "absolviert": absolviert}
|
||||||
return {"themen": len(themen), "formate": formate}
|
return formate
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/stats")
|
||||||
|
async def get_stats():
|
||||||
|
"""Tracker: Themen-Anzahl + pro Format erstellt/absolviert."""
|
||||||
|
guides = await list_guides()
|
||||||
|
themen = set(await db_list_topics()) | {g["topic"] for g in guides} | set(bausteine_topics())
|
||||||
|
if PROJECTS_DIR.is_dir():
|
||||||
|
themen |= {e.name for e in PROJECTS_DIR.iterdir() if e.is_dir()}
|
||||||
|
return {"themen": len(themen), "formate": await _formate_stats()}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/topics")
|
@router.post("/topics")
|
||||||
@@ -159,6 +168,12 @@ async def create(req: GuideCreateRequest):
|
|||||||
for g in await list_guides():
|
for g in await list_guides():
|
||||||
if g["topic"] == req.topic.strip() and g["format"] == req.format and g["status"] in ("queued", "generating"):
|
if g["topic"] == req.topic.strip() and g["format"] == req.format and g["status"] in ("queued", "generating"):
|
||||||
raise HTTPException(409, "Generierung läuft bereits")
|
raise HTTPException(409, "Generierung läuft bereits")
|
||||||
|
# Lernschulden-Regel: neue Guides nur, wenn weniger als 5 offene (erstellt, nicht absolviert)
|
||||||
|
if req.format != "OnePager" and not guide_content_path(req.topic.strip(), req.format).exists():
|
||||||
|
formate = await _formate_stats()
|
||||||
|
offen = sum(v["erstellt"] - v["absolviert"] for v in formate.values())
|
||||||
|
if offen >= MAX_OFFENE_GUIDES:
|
||||||
|
raise HTTPException(409, f"Erst Guides absolvieren — maximal {MAX_OFFENE_GUIDES} offene erlaubt ({offen} offen)")
|
||||||
await create_topic(req.topic.strip())
|
await create_topic(req.topic.strip())
|
||||||
now = datetime.now(timezone.utc).isoformat()
|
now = datetime.now(timezone.utc).isoformat()
|
||||||
guide = {
|
guide = {
|
||||||
|
|||||||
@@ -128,7 +128,26 @@ function handleFormatClick(format) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lernschulden-Regel: max. 5 erstellte, aber nicht absolvierte Guides —
|
||||||
|
// darüber sind nur Neu-Generierungen bereits erstellter Guides erlaubt.
|
||||||
|
const offeneGuides = computed(() => {
|
||||||
|
const f = props.stats?.formate || {}
|
||||||
|
return ['MiniGuide', 'Guide', 'FullGuide'].reduce(
|
||||||
|
(sum, k) => sum + ((f[k]?.erstellt ?? 0) - (f[k]?.absolviert ?? 0)), 0,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
function playLock(format) {
|
||||||
|
if (format === 'OnePager') return null
|
||||||
|
if (!props.bausteine.ready) return 'Erst Bausteine erstellen'
|
||||||
|
if (offeneGuides.value >= 5 && !props.doneByFormat[format]) {
|
||||||
|
return `Erst Guides absolvieren — ${offeneGuides.value} offen (max. 5)`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
function handlePlay(format) {
|
function handlePlay(format) {
|
||||||
|
if (playLock(format)) return
|
||||||
emit('formatClick', { format, instructions: '' })
|
emit('formatClick', { format, instructions: '' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,8 +298,8 @@ function confirmDeleteProject(name) {
|
|||||||
<template v-if="guideStatus(f.key) !== 'generating' && guideStatus(f.key) !== 'queued'">
|
<template v-if="guideStatus(f.key) !== 'generating' && guideStatus(f.key) !== 'queued'">
|
||||||
<button
|
<button
|
||||||
class="action-btn play"
|
class="action-btn play"
|
||||||
:title="f.key === 'OnePager' || bausteine.ready ? 'Generieren' : 'Erst Bausteine erstellen'"
|
:title="playLock(f.key) || 'Generieren'"
|
||||||
:disabled="f.key !== 'OnePager' && !bausteine.ready"
|
:disabled="!!playLock(f.key)"
|
||||||
@click="handlePlay(f.key)"
|
@click="handlePlay(f.key)"
|
||||||
>▶</button>
|
>▶</button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
27
templates/Prompt/OnePager-Recherche-Check-Projekt.md
Normal file
27
templates/Prompt/OnePager-Recherche-Check-Projekt.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Prüfe die Faktenbasis für einen OnePager zum Projekt "{topic}".
|
||||||
|
|
||||||
|
FAKTENBASIS:
|
||||||
|
{recherche}
|
||||||
|
|
||||||
|
Sie muss diese Dimensionen abdecken:
|
||||||
|
1. Kurzbeschreibung (Art des Projekts, Gegenstand)
|
||||||
|
2. Technische Daten (Technologie/Format, Umfang, Stand/Aktualität)
|
||||||
|
3. Kerneigenschaften (prägende Konzepte, Komponenten oder Inhalte)
|
||||||
|
4. Ein typisches Beispiel aus dem Projekt
|
||||||
|
5. Zusammenhänge (Umfeld, Abhängigkeiten, angrenzende Systeme/Themen)
|
||||||
|
6. Voraussetzungen
|
||||||
|
7. Moderne vs. veraltete Teile (oder die ausdrückliche Feststellung, dass nichts veraltet ist)
|
||||||
|
|
||||||
|
Prüfe:
|
||||||
|
1. Ist jede Dimension mit konkreten Fakten aus den Projektdateien belegt (Namen, Zahlen — nicht vage)?
|
||||||
|
2. Hat jeder Punkt einen Dateipfad als Quelle?
|
||||||
|
3. Wirkt etwas erfunden — also nicht aus dem Projekt belegbar?
|
||||||
|
|
||||||
|
Du PRÜFST nur und notierst Probleme — du änderst nichts.
|
||||||
|
|
||||||
|
Schreibe NUR die JSON-Datei nach: {out_path}
|
||||||
|
|
||||||
|
Format — alles in Ordnung:
|
||||||
|
{{"ok": true}}
|
||||||
|
Sonst (kurz und konkret, maximal 10 Punkte):
|
||||||
|
{{"probleme": ["…", "…"]}}
|
||||||
@@ -8,7 +8,7 @@ BISHERIGE FAKTENBASIS:
|
|||||||
NOTIERTE PROBLEME (von der Prüfung):
|
NOTIERTE PROBLEME (von der Prüfung):
|
||||||
{probleme}
|
{probleme}
|
||||||
|
|
||||||
Behebe NUR die notierten Probleme — fehlende Dimensionen nachrecherchieren, Vages konkretisieren, Unbelegtes belegen oder streichen. Alles andere bleibt erhalten.
|
Behebe NUR die notierten Probleme — Fehlendes anhand der oben genannten Quelle ergänzen, Vages konkretisieren, Unbelegtes belegen oder streichen. Alles andere bleibt erhalten.
|
||||||
|
|
||||||
Schreibe die VOLLSTÄNDIGE, überarbeitete Markdown-Datei nach: {out_path}
|
Schreibe die VOLLSTÄNDIGE, überarbeitete Markdown-Datei nach: {out_path}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user