Molbap's picture
|
download
raw
16.2 kB
# HF Jobs Development: operator and agent guide
This extension turns a Hugging Face Job into a remote GPU execution target while keeping the editor and source tree
local. Use the Code OSS commands for the interactive workflow and the `hf-dev` CLI primitives for automation.
## Mental model
```text
local Git checkout (authoritative)
|
| Mutagen one-way replica / sync-file
v
HF Job:/workspace/hf-dev/<repo-key>-<repo-name>
|
| editable install of the synchronized checkout
v
remote Python + CUDA
|
| debugpy over an SSH local port forward
v
local Code OSS debugger and local breakpoints
```
The local checkout is always authoritative. Do not make source changes only on the Job: Mutagen runs in
`one-way-replica` mode and can overwrite or remove remote-only changes. Edit locally, save, and let the extension sync.
## Requirements
- `hf >= 1.28.0`, authenticated with `hf auth login`.
- A token allowed to create/use Jobs and read the required Hub models. Bucket permissions are required only when the
optional persistent cache is explicitly enabled.
- An SSH public key registered at <https://huggingface.co/settings/keys>.
- `ssh`, `scp`, and Mutagen installed locally. A Homebrew Mutagen installation is supported on macOS.
- `hf-dev` installed on `PATH`, normally at `~/.local/bin/hf-dev`.
- The Code OSS Python and debugpy extensions for F5 debugging.
Check the local prerequisites without starting paid compute:
```bash
hf auth whoami
hf-dev doctor --repo "$PWD"
hf-dev plan --repo "$PWD" --flavor h200 --timeout 30m
```
`plan` prints the exact `hf jobs run` command but does not create a Job.
## Normal Code OSS workflow
1. Open the local repository in Code OSS.
2. Run **HF Dev: Start or Reconnect GPU Job** from the command palette or click the **HF Dev** status item.
3. Select hardware and one of the hard limits: `30m`, `1h`, or `8h`.
4. Edit and save files locally. Save events synchronize the file to the active Job.
5. Open the Python entry point and press F5. On macOS, use `fn`/Globe + F5 unless the function row is configured as
standard F-keys. F5 reveals the **HF Dev** Output channel without moving editor focus; remote stdout, stderr, and Hub
progress are streamed there rather than to `hf jobs logs`.
6. Run **HF Dev: Stop GPU Job** as soon as the GPU is no longer needed.
F5 is intercepted only for a Python editor when `hfDev.active` is true and no debug session is already active. During
an attached debug session, F5 keeps its normal Continue behavior. Without an active HF development Job, local F5 is
unchanged. HF Dev permits only one remote debugger per workspace: ending the local attach stops the matching remote
process, and a newer debug launch replaces an older process left by a disconnected editor.
## What `hf-dev up` does
For a newly created Job, `hf-dev up` performs these operations in order:
1. Resolve configuration and verify the current `hf` CLI version.
2. If and only if `cache_bucket` is explicitly configured, create or reuse that private Bucket and include it as a
read-write `HF_HOME` mount. The default MVP creates and mounts no Bucket.
3. Submit an SSH-enabled detached Job with the chosen image, flavor, and native Hub timeout. Without the opt-in mount,
Hugging Face libraries use the image's ephemeral Job-local cache.
4. Start a detached local watchdog. At the same hard deadline it checks the Job and sends `hf jobs cancel` if the Job
remains active. It retries after temporary network loss. The native Hub timeout remains a second guard.
5. Wait until SSH accepts connections.
6. Discover a usable remote Python interpreter (`python3`, `python`, common absolute paths, or an explicit setting).
7. Install the matching Mutagen agent through SSH and create a one-way replica of the local repository.
8. Bootstrap the remote environment. The default installs `debugpy` and installs the synchronized repository editable,
preferring `uv pip install -e .` and falling back to `python -m pip install -e .`.
9. Import `transformers` and verify that it resolves to `<remote_root>/src/transformers`, not the image's preinstalled
site-packages copy.
If setup of a newly created Job fails, `hf-dev` terminates its Mutagen session and attempts to cancel the Job
immediately.
## CLI primitives for people and agents
All repository-scoped commands accept `--repo PATH`; when omitted, `hf-dev` resolves the current Git root.
### Inspect without changing state
```bash
# Local recorded state only. Fast, but it can be stale after cancellation in the Hub UI.
hf-dev info --repo "$PWD"
# Local state plus authoritative remote Job status.
hf-dev info --repo "$PWD" --remote
# Authoritative Job details for the recorded Job.
hf-dev status --repo "$PWD"
# Check tools and versions without contacting Jobs.
hf-dev doctor --repo "$PWD"
# Preview a launch without starting paid compute.
hf-dev plan --repo "$PWD" --flavor h200 --timeout 30m
```
Agents should use `info --remote` or `status` before assuming paid compute is active. Plain `info` reports whether a
local state record exists; it deliberately avoids a Hub request.
### Start or reconnect
```bash
hf-dev up --repo "$PWD" --flavor h200 --timeout 30m
```
If the recorded Job is active, `up` reconnects, recreates synchronization, and revalidates the environment. If it is
terminal, `up` creates a new Job. Always choose an explicit flavor and timeout in automation so cost is visible in the
invocation.
Useful launch overrides:
```bash
hf-dev up --repo "$PWD" --flavor h200 --timeout 1h --cache-bucket my-org/shared-dev-cache
hf-dev up --repo "$PWD" --flavor h200 --timeout 1h --no-cache # override a TOML cache setting
hf-dev up --repo "$PWD" --flavor h200 --timeout 1h --image my-image:tag
```
### Synchronize local source
```bash
# Flush the Mutagen replica; recreate the session automatically if it is unhealthy.
hf-dev sync --repo "$PWD"
# Synchronize one file. The path must be inside the repository.
hf-dev sync-file --repo "$PWD" path/to/file.py
```
Mutagen ignores VCS data, virtual environments, Python caches, test/type/lint caches, build outputs, and
`node_modules`. The extension serializes save-triggered sync operations per workspace. A transient Mutagen flush is
retried and an unavailable session is recreated automatically.
### Run commands and open a shell
```bash
# Run from the synchronized remote repository root.
hf-dev run --repo "$PWD" 'nvidia-smi'
hf-dev run --repo "$PWD" 'python -m pytest tests/models/foo/test_modeling_foo.py -q'
# Open an interactive login shell already positioned at the remote repository root.
hf-dev shell --repo "$PWD"
```
Quote a compound remote command as one argument. Do not insert a standalone `--` after the `run` options; it would be
passed to the remote shell as part of the command.
`hf-dev run` is preferable to raw SSH for automation because it resolves the recorded Job, validates that it is active,
and changes to `remote_root` first.
### Use raw Job SSH
The JSON from `hf-dev info --remote` contains `namespace`, `job_id`, and `remote_root`. Given those values:
```bash
hf jobs ssh NAMESPACE/JOB_ID
```
The equivalent direct OpenSSH form is:
```bash
ssh JOB_ID@ssh.hf.jobs
```
Raw SSH starts outside the repository. Change to the `remote_root` reported by `hf-dev info` before running project
commands. Raw SSH is useful for an agent that needs an interactive TTY or a tool not wrapped by `hf-dev`; use
`hf-dev run` for ordinary non-interactive commands.
### Debug manually
Code OSS normally owns the attach sequence. The lower-level primitive is:
```bash
hf-dev debug --repo "$PWD" --port 5678 path/to/script.py arg1 arg2
hf-dev debug --repo "$PWD" --port 5678 --module pytest tests/path/test_file.py -k test_name
hf-dev debug-stop --repo "$PWD"
```
The command flushes source, verifies the editable import, opens an SSH local forward, starts remote debugpy, prints
`HF_DEV_DEBUG_READY`, and waits for a client. Attach a local debugpy client to `127.0.0.1:5678` with this mapping:
```text
localRoot = local repository root
remoteRoot = remote_root from hf-dev info
```
The extension obtains a free local port, starts this primitive, waits for the readiness marker, and invokes the Code
OSS debugger with `justMyCode: false`. Breakpoints must be placed in local files; path mapping associates them with the
synchronized remote files. Each debug invocation records a verified remote PID under `/tmp`; a new invocation stops
the previous debugger for that workspace before starting. `debug-stop` removes any current or legacy HF Dev debugpy
process whose working directory is the synchronized workspace. Debug stdout and stderr travel over SSH into the
extension's **HF Dev** Output channel; they are not emitted by the Job's `sleep infinity` container command and
therefore do not appear in `hf jobs logs`.
### Logs and shutdown
```bash
hf-dev logs --repo "$PWD"
hf-dev logs --repo "$PWD" --follow
# Terminates Mutagen, sends hf jobs cancel, and removes local state after success.
hf-dev down --repo "$PWD"
```
After work, verify that no development Job remains active:
```bash
hf jobs list --status RUNNING --label hf-dev=true
```
Cancellation through the Hub UI is valid, but local state remains until the next lifecycle action. Use
`info --remote` to distinguish stale local state from a running Job.
## How the extension maps to CLI primitives
- **Start or Reconnect GPU Job** -> `hf-dev up --flavor ... --timeout ...`
- **Sync Workspace Now** -> `hf-dev sync`
- save event -> `hf-dev sync-file <saved-file>`
- **Open Remote Shell** -> `hf-dev shell`
- **Run Remote Command** -> `hf-dev run '<command>'`
- **Debug Current Python File** / F5 -> save, `sync-file`, `hf-dev debug <relative-file>`, debugpy attach
- **Debug Current File with Pytest** -> save, `sync-file`, `hf-dev debug --module pytest <relative-file>`
- **Follow Job Logs** -> `hf-dev logs --follow`
- **Stop GPU Job** -> `hf-dev down`
The extension is intentionally a thin UI. Agents can use the same lifecycle entirely through `hf-dev` without Code
OSS.
## State, identity, and cache
Local state is stored per repository root under:
```text
~/.local/state/hf-dev/<repo-key>.json
```
Important fields include `job_id`, `namespace`, `remote_root`, `python`, `mutagen_session`, `timeout`,
`cancel_deadline`, `cancel_watchdog_pid`, `cache_bucket`, and `cache_home`.
The default Job name and Mutagen session contain a stable hash of the absolute local repository root. Consequently,
two clones in different local paths get separate state and remote workspaces.
The default uses the image's Job-local Hugging Face cache and creates no Bucket. Downloads are reused by later debug
runs within the same Job, then discarded when that Job ends. Every new Job starts cold.
Persistent caching is an explicit `--cache-bucket OWNER/NAME` or TOML opt-in. It mounts the Bucket read-write at
`/cache/huggingface`, sets it as `HF_HOME`, and disables symlinks. This avoids downloading the same revision again, but
large safetensors files are then read through the remote FUSE mount and can load more slowly than Xet into local Job
storage. Avoid concurrent writers and do not treat this optional mode as the MVP path.
The user's token is forwarded by secret name (`--secrets HF_TOKEN`), not embedded in the Job command. Do not print,
copy, or place token values in configuration or command arguments.
## Configuration
Configuration precedence, from lowest to highest, is built-in defaults, user TOML, repository TOML, and CLI options.
User configuration:
```text
~/.config/hf-dev/config.toml
```
Repository configuration:
```text
<repo>/.hf-dev.toml
```
Example:
```toml
[hf-dev]
image = "huggingface/transformers-all-latest-gpu"
flavor = "h200"
timeout = "1h"
python = "auto"
cache_bucket = false # default; optionally set an explicit private OWNER/NAME Bucket
forward_hf_token = true
# namespace = "my-org"
# resource_group_id = "..."
# identity_file = "~/.ssh/id_ed25519"
# bootstrap = "{python} -m pip install -q debugpy && {python} -m pip install -e ."
```
Code OSS settings:
- `hfDev.command`: launcher path; defaults to `hf-dev` and searches common user/Homebrew locations.
- `hfDev.syncOnSave`: automatically synchronize saved files; defaults to `true`.
- `hfDev.defaultFlavor`: initially selected hardware.
- `hfDev.defaultTimeout`: initially selected hard timeout (`30m`, `1h`, or `8h`).
Useful environment overrides are `HF_DEV_HF_BIN`, `HF_DEV_MUTAGEN_BIN`, `HF_DEV_SSH_BIN`, `HF_DEV_SCP_BIN`,
`HF_DEV_MUTAGEN_AGENTS`, `HF_DEV_CONFIG`, and `HF_DEV_STATE_DIR`.
## Safety rules for agents
- Never start a paid Job merely to inspect configuration; use `doctor`, `plan`, and local source inspection.
- Make the hardware flavor and timeout explicit before starting a Job.
- Use the shortest practical timeout. The extension offers `30m`, `1h`, and `8h`.
- Treat the local checkout as authoritative and never depend on remote-only source files.
- Expect model downloads to disappear with the Job. Persist checkpoints and other required outputs explicitly to a
user-chosen Hub repository or Bucket.
- On setup failure, verify cancellation. On normal completion, run `hf-dev down` and verify no labeled Job remains.
- The local watchdog supplements the Hub timeout but needs the local machine to reconnect after prolonged offline time.
- Do not manipulate another user's Job or Bucket unless explicitly authorized.
- This release targets single-node Jobs. Multi-node orchestration is not implemented yet.
## Troubleshooting
- `spawn hf-dev ENOENT`: install the launcher at `~/.local/bin/hf-dev` or set `hfDev.command` to its absolute path.
- `python: command not found`: current releases discover `python3` and common absolute interpreter paths. Reconnect with
`hf-dev up`; set `python` explicitly only for unusual images.
- `unable to flush session`: current releases retry Mutagen and recreate the session automatically. Confirm the Job is
still active with `info --remote` if recovery fails.
- Breakpoint is hollow or never hit: confirm the script reaches that code, ensure the local file was saved, and check
that the reported Transformers source is under `remote_root/src/transformers`.
- The Docker image already contains Transformers: this is expected. Bootstrap installs the synchronized checkout
editable, and debug startup refuses to proceed if `transformers.__file__` resolves to the image copy.
- First model load is slow: every new Job has a cold ephemeral cache by default. Later runs in the same Job reuse its
local files.
- Opt-in Bucket cache is unexpectedly slow: safetensors are read through the remote FUSE mount. Start a new Job with
`--no-cache`; an already-running Job cannot have its volume removed in place.
- Opt-in cache size nearly doubled after repeated F5: older releases could leave overlapping debug processes and a large
`.incomplete` download. Stop debugging, then run `hf cache prune --cache-dir /cache/huggingface/hub --yes` in the
remote shell. Release 0.1.6 and later prevent overlapping HF Dev debuggers for the same workspace.
- Job started on the wrong hardware: inspect `hf-dev plan` and choose a GPU flavor explicitly before `up`.
- Job was canceled in the Hub UI but Code OSS still shows active: local state is stale; use `info --remote`, then run
Start/Reconnect to replace the terminal Job.
- SSH authentication fails: confirm a public key is registered on the Hub and that the token can write to the Job's
namespace.
## Maintaining and shipping the extension
Validate and package from the extension directory:
```bash
node --check extension.js
python -m json.tool package.json >/dev/null
vsce package --allow-missing-repository --no-dependencies
code-oss --install-extension hf-dev-0.1.7.vsix --force
```
Publish matching launcher and VSIX artifacts to the public release Bucket:
```bash
hf cp ~/.local/bin/hf-dev hf://buckets/OWNER/hf-dev-release/0.1.7/hf-dev
hf cp hf-dev-0.1.7.vsix hf://buckets/OWNER/hf-dev-release/0.1.7/hf-dev-0.1.7.vsix
```
Keep the launcher and VSIX under the same version folder because the extension delegates all remote lifecycle work to
the external `hf-dev` executable.

Xet Storage Details

Size:
16.2 kB
·
Xet hash:
58f32217c3d3e34005d8f1f92a1b1029f7ee6d98f59587bea1a55793ad884c1d

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.