This commit is contained in:
team3
2026-06-23 11:20:44 +02:00
parent c07e1e9e30
commit 0eaa72c536
2 changed files with 34 additions and 22 deletions

View File

@@ -198,7 +198,8 @@ def _melde_p(set_p, topic: str, schritt: str):
# Grobe Anzeige-Phasen: bündeln die Feinschritte (intern bleibt alles feingranular).
# Sonderschritte (Quelle laden, Ergänzung) gehören zur Phase „Inventar".
PHASEN = (
("Inventar", ("Quelle aufbereiten", "Recherche", "Konsolidierung", "Klärung", "Ergänzung")),
("Quelle", ("Quelle aufbereiten",)),
("Inventar", ("Recherche", "Konsolidierung", "Klärung", "Ergänzung")),
("Subbausteine", ("Subbausteine finden", "Subbausteine wählen", "Subbausteine klären")),
("Stufen", ("Stufen finden", "Stufen wählen", "Stufen klären")),
("Relevanz", ("Relevanz finden", "Relevanz wählen", "Relevanz klären")),
@@ -330,24 +331,30 @@ def reset_bausteine(topic: str) -> None:
_bausteine_errors.pop(topic, None)
def _reset_ab_phase(topic: str, phase: int) -> None:
"""Artefakte AB der groben Phase löschen (1 Inventar 5 Fragen), frühere behalten.
Danach baut der normale Resume-Flow die fehlenden Phasen neu. Kumulativ: Re-Run ab P
löscht P…5. quelle.json + Crawl bleiben immer (Quellen-Wahl erhalten)."""
def _phase_idx(label: str) -> int:
"""Index der groben Phase in der kanonischen Reihenfolge (Quelle=0 … Fragen=5)."""
order = [l for l, _ in PHASEN]
return order.index(label) if label in order else 1
def _reset_ab_phase(topic: str, label: str) -> None:
"""Datei-Artefakte AB der groben Phase `label` löschen (Quelle/Inventar … Fragen), frühere
behalten. Kumulativ. quelle.json + Crawl (.done) bleiben immer (re-crawl nur bei Voll-Reset)."""
files = _bausteine_files(topic)
arbeit = files["arbeit"]
idx = _phase_idx(label)
def glob_del(pat: str) -> None:
if arbeit.is_dir():
for p in arbeit.glob(pat):
p.unlink(missing_ok=True)
if phase <= 5: # Fragen
if idx <= 5: # Fragen
files["frage_muster"].unlink(missing_ok=True)
glob_del("frage-muster-*")
if phase <= 4: # Relevanz
if idx <= 4: # Relevanz
glob_del("relevanz-*")
if phase <= 3: # Stufen + Relevanz teilen die Sidecar → ab Stufen ganz neu
if idx <= 3: # Stufen + Relevanz teilen die Sidecar → ab Stufen ganz neu
files["sidecar"].unlink(missing_ok=True)
glob_del("stufe-*")
else: # ab Relevanz: Stufen behalten, nur Relevanz-Felder strippen
@@ -358,10 +365,10 @@ def _reset_ab_phase(topic: str, phase: int) -> None:
if isinstance(s, dict):
s.pop("relevanz", None)
atomic_write_json(files["sidecar"], sc, indent=1)
if phase <= 2: # Subbausteine
if idx <= 2: # Subbausteine
files["sub_roh"].unlink(missing_ok=True)
glob_del("subbaustein-*")
if phase <= 1: # Inventar = kompletter Frischstart (alle Zwischendateien + bausteine.md)
if idx <= 1: # Inventar (und Quelle) = Inventar-Dateien + bausteine.md weg
for p_alt in _alle_slot_dateien(files):
p_alt.unlink(missing_ok=True)
files["final"].unlink(missing_ok=True)
@@ -1531,16 +1538,19 @@ async def _mirror_frage_muster_db(topic: str, muster: dict) -> None:
await db.upsert_frage_muster(topic, bnorm, sn, btitel, sub, typ, frage)
async def _reset_db_ab_phase(topic: str, ab_phase: int) -> None:
"""DB-Inhalt der Phasen ≥ ab_phase verwerfen (Inventar=1 … Fragen=5)."""
if ab_phase <= 1:
await db.delete_bausteine(topic)
await db.delete_coverage(topic)
await db.delete_pipeline_state(topic, ["Quelle aufbereiten", "Recherche", "Konsolidierung", "Klärung"])
if ab_phase <= 2:
await db.delete_subbausteine(topic)
if ab_phase <= 5:
async def _reset_db_ab_phase(topic: str, label: str) -> None:
"""DB-Inhalt der Phasen ≥ `label` verwerfen (kanonische Reihenfolge Quelle…Fragen)."""
idx = _phase_idx(label)
if idx <= 5: # Fragen
await db.delete_frage_muster(topic)
if idx <= 2: # Subbausteine (Stufen/Relevanz greifen über Sidecar→Mirror)
await db.delete_subbausteine(topic)
if idx <= 1: # Inventar: Inventar + Recherche-Schritte — Sichtung bleibt
await db.delete_bausteine(topic)
await db.delete_pipeline_state(topic, ["Recherche", "Konsolidierung", "Klärung"])
if idx <= 0: # Quelle: Sichtung neu (Coverage/inhalt + Schritt)
await db.delete_coverage(topic)
await db.delete_pipeline_state(topic, ["Quelle aufbereiten"])
async def generate_bausteine(topic: str, instructions: str = "", provider: str = DEFAULT_PROVIDER, ab_phase: int | None = None) -> None:
@@ -1574,8 +1584,10 @@ async def generate_bausteine(topic: str, instructions: str = "", provider: str =
# Re-Run ab gewählter Phase: Artefakte ab dort löschen; der Frischstart-Block
# unten wird übersprungen (er würde bei erhaltener Sidecar sonst alles wischen).
if ab_phase is not None:
_reset_ab_phase(topic, ab_phase)
await _reset_db_ab_phase(topic, ab_phase)
phasen = _phasen(topic)
label = phasen[ab_phase - 1][0] if 1 <= ab_phase <= len(phasen) else "Inventar"
_reset_ab_phase(topic, label)
await _reset_db_ab_phase(topic, label)
# Schritt „Quelle aufbereiten": Crawl (link) + PDFs + Content/Noise-Sichtung.
if not await _quelle_aufbereiten(ctx, set_p, files, q, ordner):
if is_cancelled():

View File

@@ -30,7 +30,7 @@ class BausteineCreateRequest(BaseModel):
provider: ProviderType = "claude"
source_type: SourceType = "thema"
source_ort: str = Field(default="", max_length=2000)
ab_phase: int | None = Field(default=None, ge=1, le=5) # Re-Run ab grober Phase (1 Inventar … 5 Fragen); None = Resume/Fortsetzen ohne Löschen
ab_phase: int | None = Field(default=None, ge=1, le=6) # Re-Run ab grober Phase (Position in _phasen(topic), 1-based); None = Resume/Fortsetzen ohne Löschen
class BausteineStep(BaseModel):