Add Ground Truth regression test system for OCR pipeline
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 35s
CI / test-go-edu-search (push) Successful in 26s
CI / test-python-klausur (push) Failing after 1m47s
CI / test-python-agent-core (push) Successful in 15s
CI / test-nodejs-website (push) Successful in 22s

Extract _build_grid_core() from build_grid() endpoint for reuse.
New ocr_pipeline_regression.py with endpoints to mark sessions as
ground truth, list them, and run regression comparisons after code
changes. Frontend button in StepGroundTruth.tsx to mark/update GT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-18 13:46:48 +01:00
parent c894a0feeb
commit f655db30e4
5 changed files with 482 additions and 22 deletions

View File

@@ -43,6 +43,9 @@ export function StepGroundTruth({ sessionId, onNext }: StepGroundTruthProps) {
const [drawingRegion, setDrawingRegion] = useState(false)
const [dragStart, setDragStart] = useState<{ x: number; y: number } | null>(null)
const [dragEnd, setDragEnd] = useState<{ x: number; y: number } | null>(null)
const [isGroundTruth, setIsGroundTruth] = useState(false)
const [gtSaving, setGtSaving] = useState(false)
const [gtMessage, setGtMessage] = useState('')
const leftPanelRef = useRef<HTMLDivElement>(null)
const rightPanelRef = useRef<HTMLDivElement>(null)
@@ -86,6 +89,10 @@ export function StepGroundTruth({ sessionId, onNext }: StepGroundTruthProps) {
: `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image/original`,
})
// Check if session has ground truth reference
const gt = data.ground_truth
setIsGroundTruth(!!gt?.build_grid_reference)
// Load existing validation data
const valResp = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/reconstruction/validation`)
if (valResp.ok) {
@@ -196,6 +203,31 @@ export function StepGroundTruth({ sessionId, onNext }: StepGroundTruthProps) {
}
}
// Mark/update ground truth reference
const handleMarkGroundTruth = async () => {
if (!sessionId) return
setGtSaving(true)
setGtMessage('')
try {
const resp = await fetch(
`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/mark-ground-truth`,
{ method: 'POST' }
)
if (!resp.ok) {
const body = await resp.text().catch(() => '')
throw new Error(`Ground Truth fehlgeschlagen (${resp.status}): ${body}`)
}
const data = await resp.json()
setIsGroundTruth(true)
setGtMessage(`Ground Truth gespeichert (${data.cells_saved} Zellen)`)
setTimeout(() => setGtMessage(''), 5000)
} catch (e) {
setGtMessage(e instanceof Error ? e.message : String(e))
} finally {
setGtSaving(false)
}
}
// Handle manual region drawing on reconstruction
const handleReconMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
if (!drawingRegion) return
@@ -570,8 +602,20 @@ export function StepGroundTruth({ sessionId, onNext }: StepGroundTruthProps) {
<div className="text-sm text-gray-500 dark:text-gray-400">
{status === 'saved' && <span className="text-green-600 dark:text-green-400">Validierung gespeichert</span>}
{status === 'saving' && <span>Speichere...</span>}
{gtMessage && (
<span className={gtMessage.includes('fehlgeschlagen') ? 'text-red-500' : 'text-amber-600 dark:text-amber-400'}>
{gtMessage}
</span>
)}
</div>
<div className="flex items-center gap-3">
<button
onClick={handleMarkGroundTruth}
disabled={gtSaving || status === 'saving'}
className="px-4 py-2 text-sm bg-amber-600 text-white rounded hover:bg-amber-700 disabled:opacity-50"
>
{gtSaving ? 'Speichere...' : isGroundTruth ? 'Ground Truth aktualisieren' : 'Als Ground Truth markieren'}
</button>
<button
onClick={handleSave}
disabled={status === 'saving'}