# Model Fine-Tuning

Provon can train adapters and small models from project Datasets and serve the resulting
checkpoints behind the AI Gateway. The Node runtime owns job scheduling, checkpoint tracking, and
deployment; the actual training runs in `services/python-fine-tuning`, a standalone Python service
that Node controls remotely over HTTP.

## When To Use It

Use model fine-tuning when you have curated Dataset examples and want to:

- improve model behavior on a recurring task shape seen in traces;
- produce a project-specific checkpoint that stays inside your infrastructure;
- compare a fine-tuned checkpoint against the base model in the Playground.

Fine-tuning is a follow-up step after Dataset collection. See [Datasets](../datasets/index.md) for how
training examples are produced and validated.

## Architecture

```mermaid
flowchart LR
  dataset["Dataset examples"] --> node["Node runtime"]
  node --> manifest["Dataset manifest"]
  manifest --> python["services/python-fine-tuning"]
  python --> checkpoint["Checkpoint artifact"]
  checkpoint --> runtime["Model runtime"]
  runtime --> gateway["AI Gateway self/ target"]
```

The Node runtime:

- validates the Dataset schema and example count;
- materializes a training manifest from the Dataset;
- submits the job to `services/python-fine-tuning`;
- polls for status and stores checkpoint metadata;
- registers completed checkpoints for inference and optionally auto-deploys them.

Node does **not** spawn Python child processes for training. It calls the Python service over HTTP,
which keeps PyTorch, Transformers, and PEFT dependencies out of the Node process.

## Supported Training Configuration

| Dimension               | Supported values                                                                   |
| ----------------------- | ---------------------------------------------------------------------------------- |
| Engine                  | `transformers`                                                                     |
| Method                  | `sft`, `dpo`, `orpo`, `kto`                                                        |
| Adapter                 | `lora`, `qlora`, `full`                                                            |
| Output format           | `safetensors`, `gguf`                                                              |
| GGUF quantization       | `q4_0`, `q4_k_m`, `q4_k_s`, `q5_0`, `q5_k_m`, `q6_k`, `q8_0`, `f16`, `bf16`, `f32` |
| Compressed quantization | `fp8`, `fp8_dynamic`, `fp8_static`, `int8`                                         |

Output format determines the default inference engine when the checkpoint is deployed: Safetensors
checkpoints are typically served by vLLM or SGLang; GGUF checkpoints are served by llama.cpp.
Compressed quantization produces safetensors checkpoints with compressed-tensor metadata for efficient vLLM/SGLang serving; static FP8 requires a calibration dataset.

- `sft` uses chat-formatted examples.
- `dpo`, `orpo`, and `kto` use preference-format examples (`prompt`/`chosen`/`rejected` for DPO/ORPO, `prompt`/`completion`/`label` for KTO).

## Create A Fine-Tuning Job

A job references a base model and a Dataset:

```bash
curl -X POST "$PROVON_API_URL/projects/$PROJECT_ID/fine-tuning/jobs" \
  -H "Authorization: Bearer $PROVON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "baseModelId": "<base-model-id>",
    "datasetId": "<dataset-id>",
    "engine": "transformers",
    "method": "sft",
    "adapter": "lora",
    "hyperparameters": {
      "learningRate": 0.0001,
      "numTrainEpochs": 3,
      "perDeviceTrainBatchSize": 1
    },
    "output": {
      "format": "safetensors"
    }
  }'
```

The base model must already be known to the runtime. The Dataset must use the `chat` schema with at
least one example.

## Job Lifecycle

Jobs move through `queued`, `running`, `completed`, `failed`, and `cancelled`. Poll status:

```bash
curl "$PROVON_API_URL/projects/$PROJECT_ID/fine-tuning/jobs/$JOB_ID" \
  -H "Authorization: Bearer $PROVON_API_KEY"
```

Cancel a running job:

```bash
curl -X POST "$PROVON_API_URL/projects/$PROJECT_ID/fine-tuning/jobs/$JOB_ID/cancel" \
  -H "Authorization: Bearer $PROVON_API_KEY"
```

## Checkpoints

When a job completes, it produces a checkpoint stored in the project checkpoint registry. List
checkpoints:

```bash
curl "$PROVON_API_URL/projects/$PROJECT_ID/fine-tuning/checkpoints" \
  -H "Authorization: Bearer $PROVON_API_KEY"
```

Deploy a checkpoint to the model runtime:

```bash
curl -X POST "$PROVON_API_URL/projects/$PROJECT_ID/fine-tuning/checkpoints/$CHECKPOINT_ID/deploy" \
  -H "Authorization: Bearer $PROVON_API_KEY"
```

Deployment loads the checkpoint into the model runtime and makes it available as a `self/` Gateway
target. The checkpoint must have a registered inference profile.

## Lineage

Trace the provenance of a model:

```bash
curl "$PROVON_API_URL/projects/$PROJECT_ID/fine-tuning/lineage/$ROOT_MODEL_ID" \
  -H "Authorization: Bearer $PROVON_API_KEY"
```

Lineage records the chain of base models, fine-tuning jobs, and checkpoints.

## Export Checkpoints

Export a checkpoint or a Hugging Face model to the Hugging Face Hub:

```bash
curl -X POST "$PROVON_API_URL/projects/$PROJECT_ID/fine-tuning/export" \
  -H "Authorization: Bearer $PROVON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceType": "checkpoint",
    "checkpointId": "<checkpoint-id>",
    "format": "gguf",
    "quantMethod": "q4_k_m",
    "destination": "huggingface-hub",
    "hfRepoId": "owner/repo"
  }'
```

Export requires a configured Hugging Face connector with a valid access token. The token is resolved
from the project connector and passed to the export worker securely; it is not persisted on the job
record.

## Configuration

The Python fine-tuning service is configured separately from Node:

| Variable                | Default     | Purpose                                    |
| ----------------------- | ----------- | ------------------------------------------ |
| `PROVON_HOST`           | `0.0.0.0`   | Bind host                                  |
| `PROVON_PORT`           | `8001`      | Bind port                                  |
| `PROVON_API_KEY`        | none        | Bearer token shared with Node              |
| `PROVON_LLAMA_CPP_PATH` | `llama.cpp` | Path to llama.cpp checkout for GGUF export |

Node and the Python service must see the training work directory at the same absolute path, because
training requests pass filesystem paths for the Dataset manifest and checkpoint output.

## Capability Requirements

The project API key needs:

- `workspace:read` or `project:data:read` to list jobs, checkpoints, and lineage;
- `models:manage` or `project:data:write` to create jobs, cancel jobs, and export checkpoints;
- `models:manage` or `project:models:manage` to deploy checkpoints.

## Limitations

- Fine-tuning is only available in the Node runtime. Cloudflare Workers do not provide local training
  or checkpoint storage.
- The current supported engine set is `transformers`; others may be added over time.
- GGUF export requires a local checkout of llama.cpp with `convert_hf_to_gguf.py` and the
  `llama-quantize` binary.
- Compressed quantization (FP8/INT8) requires the `compressed` optional dependency
  (`llm-compressor`) in the Python fine-tuning service.

## Next Steps

- [AI Models](./index.md) for the complete model lifecycle.
- [Datasets](../datasets/index.md) for training-data preparation.
- [Self-hosted model runtime](./runtime.md) for serving checkpoints.
- [Model providers](../ai-gateway/providers.md) for routing base-model traffic through the Gateway.
- [Self-hosting Node](../self-hosting/node.md) for deployment topology.
- [Model Fine-Tuning API](../api/fine-tuning.md) for the HTTP contract.
