- Bewertet den aktuellen Slot-Stand (3-Stufen-QS, ohne Auto-Rematch) — dieselbe Logik nach Match,
- solange keine Zuordnung geändert wurde.
+ Bewertet den aktuellen Slot-Stand (3-Stufen-QS, ohne Auto-Rematch). Nach Änderungen am Graphen
+ erscheint ein Hinweis — dann erneut „Graph bewerten“.
) : null}
+ {evaluationStale && pathQa ? (
+
+ Bewertung veraltet, neue Bewertung notwendig.
+
+ ) : null}
+
{pathQa ? (
{
+ const loadGraph = useCallback(async ({ preserveEvaluationStale = false } = {}) => {
if (!graphId) return
setBusy(true)
setLoadErr('')
@@ -153,6 +154,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
)
const findings = graph?.planning_roadmap?.last_findings
if (findings) setPathQa(findings)
+ if (!preserveEvaluationStale) setEvaluationStale(false)
} catch (e) {
setLoadErr(e.message || 'Graph konnte nicht geladen werden')
setDraft(null)
@@ -202,6 +204,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
const next = patchFn(prev)
return { ...next, dirty: true }
})
+ setEvaluationStale(true)
}, [])
const gapContextParams = useMemo(() => {
@@ -366,6 +369,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
)
return { ...structured, dirty: true }
})
+ setEvaluationStale(true)
setStartTargetReady(true)
setSemanticBrief(res?.semantic_brief_summary || null)
} catch (e) {
@@ -434,6 +438,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
})
const withPhases = syncSlotPhasesFromRoadmap(hydrated, roadmap)
setDraft({ ...withPhases, goalQuery: q, maxSteps: majorCount || withPhases.maxSteps, dirty: true })
+ setEvaluationStale(true)
setSemanticBrief(res?.semantic_brief_summary || null)
} catch (e) {
setActionErr(e.message || 'Roadmap-Generierung fehlgeschlagen')
@@ -442,14 +447,14 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
}
}
- const buildEvaluateRequest = (synced) => {
+ const buildEvaluateRequest = (synced, { llmPathQa = true, aiGapFill = true } = {}) => {
const override = majorStepsToOverridePayload(synced.slots)
return {
query: (synced.goalQuery || '').trim(),
max_steps: synced.slots.length || draft?.maxSteps || 5,
include_path_qa: true,
- include_llm_path_qa: true,
- include_ai_gap_fill: true,
+ include_llm_path_qa: llmPathQa,
+ include_ai_gap_fill: aiGapFill,
include_path_reorder: false,
include_llm_intent: false,
evaluate_only: true,
@@ -462,11 +467,13 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
}
}
- const fetchPathEvaluate = async (synced) => api.suggestProgressionPath(buildEvaluateRequest(synced))
+ const fetchPathEvaluate = async (synced, options) =>
+ api.suggestProgressionPath(buildEvaluateRequest(synced, options))
const applyEvaluateResult = (synced, res) => {
setSemanticBrief(res?.semantic_brief_summary || null)
setPathQa(res?.path_qa || null)
+ setEvaluationStale(false)
const { draft: evaluated, remainingOffers } = applyEvaluateResponseToDraft(synced, res)
return { draft: { ...evaluated, lastFindings: res?.path_qa || null }, remainingOffers }
}
@@ -632,6 +639,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
const applyOptimizeCompare = async (selectedMajorIndices) => {
if (!comparePayload || !draft) return
setCompareApplying(true)
+ setMatchNotice('Übernahme: Slots aktualisieren …')
try {
const synced = syncProgressionRoadmapFromSlots(draft)
const nextDraft = comparePayload?.unified_slot_review
@@ -642,20 +650,17 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
selectedMajorIndices,
)
const syncedNext = syncProgressionRoadmapFromSlots(nextDraft)
- const evalRes = await fetchPathEvaluate(syncedNext)
- const { draft: evaluated, remainingOffers } = applyEvaluateResult(syncedNext, evalRes)
- setDraft({ ...evaluated, dirty: true })
- const mergedOffers = mergeGapOffersForDraft(evaluated, comparePayload, evalRes)
- setGapFillOffers(mergedOffers.length > 0 ? mergedOffers : remainingOffers)
- setProposedPathQa(null)
+
+ setDraft({ ...syncedNext, dirty: false })
+ setEvaluationStale(true)
setCompareOpen(false)
setComparePayload(null)
- setMatchNotice('Ausgewählte Optimierungen übernommen — Pfad-QS neu bewertet.')
- await saveProgressionGraphDraft(api, graphId, {
- ...evaluated,
- lastFindings: evalRes?.path_qa || null,
- })
- setDraft((prev) => (prev ? { ...prev, dirty: false } : prev))
+ setProposedPathQa(null)
+
+ await saveProgressionGraphDraft(api, graphId, syncedNext)
+ setMatchNotice(
+ 'Übernommen und gespeichert. Bewertung bezieht sich noch auf den vorherigen Stand — bitte „Graph bewerten“.',
+ )
} catch (e) {
setActionErr(e.message || 'Übernahme fehlgeschlagen')
} finally {
@@ -692,7 +697,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
setActionErr('')
try {
await saveProgressionGraphDraft(api, graphId, { ...draft, lastFindings: pathQa })
- await loadGraph()
+ await loadGraph({ preserveEvaluationStale: true })
if (typeof onSaved === 'function') await onSaved()
alert('Progressionsgraph gespeichert.')
} catch (e) {
@@ -707,6 +712,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
const next = applyGapOfferToDraft(prev, offer, { slotIndex })
return { ...next, dirty: true }
})
+ setEvaluationStale(true)
setGapFillOffers((prev) => prev.filter((o) => o.offer_id !== offer?.offer_id))
}
@@ -719,6 +725,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
const next = applyGapOfferToDraft(prev, offer, { insertNewSlot: true })
return { ...next, dirty: true }
})
+ setEvaluationStale(true)
setGapFillOffers((prev) => prev.filter((o) => o.offer_id !== offer?.offer_id))
}
@@ -832,6 +839,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
if (resolvedSlot != null) {
setSlotQuickCreateIndex(resolvedSlot)
setDraft((prev) => applyGapOfferToDraft(prev, enrichedOffer, { slotIndex: resolvedSlot }))
+ setEvaluationStale(true)
}
setSlotQuickCreateDraft(aiDraft)
setGapFillOffers((prev) => prev.filter((o) => o.offer_id !== offer?.offer_id))
@@ -880,6 +888,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
const created = await api.createExercise(payload)
if (!created?.id) throw new Error('Anlegen fehlgeschlagen')
setDraft((prev) => setSlotPrimaryLibrary(prev, slotQuickCreateIndex, created))
+ setEvaluationStale(true)
setSlotQuickCreateDraft(null)
setSlotQuickCreateIndex(null)
setActiveOffer(null)
@@ -1168,6 +1177,7 @@ export default function ProgressionGraphEditor({ graphId, embedded = false, onSa
slotCount={draft.slots.length}
loading={evaluating}
error=""
+ evaluationStale={evaluationStale}
onEvaluate={runEvaluate}
onApplyGapOffer={handleApplyGapOffer}
onInsertGapSlot={handleInsertGapSlot}