#!/usr/bin/env python3
"""Standalone adapter-merge script for the CUDA backend — invoked as a subprocess by
app/services/finetune/cuda_backend.py. Loads the base model + the LoRA adapter
cuda_train.py saved, merges them, and writes a plain fp16 HF-format checkpoint that
app/services/finetune/shared.py::run_gguf_convert (llama.cpp's converter, shared by
every backend) can turn into a GGUF file.

NOTE: same caveat as cuda_train.py — written against Unsloth/PEFT's documented API,
not yet run against real hardware.
"""

import argparse
import sys


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--model", required=True, help="Base model repo id (unused directly — kept for parity/logging; Unsloth resolves it from the adapter's own config)")
    parser.add_argument("--adapter-path", required=True)
    parser.add_argument("--save-path", required=True)
    parser.add_argument("--max-seq-length", type=int, default=2048)
    args = parser.parse_args()

    from unsloth import FastLanguageModel

    print(f"Loading base model + adapter from {args.adapter_path}...", flush=True)
    # Unsloth resolves the base model from the adapter directory's own
    # adapter_config.json (base_model_name_or_path), so pointing from_pretrained at
    # the adapter directory loads both in one call.
    model, tokenizer = FastLanguageModel.from_pretrained(
        model_name=args.adapter_path,
        max_seq_length=args.max_seq_length,
        dtype=None,
        load_in_4bit=True,
    )

    print(f"Merging and saving fp16 checkpoint to {args.save_path}...", flush=True)
    model.save_pretrained_merged(args.save_path, tokenizer, save_method="merged_16bit")
    print("Done.", flush=True)


if __name__ == "__main__":
    try:
        main()
    except Exception as exc:  # noqa: BLE001
        import traceback

        traceback.print_exc()
        print(f"FATAL: {exc}", flush=True)
        sys.exit(1)
