> ## Documentation Index
> Fetch the complete documentation index at: https://docs.baseten.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Tinker

> Move existing Tinker training scripts to Loops with one install change.

Most Tinker code runs on Loops with one install change, so migrating is largely a packaging exercise. The forward pass, backward pass, optimizer step, sampling, and all shared types carry over without modification. Checkpoints come back as paginated presigned URLs rather than a single archive, authentication uses a `BASETEN_API_KEY` instead of a Thinking Machines key, and cluster routing resolves to your Baseten org rather than a Tinker tenant.

## Compatibility at a glance

The `baseten-loops[tinker]` extra installs a `tinker` namespace package so existing imports work unchanged. The table below shows where Loops and Tinker align and where they diverge.

| Aspect | Tinker | Loops |
| - | - | - |
| Import | `import tinker` | `import tinker` (provided by `baseten-loops[tinker]`) |
| ServiceClient construction | `tinker.ServiceClient(...)` | `tinker.ServiceClient()` |
| Forward / backward / optimizer | identical | identical |
| Loss input shape | 1-D or `(N, K)` | 1-D or `(N, K)` |
| Image inputs | inline or asset pointer | inline `ImageChunk` only |
| Checkpoint download | single archive | paginated presigned URLs |
| Authentication | Thinking Machines key | `BASETEN_API_KEY` |
| Cluster scope | per Tinker tenant | per Baseten org, optionally scoped to a team |

## Install the Tinker compatibility package

Add `baseten-loops` with the `[tinker]` extra to your project. The extra pulls in `baseten-loops-tinker`, which provides the `tinker` namespace. If you don't have a uv project yet, initialize one first:

```bash theme={"system"}
uv init loops-app
cd loops-app
uv add 'baseten-loops[tinker]'
```

Once installed, existing code that starts with `import tinker` works without modification:

```python theme={"system"}
import tinker  # provided by baseten-loops-tinker
```

### Use with tinker-cookbook

`tinker-cookbook` depends on the original Thinking Machines `tinker` package. Without an override, installing `tinker-cookbook` first pulls in that package, and its files conflict with the `tinker` namespace provided by `baseten-loops[tinker]` when that extra is added later. The fix is to declare a `uv` override in `pyproject.toml` before installing any dependencies. There is no CLI command for this step.

Add the override first:

```toml theme={"system"}
[tool.uv]
override-dependencies = [
    "tinker ; python_version < '0'",
]
```

Then add the dependencies:

```bash theme={"system"}
uv init
uv add tinker-cookbook
uv add 'baseten-loops[tinker]'
```

The `baseten-loops[tinker]` extra provides the `tinker` namespace instead of the original package.

## What's the same

The training loop API is call-compatible with Tinker. `forward`, `backward`, `forward_backward_custom`, `optim_step`, `save_weights`, and the sampling interface share the same method names. The shared types (`Datum`, `ModelInput`, `TensorData`, `SamplingParams`, and `AdamParams`) are available under `tinker.types` with the same field names.

## What's different

### Top-K soft targets

Cross-entropy `target_tokens` and `weights` support Tinker's `(N, K)` form for top-K soft targets. Both tensors must have the same shape, and every `Datum` in a batch must use the same K. Returned logprobs preserve the `(N, K)` shape.

### Image inputs must be inline

Tinker's `ImageAssetPointerChunk` references an image asset stored out of band. Loops ships the type so that `tinker` imports and `ModelInput` parsing resolve, but Baseten samplers don't fetch out-of-band assets, so constructing one raises `ValueError` naming `ImageChunk` as the supported path. Recipes that read images from disk or memory and build `ImageChunk` values port unchanged; recipes that pass asset pointers do not.

### Checkpoints come back as folders

Tinker returns a single archive URL for a checkpoint. Loops returns a folder of files behind paginated presigned URLs, because weight sync writes an unzipped folder rather than a compressed archive. Consumer code paginates using `?page_token=` and `?page_size=` query parameters instead of downloading a single file. See [`GET /v1/loops/checkpoints/{checkpoint_id}/files`](/reference/loops-api/checkpoints/get-checkpoint-files) for the route.

### Authentication is a Baseten API key

Set `BASETEN_API_KEY` in your environment before constructing `ServiceClient`; the SDK reads it by default. Pass `api_key=...` only when you need to override the environment variable. The Thinking Machines key used by Tinker is not accepted. See [API keys](/organization/api-keys) for how to generate one.

### LoRA module selection is fixed per model family

Tinker lets you choose which module groups a LoRA adapter trains through the `train_mlp`, `train_attn`, and `train_unembed` flags on `create_lora_training_client`. Loops picks target modules server-side per model family: attention and MLP on, unembed off, for every supported family except Kimi K2.5 VL, which trains attention and unembed but not MLP. The compatibility package accepts these flags when they match your model family's coverage and raises `NotImplementedError` at submission when they diverge, rather than silently ignoring them.

### Cluster routing is per-org

Loops sessions resolve to the caller's Baseten org and the cluster configured for that org. Tinker uses per-tenant scoping, where the tenant determines the cluster. In practice this means you don't choose a cluster when creating a session. Your org's configuration determines it automatically. To place workloads in a specific Baseten team, pass `team="..."` to `ServiceClient`; see [Choose a team](/loops/concepts#choose-a-team).

## Run tinker-cookbook recipes on Loops

The cookbook recipes contain self-contained examples covering supervised fine-tuning, reinforcement learning from human feedback, distillation, and sampling. Recipes that use one-dimensional loss inputs or `(N, K)` cross-entropy targets run on Loops without modification. Running a recipe end to end is the fastest way to confirm that your environment is configured correctly and that the `tinker` namespace is resolving to the Loops compatibility package.
