Fit JSONL Conversations Into a Token Budget
Context fitter. A training job does not reject a record that is too long — it truncates it, from whichever end the trainer happens to cut from, and carries on. The result is a dataset where some records end mid-answer, and nothing tells you until the model learns to do the same. This drops whole turns instead, on a rule you choose.
100% client-side. No upload.
Fit
Context Fitter
Set a budget, pick which end of the conversation is expendable, and every record that does not fit has whole turns removed until it does. The system message and the last exchange are protected by default, because those are the two parts a training example cannot be missing.
Why turns and not characters
Cutting a conversation at a character count leaves a record whose last message stops mid-sentence. Trained on, that teaches the model to stop mid-sentence. Dropping a whole turn leaves every remaining message intact and the conversation still coherent — shorter, but not damaged.
The same reasoning drives the two guards. Keep the system message holds on to the instruction that defines the behaviour you are training; drop it and the record is teaching something else. Keep the last N turns protects the exchange at the end, which is usually the actual example — the question and the answer you want learned. Everything before it is context, and context is what there is too much of.
The three strategies
- From the start — oldest turns first. The default, and right for a long chat where the recent exchange is the point. This is what a chat client does at inference time, so training this way matches how the model will be used.
- From the middle — keep both ends. Right when the beginning carries setup that matters (a document, a schema, a persona) and the end carries the answer. Turns are removed from the middle outwards, furthest from either end first.
- From the end — newest turns first. Right when the record is a transcript and you want the opening, or when a long trailing tool-call sequence is what has blown the budget.
After trimming, a conversation left ending on a user turn has that turn removed too:
a record with a question and no answer is not a training example. That extra removal is counted.
Reading the summary
Still over budget is the number worth looking at. A record lands there for one of
two reasons: everything droppable has already gone and what remains — a system message plus the
protected last turns — is still too long, or the record has no message list to trim at all, which
is the case for prompt/completion and plain-text formats. The summary
says which. Lower keep the last, raise the budget, or set Still over budget to
drop the record and lose the handful you cannot fix.
The longest record is reported with its line number, so the outlier that set your budget is one click away in the viewer.
How the tokens are counted
With the same estimator as the token counter: a BPE-shaped segmenter over the message content, plus three tokens per message for the role framing, with a per-model correction factor. It counts the conversation rather than the JSON line, so you are not charged for braces and key names.
It is an estimate, and it is deliberately not exact — a real tokenizer is a megabyte of vocabulary. Leave a margin: fitting to 8,000 against an 8,192 limit is safer than fitting to 8,192, and the budget field defaults to the model's context length so you can see what you are cutting it against.
Tips & common pitfalls
- The budget is the whole record, not just the prompt. For fine-tuning, the completion is inside the same limit. Budget for both.
- Check what you removed before you train on it. If half the file was trimmed, the file is not too long — the format is wrong, or one field is carrying a document that should be a reference.
- Run this after deduplicating and before splitting. Trimming changes record content, and duplicate detection on trimmed records finds duplicates that were not there.
- Nothing is padded. Records under budget come through byte-identical.
Related tools
- Token counter — what the file costs before you change it.
- JSONL truncate — cap record count, string length and array size, rather than conversation length.
- OpenAI fine-tune validator — the other reasons a job rejects a file.
- Train / val / test split — after the file is the right shape.