Spaces:
Running
Running
implement victors viewer which is wayy better
Browse files- .DS_Store +0 -0
- package-lock.json +0 -0
- src/components/Logo.tsx +24 -0
- src/components/landing/ActionList.tsx +57 -0
- src/components/landing/LandingHeader.tsx +9 -0
- src/components/landing/PermissionModal.tsx +59 -0
- src/components/landing/RecordingModal.tsx +221 -0
- src/components/landing/RobotModelSelector.tsx +39 -0
- src/components/landing/TeleoperationModal.tsx +166 -0
- src/components/landing/types.ts +7 -0
- src/components/training/ConfigurationTab.tsx +28 -0
- src/components/training/MonitoringTab.tsx +34 -0
- src/components/training/TrainingControls.tsx +60 -0
- src/components/training/TrainingHeader.tsx +59 -0
- src/components/training/TrainingTabs.tsx +44 -0
- src/components/training/config/AdvancedConfig.tsx +56 -0
- src/components/training/config/DatasetConfig.tsx +79 -0
- src/components/training/config/EnvEvalConfig.tsx +121 -0
- src/components/training/config/LoggingConfig.tsx +119 -0
- src/components/training/config/OptimizerConfig.tsx +118 -0
- src/components/training/config/PolicyConfig.tsx +85 -0
- src/components/training/config/TrainingParams.tsx +89 -0
- src/components/training/config/WandbConfig.tsx +130 -0
- src/components/training/monitoring/MonitoringStats.tsx +85 -0
- src/components/training/monitoring/TrainingLogs.tsx +50 -0
- src/components/training/types.ts +87 -0
- src/contexts/ThemeContext.tsx +64 -0
- src/pages/EditDataset.tsx +15 -0
.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
src/components/Logo.tsx
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { cn } from '@/lib/utils';
|
4 |
+
|
5 |
+
interface LogoProps extends React.HTMLAttributes<HTMLDivElement> {
|
6 |
+
iconOnly?: boolean;
|
7 |
+
}
|
8 |
+
|
9 |
+
const Logo: React.FC<LogoProps> = ({ className, iconOnly = false }) => {
|
10 |
+
return (
|
11 |
+
<div className={cn("flex items-center gap-2", className)}>
|
12 |
+
<img
|
13 |
+
src="/lovable-uploads/5e648747-34b7-4d8f-93fd-4dbd00aeeefc.png"
|
14 |
+
alt="LiveLab Logo"
|
15 |
+
className="h-8 w-8"
|
16 |
+
/>
|
17 |
+
{!iconOnly && (
|
18 |
+
<span className="text-xl font-bold text-white">LiveLab</span>
|
19 |
+
)}
|
20 |
+
</div>
|
21 |
+
);
|
22 |
+
};
|
23 |
+
|
24 |
+
export default Logo;
|
src/components/landing/ActionList.tsx
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Button } from "@/components/ui/button";
|
4 |
+
import { ArrowRight } from "lucide-react";
|
5 |
+
import { Action } from './types';
|
6 |
+
|
7 |
+
interface ActionListProps {
|
8 |
+
actions: Action[];
|
9 |
+
robotModel: string;
|
10 |
+
}
|
11 |
+
|
12 |
+
const ActionList: React.FC<ActionListProps> = ({ actions, robotModel }) => {
|
13 |
+
const isLeKiwi = robotModel === "LeKiwi";
|
14 |
+
const isDisabled = !robotModel || isLeKiwi;
|
15 |
+
|
16 |
+
return (
|
17 |
+
<div className="pt-6">
|
18 |
+
{!robotModel && (
|
19 |
+
<p className="text-center text-gray-400 mb-4">
|
20 |
+
Please select a robot model to continue.
|
21 |
+
</p>
|
22 |
+
)}
|
23 |
+
{isLeKiwi && (
|
24 |
+
<p className="text-center text-yellow-500 mb-4">
|
25 |
+
LeKiwi model is not yet supported. Please select another model to continue.
|
26 |
+
</p>
|
27 |
+
)}
|
28 |
+
<div className="space-y-4">
|
29 |
+
{actions.map((action, index) => (
|
30 |
+
<div
|
31 |
+
key={index}
|
32 |
+
className={`flex items-center justify-between p-4 bg-gray-800 rounded-lg border border-gray-700 transition-opacity ${
|
33 |
+
isDisabled ? "opacity-50" : ""
|
34 |
+
}`}
|
35 |
+
>
|
36 |
+
<div>
|
37 |
+
<h3 className="font-semibold text-lg">{action.title}</h3>
|
38 |
+
<p className="text-gray-400 text-sm">
|
39 |
+
{action.description}
|
40 |
+
</p>
|
41 |
+
</div>
|
42 |
+
<Button
|
43 |
+
onClick={action.handler}
|
44 |
+
size="icon"
|
45 |
+
className={`${action.color} text-white`}
|
46 |
+
disabled={isDisabled}
|
47 |
+
>
|
48 |
+
<ArrowRight className="w-5 h-5" />
|
49 |
+
</Button>
|
50 |
+
</div>
|
51 |
+
))}
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
);
|
55 |
+
};
|
56 |
+
|
57 |
+
export default ActionList;
|
src/components/landing/LandingHeader.tsx
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
const LandingHeader = () => {
|
3 |
+
return <div className="text-center space-y-4 max-w-lg w-full">
|
4 |
+
<img src="/lovable-uploads/5e648747-34b7-4d8f-93fd-4dbd00aeeefc.png" alt="LiveLab Logo" className="mx-auto h-20 w-20" />
|
5 |
+
<h1 className="text-5xl font-bold tracking-tight">LiveLab</h1>
|
6 |
+
<p className="text-xl text-gray-400">LeRobot but on HFSpace.</p>
|
7 |
+
</div>;
|
8 |
+
};
|
9 |
+
export default LandingHeader;
|
src/components/landing/PermissionModal.tsx
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from "react";
|
3 |
+
import { Button } from "@/components/ui/button";
|
4 |
+
import {
|
5 |
+
Dialog,
|
6 |
+
DialogContent,
|
7 |
+
DialogHeader,
|
8 |
+
DialogTitle,
|
9 |
+
DialogDescription,
|
10 |
+
} from "@/components/ui/dialog";
|
11 |
+
import { Camera, Mic } from "lucide-react";
|
12 |
+
|
13 |
+
interface PermissionModalProps {
|
14 |
+
open: boolean;
|
15 |
+
onOpenChange: (open: boolean) => void;
|
16 |
+
onPermissionsResult: (allow: boolean) => void;
|
17 |
+
}
|
18 |
+
|
19 |
+
const PermissionModal: React.FC<PermissionModalProps> = ({ open, onOpenChange, onPermissionsResult }) => {
|
20 |
+
return (
|
21 |
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
22 |
+
<DialogContent className="bg-gray-900 border-gray-800 text-white sm:max-w-[480px] p-8">
|
23 |
+
<DialogHeader>
|
24 |
+
<div className="flex justify-center items-center gap-4 mb-4">
|
25 |
+
<Camera className="w-8 h-8 text-orange-500" />
|
26 |
+
<Mic className="w-8 h-8 text-orange-500" />
|
27 |
+
</div>
|
28 |
+
<DialogTitle className="text-white text-center text-2xl font-bold">
|
29 |
+
Enable Camera & Microphone
|
30 |
+
</DialogTitle>
|
31 |
+
</DialogHeader>
|
32 |
+
<div className="text-center space-y-6 py-4">
|
33 |
+
<DialogDescription className="text-gray-400 text-base leading-relaxed">
|
34 |
+
LiveLab requires access to your camera and microphone for a fully
|
35 |
+
immersive telepresence experience. This enables real-time video
|
36 |
+
feedback and voice command capabilities.
|
37 |
+
</DialogDescription>
|
38 |
+
<div className="flex flex-col sm:flex-row gap-4 justify-center pt-2">
|
39 |
+
<Button
|
40 |
+
onClick={() => onPermissionsResult(true)}
|
41 |
+
className="w-full sm:w-auto bg-orange-500 hover:bg-orange-600 text-white px-10 py-6 text-lg transition-all shadow-md shadow-orange-500/30 hover:shadow-lg hover:shadow-orange-500/40"
|
42 |
+
>
|
43 |
+
Allow Access
|
44 |
+
</Button>
|
45 |
+
<Button
|
46 |
+
onClick={() => onPermissionsResult(false)}
|
47 |
+
variant="outline"
|
48 |
+
className="w-full sm:w-auto border-gray-500 hover:border-gray-200 px-10 py-6 text-lg text-zinc-500 bg-zinc-900 hover:bg-zinc-800"
|
49 |
+
>
|
50 |
+
Continue without
|
51 |
+
</Button>
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
</DialogContent>
|
55 |
+
</Dialog>
|
56 |
+
);
|
57 |
+
};
|
58 |
+
|
59 |
+
export default PermissionModal;
|
src/components/landing/RecordingModal.tsx
ADDED
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Button } from "@/components/ui/button";
|
4 |
+
import { Input } from "@/components/ui/input";
|
5 |
+
import { Label } from "@/components/ui/label";
|
6 |
+
import {
|
7 |
+
Select,
|
8 |
+
SelectContent,
|
9 |
+
SelectItem,
|
10 |
+
SelectTrigger,
|
11 |
+
SelectValue,
|
12 |
+
} from "@/components/ui/select";
|
13 |
+
import {
|
14 |
+
Dialog,
|
15 |
+
DialogContent,
|
16 |
+
DialogHeader,
|
17 |
+
DialogTitle,
|
18 |
+
DialogDescription,
|
19 |
+
} from "@/components/ui/dialog";
|
20 |
+
|
21 |
+
interface RecordingModalProps {
|
22 |
+
open: boolean;
|
23 |
+
onOpenChange: (open: boolean) => void;
|
24 |
+
leaderPort: string;
|
25 |
+
setLeaderPort: (value: string) => void;
|
26 |
+
followerPort: string;
|
27 |
+
setFollowerPort: (value: string) => void;
|
28 |
+
leaderConfig: string;
|
29 |
+
setLeaderConfig: (value: string) => void;
|
30 |
+
followerConfig: string;
|
31 |
+
setFollowerConfig: (value: string) => void;
|
32 |
+
leaderConfigs: string[];
|
33 |
+
followerConfigs: string[];
|
34 |
+
datasetRepoId: string;
|
35 |
+
setDatasetRepoId: (value: string) => void;
|
36 |
+
singleTask: string;
|
37 |
+
setSingleTask: (value: string) => void;
|
38 |
+
numEpisodes: number;
|
39 |
+
setNumEpisodes: (value: number) => void;
|
40 |
+
isLoadingConfigs: boolean;
|
41 |
+
onStart: () => void;
|
42 |
+
}
|
43 |
+
|
44 |
+
const RecordingModal: React.FC<RecordingModalProps> = ({
|
45 |
+
open,
|
46 |
+
onOpenChange,
|
47 |
+
leaderPort,
|
48 |
+
setLeaderPort,
|
49 |
+
followerPort,
|
50 |
+
setFollowerPort,
|
51 |
+
leaderConfig,
|
52 |
+
setLeaderConfig,
|
53 |
+
followerConfig,
|
54 |
+
setFollowerConfig,
|
55 |
+
leaderConfigs,
|
56 |
+
followerConfigs,
|
57 |
+
datasetRepoId,
|
58 |
+
setDatasetRepoId,
|
59 |
+
singleTask,
|
60 |
+
setSingleTask,
|
61 |
+
numEpisodes,
|
62 |
+
setNumEpisodes,
|
63 |
+
isLoadingConfigs,
|
64 |
+
onStart,
|
65 |
+
}) => {
|
66 |
+
return (
|
67 |
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
68 |
+
<DialogContent className="bg-gray-900 border-gray-800 text-white sm:max-w-[600px] p-8 max-h-[90vh] overflow-y-auto">
|
69 |
+
<DialogHeader>
|
70 |
+
<div className="flex justify-center items-center gap-4 mb-4">
|
71 |
+
<div className="w-8 h-8 bg-red-500 rounded-full flex items-center justify-center">
|
72 |
+
<span className="text-white font-bold text-sm">REC</span>
|
73 |
+
</div>
|
74 |
+
</div>
|
75 |
+
<DialogTitle className="text-white text-center text-2xl font-bold">
|
76 |
+
Configure Recording
|
77 |
+
</DialogTitle>
|
78 |
+
</DialogHeader>
|
79 |
+
<div className="space-y-6 py-4">
|
80 |
+
<DialogDescription className="text-gray-400 text-base leading-relaxed text-center">
|
81 |
+
Configure the robot arm settings and dataset parameters for
|
82 |
+
recording.
|
83 |
+
</DialogDescription>
|
84 |
+
|
85 |
+
<div className="grid grid-cols-1 gap-6">
|
86 |
+
<div className="space-y-4">
|
87 |
+
<h3 className="text-lg font-semibold text-white border-b border-gray-700 pb-2">
|
88 |
+
Robot Configuration
|
89 |
+
</h3>
|
90 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
91 |
+
<div className="space-y-2">
|
92 |
+
<Label htmlFor="recordLeaderPort" className="text-sm font-medium text-gray-300">
|
93 |
+
Leader Port
|
94 |
+
</Label>
|
95 |
+
<Input
|
96 |
+
id="recordLeaderPort"
|
97 |
+
value={leaderPort}
|
98 |
+
onChange={(e) => setLeaderPort(e.target.value)}
|
99 |
+
placeholder="/dev/tty.usbmodem5A460816421"
|
100 |
+
className="bg-gray-800 border-gray-700 text-white"
|
101 |
+
/>
|
102 |
+
</div>
|
103 |
+
<div className="space-y-2">
|
104 |
+
<Label htmlFor="recordLeaderConfig" className="text-sm font-medium text-gray-300">
|
105 |
+
Leader Calibration Config
|
106 |
+
</Label>
|
107 |
+
<Select value={leaderConfig} onValueChange={setLeaderConfig}>
|
108 |
+
<SelectTrigger className="bg-gray-800 border-gray-700 text-white">
|
109 |
+
<SelectValue placeholder={isLoadingConfigs ? "Loading configs..." : "Select leader config"} />
|
110 |
+
</SelectTrigger>
|
111 |
+
<SelectContent className="bg-gray-800 border-gray-700">
|
112 |
+
{leaderConfigs.map((config) => (
|
113 |
+
<SelectItem key={config} value={config} className="text-white hover:bg-gray-700">
|
114 |
+
{config}
|
115 |
+
</SelectItem>
|
116 |
+
))}
|
117 |
+
</SelectContent>
|
118 |
+
</Select>
|
119 |
+
</div>
|
120 |
+
<div className="space-y-2">
|
121 |
+
<Label htmlFor="recordFollowerPort" className="text-sm font-medium text-gray-300">
|
122 |
+
Follower Port
|
123 |
+
</Label>
|
124 |
+
<Input
|
125 |
+
id="recordFollowerPort"
|
126 |
+
value={followerPort}
|
127 |
+
onChange={(e) => setFollowerPort(e.target.value)}
|
128 |
+
placeholder="/dev/tty.usbmodem5A460816621"
|
129 |
+
className="bg-gray-800 border-gray-700 text-white"
|
130 |
+
/>
|
131 |
+
</div>
|
132 |
+
<div className="space-y-2">
|
133 |
+
<Label htmlFor="recordFollowerConfig" className="text-sm font-medium text-gray-300">
|
134 |
+
Follower Calibration Config
|
135 |
+
</Label>
|
136 |
+
<Select value={followerConfig} onValueChange={setFollowerConfig}>
|
137 |
+
<SelectTrigger className="bg-gray-800 border-gray-700 text-white">
|
138 |
+
<SelectValue placeholder={isLoadingConfigs ? "Loading configs..." : "Select follower config"} />
|
139 |
+
</SelectTrigger>
|
140 |
+
<SelectContent className="bg-gray-800 border-gray-700">
|
141 |
+
{followerConfigs.map((config) => (
|
142 |
+
<SelectItem key={config} value={config} className="text-white hover:bg-gray-700">
|
143 |
+
{config}
|
144 |
+
</SelectItem>
|
145 |
+
))}
|
146 |
+
</SelectContent>
|
147 |
+
</Select>
|
148 |
+
</div>
|
149 |
+
</div>
|
150 |
+
</div>
|
151 |
+
|
152 |
+
<div className="space-y-4">
|
153 |
+
<h3 className="text-lg font-semibold text-white border-b border-gray-700 pb-2">
|
154 |
+
Dataset Configuration
|
155 |
+
</h3>
|
156 |
+
<div className="grid grid-cols-1 gap-4">
|
157 |
+
<div className="space-y-2">
|
158 |
+
<Label htmlFor="datasetRepoId" className="text-sm font-medium text-gray-300">
|
159 |
+
Dataset Repository ID *
|
160 |
+
</Label>
|
161 |
+
<Input
|
162 |
+
id="datasetRepoId"
|
163 |
+
value={datasetRepoId}
|
164 |
+
onChange={(e) => setDatasetRepoId(e.target.value)}
|
165 |
+
placeholder="username/dataset_name"
|
166 |
+
className="bg-gray-800 border-gray-700 text-white"
|
167 |
+
/>
|
168 |
+
</div>
|
169 |
+
<div className="space-y-2">
|
170 |
+
<Label htmlFor="singleTask" className="text-sm font-medium text-gray-300">
|
171 |
+
Task Name *
|
172 |
+
</Label>
|
173 |
+
<Input
|
174 |
+
id="singleTask"
|
175 |
+
value={singleTask}
|
176 |
+
onChange={(e) => setSingleTask(e.target.value)}
|
177 |
+
placeholder="e.g., pick_and_place"
|
178 |
+
className="bg-gray-800 border-gray-700 text-white"
|
179 |
+
/>
|
180 |
+
</div>
|
181 |
+
<div className="space-y-2">
|
182 |
+
<Label htmlFor="numEpisodes" className="text-sm font-medium text-gray-300">
|
183 |
+
Number of Episodes
|
184 |
+
</Label>
|
185 |
+
<Input
|
186 |
+
id="numEpisodes"
|
187 |
+
type="number"
|
188 |
+
min="1"
|
189 |
+
max="100"
|
190 |
+
value={numEpisodes}
|
191 |
+
onChange={(e) => setNumEpisodes(parseInt(e.target.value))}
|
192 |
+
className="bg-gray-800 border-gray-700 text-white"
|
193 |
+
/>
|
194 |
+
</div>
|
195 |
+
</div>
|
196 |
+
</div>
|
197 |
+
</div>
|
198 |
+
|
199 |
+
<div className="flex flex-col sm:flex-row gap-4 justify-center pt-4">
|
200 |
+
<Button
|
201 |
+
onClick={onStart}
|
202 |
+
className="w-full sm:w-auto bg-red-500 hover:bg-red-600 text-white px-10 py-6 text-lg transition-all shadow-md shadow-red-500/30 hover:shadow-lg hover:shadow-red-500/40"
|
203 |
+
disabled={isLoadingConfigs}
|
204 |
+
>
|
205 |
+
Start Recording
|
206 |
+
</Button>
|
207 |
+
<Button
|
208 |
+
onClick={() => onOpenChange(false)}
|
209 |
+
variant="outline"
|
210 |
+
className="w-full sm:w-auto border-gray-500 hover:border-gray-200 px-10 py-6 text-lg text-zinc-500 bg-zinc-900 hover:bg-zinc-800"
|
211 |
+
>
|
212 |
+
Cancel
|
213 |
+
</Button>
|
214 |
+
</div>
|
215 |
+
</div>
|
216 |
+
</DialogContent>
|
217 |
+
</Dialog>
|
218 |
+
);
|
219 |
+
};
|
220 |
+
|
221 |
+
export default RecordingModal;
|
src/components/landing/RobotModelSelector.tsx
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
3 |
+
import { Label } from "@/components/ui/label";
|
4 |
+
interface RobotModelSelectorProps {
|
5 |
+
robotModel: string;
|
6 |
+
onValueChange: (model: string) => void;
|
7 |
+
}
|
8 |
+
const RobotModelSelector: React.FC<RobotModelSelectorProps> = ({
|
9 |
+
robotModel,
|
10 |
+
onValueChange
|
11 |
+
}) => {
|
12 |
+
return <>
|
13 |
+
<h2 className="text-2xl font-semibold text-center text-white">
|
14 |
+
Select Robot Model
|
15 |
+
</h2>
|
16 |
+
<RadioGroup value={robotModel} onValueChange={onValueChange} className="space-y-2">
|
17 |
+
<div>
|
18 |
+
<RadioGroupItem value="SO100" id="so100" className="sr-only" />
|
19 |
+
<Label htmlFor="so100" className="flex items-center space-x-4 p-4 rounded-lg bg-gray-800 border border-gray-700 cursor-pointer transition-all">
|
20 |
+
<span className="w-6 h-6 rounded-full border-2 border-gray-500 flex items-center justify-center">
|
21 |
+
{robotModel === "SO100" && <span className="w-3 h-3 rounded-full bg-orange-500" />}
|
22 |
+
</span>
|
23 |
+
<span className="text-lg flex-1">SO100/SO101
|
24 |
+
</span>
|
25 |
+
</Label>
|
26 |
+
</div>
|
27 |
+
<div>
|
28 |
+
<RadioGroupItem value="LeKiwi" id="lekiwi" className="sr-only" />
|
29 |
+
<Label htmlFor="lekiwi" className="flex items-center space-x-4 p-4 rounded-lg bg-gray-800 border border-gray-700 cursor-pointer transition-all">
|
30 |
+
<span className="w-6 h-6 rounded-full border-2 border-gray-500 flex items-center justify-center">
|
31 |
+
{robotModel === "LeKiwi" && <span className="w-3 h-3 rounded-full bg-orange-500" />}
|
32 |
+
</span>
|
33 |
+
<span className="text-lg flex-1">LeKiwi</span>
|
34 |
+
</Label>
|
35 |
+
</div>
|
36 |
+
</RadioGroup>
|
37 |
+
</>;
|
38 |
+
};
|
39 |
+
export default RobotModelSelector;
|
src/components/landing/TeleoperationModal.tsx
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Button } from "@/components/ui/button";
|
4 |
+
import { Input } from "@/components/ui/input";
|
5 |
+
import { Label } from "@/components/ui/label";
|
6 |
+
import {
|
7 |
+
Select,
|
8 |
+
SelectContent,
|
9 |
+
SelectItem,
|
10 |
+
SelectTrigger,
|
11 |
+
SelectValue,
|
12 |
+
} from "@/components/ui/select";
|
13 |
+
import {
|
14 |
+
Dialog,
|
15 |
+
DialogContent,
|
16 |
+
DialogHeader,
|
17 |
+
DialogTitle,
|
18 |
+
DialogDescription,
|
19 |
+
} from "@/components/ui/dialog";
|
20 |
+
import { Settings } from "lucide-react";
|
21 |
+
|
22 |
+
interface TeleoperationModalProps {
|
23 |
+
open: boolean;
|
24 |
+
onOpenChange: (open: boolean) => void;
|
25 |
+
leaderPort: string;
|
26 |
+
setLeaderPort: (value: string) => void;
|
27 |
+
followerPort: string;
|
28 |
+
setFollowerPort: (value: string) => void;
|
29 |
+
leaderConfig: string;
|
30 |
+
setLeaderConfig: (value: string) => void;
|
31 |
+
followerConfig: string;
|
32 |
+
setFollowerConfig: (value: string) => void;
|
33 |
+
leaderConfigs: string[];
|
34 |
+
followerConfigs: string[];
|
35 |
+
isLoadingConfigs: boolean;
|
36 |
+
onStart: () => void;
|
37 |
+
}
|
38 |
+
|
39 |
+
const TeleoperationModal: React.FC<TeleoperationModalProps> = ({
|
40 |
+
open,
|
41 |
+
onOpenChange,
|
42 |
+
leaderPort,
|
43 |
+
setLeaderPort,
|
44 |
+
followerPort,
|
45 |
+
setFollowerPort,
|
46 |
+
leaderConfig,
|
47 |
+
setLeaderConfig,
|
48 |
+
followerConfig,
|
49 |
+
setFollowerConfig,
|
50 |
+
leaderConfigs,
|
51 |
+
followerConfigs,
|
52 |
+
isLoadingConfigs,
|
53 |
+
onStart,
|
54 |
+
}) => {
|
55 |
+
return (
|
56 |
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
57 |
+
<DialogContent className="bg-gray-900 border-gray-800 text-white sm:max-w-[600px] p-8">
|
58 |
+
<DialogHeader>
|
59 |
+
<div className="flex justify-center items-center gap-4 mb-4">
|
60 |
+
<Settings className="w-8 h-8 text-yellow-500" />
|
61 |
+
</div>
|
62 |
+
<DialogTitle className="text-white text-center text-2xl font-bold">
|
63 |
+
Configure Teleoperation
|
64 |
+
</DialogTitle>
|
65 |
+
</DialogHeader>
|
66 |
+
<div className="space-y-6 py-4">
|
67 |
+
<DialogDescription className="text-gray-400 text-base leading-relaxed text-center">
|
68 |
+
Configure the robot arm ports and calibration settings for
|
69 |
+
teleoperation.
|
70 |
+
</DialogDescription>
|
71 |
+
|
72 |
+
<div className="grid grid-cols-1 gap-6">
|
73 |
+
<div className="space-y-2">
|
74 |
+
<Label htmlFor="leaderPort" className="text-sm font-medium text-gray-300">
|
75 |
+
Leader Port
|
76 |
+
</Label>
|
77 |
+
<Input
|
78 |
+
id="leaderPort"
|
79 |
+
value={leaderPort}
|
80 |
+
onChange={(e) => setLeaderPort(e.target.value)}
|
81 |
+
placeholder="/dev/tty.usbmodem5A460816421"
|
82 |
+
className="bg-gray-800 border-gray-700 text-white"
|
83 |
+
/>
|
84 |
+
</div>
|
85 |
+
|
86 |
+
<div className="space-y-2">
|
87 |
+
<Label htmlFor="leaderConfig" className="text-sm font-medium text-gray-300">
|
88 |
+
Leader Calibration Config
|
89 |
+
</Label>
|
90 |
+
<Select value={leaderConfig} onValueChange={setLeaderConfig}>
|
91 |
+
<SelectTrigger className="bg-gray-800 border-gray-700 text-white">
|
92 |
+
<SelectValue
|
93 |
+
placeholder={
|
94 |
+
isLoadingConfigs ? "Loading configs..." : "Select leader config"
|
95 |
+
}
|
96 |
+
/>
|
97 |
+
</SelectTrigger>
|
98 |
+
<SelectContent className="bg-gray-800 border-gray-700">
|
99 |
+
{leaderConfigs.map((config) => (
|
100 |
+
<SelectItem key={config} value={config} className="text-white hover:bg-gray-700">
|
101 |
+
{config}
|
102 |
+
</SelectItem>
|
103 |
+
))}
|
104 |
+
</SelectContent>
|
105 |
+
</Select>
|
106 |
+
</div>
|
107 |
+
|
108 |
+
<div className="space-y-2">
|
109 |
+
<Label htmlFor="followerPort" className="text-sm font-medium text-gray-300">
|
110 |
+
Follower Port
|
111 |
+
</Label>
|
112 |
+
<Input
|
113 |
+
id="followerPort"
|
114 |
+
value={followerPort}
|
115 |
+
onChange={(e) => setFollowerPort(e.target.value)}
|
116 |
+
placeholder="/dev/tty.usbmodem5A460816621"
|
117 |
+
className="bg-gray-800 border-gray-700 text-white"
|
118 |
+
/>
|
119 |
+
</div>
|
120 |
+
|
121 |
+
<div className="space-y-2">
|
122 |
+
<Label htmlFor="followerConfig" className="text-sm font-medium text-gray-300">
|
123 |
+
Follower Calibration Config
|
124 |
+
</Label>
|
125 |
+
<Select value={followerConfig} onValueChange={setFollowerConfig}>
|
126 |
+
<SelectTrigger className="bg-gray-800 border-gray-700 text-white">
|
127 |
+
<SelectValue
|
128 |
+
placeholder={
|
129 |
+
isLoadingConfigs ? "Loading configs..." : "Select follower config"
|
130 |
+
}
|
131 |
+
/>
|
132 |
+
</SelectTrigger>
|
133 |
+
<SelectContent className="bg-gray-800 border-gray-700">
|
134 |
+
{followerConfigs.map((config) => (
|
135 |
+
<SelectItem key={config} value={config} className="text-white hover:bg-gray-700">
|
136 |
+
{config}
|
137 |
+
</SelectItem>
|
138 |
+
))}
|
139 |
+
</SelectContent>
|
140 |
+
</Select>
|
141 |
+
</div>
|
142 |
+
</div>
|
143 |
+
|
144 |
+
<div className="flex flex-col sm:flex-row gap-4 justify-center pt-4">
|
145 |
+
<Button
|
146 |
+
onClick={onStart}
|
147 |
+
className="w-full sm:w-auto bg-yellow-500 hover:bg-yellow-600 text-white px-10 py-6 text-lg transition-all shadow-md shadow-yellow-500/30 hover:shadow-lg hover:shadow-yellow-500/40"
|
148 |
+
disabled={isLoadingConfigs}
|
149 |
+
>
|
150 |
+
Start Teleoperation
|
151 |
+
</Button>
|
152 |
+
<Button
|
153 |
+
onClick={() => onOpenChange(false)}
|
154 |
+
variant="outline"
|
155 |
+
className="w-full sm:w-auto border-gray-500 hover:border-gray-200 px-10 py-6 text-lg text-zinc-500 bg-zinc-900 hover:bg-zinc-800"
|
156 |
+
>
|
157 |
+
Cancel
|
158 |
+
</Button>
|
159 |
+
</div>
|
160 |
+
</div>
|
161 |
+
</DialogContent>
|
162 |
+
</Dialog>
|
163 |
+
);
|
164 |
+
};
|
165 |
+
|
166 |
+
export default TeleoperationModal;
|
src/components/landing/types.ts
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
export interface Action {
|
3 |
+
title: string;
|
4 |
+
description: string;
|
5 |
+
handler: () => void;
|
6 |
+
color: string;
|
7 |
+
}
|
src/components/training/ConfigurationTab.tsx
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import DatasetConfig from './config/DatasetConfig';
|
4 |
+
import PolicyConfig from './config/PolicyConfig';
|
5 |
+
import TrainingParams from './config/TrainingParams';
|
6 |
+
import OptimizerConfig from './config/OptimizerConfig';
|
7 |
+
import LoggingConfig from './config/LoggingConfig';
|
8 |
+
import WandbConfig from './config/WandbConfig';
|
9 |
+
import EnvEvalConfig from './config/EnvEvalConfig';
|
10 |
+
import AdvancedConfig from './config/AdvancedConfig';
|
11 |
+
import { ConfigComponentProps } from './types';
|
12 |
+
|
13 |
+
const ConfigurationTab: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
14 |
+
return (
|
15 |
+
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6">
|
16 |
+
<DatasetConfig config={config} updateConfig={updateConfig} />
|
17 |
+
<PolicyConfig config={config} updateConfig={updateConfig} />
|
18 |
+
<TrainingParams config={config} updateConfig={updateConfig} />
|
19 |
+
<OptimizerConfig config={config} updateConfig={updateConfig} />
|
20 |
+
<LoggingConfig config={config} updateConfig={updateConfig} />
|
21 |
+
<WandbConfig config={config} updateConfig={updateConfig} />
|
22 |
+
<EnvEvalConfig config={config} updateConfig={updateConfig} />
|
23 |
+
<AdvancedConfig config={config} updateConfig={updateConfig} />
|
24 |
+
</div>
|
25 |
+
);
|
26 |
+
};
|
27 |
+
|
28 |
+
export default ConfigurationTab;
|
src/components/training/MonitoringTab.tsx
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import MonitoringStats from './monitoring/MonitoringStats';
|
4 |
+
import TrainingLogs from './monitoring/TrainingLogs';
|
5 |
+
import { TrainingStatus, LogEntry } from './types';
|
6 |
+
|
7 |
+
interface MonitoringTabProps {
|
8 |
+
trainingStatus: TrainingStatus;
|
9 |
+
logs: LogEntry[];
|
10 |
+
logContainerRef: React.RefObject<HTMLDivElement>;
|
11 |
+
getProgressPercentage: () => number;
|
12 |
+
formatTime: (seconds: number) => string;
|
13 |
+
}
|
14 |
+
|
15 |
+
const MonitoringTab: React.FC<MonitoringTabProps> = ({
|
16 |
+
trainingStatus,
|
17 |
+
logs,
|
18 |
+
logContainerRef,
|
19 |
+
getProgressPercentage,
|
20 |
+
formatTime
|
21 |
+
}) => {
|
22 |
+
return (
|
23 |
+
<div className="space-y-6">
|
24 |
+
<MonitoringStats
|
25 |
+
trainingStatus={trainingStatus}
|
26 |
+
getProgressPercentage={getProgressPercentage}
|
27 |
+
formatTime={formatTime}
|
28 |
+
/>
|
29 |
+
<TrainingLogs logs={logs} logContainerRef={logContainerRef} />
|
30 |
+
</div>
|
31 |
+
);
|
32 |
+
};
|
33 |
+
|
34 |
+
export default MonitoringTab;
|
src/components/training/TrainingControls.tsx
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Button } from '@/components/ui/button';
|
4 |
+
import { Loader2, Play, Square } from 'lucide-react';
|
5 |
+
import { TrainingConfig, TrainingStatus } from './types';
|
6 |
+
|
7 |
+
interface TrainingControlsProps {
|
8 |
+
trainingStatus: TrainingStatus;
|
9 |
+
isStartingTraining: boolean;
|
10 |
+
trainingConfig: TrainingConfig;
|
11 |
+
handleStartTraining: () => void;
|
12 |
+
handleStopTraining: () => void;
|
13 |
+
}
|
14 |
+
|
15 |
+
const TrainingControls: React.FC<TrainingControlsProps> = ({
|
16 |
+
trainingStatus,
|
17 |
+
isStartingTraining,
|
18 |
+
trainingConfig,
|
19 |
+
handleStartTraining,
|
20 |
+
handleStopTraining,
|
21 |
+
}) => {
|
22 |
+
return (
|
23 |
+
<div className="fixed bottom-6 right-6 flex gap-3">
|
24 |
+
{!trainingStatus.training_active ? (
|
25 |
+
<Button
|
26 |
+
onClick={handleStartTraining}
|
27 |
+
disabled={
|
28 |
+
isStartingTraining || !trainingConfig.dataset_repo_id.trim()
|
29 |
+
}
|
30 |
+
size="lg"
|
31 |
+
className="bg-green-500 hover:bg-green-600 text-white font-semibold px-6 py-3 text-base rounded-full shadow-lg transition-all hover:scale-105"
|
32 |
+
>
|
33 |
+
{isStartingTraining ? (
|
34 |
+
<>
|
35 |
+
<Loader2 className="w-5 h-5 mr-2 animate-spin" />
|
36 |
+
Starting...
|
37 |
+
</>
|
38 |
+
) : (
|
39 |
+
<>
|
40 |
+
<Play className="w-5 h-5 mr-2" />
|
41 |
+
Start Training
|
42 |
+
</>
|
43 |
+
)}
|
44 |
+
</Button>
|
45 |
+
) : (
|
46 |
+
<Button
|
47 |
+
onClick={handleStopTraining}
|
48 |
+
disabled={!trainingStatus.available_controls.stop_training}
|
49 |
+
size="lg"
|
50 |
+
className="bg-red-500 hover:bg-red-600 text-white font-semibold px-6 py-3 text-base rounded-full shadow-lg transition-all hover:scale-105"
|
51 |
+
>
|
52 |
+
<Square className="w-5 h-5 mr-2" />
|
53 |
+
Stop Training
|
54 |
+
</Button>
|
55 |
+
)}
|
56 |
+
</div>
|
57 |
+
);
|
58 |
+
};
|
59 |
+
|
60 |
+
export default TrainingControls;
|
src/components/training/TrainingHeader.tsx
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { useNavigate } from 'react-router-dom';
|
4 |
+
import { Button } from '@/components/ui/button';
|
5 |
+
import { ArrowLeft } from 'lucide-react';
|
6 |
+
import Logo from '@/components/Logo';
|
7 |
+
import { TrainingStatus } from './types';
|
8 |
+
|
9 |
+
interface TrainingHeaderProps {
|
10 |
+
trainingStatus: TrainingStatus;
|
11 |
+
}
|
12 |
+
|
13 |
+
const TrainingHeader: React.FC<TrainingHeaderProps> = ({ trainingStatus }) => {
|
14 |
+
const navigate = useNavigate();
|
15 |
+
|
16 |
+
const getStatusColor = () => {
|
17 |
+
if (trainingStatus.training_active) return "text-green-400";
|
18 |
+
return "text-gray-400";
|
19 |
+
};
|
20 |
+
|
21 |
+
const getStatusText = () => {
|
22 |
+
if (trainingStatus.training_active) return "Training Active";
|
23 |
+
return "Ready to Train";
|
24 |
+
};
|
25 |
+
|
26 |
+
return (
|
27 |
+
<div className="flex items-center justify-between mb-8">
|
28 |
+
<div className="flex items-center gap-4">
|
29 |
+
<Button
|
30 |
+
variant="ghost"
|
31 |
+
size="icon"
|
32 |
+
onClick={() => navigate("/")}
|
33 |
+
className="text-slate-400 hover:bg-slate-800 hover:text-white rounded-lg"
|
34 |
+
>
|
35 |
+
<ArrowLeft className="w-5 h-5" />
|
36 |
+
</Button>
|
37 |
+
<Logo />
|
38 |
+
<h1 className="text-3xl font-bold text-white">
|
39 |
+
Training Dashboard
|
40 |
+
</h1>
|
41 |
+
</div>
|
42 |
+
|
43 |
+
<div className="flex items-center gap-3">
|
44 |
+
<div
|
45 |
+
className={`w-3 h-3 rounded-full ${
|
46 |
+
trainingStatus.training_active
|
47 |
+
? "bg-green-400 animate-pulse"
|
48 |
+
: "bg-slate-500"
|
49 |
+
}`}
|
50 |
+
></div>
|
51 |
+
<span className={`font-semibold ${getStatusColor()}`}>
|
52 |
+
{getStatusText()}
|
53 |
+
</span>
|
54 |
+
</div>
|
55 |
+
</div>
|
56 |
+
);
|
57 |
+
};
|
58 |
+
|
59 |
+
export default TrainingHeader;
|
src/components/training/TrainingTabs.tsx
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Button } from '@/components/ui/button';
|
4 |
+
import { Settings, Activity } from 'lucide-react';
|
5 |
+
|
6 |
+
type Tab = 'config' | 'monitoring';
|
7 |
+
|
8 |
+
interface TrainingTabsProps {
|
9 |
+
activeTab: Tab;
|
10 |
+
setActiveTab: (tab: Tab) => void;
|
11 |
+
}
|
12 |
+
|
13 |
+
const TrainingTabs: React.FC<TrainingTabsProps> = ({ activeTab, setActiveTab }) => {
|
14 |
+
return (
|
15 |
+
<div className="flex gap-2 mb-6 border-b border-slate-700">
|
16 |
+
<Button
|
17 |
+
variant="ghost"
|
18 |
+
onClick={() => setActiveTab("config")}
|
19 |
+
className={`flex items-center gap-2 rounded-none px-4 py-3 text-sm font-semibold transition-colors ${
|
20 |
+
activeTab === "config"
|
21 |
+
? "text-white border-b-2 border-white"
|
22 |
+
: "text-slate-400 hover:bg-slate-800 hover:text-white"
|
23 |
+
}`}
|
24 |
+
>
|
25 |
+
<Settings className="w-4 h-4" />
|
26 |
+
Configuration
|
27 |
+
</Button>
|
28 |
+
<Button
|
29 |
+
variant="ghost"
|
30 |
+
onClick={() => setActiveTab("monitoring")}
|
31 |
+
className={`flex items-center gap-2 rounded-none px-4 py-3 text-sm font-semibold transition-colors ${
|
32 |
+
activeTab === "monitoring"
|
33 |
+
? "text-white border-b-2 border-white"
|
34 |
+
: "text-slate-400 hover:bg-slate-800 hover:text-white"
|
35 |
+
}`}
|
36 |
+
>
|
37 |
+
<Activity className="w-4 h-4" />
|
38 |
+
Monitoring
|
39 |
+
</Button>
|
40 |
+
</div>
|
41 |
+
);
|
42 |
+
};
|
43 |
+
|
44 |
+
export default TrainingTabs;
|
src/components/training/config/AdvancedConfig.tsx
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { Input } from '@/components/ui/input';
|
5 |
+
import { Label } from '@/components/ui/label';
|
6 |
+
import { Switch } from '@/components/ui/switch';
|
7 |
+
import { Settings } from 'lucide-react';
|
8 |
+
import { ConfigComponentProps } from '../types';
|
9 |
+
|
10 |
+
const AdvancedConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
11 |
+
return (
|
12 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
13 |
+
<CardHeader>
|
14 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
15 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
16 |
+
<Settings className="w-5 h-5 text-sky-400" />
|
17 |
+
</div>
|
18 |
+
Advanced Options
|
19 |
+
</CardTitle>
|
20 |
+
</CardHeader>
|
21 |
+
<CardContent className="space-y-4">
|
22 |
+
<div>
|
23 |
+
<Label htmlFor="config_path" className="text-slate-300">
|
24 |
+
Config Path (optional)
|
25 |
+
</Label>
|
26 |
+
<Input
|
27 |
+
id="config_path"
|
28 |
+
value={config.config_path || ""}
|
29 |
+
onChange={(e) =>
|
30 |
+
updateConfig("config_path", e.target.value || undefined)
|
31 |
+
}
|
32 |
+
placeholder="path/to/config.yaml"
|
33 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
34 |
+
/>
|
35 |
+
</div>
|
36 |
+
<div className="flex items-center space-x-3 pt-2">
|
37 |
+
<Switch
|
38 |
+
id="use_policy_training_preset"
|
39 |
+
checked={config.use_policy_training_preset}
|
40 |
+
onCheckedChange={(checked) =>
|
41 |
+
updateConfig("use_policy_training_preset", checked)
|
42 |
+
}
|
43 |
+
/>
|
44 |
+
<Label
|
45 |
+
htmlFor="use_policy_training_preset"
|
46 |
+
className="text-slate-300"
|
47 |
+
>
|
48 |
+
Use Policy Training Preset
|
49 |
+
</Label>
|
50 |
+
</div>
|
51 |
+
</CardContent>
|
52 |
+
</Card>
|
53 |
+
);
|
54 |
+
};
|
55 |
+
|
56 |
+
export default AdvancedConfig;
|
src/components/training/config/DatasetConfig.tsx
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { Input } from '@/components/ui/input';
|
5 |
+
import { Label } from '@/components/ui/label';
|
6 |
+
import { Database } from 'lucide-react';
|
7 |
+
import { ConfigComponentProps } from '../types';
|
8 |
+
|
9 |
+
const DatasetConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
10 |
+
return (
|
11 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
12 |
+
<CardHeader>
|
13 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
14 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
15 |
+
<Database className="w-5 h-5 text-sky-400" />
|
16 |
+
</div>
|
17 |
+
Dataset Configuration
|
18 |
+
</CardTitle>
|
19 |
+
</CardHeader>
|
20 |
+
<CardContent className="space-y-4">
|
21 |
+
<div>
|
22 |
+
<Label htmlFor="dataset_repo_id" className="text-slate-300">
|
23 |
+
Dataset Repository ID *
|
24 |
+
</Label>
|
25 |
+
<Input
|
26 |
+
id="dataset_repo_id"
|
27 |
+
value={config.dataset_repo_id}
|
28 |
+
onChange={(e) =>
|
29 |
+
updateConfig("dataset_repo_id", e.target.value)
|
30 |
+
}
|
31 |
+
placeholder="e.g., your-username/your-dataset"
|
32 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
33 |
+
/>
|
34 |
+
<p className="text-xs text-slate-500 mt-1">
|
35 |
+
HuggingFace Hub dataset repository ID
|
36 |
+
</p>
|
37 |
+
</div>
|
38 |
+
|
39 |
+
<div>
|
40 |
+
<Label htmlFor="dataset_revision" className="text-slate-300">
|
41 |
+
Dataset Revision (optional)
|
42 |
+
</Label>
|
43 |
+
<Input
|
44 |
+
id="dataset_revision"
|
45 |
+
value={config.dataset_revision || ""}
|
46 |
+
onChange={(e) =>
|
47 |
+
updateConfig(
|
48 |
+
"dataset_revision",
|
49 |
+
e.target.value || undefined
|
50 |
+
)
|
51 |
+
}
|
52 |
+
placeholder="main"
|
53 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
54 |
+
/>
|
55 |
+
<p className="text-xs text-slate-500 mt-1">
|
56 |
+
Git revision (branch, tag, or commit hash)
|
57 |
+
</p>
|
58 |
+
</div>
|
59 |
+
|
60 |
+
<div>
|
61 |
+
<Label htmlFor="dataset_root" className="text-slate-300">
|
62 |
+
Dataset Root Directory (optional)
|
63 |
+
</Label>
|
64 |
+
<Input
|
65 |
+
id="dataset_root"
|
66 |
+
value={config.dataset_root || ""}
|
67 |
+
onChange={(e) =>
|
68 |
+
updateConfig("dataset_root", e.target.value || undefined)
|
69 |
+
}
|
70 |
+
placeholder="./data"
|
71 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
72 |
+
/>
|
73 |
+
</div>
|
74 |
+
</CardContent>
|
75 |
+
</Card>
|
76 |
+
);
|
77 |
+
};
|
78 |
+
|
79 |
+
export default DatasetConfig;
|
src/components/training/config/EnvEvalConfig.tsx
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { Input } from '@/components/ui/input';
|
5 |
+
import { Label } from '@/components/ui/label';
|
6 |
+
import { Switch } from '@/components/ui/switch';
|
7 |
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
8 |
+
import { Activity } from 'lucide-react';
|
9 |
+
import { ConfigComponentProps } from '../types';
|
10 |
+
|
11 |
+
const EnvEvalConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
12 |
+
return (
|
13 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
14 |
+
<CardHeader>
|
15 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
16 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
17 |
+
<Activity className="w-5 h-5 text-sky-400" />
|
18 |
+
</div>
|
19 |
+
Environment & Evaluation
|
20 |
+
</CardTitle>
|
21 |
+
</CardHeader>
|
22 |
+
<CardContent className="space-y-4">
|
23 |
+
<div>
|
24 |
+
<Label htmlFor="env_type" className="text-slate-300">
|
25 |
+
Environment Type (optional)
|
26 |
+
</Label>
|
27 |
+
<Select
|
28 |
+
value={config.env_type || "none"}
|
29 |
+
onValueChange={(value) =>
|
30 |
+
updateConfig(
|
31 |
+
"env_type",
|
32 |
+
value === "none" ? undefined : value
|
33 |
+
)
|
34 |
+
}
|
35 |
+
>
|
36 |
+
<SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
|
37 |
+
<SelectValue placeholder="Select environment type" />
|
38 |
+
</SelectTrigger>
|
39 |
+
<SelectContent className="bg-slate-800 border-slate-600">
|
40 |
+
<SelectItem value="none">None</SelectItem>
|
41 |
+
<SelectItem value="aloha">Aloha</SelectItem>
|
42 |
+
<SelectItem value="pusht">PushT</SelectItem>
|
43 |
+
<SelectItem value="xarm">XArm</SelectItem>
|
44 |
+
<SelectItem value="gym_manipulator">
|
45 |
+
Gym Manipulator
|
46 |
+
</SelectItem>
|
47 |
+
<SelectItem value="hil">HIL</SelectItem>
|
48 |
+
</SelectContent>
|
49 |
+
</Select>
|
50 |
+
</div>
|
51 |
+
<div>
|
52 |
+
<Label htmlFor="env_task" className="text-slate-300">
|
53 |
+
Environment Task (optional)
|
54 |
+
</Label>
|
55 |
+
<Input
|
56 |
+
id="env_task"
|
57 |
+
value={config.env_task || ""}
|
58 |
+
onChange={(e) =>
|
59 |
+
updateConfig("env_task", e.target.value || undefined)
|
60 |
+
}
|
61 |
+
placeholder="e.g., insertion_human"
|
62 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
63 |
+
/>
|
64 |
+
</div>
|
65 |
+
<div className="grid grid-cols-2 gap-4">
|
66 |
+
<div>
|
67 |
+
<Label htmlFor="eval_n_episodes" className="text-slate-300">
|
68 |
+
Eval Episodes
|
69 |
+
</Label>
|
70 |
+
<Input
|
71 |
+
id="eval_n_episodes"
|
72 |
+
type="number"
|
73 |
+
value={config.eval_n_episodes}
|
74 |
+
onChange={(e) =>
|
75 |
+
updateConfig(
|
76 |
+
"eval_n_episodes",
|
77 |
+
parseInt(e.target.value)
|
78 |
+
)
|
79 |
+
}
|
80 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
81 |
+
/>
|
82 |
+
</div>
|
83 |
+
<div>
|
84 |
+
<Label htmlFor="eval_batch_size" className="text-slate-300">
|
85 |
+
Eval Batch Size
|
86 |
+
</Label>
|
87 |
+
<Input
|
88 |
+
id="eval_batch_size"
|
89 |
+
type="number"
|
90 |
+
value={config.eval_batch_size}
|
91 |
+
onChange={(e) =>
|
92 |
+
updateConfig(
|
93 |
+
"eval_batch_size",
|
94 |
+
parseInt(e.target.value)
|
95 |
+
)
|
96 |
+
}
|
97 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
98 |
+
/>
|
99 |
+
</div>
|
100 |
+
</div>
|
101 |
+
<div className="flex items-center space-x-3 pt-2">
|
102 |
+
<Switch
|
103 |
+
id="eval_use_async_envs"
|
104 |
+
checked={config.eval_use_async_envs}
|
105 |
+
onCheckedChange={(checked) =>
|
106 |
+
updateConfig("eval_use_async_envs", checked)
|
107 |
+
}
|
108 |
+
/>
|
109 |
+
<Label
|
110 |
+
htmlFor="eval_use_async_envs"
|
111 |
+
className="text-slate-300"
|
112 |
+
>
|
113 |
+
Use Asynchronous Environments
|
114 |
+
</Label>
|
115 |
+
</div>
|
116 |
+
</CardContent>
|
117 |
+
</Card>
|
118 |
+
);
|
119 |
+
};
|
120 |
+
|
121 |
+
export default EnvEvalConfig;
|
src/components/training/config/LoggingConfig.tsx
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { Input } from '@/components/ui/input';
|
5 |
+
import { Label } from '@/components/ui/label';
|
6 |
+
import { Switch } from '@/components/ui/switch';
|
7 |
+
import { FileText } from 'lucide-react';
|
8 |
+
import { ConfigComponentProps } from '../types';
|
9 |
+
|
10 |
+
const LoggingConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
11 |
+
return (
|
12 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
13 |
+
<CardHeader>
|
14 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
15 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
16 |
+
<FileText className="w-5 h-5 text-sky-400" />
|
17 |
+
</div>
|
18 |
+
Logging & Checkpointing
|
19 |
+
</CardTitle>
|
20 |
+
</CardHeader>
|
21 |
+
<CardContent className="space-y-4">
|
22 |
+
<div className="grid grid-cols-3 gap-4">
|
23 |
+
<div>
|
24 |
+
<Label htmlFor="log_freq" className="text-slate-300">
|
25 |
+
Log Frequency
|
26 |
+
</Label>
|
27 |
+
<Input
|
28 |
+
id="log_freq"
|
29 |
+
type="number"
|
30 |
+
value={config.log_freq}
|
31 |
+
onChange={(e) =>
|
32 |
+
updateConfig("log_freq", parseInt(e.target.value))
|
33 |
+
}
|
34 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
35 |
+
/>
|
36 |
+
</div>
|
37 |
+
<div>
|
38 |
+
<Label htmlFor="save_freq" className="text-slate-300">
|
39 |
+
Save Frequency
|
40 |
+
</Label>
|
41 |
+
<Input
|
42 |
+
id="save_freq"
|
43 |
+
type="number"
|
44 |
+
value={config.save_freq}
|
45 |
+
onChange={(e) =>
|
46 |
+
updateConfig("save_freq", parseInt(e.target.value))
|
47 |
+
}
|
48 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
49 |
+
/>
|
50 |
+
</div>
|
51 |
+
<div>
|
52 |
+
<Label htmlFor="eval_freq" className="text-slate-300">
|
53 |
+
Eval Frequency
|
54 |
+
</Label>
|
55 |
+
<Input
|
56 |
+
id="eval_freq"
|
57 |
+
type="number"
|
58 |
+
value={config.eval_freq}
|
59 |
+
onChange={(e) =>
|
60 |
+
updateConfig("eval_freq", parseInt(e.target.value))
|
61 |
+
}
|
62 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
63 |
+
/>
|
64 |
+
</div>
|
65 |
+
</div>
|
66 |
+
<div>
|
67 |
+
<Label htmlFor="output_dir" className="text-slate-300">
|
68 |
+
Output Directory
|
69 |
+
</Label>
|
70 |
+
<Input
|
71 |
+
id="output_dir"
|
72 |
+
value={config.output_dir}
|
73 |
+
onChange={(e) => updateConfig("output_dir", e.target.value)}
|
74 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
75 |
+
/>
|
76 |
+
</div>
|
77 |
+
<div>
|
78 |
+
<Label htmlFor="job_name" className="text-slate-300">
|
79 |
+
Job Name (optional)
|
80 |
+
</Label>
|
81 |
+
<Input
|
82 |
+
id="job_name"
|
83 |
+
value={config.job_name || ""}
|
84 |
+
onChange={(e) =>
|
85 |
+
updateConfig("job_name", e.target.value || undefined)
|
86 |
+
}
|
87 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
88 |
+
/>
|
89 |
+
</div>
|
90 |
+
<div className="flex items-center space-x-3 pt-2">
|
91 |
+
<Switch
|
92 |
+
id="save_checkpoint"
|
93 |
+
checked={config.save_checkpoint}
|
94 |
+
onCheckedChange={(checked) =>
|
95 |
+
updateConfig("save_checkpoint", checked)
|
96 |
+
}
|
97 |
+
/>
|
98 |
+
<Label htmlFor="save_checkpoint" className="text-slate-300">
|
99 |
+
Save Checkpoints
|
100 |
+
</Label>
|
101 |
+
</div>
|
102 |
+
<div className="flex items-center space-x-3">
|
103 |
+
<Switch
|
104 |
+
id="resume"
|
105 |
+
checked={config.resume}
|
106 |
+
onCheckedChange={(checked) =>
|
107 |
+
updateConfig("resume", checked)
|
108 |
+
}
|
109 |
+
/>
|
110 |
+
<Label htmlFor="resume" className="text-slate-300">
|
111 |
+
Resume from Checkpoint
|
112 |
+
</Label>
|
113 |
+
</div>
|
114 |
+
</CardContent>
|
115 |
+
</Card>
|
116 |
+
);
|
117 |
+
};
|
118 |
+
|
119 |
+
export default LoggingConfig;
|
src/components/training/config/OptimizerConfig.tsx
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { Input } from '@/components/ui/input';
|
5 |
+
import { Label } from '@/components/ui/label';
|
6 |
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
7 |
+
import { Settings } from 'lucide-react';
|
8 |
+
import { ConfigComponentProps } from '../types';
|
9 |
+
|
10 |
+
const OptimizerConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
11 |
+
return (
|
12 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
13 |
+
<CardHeader>
|
14 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
15 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
16 |
+
<Settings className="w-5 h-5 text-sky-400" />
|
17 |
+
</div>
|
18 |
+
Optimizer Configuration
|
19 |
+
</CardTitle>
|
20 |
+
</CardHeader>
|
21 |
+
<CardContent className="space-y-4">
|
22 |
+
<div>
|
23 |
+
<Label htmlFor="optimizer_type" className="text-slate-300">
|
24 |
+
Optimizer Type
|
25 |
+
</Label>
|
26 |
+
<Select
|
27 |
+
value={config.optimizer_type || "adam"}
|
28 |
+
onValueChange={(value) =>
|
29 |
+
updateConfig("optimizer_type", value)
|
30 |
+
}
|
31 |
+
>
|
32 |
+
<SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
|
33 |
+
<SelectValue />
|
34 |
+
</SelectTrigger>
|
35 |
+
<SelectContent className="bg-slate-800 border-slate-600">
|
36 |
+
<SelectItem value="adam">Adam</SelectItem>
|
37 |
+
<SelectItem value="adamw">AdamW</SelectItem>
|
38 |
+
<SelectItem value="sgd">SGD</SelectItem>
|
39 |
+
<SelectItem value="multi_adam">Multi Adam</SelectItem>
|
40 |
+
</SelectContent>
|
41 |
+
</Select>
|
42 |
+
</div>
|
43 |
+
<div className="grid grid-cols-3 gap-4">
|
44 |
+
<div>
|
45 |
+
<Label htmlFor="optimizer_lr" className="text-slate-300">
|
46 |
+
Learning Rate
|
47 |
+
</Label>
|
48 |
+
<Input
|
49 |
+
id="optimizer_lr"
|
50 |
+
type="number"
|
51 |
+
step="0.0001"
|
52 |
+
value={config.optimizer_lr || ""}
|
53 |
+
onChange={(e) =>
|
54 |
+
updateConfig(
|
55 |
+
"optimizer_lr",
|
56 |
+
e.target.value
|
57 |
+
? parseFloat(e.target.value)
|
58 |
+
: undefined
|
59 |
+
)
|
60 |
+
}
|
61 |
+
placeholder="Use policy default"
|
62 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
63 |
+
/>
|
64 |
+
</div>
|
65 |
+
<div>
|
66 |
+
<Label
|
67 |
+
htmlFor="optimizer_weight_decay"
|
68 |
+
className="text-slate-300"
|
69 |
+
>
|
70 |
+
Weight Decay
|
71 |
+
</Label>
|
72 |
+
<Input
|
73 |
+
id="optimizer_weight_decay"
|
74 |
+
type="number"
|
75 |
+
step="0.0001"
|
76 |
+
value={config.optimizer_weight_decay || ""}
|
77 |
+
onChange={(e) =>
|
78 |
+
updateConfig(
|
79 |
+
"optimizer_weight_decay",
|
80 |
+
e.target.value
|
81 |
+
? parseFloat(e.target.value)
|
82 |
+
: undefined
|
83 |
+
)
|
84 |
+
}
|
85 |
+
placeholder="Use policy default"
|
86 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
87 |
+
/>
|
88 |
+
</div>
|
89 |
+
<div>
|
90 |
+
<Label
|
91 |
+
htmlFor="optimizer_grad_clip_norm"
|
92 |
+
className="text-slate-300"
|
93 |
+
>
|
94 |
+
Gradient Clipping
|
95 |
+
</Label>
|
96 |
+
<Input
|
97 |
+
id="optimizer_grad_clip_norm"
|
98 |
+
type="number"
|
99 |
+
value={config.optimizer_grad_clip_norm || ""}
|
100 |
+
onChange={(e) =>
|
101 |
+
updateConfig(
|
102 |
+
"optimizer_grad_clip_norm",
|
103 |
+
e.target.value
|
104 |
+
? parseFloat(e.target.value)
|
105 |
+
: undefined
|
106 |
+
)
|
107 |
+
}
|
108 |
+
placeholder="Use policy default"
|
109 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
110 |
+
/>
|
111 |
+
</div>
|
112 |
+
</div>
|
113 |
+
</CardContent>
|
114 |
+
</Card>
|
115 |
+
);
|
116 |
+
};
|
117 |
+
|
118 |
+
export default OptimizerConfig;
|
src/components/training/config/PolicyConfig.tsx
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { Label } from '@/components/ui/label';
|
5 |
+
import { Switch } from '@/components/ui/switch';
|
6 |
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
7 |
+
import { Cpu } from 'lucide-react';
|
8 |
+
import { ConfigComponentProps } from '../types';
|
9 |
+
|
10 |
+
const PolicyConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
11 |
+
return (
|
12 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
13 |
+
<CardHeader>
|
14 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
15 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
16 |
+
<Cpu className="w-5 h-5 text-sky-400" />
|
17 |
+
</div>
|
18 |
+
Policy Configuration
|
19 |
+
</CardTitle>
|
20 |
+
</CardHeader>
|
21 |
+
<CardContent className="space-y-4">
|
22 |
+
<div>
|
23 |
+
<Label htmlFor="policy_type" className="text-slate-300">
|
24 |
+
Policy Type
|
25 |
+
</Label>
|
26 |
+
<Select
|
27 |
+
value={config.policy_type}
|
28 |
+
onValueChange={(value) =>
|
29 |
+
updateConfig("policy_type", value)
|
30 |
+
}
|
31 |
+
>
|
32 |
+
<SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
|
33 |
+
<SelectValue />
|
34 |
+
</SelectTrigger>
|
35 |
+
<SelectContent className="bg-slate-800 border-slate-600">
|
36 |
+
<SelectItem value="act">ACT (Action Chunking Transformer)</SelectItem>
|
37 |
+
<SelectItem value="diffusion">Diffusion Policy</SelectItem>
|
38 |
+
<SelectItem value="pi0">PI0</SelectItem>
|
39 |
+
<SelectItem value="smolvla">SmolVLA</SelectItem>
|
40 |
+
<SelectItem value="tdmpc">TD-MPC</SelectItem>
|
41 |
+
<SelectItem value="vqbet">VQ-BeT</SelectItem>
|
42 |
+
<SelectItem value="pi0fast">PI0 Fast</SelectItem>
|
43 |
+
<SelectItem value="sac">SAC</SelectItem>
|
44 |
+
<SelectItem value="reward_classifier">Reward Classifier</SelectItem>
|
45 |
+
</SelectContent>
|
46 |
+
</Select>
|
47 |
+
</div>
|
48 |
+
<div>
|
49 |
+
<Label htmlFor="policy_device" className="text-slate-300">
|
50 |
+
Device
|
51 |
+
</Label>
|
52 |
+
<Select
|
53 |
+
value={config.policy_device || "cuda"}
|
54 |
+
onValueChange={(value) =>
|
55 |
+
updateConfig("policy_device", value)
|
56 |
+
}
|
57 |
+
>
|
58 |
+
<SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
|
59 |
+
<SelectValue />
|
60 |
+
</SelectTrigger>
|
61 |
+
<SelectContent className="bg-slate-800 border-slate-600">
|
62 |
+
<SelectItem value="cuda">CUDA (GPU)</SelectItem>
|
63 |
+
<SelectItem value="cpu">CPU</SelectItem>
|
64 |
+
<SelectItem value="mps">MPS (Apple Silicon)</SelectItem>
|
65 |
+
</SelectContent>
|
66 |
+
</Select>
|
67 |
+
</div>
|
68 |
+
<div className="flex items-center space-x-3 pt-2">
|
69 |
+
<Switch
|
70 |
+
id="policy_use_amp"
|
71 |
+
checked={config.policy_use_amp}
|
72 |
+
onCheckedChange={(checked) =>
|
73 |
+
updateConfig("policy_use_amp", checked)
|
74 |
+
}
|
75 |
+
/>
|
76 |
+
<Label htmlFor="policy_use_amp" className="text-slate-300">
|
77 |
+
Use Automatic Mixed Precision (AMP)
|
78 |
+
</Label>
|
79 |
+
</div>
|
80 |
+
</CardContent>
|
81 |
+
</Card>
|
82 |
+
);
|
83 |
+
};
|
84 |
+
|
85 |
+
export default PolicyConfig;
|
src/components/training/config/TrainingParams.tsx
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { Input } from '@/components/ui/input';
|
5 |
+
import { Label } from '@/components/ui/label';
|
6 |
+
import { TrendingUp } from 'lucide-react';
|
7 |
+
import { ConfigComponentProps } from '../types';
|
8 |
+
|
9 |
+
const TrainingParams: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
10 |
+
return (
|
11 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
12 |
+
<CardHeader>
|
13 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
14 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
15 |
+
<TrendingUp className="w-5 h-5 text-sky-400" />
|
16 |
+
</div>
|
17 |
+
Training Parameters
|
18 |
+
</CardTitle>
|
19 |
+
</CardHeader>
|
20 |
+
<CardContent className="space-y-4">
|
21 |
+
<div className="grid grid-cols-2 gap-4">
|
22 |
+
<div>
|
23 |
+
<Label htmlFor="steps" className="text-slate-300">
|
24 |
+
Training Steps
|
25 |
+
</Label>
|
26 |
+
<Input
|
27 |
+
id="steps"
|
28 |
+
type="number"
|
29 |
+
value={config.steps}
|
30 |
+
onChange={(e) =>
|
31 |
+
updateConfig("steps", parseInt(e.target.value))
|
32 |
+
}
|
33 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
34 |
+
/>
|
35 |
+
</div>
|
36 |
+
<div>
|
37 |
+
<Label htmlFor="batch_size" className="text-slate-300">
|
38 |
+
Batch Size
|
39 |
+
</Label>
|
40 |
+
<Input
|
41 |
+
id="batch_size"
|
42 |
+
type="number"
|
43 |
+
value={config.batch_size}
|
44 |
+
onChange={(e) =>
|
45 |
+
updateConfig("batch_size", parseInt(e.target.value))
|
46 |
+
}
|
47 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
48 |
+
/>
|
49 |
+
</div>
|
50 |
+
</div>
|
51 |
+
<div className="grid grid-cols-2 gap-4">
|
52 |
+
<div>
|
53 |
+
<Label htmlFor="seed" className="text-slate-300">
|
54 |
+
Random Seed
|
55 |
+
</Label>
|
56 |
+
<Input
|
57 |
+
id="seed"
|
58 |
+
type="number"
|
59 |
+
value={config.seed || ""}
|
60 |
+
onChange={(e) =>
|
61 |
+
updateConfig(
|
62 |
+
"seed",
|
63 |
+
e.target.value ? parseInt(e.target.value) : undefined
|
64 |
+
)
|
65 |
+
}
|
66 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
67 |
+
/>
|
68 |
+
</div>
|
69 |
+
<div>
|
70 |
+
<Label htmlFor="num_workers" className="text-slate-300">
|
71 |
+
Number of Workers
|
72 |
+
</Label>
|
73 |
+
<Input
|
74 |
+
id="num_workers"
|
75 |
+
type="number"
|
76 |
+
value={config.num_workers}
|
77 |
+
onChange={(e) =>
|
78 |
+
updateConfig("num_workers", parseInt(e.target.value))
|
79 |
+
}
|
80 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
81 |
+
/>
|
82 |
+
</div>
|
83 |
+
</div>
|
84 |
+
</CardContent>
|
85 |
+
</Card>
|
86 |
+
);
|
87 |
+
};
|
88 |
+
|
89 |
+
export default TrainingParams;
|
src/components/training/config/WandbConfig.tsx
ADDED
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { Input } from '@/components/ui/input';
|
5 |
+
import { Label } from '@/components/ui/label';
|
6 |
+
import { Switch } from '@/components/ui/switch';
|
7 |
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
8 |
+
import { TrendingUp } from 'lucide-react';
|
9 |
+
import { ConfigComponentProps } from '../types';
|
10 |
+
|
11 |
+
const WandbConfig: React.FC<ConfigComponentProps> = ({ config, updateConfig }) => {
|
12 |
+
return (
|
13 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
14 |
+
<CardHeader>
|
15 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
16 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
17 |
+
<TrendingUp className="w-5 h-5 text-sky-400" />
|
18 |
+
</div>
|
19 |
+
Weights & Biases
|
20 |
+
</CardTitle>
|
21 |
+
</CardHeader>
|
22 |
+
<CardContent className="space-y-4">
|
23 |
+
<div className="flex items-center space-x-3 pt-2">
|
24 |
+
<Switch
|
25 |
+
id="wandb_enable"
|
26 |
+
checked={config.wandb_enable}
|
27 |
+
onCheckedChange={(checked) =>
|
28 |
+
updateConfig("wandb_enable", checked)
|
29 |
+
}
|
30 |
+
/>
|
31 |
+
<Label htmlFor="wandb_enable" className="text-slate-300">
|
32 |
+
Enable Weights & Biases Logging
|
33 |
+
</Label>
|
34 |
+
</div>
|
35 |
+
{config.wandb_enable && (
|
36 |
+
<>
|
37 |
+
<div>
|
38 |
+
<Label htmlFor="wandb_project" className="text-slate-300">
|
39 |
+
W&B Project Name
|
40 |
+
</Label>
|
41 |
+
<Input
|
42 |
+
id="wandb_project"
|
43 |
+
value={config.wandb_project || ""}
|
44 |
+
onChange={(e) =>
|
45 |
+
updateConfig(
|
46 |
+
"wandb_project",
|
47 |
+
e.target.value || undefined
|
48 |
+
)
|
49 |
+
}
|
50 |
+
placeholder="my-robotics-project"
|
51 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
52 |
+
/>
|
53 |
+
</div>
|
54 |
+
<div>
|
55 |
+
<Label htmlFor="wandb_entity" className="text-slate-300">
|
56 |
+
W&B Entity (optional)
|
57 |
+
</Label>
|
58 |
+
<Input
|
59 |
+
id="wandb_entity"
|
60 |
+
value={config.wandb_entity || ""}
|
61 |
+
onChange={(e) =>
|
62 |
+
updateConfig(
|
63 |
+
"wandb_entity",
|
64 |
+
e.target.value || undefined
|
65 |
+
)
|
66 |
+
}
|
67 |
+
placeholder="your-username"
|
68 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
69 |
+
/>
|
70 |
+
</div>
|
71 |
+
<div>
|
72 |
+
<Label htmlFor="wandb_notes" className="text-slate-300">
|
73 |
+
W&B Notes (optional)
|
74 |
+
</Label>
|
75 |
+
<Input
|
76 |
+
id="wandb_notes"
|
77 |
+
value={config.wandb_notes || ""}
|
78 |
+
onChange={(e) =>
|
79 |
+
updateConfig(
|
80 |
+
"wandb_notes",
|
81 |
+
e.target.value || undefined
|
82 |
+
)
|
83 |
+
}
|
84 |
+
placeholder="Training run notes..."
|
85 |
+
className="bg-slate-900 border-slate-600 text-white rounded-lg"
|
86 |
+
/>
|
87 |
+
</div>
|
88 |
+
<div>
|
89 |
+
<Label htmlFor="wandb_mode" className="text-slate-300">
|
90 |
+
W&B Mode
|
91 |
+
</Label>
|
92 |
+
<Select
|
93 |
+
value={config.wandb_mode || "online"}
|
94 |
+
onValueChange={(value) =>
|
95 |
+
updateConfig("wandb_mode", value)
|
96 |
+
}
|
97 |
+
>
|
98 |
+
<SelectTrigger className="bg-slate-900 border-slate-600 text-white rounded-lg">
|
99 |
+
<SelectValue />
|
100 |
+
</SelectTrigger>
|
101 |
+
<SelectContent className="bg-slate-800 border-slate-600">
|
102 |
+
<SelectItem value="online">Online</SelectItem>
|
103 |
+
<SelectItem value="offline">Offline</SelectItem>
|
104 |
+
<SelectItem value="disabled">Disabled</SelectItem>
|
105 |
+
</SelectContent>
|
106 |
+
</Select>
|
107 |
+
</div>
|
108 |
+
<div className="flex items-center space-x-3 pt-2">
|
109 |
+
<Switch
|
110 |
+
id="wandb_disable_artifact"
|
111 |
+
checked={config.wandb_disable_artifact}
|
112 |
+
onCheckedChange={(checked) =>
|
113 |
+
updateConfig("wandb_disable_artifact", checked)
|
114 |
+
}
|
115 |
+
/>
|
116 |
+
<Label
|
117 |
+
htmlFor="wandb_disable_artifact"
|
118 |
+
className="text-slate-300"
|
119 |
+
>
|
120 |
+
Disable Artifacts
|
121 |
+
</Label>
|
122 |
+
</div>
|
123 |
+
</>
|
124 |
+
)}
|
125 |
+
</CardContent>
|
126 |
+
</Card>
|
127 |
+
);
|
128 |
+
};
|
129 |
+
|
130 |
+
export default WandbConfig;
|
src/components/training/monitoring/MonitoringStats.tsx
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent } from '@/components/ui/card';
|
4 |
+
import { Progress } from '@/components/ui/progress';
|
5 |
+
import { TrainingStatus } from '../types';
|
6 |
+
import { TrendingUp, CheckCircle, Activity, Clock } from 'lucide-react';
|
7 |
+
|
8 |
+
interface MonitoringStatsProps {
|
9 |
+
trainingStatus: TrainingStatus;
|
10 |
+
getProgressPercentage: () => number;
|
11 |
+
formatTime: (seconds: number) => string;
|
12 |
+
}
|
13 |
+
|
14 |
+
const MonitoringStats: React.FC<MonitoringStatsProps> = ({ trainingStatus, getProgressPercentage, formatTime }) => {
|
15 |
+
return (
|
16 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
17 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
18 |
+
<CardContent className="p-6">
|
19 |
+
<div className="flex items-center gap-4">
|
20 |
+
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-blue-500/20 text-blue-400">
|
21 |
+
<TrendingUp className="w-6 h-6" />
|
22 |
+
</div>
|
23 |
+
<div>
|
24 |
+
<h3 className="text-sm text-slate-400 mb-1">Progress</h3>
|
25 |
+
<div className="text-2xl font-bold text-white">
|
26 |
+
{trainingStatus.current_step} / {trainingStatus.total_steps}
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
</div>
|
30 |
+
<Progress value={getProgressPercentage()} className="mt-4" />
|
31 |
+
</CardContent>
|
32 |
+
</Card>
|
33 |
+
|
34 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
35 |
+
<CardContent className="p-6">
|
36 |
+
<div className="flex items-center gap-4">
|
37 |
+
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-500/20 text-green-400">
|
38 |
+
<CheckCircle className="w-6 h-6" />
|
39 |
+
</div>
|
40 |
+
<div>
|
41 |
+
<h3 className="text-sm text-slate-400 mb-1">Current Loss</h3>
|
42 |
+
<div className="text-2xl font-bold text-white">
|
43 |
+
{trainingStatus.current_loss?.toFixed(4) || "N/A"}
|
44 |
+
</div>
|
45 |
+
</div>
|
46 |
+
</div>
|
47 |
+
</CardContent>
|
48 |
+
</Card>
|
49 |
+
|
50 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
51 |
+
<CardContent className="p-6">
|
52 |
+
<div className="flex items-center gap-4">
|
53 |
+
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-orange-500/20 text-orange-400">
|
54 |
+
<Activity className="w-6 h-6" />
|
55 |
+
</div>
|
56 |
+
<div>
|
57 |
+
<h3 className="text-sm text-slate-400 mb-1">Learning Rate</h3>
|
58 |
+
<div className="text-2xl font-bold text-white">
|
59 |
+
{trainingStatus.current_lr?.toExponential(2) || "N/A"}
|
60 |
+
</div>
|
61 |
+
</div>
|
62 |
+
</div>
|
63 |
+
</CardContent>
|
64 |
+
</Card>
|
65 |
+
|
66 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
67 |
+
<CardContent className="p-6">
|
68 |
+
<div className="flex items-center gap-4">
|
69 |
+
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-purple-500/20 text-purple-400">
|
70 |
+
<Clock className="w-6 h-6" />
|
71 |
+
</div>
|
72 |
+
<div>
|
73 |
+
<h3 className="text-sm text-slate-400 mb-1">ETA</h3>
|
74 |
+
<div className="text-2xl font-bold text-white">
|
75 |
+
{trainingStatus.eta_seconds ? formatTime(trainingStatus.eta_seconds) : "N/A"}
|
76 |
+
</div>
|
77 |
+
</div>
|
78 |
+
</div>
|
79 |
+
</CardContent>
|
80 |
+
</Card>
|
81 |
+
</div>
|
82 |
+
);
|
83 |
+
};
|
84 |
+
|
85 |
+
export default MonitoringStats;
|
src/components/training/monitoring/TrainingLogs.tsx
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
4 |
+
import { FileText } from 'lucide-react';
|
5 |
+
import { LogEntry } from '../types';
|
6 |
+
|
7 |
+
interface TrainingLogsProps {
|
8 |
+
logs: LogEntry[];
|
9 |
+
logContainerRef: React.RefObject<HTMLDivElement>;
|
10 |
+
}
|
11 |
+
|
12 |
+
const TrainingLogs: React.FC<TrainingLogsProps> = ({ logs, logContainerRef }) => {
|
13 |
+
return (
|
14 |
+
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
|
15 |
+
<CardHeader>
|
16 |
+
<CardTitle className="flex items-center gap-3 text-white">
|
17 |
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-slate-700">
|
18 |
+
<FileText className="w-5 h-5 text-sky-400" />
|
19 |
+
</div>
|
20 |
+
Training Logs
|
21 |
+
</CardTitle>
|
22 |
+
</CardHeader>
|
23 |
+
<CardContent>
|
24 |
+
<div
|
25 |
+
ref={logContainerRef}
|
26 |
+
className="bg-slate-900 rounded-lg p-4 h-96 overflow-y-auto font-mono text-sm border border-slate-700"
|
27 |
+
>
|
28 |
+
{logs.length === 0 ? (
|
29 |
+
<div className="text-slate-500 text-center py-8">
|
30 |
+
No training logs yet. Start training to see output.
|
31 |
+
</div>
|
32 |
+
) : (
|
33 |
+
logs.map((log, index) => (
|
34 |
+
<div key={index} className="flex items-start mb-1">
|
35 |
+
<span className="text-slate-500 mr-4">
|
36 |
+
{new Date(log.timestamp * 1000).toLocaleTimeString()}
|
37 |
+
</span>
|
38 |
+
<span className="flex-1 text-slate-300 break-words whitespace-pre-wrap">
|
39 |
+
{log.message}
|
40 |
+
</span>
|
41 |
+
</div>
|
42 |
+
))
|
43 |
+
)}
|
44 |
+
</div>
|
45 |
+
</CardContent>
|
46 |
+
</Card>
|
47 |
+
);
|
48 |
+
};
|
49 |
+
|
50 |
+
export default TrainingLogs;
|
src/components/training/types.ts
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
export interface TrainingConfig {
|
3 |
+
// Dataset configuration
|
4 |
+
dataset_repo_id: string;
|
5 |
+
dataset_revision?: string;
|
6 |
+
dataset_root?: string;
|
7 |
+
dataset_episodes?: number[];
|
8 |
+
|
9 |
+
// Policy configuration
|
10 |
+
policy_type: string;
|
11 |
+
|
12 |
+
// Core training parameters
|
13 |
+
steps: number;
|
14 |
+
batch_size: number;
|
15 |
+
seed?: number;
|
16 |
+
num_workers: number;
|
17 |
+
|
18 |
+
// Logging and checkpointing
|
19 |
+
log_freq: number;
|
20 |
+
save_freq: number;
|
21 |
+
eval_freq: number;
|
22 |
+
save_checkpoint: boolean;
|
23 |
+
|
24 |
+
// Output configuration
|
25 |
+
output_dir: string;
|
26 |
+
resume: boolean;
|
27 |
+
job_name?: string;
|
28 |
+
|
29 |
+
// Weights & Biases
|
30 |
+
wandb_enable: boolean;
|
31 |
+
wandb_project?: string;
|
32 |
+
wandb_entity?: string;
|
33 |
+
wandb_notes?: string;
|
34 |
+
wandb_run_id?: string;
|
35 |
+
wandb_mode?: string;
|
36 |
+
wandb_disable_artifact: boolean;
|
37 |
+
|
38 |
+
// Environment and evaluation
|
39 |
+
env_type?: string;
|
40 |
+
env_task?: string;
|
41 |
+
eval_n_episodes: number;
|
42 |
+
eval_batch_size: number;
|
43 |
+
eval_use_async_envs: boolean;
|
44 |
+
|
45 |
+
// Policy-specific parameters
|
46 |
+
policy_device?: string;
|
47 |
+
policy_use_amp: boolean;
|
48 |
+
|
49 |
+
// Optimizer parameters
|
50 |
+
optimizer_type?: string;
|
51 |
+
optimizer_lr?: number;
|
52 |
+
optimizer_weight_decay?: number;
|
53 |
+
optimizer_grad_clip_norm?: number;
|
54 |
+
|
55 |
+
// Advanced configuration
|
56 |
+
use_policy_training_preset: boolean;
|
57 |
+
config_path?: string;
|
58 |
+
}
|
59 |
+
|
60 |
+
export interface TrainingStatus {
|
61 |
+
training_active: boolean;
|
62 |
+
current_step: number;
|
63 |
+
total_steps: number;
|
64 |
+
current_loss?: number;
|
65 |
+
current_lr?: number;
|
66 |
+
grad_norm?: number;
|
67 |
+
epoch_time?: number;
|
68 |
+
eta_seconds?: number;
|
69 |
+
available_controls: {
|
70 |
+
stop_training: boolean;
|
71 |
+
pause_training: boolean;
|
72 |
+
resume_training: boolean;
|
73 |
+
};
|
74 |
+
}
|
75 |
+
|
76 |
+
export interface LogEntry {
|
77 |
+
timestamp: number;
|
78 |
+
message: string;
|
79 |
+
}
|
80 |
+
|
81 |
+
export interface ConfigComponentProps {
|
82 |
+
config: TrainingConfig;
|
83 |
+
updateConfig: <T extends keyof TrainingConfig>(
|
84 |
+
key: T,
|
85 |
+
value: TrainingConfig[T]
|
86 |
+
) => void;
|
87 |
+
}
|
src/contexts/ThemeContext.tsx
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import { createContext, ReactNode, useState, useEffect } from "react";
|
3 |
+
|
4 |
+
export type Theme = "dark" | "light" | "system";
|
5 |
+
|
6 |
+
export interface ThemeProviderState {
|
7 |
+
theme: Theme;
|
8 |
+
setTheme: (theme: Theme) => void;
|
9 |
+
}
|
10 |
+
|
11 |
+
const initialState: ThemeProviderState = {
|
12 |
+
theme: "system",
|
13 |
+
setTheme: () => null,
|
14 |
+
};
|
15 |
+
|
16 |
+
export const ThemeProviderContext =
|
17 |
+
createContext<ThemeProviderState>(initialState);
|
18 |
+
|
19 |
+
interface ThemeProviderProps {
|
20 |
+
children: ReactNode;
|
21 |
+
defaultTheme?: Theme;
|
22 |
+
storageKey?: string;
|
23 |
+
}
|
24 |
+
|
25 |
+
export function ThemeProvider({
|
26 |
+
children,
|
27 |
+
defaultTheme = "system",
|
28 |
+
storageKey = "vite-ui-theme",
|
29 |
+
...props
|
30 |
+
}: ThemeProviderProps) {
|
31 |
+
const [theme, setTheme] = useState<Theme>(
|
32 |
+
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
|
33 |
+
);
|
34 |
+
|
35 |
+
useEffect(() => {
|
36 |
+
const root = window.document.documentElement;
|
37 |
+
root.classList.remove("light", "dark");
|
38 |
+
|
39 |
+
if (theme === "system") {
|
40 |
+
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
41 |
+
.matches
|
42 |
+
? "dark"
|
43 |
+
: "light";
|
44 |
+
root.classList.add(systemTheme);
|
45 |
+
return;
|
46 |
+
}
|
47 |
+
|
48 |
+
root.classList.add(theme);
|
49 |
+
}, [theme]);
|
50 |
+
|
51 |
+
const value = {
|
52 |
+
theme,
|
53 |
+
setTheme: (newTheme: Theme) => {
|
54 |
+
localStorage.setItem(storageKey, newTheme);
|
55 |
+
setTheme(newTheme);
|
56 |
+
},
|
57 |
+
};
|
58 |
+
|
59 |
+
return (
|
60 |
+
<ThemeProviderContext.Provider {...props} value={value}>
|
61 |
+
{children}
|
62 |
+
</ThemeProviderContext.Provider>
|
63 |
+
);
|
64 |
+
}
|
src/pages/EditDataset.tsx
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import React from 'react';
|
3 |
+
|
4 |
+
const EditDataset = () => {
|
5 |
+
return (
|
6 |
+
<div className="min-h-screen bg-black text-white flex flex-col items-center justify-center p-4">
|
7 |
+
<h1 className="text-5xl font-bold tracking-tight">Edit Dataset</h1>
|
8 |
+
<p className="mt-4 text-xl text-gray-400">
|
9 |
+
This page is under construction.
|
10 |
+
</p>
|
11 |
+
</div>
|
12 |
+
);
|
13 |
+
};
|
14 |
+
|
15 |
+
export default EditDataset;
|