Skip to content

CLI reference

The distil CLI drives the whole platform. Install it from Install the CLI. Check your version with distil --version, run distil update to move to the current one, and distil <group> --help for a command’s flags.

Every stage is an entity created by one command, from local files or by running a job over the entity before it. How the platform works explains the chain.

Stage Entity it creates
Traces traces object (PreparedTraces)
Dataset, from files or from a traces object dataset (Dataset)
Relabel traces, test or train dataset, with the parent’s ID and the operation
Synthetic data generation, test or train dataset, with the parent’s ID and the operation
Teacher evaluation teacher evaluation (TeacherEvaluation)
Model training model (SLM)
Deployment deployment (Deployment)
Inference endpoint inference endpoint (InferenceEndpoint)

The command that creates each one:

distil traces upload --traces <file>
distil traces create-from-inference-endpoint <unique-endpoint-name>
distil dataset create --data <dir>
distil dataset create-from-traces --config <file> --job-description <file> <traces-id>
distil dataset relabel-traces-test [--smoke] <dataset-id>
distil dataset relabel-traces-train [--smoke] <dataset-id>
distil dataset generate-synthetic-data-test [--smoke] <dataset-id>
distil dataset generate-synthetic-data-train [--smoke] <dataset-id>
distil teacher-evaluation create-from-dataset <dataset-id>
distil slm create-from-dataset [--smoke] <dataset-id>
distil deployment create-from-slm <slm-id>
distil inference-endpoint create-from-deployment --name <prefix> \
  --fallback-model <owner/model> <deployment-id>

The commands that read each one, as subcommands of its group:

Command group Read subcommands
distil traces download
distil dataset status, logs, metrics, download, download-metadata
distil teacher-evaluation status, logs, metrics, download-metadata, download-predictions
distil slm status, logs, metrics, download, download-metadata, download-predictions
distil deployment status, logs
distil inference-endpoint download-traces

Every group also has list (alias ls) and show. status and logs on a dataset report the expand job that produced it; a dataset created from files or from a traces object has no job, so its status reads Success at once and its logs are empty.

distil seed-dataset and distil training-dataset read old entities: list, show, download and download-metadata. Their other commands print the replacement and exit 1. See Migrating old entities.

You can create an account and sign in without leaving the terminal. distil signup (aliases register, join) creates an account, distil auth (alias login) signs in to an existing one, distil whoami prints the current user, distil access-token prints an access token for the REST API, and distil logout signs out:

distil signup
distil auth
distil whoami
distil access-token
distil logout

distil signup and distil auth are the same command: each prints a one-time code, opens the sign-in page in your browser, and finishes with Logged in as <email>. The full flow and its messages are on Account and authentication. The session is kept in ~/.config/distillabs/session.json (under $XDG_CONFIG_HOME when set) and refreshed, so a run spanning hours needs no second login.

distil access-token refreshes the session and prints a new access token, and nothing else, so it drops straight into a header:

curl -H "Authorization: Bearer $(distil access-token)" https://api.distillabs.ai/datasets

Tokens are short-lived, so fetch a new one rather than holding it. Signed out, the command exits 1 with Not logged in or session expired. See Using the REST API.

--output json (-o json) is registered on every command that reads, creates, deletes, links or downloads. It prints one JSON object or array on stdout and nothing else:

  • reads and creates print the entity as the API returns it; inference-endpoint create-traces-export adds the file it wrote under files;
  • deletes print {"id": ..., "deleted": true} (name instead of id for api-keys delete);
  • inference-endpoint link-api-key and unlink-api-key print {"unique_endpoint_name": ..., "api_key_name": ..., "linked": true} (false after unlinking);
  • downloads print {"files": [...]} with every file written, or an empty list when there was nothing to write.

An error prints {"error": "..."} and exits 1. --output isn’t registered on auth, signup, logout, access-token (it already prints the bare token), update, docs, skill install, skill uninstall or the deprecated commands; passing it there fails with No flag registered for --output.

Without --output json a command prints its result formatted for reading, which is what the examples on this page show. Use the JSON form in scripts; the agent skill does.

--data <dir> names a directory, and the CLI uploads the files in it by name.

Command Required in --data <dir> Optional
distil traces upload traces.jsonl none
distil dataset create config.yaml, job_description.json train.jsonl, test.jsonl, traces.jsonl; a missing one is stored as an empty split
distil slm create model.tar, config.yaml none

config.yml is accepted for config.yaml. The per-file flags (--traces, --train, --test, --config, --job-description, --model) replace one path each, and combining them with --data is refused; with per-file flags, dataset create needs --config and --job-description and any of the other three. Only the files you give are uploaded: the platform reads a missing data file as “this dataset has none of this kind” (Dataset overview). A file named but not found is reported before anything uploads.

The download commands write these same names, so a downloaded directory is accepted unchanged by dataset create --data. That is how a change to the rows is made, since no override reaches the data.

distil dataset create-from-traces takes --config and --job-description as required inputs, because a traces object holds neither file. distil slm create --data <dir> registers an existing model.tar and config.yaml as an SLM; the tarball must hold the LoRA adapter under model-adapter/. It doesn’t train a model.

Print your credit balances:

distil credits-balance

The output has one row per metered route, sorted by name. An excerpt:

datasets_from_datasets_generate_synthetic_data_post    5
datasets_from_datasets_relabel_traces_post             5
datasets_from_datasets_smoke_post                     20
datasets_from_prepared_traces_post                     5
datasets_post                                          5
prepared_traces_post                                 100
slms_from_datasets_post                                5
slms_from_datasets_smoke_post                         20
teacher_evaluations_post                               5

An account with nothing metered prints No metered endpoints. A --smoke submission spends the *_smoke_post key of its route. The route keys and starting balances are in How the platform works.

Upload a trace file, or have the platform pull an endpoint’s records:

distil traces upload --traces traces.jsonl
distil traces create-from-inference-endpoint <unique-endpoint-name>

Both print Prepared traces created. ID: <traces-id>. traces upload checks only that the file is there; a malformed trace fails the first expand that reads it, and the job log names the cause. --data <dir> with a traces.jsonl in it replaces --traces.

From a traces object, with the config and the job description written first (Dataset overview):

distil dataset create-from-traces --config input/config.yaml \
  --job-description input/job_description.json <traces-id>

The command prints Dataset created. ID: <dataset-id>. No job runs: the traces are copied, train and test are empty, and the dataset is ready at once. The trace file’s shape must match trace_processing.observation_format in the config: langfuse for a traces object from an endpoint (Traces). A traces.jsonl downloaded from such a traces object uploads again with dataset create --traces and the same langfuse setting, which is how a test set is put next to new endpoint traces (Model iterations).

From files on disk, any subset of the three data files:

distil dataset create --data input
distil dataset create --config input/config.yaml --job-description input/job_description.json \
  --traces traces.jsonl --test test.jsonl

dataset create validates the files before creating anything, exactly as the jobs will parse them. An invalid bundle exits 1 with the reason on stderr and creates nothing: a file missing from the directory is caught locally, and an unsupported task, a row in the wrong shape or a row present in both splits come back from the platform with the row named (Uploads and validation).

Each expand creates a new dataset whose parent is the one you name, and prints the new ID (The expand operations). Relabel traces into the test split or the train split:

distil dataset relabel-traces-test <dataset-id>
distil dataset relabel-traces-train <dataset-id>

The count comes from trace_processing.num_test_relabelled or num_train_relabelled in the parent’s config, or from the override. Generate synthetic rows into the test split or the train split:

distil dataset generate-synthetic-data-test <dataset-id>
distil dataset generate-synthetic-data-train <dataset-id>

The target comes from synthgen.test_generation_target or train_generation_target. All four take --smoke, which forces the count or the target to 128 and spends the smoke credit (Smoke runs):

distil dataset relabel-traces-test --smoke <dataset-id>
distil dataset generate-synthetic-data-train --smoke \
  --config synthgen/smoke-2/config.yaml <dataset-id>

Poll the new dataset’s status until it reads Success:

distil dataset status <new-dataset-id>

Six creates accept --config <file> (-c) and --job-description <file>: the four expand commands, teacher-evaluation create-from-dataset and slm create-from-dataset. Both flags are optional and independent; an omitted one is inherited from the parent. Each file replaces the parent’s whole (Overrides), so read the parent’s file, edit it, and send it back. Download the parent’s files into iter-2, which is free:

distil dataset download-metadata -d iter-2 <dataset-id>

dataset download-metadata writes config.yaml and job_description.json and nothing else. Edit iter-2/config.yaml and submit it with the create:

distil dataset generate-synthetic-data-train --config iter-2/config.yaml <dataset-id>

The config of a dataset written by an expand is fully expanded: every default written out, keys sorted, comments dropped. A dataset from dataset create --data returns its config as uploaded; one from dataset create-from-traces returns it normalized (comments dropped).

A config with a complete base but no synthgen or tuning section is accepted, and those sections take their defaults with no error. A config missing base.task is refused with Invalid config: ... base.task Field required. To check what a submission ran with, read the new dataset’s config back and diff it against what you sent.

Each create prints the new entity’s ID. Teacher evaluation:

distil teacher-evaluation create-from-dataset <dataset-id>

Model training, as a smoke run and as a full run:

distil slm create-from-dataset --smoke <dataset-id>
distil slm create-from-dataset <dataset-id>

--smoke runs the small version of the job (Smoke runs) and combines with the override flags. Training takes its config from the dataset. A sweep is one submission per config against the same dataset; submissions run concurrently. Retraining the same dataset under a new config regenerates nothing:

distil dataset download-metadata -d retrain <dataset-id>
# in retrain/config.yaml, under tuning: num_train_epochs: 6
distil slm create-from-dataset --config retrain/config.yaml <dataset-id>

<group> is a command group such as slm or dataset, and <id> is the entity’s ID:

distil <group> status <id>
distil <group> logs <id>
distil <group> list

status prints the job’s status; the values are in Job status. Deployments report deployment_status and endpoint_status instead.

Read the status field, not the exit code: a status command that reaches the platform exits 0 whatever the job did. Creates and downloads report through the exit code: a rejected bundle, an invalid config or a download of something not produced yet exits 1 with the reason on stderr (Dataset <id> is not ready. Its job status is JOB_RUNNING.).

logs prints the whole job log. It fills while the job runs; in the first seconds it reads No logs for this dataset.

list returns one object per entity, newest first, with id, created_at and the parent’s ID under its own key (dataset_id, slm_id, and so on). A dataset also carries its operation and its parent as parent_dataset_id or parent_prepared_traces_id; a traces object its source. dataset show <id> prints the same for one dataset, plus its status (Success at once for a dataset no job produced). list is how you recover a lost ID, and show walks a chain back to its root.

Read the scores of each stage that has them, then download the per-example predictions:

distil teacher-evaluation metrics <teacher-evaluation-id>
distil slm metrics <slm-id>

distil teacher-evaluation download-predictions <teacher-evaluation-id>
distil slm download-predictions <slm-id>

The *_performance object maps metric name to score (Metrics). A metric the run didn’t compute is null rather than absent. A predictions file is JSONL, one test example per line, with the prompt (the test row’s messages), the reference completion, the prediction and that example’s score under each metric name. slm download-predictions holds the tuned model’s predictions only; the base model’s scores are in slm metrics.

Download the whole dataset into data, count the rows, and read the sizes:

distil dataset download -d data <dataset-id>
wc -l data/train.jsonl data/test.jsonl data/traces.jsonl
distil dataset metrics <dataset-id>

Both are free. download writes all five files; an empty split is an empty file. metrics reports train_data_size_bytes, test_data_size_bytes and traces_size_bytes. The rows of a split are the parent’s rows followed by the ones the expand added; to see only the new rows, download the parent too and diff.

Entity Data files Config + job description
Traces object traces download none: it holds neither
Dataset dataset download dataset download-metadata
Teacher evaluation none teacher-evaluation download-metadata
SLM slm download slm download-metadata

Every command that writes a directory takes --destination <dir> (-d) and otherwise names one after the entity (<id>-traces, <id>-data, <id>-metadata, <id>-slm). The *-predictions commands write a single file, take --file-name <path> (-d), and default to <id>-teacher-evaluation-predictions.jsonl or <id>-slm-predictions.jsonl. Downloads overwrite what is there.

distil slm download writes model.tar (the LoRA adapter under model-adapter/, the inference client and the licenses) and config.yaml, and checks free disk space first. distil slm download-metadata writes config.yaml, job_description.json and model_client.py, a few kilobytes.

Deploy a model, check its status, and delete it:

distil deployment create-from-slm <slm-id>
distil deployment status <deployment-id>
distil deployment delete <deployment-id>

Poll deployment_status to JOB_SUCCESS before creating the endpoint; create-from-deployment refuses earlier with Deployment is not ready. After deployment delete, deployment_status stays JOB_SUCCESS; endpoint_status reading stopped confirms the deployment is down. How deployments behave: distil labs inference.

Create an endpoint, with or without a deployment behind it, and list or show endpoints. list has the alias ls:

distil inference-endpoint create --name <prefix> \
  --fallback-model <owner/model> [--trace-sample-rate <0-1>]
distil inference-endpoint create-from-deployment --name <prefix> \
  --fallback-model <owner/model> <deployment-id>
distil inference-endpoint list
distil inference-endpoint show <unique-endpoint-name>

For example, this creates an endpoint with the prefix support:

distil inference-endpoint create --name support --fallback-model "openai/gpt-4.1-mini"

The command prints the unique endpoint name on its Endpoint Name line, such as support-yeOdAS. create makes an endpoint in front of your production model; create-from-deployment one with a deployed model as its primary. --name is a prefix: record the unique endpoint name the command prints, which every other command and the model field of every request take. --fallback-model is an OpenRouter slug in owner/model form. --trace-sample-rate sets the fraction of calls recorded, default 1; show reports it as trace_sampling_rate.

Create, list and delete keys, and link a key to an endpoint or unlink it:

distil api-keys create <key-name>
distil api-keys list
distil api-keys delete <key-name>
distil inference-endpoint link-api-key <unique-endpoint-name> <key-name>
distil inference-endpoint unlink-api-key <unique-endpoint-name> <key-name>

api-keys create prints the secret once and writes it to <key-name>.json. --no-file skips the file, for a key that goes straight into a secret store.

Call an endpoint with a chat completions request, where support-yeOdAS is the unique endpoint name and <api-key> is the secret of a key linked to it:

curl https://inference.distillabs.ai/v1/chat/completions \
  -H "Authorization: Bearer <api-key>" -H "Content-Type: application/json" -d '{"model": "support-yeOdAS", "messages": [{"role": "user", "content": "Say hi."}]}'

model carries the unique endpoint name; the rest is an ordinary chat completions request. A trained model behind an endpoint is queried with its own client: Querying your model.

Download the newest 1,000 records from the last day, every record since a date, up to 5,000 records, or records into a named file:

distil inference-endpoint download-traces <unique-endpoint-name>
distil inference-endpoint download-traces --from-start-time 2026-09-01 --all <unique-endpoint-name>
distil inference-endpoint download-traces --count 5000 <unique-endpoint-name>
distil inference-endpoint download-traces --file-name raw-traces.jsonl <unique-endpoint-name>

The command writes JSONL, one record per call, to <unique-endpoint-name>-traces.jsonl unless --file-name says otherwise. --from-start-time and --to-start-time take an ISO 8601 date or date-time; the default window is the last day. Within it, --count (-c) caps the records (default 1000) and --all takes every one; passing both is refused. With no records in the window, no file is written.

The file holds endpoint records, not traces: convert it as in Endpoint records to traces and upload the result with distil dataset create --traces or distil traces upload --traces.

Create a traces object from the endpoint’s records, with no download:

distil traces create-from-inference-endpoint <unique-endpoint-name>

The command prints Prepared traces created. ID: <traces-id>. The platform pulls the records, so nothing is downloaded or converted; the dataset you create from it needs trace_processing.observation_format: langfuse in its config. Which records it includes and when: Endpoint records to traces.

Command What it does
distil --version Prints the installed version
distil update Replaces the binary in place
distil docs Opens these docs
distil skill install Installs or updates the agent skill for every coding agent
distil skill uninstall Removes the agent skill