File size: 3,527 Bytes
ca28016
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import React, { useState } from 'react';
import { Button } from '@/components/ui/button';
import { ZoomIn, ZoomOut } from 'lucide-react';
import {
  ResponsiveContainer,
  LineChart,
  Line,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
  Brush
} from 'recharts';

// Accept metrics as a flat array (metrics_history)
interface ChartPanelProps {
  metrics: any[];
}

const ChartPanel: React.FC<ChartPanelProps> = ({ metrics }) => {
  const [zoom, setZoom] = useState(1);

  const handleZoomIn = () => setZoom(prev => Math.min(prev * 1.2, 5));
  const handleZoomOut = () => setZoom(prev => Math.max(prev / 1.2, 1));
  
  // Use metrics directly as epochs array
  const epochs = Array.isArray(metrics) ? metrics : [];
  const lossData = epochs.map(e => ({ epoch: e.epoch, loss: e.loss, val_loss: e.val_loss }));
  const accData = epochs.map(e => ({ epoch: e.epoch, accuracy: e.accuracy, val_acc: e.val_acc }));

  // Adjust data based on zoom
  const zoomedDataLength = Math.ceil(lossData.length / zoom);
  const startIndex = Math.max(0, lossData.length - zoomedDataLength);
  const zoomedLossData = lossData.slice(startIndex);
  const zoomedAccData = accData.slice(startIndex);

  return (
    <div className="relative flex-grow w-full h-full mt-2 space-y-4">
      {lossData.length > 0 ? (
        <>
          <div className="h-1/2 w-full">
            <ResponsiveContainer>
              <LineChart data={zoomedLossData}>
                <CartesianGrid strokeDasharray="3 3" stroke="#ffffff20" />
                <XAxis dataKey="epoch" stroke="#888888" />
                <YAxis stroke="#888888" />
                <Tooltip contentStyle={{ backgroundColor: '#111', border: '1px solid #555' }} />
                <Legend />
                <Line type="monotone" dataKey="loss" stroke="#8884d8" name="Training Loss" />
                <Line type="monotone" dataKey="val_loss" stroke="#82ca9d" name="Validation Loss" />
                <Brush dataKey="epoch" height={30} stroke="#8884d8" />
              </LineChart>
            </ResponsiveContainer>
          </div>
          <div className="h-1/2 w-full">
            <ResponsiveContainer>
              <LineChart data={zoomedAccData}>
                <CartesianGrid strokeDasharray="3 3" stroke="#ffffff20" />
                <XAxis dataKey="epoch" stroke="#888888" />
                <YAxis stroke="#888888" />
                <Tooltip contentStyle={{ backgroundColor: '#111', border: '1px solid #555' }} />
                <Legend />
                <Line type="monotone" dataKey="accuracy" stroke="#ffc658" name="Training Accuracy" />
                <Line type="monotone" dataKey="val_acc" stroke="#ff8042" name="Validation Accuracy" />
                <Brush dataKey="epoch" height={30} stroke="#ffc658" />
              </LineChart>
            </ResponsiveContainer>
          </div>
        </>
      ) : (
        <div className="flex items-center justify-center h-full text-gray-400">
          <p>No training data available yet. Start a training job to see metrics.</p>
        </div>
      )}
      <div className="absolute bottom-8 right-8 z-20 flex items-center gap-2">
        <Button onClick={handleZoomOut} variant="outline" size="icon" className="h-8 w-8 btn-neon">
          <ZoomOut className="h-4 w-4" />
        </Button>
        <Button onClick={handleZoomIn} variant="outline" size="icon" className="h-8 w-8 btn-neon">
          <ZoomIn className="h-4 w-4" />
        </Button>
      </div>
    </div>
  );
};

export default ChartPanel;