Qwen/Qwen3.5-2B, one of the supported base models.
Prerequisites
- Python 3.12+ and uv: The quickstart uses
uvto install the Loops client and run the training script. - API key: A workspace API key with org access to Loops, exported as
BASETEN_API_KEY.
Loops is in early access. To enable it for your workspace, fill out the signup form.
Install
Installbaseten-loops with the [tinker] extra into a uv project. Create one first if you don’t have it:
[tinker] extra pulls in baseten-loops-tinker, which re-exports the public API under the tinker namespace so existing import tinker scripts run unchanged.
Verify the install by running uv run python train_loops.py:
baseten-loops-tinker version confirm Baseten’s Tinker compatibility package is installed, not the upstream tinker package.
Provision a trainer
A Loops session pairs a trainer server (forward, backward, and optimizer steps) with a sampling server (generates from current weights). Constructing aServiceClient and calling create_lora_training_client() provisions both and returns a TrainingClient. The call blocks until the trainer is ready, which takes several minutes for a small base model like this one and can reach tens of minutes for the largest supported models. The SDK gives up waiting after an hour.
Replace the contents of train_loops.py with the provision step:
train_loops.py
Run a training round trip
The smallest complete round trip is one forward pass, one backward pass, one optimizer step, and one weight save. The block below mirrors the canonical supervised fine-tuning (SFT) example: it tokenizes a prompt-and-answer pair, masks the prompt positions from the loss, runs the round trip, and saves a named checkpoint. Append totrain_loops.py:
train_loops.py
forward_backward() is the first training operation you submit after provisioning. save_weights_for_sampler() publishes a sampler checkpoint under sampler_weights/ that you can deploy to inference. This checkpoint omits optimizer state, so you can’t resume training from it; use save_state() when you need a resumable checkpoint.
Sample from the tuned weights
The checkpoint you saved is already on the paired sampler, so you can generate from it without deploying anything.create_sampling_client() takes the bt:// URI that save_weights_for_sampler() returned and binds a SamplingClient to those weights. Append to train_loops.py:
train_loops.py
step-1 weights your trainer published seconds earlier, without a restart or a deploy step in between. In a longer run, this same call is how you evaluate checkpoints mid-training.
List checkpoints
Everysave_weights_for_sampler() call creates a checkpoint. The bound TrainingClient lists them with list_checkpoints(), no arguments needed. Append to train_loops.py:
train_loops.py
transformers about PyTorch being unavailable and from the Hugging Face Hub about unauthenticated requests. Both are harmless here: the client only uses transformers for tokenization, and the tokenizer download works without a token.
The HTTP API returns the same listing for scripts and CI pipelines that don’t run Python. Use the run_id your script printed when provisioning. The response includes the same globally unique id and checkpoint name:
get_checkpoint_archive_url() with the globally unique id value as the checkpoint_id argument. From a separate Python session where training_client isn’t in scope, construct tinker.ServiceClient() and call the same method on it. If the checkpoint files live in S3, export S3_REGION to that bucket’s AWS region first, for example export S3_REGION=us-west-2.
Skip the cold start on re-runs
Your first run provisioned a trainer and sampler. The second run doesn’t have to. Grab thesession_id your script printed (session_id=2qjl22w in the example output above), point the next run at it, and Loops reuses the same trainer and sampler:
reuse_from_session_id in the body of POST /v1/loops/runs or POST /v1/loops/samplers.
Reuse is best-effort. If the prior trainer is stopped, failed, or unhealthy, Loops provisions a fresh one and your script still runs.
Shut down the session
You’re billed for the trainer and sampler’s GPUs until you deactivate them. When you’re done experimenting, check what’s live and shut it down:truss loops view lists the deployments that are still running, with the ID, base model, and status of each. Pass the Deployment ID to deactivate. Your checkpoints survive the shutdown: you can still list them, fetch their files, and deploy them to inference afterward.
Next steps
- Deploy a checkpoint: Serve
step-1as a dedicated inference deployment and call it over the OpenAI-compatible route. - Train on a dataset: Replace the single example with a batched loop, resumable checkpoints, and mid-training evals.
- Loops concepts: Sessions, trainers, samplers, checkpoints, and how weight sync works.
- Tinker compatibility: What carries over from Tinker unchanged and what differs: checkpoint layout, authentication, and cluster routing.
- Loops API reference: Every HTTP route, for scripting deployments and CI pipelines.