'use client'
/**
* Test Quality Dashboard - BQAS (Breakpilot Quality Assurance System)
*
* Umfassendes Qualitaetssicherungs-Dashboard mit:
* - Golden Test Suite Ergebnisse
* - Synthetic Test Generierung
* - Regression Tracking
* - Test Run Historie
* - RAG Correction Tests
*/
import { useState, useEffect, useCallback, useRef } from 'react'
import Link from 'next/link'
import { PagePurpose } from '@/components/common/PagePurpose'
import { AIToolsSidebarResponsive } from '@/components/ai/AIToolsSidebar'
import type { TestRun, BQASMetrics, TrendData, TabType } from './types'
// API Configuration - Use internal proxy to avoid CORS issues
const BQAS_API_BASE = '/api/bqas'
// ============================================================================
// Toast Notification Component
// ============================================================================
interface Toast {
id: number
type: 'success' | 'error' | 'info' | 'loading'
message: string
}
function ToastContainer({ toasts, onDismiss }: { toasts: Toast[]; onDismiss: (id: number) => void }) {
return (
{isRunning ? (
Laeuft...
) : (
'Tests starten'
)}
{metrics && (
Pass Rate
{passRate.toFixed(1)}%
= 80 ? 'bg-emerald-500' : passRate >= 60 ? 'bg-amber-500' : 'bg-red-500'
}`}
style={{ width: `${passRate}%` }}
/>
{metrics.total_tests}
Tests
{metrics.passed_tests}
Bestanden
{metrics.failed_tests}
Fehlgeschlagen
Durchschnittlicher Score: {metrics.avg_composite_score.toFixed(2)}
)}
{lastRun && (
Letzter Lauf: {new Date(lastRun).toLocaleString('de-DE')}
)}
)
}
function TrendChart({ data }: { data: TrendData }) {
if (!data || data.dates.length === 0) {
return (
Keine Trend-Daten verfuegbar
)
}
const maxScore = Math.max(...data.scores, 5)
const minScore = Math.min(...data.scores, 0)
const range = maxScore - minScore || 1
return (
{maxScore.toFixed(1)}
{((maxScore + minScore) / 2).toFixed(1)}
{minScore.toFixed(1)}
{
const x = (i / (data.scores.length - 1 || 1)) * 100
const y = 100 - ((score - minScore) / range) * 100
return `${x},${y}`
})
.join(' ')}
/>
{data.scores.map((score, i) => {
const x = (i / (data.scores.length - 1 || 1)) * 100
const y = 100 - ((score - minScore) / range) * 100
return
})}
{data.dates.slice(0, 5).map((date, i) => (
{new Date(date).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' })}
))}
{data.trend === 'improving' ? 'Verbessernd' : data.trend === 'declining' ? 'Verschlechternd' : 'Stabil'}
)
}
function TestRunsTable({ runs }: { runs: TestRun[] }) {
if (runs.length === 0) {
return (
Keine Test-Laeufe vorhanden
)
}
return (
ID
Zeitpunkt
Commit
Golden Score
Tests
Bestanden
Dauer
{runs.map((run) => (
#{run.id}
{new Date(run.timestamp).toLocaleString('de-DE')}
{run.git_commit?.slice(0, 7) || '-'}
= 4 ? 'text-emerald-600' : run.golden_score >= 3 ? 'text-amber-600' : 'text-red-600'
}`}
>
{run.golden_score.toFixed(2)}
{run.total_tests}
{run.passed_tests}
/
{run.failed_tests}
{run.duration_seconds.toFixed(1)}s
))}
)
}
function IntentScoresChart({ scores }: { scores: Record
}) {
const entries = Object.entries(scores).sort((a, b) => b[1] - a[1])
if (entries.length === 0) {
return (
Keine Intent-Scores verfuegbar
)
}
return (
{entries.map(([intent, score]) => (
{intent.replace(/_/g, ' ')}
= 4 ? 'text-emerald-600' : score >= 3 ? 'text-amber-600' : 'text-red-600'
}`}
>
{score.toFixed(2)}
= 4 ? 'bg-emerald-500' : score >= 3 ? 'bg-amber-500' : 'bg-red-500'
}`}
style={{ width: `${(score / 5) * 100}%` }}
/>
))}
)
}
// Demo failed test details for illustration
const FAILED_TEST_DETAILS: Record
= {
// Golden Suite - Intent Tests
'INT-001': {
description: 'Student Observation - Simple',
cause: 'Notiz zu Max wird nicht korrekt als student_observation erkannt',
action: 'Training-Daten fuer kurze Notiz-Befehle erweitern',
},
'INT-002': {
description: 'Student Observation - Needs Help',
cause: 'Anfrage "Anna braucht extra Uebungsblatt" wird falsch klassifiziert',
action: 'Intent-Erkennung fuer Hilfe-Anfragen verbessern',
},
'INT-003': {
description: 'Reminder - Simple',
cause: 'Erinnerungs-Intent nicht erkannt',
action: 'Trigger-Woerter fuer Erinnerungen pruefen',
},
'INT-010': {
description: 'Quick Activity - With Time',
cause: '"10 Minuten Einstieg" wird nicht als quick_activity erkannt',
action: 'Zeitmuster in Quick-Activity-Intent aufnehmen',
},
'INT-011': {
description: 'Quiz Generate - Vocabulary',
cause: 'Vokabeltest wird nicht als quiz_generate klassifiziert',
action: 'Quiz-Keywords wie "Test", "Vokabel" staerker gewichten',
},
'INT-012': {
description: 'Quiz Generate - Short Test',
cause: '"Kurzer Test zu Kapitel 5" falsch erkannt',
action: 'Kontext-Keywords fuer Quiz verbessern',
},
'INT-015': {
description: 'Class Message',
cause: 'Nachricht an Klasse wird als anderer Intent erkannt',
action: 'Klassen-Nachrichten-Patterns erweitern',
},
'INT-019': {
description: 'Operator Checklist',
cause: 'Operatoren-Anfrage nicht korrekt klassifiziert',
action: 'EH/Operator-bezogene Intents pruefen',
},
'INT-021': {
description: 'Feedback Suggest',
cause: 'Feedback-Vorschlag Intent nicht erkannt',
action: 'Feedback-Synonyme hinzufuegen',
},
'INT-022': {
description: 'Reminder Schedule - Tomorrow',
cause: 'Zeitbasierte Erinnerung falsch klassifiziert',
action: 'Zeitausdrucke wie "morgen" besser verarbeiten',
},
'INT-023': {
description: 'Task Summary',
cause: 'Zusammenfassungs-Intent nicht erkannt',
action: 'Summary-Trigger erweitern',
},
// RAG Tests
'RAG-EH-001': {
description: 'EH Passage Retrieval - Textanalyse Sachtext',
cause: 'EH-Passage nicht gefunden oder unvollstaendig',
action: 'RAG-Retrieval fuer Textanalyse optimieren',
},
'RAG-HAL-002': {
description: 'No Fictional EH Passages',
cause: 'System generiert fiktive EH-Inhalte',
action: 'Hallucination-Control verstaerken',
},
'RAG-HAL-004': {
description: 'Grounded Response Only',
cause: 'Antwort basiert nicht auf vorhandenen Daten',
action: 'Grounding-Check im Response-Flow einbauen',
},
'RAG-CIT-003': {
description: 'Multiple Source Attribution',
cause: 'Mehrere Quellen nicht korrekt zugeordnet',
action: 'Multi-Source Citation verbessern',
},
'RAG-EDGE-002': {
description: 'Ambiguous Operator Query',
cause: 'Bei mehrdeutiger Anfrage keine Klaerung angefordert',
action: 'Clarification-Flow implementieren',
},
// Synthetic Tests
'SYN-STUD-003': {
description: 'Synthetic Student Observation',
cause: 'Generierte Variante nicht erkannt',
action: 'Robustheit gegen Variationen erhoehen',
},
'SYN-WORK-002': {
description: 'Synthetic Worksheet Generate',
cause: 'Arbeitsblatt-Intent bei Variation nicht erkannt',
action: 'Mehr Variationen ins Training aufnehmen',
},
'SYN-WORK-005': {
description: 'Synthetic Worksheet mit Tippfehler',
cause: 'Tippfehler fuehrt zu Fehlklassifikation',
action: 'Tippfehler-Normalisierung pruefen',
},
'SYN-REM-001': {
description: 'Synthetic Reminder',
cause: 'Reminder-Variante nicht erkannt',
action: 'Reminder-Patterns erweitern',
},
'SYN-REM-004': {
description: 'Synthetic Reminder mit Dialekt',
cause: 'Dialekt-Formulierung nicht verstanden',
action: 'Dialekt-Normalisierung verbessern',
},
}
function FailedTestsList({ testIds }: { testIds: string[] }) {
const [expandedTest, setExpandedTest] = useState(null)
if (testIds.length === 0) {
return (
)
}
return (
{testIds.map((testId) => {
const details = FAILED_TEST_DETAILS[testId]
const isExpanded = expandedTest === testId
return (
setExpandedTest(isExpanded ? null : testId)}
className="w-full flex items-center justify-between p-3 bg-red-50 hover:bg-red-100 transition-colors"
>
{testId}
{details && (
- {details.description}
)}
{isExpanded && details && (
Empfohlene Aktion
{details.action}
)}
)
})}
Tipp: Klicken Sie auf einen Test um Details zur Ursache und empfohlene Aktionen zu sehen.
Fehlgeschlagene Tests sollten vor dem naechsten Release behoben werden.
)
}
// ============================================================================
// Guide Tab Component
// ============================================================================
function GuideTab() {
return (
{/* Introduction */}
Was ist BQAS?
Das Breakpilot Quality Assurance System (BQAS) ist unser automatisiertes Test-Framework
zur kontinuierlichen Qualitaetssicherung der KI-Komponenten. Es stellt sicher, dass Aenderungen am
Voice-Service, den Prompts oder den RAG-Pipelines keine Regressionen verursachen.
{/* For Whom */}
Fuer wen ist dieses Dashboard?
Entwickler
Pruefen Sie nach Code-Aenderungen ob alle Tests noch bestehen. Analysieren Sie fehlgeschlagene Tests
und implementieren Sie Fixes.
Data Scientists
Analysieren Sie Intent-Scores, Faithfulness und Relevance. Identifizieren Sie Schwachstellen
in den ML-Modellen und RAG-Pipelines.
Auditoren / QA
Dokumentieren Sie die Testabdeckung und Qualitaetsmetriken. Nutzen Sie die Historie
fuer Audit-Trails und Compliance-Nachweise.
{/* Test Suites Explained */}
Die drei Test-Suites
1
Golden Suite (97 Tests)
Was: Manuell validierte Referenz-Tests mit definierten Erwartungen. Jeder Test
hat eine Eingabe, eine erwartete Ausgabe und Bewertungskriterien.
Wann ausfuehren: Nach jeder Aenderung am Voice-Service oder den Prompts.
Automatisch taeglich um 07:00 Uhr via launchd.
Ziel-Score: {'>'}= 4.0 (von 5.0)
2
RAG/Korrektur Tests
Was: Tests fuer das Retrieval-Augmented Generation System. Pruefen ob der richtige
Erwartungshorizont gefunden wird und ob Antworten korrekt zitiert werden.
Wann ausfuehren: Nach Aenderungen an Qdrant, Chunking-Strategien oder EH-Uploads.
Kategorien: EH-Retrieval, Operator-Alignment, Hallucination-Control, Citation-Enforcement,
Privacy-Compliance, Namespace-Isolation
3
Synthetic Tests
Was: LLM-generierte Variationen der Golden-Tests. Testet Robustheit gegenueber
Umformulierungen, Tippfehlern, Dialekt und Edge-Cases.
Wann ausfuehren: Woechentlich oder vor Major-Releases.
Hinweis: Generierung dauert laenger da LLM-Calls benoetigt werden.
{/* Metrics Explained */}
Metriken verstehen
Metrik
Beschreibung
Zielwert
Composite Score
Gewichteter Durchschnitt aller Einzelmetriken (1-5)
{'>'}= 4.0
Intent Accuracy
Wie oft wird die richtige Nutzerabsicht erkannt?
{'>'}= 90%
Faithfulness
Ist die Antwort dem EH treu? Keine Halluzinationen?
{'>'}= 4.0
Relevance
Beantwortet die Antwort die Frage des Nutzers?
{'>'}= 4.0
Coherence
Ist die Antwort logisch aufgebaut und verstaendlich?
{'>'}= 4.0
Safety Pass Rate
Werden kritische Inhalte korrekt gefiltert?
100%
{/* Workflow */}
Typischer Workflow
{[
{ step: 1, title: 'Tests starten', desc: 'Klicken Sie auf "Tests starten" bei der gewuenschten Suite. Eine Benachrichtigung zeigt den Status.' },
{ step: 2, title: 'Ergebnisse pruefen', desc: 'Nach Abschluss werden Pass Rate und Score angezeigt. Pruefen Sie ob der Zielwert erreicht wurde.' },
{ step: 3, title: 'Fehlgeschlagene Tests analysieren', desc: 'Klicken Sie auf fehlgeschlagene Tests um Ursache und empfohlene Aktionen zu sehen.' },
{ step: 4, title: 'Fixes implementieren', desc: 'Beheben Sie die identifizierten Probleme im Code, Prompts oder Training-Daten.' },
{ step: 5, title: 'Erneut testen', desc: 'Fuehren Sie die Tests erneut aus um zu verifizieren dass die Fixes wirksam sind.' },
{ step: 6, title: 'Dokumentieren', desc: 'Nutzen Sie die Historie als Audit-Trail. Exportieren Sie Reports fuer Compliance-Nachweise.' },
].map((item) => (
))}
{/* FAQ */}
Haeufige Fragen
{[
{
q: 'Wie lange dauert ein Test-Lauf?',
a: 'Golden Suite: ca. 45 Sekunden. RAG Tests: ca. 60 Sekunden. Synthetic Tests: 2-5 Minuten (abhaengig von LLM-Verfuegbarkeit).',
},
{
q: 'Was passiert wenn Tests fehlschlagen?',
a: 'Fehlgeschlagene Tests werden rot markiert. Klicken Sie darauf um Details zu sehen. Bei kritischen Regressionen wird automatisch eine Desktop-Benachrichtigung gesendet.',
},
{
q: 'Wann werden Tests automatisch ausgefuehrt?',
a: 'Die Golden Suite laeuft taeglich um 07:00 Uhr via launchd. Zusaetzlich bei jedem Commit im voice-service via Git-Hook (Quick-Tests).',
},
{
q: 'Wie kann ich einen neuen Golden-Test hinzufuegen?',
a: 'Tests werden in /voice-service/bqas/golden_tests.json definiert. Jeder Test braucht: ID, Input, Expected Intent, Bewertungskriterien.',
},
{
q: 'Was bedeutet "Demo-Daten"?',
a: 'Wenn die Voice-Service API nicht erreichbar ist, werden Demo-Daten angezeigt. Dies ist normal in der Entwicklungsumgebung wenn der Service nicht laeuft.',
},
].map((faq, i) => (
))}
{/* Links */}
CI/CD Scheduler
Automatische Test-Planung konfigurieren
RAG Management
Erwartungshorizonte und Chunking verwalten
)
}
// ============================================================================
// Main Component
// ============================================================================
export default function TestQualityPage() {
const [activeTab, setActiveTab] = useState('overview')
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
// Toast state - using useRef for stable ID counter
const [toasts, setToasts] = useState([])
const toastIdRef = useRef(0)
const addToast = useCallback((type: Toast['type'], message: string) => {
const id = ++toastIdRef.current
console.log('Adding toast:', id, type, message) // Debug log
setToasts((prev) => [...prev, { id, type, message }])
if (type !== 'loading') {
setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id))
}, 5000)
}
return id
}, [])
const removeToast = useCallback((id: number) => {
setToasts((prev) => prev.filter((t) => t.id !== id))
}, [])
const updateToast = useCallback((id: number, type: Toast['type'], message: string) => {
console.log('Updating toast:', id, type, message) // Debug log
setToasts((prev) =>
prev.map((t) => (t.id === id ? { ...t, type, message } : t))
)
if (type !== 'loading') {
setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id))
}, 5000)
}
}, [])
// Data states
const [goldenMetrics, setGoldenMetrics] = useState(null)
const [syntheticMetrics, setSyntheticMetrics] = useState(null)
const [ragMetrics, setRagMetrics] = useState(null)
const [testRuns, setTestRuns] = useState([])
const [trendData, setTrendData] = useState(null)
// Running states
const [isRunningGolden, setIsRunningGolden] = useState(false)
const [isRunningSynthetic, setIsRunningSynthetic] = useState(false)
const [isRunningRag, setIsRunningRag] = useState(false)
// Demo data for when API is not available
const DEMO_GOLDEN_METRICS: BQASMetrics = {
total_tests: 97,
passed_tests: 89,
failed_tests: 8,
avg_intent_accuracy: 91.7,
avg_faithfulness: 4.2,
avg_relevance: 4.1,
avg_coherence: 4.3,
safety_pass_rate: 0.98,
avg_composite_score: 4.15,
scores_by_intent: {
korrektur_anfrage: 4.5,
erklaerung_anfrage: 4.3,
hilfe_anfrage: 4.1,
feedback_anfrage: 3.9,
smalltalk: 4.2,
},
failed_test_ids: ['GT-023', 'GT-045', 'GT-067', 'GT-072', 'GT-081', 'GT-089', 'GT-092', 'GT-095'],
}
const DEMO_TREND: TrendData = {
dates: ['2026-01-02', '2026-01-09', '2026-01-16', '2026-01-23', '2026-01-30'],
scores: [3.9, 4.0, 4.1, 4.15, 4.15],
trend: 'improving',
}
const DEMO_RUNS: TestRun[] = [
{ id: 1, timestamp: '2026-01-30T07:00:00Z', git_commit: 'abc1234', golden_score: 4.15, synthetic_score: 3.9, total_tests: 97, passed_tests: 89, failed_tests: 8, duration_seconds: 45.2 },
{ id: 2, timestamp: '2026-01-29T07:00:00Z', git_commit: 'def5678', golden_score: 4.12, synthetic_score: 3.85, total_tests: 97, passed_tests: 88, failed_tests: 9, duration_seconds: 44.8 },
{ id: 3, timestamp: '2026-01-28T07:00:00Z', git_commit: '9ab0123', golden_score: 4.10, synthetic_score: 3.82, total_tests: 97, passed_tests: 87, failed_tests: 10, duration_seconds: 46.1 },
]
// Fetch data
const fetchData = useCallback(async () => {
setIsLoading(true)
setError(null)
try {
const runsResponse = await fetch(`${BQAS_API_BASE}/runs`)
if (runsResponse.ok) {
const runsData = await runsResponse.json()
if (runsData.runs && runsData.runs.length > 0) {
setTestRuns(runsData.runs)
} else {
setTestRuns(DEMO_RUNS)
}
} else {
setTestRuns(DEMO_RUNS)
}
const trendResponse = await fetch(`${BQAS_API_BASE}/trend?days=30`)
if (trendResponse.ok) {
const trend = await trendResponse.json()
if (trend.dates && trend.dates.length > 0) {
setTrendData(trend)
} else {
setTrendData(DEMO_TREND)
}
} else {
setTrendData(DEMO_TREND)
}
const metricsResponse = await fetch(`${BQAS_API_BASE}/latest-metrics`)
if (metricsResponse.ok) {
const metrics = await metricsResponse.json()
setGoldenMetrics(metrics.golden || DEMO_GOLDEN_METRICS)
setSyntheticMetrics(metrics.synthetic || null)
setRagMetrics(metrics.rag || null)
} else {
setGoldenMetrics(DEMO_GOLDEN_METRICS)
}
} catch (err) {
console.error('Failed to fetch BQAS data, using demo data:', err)
setTestRuns(DEMO_RUNS)
setTrendData(DEMO_TREND)
setGoldenMetrics(DEMO_GOLDEN_METRICS)
} finally {
setIsLoading(false)
}
}, [])
useEffect(() => {
fetchData()
}, [fetchData])
// Run test suites with toast feedback
const runGoldenTests = async () => {
setIsRunningGolden(true)
const loadingToast = addToast('loading', 'Golden Suite wird ausgefuehrt...')
try {
const response = await fetch(`${BQAS_API_BASE}/run/golden`, {
method: 'POST',
})
if (response.ok) {
const result = await response.json()
setGoldenMetrics(result.metrics)
updateToast(loadingToast, 'success', `Golden Suite abgeschlossen: ${result.metrics?.passed_tests || 89}/${result.metrics?.total_tests || 97} bestanden`)
await fetchData()
} else {
updateToast(loadingToast, 'info', 'Golden Suite: Demo-Modus (API nicht verfuegbar)')
}
} catch (err) {
console.error('Failed to run golden tests:', err)
updateToast(loadingToast, 'info', 'Golden Suite: Demo-Modus (API nicht verfuegbar)')
} finally {
setIsRunningGolden(false)
}
}
const runSyntheticTests = async () => {
setIsRunningSynthetic(true)
const loadingToast = addToast('loading', 'Synthetic Tests werden generiert und ausgefuehrt...')
try {
const response = await fetch(`${BQAS_API_BASE}/run/synthetic`, {
method: 'POST',
})
if (response.ok) {
const result = await response.json()
setSyntheticMetrics(result.metrics)
updateToast(loadingToast, 'success', 'Synthetic Tests abgeschlossen')
await fetchData()
} else {
updateToast(loadingToast, 'info', 'Synthetic Tests: Demo-Modus (API nicht verfuegbar)')
}
} catch (err) {
console.error('Failed to run synthetic tests:', err)
updateToast(loadingToast, 'info', 'Synthetic Tests: Demo-Modus (API nicht verfuegbar)')
} finally {
setIsRunningSynthetic(false)
}
}
const runRagTests = async () => {
setIsRunningRag(true)
const loadingToast = addToast('loading', 'RAG/Korrektur Tests werden ausgefuehrt...')
try {
const response = await fetch(`${BQAS_API_BASE}/run/rag`, {
method: 'POST',
})
if (response.ok) {
const result = await response.json()
setRagMetrics(result.metrics)
updateToast(loadingToast, 'success', 'RAG Tests abgeschlossen')
await fetchData()
} else {
updateToast(loadingToast, 'info', 'RAG Tests: Demo-Modus (API nicht verfuegbar)')
}
} catch (err) {
console.error('Failed to run RAG tests:', err)
updateToast(loadingToast, 'info', 'RAG Tests: Demo-Modus (API nicht verfuegbar)')
} finally {
setIsRunningRag(false)
}
}
// Tab content renderer
const renderTabContent = () => {
switch (activeTab) {
case 'overview':
return (
Score-Trend (30 Tage)
{/* Quick Help */}
Neu hier? Wechseln Sie zum Tab "Anleitung" fuer eine ausfuehrliche Erklaerung
des BQAS-Systems und wie Sie es nutzen koennen.
)
case 'golden':
return (
Golden Test Suite
Validierte Referenz-Tests gegen definierte Erwartungen
{isRunningGolden ? 'Laeuft...' : 'Tests starten'}
{goldenMetrics && (
<>
{goldenMetrics.total_tests}
Tests
{goldenMetrics.passed_tests}
Bestanden
{goldenMetrics.failed_tests}
Fehlgeschlagen
{goldenMetrics.avg_intent_accuracy.toFixed(0)}%
Intent Accuracy
{goldenMetrics.avg_composite_score.toFixed(2)}
Composite Score
Scores nach Intent
Fehlgeschlagene Tests ({goldenMetrics.failed_tests})
>
)}
)
case 'rag':
return (
RAG/Korrektur Test Suite
Erwartungshorizont-Retrieval, Operatoren-Alignment, Citations
{isRunningRag ? 'Laeuft...' : 'Tests starten'}
{ragMetrics ? (
<>
{ragMetrics.total_tests}
Tests
{ragMetrics.avg_faithfulness.toFixed(2)}
Faithfulness
{ragMetrics.avg_relevance.toFixed(2)}
Relevance
{(ragMetrics.safety_pass_rate * 100).toFixed(0)}%
Safety Pass
RAG Kategorien
Fehlgeschlagene Tests
>
) : (
Noch keine RAG-Test-Ergebnisse
Klicke "Tests starten" um die RAG-Suite auszufuehren
)}
Test-Kategorien
EH Retrieval
Korrektes Abrufen von Erwartungshorizont-Passagen
Operator Alignment
Passende Operatoren fuer Abitur-Aufgaben
Hallucination Control
Keine erfundenen Fakten oder Inhalte
Citation Enforcement
Quellenangaben bei EH-Bezuegen
Privacy Compliance
Keine PII-Leaks, DSGVO-Konformitaet
Namespace Isolation
Strikte Trennung zwischen Lehrern
)
case 'synthetic':
return (
Synthetic Test Suite
LLM-generierte Variationen fuer Robustheit-Tests
{isRunningSynthetic ? 'Laeuft...' : 'Tests starten'}
{syntheticMetrics ? (
<>
{syntheticMetrics.total_tests}
Generierte Tests
{syntheticMetrics.passed_tests}
Bestanden
{syntheticMetrics.avg_composite_score.toFixed(2)}
Avg Score
{syntheticMetrics.avg_coherence.toFixed(2)}
Coherence
Intent-Variationen
Fehlgeschlagene Tests
>
) : (
Noch keine synthetischen Tests ausgefuehrt
Klicke "Tests starten" um Variationen zu generieren
)}
)
case 'history':
return (
Test Run Historie
)
case 'guide' as TabType:
return
default:
return null
}
}
return (
{/* KI-Werkzeuge Sidebar */}
{error && (
)}
{[
{ id: 'overview', label: 'Uebersicht' },
{ id: 'golden', label: 'Golden Suite' },
{ id: 'rag', label: 'RAG/Korrektur' },
{ id: 'synthetic', label: 'Synthetic' },
{ id: 'history', label: 'Historie' },
{ id: 'guide', label: 'Anleitung', highlight: true },
].map((tab) => (
setActiveTab(tab.id as TabType)}
className={`py-4 text-sm font-medium border-b-2 transition-colors ${
activeTab === tab.id
? 'border-teal-600 text-teal-600'
: tab.highlight
? 'border-transparent text-blue-500 hover:text-blue-600'
: 'border-transparent text-slate-500 hover:text-slate-700'
}`}
>
{tab.label}
))}
{isLoading ? (
) : (
renderTabContent()
)}
Voice Service: voice-service:8091
Scheduler (CI/CD)
Compliance Audit
)
}