// Requires the matching optional native training build. Checking this source
// does not open a model. Running it uses the explicit local files below.
// Supply a supported floating dense-Llama GGUF, disjoint question,answer CSVs,
// and an existing checkpoints directory. All published destinations must be new.
import aner.gen
import aner.llm

let base = gen.import_nf4("models/base-f16.gguf", double_quant: true,
    max_memory_bytes: 4294967296)
let targets = List("attention.q_proj", "attention.v_proj")
let adapted = base.qlora(targets, rank: 8, alpha: 16.0, seed: 71)

// Other explicit choices on an unchanged imported floating or NF4 base:
// let adapted = base.lora(targets, rank: 8, alpha: 16.0, seed: 71)
// let adapted = base.rs_lora(targets, rank: 8, alpha: 16.0, seed: 71)
// let adapted = base.lora_fa(targets, rank: 8, alpha: 16.0, seed: 71)
// let adapted = base.tinylora(targets, rank: 8, parameters: 13,
//     seed: 71, sharing: "global")
// Full fine-tuning uses gen.import_model(...) and its unadapted floating model.

let training = gen.supervised_csv(adapted, "training.csv", input: "question",
    target: "answer", role: "training", context: 128)
let validation = gen.supervised_csv(adapted, "validation.csv", input: "question",
    target: "answer", role: "validation", context: 128)
print(adapted.method())
print(adapted.storage())
print(adapted.memory_summary())

let plan = adapted.fine_tune_plan(training, validation, steps: 2,
    micro_batch: 1, accumulation: 2, rate: 0.0002, seed: 71)
print(plan.summary())
let scheduled = plan.checkpoints(directory: "checkpoints", every: 1)
let job = scheduled.start()
print(job.status())
// job.stop() requests a cooperative stop at a committed update boundary.
let result = job.wait()
print(result.summary())

if result.status() == "failed" {
    print(result.error())
} else {
    let tuned = result.model()
    tuned.checkpoint("checkpoint-final")
    let restored = gen.load_pretrained_checkpoint("checkpoint-final")
    let continuation = restored.resume_plan(training, validation, steps: 1)
    let continued = continuation.start().wait()
    print(continued.summary())
    if continued.status() == "failed" {
        print(continued.error())
    } else {
        let ready = continued.model()
        ready.save_model("adapted-f32.gguf")
        // Alternative: ready.save_adapter("adapted-package") publishes both
        // the exact decoded frozen base and standard deployment adapter factors.
        // Load adapted-package/base.gguf and apply the returned adapter manifest;
        // applying that manifest to the original floating source is rejected.
        let deployed = llm.load("adapted-f32.gguf", context: 128, device: "cpu")
        print(deployed.generate("Explain the purpose of held-out data.",
            max_tokens: 32, temperature: 0.0, seed: 0).text())
        deployed.close()
    }
}
