Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Install (editable, requires Python ≥3.10):
pip install -e .
```

`lerobot` is pinned to a release tag on `huggingface/lerobot` (currently `v0.6.1`, see [pyproject.toml](pyproject.toml)) — installed from git rather than PyPI because the published wheels don't expose the `lerobot-rollout` script that [lelab/rollout.py](lelab/rollout.py) shells out to. Bump the tag when you want a newer upstream release; expect import-path drift (e.g. `lerobot.configs.dataset`, `lerobot.utils.feature_utils`, `lerobot.common.control_utils`) and adjust call sites accordingly.
`lerobot` is pinned to a ref on `huggingface/lerobot` (currently `v0.7.0`, see [pyproject.toml](pyproject.toml)), installed from git rather than PyPI because the published wheels don't expose the `lerobot-rollout` script that [lelab/rollout.py](lelab/rollout.py) shells out to. Bump the ref when you want a newer upstream release; expect import-path drift (e.g. `lerobot.configs.dataset`, `lerobot.utils.feature_utils`, `lerobot.common.control_utils`) and CLI flag renames (v0.7.0 replaced `--wandb.*` with `--trackio.*`). [tests/test_lerobot_cli.py](tests/test_lerobot_cli.py) runs the training and rollout argv LeLab builds through the installed LeRobot's parsers, so run the tests right after a bump. Since v0.7.0 the arms' motors go through LeRobot's rustypot-backed `SerialMotorsBus`: use its public methods (`set_operating_mode`, `set_half_turn_homings`, `disconnect`), not Feetech SDK internals.

Run servers (entry point defined in [pyproject.toml](pyproject.toml)):

Expand All @@ -35,8 +35,8 @@ Run the Python tests with `pytest` (config in [pyproject.toml](pyproject.toml);

[server.py](lelab/server.py) is a thin FastAPI router. Each feature lives in its own module that owns its global state and exposes `handle_*` functions plus a Pydantic request model:

- [record.py](lelab/record.py) — dataset recording (wraps `lerobot.record.record`); patches `lerobot.common.utils.control_utils` keyboard listener so frontend buttons replace arrow-key controls. The worker is a daemon thread, so the server's shutdown hook calls `stop_recording_and_wait()` to let an active session finalize on Ctrl+C.
- [teleoperate.py](lelab/teleoperate.py) — leader→follower teleoperation (wraps `lerobot.teleoperate`).
- [record.py](lelab/record.py): dataset recording around LeRobot's `record_loop`; frontend buttons set the same `events` dict LeRobot's keyboard listener would. The worker is a daemon thread, so the server's shutdown hook calls `stop_recording_and_wait()` to let an active session finalize on Ctrl+C.
- [teleoperate.py](lelab/teleoperate.py): leader→follower teleoperation loop over LeRobot's SO-101 leader and follower.
- [replay.py](lelab/replay.py): plays a recorded episode's actions on the follower arm, an in-process take on `lerobot-replay` so the dataset browser can stop it.
- [calibrate.py](lelab/calibrate.py) — step-by-step web calibration with a `CalibrationManager` singleton and `_step_complete` threading.Event.
- [train.py](lelab/train.py) — wraps the LeRobot training CLI as a subprocess (psutil for lifecycle, queue for log streaming).
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/jobs/JobsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const JobsSection: React.FC = () => {
...j,
state: s.state,
metrics: s.metrics,
wandb_run_url: s.wandb_run_url,
tracking_url: s.tracking_url,
checkpoint_count: s.checkpoint_count,
};
});
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/training/PolicyExtraDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface Props {
open: boolean;
onOpenChange: (open: boolean) => void;
policyType: string;
packageName: string; // the probed module, e.g. "transformers"
packageName: string; // the requirements still unmet, e.g. "transformers<5.23.0,>=5.22.0"
installTarget: string; // e.g. "lerobot[smolvla]"
installHint: string; // e.g. "pip install 'lerobot[smolvla]'"
}
Expand Down Expand Up @@ -63,11 +63,11 @@ const PolicyExtraDialog: React.FC<Props> = ({
idleTitle={title}
idleDescription={
<>
Training a <span className="font-semibold">{policyType}</span> policy needs the{" "}
Training a <span className="font-semibold">{policyType}</span> policy needs{" "}
<code className="px-1 py-0.5 rounded bg-slate-900 text-sky-300">{packageName}</code>{" "}
package (installed via{" "}
(installed via{" "}
<code className="px-1 py-0.5 rounded bg-slate-900 text-sky-300">{installTarget}</code>),
which isn't in this environment yet. Install it to train this policy.
which this environment doesn't have yet. Install it to train this policy.
</>
}
doneDescription={<RestartInstructions purpose={`${policyType} training`} />}
Expand Down
68 changes: 0 additions & 68 deletions frontend/src/components/training/WandbInstallDialog.tsx

This file was deleted.

65 changes: 12 additions & 53 deletions frontend/src/components/training/config/AdvancedCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,74 +251,33 @@ const AdvancedCard: React.FC<ConfigComponentProps> = ({ config, updateConfig })
</div>
</section>

{config.wandb_enable && (
{config.trackio_enable && (
<>
<Separator className="bg-slate-700" />
<section className="space-y-4">
<SectionHeading>Weights & Biases</SectionHeading>
<SectionHeading>Trackio</SectionHeading>
<div>
<Label htmlFor="wandb_entity" className="text-slate-300">
W&B Entity (optional)
<Label htmlFor="trackio_space_id" className="text-slate-300">
Trackio Space (optional)
</Label>
<Input
id="wandb_entity"
value={config.wandb_entity || ''}
id="trackio_space_id"
value={config.trackio_space_id || ''}
onChange={(e) =>
updateConfig('wandb_entity', e.target.value || undefined)
updateConfig('trackio_space_id', e.target.value || undefined)
}
placeholder="your-username"
placeholder="your-username/lelab-trackio"
className="bg-slate-900 border-slate-600 text-white rounded-lg"
/>
</div>
<div>
<Label htmlFor="wandb_notes" className="text-slate-300">
W&B Notes (optional)
</Label>
<Input
id="wandb_notes"
value={config.wandb_notes || ''}
onChange={(e) =>
updateConfig('wandb_notes', e.target.value || undefined)
}
placeholder="Training run notes..."
className="bg-slate-900 border-slate-600 text-white rounded-lg"
/>
</div>
<div>
<Label htmlFor="wandb_mode" className="text-slate-300">
W&B Mode
</Label>
<Select
value={config.wandb_mode || 'online'}
onValueChange={(value) => updateConfig('wandb_mode', value)}
>
<SelectTrigger id="wandb_mode" className="bg-slate-900 border-slate-600 text-white rounded-lg">
<SelectValue />
</SelectTrigger>
<SelectContent className="bg-slate-800 border-slate-600 text-white">
<SelectItem value="online">Online</SelectItem>
<SelectItem value="offline">Offline</SelectItem>
<SelectItem value="disabled">Disabled</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center space-x-3">
<Switch
id="wandb_disable_artifact"
checked={config.wandb_disable_artifact}
onCheckedChange={(checked) =>
updateConfig('wandb_disable_artifact', checked)
}
/>
<Label htmlFor="wandb_disable_artifact" className="text-slate-300">
Disable Artifacts
</Label>
<p className="text-xs text-slate-500 mt-1">
Signed in, runs sync to a private lelab-trackio Space on your account by default.
</p>
</div>
</section>
</>
)}

{!config.wandb_enable && <Separator className="bg-slate-700" />}
{!config.trackio_enable && <Separator className="bg-slate-700" />}

{/* Misc */}
<section className="space-y-4">
Expand Down
63 changes: 13 additions & 50 deletions frontend/src/components/training/config/EssentialsCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { NumberInput } from '@/components/ui/number-input';
Expand All @@ -14,44 +14,13 @@ import {
import { ConfigComponentProps } from '../types';
import DatasetCombobox from '@/components/replay/DatasetCombobox';
import { DatasetItem } from '@/lib/replayApi';
import WandbInstallDialog from '../WandbInstallDialog';
import { useApi } from '@/contexts/ApiContext';

interface EssentialsCardProps extends ConfigComponentProps {
datasets: DatasetItem[];
datasetsLoading: boolean;
}

const EssentialsCard: React.FC<EssentialsCardProps> = ({ config, updateConfig, datasets, datasetsLoading }) => {
const { baseUrl, fetchWithHeaders } = useApi();
const [wandbDialogOpen, setWandbDialogOpen] = useState(false);
const [wandbInstallHint, setWandbInstallHint] = useState('pip install wandb');

const handleWandbToggle = async (checked: boolean) => {
if (!checked) {
updateConfig('wandb_enable', false);
return;
}
// Check availability before flipping the switch on. If wandb isn't
// importable in this lelab process, surface the same install flow used
// for the training extra (accelerate) instead of letting the user start
// a run that will fail.
try {
const r = await fetchWithHeaders(`${baseUrl}/system/wandb-extra`);
const data: { available: boolean; install_hint: string } = await r.json();
if (data.available) {
updateConfig('wandb_enable', true);
} else {
setWandbInstallHint(data.install_hint);
setWandbDialogOpen(true);
}
} catch {
// Backend unreachable — let the user proceed; training start will
// surface the real error if wandb is genuinely missing.
updateConfig('wandb_enable', true);
}
};

return (
<Card className="bg-slate-800/50 border-slate-700 rounded-xl">
<CardHeader>
Expand Down Expand Up @@ -140,35 +109,29 @@ const EssentialsCard: React.FC<EssentialsCardProps> = ({ config, updateConfig, d

<div className="flex items-center space-x-3 pt-6">
<Switch
id="wandb_enable"
checked={config.wandb_enable}
onCheckedChange={handleWandbToggle}
id="trackio_enable"
checked={config.trackio_enable}
onCheckedChange={(checked) => updateConfig('trackio_enable', checked)}
className="data-[state=checked]:bg-green-500"
/>
<Label htmlFor="wandb_enable" className="text-slate-300">
Enable Weights & Biases
<Label htmlFor="trackio_enable" className="text-slate-300">
Track with Trackio
</Label>
</div>
</div>

<WandbInstallDialog
open={wandbDialogOpen}
onOpenChange={setWandbDialogOpen}
installHint={wandbInstallHint}
/>

{config.wandb_enable && (
{config.trackio_enable && (
<div>
<Label htmlFor="wandb_project" className="text-slate-300">
W&B Project Name
<Label htmlFor="trackio_project" className="text-slate-300">
Trackio Project Name
</Label>
<Input
id="wandb_project"
value={config.wandb_project || ''}
id="trackio_project"
value={config.trackio_project || ''}
onChange={(e) =>
updateConfig('wandb_project', e.target.value || undefined)
updateConfig('trackio_project', e.target.value || undefined)
}
placeholder="my-robotics-project"
placeholder="lerobot"
className="bg-slate-900 border-slate-600 text-white rounded-lg"
/>
</div>
Expand Down
11 changes: 4 additions & 7 deletions frontend/src/components/training/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@ export interface TrainingConfig {
// Output configuration
resume: boolean;

// Weights & Biases
wandb_enable: boolean;
wandb_project?: string;
wandb_entity?: string;
wandb_notes?: string;
wandb_mode?: string;
wandb_disable_artifact: boolean;
// Trackio
trackio_enable: boolean;
trackio_project?: string;
trackio_space_id?: string;

// Policy-specific parameters
policy_device?: string;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/useInstallExtra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface UseInstallExtraResult {
}

/**
* Drives the backend extra-install flow (`accelerate`, `wandb`, …). Seeds state
* Drives the backend extra-install flow (`accelerate`, policy extras, …). Seeds state
* from `${endpointPrefix}/install-status`, polls while installing, and exposes
* install/retry handlers. Pass `enabled=false` to gate seeding on dialog open.
*/
Expand Down
13 changes: 5 additions & 8 deletions frontend/src/lib/jobsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,9 @@ export interface TrainingRequest {
save_freq: number;
save_checkpoint: boolean;
resume: boolean;
wandb_enable: boolean;
wandb_project?: string;
wandb_entity?: string;
wandb_notes?: string;
wandb_mode?: string;
wandb_disable_artifact: boolean;
trackio_enable: boolean;
trackio_project?: string;
trackio_space_id?: string;
policy_device?: string;
policy_use_amp: boolean;
optimizer_type?: string;
Expand Down Expand Up @@ -79,7 +76,7 @@ export interface JobRecord {
hf_flavor: string | null;
hf_repo_id: string | null;
hf_job_url: string | null;
wandb_run_url: string | null;
tracking_url: string | null;
checkpoint_count: number;
}

Expand All @@ -89,7 +86,7 @@ export interface JobProgressSnapshot {
id: string;
state: JobState;
metrics: TrainingMetrics;
wandb_run_url: string | null;
tracking_url: string | null;
checkpoint_count: number;
}

Expand Down
Loading
Loading