This commit is contained in:
team3
2026-06-18 20:24:24 +02:00
parent d80b22d53e
commit bf6ab18ffd
12 changed files with 176 additions and 86 deletions

View File

@@ -77,13 +77,13 @@ export async function chatBaustein({ topic, baustein, section, section_kompakt =
export async function pruefeBaustein({
topic, baustein, section, section_kompakt = '', provider,
aktion = 'frage', frage = '', letzte_bewertung = '', score_vor_frage = 0, streak = 0,
aktion = 'frage', frage = '', letzte_bewertung = '', vermeide = [],
tier2 = false, tier3 = false, messages = [], gruendlich = false,
}) {
const res = await fetch(`${BASE}/bausteine/pruefung`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ topic, baustein, section, section_kompakt, aktion, frage, letzte_bewertung, score_vor_frage, streak, tier2, tier3, messages, provider, gruendlich }),
body: JSON.stringify({ topic, baustein, section, section_kompakt, aktion, frage, letzte_bewertung, vermeide, tier2, tier3, messages, provider, gruendlich }),
})
return jsonOrThrow(res)
}

View File

@@ -53,8 +53,6 @@ const pruefMessages = slot.messages // {role, kind: 'frage'|'nachfrage'|'antwort
const pruefPhase = slot.phase
const aktuelleFrage = slot.aktuelleFrage // ankert Bewertung/Diskussion
const letztesFeedback = slot.letztesFeedback // Kontext für die Diskussion über eine Bewertung
const scoreVorFrage = slot.scoreVorFrage // Score, als die Frage gestellt wurde → driftfreies (Re-)Bewerten
const streakVorFrage = slot.streakVorFrage // Streak, als die Frage gestellt wurde (driftfrei)
// Transient (pro Instanz): Eingabe-Entwurf, Lade-Spinner.
const pruefInput = ref('')
const pruefLoading = ref(false)
@@ -107,12 +105,10 @@ async function pruefSenden(payload, onOk) {
}
}
// Frage anzeigen (frisch oder vorgemerkt). scoreVorFrage = LIVE-Score zur Anzeigezeit (driftfrei).
// Frage anzeigen (frisch oder vorgemerkt). Basis/Streak führt der Server je Frage.
function frageZeigen(text) {
aktuelleFrage.value = text
letztesFeedback.value = ''
scoreVorFrage.value = st.value.gute_antworten
streakVorFrage.value = st.value.streak
pruefMessages.value.push({ role: 'assistant', kind: 'frage', content: text })
pruefPhase.value = 'frage_offen'
}
@@ -134,26 +130,34 @@ function istBekannt(text) {
return !n || gestellteFragen().has(n) || pool.value.some((f) => normFrage(f) === n)
}
// Schon gestellte + bereits vorgemerkte Fragen — der Generator/Kritiker soll sie
// (auch sinngemäß) meiden. Original-Texte, nicht normalisiert.
function vermeideListe() {
const gestellt = pruefMessages.value.filter((m) => m.kind === 'frage').map((m) => m.content)
return [...gestellt, ...pool.value]
}
async function frageEinzeln() {
const res = await pruefeBaustein({
topic: props.topic, baustein: props.baustein, section: props.section, section_kompakt: props.sectionKompakt,
provider: props.provider, aktion: 'frage', messages: pruefDialog(),
provider: props.provider, aktion: 'frage', messages: pruefDialog(), vermeide: vermeideListe(),
})
return res.frage
}
// Pool parallel auf ZIEL auffüllen; jede Auflösung dedupt + füllt nach.
// Pool SEQUENZIELL auffüllen je eine Generierung; jede neue sieht die vorigen
// (Verlauf + Pool) in der Vermeide-Liste und divergiert. Verhindert ähnliche Dubletten.
function poolFuellen() {
while (pool.value.length + inflight.value < POOL_ZIEL && poolMiss < 6) {
inflight.value++
frageEinzeln()
.then((text) => {
if (text && !istBekannt(text)) { pool.value.push(text); poolMiss = 0 }
else poolMiss++
})
.catch(() => { poolMiss++ })
.finally(() => { inflight.value--; poolFuellen() })
}
if (inflight.value > 0) return
if (pool.value.length >= POOL_ZIEL || poolMiss >= 6) return
inflight.value++
frageEinzeln()
.then((text) => {
if (text && !istBekannt(text)) { pool.value.push(text); poolMiss = 0 }
else poolMiss++
})
.catch(() => { poolMiss++ })
.finally(() => { inflight.value--; poolFuellen() })
}
// Eine Frage aus dem Pool nehmen; ist er leer, auf die erste eintreffende warten.
@@ -222,10 +226,8 @@ function bewerten(res) {
}
function antwortPayload() {
return {
aktion: 'antwort', frage: aktuelleFrage.value, score_vor_frage: scoreVorFrage.value,
streak: streakVorFrage.value, tier2: props.tier2, tier3: props.tier3,
}
// Basis/Streak führt der Server je Frage (frage = Anker). Frontend schickt sie nicht mehr.
return { aktion: 'antwort', frage: aktuelleFrage.value, tier2: props.tier2, tier3: props.tier3 }
}
// Hintergrund: voller Bewerter+Kritiker. Persistiert serverseitig; korrigiert die Anzeige bei Abweichung.
@@ -239,8 +241,8 @@ async function verdictPruefen(gruendlich = false) {
topic: props.topic, baustein: props.baustein, section: props.section, section_kompakt: props.sectionKompakt,
provider: props.provider, messages: pruefDialog(), ...antwortPayload(), aktion: 'antwort_pruefen', gruendlich,
})
if (meine !== pruefenRun) return // eine neuere Bewertung läuft → diese verwerfen
// Streak kommt verbindlich vom Backend (persistiert) und fließt über applyPruefung in st.
// Server führt den Score (serialisiert, absolut) — IMMER übernehmen, auch wenn man
// schon bei der nächsten Frage ist. Sonst geht die Korrektur verloren.
applyPruefung(res)
if (ziel) {
if (res.bewertung !== ziel.bewertung || (res.feedback && res.feedback !== ziel.content)) {
@@ -250,7 +252,7 @@ async function verdictPruefen(gruendlich = false) {
ziel.geprueft = true
}
} catch {
if (meine === pruefenRun && ziel) ziel.geprueft = true
if (ziel) ziel.geprueft = true
} finally {
if (gruendlich && meine === pruefenRun) pruefLoading.value = false
}

View File

@@ -15,8 +15,6 @@ export function usePruefSlot(key) {
phase: ref('idle'),
aktuelleFrage: ref(''),
letztesFeedback: ref(''),
scoreVorFrage: ref(0),
streakVorFrage: ref(0),
pool: ref([]), // vorgenerierte, geprüfte, unbeantwortete Fragen
inflight: ref(0), // laufende Pool-Generierungen (instanzübergreifend koordiniert)
})