Frequently Asked Questions
Platform support
Running on Windows
Experimaestro creates symbolic links inside the workspace (e.g. to point at the
latest run of an experiment, to track partial dependencies, and for the
current symlink). On Windows, os.symlink requires either administrator
privileges or Developer Mode to be enabled (Settings → Update & Security →
For developers). If neither is available, the scheduler will fail with an
OSError when it tries to create those links.
Enable Developer Mode (recommended) or run the experiment from an elevated shell. SSH support is currently not functional on any platform and is unrelated to this requirement.
Controlling tasks
Wait a for a task to complete
# Submit the task
output = mytask.submit()
# Wait until it completes, and returns a boolean (success flag)
output.__xpm__.wait()
Data preparation
How do I pre-download datasets or resources before running on an offline cluster?
Use Prepare: a Config subclass whose prepare()
method is auto-invoked once before any task that references it. Library
helpers like datamaestro.prepare_dataset(...) already return Prepare
instances, so you usually do not write one by hand.
To pre-warm caches from a connected node (driver / login node) without launching any actual tasks:
uv run experimaestro run-experiment --run-mode prepare my_experiment.py
This walks every Prepare referenced in your submitted tasks’ params,
runs each prepare() exactly once in the driver process, and exits. This
holds for preparations that were submitted as jobs too: PREPARE mode
schedules nothing, so they also run in the driver.
Afterwards the compute nodes can run the experiment without internet
access:
uv run experimaestro run-experiment my_experiment.py
Where the data is stored. PREPARE mode creates no entries under
workspace/jobs/ — the only on-disk effect is what prepare() itself
writes (typically a tool-managed cache like
~/.cache/datamaestro/ or ~/.cache/huggingface/). In NORMAL mode the same
prep runs before each dependent task, plus each task gets its usual
workspace/jobs/<task-id>/<hash>/ folder for outputs and logs.
See Prepare configurations for the full design and the MNIST demo for a worked example.
Can I write my own Prepare for things other than datasets?
Yes — anything that needs a one-shot, idempotent setup before tasks run:
HF model downloads, credential fetching, populating a shared scratch
folder, building a Docker layer cache, etc. Subclass Prepare, override
prepare(self), and return it from a helper function that experiments
call:
from pathlib import Path
from experimaestro import Prepare, Param
class HFModelPrep(Prepare):
model_id: Param[str]
def is_prepared(self) -> bool:
from huggingface_hub import try_to_load_from_cache
return try_to_load_from_cache(self.model_id, "config.json") is not None
def prepare(self) -> None:
from huggingface_hub import snapshot_download
snapshot_download(self.model_id) # idempotent
def prepare_model(model_id: str) -> HFModelPrep:
return HFModelPrep.C(model_id=model_id)
Any task that takes an HFModelPrep in its params will have the download
triggered automatically before it runs.
My preparation is too heavy for the login node — can it run on a compute node?
Yes: call submit() on the Prepare and it runs as a job, with a launcher
and a resource request of its own.
dataset = prepare_dataset("irds.msmarco-passage").submit(
launcher=preprocessing_launcher
)
Train.C(dataset=dataset).submit(launcher=gpu_launcher)
This requires the Prepare to implement is_prepared(), since the
preparation writes to a cache directory rather than to its job directory —
that check, and not the job’s .done marker, is what decides whether the
work still has to be done. Downstream identifiers are unaffected, so moving
a preparation to a job does not invalidate anything you have already
computed. See
Preparing as a job.
Post-experiment workflows
How do I run code after my experiment finishes?
Use Action (alpha). An Action is a Config whose
execute(interaction) runs after the experiment completes — for picking
the best model, pushing to HF Hub, copying artefacts to a results folder,
sending Slack notifications, etc.
Register an Action with the experiment during submission:
from experimaestro import Action, Interaction, Param
class CopyResults(Action):
runs: Param[list[MyEvalTask]]
def describe(self) -> str:
return "Copy results to a destination folder"
def execute(self, interaction: Interaction) -> None:
dest = interaction.text("dest", "Destination folder:", default="./results")
# ... copy logic
# in experiment.py
helper.xp.add_action(CopyResults.C(runs=evaluations))
Then list and run the registered actions:
uv run experimaestro experiments actions list <experiment-id>
uv run experimaestro experiments actions run <experiment-id> <action-id>
Actions can also be invoked from the TUI / web UI. See
Experiment Actions for the full API and the
MNIST demo for the
ExportBestModel example.
Debugging
How to run a task without scheduling it?
Method 1: creates an instance of a task with its .instance(context) method. The context contains information which might
be necessary to create the directory structure for executing the task. If no context is provided, the default context
is used.
context = DirectoryContext("/tmp/taskfolder")
task.instance(context).execute()
The main problem with this approach is that resources are shared between experimaestro and the task
How to Debug a failed task ?
If a task failed, you can rerun it with debugpy.
Using vsCode
If the task is already generated, you can run it with the python debugger directly within vsCode.
open the task python file
.../HASHID/task_name.py.Run the dubugger Using the following configuration:
In .vscode/launch.json :
{
"name": "Python: XPM Task",
"type": "debugpy",
"request": "launch",
"module": "experimaestro",
"console": "integratedTerminal",
"justMyCode": false,
"args": [
"run",
"params.json"
],
// "python": "${workspaceFolder}/.venv/bin/python",
"cwd": "${fileDirname}",
"env": {
"CUDA_VISIBLE_DEVICES": "1",
}
}
NOTE: if the task needs GPU support, you may need to open VS-Code on a node with access to a GPU.