| """HF Jobs SFT launcher — exact mirror of working Colab cells. |
| |
| No TRL OpenEnv, no env server, no GRPO. Pure SFT on 611 traces. |
| Same code that works on Colab T4. H200 just runs it ~10× faster. |
| """ |
| from __future__ import annotations |
|
|
| import os |
| import subprocess |
| import sys |
|
|
| WORK = "/tmp/opsguard" |
| HUB_REPO = "sai1906/opsguard-sft" |
|
|
|
|
| def sh(cmd): |
| print(f"[sh] {cmd if isinstance(cmd, str) else ' '.join(cmd)}", flush=True) |
| return subprocess.run(cmd, shell=isinstance(cmd, str), check=True) |
|
|
|
|
| def main(): |
| if not os.environ.get("HF_TOKEN"): |
| raise SystemExit("HF_TOKEN required") |
|
|
| sh([sys.executable, "-m", "pip", "install", "-q", "-U", |
| "transformers>=4.46", "trl>=0.18", "peft>=0.13", "bitsandbytes>=0.44", |
| "datasets", "accelerate>=1.0", "huggingface_hub", "matplotlib", "networkx>=3"]) |
|
|
| sh(f"rm -rf {WORK}") |
| sh(["git", "clone", "https://huggingface.co/spaces/sai1906/opsguard", WORK]) |
| os.chdir(WORK) |
| sys.path.insert(0, WORK) |
|
|
| from huggingface_hub import login, HfApi |
| login(token=os.environ["HF_TOKEN"]) |
| HfApi(token=os.environ["HF_TOKEN"]).create_repo(HUB_REPO, repo_type="model", exist_ok=True) |
| print(f"[preflight] repo {HUB_REPO} ready", flush=True) |
|
|
| import torch |
| from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig |
| from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training |
|
|
| MODEL = "Qwen/Qwen2.5-7B-Instruct" |
| print(f"[model] loading {MODEL} 4bit...", flush=True) |
| bnb = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16, |
| bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4") |
| tok = AutoTokenizer.from_pretrained(MODEL) |
| if tok.pad_token_id is None: |
| tok.pad_token_id = tok.eos_token_id |
| model = AutoModelForCausalLM.from_pretrained( |
| MODEL, quantization_config=bnb, torch_dtype=torch.bfloat16, device_map="auto", |
| ) |
| model = prepare_model_for_kbit_training(model, use_gradient_checkpointing=True) |
| lc = LoraConfig(r=32, lora_alpha=64, lora_dropout=0.0, bias="none", |
| target_modules=["q_proj","k_proj","v_proj","o_proj", |
| "gate_proj","up_proj","down_proj"], |
| task_type="CAUSAL_LM") |
| model = get_peft_model(model, lc) |
| model.print_trainable_parameters() |
|
|
| import json |
| from datasets import Dataset |
| rows = [json.loads(l) for l in open("data/sft_traces.jsonl")] |
| print(f"[data] {len(rows)} SFT traces loaded", flush=True) |
| texts = [r["prompt"] + "\n\nACTION:\n" + r["completion"] + tok.eos_token for r in rows] |
| ds = Dataset.from_list([{"text": t} for t in texts]) |
|
|
| from trl import SFTConfig, SFTTrainer |
| import inspect |
|
|
| def safe_kwargs(cls, kw): |
| sig = inspect.signature(cls).parameters |
| return {k: v for k, v in kw.items() if k in sig} |
|
|
| raw = dict( |
| output_dir="/tmp/opsguard-sft", |
| per_device_train_batch_size=4, |
| gradient_accumulation_steps=4, |
| num_train_epochs=2, |
| learning_rate=1e-4, |
| warmup_ratio=0.03, |
| logging_steps=5, |
| save_strategy="epoch", |
| save_total_limit=1, |
| bf16=True, |
| max_seq_length=2048, |
| dataset_text_field="text", |
| report_to="none", |
| push_to_hub=False, |
| ) |
| cfg = SFTConfig(**safe_kwargs(SFTConfig, raw)) |
| trainer = SFTTrainer(model=model, train_dataset=ds, args=cfg, processing_class=tok) |
| trainer.train() |
|
|
| print("[push] saving + pushing to Hub...", flush=True) |
| model.save_pretrained("/tmp/opsguard-sft-lora") |
| tok.save_pretrained("/tmp/opsguard-sft-lora") |
| HfApi(token=os.environ["HF_TOKEN"]).upload_folder( |
| folder_path="/tmp/opsguard-sft-lora", repo_id=HUB_REPO, repo_type="model", |
| ) |
| print(f"DONE: pushed to https://huggingface.co/{HUB_REPO}", flush=True) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|