# Datum A `Datum` is one training example: the tokens the model reads, plus the tensors its [loss function](https://tinker-docs.thinkingmachines.ai/tinker/losses/index.md) needs. - **`model_input`** — the input token sequence, of length `N`. - **`loss_fn_inputs`** — a dict of per-token tensors, each with one row per input position: shape `(N,)`, or `(N, K)` for inputs that carry `K` values per position. It always holds `target_tokens`, plus whatever else the loss takes (`weights` for cross-entropy; `logprobs` and `advantages` for RL losses). Position `i` of the input predicts token `i + 1`, so both are built from the full sequence, shifted by one: ```python full_sequence = prompt_tokens + completion_tokens datum = types.Datum( model_input=types.ModelInput.from_ints(full_sequence[:-1]), loss_fn_inputs={ "target_tokens": full_sequence[1:], "weights": [0.0] * (len(prompt_tokens) - 1) + [1.0] * len(completion_tokens), }, ) ``` Everything the loss needs is in the datum, so each datum is self-contained. ## Provenance Some features need information captured when tokens were sampled. To use it at training time, the datum has to identify the sampled sequence behind each of its tokens. That annotation is the datum's provenance. > **Provenance works only within one session** > > A training request can only name sequences sampled in its own session. Sample and train in the same session. Every sequence a sampling request returns carries a `sequence_id`. Provenance is a list of runs naming those IDs: - `PromptProvenanceSpan(sequence_id, offset, length)` — tokens `[offset, offset + length)` of the sequence's prompt. - `SampledProvenanceSpan(sequence_id, offset, length)` — tokens `[offset, offset + length)` of the sequence's sampled tokens. `Datum.with_provenance` attaches two lists of runs: one for `model_input` and one for `target_tokens`. Each list tiles its field: runs cover consecutive positions, and their lengths sum to the field's length. For a prompt and one sampled sequence, the input covers the prompt and all but the last sampled token, and the targets cover the same tokens shifted by one: ```python from tinker import types prompt_tokens = prompt.to_ints() sampled_tokens = sequence.tokens sid = sequence.sequence_id n_prompt, n_sampled = len(prompt_tokens), len(sampled_tokens) datum = types.Datum( model_input=types.ModelInput.from_ints(prompt_tokens + sampled_tokens[:-1]), loss_fn_inputs={ "target_tokens": prompt_tokens[1:] + sampled_tokens, "logprobs": [0.0] * (n_prompt - 1) + sequence.logprobs, "advantages": [0.0] * (n_prompt - 1) + [advantage] * n_sampled, }, ).with_provenance( model_input=[ types.PromptProvenanceSpan(sequence_id=sid, length=n_prompt), # A one-token completion has no sampled token in the input; omit this run then. types.SampledProvenanceSpan(sequence_id=sid, length=n_sampled - 1), ], loss_fn_inputs=[ types.PromptProvenanceSpan(sequence_id=sid, offset=1, length=n_prompt - 1), types.SampledProvenanceSpan(sequence_id=sid, length=n_sampled), ], ) ``` Any loss accepts provenance, and the server checks it on every request. Each run must fit inside the sequence it names, and a run covering a whole sampled sequence must carry exactly that sequence's tokens, as must a model input run covering a whole prompt. Features that require provenance: - [StableReinforce](https://tinker-docs.thinkingmachines.ai/tinker/losses/stable-reinforce/index.md)