once you have your environment and dataset ready, launching a training run consists of the following steps:
- make sure to actually validate everything before you start a training run. see testing for more information.
- bundle your environment, then upload the bundle and datasets
- (optional) adjust any training parameters
- launch the training run!
if you scaffolded your project with castform setup, all of this is already wired into main.py: bare python main.py prepares data, uploads, and validates the uploaded assets, and python main.py launch runs the same path and then a confirmed launch that trains on precisely what was validated. the calls are worth knowing either way.
quickstart
launching is three calls: dump_bundle serializes your environment, upload_assets uploads the bundle and datasets, and TrainerClient.launch_training_run starts the job.
import dataclasses
from benchmax.bundle import dump_bundle
from castform.platform import ensure_session
from castform.platform.environment_assets import upload_assets
from castform.platform.client import TrainerClient
ensure_session()
# 1. bundle the env (via cloudpickle): the class + its constructor args
bundle = dump_bundle(
MyEnv,
constructor_args={"search": search}, # kwargs passed to MyEnv.__init__
pip_dependencies=["some-package>=1.0"], # installed remotely before unpickling
)
# 2. upload the bundle + datasets (stored under one dataset prefix)
uploaded = upload_assets(
bundle=bundle,
train_dataset=train_data,
eval_dataset=eval_data,
run_name="my-run",
)
# 3. start the job
trainer = TrainerClient()
run_id = trainer.launch_training_run(
**dataclasses.asdict(uploaded),
)
# view at https://app.castform.com/experiments/{run_id}
bundle parameters
dump_bundle captures project-local Python modules reachable from your environment automatically. list every package the environment imports at rollout time in pip_dependencies; source from a different project must be passed through local_modules or named as an installed distribution.
| parameter | default | description |
|---|---|---|
env_class | required | your environment class — a BaseEnv or HarborEnv subclass (first positional argument) |
constructor_args | None | dict of kwargs passed to your environment’s __init__. must be pickle-safe: use connection parameters (URLs, keys, config dicts), not SDK clients or open connections |
pip_dependencies | None | pip packages to install on the remote machine before unpickling (PEP 508 strings) |
local_modules | None | module objects from outside your project to pickle by value |
upload parameters
upload_assets writes train_dataset as train.jsonl and eval_dataset as eval.jsonl, plus any dataset_files, under a single dataset prefix. the returned dataset_path is that blob prefix (None when no dataset files were supplied), and launch_training_run accepts it as its dataset_path argument.
| parameter | default | description |
|---|---|---|
bundle | required | the Bundle returned by dump_bundle |
train_dataset | None | list of dicts, training examples. omit when the environment resolves this split at runtime (e.g. Harbor); [] uploads an empty JSONL deliberately |
eval_dataset | None | list of dicts, evaluation examples, same semantics |
dataset_files | None | extra dataset content: relative file name mapped to bytes, text, or a local path; uploaded under the same prefix as the JSONL splits |
run_name | required | name for the run (used in upload paths) |
api_key | None | castform API key; when omitted the bearer resolves from the active credential seam |
base_url | config.platform_url() | platform URL override |
training parameters
pass training hyperparameters via launcher_args to control the training loop:
run_id = trainer.launch_training_run(
**dataclasses.asdict(uploaded),
launcher_args={
"model": "Qwen/Qwen3.5-4B",
"learning_rate": 1e-5,
"num_epochs": 5,
"group_size": 9,
"max_context_tokens": 8000,
"lora_rank": 128,
"lora_alpha": 256,
},
)
the platform validates launcher_args against a fixed schema; unknown keys
return a 400. for the authoritative list of accepted args (names, defaults,
ranges, soft caps), call:
trainer.print_launch_args() # human-readable
trainer.list_launch_args() # programmatic: list[LaunchArgSpec]
or hit GET /v1/train/launch-args directly.
| parameter | type | default | description |
|---|---|---|---|
model | string | Qwen/Qwen3.5-4B | huggingface model id. see supported models |
learning_rate | number | 1e-5 | adam learning rate |
num_epochs | integer | 5 | passes over the training dataset. kept small by default for fast first-launch feedback; increase for longer runs |
group_size | integer | model default | rollouts generated per prompt for GRPO advantage estimation |
max_context_tokens | integer | model default | total prompt and response tokens across the whole rollout. values above 32768 trigger a memory-risk warning |
max_train_examples | integer | — | optional source-level limit applied while the environment constructs the training dataset |
max_eval_examples | integer | — | optional source-level limit applied while the environment constructs the evaluation dataset |
lora_rank | integer | 128 | LoRA rank, controlling the dimensionality of the low-rank adapter matrices |
lora_alpha | number | 256 | LoRA alpha, scaling the adapter output. convention is 2x lora_rank |
kl_loss_coef | number | 0 | KL penalty coefficient. 0 disables the penalty |
eval_interval | integer | — | how often (in rollout steps) to run the eval loop. defaults to 20 when omitted |
hf_checkpoint | string | — | huggingface repo id of a compatible custom starting checkpoint |
next steps
- monitoring a run: track metrics, inspect completions, debug reward behavior
- evaluating a run: compare your model against baselines
- testing: validate your environment before launching