Skip to main content
If your training examples already exist outside the project, upload them as a JSONL file instead of recording traffic through the inference proxy. Model Distillation validates the file, assigns training and validation splits, and creates a dataset that works with relabeling, fine-tuning, and evaluations like any other dataset. Uploads are available only through the Management API. The UI shows the resulting dataset and an Uploaded source panel in the dataset settings, but it doesn’t offer a file picker. This page describes how an upload progresses, how to prepare the file, how to upload it in parts, and how to read the validation results.

Prerequisites

Before you begin, make sure you have the following:
  • A W&B API key for a team that has access to Model Distillation. The examples read it from the WANDB_API_KEY environment variable, pass it in the Authorization header, and name the entity in the Wandb-Entity header.
  • A project in that team. The project alias appears in every request path. To create one, see the Quick Start.
  • The tools for the tab you plan to use in Upload the file. You need only one of the following sets:
    • curl together with jq and split.
    • Python 3.9 or later with the requests package.
    • Node.js 18 or later, with no extra packages.

How an upload works

An upload is an import session scoped to a project. One session holds exactly one JSONL file, which you send directly to object storage in parts. The session moves through the following states: A session that is still uploading 7 days after creation expires. Part and completion requests to it return 410 Gone, and an hourly job moves it to expired and discards its parts. Uploaded objects and incomplete multipart uploads are deleted 8 days after they were written, whether or not the session completed. An entity is the team or personal account named in the Wandb-Entity header. Each entity can have two sessions in progress at a time, with up to 2 GiB of declared file size between them. A session counts toward both limits from creation until it reaches ready, failed, cancelled, or expired. A request that would exceed either limit returns 429 Too Many Requests. To free capacity, cancel a session you no longer need, or wait for a running session to finish.

Prepare the file

Before you create an import session, make sure the file matches the row format and stays within the limits described in this section. Validation runs only after you complete the upload, and a failed session can’t be reopened. To fix a formatting problem, create a new session and upload the file again. The file must be UTF-8 JSONL: one JSON object per line, with no duplicate keys within an object.

Row format

Each row is an OpenAI Chat Completions request whose last message is the assistant response to train toward. Model Distillation stores the preceding messages, plus any tools, tool_choice, and response_format, as the input, and the final assistant message as the output.
A row must contain at least two messages and can carry up to 1,000. tools accepts up to 128 function tools and 1 MiB of JSON. A row whose last message isn’t from the assistant fails with missing_assistant_target.

Optional fields

Rows can carry the following optional fields: Rows must not contain other top-level fields.

Limits

Files and import sessions must stay within the following limits:

Upload the file

An upload proceeds in stages: create the session, upload the file in parts, complete the upload, and poll until the dataset is ready. The following tabs show the whole flow as one script in curl, Python, and JavaScript. Numbered comments mark the stages, and How the script works explains each one. The scripts use a project alias of ticket-classifier and a file named tickets.jsonl. Before you run a script, replace the entity, alias, and filename with your own, and set WANDB_API_KEY in your environment. Use a new idempotency_key for each distinct upload.

How the script works

The numbered comments in each script correspond to the following stages.
1

Create the import session

The script sends the filename and exact byte size, the split policy, and an optional duplicate policy. To learn what each policy does, see Splits and duplicates. The request also requires a unique idempotency_key, so a retried request returns the existing session instead of creating a second one.The response is 201 Created with the session. The script keeps id, file.part_size_bytes, and file.part_count from it. The following excerpt shows those fields:
2

Request upload URLs

The script requests a signed URL for every part number from 1 through file.part_count. The response lists each part_number with its url, plus expires_in_seconds. Each URL is valid for 15 minutes. If the URLs expire before you use them, request them again.
3

Upload the parts

The script reads the file in file.part_size_bytes chunks and sends a PUT request with each chunk to the URL that matches its part number. Part numbers start at 1, so the first chunk is part 1. Every part except the last must be exactly file.part_size_bytes long, and the last part holds the remainder. You can send the parts in any order and in parallel.The signed URLs carry their own authorization, so the script doesn’t add the Authorization or Wandb-Entity headers to these requests.
4

Optional: Check which parts arrived

To resume after an interrupted transfer, read the session. file.parts lists each uploaded part with its size, and file.uploaded_bytes totals them. Request new URLs for any missing parts and send them again. The Python and JavaScript scripts compute the missing part numbers, and the curl script prints the list of uploaded parts. None of the scripts resends missing parts, because a single uninterrupted run doesn’t produce any.
5

Complete the upload

When every part is present, the script completes the upload. Model Distillation verifies that the parts match the declared size, computes a digest, and queues validation and dataset creation.The response is 202 Accepted. If a part is missing, the response is 409 Conflict with type upload_incomplete. If a part has the wrong size, the type is upload_manifest_mismatch. In both cases, the session stays in uploading, so you can fix the parts and complete the upload again. If you complete an upload that’s already complete, the request returns the current session without error.
6

Poll until the dataset is ready

The script reads the session every 10 seconds until state is ready or failed. While validation runs, validation.validated_rows counts parsed rows, and counters reports staged_rows, rejected_rows, and rows_by_split.When state is ready, dataset_id identifies the new dataset. Use it with relabeling, fine-tuning, and evaluations, or open it in the UI. If state is failed, see Read validation results.

Splits and duplicates

split_policy determines how rows are divided between training and validation:
  • preserve keeps the split value on each row. A row without split fails validation with missing_split.
  • automatic ignores any split value on the rows. It hashes each row’s group_id, or its row identity when group_id is absent, and assigns the fraction in val_fraction to the validation split. The default fraction is 0.2. Rows that share a group_id are always assigned to the same split.
duplicate_policy.split_overlap determines what happens when an identical input appears in both splits after assignment:
  • reject, the default, records a validation error for each overlapping row, and the import fails.
  • drop_train drops the training copies and keeps the validation copies.
A row_id that appears more than once in the file fails validation with duplicate_row_id under either policy.

Read validation results

A failed session reports error as a short summary, and validation carries the details:
  • error_count is the total number of problems found.
  • errors lists up to 100 problems, ordered by line, each with physical_line, code, and message.
  • errors_truncated is true when more problems exist than the list shows.
Common codes are invalid_json, invalid_utf8, duplicate_json_key, row_too_large, missing_assistant_target, tools_too_large, missing_split, and duplicate_row_id. Fix the file and create a new session. A failed session can’t be reopened.

Cancel an upload

To discard a session, delete it. Cancellation works in every state except ready, so you can also abandon a session that is still validating or that failed. The response is 204 No Content, the state becomes cancelled, uploaded parts are discarded, and any partially created dataset is removed. Repeating the request on a cancelled session returns 204 No Content again.
A session that already reached ready returns 409 Conflict with type dataset_import_ready. To remove the dataset it created, delete the dataset instead. See Delete a dataset.

Errors

Import session requests can return the following errors. For request and response schemas, see the following pages in the Management API reference:

Next steps

Relabeling

Ask a stronger model to rewrite the assistant responses in the uploaded dataset without changing the original rows.

Fine-tuning

Train a supported base model on the uploaded dataset, using the original outputs or a relabeled output set.
Last modified on September 21, 2026