Metadata-Version: 2.5
Name: cairn-operator-training
Version: 0.1.0
Summary: SFT for cairn: one Unsloth QLoRA/LoRA trainer as flow stages (sft-train, DPO, distill) that an engine runs on a CUDA GPU box, plus the stub and CPU/MPS local trainers.
Project-URL: Homepage, https://cairndev.sh
Project-URL: Documentation, https://docs.cairndev.sh
Project-URL: Source, https://github.com/cairndev-sh/cairn-extensions
Project-URL: Issues, https://github.com/cairndev-sh/cairn-extensions/issues
Author: cairn maintainers
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Requires-Python: >=3.12
Requires-Dist: cairn-contracts-training-stages==1.2.0
Requires-Dist: cairn-contracts-training==1.0.0
Requires-Dist: cairndev<0.4,>=0.2.0
Provides-Extra: gpu
Requires-Dist: accelerate==1.14.0; extra == 'gpu'
Requires-Dist: bitsandbytes==0.50.1; extra == 'gpu'
Requires-Dist: boto3>=1.34; extra == 'gpu'
Requires-Dist: datasets==4.3.0; extra == 'gpu'
Requires-Dist: peft==0.20.0; extra == 'gpu'
Requires-Dist: torch==2.11.0; extra == 'gpu'
Requires-Dist: transformers==5.5.0; extra == 'gpu'
Requires-Dist: trl==0.24.0; extra == 'gpu'
Requires-Dist: unsloth-zoo==2026.8.15; extra == 'gpu'
Requires-Dist: unsloth==2026.8.21; extra == 'gpu'
Provides-Extra: local
Requires-Dist: accelerate<2,>=1.14; extra == 'local'
Requires-Dist: datasets<4.4,>=4.3; extra == 'local'
Requires-Dist: peft<0.21,>=0.20; extra == 'local'
Requires-Dist: torch<2.12,>=2.11; extra == 'local'
Requires-Dist: transformers<5.6,>=5.5; extra == 'local'
Requires-Dist: trl<0.25,>=0.24; extra == 'local'
Provides-Extra: test
Requires-Dist: huggingface-hub>=0.20; extra == 'test'
Requires-Dist: moto[s3]>=5; extra == 'test'
Requires-Dist: pyyaml>=6; extra == 'test'
Provides-Extra: track-mlflow
Requires-Dist: mlflow>=2.14; extra == 'track-mlflow'
Description-Content-Type: text/markdown

# cairn-operator-training

One SFT trainer for [cairn](https://github.com/cairndev-sh/cairn): supervised fine-tuning with
LoRA or 4-bit QLoRA (Unsloth + TRL + PEFT + Transformers), as governed pipeline legs. The
trainer does not pick the GPU: the profile's ml engine does (`ml_engine` / `ml_target`), so the
same stage runs on an ssh GPU box today and on whatever the SkyPilot engine reaches as it is
proven (clouds, Kubernetes, RunPod, Vast).

- **`train.run` operations:** `train/stub` (deterministic, no GPU) and `train/local`
  (CPU/MPS LoRA, for smoke runs).
- **ml-flow stages** (`cairn.flow_stages`): `sample.fidelity` → `train.qlora` →
  `eval.checkpoints` → `package.adapter` (the `sft-train` flow), and `train.dpo`,
  `dataset.dpo_pairs`, `train.distill`.
- **`compress/distill`:** knowledge distillation (TRL's GKD trainer).
- **Contracts:** implements `train.run` (`cairn-contracts-training`) and the four
  `training.stage.*` stage contracts (`cairn-contracts-training-stages`); `train.qlora` implements
  `training.stage.train_qlora/1.2.0`, which takes governed `train_uri` / `eval_uri` / `test_uri`
  splits, the batch, accumulation, checkpointing and dropout knobs, and `baseline_eval`: the base
  model's validation loss, measured after training with the adapter disabled (with
  `init_adapter_uri`, the bare base model, not the adapter the run continued).

## Install

This wheel and the contract wheels it pins are not on PyPI yet. Until they are, install from a
checkout of this repository, into the environment `cairndev` 0.2.x or 0.3.x runs in:

```bash
git clone https://github.com/cairndev-sh/cairn-extensions && cd cairn-extensions
# the control plane: no torch, no CUDA
pip install ./packages/cairn-contracts-training ./packages/cairn-contracts-training-stages \
  ./packages/cairn-operator-training
# a CUDA box the ssh engine runs the trainer on
pip install ./packages/cairn-contracts-training ./packages/cairn-contracts-training-stages \
  './packages/cairn-operator-training[gpu]'
# the same box, reporting to MLflow
pip install ./packages/cairn-contracts-training ./packages/cairn-contracts-training-stages \
  './packages/cairn-operator-training[gpu,track-mlflow]'
```

| Extra | What it is for |
|---|---|
| `gpu` | The Unsloth QLoRA trainer, pinned exactly to `images.py::TRAINER_PINS` (torch, unsloth, trl, peft, transformers, datasets, bitsandbytes, accelerate), plus `boto3` for `s3://` data, checkpoint mirroring and `artifact_target: s3`. Install it on the GPU box. |
| `local` | Plain LoRA on CPU/MPS (no unsloth or bitsandbytes), ranges floored at the same pins. It is the pins only: a CPU box that reads `s3://` data, mirrors checkpoints or publishes to `s3` also needs `pip install boto3`. |
| `track-mlflow` | MLflow run, dataset and model logging (`report_to: [mlflow]`). Not part of `gpu`: without it, a run asked to report to MLflow trains untracked, and `train.qlora` returns why as `tracking_error` (the pipeline step's `train_qlora_tracking_error` finding). |

boto3 is imported only when a run reads or writes S3, so a run without S3 needs none. In a flow
stage the trainer writes its checkpoints and adapter under the job's workdir
(`<workdir>/train-output/<output_name>`); the published adapter (`artifact_target: s3` or `hf`)
is the adapter files only, never the `checkpoint-*` directories.

The base install pins `cairndev>=0.2.0,<0.4` and the two contract wheels exactly. Check an
install with `cairn doctor` and `cairn pack check`.

## Support

Carried from cairn's support matrix as it stood at `cairndev` 0.1.3, when this pack was
private. **No row has been re-proven on this public wheel yet**: the first proof is the
qualification run in cairndev-sh/cairn-extensions#9 (an ssh GPU box, `cairndev` 0.2.0), and
each row will name the `cairndev` release it was proven on as it is re-run.

| Path | Status | Evidence so far | Proven on `cairndev` |
|---|---|---|---|
| SFT with LoRA / QLoRA on an ssh GPU box (`ml_target` = a `compute.ssh` connection) | SUPPORTED | 2026-08-28 GPU campaign, before the public 0.1 releases | not yet (#9) |
| `stub: true` trainer tier | SUPPORTED | pack tests | 0.2.0 pre-release (local run; CI lands with #1) |
| DPO (`train.dpo`) | EXPERIMENTAL | pack tests; no GPU run recorded | not yet |
| Distillation (`compress/distill`, `train.distill`) | EXPERIMENTAL | pack tests; no GPU run recorded | not yet |

Where a run goes is the engine's: an ssh GPU box (`cairn connect add ssh`, profile
`ml_engine = "builtin"`, `ml_target = "<connection>"`) is the proven path; the box installs
`cairn-operator-training[gpu]`. Design notes: [`DESIGN.md`](DESIGN.md).

**With sign-in on (cairn 0.2.0 and later).** A connection bound as the profile's `ml_target`
belongs to the deployment (the `default` account), and a team account's flow legs cannot use it.
A team account creates its own `compute.ssh` connection under its own name (connection names are
unique across a deployment) and names it as the target in its pipelines or flows. Credentials this wheel reads from the environment (`HF_TOKEN`, `WANDB_*`, `AWS_*`) are not
declared as `secrets:`; see [`DESIGN.md`](DESIGN.md) §4.

Apache-2.0 — see [`LICENSE`](LICENSE).
