This commit is contained in:
team3
2026-07-23 16:07:36 +02:00
commit cb68cd671b
88 changed files with 10029 additions and 0 deletions

114
tests/test_buendel.py Normal file
View File

@@ -0,0 +1,114 @@
"""Bündeln: Claim mehrerer Tasks je Call, Teil-Antwort-Retry statt Verwerfen."""
import asyncio
from backend import db, engine, graph, guide
from .conftest import topic_anlegen
def _tasks_anlegen(topic, run_id, n=5):
return [db.insert("tasks", run_id=run_id, topic=topic, knoten="llm_pruefung",
item=f"p{i}", payload=db.j({"baustein_id": i}))
for i in range(n)]
def test_buendel_claim_mittel_nimmt_drei():
topic = topic_anlegen()
run_id = db.insert("runs", topic=topic, status="running")
ids = _tasks_anlegen(topic, run_id)
g = graph.get()
db.execute("UPDATE tasks SET status='laufend' WHERE id=?", ids[0])
t = dict(db.one("SELECT * FROM tasks WHERE id=?", ids[0]))
mitglieder = engine._buendel_claimen(g, t, g.knoten["llm_pruefung"])
assert len(mitglieder) == 2 # Klasse mittel = 3 → Haupt-Task + 2
laufend = db.query("SELECT id FROM tasks WHERE topic=? AND status='laufend'",
topic)
assert len(laufend) == 3
assert db.one("SELECT COUNT(*) c FROM tasks WHERE topic=? AND "
"status='offen'", topic)["c"] == 2
def test_buendel_claim_nur_fuer_optin_worker():
topic = topic_anlegen()
run_id = db.insert("runs", topic=topic, status="running")
for i in range(3): # writer: Klasse lang → nie bündeln
db.insert("tasks", run_id=run_id, topic=topic, knoten="writer",
item=f"w{i}", payload=db.j({"baustein_id": i}))
g = graph.get()
t = dict(db.one("SELECT * FROM tasks WHERE topic=? AND item='w0'", topic))
assert engine._buendel_claimen(g, t, g.knoten["writer"]) == []
def test_teil_kaputt_wird_neu_dann_fehler():
topic = topic_anlegen()
run_id = db.insert("runs", topic=topic, status="running")
ids = _tasks_anlegen(topic, run_id, n=2)
db.execute("UPDATE tasks SET status='laufend' WHERE topic=?", topic)
g = graph.get()
kn = g.knoten["llm_pruefung"]
t = dict(db.one("SELECT * FROM tasks WHERE id=?", ids[0]))
t["_buendel"] = [dict(db.one("SELECT * FROM tasks WHERE id=?", ids[1]))]
erg = engine.Ergebnis(daten={"befunde": 0},
teil_status={ids[0]: "fertig", ids[1]: "neu"})
engine._abschluss_schreiben(g, t, kn, erg)
haupt = db.one("SELECT * FROM tasks WHERE id=?", ids[0])
teil = db.one("SELECT * FROM tasks WHERE id=?", ids[1])
assert haupt["status"] == "fertig"
assert teil["status"] == "offen" and teil["versuch"] == 1
# zweiter kaputter Durchlauf → sichtbar 'fehler' (max_versuche=2)
db.execute("UPDATE tasks SET status='laufend' WHERE id=?", ids[1])
t2 = dict(db.one("SELECT * FROM tasks WHERE id=?", ids[0]))
t2["_buendel"] = [dict(db.one("SELECT * FROM tasks WHERE id=?", ids[1]))]
engine._abschluss_schreiben(g, t2, kn, engine.Ergebnis(
teil_status={ids[1]: "neu"}))
assert db.one("SELECT status FROM tasks WHERE id=?", ids[1])["status"] == "fehler"
async def test_llm_pruefung_buendel_teilantwort(monkeypatch):
"""Fehlt eine Section in der Antwort, geht NUR ihr Task auf 'neu'."""
topic = topic_anlegen()
run_id = db.insert("runs", topic=topic, status="running")
b1 = db.insert("bausteine", topic=topic, titel="B1")
b2 = db.insert("bausteine", topic=topic, titel="B2")
db.insert("sections", baustein_id=b1, text="Text eins.")
db.insert("sections", baustein_id=b2, text="Text zwei.")
t1 = db.insert("tasks", run_id=run_id, topic=topic, knoten="llm_pruefung",
item="p1", payload=db.j({"baustein_id": b1}), status="laufend")
t2 = db.insert("tasks", run_id=run_id, topic=topic, knoten="llm_pruefung",
item="p2", payload=db.j({"baustein_id": b2}), status="laufend")
from backend import llm as llm_mod
async def antwort(**kw):
assert f"=== SECTION {b2} ===" in kw["werte"]["sections"]
return f"===OK===\nSECTION: {b1}" # b2 fehlt in beiden Stimmen
monkeypatch.setattr(llm_mod, "call", antwort)
task = dict(db.one("SELECT * FROM tasks WHERE id=?", t1))
task["_buendel"] = [dict(db.one("SELECT * FROM tasks WHERE id=?", t2))]
erg = await guide.llm_pruefung(task)
assert erg.teil_status[t1] == "fertig"
assert erg.teil_status[t2] == "neu"
async def test_worker_exception_setzt_buendel_zurueck():
topic = topic_anlegen()
run_id = db.insert("runs", topic=topic, status="running")
ids = _tasks_anlegen(topic, run_id, n=2)
db.execute("UPDATE tasks SET status='laufend' WHERE topic=?", topic)
g = graph.get()
async def kaputt(task):
raise RuntimeError("boom")
alt = engine.WORKER["guide.llm_pruefung"]
engine.WORKER["guide.llm_pruefung"] = kaputt
try:
t = dict(db.one("SELECT * FROM tasks WHERE id=?", ids[0]))
t["_buendel"] = [dict(db.one("SELECT * FROM tasks WHERE id=?", ids[1]))]
await engine._ausfuehren(g, t, g.knoten["llm_pruefung"],
asyncio.Semaphore(1), asyncio.Semaphore(1),
asyncio.Event())
finally:
engine.WORKER["guide.llm_pruefung"] = alt
for tid in ids:
z = db.one("SELECT * FROM tasks WHERE id=?", tid)
assert z["status"] == "offen" and z["versuch"] == 1