LizzyDocs

Stage 04 · Plan & train

Validate the exact plan, then approve live spend.

Every live training request is tied to a successful dry run and its immutable plan hash. The copilot can prepare the action; a person approves the spend.

Propose a version-scoped recipe

The proposal combines an immutable dataset version, explicit rewards, an objective, and a desired outcome. Review every generated stage and budget before validation.

POST/v1/distill/recipes/propose

Send dataset_version, rewards, objective, and outcome. A proposal is advice; it does not start compute.

Validate the exact plan with a dry run

Create a run with dry_run: true. Lizzy resolves the version and rewards, compiles the stage graph, validates compute and budget, and stores a plan_hash. No GPU training or student publication occurs.

curlexample
curl -X POST "https://<your-lizzy-host>/v1/distill/runs" \
+  -H "Authorization: Bearer $LIZZY_API_TOKEN" \
+  -H "Content-Type: application/json" \
+  -H "Idempotency-Key: dry-run-001" \
+  -d '{"dataset_version":"<dataset_version_id>","student_model_name":"support-student-v1","recipe":{"student_model":"<base-model>","rewards":["<reward_id>"],"stages":[{"kind":"sft","config":{"epochs":1}},{"kind":"eval","config":{}},{"kind":"publish","config":{}}]},"budget":{"max_cost_usd":25},"dry_run":true}'
Pythonexample
import os, requests

dry_run = requests.post(
    "https://<your-lizzy-host>/v1/distill/runs",
    headers={"Authorization": f"Bearer {os.environ['LIZZY_API_TOKEN']}", "Idempotency-Key": "dry-run-001"},
    json={
        "dataset_version": "<dataset_version_id>",
        "student_model_name": "support-student-v1",
        "recipe": recipe,
        "budget": {"max_cost_usd": 25},
        "dry_run": True,
    },
).json()
TypeScriptexample
const response = await fetch("https://<your-lizzy-host>/v1/distill/runs", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.LIZZY_API_TOKEN}`, "Content-Type": "application/json", "Idempotency-Key": "dry-run-001" },
  body: JSON.stringify({
    dataset_version: "<dataset_version_id>", student_model_name: "support-student-v1",
    recipe, budget: { max_cost_usd: 25 }, dry_run: true,
  }),
});
if (!response.ok) throw await response.json();
const dryRun = await response.json();

Approve live training

  1. 1
    Dry run succeeds

    Keep its ID and immutable plan hash.

  2. 2
    Review the proposal

    Check version, rewards, stages, compute, and hard budget.

  3. 3
    Approve in Lizzy

    The copilot creates an approval request, not an unbounded live call.

  4. 4
    Start the exact plan

    Send validated_dry_run and the matching plan_hash with dry_run false.

Live run bodyapproval-gated
{
  "dataset_version": "<dataset_version_id>",
  "student_model_name": "support-student-v1",
  "recipe": { "<same recipe used for validation>": "<unchanged>" },
  "budget": { "max_cost_usd": 25 },
  "dry_run": false,
  "validated_dry_run": "<dry_run_id>",
  "plan_hash": "<plan_hash>"
}

Watch a run without polling forever

Persist the run ID and inspect GET /v1/distill/runs/{run_id}. Terminal states are succeeded, failed, and canceled. Bounded watches resume from the last checkpoint and return control when there is no progress.

POST/v1/distill/runs/{run_id}/cancel

Cancel a running job to stop future spend. Do not retry a failed stage blindly; inspect logs and use the UI for an explicit retry decision.

Built for humans and copilots.

Every risky action has an explicit handoff, validation, or approval boundary.

Recovery guide