Spaces:
Runtime error
Runtime error
| 'use client'; | |
| import React, { useRef, useEffect, useState, useMemo } from 'react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Input } from './ui/input'; | |
| import { Label } from './ui/label'; | |
| import { Slider } from './ui/slider'; | |
| import { Switch } from './ui/switch'; | |
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; | |
| import { ScrollArea } from './ui/scroll-area'; | |
| import { Badge } from './ui/badge'; | |
| import { Separator } from './ui/separator'; | |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'; | |
| import NeonAnalyzerChart from './visualizations/NeonAnalyzerChart'; | |
| // Chart Panel Component | |
| export const ChartPanel: React.FC<{ | |
| metrics?: { epoch: number; loss: number; accuracy: number; val_loss?: number; val_accuracy?: number }[]; | |
| jobStatus?: string; | |
| isLoading: boolean; | |
| freeze: () => void; | |
| resume: () => void; | |
| exportMetrics: () => void; | |
| currentEpoch?: number; | |
| totalEpochs?: number; | |
| currentAccuracy?: number; | |
| currentLoss?: number; | |
| backendConnected?: boolean | null; | |
| }> = ({ | |
| metrics = [], | |
| jobStatus = 'idle', | |
| isLoading, | |
| freeze, | |
| resume, | |
| exportMetrics, | |
| currentEpoch = 0, | |
| totalEpochs = 0, | |
| currentAccuracy = 0, | |
| currentLoss = 0, | |
| backendConnected = null, | |
| }) => { | |
| const containerRef = useRef<HTMLDivElement>(null); | |
| const [size, setSize] = useState({ width: 800, height: 600 }); | |
| useEffect(() => { | |
| const handleResize = () => { | |
| if (containerRef.current) { | |
| const rect = containerRef.current.getBoundingClientRect(); | |
| setSize({ | |
| width: Math.max(300, rect.width - 40), // Minimum width with padding | |
| height: Math.max(200, rect.height - 120) // Account for header and padding | |
| }); | |
| } | |
| }; | |
| handleResize(); | |
| const resizeObserver = new ResizeObserver(handleResize); | |
| if (containerRef.current) { | |
| resizeObserver.observe(containerRef.current); | |
| } | |
| return () => { | |
| resizeObserver.disconnect(); | |
| window.removeEventListener('resize', handleResize); | |
| }; | |
| }, []); | |
| // Format metrics for the chart | |
| const traces = [ | |
| { | |
| data: metrics.map(m => m.accuracy), | |
| color: '#39ff14', // Neon green | |
| label: 'Accuracy' | |
| }, | |
| { | |
| data: metrics.map(m => m.loss), | |
| color: '#ff00ff', // Neon pink | |
| label: 'Loss' | |
| }, | |
| { | |
| data: metrics.map(m => m.val_accuracy || 0), | |
| color: '#00ffe7', // Neon cyan | |
| label: 'Val Acc' | |
| } | |
| ]; | |
| // Ensure traces have valid data | |
| const validTraces = traces.filter(trace => trace.data.length > 0); | |
| const hasValidData = validTraces.length > 0 && validTraces[0].data.length > 0; | |
| // Status display text and colors | |
| const statusText = typeof jobStatus === 'string' && jobStatus.length > 0 ? jobStatus.toUpperCase() : 'IDLE'; | |
| const isRunning = jobStatus === 'running'; | |
| const isCompleted = jobStatus === 'completed'; | |
| const isFailed = jobStatus === 'failed'; | |
| // Get status-specific styling | |
| const getStatusStyling = () => { | |
| if (backendConnected === false) { | |
| return { | |
| badgeClass: 'bg-[#ff6600]/10 text-[#ff6600] border-[#ff6600]/30', | |
| headerClass: 'text-[#ff6600]', | |
| text: 'OFFLINE' | |
| }; | |
| } else if (backendConnected === null) { | |
| return { | |
| badgeClass: 'bg-[#ffff00]/10 text-[#ffff00] border-[#ffff00]/30', | |
| headerClass: 'text-[#ffff00]', | |
| text: 'CONNECTING' | |
| }; | |
| } else if (isRunning) { | |
| return { | |
| badgeClass: 'bg-[#39ff14]/10 text-[#39ff14] border-[#39ff14]/30', | |
| headerClass: 'text-[#39ff14]', | |
| text: 'LIVE TRAINING' | |
| }; | |
| } else if (isCompleted) { | |
| return { | |
| badgeClass: 'bg-[#00ffe7]/10 text-[#00ffe7] border-[#00ffe7]/30', | |
| headerClass: 'text-[#00ffe7]', | |
| text: 'COMPLETED' | |
| }; | |
| } else if (isFailed) { | |
| return { | |
| badgeClass: 'bg-[#ff0080]/10 text-[#ff0080] border-[#ff0080]/30', | |
| headerClass: 'text-[#ff0080]', | |
| text: 'FAILED' | |
| }; | |
| } else { | |
| return { | |
| badgeClass: 'bg-[#00ffe7]/10 text-[#00ffe7] border-[#00ffe7]/30', | |
| headerClass: 'text-[#00ffe7]', | |
| text: 'READY' | |
| }; | |
| } | |
| }; | |
| const statusStyling = getStatusStyling(); | |
| return ( | |
| <div ref={containerRef} className="flex flex-col h-full w-full min-h-[700px] bg-[#0a0f1c] rounded-lg border border-[#00ffe7]/30 shadow-lg shadow-[#00ffe7]/20 max-w-full overflow-hidden"> | |
| {/* Header */} | |
| <div className="flex items-center justify-between p-2 border-b border-[#00ffe7]/20 flex-shrink-0"> | |
| <div className="flex items-center space-x-4 min-w-0 flex-1"> | |
| <h2 className="text-xl font-bold font-mono text-[#ffff00] truncate">SPECTRUM ANALYZER</h2> | |
| <Badge variant="outline" className={`text-xs font-semibold ${statusStyling.badgeClass}`}> | |
| {statusStyling.text} | |
| </Badge> | |
| {backendConnected && currentEpoch > 0 && ( | |
| <div className="text-xs font-mono text-[#00ffe7]/80 hidden sm:block"> | |
| EPOCH: {currentEpoch}/{totalEpochs} | ACC: {(currentAccuracy || 0).toFixed(1)}% | LOSS: {(currentLoss || 0).toFixed(4)} | |
| </div> | |
| )} | |
| </div> | |
| <div className="flex items-center space-x-2 flex-shrink-0"> | |
| <Button variant="outline" className="neon-button border-[#00ffe7]/50 text-[#00ffe7] bg-black/40 hover:bg-[#00ffe7]/10 hover:shadow-[0_0_16px_#00ffe7] transition-all duration-300 font-mono text-xs tracking-wide h-9 px-3"> | |
| FREEZE | |
| </Button> | |
| <Button variant="outline" className="neon-button border-[#00ffe7]/50 text-[#00ffe7] bg-black/40 hover:bg-[#00ffe7]/10 hover:shadow-[0_0_16px_#00ffe7] transition-all duration-300 font-mono text-xs tracking-wide h-9 px-3" disabled={!hasValidData}> | |
| EXPORT | |
| </Button> | |
| </div> | |
| </div> | |
| {/* Chart Content */} | |
| <div className="flex-1 p-4 overflow-hidden"> | |
| {backendConnected === false ? ( | |
| <div className="flex items-center justify-center h-full"> | |
| <div className="text-center"> | |
| <div className="w-16 h-16 mx-auto mb-4 border-2 border-[#ff6600]/30 rounded-full flex items-center justify-center animate-pulse"> | |
| <span className="text-[#ff6600] text-2xl">⚠️</span> | |
| </div> | |
| <p className="text-[#ff6600] font-mono text-sm">Backend Offline</p> | |
| </div> | |
| </div> | |
| ) : backendConnected === null ? ( | |
| <div className="flex items-center justify-center h-full"> | |
| <div className="text-center"> | |
| <div className="w-16 h-16 mx-auto mb-4 border-2 border-[#ffff00]/30 rounded-full flex items-center justify-center animate-pulse"> | |
| <span className="text-[#ffff00] text-2xl">🔄</span> | |
| </div> | |
| <p className="text-[#ffff00] font-mono text-sm">Connecting to training backend...</p> | |
| </div> | |
| </div> | |
| ) : hasValidData ? ( | |
| <NeonAnalyzerChart | |
| width={size.width} | |
| height={size.height} | |
| traces={validTraces} | |
| backgroundColor="rgba(10, 15, 28, 0.95)" | |
| gridColor="rgba(0, 255, 231, 0.2)" | |
| overlays={[ | |
| { text: 'Training Metrics', color: '#39ff14', x: 120, y: 40, fontSize: 24 } | |
| ]} | |
| /> | |
| ) : ( | |
| <div className="flex items-center justify-center h-full"> | |
| <div className="text-center"> | |
| <div className="w-16 h-16 mx-auto mb-4 border-2 border-[#00ffe7]/30 rounded-full flex items-center justify-center"> | |
| <span className="text-[#00ffe7] text-2xl">📊</span> | |
| </div> | |
| <p className="text-[#00ffe7] font-mono text-sm">No training data available</p> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| // Terminal Panel Component | |
| export const TerminalPanel: React.FC<{ | |
| logs: Array<{ time: string; level: string; message: string }> | string[]; | |
| isPolling?: boolean; | |
| clearLogs: () => void; | |
| exportLogs: () => void; | |
| stopJob: () => void; | |
| }> = ({ logs, isPolling = false, clearLogs, exportLogs, stopJob }) => { | |
| const visibleLogs = useMemo(() => logs.slice(-50), [logs]); | |
| const renderLogLine = (log, index) => { | |
| if (typeof log === 'string') { | |
| return ( | |
| <div key={index} className="font-mono text-xs text-[#39ff14] leading-relaxed break-words whitespace-pre-wrap"> | |
| {log} | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div key={index} className="font-mono text-xs leading-relaxed break-words"> | |
| <span className="text-[#39ff14]/70">{log.timestamp}</span> | |
| <span className="text-[#39ff14]/50 mx-2">[{log.level}]</span> | |
| <span className="text-[#39ff14]">{log.message}</span> | |
| </div> | |
| ); | |
| }; | |
| return ( | |
| <Card className="neon-card glass terminal-panel h-full w-full bg-black/98 border-[#39ff14]/20 shadow-[0_0_16px_#39ff1444,inset_0_0_16px_rgba(0,0,0,0.8)] backdrop-blur-sm overflow-hidden max-w-full flex flex-col" style={{ fontFamily: 'Share Tech Mono, Space Mono, VT323, monospace', borderRadius: '12px' }}> | |
| <div className="terminal-header p-3 border-b border-[#39ff14]/30 flex-shrink-0"> | |
| <div className="flex items-center justify-between min-w-0"> | |
| <div className="flex items-center space-x-3 min-w-0 flex-1"> | |
| <div className="w-2 h-2 rounded-full shadow-[0_0_12px_#39ff14] bg-[#39ff14]/50 flex-shrink-0"></div> | |
| <h3 className="text-[#39ff14] font-mono text-sm font-medium tracking-wide text-shadow-neon truncate">NEURAL LOG</h3> | |
| <Badge variant="outline" className="border-[#39ff14]/50 bg-[#39ff14]/10 text-xs shadow-[0_0_8px_#39ff14] text-[#39ff14]/70 flex-shrink-0"> | |
| {isPolling ? 'ACTIVE' : 'STANDBY'} | |
| </Badge> | |
| </div> | |
| <div className="flex items-center space-x-2 flex-shrink-0"> | |
| <span className="text-[#39ff14]/70 font-mono text-xs">LINES: {visibleLogs.length}</span> | |
| <Button | |
| size="sm" | |
| variant="outline" | |
| className="border-[#39ff14]/30 text-[#39ff14] text-xs hover:bg-[#39ff14]/10 hover:shadow-[0_0_8px_#39ff14] h-5 px-2" | |
| onClick={clearLogs} | |
| > | |
| CLEAR | |
| </Button> | |
| <Button | |
| size="sm" | |
| variant="outline" | |
| className="border-[#39ff14]/30 text-[#39ff14] text-xs hover:bg-[#39ff14]/10 hover:shadow-[0_0_8px_#39ff14] h-5 px-2" | |
| onClick={exportLogs} | |
| > | |
| EXPORT | |
| </Button> | |
| <Button | |
| size="sm" | |
| variant="outline" | |
| className="border-[#ff0080]/30 text-[#ff0080] text-xs hover:bg-[#ff0080]/10 hover:shadow-[0_0_8px_#ff0080] h-5 px-2" | |
| onClick={stopJob} | |
| > | |
| STOP | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex-1 min-h-0 overflow-hidden"> | |
| <ScrollArea className="h-full pr-2"> | |
| <div className="p-3 space-y-1 max-w-full"> | |
| {visibleLogs.length === 0 ? ( | |
| <div className="flex items-center justify-center h-32"> | |
| <div className="text-center"> | |
| <div className="w-12 h-12 mx-auto mb-3 border border-[#39ff14]/30 rounded-full flex items-center justify-center"> | |
| <span className="text-[#39ff14] text-xl">📟</span> | |
| </div> | |
| <p className="font-mono text-xs text-[#39ff14]/70">No logs available</p> | |
| <p className="font-mono text-xs text-[#39ff14]/50 mt-1">Start training to see real-time output</p> | |
| </div> | |
| </div> | |
| ) : ( | |
| visibleLogs.map((log, index) => renderLogLine(log, index)) | |
| )} | |
| </div> | |
| </ScrollArea> | |
| </div> | |
| </Card> | |
| ); | |
| }; | |
| // Controls Panel Component | |
| export const ControlsPanel: React.FC<{ | |
| onTrainingStart?: (jobId: string) => void; | |
| isTraining?: boolean; | |
| currentJobId?: string | null; | |
| jobStatus?: import('@/lib/training-api').JobStatus | null; | |
| backendConnected?: boolean | null; | |
| }> = ({ onTrainingStart, isTraining = false, currentJobId, jobStatus, backendConnected = null }) => { | |
| // Form state for training parameters | |
| const [formData, setFormData] = React.useState({ | |
| modelName: 'ZPE_Quantum_Neural_Net', | |
| totalEpochs: 60, | |
| batchSize: 32, | |
| learningRate: 0.001, | |
| weightDecay: 0.0001, | |
| baseConfigId: '', | |
| sequenceLength: 32, | |
| labelSmoothing: 0.1, | |
| mixupAlpha: 1.0, | |
| // ZPE Parameters | |
| momentumParams: [0.9, 0.85, 0.8, 0.75, 0.7, 0.65], | |
| strengthParams: [0.35, 0.3, 0.25, 0.2, 0.15, 0.1], | |
| noiseParams: [0.3, 0.25, 0.2, 0.15, 0.1, 0.05], | |
| couplingParams: [0.85, 0.8, 0.75, 0.7, 0.65, 0.6], | |
| }); | |
| const [isSubmitting, setIsSubmitting] = React.useState(false); | |
| // Import the API | |
| const { trainingAPI } = React.useMemo(() => { | |
| // We'll import this dynamically to avoid SSR issues | |
| return require('@/lib/training-api'); | |
| }, []); | |
| // Handle form field updates | |
| const updateField = (field: string, value: any) => { | |
| setFormData(prev => ({ ...prev, [field]: value })); | |
| }; | |
| // Handle ZPE parameter updates | |
| const updateZPEParam = (paramType: 'momentumParams' | 'strengthParams' | 'noiseParams' | 'couplingParams', index: number, value: number) => { | |
| setFormData(prev => ({ | |
| ...prev, | |
| [paramType]: prev[paramType].map((v, i) => i === index ? value : v) | |
| })); | |
| }; | |
| // Handle training submission | |
| const handleStartTraining = async () => { | |
| if (isSubmitting || isTraining || !backendConnected) return; | |
| try { | |
| setIsSubmitting(true); | |
| console.log('🚀 Submitting training request with params:', formData); | |
| // Prepare parameters for backend API | |
| const trainingParams = { | |
| model_name: formData.modelName, | |
| total_epochs: formData.totalEpochs, | |
| batch_size: formData.batchSize, | |
| learning_rate: formData.learningRate, | |
| sequence_length: formData.sequenceLength, | |
| label_smoothing: formData.labelSmoothing, | |
| mixup_alpha: formData.mixupAlpha, | |
| base_config_id: formData.baseConfigId || undefined, | |
| weightDecay: formData.weightDecay, | |
| momentumParams: formData.momentumParams, | |
| strengthParams: formData.strengthParams, | |
| noiseParams: formData.noiseParams, | |
| couplingParams: formData.couplingParams, | |
| }; | |
| // Call the backend API | |
| const response = await trainingAPI.startTraining(trainingParams); | |
| if (response.status === 'training_started' && response.job_id) { | |
| console.log('✅ Training started successfully:', response.job_id); | |
| onTrainingStart?.(response.job_id); | |
| } else { | |
| throw new Error('Unexpected response from training API'); | |
| } | |
| } catch (error) { | |
| console.error('❌ Training start failed:', error); | |
| alert(`Training failed to start: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } finally { | |
| setIsSubmitting(false); | |
| } | |
| }; | |
| return ( | |
| <Card className="neon-card glass controls-panel h-full w-full bg-black/98 border-[#00ffe7]/30 backdrop-blur-sm flex flex-col overflow-hidden" style={{ borderRadius: '12px', minWidth: '0' }}> | |
| <div className="controls-header p-3 border-b border-[#00ffe7]/30 flex-shrink-0"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center space-x-3 min-w-0 flex-1"> | |
| <div className="w-3 h-3 bg-[#00ffe7] rounded-full animate-pulse shadow-[0_0_12px_#00ffe7] flex-shrink-0"></div> | |
| <h3 className="text-[#00ffe7] font-mono text-sm font-medium tracking-wide text-shadow-neon truncate">NEURAL CONTROLS</h3> | |
| <Badge variant="outline" className={`border-[#00ffe7]/50 bg-[#00ffe7]/10 text-xs shadow-[0_0_8px_#00ffe7] flex-shrink-0 ${ | |
| backendConnected === false ? 'text-[#ff6600] border-[#ff6600]/50 bg-[#ff6600]/10' : | |
| isTraining ? 'text-[#39ff14] border-[#39ff14]/50 bg-[#39ff14]/10' : 'text-[#00ffe7]' | |
| }`}> | |
| {backendConnected === false ? 'OFFLINE' : isTraining ? 'TRAINING' : 'READY'} | |
| </Badge> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Backend Offline Banner */} | |
| {backendConnected === false && ( | |
| <div className="bg-[#ff6600]/10 border-y border-[#ff6600]/30 p-3 flex-shrink-0"> | |
| <div className="flex items-center gap-2 min-w-0"> | |
| <span className="text-[#ff6600] text-sm flex-shrink-0">⚠️</span> | |
| <div className="min-w-0 flex-1"> | |
| <p className="text-[#ff6600] font-mono text-xs font-semibold truncate">Training Backend Offline</p> | |
| <p className="text-[#ff6600]/70 font-mono text-xs truncate">Connect to backend server to enable training</p> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| <Tabs defaultValue="neural" className="flex-1 flex flex-col min-h-0 overflow-hidden"> | |
| <TabsList className="grid w-full grid-cols-4 bg-black/60 border-[#00ffe7]/30 mx-3 mt-3 flex-shrink-0"> | |
| <TabsTrigger value="neural" className="text-xs text-[#00ffe7] data-[state=active]:bg-[#00ffe7]/20 truncate">Neural</TabsTrigger> | |
| <TabsTrigger value="hyper" className="text-xs text-[#00ffe7] data-[state=active]:bg-[#00ffe7]/20 truncate">Hyper</TabsTrigger> | |
| <TabsTrigger value="model" className="text-xs text-[#00ffe7] data-[state=active]:bg-[#00ffe7]/20 truncate">Model</TabsTrigger> | |
| <TabsTrigger value="config" className="text-xs text-[#00ffe7] data-[state=active]:bg-[#00ffe7]/20 truncate">Config</TabsTrigger> | |
| </TabsList> | |
| <TabsContent value="neural" className="flex-1 min-h-0 overflow-hidden"> | |
| <ScrollArea className="h-full w-full pr-2"> | |
| <div className="p-3 space-y-4"> | |
| <h4 className="text-[#00ffe7] font-mono text-sm font-medium mb-2 tracking-wide text-shadow-neon">NEURAL TRAINING</h4> | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">AUTO TRAIN</Label> | |
| <Switch className="data-[state=checked]:bg-[#39ff14] data-[state=checked]:shadow-[0_0_8px_#39ff14]" disabled={!backendConnected} /> | |
| </div> | |
| <div className="flex items-center justify-between"> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">EARLY STOPPING</Label> | |
| <Switch className="data-[state=checked]:bg-[#39ff14] data-[state=checked]:shadow-[0_0_8px_#39ff14]" disabled={!backendConnected} /> | |
| </div> | |
| <div className="flex items-center justify-between"> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">GRADIENT CLIPPING</Label> | |
| <Switch className="data-[state=checked]:bg-[#39ff14] data-[state=checked]:shadow-[0_0_8px_#39ff14]" disabled={!backendConnected} /> | |
| </div> | |
| </div> | |
| {/* Real-time Training Stats */} | |
| {isTraining && jobStatus && backendConnected && ( | |
| <div className="mt-4 p-3 border border-[#39ff14]/30 rounded-lg bg-[#39ff14]/5"> | |
| <h5 className="text-[#39ff14] font-mono text-xs tracking-wide mb-2">TRAINING STATUS</h5> | |
| <div className="space-y-1 text-xs font-mono"> | |
| <div className="flex justify-between"> | |
| <span className="text-[#00ffe7]/70">Epoch:</span> | |
| <span className="text-[#39ff14]">{jobStatus.current_epoch}/{jobStatus.total_epochs}</span> | |
| </div> | |
| <div className="flex justify-between"> | |
| <span className="text-[#00ffe7]/70">Accuracy:</span> | |
| <span className="text-[#39ff14]">{(jobStatus.accuracy || 0).toFixed(2)}%</span> | |
| </div> | |
| <div className="flex justify-between"> | |
| <span className="text-[#00ffe7]/70">Loss:</span> | |
| <span className="text-[#39ff14]">{(jobStatus.loss || 0).toFixed(4)}</span> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| </ScrollArea> | |
| </TabsContent> | |
| <TabsContent value="hyper" className="flex-1 min-h-0 overflow-hidden"> | |
| <ScrollArea className="h-full w-full pr-2"> | |
| <div className="p-3 space-y-4"> | |
| <h4 className="text-[#00ffe7] font-mono text-sm font-medium mb-2 tracking-wide text-shadow-neon">HYPERPARAMETERS</h4> | |
| <div className="space-y-3"> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">LEARNING RATE</Label> | |
| <div className="flex items-center space-x-2 mt-1"> | |
| <Slider | |
| value={[formData.learningRate * 1000]} | |
| max={10} | |
| min={0.1} | |
| step={0.1} | |
| className="flex-1" | |
| onValueChange={(value) => updateField('learningRate', value[0] / 1000)} | |
| disabled={!backendConnected} | |
| /> | |
| <Input | |
| className="neon-border glass focus:border-[var(--neon-cyan)] focus:shadow-[0_0_16px_var(--neon-cyan)] w-16 h-6 bg-black border-[#39ff14]/30 text-[#39ff14] text-xs text-center" | |
| value={formData.learningRate} | |
| onChange={(e) => updateField('learningRate', parseFloat(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| </div> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">BATCH SIZE</Label> | |
| <Select value={formData.batchSize.toString()} onValueChange={(value) => updateField('batchSize', parseInt(value))} disabled={!backendConnected}> | |
| <SelectTrigger className="neon-border glass w-full h-8 bg-black border-[#39ff14]/30 text-[#39ff14] text-xs"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent className="bg-black border-[#39ff14]/30"> | |
| <SelectItem value="16">16</SelectItem> | |
| <SelectItem value="32">32</SelectItem> | |
| <SelectItem value="64">64</SelectItem> | |
| <SelectItem value="128">128</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">EPOCHS</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffe7] focus:shadow-[0_0_16px_#00ffe7] w-full h-8 bg-black border-[#39ff14]/30 text-[#39ff14] text-xs" | |
| value={formData.totalEpochs} | |
| onChange={(e) => updateField('totalEpochs', parseInt(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">WEIGHT DECAY</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffe7] focus:shadow-[0_0_16px_#00ffe7] w-full h-8 bg-black border-[#39ff14]/30 text-[#39ff14] text-xs" | |
| value={formData.weightDecay} | |
| onChange={(e) => updateField('weightDecay', parseFloat(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| </ScrollArea> | |
| </TabsContent> | |
| <TabsContent value="model" className="flex-1 min-h-0 overflow-hidden"> | |
| <ScrollArea className="h-full w-full pr-2"> | |
| <div className="p-3 space-y-4"> | |
| <h4 className="text-[#00ffe7] font-mono text-sm font-medium mb-2 tracking-wide text-shadow-neon">MODEL CONFIG</h4> | |
| <div className="space-y-3"> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">MODEL TYPE</Label> | |
| <Select defaultValue="zpe" disabled={!backendConnected}> | |
| <SelectTrigger className="neon-border glass w-full h-8 bg-black border-[#39ff14]/30 text-[#39ff14] text-xs"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent className="bg-black border-[#39ff14]/30"> | |
| <SelectItem value="zpe">ZPE DeepNet</SelectItem> | |
| <SelectItem value="quantum">Quantum Enhanced</SelectItem> | |
| <SelectItem value="classical">Classical CNN</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">OPTIMIZER</Label> | |
| <Select defaultValue="adam" disabled={!backendConnected}> | |
| <SelectTrigger className="neon-border glass w-full h-8 bg-black border-[#39ff14]/30 text-[#39ff14] text-xs"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent className="bg-black border-[#39ff14]/30"> | |
| <SelectItem value="adam">Adam</SelectItem> | |
| <SelectItem value="sgd">SGD</SelectItem> | |
| <SelectItem value="adamw">AdamW</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">LOSS FUNCTION</Label> | |
| <Select defaultValue="crossentropy" disabled={!backendConnected}> | |
| <SelectTrigger className="neon-border glass w-full h-8 bg-black border-[#39ff14]/30 text-[#39ff14] text-xs"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent className="bg-black border-[#39ff14]/30"> | |
| <SelectItem value="crossentropy">Cross Entropy</SelectItem> | |
| <SelectItem value="mse">Mean Squared Error</SelectItem> | |
| <SelectItem value="huber">Huber Loss</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| </div> | |
| </div> | |
| </ScrollArea> | |
| </TabsContent> | |
| <TabsContent value="config" className="flex-1 min-h-0 max-w-full overflow-hidden"> | |
| <Tabs defaultValue="general" className="flex-1 flex flex-col min-h-0 max-w-full"> | |
| <TabsList className="grid w-full grid-cols-3 bg-black/60 border-[#00ffe7]/30 mx-1 mt-2 flex-shrink-0 max-w-full"> | |
| <TabsTrigger value="general" className="text-xs text-[#00ffe7] data-[state=active]:bg-[#00ffe7]/20 truncate px-1">General</TabsTrigger> | |
| <TabsTrigger value="zpe" className="text-xs text-[#00ffe7] data-[state=active]:bg-[#00ffe7]/20 truncate px-1">ZPE</TabsTrigger> | |
| <TabsTrigger value="quantum" className="text-xs text-[#00ffe7] data-[state=active]:bg-[#00ffe7]/20 truncate px-1">Quantum</TabsTrigger> | |
| </TabsList> | |
| <div className="flex-1 min-h-0 overflow-hidden"> | |
| <TabsContent value="general" className="h-full m-0"> | |
| <div className="h-full overflow-y-scroll" style={{ | |
| maxHeight: '500px', | |
| paddingRight: '12px', | |
| scrollbarWidth: 'thick', | |
| scrollbarColor: '#00ffe7 #0a0f1c' | |
| }}> | |
| <div className="p-4 space-y-4 flex flex-col items-center pb-20"> | |
| <div className="w-full max-w-md"> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">MODEL NAME</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffe7] focus:shadow-[0_0_16px_#00ffe7] w-full h-8 bg-black/40 border-[#00ffe7]/30 text-[#00ffe7] text-xs mt-1 font-mono" | |
| value={formData.modelName} | |
| onChange={(e) => updateField('modelName', e.target.value)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| <div className="w-full max-w-md grid grid-cols-2 gap-2"> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">TOTAL EPOCHS</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffe7] focus:shadow-[0_0_16px_#00ffe7] w-full h-8 bg-black/40 border-[#00ffe7]/30 text-[#00ffe7] text-xs mt-1 font-mono text-center" | |
| value={formData.totalEpochs} | |
| onChange={(e) => updateField('totalEpochs', parseInt(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">BATCH SIZE</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffe7] focus:shadow-[0_0_16px_#00ffe7] w-full h-8 bg-black/40 border-[#00ffe7]/30 text-[#00ffe7] text-xs mt-1 font-mono text-center" | |
| value={formData.batchSize} | |
| onChange={(e) => updateField('batchSize', parseInt(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| </div> | |
| <div className="w-full max-w-md grid grid-cols-2 gap-2"> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">LEARNING RATE</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffe7] focus:shadow-[0_0_16px_#00ffe7] w-full h-8 bg-black/40 border-[#00ffe7]/30 text-[#00ffe7] text-xs mt-1 font-mono text-center" | |
| value={formData.learningRate} | |
| onChange={(e) => updateField('learningRate', parseFloat(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| <div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">WEIGHT DECAY</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffe7] focus:shadow-[0_0_16px_#00ffe7] w-full h-8 bg-black/40 border-[#00ffe7]/30 text-[#00ffe7] text-xs mt-1 font-mono text-center" | |
| value={formData.weightDecay} | |
| onChange={(e) => updateField('weightDecay', parseFloat(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| </div> | |
| <div className="w-full max-w-md"> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">BASE CONFIG ID (OPTIONAL)</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffe7] focus:shadow-[0_0_16px_#00ffe7] w-full h-8 bg-black/40 border-[#00ffe7]/30 text-[#00ffe7]/60 text-xs mt-1 font-mono" | |
| value={formData.baseConfigId} | |
| onChange={(e) => updateField('baseConfigId', e.target.value)} | |
| placeholder="e.g., config_123456" | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| <div className="w-full max-w-md flex flex-col gap-2 pt-3 pb-12"> | |
| <Button | |
| className={`neon-button transition-all duration-300 text-xs h-8 font-mono tracking-wide w-full ${ | |
| backendConnected ? | |
| 'bg-[#39ff14]/20 border-[#39ff14]/50 text-[#39ff14] hover:bg-[#39ff14]/30 hover:shadow-[0_0_16px_#39ff14]' : | |
| 'bg-[#666]/20 border-[#666]/50 text-[#666] cursor-not-allowed' | |
| }`} | |
| onClick={handleStartTraining} | |
| disabled={isSubmitting || isTraining || !backendConnected} | |
| > | |
| {!backendConnected ? 'BACKEND OFFLINE' : | |
| isSubmitting ? 'STARTING...' : | |
| isTraining ? 'TRAINING IN PROGRESS' : | |
| 'START NEW TRAINING SESSION'} | |
| </Button> | |
| <Button | |
| variant="outline" | |
| className={`neon-button transition-all duration-300 text-xs h-8 font-mono tracking-wide w-full ${ | |
| backendConnected ? | |
| 'border-[#00ffe7]/50 text-[#00ffe7] bg-black/40 hover:bg-[#00ffe7]/10 hover:shadow-[0_0_16px_#00ffe7]' : | |
| 'border-[#666]/50 text-[#666] bg-black/40 cursor-not-allowed' | |
| }`} | |
| disabled={!formData.baseConfigId || !backendConnected} | |
| > | |
| CONTINUE FROM CHECKPOINT (.PTH) | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| </TabsContent> | |
| <TabsContent value="zpe" className="h-full m-0"> | |
| <div className="h-full overflow-y-scroll" style={{ | |
| maxHeight: '500px', | |
| paddingRight: '12px', | |
| scrollbarWidth: 'thick', | |
| scrollbarColor: '#00ffe7 #0a0f1c' | |
| }}> | |
| <div className="p-4 space-y-6 flex flex-col items-center pb-20"> | |
| <h4 className="text-[#00ffe7] font-mono text-sm font-medium tracking-wide text-shadow-neon text-center">ZPE CONFIGURATION</h4> | |
| <div className="w-full max-w-md space-y-4"> | |
| <div className="flex items-center justify-center gap-2 mb-2"> | |
| <div className="w-2 h-2 bg-[#39ff14] rounded-full animate-pulse shadow-[0_0_8px_#39ff14] flex-shrink-0"></div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">MOMENTUM (6 LAYERS)</Label> | |
| </div> | |
| <div className="grid grid-cols-3 gap-2 w-full"> | |
| {formData.momentumParams.map((value, index) => ( | |
| <div key={index} className="min-w-0"> | |
| <Label className="text-[#00ffe7] font-mono text-xs block mb-1 text-center">L{index + 1}</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#39ff14] focus:shadow-[0_0_8px_#39ff14] w-full h-8 bg-black/40 border-[#39ff14]/30 text-[#39ff14] text-xs font-mono text-center" | |
| value={value} | |
| onChange={(e) => updateZPEParam('momentumParams', index, parseFloat(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="w-full max-w-md space-y-4"> | |
| <div className="flex items-center justify-center gap-2 mb-2"> | |
| <div className="w-2 h-2 bg-[#ff00ff] rounded-full animate-pulse shadow-[0_0_8px_#ff00ff] flex-shrink-0"></div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">STRENGTH (6 LAYERS)</Label> | |
| </div> | |
| <div className="grid grid-cols-3 gap-2 w-full"> | |
| {formData.strengthParams.map((value, index) => ( | |
| <div key={index} className="min-w-0"> | |
| <Label className="text-[#00ffe7] font-mono text-xs block mb-1 text-center">L{index + 1}</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#ff00ff] focus:shadow-[0_0_8px_#ff00ff] w-full h-8 bg-black/40 border-[#ff00ff]/30 text-[#ff00ff] text-xs font-mono text-center" | |
| value={value} | |
| onChange={(e) => updateZPEParam('strengthParams', index, parseFloat(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="w-full max-w-md space-y-4"> | |
| <div className="flex items-center justify-center gap-2 mb-2"> | |
| <div className="w-2 h-2 bg-[#ffa500] rounded-full animate-pulse shadow-[0_0_8px_#ffa500] flex-shrink-0"></div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">NOISE PARAMETERS</Label> | |
| </div> | |
| <div className="grid grid-cols-3 gap-2 w-full"> | |
| {formData.noiseParams.map((value, index) => ( | |
| <div key={index} className="min-w-0"> | |
| <Label className="text-[#00ffe7] font-mono text-xs block mb-1 text-center">N{index + 1}</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#ffa500] focus:shadow-[0_0_8px_#ffa500] w-full h-8 bg-black/40 border-[#ffa500]/30 text-[#ffa500] text-xs font-mono text-center" | |
| value={value} | |
| onChange={(e) => updateZPEParam('noiseParams', index, parseFloat(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="w-full max-w-md space-y-4 pb-12"> | |
| <div className="flex items-center justify-center gap-2 mb-2"> | |
| <div className="w-2 h-2 bg-[#00ffff] rounded-full animate-pulse shadow-[0_0_8px_#00ffff] flex-shrink-0"></div> | |
| <Label className="text-[#39ff14] font-mono text-xs tracking-wide">COUPLING STRENGTH</Label> | |
| </div> | |
| <div className="grid grid-cols-3 gap-2 w-full"> | |
| {formData.couplingParams.map((value, index) => ( | |
| <div key={index} className="min-w-0"> | |
| <Label className="text-[#00ffe7] font-mono text-xs block mb-1 text-center">C{index + 1}</Label> | |
| <Input | |
| className="neon-border glass focus:border-[#00ffff] focus:shadow-[0_0_8px_#00ffff] w-full h-8 bg-black/40 border-[#00ffff]/30 text-[#00ffff] text-xs font-mono text-center" | |
| value={value} | |
| onChange={(e) => updateZPEParam('couplingParams', index, parseFloat(e.target.value) || 0)} | |
| disabled={!backendConnected} | |
| /> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </TabsContent> | |
| <TabsContent value="quantum" className="h-full m-0"> | |
| <div className="h-full overflow-y-scroll" style={{ | |
| maxHeight: '500px', | |
| paddingRight: '12px', | |
| scrollbarWidth: 'thick', | |
| scrollbarColor: '#00ffe7 #0a0f1c' | |
| }}> | |
| <div className="p-4 space-y-4 flex flex-col items-center pb-20"> | |
| <h4 className="text-[#00ffe7] font-mono text-sm font-medium tracking-wide text-shadow-neon text-center">QUANTUM CONFIGURATION</h4> | |
| <div className="flex items-center justify-center h-32"> | |
| <div className="text-center"> | |
| <div className="w-12 h-12 mx-auto mb-3 border border-[#00ffe7]/30 rounded-full flex items-center justify-center"> | |
| <span className="text-[#00ffe7] text-xl">⚛️</span> | |
| </div> | |
| <p className="text-[#00ffe7]/70 font-mono text-xs">Quantum parameters coming soon...</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </TabsContent> | |
| </div> | |
| </Tabs> | |
| </TabsContent> | |
| </Tabs> | |
| </Card> | |
| ); | |
| }; | |
| // Job History Panel Component | |
| export const JobHistoryPanel: React.FC = () => { | |
| return ( | |
| <Card className="neon-card glass h-full w-full bg-black/98 border-[#00ffe7]/30 backdrop-blur-sm flex flex-col" style={{ borderRadius: '12px' }}> | |
| <div className="p-3 border-b border-[#00ffe7]/30 flex-shrink-0"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center space-x-3"> | |
| <div className="w-2 h-2 bg-[#3b82f6] rounded-full"></div> | |
| <h3 className="text-[#9ca3af] font-mono text-sm font-medium tracking-wide">Job History</h3> | |
| </div> | |
| </div> | |
| <div className="text-xs text-[#6b7280] mt-1">View previous, past and ongoing training jobs.</div> | |
| </div> | |
| <ScrollArea className="flex-1 min-h-0"> | |
| <div className="p-3"> | |
| <div className="text-center text-[#9ca3af] text-xs py-8"> | |
| No training jobs found. | |
| </div> | |
| </div> | |
| </ScrollArea> | |
| </Card> | |
| ); | |
| }; |