update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { chatBaustein, pruefeBaustein } from '../api.js'
|
||||
import { chatBaustein, pruefeBaustein, fetchFrageMuster } from '../api.js'
|
||||
import { usePruefSlot } from '../pruefungCache.js'
|
||||
import { renderMarkdown } from '../markdown.js'
|
||||
import { useChat, istUnten } from '../composables/useChat.js'
|
||||
@@ -53,6 +53,9 @@ 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 musterQuelle = slot.musterQuelle // unveränderliche Voll-Liste der Frage-Muster (leer = Fallback)
|
||||
const musterPool = slot.musterPool // Arbeitskopie, ohne Zurücklegen; leer → Reset
|
||||
const musterGeladen = slot.musterGeladen
|
||||
// Transient (pro Instanz): Eingabe-Entwurf, Lade-Spinner.
|
||||
const pruefInput = ref('')
|
||||
const pruefLoading = ref(false)
|
||||
@@ -124,36 +127,76 @@ function gestellteFragen() {
|
||||
return new Set(pruefMessages.value.filter((m) => m.kind === 'frage').map((m) => normFrage(m.content)))
|
||||
}
|
||||
|
||||
// --- Fragen-Pool: ZIEL vorgenerierte, geprüfte, unbeantwortete Fragen je Baustein (im Slot) ---
|
||||
// --- Fragen-Pool: ZIEL vorformulierte, unbeantwortete Fragen je Baustein (im Slot) ---
|
||||
const POOL_ZIEL = 5
|
||||
const pool = slot.pool
|
||||
const inflight = slot.inflight
|
||||
let poolMiss = 0 // aufeinanderfolgende Dubletten/Fehler — Cap gegen Endlosschleife (kleine Bausteine)
|
||||
let poolMiss = 0 // nur Fallback: aufeinanderfolgende Dubletten/Fehler — Cap gegen Endlosschleife
|
||||
|
||||
// Muster-Modus aktiv, sobald geladen und Muster vorhanden; sonst Fallback auf Live-Generierung.
|
||||
const musterModus = computed(() => musterGeladen.value && musterQuelle.value.length > 0)
|
||||
|
||||
function istBekannt(text) {
|
||||
const n = normFrage(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.
|
||||
// Schon gestellte + bereits vorgemerkte Fragen — der Generator soll sie (auch
|
||||
// sinngemäß) meiden. Nur Fallback-Modus. Original-Texte, nicht normalisiert.
|
||||
function vermeideListe() {
|
||||
const gestellt = pruefMessages.value.filter((m) => m.kind === 'frage').map((m) => m.content)
|
||||
return [...gestellt, ...pool.value]
|
||||
}
|
||||
|
||||
// Muster-Sidecar einmal je Baustein laden (Slot-cached). Leer → Fallback.
|
||||
async function musterLaden() {
|
||||
if (musterGeladen.value) return
|
||||
try {
|
||||
const res = await fetchFrageMuster(props.topic, props.baustein)
|
||||
musterQuelle.value = (res.muster || []).map((m) => (m.frage || '').trim()).filter(Boolean)
|
||||
} catch {
|
||||
musterQuelle.value = []
|
||||
}
|
||||
musterGeladen.value = true
|
||||
}
|
||||
|
||||
function mischen(arr) { // Fisher-Yates
|
||||
const a = [...arr]
|
||||
for (let i = a.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1))
|
||||
;[a[i], a[j]] = [a[j], a[i]]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Ein Muster ohne Zurücklegen ziehen; Pool leer → Reset (neu mischen). null = Fallback.
|
||||
function musterNehmen() {
|
||||
if (!musterQuelle.value.length) return null
|
||||
if (!musterPool.value.length) musterPool.value = mischen(musterQuelle.value)
|
||||
return musterPool.value.shift()
|
||||
}
|
||||
|
||||
function frageEinzeln() {
|
||||
// Ein Agent → eine Frage (Backend wählt zufälligen Typ + Subbaustein, dann Kritiker).
|
||||
const muster = musterNehmen()
|
||||
if (muster) {
|
||||
// Muster-Modus: aus gezogener Saat eine konkrete, leicht andere Frage formulieren.
|
||||
return pruefeBaustein({
|
||||
topic: props.topic, baustein: props.baustein, section: props.section, section_kompakt: props.sectionKompakt,
|
||||
provider: props.provider, aktion: 'frage', muster,
|
||||
}).then((res) => res.frage)
|
||||
}
|
||||
// Fallback (kein Muster-Sidecar): Live-Generierung mit Vermeide-Liste.
|
||||
return pruefeBaustein({
|
||||
topic: props.topic, baustein: props.baustein, section: props.section, section_kompakt: props.sectionKompakt,
|
||||
provider: props.provider, aktion: 'frage', messages: pruefDialog(), vermeide: vermeideListe(),
|
||||
}).then((res) => res.frage)
|
||||
}
|
||||
|
||||
// Pool PARALLEL auffüllen: pro fehlender Frage ein eigener Agent gleichzeitig.
|
||||
// Initial 5 parallel; nach Verbrauch genau die fehlende(n) nach.
|
||||
// Pool PARALLEL auffüllen: pro fehlender Frage ein eigener Agent. Im Muster-Modus
|
||||
// zieht jeder ein DISTINKTES Muster (ohne Zurücklegen) → keine Kollision, kein Miss-Cap.
|
||||
function poolFuellen() {
|
||||
while (pool.value.length + inflight.value < POOL_ZIEL && poolMiss < 3) {
|
||||
const cap = musterModus.value ? Infinity : 3
|
||||
while (pool.value.length + inflight.value < POOL_ZIEL && poolMiss < cap) {
|
||||
inflight.value++
|
||||
frageEinzeln()
|
||||
.then((text) => {
|
||||
@@ -168,9 +211,10 @@ function poolFuellen() {
|
||||
// Eine Frage aus dem Pool nehmen; ist er leer, auf die erste eintreffende warten.
|
||||
async function poolNehmen() {
|
||||
if (!pool.value.length) {
|
||||
const cap = musterModus.value ? Infinity : 3
|
||||
pruefLoading.value = true
|
||||
try {
|
||||
while (!pool.value.length && (inflight.value > 0 || poolMiss < 3)) {
|
||||
while (!pool.value.length && (inflight.value > 0 || poolMiss < cap)) {
|
||||
if (!inflight.value) poolFuellen()
|
||||
await new Promise((r) => setTimeout(r, 120))
|
||||
}
|
||||
@@ -179,10 +223,11 @@ async function poolNehmen() {
|
||||
return pool.value.length ? pool.value.shift() : null
|
||||
}
|
||||
|
||||
// Erste Frage (Phase idle): ZIEL parallel anstoßen, schnellste zuerst zeigen.
|
||||
// Erste Frage (Phase idle): Muster laden, ZIEL parallel anstoßen, schnellste zuerst zeigen.
|
||||
async function frageAnfordern() {
|
||||
if (pruefLoading.value) return
|
||||
poolMiss = 0
|
||||
await musterLaden()
|
||||
poolFuellen()
|
||||
const text = await poolNehmen()
|
||||
if (text) {
|
||||
@@ -197,6 +242,7 @@ async function frageAnfordern() {
|
||||
// „Nächste Frage": sofort aus dem Pool, dann nachfüllen.
|
||||
async function naechsteFrageZeigen() {
|
||||
if (pruefLoading.value) return
|
||||
await musterLaden()
|
||||
const text = await poolNehmen()
|
||||
if (text) {
|
||||
frageZeigen(text)
|
||||
|
||||
Reference in New Issue
Block a user