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 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
sed replacement left orphaned hostname references in story page and empty lines in getApiBase functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
278 lines
11 KiB
TypeScript
278 lines
11 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import type { TrainingConfig } from '../types'
|
|
|
|
const BUNDESLAENDER = [
|
|
{ code: 'ni', name: 'Niedersachsen', allowed: true },
|
|
{ code: 'by', name: 'Bayern', allowed: true },
|
|
{ code: 'nw', name: 'NRW', allowed: true },
|
|
{ code: 'he', name: 'Hessen', allowed: true },
|
|
{ code: 'bw', name: 'Baden-Wuerttemberg', allowed: true },
|
|
{ code: 'rp', name: 'Rheinland-Pfalz', allowed: true },
|
|
{ code: 'sn', name: 'Sachsen', allowed: true },
|
|
{ code: 'sh', name: 'Schleswig-Holstein', allowed: true },
|
|
{ code: 'th', name: 'Thueringen', allowed: true },
|
|
{ code: 'be', name: 'Berlin', allowed: false },
|
|
{ code: 'bb', name: 'Brandenburg', allowed: false },
|
|
{ code: 'hb', name: 'Bremen', allowed: false },
|
|
{ code: 'hh', name: 'Hamburg', allowed: false },
|
|
{ code: 'mv', name: 'Mecklenburg-Vorpommern', allowed: false },
|
|
{ code: 'sl', name: 'Saarland', allowed: false },
|
|
{ code: 'st', name: 'Sachsen-Anhalt', allowed: false },
|
|
]
|
|
|
|
export function NewTrainingModal({ isOpen, onClose, onSubmit }: {
|
|
isOpen: boolean
|
|
onClose: () => void
|
|
onSubmit: (config: Partial<TrainingConfig>) => void
|
|
}) {
|
|
const [step, setStep] = useState(1)
|
|
const [config, setConfig] = useState<Partial<TrainingConfig>>({
|
|
batch_size: 16,
|
|
learning_rate: 0.00005,
|
|
epochs: 10,
|
|
warmup_steps: 500,
|
|
weight_decay: 0.01,
|
|
gradient_accumulation: 4,
|
|
mixed_precision: true,
|
|
bundeslaender: [],
|
|
})
|
|
|
|
if (!isOpen) return null
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
|
|
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-2xl max-h-[90vh] overflow-hidden">
|
|
{/* Header */}
|
|
<div className="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center">
|
|
<div>
|
|
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">
|
|
Neue Indexierung starten
|
|
</h2>
|
|
<p className="text-sm text-gray-500">Schritt {step} von 3</p>
|
|
</div>
|
|
<button onClick={onClose} className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg">
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
{/* Step Indicator */}
|
|
<StepIndicator currentStep={step} />
|
|
|
|
{/* Step Content */}
|
|
<div className="p-6 overflow-y-auto max-h-[50vh]">
|
|
{step === 1 && (
|
|
<BundeslaenderStep config={config} setConfig={setConfig} />
|
|
)}
|
|
{step === 2 && (
|
|
<ParameterStep config={config} setConfig={setConfig} />
|
|
)}
|
|
{step === 3 && (
|
|
<ConfirmStep config={config} />
|
|
)}
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<div className="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-between">
|
|
<button
|
|
onClick={() => step > 1 ? setStep(step - 1) : onClose()}
|
|
className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700"
|
|
>
|
|
{step > 1 ? 'Zurueck' : 'Abbrechen'}
|
|
</button>
|
|
<button
|
|
onClick={() => step < 3 ? setStep(step + 1) : onSubmit(config)}
|
|
disabled={step === 1 && (!config.bundeslaender || config.bundeslaender.length === 0)}
|
|
className="px-6 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{step < 3 ? 'Weiter' : 'Indexierung starten'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// --- Internal step components ---
|
|
|
|
function StepIndicator({ currentStep }: { currentStep: number }) {
|
|
return (
|
|
<div className="px-6 py-4 bg-gray-50 dark:bg-gray-900">
|
|
<div className="flex items-center justify-center gap-4">
|
|
{[1, 2, 3].map((s) => (
|
|
<div key={s} className="flex items-center">
|
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium ${
|
|
s <= currentStep
|
|
? 'bg-blue-600 text-white'
|
|
: 'bg-gray-200 dark:bg-gray-700 text-gray-500'
|
|
}`}>
|
|
{s < currentStep ? '\u2713' : s}
|
|
</div>
|
|
{s < 3 && (
|
|
<div className={`w-16 h-1 mx-2 rounded ${
|
|
s < currentStep ? 'bg-blue-600' : 'bg-gray-200 dark:bg-gray-700'
|
|
}`} />
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="flex justify-center gap-20 mt-2 text-xs text-gray-500">
|
|
<span>Daten</span>
|
|
<span>Parameter</span>
|
|
<span>Bestaetigen</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function BundeslaenderStep({ config, setConfig }: {
|
|
config: Partial<TrainingConfig>
|
|
setConfig: (config: Partial<TrainingConfig>) => void
|
|
}) {
|
|
return (
|
|
<div>
|
|
<h3 className="font-medium text-gray-900 dark:text-white mb-4">
|
|
Waehlen Sie die Bundeslaender fuer die Indexierung
|
|
</h3>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-4">
|
|
Nur Bundeslaender mit verfuegbaren Dokumenten koennen ausgewaehlt werden.
|
|
</p>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
{BUNDESLAENDER.map((bl) => (
|
|
<label
|
|
key={bl.code}
|
|
className={`flex items-center p-3 rounded-lg border-2 transition cursor-pointer ${
|
|
config.bundeslaender?.includes(bl.code)
|
|
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
|
|
: bl.allowed
|
|
? 'border-gray-200 dark:border-gray-700 hover:border-blue-300'
|
|
: 'border-gray-200 dark:border-gray-700 opacity-50 cursor-not-allowed'
|
|
}`}
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
disabled={!bl.allowed}
|
|
checked={config.bundeslaender?.includes(bl.code)}
|
|
onChange={(e) => {
|
|
if (e.target.checked) {
|
|
setConfig({ ...config, bundeslaender: [...(config.bundeslaender || []), bl.code] })
|
|
} else {
|
|
setConfig({ ...config, bundeslaender: config.bundeslaender?.filter(c => c !== bl.code) })
|
|
}
|
|
}}
|
|
className="sr-only"
|
|
/>
|
|
<span className={`w-5 h-5 rounded border-2 flex items-center justify-center mr-3 ${
|
|
config.bundeslaender?.includes(bl.code)
|
|
? 'bg-blue-500 border-blue-500 text-white'
|
|
: 'border-gray-300 dark:border-gray-600'
|
|
}`}>
|
|
{config.bundeslaender?.includes(bl.code) && '\u2713'}
|
|
</span>
|
|
<span className="flex-1 text-gray-900 dark:text-white">{bl.name}</span>
|
|
{!bl.allowed && (
|
|
<span className="text-xs text-red-500">Keine Daten</span>
|
|
)}
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ParameterStep({ config, setConfig }: {
|
|
config: Partial<TrainingConfig>
|
|
setConfig: (config: Partial<TrainingConfig>) => void
|
|
}) {
|
|
return (
|
|
<div className="space-y-6">
|
|
<h3 className="font-medium text-gray-900 dark:text-white mb-4">
|
|
Indexierungs-Parameter
|
|
</h3>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
Diese Parameter steuern die Batch-Verarbeitung der Dokumente.
|
|
</p>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Batch Size
|
|
</label>
|
|
<input
|
|
type="number"
|
|
value={config.batch_size}
|
|
onChange={(e) => setConfig({ ...config, batch_size: parseInt(e.target.value) })}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700"
|
|
/>
|
|
<p className="text-xs text-gray-500 mt-1">Dokumente pro Batch</p>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
|
Durchlaeufe
|
|
</label>
|
|
<input
|
|
type="number"
|
|
value={config.epochs}
|
|
onChange={(e) => setConfig({ ...config, epochs: parseInt(e.target.value) })}
|
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700"
|
|
/>
|
|
<p className="text-xs text-gray-500 mt-1">Fuer Validierung</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3 p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
|
<input
|
|
type="checkbox"
|
|
id="mixedPrecision"
|
|
checked={config.mixed_precision}
|
|
onChange={(e) => setConfig({ ...config, mixed_precision: e.target.checked })}
|
|
className="w-4 h-4 text-blue-600 rounded"
|
|
/>
|
|
<label htmlFor="mixedPrecision" className="text-sm text-gray-700 dark:text-gray-300">
|
|
Parallele Verarbeitung - schneller bei grossem Datensatz
|
|
</label>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ConfirmStep({ config }: { config: Partial<TrainingConfig> }) {
|
|
return (
|
|
<div>
|
|
<h3 className="font-medium text-gray-900 dark:text-white mb-4">
|
|
Konfiguration bestaetigen
|
|
</h3>
|
|
|
|
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 space-y-3">
|
|
<div className="flex justify-between">
|
|
<span className="text-gray-600 dark:text-gray-400">Bundeslaender</span>
|
|
<span className="font-medium text-gray-900 dark:text-white">
|
|
{config.bundeslaender?.length || 0} ausgewaehlt
|
|
</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-gray-600 dark:text-gray-400">Batch Size</span>
|
|
<span className="font-medium text-gray-900 dark:text-white">{config.batch_size}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-gray-600 dark:text-gray-400">Parallele Verarbeitung</span>
|
|
<span className="font-medium text-gray-900 dark:text-white">
|
|
{config.mixed_precision ? 'Aktiviert' : 'Deaktiviert'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4 p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
|
|
<p className="text-sm text-blue-800 dark:text-blue-200">
|
|
<strong>Was passiert:</strong> Die ausgewaehlten Dokumente werden extrahiert,
|
|
in Chunks aufgeteilt, und als Vektoren in Qdrant indexiert.
|
|
Dieser Prozess kann je nach Datenmenge einige Minuten dauern.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|