update
This commit is contained in:
@@ -13,7 +13,8 @@ from database import (
|
||||
create_topic, list_topics as db_list_topics, delete_topic,
|
||||
list_progress, set_progress, delete_progress,
|
||||
create_element, list_elements, get_element, update_element, delete_element,
|
||||
list_baustein_progress, set_baustein_score, set_baustein_streak, set_baustein_absolviert, set_baustein_verstanden, set_baustein_gemeistert, delete_baustein_daten,
|
||||
list_baustein_progress, get_baustein_progress, set_offene_frage,
|
||||
set_baustein_score, set_baustein_streak, set_baustein_absolviert, set_baustein_verstanden, set_baustein_gemeistert, delete_baustein_daten,
|
||||
)
|
||||
from bausteine import generate_bausteine, cancel_bausteine, bausteine_status, active_bausteine, reset_bausteine, lade_quelle, lade_uebersicht
|
||||
from elements import generate_element, chat_with_guide, chat_with_element, check_element, style_element, refine_suggestion
|
||||
@@ -233,12 +234,32 @@ async def baustein_chat_route(req: BausteinChatRequest):
|
||||
return {"reply": reply}
|
||||
|
||||
|
||||
# Bewertungen je (topic, baustein) serialisieren — sonst überschreiben zwei
|
||||
# gleichzeitige Bewertungen den absoluten Score mit veralteter Basis (Race).
|
||||
_pruef_locks: dict[tuple[str, str], asyncio.Lock] = {}
|
||||
|
||||
|
||||
def _pruef_lock(topic: str, baustein: str) -> asyncio.Lock:
|
||||
key = (topic, baustein)
|
||||
lock = _pruef_locks.get(key)
|
||||
if lock is None:
|
||||
lock = _pruef_locks[key] = asyncio.Lock()
|
||||
return lock
|
||||
|
||||
|
||||
def _basis(stand: dict, frage: str) -> tuple[int, int, bool]:
|
||||
"""Score-/Streak-Basis VOR der Frage. Gleiche offene Frage → Re-Bewertung auf derselben
|
||||
Basis (idempotent); sonst neue Frage auf dem aktuellen Stand. → (basis, streak_basis, re_bewertung)."""
|
||||
re_bewertung = stand["offene_frage"] == frage and stand["offene_basis"] is not None
|
||||
if re_bewertung:
|
||||
streak_basis = stand["offene_streak"] if stand["offene_streak"] is not None else stand["streak"]
|
||||
return stand["offene_basis"], streak_basis, True
|
||||
return stand["gute_antworten"], stand["streak"], False
|
||||
|
||||
|
||||
@router.post("/bausteine/pruefung", response_model=BausteinPruefungResponse)
|
||||
async def baustein_pruefung_route(req: BausteinPruefungRequest):
|
||||
stand = next(
|
||||
(p for p in await list_baustein_progress(req.topic) if p["baustein"] == req.baustein),
|
||||
{"gute_antworten": 0, "absolviert": None, "verstanden": None, "gemeistert": None},
|
||||
)
|
||||
stand = await get_baustein_progress(req.topic, req.baustein)
|
||||
gute = stand["gute_antworten"]
|
||||
absolviert = stand["absolviert"] is not None
|
||||
verstanden = stand["verstanden"] is not None
|
||||
@@ -247,7 +268,7 @@ async def baustein_pruefung_route(req: BausteinPruefungRequest):
|
||||
msgs = [m.model_dump() for m in req.messages]
|
||||
|
||||
if req.aktion == "frage":
|
||||
frage = await pruefung_frage(req.topic, req.baustein, req.section, kompakt, msgs, provider=req.provider)
|
||||
frage = await pruefung_frage(req.topic, req.baustein, req.section, kompakt, msgs, vermeide=req.vermeide, provider=req.provider)
|
||||
if frage is None:
|
||||
raise HTTPException(502, "Frage fehlgeschlagen — bitte erneut versuchen")
|
||||
return {"frage": frage, "gute_antworten": gute, "absolviert": absolviert, "verstanden": verstanden, "gemeistert": gemeistert}
|
||||
@@ -271,47 +292,58 @@ async def baustein_pruefung_route(req: BausteinPruefungRequest):
|
||||
raise HTTPException(400, "Antwort braucht eine laufende Frage")
|
||||
|
||||
if req.aktion == "antwort":
|
||||
# Vorschau: nur Bewerter, kein Kritiker, KEIN Persist, KEINE Meilensteine.
|
||||
# Vorschau: nur Bewerter, kein Kritiker, KEIN Persist. Basis serverseitig (best effort).
|
||||
data = await pruefung_bewertung_schnell(
|
||||
req.topic, req.baustein, req.section, kompakt, req.frage, msgs, gute, provider=req.provider,
|
||||
)
|
||||
if data is None:
|
||||
raise HTTPException(502, "Bewertung fehlgeschlagen — bitte erneut versuchen")
|
||||
score = score_berechnen(req.score_vor_frage, data["bewertung"] == "gut", req.streak, req.tier2, req.tier3, absolviert, verstanden, gemeistert)
|
||||
basis, streak_basis, _ = _basis(stand, req.frage)
|
||||
score = score_berechnen(basis, data["bewertung"] == "gut", streak_basis, req.tier2, req.tier3, absolviert, verstanden, gemeistert)
|
||||
return {"feedback": data["feedback"], "bewertung": data["bewertung"], "gute_antworten": score,
|
||||
"absolviert": absolviert, "verstanden": verstanden, "gemeistert": gemeistert}
|
||||
|
||||
# aktion == "antwort_pruefen": verbindlich (Bewerter + Kritiker), persistiert Score + Meilensteine.
|
||||
data = await pruefung_bewertung(
|
||||
req.topic, req.baustein, req.section, kompakt, req.frage, msgs, gute, provider=req.provider,
|
||||
role="guide" if req.gruendlich else "judge",
|
||||
)
|
||||
if data is None:
|
||||
raise HTTPException(502, "Bewertung fehlgeschlagen — bitte erneut versuchen")
|
||||
# aktion == "antwort_pruefen": verbindlich, persistiert. Serialisiert je Baustein, damit
|
||||
# die Basis immer den geprüften Vorgänger-Stand sieht (kein Stale-Overwrite).
|
||||
async with _pruef_lock(req.topic, req.baustein):
|
||||
stand = await get_baustein_progress(req.topic, req.baustein) # nach evtl. Vorgänger-Persist neu lesen
|
||||
absolviert = stand["absolviert"] is not None
|
||||
verstanden = stand["verstanden"] is not None
|
||||
gemeistert = stand["gemeistert"] is not None
|
||||
basis, streak_basis, re_bewertung = _basis(stand, req.frage)
|
||||
if not re_bewertung:
|
||||
# Neue Frage: Basis/Streak einfrieren (Anker für spätere Re-Bewertung dieser Frage).
|
||||
await set_offene_frage(req.topic, req.baustein, req.frage, basis, streak_basis)
|
||||
|
||||
# Score driftfrei aus dem Basis-Score rechnen (Re-Bewertung ersetzt das vorige Ergebnis).
|
||||
score = score_berechnen(
|
||||
req.score_vor_frage, data["bewertung"] == "gut", req.streak, req.tier2, req.tier3, absolviert, verstanden, gemeistert,
|
||||
)
|
||||
war_absolviert, war_verstanden = absolviert, verstanden
|
||||
gute = await set_baustein_score(req.topic, req.baustein, score)
|
||||
if score >= NOETIG and not absolviert:
|
||||
absolviert = True
|
||||
if await set_baustein_absolviert(req.topic, req.baustein):
|
||||
asyncio.create_task(baustein_element_anlegen(req.topic, req.baustein, req.section, req.provider))
|
||||
if score >= MASTERY and not verstanden:
|
||||
await set_baustein_verstanden(req.topic, req.baustein)
|
||||
verstanden = True
|
||||
if score >= MEISTERN and not gemeistert:
|
||||
await set_baustein_gemeistert(req.topic, req.baustein)
|
||||
gemeistert = True
|
||||
# Streak persistieren — Reset nur am Deckel der gerade aktiven Stufe.
|
||||
streak = streak_berechnen(
|
||||
req.streak, data["bewertung"] == "gut", req.tier2, req.tier3,
|
||||
absolviert and not war_absolviert, verstanden and not war_verstanden,
|
||||
)
|
||||
await set_baustein_streak(req.topic, req.baustein, streak)
|
||||
return {"feedback": data["feedback"], "bewertung": data["bewertung"], "gute_antworten": gute, "streak": streak, "absolviert": absolviert, "verstanden": verstanden, "gemeistert": gemeistert}
|
||||
data = await pruefung_bewertung(
|
||||
req.topic, req.baustein, req.section, kompakt, req.frage, msgs, basis, provider=req.provider,
|
||||
role="guide" if req.gruendlich else "judge",
|
||||
)
|
||||
if data is None:
|
||||
raise HTTPException(502, "Bewertung fehlgeschlagen — bitte erneut versuchen")
|
||||
|
||||
score = score_berechnen(
|
||||
basis, data["bewertung"] == "gut", streak_basis, req.tier2, req.tier3, absolviert, verstanden, gemeistert,
|
||||
)
|
||||
war_absolviert, war_verstanden = absolviert, verstanden
|
||||
gute = await set_baustein_score(req.topic, req.baustein, score)
|
||||
if score >= NOETIG and not absolviert:
|
||||
absolviert = True
|
||||
if await set_baustein_absolviert(req.topic, req.baustein):
|
||||
asyncio.create_task(baustein_element_anlegen(req.topic, req.baustein, req.section, req.provider))
|
||||
if score >= MASTERY and not verstanden:
|
||||
await set_baustein_verstanden(req.topic, req.baustein)
|
||||
verstanden = True
|
||||
if score >= MEISTERN and not gemeistert:
|
||||
await set_baustein_gemeistert(req.topic, req.baustein)
|
||||
gemeistert = True
|
||||
# Streak persistieren — Reset nur am Deckel der gerade aktiven Stufe.
|
||||
streak = streak_berechnen(
|
||||
streak_basis, data["bewertung"] == "gut", req.tier2, req.tier3,
|
||||
absolviert and not war_absolviert, verstanden and not war_verstanden,
|
||||
)
|
||||
await set_baustein_streak(req.topic, req.baustein, streak)
|
||||
return {"feedback": data["feedback"], "bewertung": data["bewertung"], "gute_antworten": gute, "streak": streak, "absolviert": absolviert, "verstanden": verstanden, "gemeistert": gemeistert}
|
||||
|
||||
|
||||
# --- Guides ---
|
||||
|
||||
Reference in New Issue
Block a user