Imported from lili-chen/rltf (
AGENTS.md). Install upstream withnpx skills add lili-chen/rltf. Copyright stays with the author.
Tinker Cookbook Agent Guide
Working notes for future agents hacking on tinker-cookbook. Additional docs can be found in the llms.txt (condensed) / llms-full.txt (complete), CONTRIBUTING, and the bundled documentation.
Mission & Scope
tinker-cookbookis the client-side layer for the hosted Tinker service. You author training/eval loops that run on a CPU machine; Tinker executes the heavy GPU work (LoRA fine-tuning, sampling, checkpointing) on synchronized worker pools (a.k.a. clock cycles).- The cookbook must mirror the public docs. Both
llms.txtandllms-full.txtare autogenerated outside this repo—treat them as read-only and coordinate with maintainers when they need a refresh. - Primary users: (1) researchers cloning recipes and swapping in their data/envs; (2) SDK developers extending abstractions like renderers, datasets, evaluators, completers.
Tooling & Setup
- Python ≥3.11. Follow the onboarding instructions: join the waitlist, create a
TINKER_API_KEYin the console,pip install tinker, thenpip install -e .[dev](oruv pip install -e .[dev]). Most contributors already have the env variable set; if requests fail with auth errors, re-export it. - Optional extras (
vector-search,wandb,verifiers, etc.) are defined inpyproject.toml. - CLI utilities expect datasets, logs, and checkpoints to live under user-controlled paths (default
/tmp/tinker-examples/...). Clean up disk usage between runs. - Heavy examples (smoke tests, RL recipes) download Hugging Face datasets and call the hosted API; run them only when you have network+API access.
Architecture & Patterns
- Builder pattern (per CONTRIBUTING):
- Config objects are lightweight
chzdataclasses (e.g.,SupervisedDatasetBuilder,RLDatasetBuilder,EnvGroupBuilder,EvaluatorBuilder). They capture parameters, stay serializable, and usually expose a.build()/__call__()that returns heavyweight runtime objects. - Launch scripts define a CLI-facing
CLIConfig(parsed bychz) that instantiates the richer trainingConfig. This gives every recipe a consistentpython -m ... key=valueinterface. - Env builders compose like
RLDatasetBuilder → EnvGroupBuilder → Env. Groups let us share metadata (tags, pairwise comparisons) and center rewards across related rollouts.
- Config objects are lightweight
- Completers: algorithms interact with the
TokenCompleterinterface.TinkerTokenCompleter(wrapping aSamplingClient) is the default implementation, but evaluators may accept anyTokenCompleterorMessageCompleter. - Renderers & tokenizer utils: pick the renderer that matches your tokenizer/model pair (e.g.,
role_colon,llama3,qwen3).TrainOnWhatcontrols which tokens get weight=1 in SFT. Tokenizers are cached viatokenizer_utils.get_tokenizer, with Llama-3 names remapped tothinkingmachineslabinc/meta-llama-3-tokenizerto bypass HF gating. - Loss plumbing: every
tinker.Datumbundles amodel_inputplusloss_fn_inputs(TensorData). Use helpers such asconversation_to_datum,datum_from_tokens_weights, and_remove_maskinstead of constructing dicts manually. Built-in losses:cross_entropy,importance_sampling,ppo;forward_backward_customcovers bespoke differentiable objectives.
Conventions & Notation (from CONTRIBUTING)
- Subscripts:
_P(problems/prompts),_G(groups of rollouts sharing metadata),_T(tokens/time),_D(datums), with flattened forms like_PG. Example:tokens_P_G_T[p][g][t]indexes tokens for problemp, group memberg, tokent. Keep these suffixes when naming tensors/metrics so downstream tooling can interpret shapes. - Env lifecycle:
Envobjects are single-use (noreset); create them viaEnvGroupBuilder, which returns correlated envs (for GRPO-style centering or multi-agent comparisons). Datasets return groups, not individual envs. - Typing: prefer explicit typing, avoid
Any/type: ignore. Keep generics readable. Converters likeTensorData.from_numpyand helper casting utilities already exist; use them. chzusage: configuration objects (Config, dataset builders, CLI configs) are@chz.chzclasses so they can be serialized, logged, and hydrated from CLI key-value pairs.- Logging style: training scripts rely on
ml_logfor metrics (metrics.jsonl, optional W&B) andlogtreefor HTML transcripts. When adding new metrics, follow theml_log.log_metricsshape conventions (str → float/int/str). - Safe iteration: functions like
safezip,timed, andscope(tracing) are widely used; follow those patterns instead of hand-writing logging/zip logic.
Data & Rendering
- Rendering is the bridge between chat-style data and token sequences.
renderers.pydefinesRenderer.build_supervised_example,build_generation_prompt,get_stop_sequences, andparse_response. UseTrainOnWhatto switch between “last assistant only” vs “all assistant messages” vs “prompt distillation” setups. - For supervised chat datasets, reuse
SupervisedDatasetFromHFDataset,StreamingSupervisedDatasetFromHFDataset, orFromConversationFileBuilder. They expect HF rows withmessagesarrays; map them through a renderer and optional max length. - RL data is organized by dimensions
_P(problems),_G(group members / rollouts per problem),_T(tokens),_D(datums). Keep arrays ragged-aware, and document shape suffixes when introducing new tensors.
Training Playbooks
Supervised Learning
- Main loop:
tinker_cookbook/supervised/train.py. It pipelines batches by submittingforward_backward_asyncandoptim_step_asyncimmediately, storing futures insideSubmittedBatch. Metrics/logging run throughml_log, stdout previews viadisplay.colorize_example. - Configs: include LR schedule (
linearmultiplier viacompute_schedule_lr_multiplier), LoRA rank, checkpoint cadence (save_every), eval cadence (eval_every,infrequent_eval_every), and dataset builders. - Hyperparameters: Call
hyperparam_utils.get_lr(model_name); LR is independent of LoRA rank. - Prompt distillation: see
tinker_cookbook/recipes/prompt_distillation. Renderers assign weight=0 to context instructions and weight=1 to distilled responses. - Sweeps:
tinker_cookbook/recipes/sl_loop.pydoubles as a sweep harness (log-scale grid, aggregate frommetrics.jsonl). Keep these scripts runnable; they double as docs tests.
Reinforcement Learning
- Main loop:
tinker_cookbook/rl/train.py. Steps: build dataset (RLDatasetBuilder), get groups of envs (EnvGroupBuilder), collect rollouts (do_group_rollout), compute advantages (compute_advantages), assemble datums (assemble_training_data), runforward_backward_async(..., loss_fn="importance_sampling" | "ppo"), applyoptim_step_async. - Policies: implement the
TokenCompleterinterface. Training loops usually instantiateTinkerTokenCompleter, but tests may stub a completer. - Hyperparameters: key knobs are
batch_sizevsgroup_size,num_substeps(similar to PPO epochs but still single-pass), and advanced configs:StreamMinibatchConfigoverlaps sampling with training (still on-policy).AsyncConfigenables bounded off-policy lag (“off-by-K”). Monitor KL metrics (compute_kl_sample_train,compute_post_kl) plus reward trends to make sure drift stays manageable.
- Environments:
Env,EnvGroupBuilder, andRLDatasetlive intinker_cookbook/rl/types.py. Groups make it easy to compute pairwise rewards (preference models) or multi-agent games. Example:recipes/multiplayer_rl/twenty_questions. - Recipes:
rl_basic.pydemonstrates default metrics: reward, entropy,ac_tokens_per_turn, format rate, KL approximations, and progress/time tokens.
Preferences & Distillation
- DPO:
tinker_cookbook/preference/train_dpo.py(CLI inrecipes/preference/train.py). Important knobs: dataset builder (choose whichever comparison corpus you need),renderer_name,dpo_beta, LR (often 1e-5 to 1e-6). Metrics likedpo_loss,accuracy,margin,chosen/rejected_rewardcome from the implicit reward model. - RLHF pipeline:
recipes/preference/rlhf/rlhf_pipeline.pywalks through the standard three stages (supervised warm-start, preference model, RL self-play using pairwise comparisons). - Distillation:
distillation/train_on_policy.pyhandles on-policy or SFT-style distillation; combine withrenderers,hyperparam_utils, andsampling_clientutilities.
Evaluations & Sampling
- Inline evaluators implement either
TrainingClientEvaluatororSamplingClientEvaluator. Training loops accept builder lists (evaluator_builders,infrequent_evaluator_builders). Inspect AI integration is ineval/inspect_evaluators.pyandeval/run_inspect_evals.py. - Sampling clients come from
training_client.save_weights_and_get_sampling_client(name=...). To export weights, useRestClient.get_checkpoint_archive_url_from_tinker_path.
Async & Performance
-
Worker pools advance in ~10s clock cycles. Submit
forward_backward_asyncandoptim_step_asyncback-to-back, then await both futures to keep them on the same cycle. -
Pipeline batches: enqueue the next
forward_backward_asyncbefore awaiting the previous batch’s results so there’s always work when a clock cycle begins. -
Use async everywhere performance matters (RL loops, production SFT). The synchronous helpers exist only for pedagogy (e.g.,
recipes/sl_loop.py) and small tests. -
Training CLIs (
recipes/*/*.py) callcli_utils.check_log_dirat startup to decide whether to delete, resume, or prompt about an existinglog_path. This convention is specific to training/eval entry points and is wired throughchzCLI configs so users can choose behaviors (delete,resume,ask,raise). -
ml_loghandles structured logging: metrics stream to stdout,metrics.jsonl, and optionally Weights & Biases (wandb_project,wandb_name). Uselogtreescopes for HTML transcripts when you need qualitative review of rollouts. -
checkpoint_utils.save_checkpoint_asyncwrites{log_path}/checkpoints.jsonlentries for state and/or sampler checkpoints.get_last_checkpointfilters by key (state_path,sampler_path) before resuming. -
After each optimizer step sequence, call
save_weights_for_sampler[_async](orsave_weights_and_get_sampling_client) and then create a new sampling client. ExistingSamplingClients do not automatically pick up fresh weights, so evaluators must use the newly returned client handle.
Testing & Troubleshooting
- Lightweight checks:
pytest tinker_cookbook/tests/test_renderers.py,pytest tinker_cookbook/tests/test_utils.py.tests/smoke_tests.pyspins up real training runs (needs HF + API access). - Example data lives in
tinker_cookbook/example_data/(e.g.,conversations.jsonl,multilingual.txt) and mirrors the formats documented intraining-sampling. - If you hit auth/network issues, double-check
TINKER_API_KEY, ensure your environment can reach the Tinker service, and verify dependencies (pip show tinker). - Resize datasets/batch sizes in recipes when debugging;
dataset_builderobjects usually acceptn_batches,batch_size, andgroup_sizefields so you can shrink workloads.
Common Pitfalls
- LoRA LR mismatch: LoRA typically needs learning rates tens of times higher than full fine-tuning. Use
hyperparam_utils.get_lror the LR formula above. Rank does not change the optimal LR. - Renderer/tokenizer mismatch: The renderer determines BOS/EOS tokens and stop sequences. Pair
renderer_namewith the tokenizer family your model expects (llama3,qwen3,role_colon, etc.). Otherwise loss weights and sampling stops will be wrong. - Loss inputs wrong shape: Stick to helper functions so
loss_fn_inputs["weights"],["target_tokens"],["advantages"], etc., end up asTensorDatawith the right dtype. Custom DPO/RL objectives often fail here. - Async gaps: Awaiting
forward_backwardbefore submittingoptim_stepwastes two extra clock cycles. Submit both first, then await results. - Sampler desync: Saving weights isn’t enough; always request a new sampling client (e.g., via
save_weights_and_get_sampling_client) before running evals so the client reflects the latest checkpoint. - Group semantics: RL advantages are centered within each group.
- DPO beta / LR: Too large a beta or LR makes the policy collapse; start with
dpo_beta=0.1, LR≈1e-5, and watchaccuracy+margintrends.
Quick Reference Commands
- Environment setup:
python -m venv .venv source .venv/bin/activate pip install tinker pip install -e .[dev] # Set once per shell if needed export TINKER_API_KEY=sk-... - Basic SFT run (default recipe):
python -m tinker_cookbook.recipes.sl_basic \ model_name=meta-llama/Llama-3.2-1B \ log_path=/tmp/tinker-examples/sl_basic - Custom JSONL SFT (bring your own conversations file):
python -m tinker_cookbook.recipes.sl_basic \ dataset_path=/path/to/conversations.jsonl \ renderer_name=role_colon \ train_on_what=all_assistant_messages \ log_path=/tmp/tinker-examples/sl_jsonl - RL basic run (default reward):
python -m tinker_cookbook.recipes.rl_basic \ model_name=meta-llama/Llama-3.1-8B \ log_path=/tmp/tinker-examples/rl_basic - DPO training (generic preference dataset):
python -m tinker_cookbook.recipes.preference.train \ log_path=/tmp/dpo-run \ model_name=meta-llama/Llama-3.2-1B \ dataset=<preference_dataset> renderer_name=role_colon \ learning_rate=1e-5 dpo_beta=0.1 - Inspect eval after training:
python -m tinker_cookbook.eval.run_inspect_evals \ model_path=tinker://YOUR_MODEL \ model_name=meta-llama/Llama-3.2-1B \ tasks=<inspect_task_id> \ renderer_name=role_colon