Skip to main content
The fine-tuning API accepts two file formats: JSONL for text data and Parquet for pre-tokenized data. Pick JSONL unless you need to set custom attention masks or labels, or you want to skip tokenization to speed up repeated experiments. The file size limit for either format is 100 GB. The file ID returned by client.files.upload() is what you pass as training_file to a fine-tuning job.

Pick a data format

Each line of a JSONL file is one training example, formatted to match your task. If the same file has two possible formats (for example both text and messages), the server rejects it. Trim unused fields before upload to speed up data transfer.

Conversational data

Conversations are represented using a messages array. Each message has a role (system, user, or assistant) and content. The conversation must start with system or user and alternate user and assistant afterwards.
By default, training computes loss only on assistant messages. Pass train_on_inputs=True to include the rest. To mask or weight individual messages, see Data weights. The dataset is automatically formatted into the model’s chat template if one is defined. Instruction-tuned models always have a chat template; base models usually don’t. Example datasets:

Instruction data

Each line carries a prompt and a completion field.
By default, training computes loss only on completion. Pass train_on_inputs=True to include prompt. To scale a sample’s contribution to the loss, see Data weights. Example datasets:

Generic text data

Each line carries a single text field. Use this for plain text completions.
Example datasets:

Preference data

Used for preference fine-tuning with DPO. Each line carries:
  • input.messages: a context in conversational format.
  • preferred_output: a single assistant message representing the ideal response.
  • non_preferred_output: a single assistant message representing the suboptimal response.
Each output must contain exactly one assistant message.

Tool-calling data

For training a model to invoke tools, the line carries a tools array listing the available tools. Assistant messages can include tool_calls instead of content, and tool-role messages carry call results. See function-calling fine-tuning for the end-to-end workflow.
For preference fine-tuning, the tools field nests inside input:

Reasoning data

For fine-tuning reasoning models, assistant messages support a reasoning or reasoning_content field that carries the chain of thought. See reasoning fine-tuning for the full workflow.
When fine-tuning reasoning models on conversational data, only the last assistant message is trained on by default. For multi-turn reasoning, split the conversation so each assistant message is the final message in its own example.
Reasoning models should always be fine-tuned with reasoning data. Training without it can degrade the model’s reasoning ability. If your dataset doesn’t include reasoning, use an instruct model instead.
For preference fine-tuning, both outputs carry reasoning:

Data weights

Two independent controls adjust how much each part of your data contributes to the training loss. You can use either one on its own or both together in the same file.

Per-message weights

Set a weight on an individual message to control whether it contributes to the loss. Only 0 and 1 are supported: a message with weight=0 is masked, and weight=1 includes it. This is a finer-grained version of train_on_inputs, letting you mask or include specific messages rather than whole roles. Per-message weights are only available for conversational data, since they weight individual messages.

Sample weights

Set a root-level weight on a line to scale that entire sample’s contribution to the loss. It’s a non-negative floating-point multiplier applied to the sample’s tokens, and it works with every JSONL format and training method, including instruction data.

Combining weights

You can set per-message weights and a sample weight in the same conversational file. The sample weight scales the loss for the whole line, and the per-message weights determine which messages within it contribute.

Packing

For JSONL training data, Together uses sample packing: multiple short examples are concatenated up to max_seq_length so each training window uses the full context length instead of being padded out. Packing is enabled by default and makes the effective batch size larger than the batch_size you set, which significantly reduces the total number of training steps and overall training time. To control packing, either set the packing flag to false for JSONL input, or supply a pre-tokenized Parquet file. The packing flag applies only to JSONL input; it has no effect on Parquet data.

Tokenized (Parquet) data

Use Parquet when you want to skip tokenization on every job, customize attention masks or labels, or run with a tokenizer that differs from the base model’s. The file must be .parquet and under 100 GB. Allowed fields:
You don’t need to shift labels relative to input_ids. The trainer shifts them internally for next-token prediction.
Here’s a worked example with the together-py tokenize_data.py script:
If --packing is passed, the script concatenates multiple short sequences into each max_seq_length window to reduce wasted compute, matching the packing training applies by default. Otherwise, each example is padded to its own window. Loading the resulting Parquet:
Python

Validate and upload

Run a local data validation check before uploading to avoid unnecessary charges. The client-side check verifies the file is UTF-8, each non-empty line parses as JSON, the line count exceeds the minimum, and the file is under the maximum size. Full schema validation (conversation roles, tool calls, and other dataset requirements) runs on the server during ingestion after upload, and is reported through the file’s processing_status.
check_file() returns a report you can inspect before uploading. A passing file looks like:
Successful upload returns a file object with an id field. Save the ID—you’ll pass it as training_file to client.fine_tuning.create(). See the quickstart for the full fine-tuning lifecycle.
If you upload a file whose contents already exist on Together AI, client.files.upload() doesn’t create a duplicate. It returns the existing file’s metadata, including its id, so you can reuse it directly. To force a re-upload, delete the existing file first with client.files.delete(<file_id>).

Wait for server-side validation

Upload returns before ingestion finishes, so poll the Files API until processing_status reaches COMPLETED before you use the file. If the dataset doesn’t meet fine-tuning requirements, processing_status becomes INVALID_FORMAT and validation_report.error carries a user-facing description of the problem.
The exact validation_report schema may evolve, so treat processing_status as the authoritative readiness signal.

Split into train and validation

To carve a validation set out of a single JSONL file:
Then pass both files to the job and set n_evals above 0:
Python
The model evaluates against the validation set at the specified intervals. Eval loss appears in the training metrics and (if wandb_api_key is set) on your W&B dashboard.