File size: 4,174 Bytes
23ee7f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from __future__ import annotations

import hashlib
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict


def normalize_training_config(config: Dict[str, Any]) -> Dict[str, Any]:
    return {
        "model_id": str(config.get("model_id", "LiquidAI/LFM2.5-VL-450M")),
        "revision": str(config.get("revision", "main")),
        "epochs": int(config.get("epochs", 1)),
        "learning_rate": float(config.get("learning_rate", 2e-4)),
        "lora_r": int(config.get("lora_r", 16)),
        "lora_alpha": int(config.get("lora_alpha", 32)),
        "lora_dropout": float(config.get("lora_dropout", 0.05)),
        "dataset_manifest_path": str(config.get("dataset_manifest_path", "data/manifests/dataset_manifest_v1.json")),
        "dataset_split_path": str(config.get("dataset_split_path", "data/manifests/dataset_splits_v1.json")),
    }


def _stable_hash(payload: Dict[str, Any]) -> str:
    canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
    return hashlib.sha256(canonical).hexdigest()


def _write_json(path: Path, payload: Dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")


def create_training_artifacts(
    artifact_root: Path,
    run_id: str,
    artifact_volume: str,
    config: Dict[str, Any],
) -> Dict[str, Any]:
    normalized = normalize_training_config(config)
    run_dir = artifact_root / run_id
    checkpoint_dir = run_dir / "checkpoint-lora-v1"
    checkpoint_dir.mkdir(parents=True, exist_ok=True)

    adapter_config = {
        "base_model_name_or_path": normalized["model_id"],
        "peft_type": "LORA",
        "r": normalized["lora_r"],
        "lora_alpha": normalized["lora_alpha"],
        "lora_dropout": normalized["lora_dropout"],
        "inference_mode": False,
        "task_type": "CAUSAL_LM",
        "note": "Phase 6 scaffold checkpoint artifact.",
    }
    _write_json(checkpoint_dir / "adapter_config.json", adapter_config)

    # Scaffold artifact to anchor downstream wiring before full trainer loop.
    (checkpoint_dir / "adapter_model.safetensors").write_bytes(
        json.dumps(
            {
                "artifact_type": "phase6.scaffold.weights",
                "note": "Placeholder adapter blob. Replace in full LoRA trainer.",
            },
            sort_keys=True,
        ).encode("utf-8")
    )
    _write_json(
        checkpoint_dir / "training_args.json",
        {
            "epochs": normalized["epochs"],
            "learning_rate": normalized["learning_rate"],
            "dataset_manifest_path": normalized["dataset_manifest_path"],
            "dataset_split_path": normalized["dataset_split_path"],
        },
    )

    reproducibility = {
        "run_id": run_id,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "config_hash": _stable_hash(normalized),
        "config": normalized,
    }
    _write_json(run_dir / "reproducibility.json", reproducibility)

    run_manifest = {
        "run_id": run_id,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "artifact_volume": artifact_volume,
        "model_id": normalized["model_id"],
        "revision": normalized["revision"],
        "checkpoint_dir": str(checkpoint_dir),
        "adapter_artifact_ref": f"modal-volume://{artifact_volume}/{run_id}/checkpoint-lora-v1",
        "dataset_manifest_path": normalized["dataset_manifest_path"],
        "dataset_split_path": normalized["dataset_split_path"],
        "config_hash": reproducibility["config_hash"],
        "training_mode": "phase6_scaffold",
    }
    manifest_path = run_dir / "run_manifest.json"
    _write_json(manifest_path, run_manifest)

    return {
        "status": "ok",
        "run_id": run_id,
        "manifest_path": str(manifest_path),
        "checkpoint_dir": str(checkpoint_dir),
        "adapter_artifact_ref": run_manifest["adapter_artifact_ref"],
        "artifact_volume": artifact_volume,
        "config_hash": reproducibility["config_hash"],
        "training_mode": "phase6_scaffold",
    }