Fine-tuning.
Train a LoRA or QLoRA adapter on a curated base model through an OpenAI-compatible API. You upload a dataset and submit a job; GPU.ai provisions the training GPU, runs it, and tears the GPU down when it finishes. You pay per GPU-hour for the time the run takes.
§ 07.1How it works¶
- Upload a JSONL chat dataset to
POST /v1/fileswithpurpose=fine-tune. The server validates it and returns a file id. - Create a job with
POST /v1/fine_tuning/jobs, referencing thattraining_fileand choosing a method (loraorqlora). - Track it with
GET /v1/fine_tuning/jobs/{id}and the events endpoint for milestone log lines. - Download the adapter once the job is
succeeded: the job'sresult_filesare short-lived signed URLs foradapter_model.safetensorsandadapter_config.json.
Everything lives under https://api.gpu.ai/v1 with Authorization: Bearer gpuai_live_.... The write calls (upload, create, cancel) need serverless:write; the reads (list, get, events) accept either serverless:read or serverless:write. A full_access key covers everything.
§ 07.2Base models¶
Fine-tuning runs against an operator-curated allowlist of tunable base models. Pass the id as the model field on a create request; anything outside the list is rejected with a 422.
| Base model id | Model |
|---|---|
qwen2.5-7b-instruct | Qwen 2.5 7B Instruct (Apache-2.0) |
The list grows over time. Want a base we don't carry yet? Tell us at support@gpu.ai.
§ 07.3Datasets¶
Datasets are JSONL, one JSON object per line, in the OpenAI chat format:
{"messages":[{"role":"system","content":"You are a terse assistant."},{"role":"user","content":"2+2?"},{"role":"assistant","content":"4"}]}
{"messages":[{"role":"user","content":"Capital of France?"},{"role":"assistant","content":"Paris"}]}Every line must be valid JSON with a messages array; roles are system, user, and assistant; content is a string; and each example needs at least one assistant message. You need at least 10 examples, and the dataset caps at 100 MiB. A malformed dataset is rejected at upload with an error naming the offending line, so you find out before a GPU is ever provisioned.
§ 07.4Endpoints¶
/v1/filesAUTH/v1/fine_tuning/jobsAUTH/v1/fine_tuning/jobsAUTH/v1/fine_tuning/jobs/{id}AUTH/v1/fine_tuning/jobs/{id}/eventsAUTH/v1/fine_tuning/jobs/{id}/cancelAUTHThe job list is cursor-paginated with ?after= and ?limit=, following the same conventions as the rest of the API — see conventions.
§ 07.5A full run¶
The API mirrors OpenAI's files and fine_tuning.jobs, so the official SDKs work unchanged. Two native extensions carry what OpenAI has no field for: method and gpuai.
# Upload the dataset and start a QLoRA job in one step
gpu fine-tune create \
--model qwen2.5-7b-instruct \
--dataset ./train.jsonl \
--method qlora
# Track it
gpu fine-tune get 3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34
gpu fine-tune events 3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34
# Download the adapter once it succeeds
gpu fine-tune download 3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34 --out ./adapters/3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34
# Stop early if you need to
gpu fine-tune cancel 3f9a2c71-8e4d-4b02-9f11-6c2d5a7e0b34§ 07.6Job lifecycle¶
| Status | Meaning |
|---|---|
queued | Accepted; waiting for a training GPU. |
running | Training in progress. |
succeeded | Finished. result_files holds the downloadable adapter. |
failed | Training failed; the job's error object says why. |
cancelled | Cancelled by you. The GPU is torn down and billing stops. |
created_at and finished_at are unix seconds. result_files is empty until the job succeeds, then holds freshly-signed download URLs regenerated on every read — they are short-lived and never persisted, so fetch the job again rather than caching a URL. fine_tuned_model is null: this release delivers the adapter as downloadable files rather than a hosted endpoint.
§ 07.7Billing & the budget cap¶
Fine-tuning bills per GPU-hour for the duration of the training run, not per token. A job that fails or is cancelled bills only for the GPU-hours it actually consumed.
§ 07.8Errors¶
Errors use the OpenAI envelope, so SDK error handling works as-is:
{ "error": { "message": "model is not tunable", "type": "invalid_request_error", "code": "unprocessable_entity" } }The two you are most likely to hit, both raised before any GPU is provisioned: a base model outside the allowlist returns 422 unprocessable_entity on create, and a malformed dataset returns 400 invalid_dataset on upload, naming the offending line. Note that a create call does not reserve the run's full cost up front, so keep enough balance to cover the GPU-hours you expect the job to take.