---
title: "Recipes"
description: "Recipes that show what TypeScript unlocks for an Arkor training run today."
---

This section collects the patterns that show up first when you put logic inside an Arkor training run. Each recipe stays inside today's public SDK: no roadmap APIs, no internal imports, nothing that requires forking the runtime.

## Recipes

| Recipe | What it shows | Built on |
| --- | --- | --- |
| [Mid-run evaluation](/docs/framework/cookbook/mid-run-eval) | Sanity-check the half-trained model against a fixed prompt at every checkpoint, before the run finishes. | [`onCheckpoint({ infer })`](/docs/framework/sdk/callbacks) |
| [Structured outputs and function calling](/docs/framework/cookbook/structured-outputs) | Constrain a checkpoint's output to a JSON schema, parse it as a typed object, and let the model call functions. | [`infer({ responseFormat, tools })`](/docs/framework/sdk/infer) |
| [Early stopping on diverging loss](/docs/framework/cookbook/early-stopping) | Abort a run automatically when the loss curve goes the wrong way, and stop the GPU on the backend too. | [`onLog`](/docs/framework/sdk/callbacks), [`AbortSignal`](/docs/framework/sdk/trainer-control), [`trainer.cancel()`](/docs/framework/sdk/trainer-control) |
| [Slack / Discord notifications](/docs/framework/cookbook/notifications) | Post to a webhook on completion or failure, without leaving the trainer file. | [`onCompleted` / `onFailed`](/docs/framework/sdk/callbacks), `fetch` |
| [Programmatic runs (no CLI)](/docs/framework/cookbook/programmatic-runs) | Drive training from a Next.js API route, a cron worker, or CI without going through `arkor dev` / `arkor start`. | [`runTrainer`](/docs/framework/sdk/overview), [`Trainer.start / wait`](/docs/framework/sdk/trainer-control) |
| [Customizing the starter templates](/docs/framework/cookbook/customizing-templates) | Treat the scaffolded templates as starting points. Change the dataset, hyperparameters, callbacks, and base model. | [`createTrainer`](/docs/framework/sdk/create-trainer), [`DatasetSource`](/docs/framework/sdk/dataset) |

## A note on callback exceptions before you start

Three of the recipes below put logic inside the lifecycle callbacks. A `throw` inside a callback rejects `trainer.wait()` immediately with your error ([SDK § Lifecycle callbacks](/docs/framework/sdk/callbacks)); it is not routed through the SSE reconnect loop, not silently retried, and the event is not swallowed. The reconnect loop handles only transport failures (a dropped connection, a transient 5xx). For non-fatal handling, catch inside the callback.

The recipes here use a simple convention to stay deterministic:

- **State changes go through outer variables.** Use an `AbortController`, a closure flag, or a returned Promise rather than throwing.
- **Side effects are guarded with `try / catch` inside the callback.** If a Slack post fails, log it and continue; do not let it bubble.

This is a pattern, not a limit on what you can do. Once you have it, the recipes compose cleanly.

## Things this section does not cover

- **Production-serving recipes.** Publishing a trained adapter at a stable `*.arkor.app` endpoint already works, per [Deployments](/docs/framework/sdk/deployments); this section simply has no serving recipes yet (`createArkor`'s `deploy` manifest slot is still a reserved type field with no implementation).
- **Multiple trainers per project.** `createArkor` accepts a single `trainer`; running several together is a programmatic-run pattern (see the recipe), not a manifest pattern.
- **Custom base models beyond what the backend accepts.** The `model` field is forwarded to the cloud API verbatim; today the curated path is Gemma. Recipes do not pretend other models work end to end.
