File size: 4,234 Bytes
dfb775d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
113
114
115
116
117
118
119
"""XTrainConfig YAML round-trip and validation against the canonical 10-section schema."""

from __future__ import annotations

import pytest
import yaml
from pydantic import ValidationError

from mindxtrain.config.loader import list_recipes, load_config, render_recipe
from mindxtrain.config.schema import LoraMethod, XTrainConfig


def test_qwen3_8b_sft_lora_validates(tmp_path):
    yaml_text = render_recipe("qwen3_8b_sft_lora")
    cfg_path = tmp_path / "run.yaml"
    cfg_path.write_text(yaml_text)
    cfg = load_config(cfg_path)
    assert cfg.meta.run_name == "qwen3_8b_sft_lora"
    assert cfg.meta.seed == 2048
    assert cfg.model.name == "Qwen/Qwen3-8B"
    assert cfg.hardware.gfx_arch == "gfx942"
    assert cfg.hardware.gpus == 1
    assert cfg.autotune.policy == "aot_only"
    assert cfg.train.backend == "axolotl"
    assert isinstance(cfg.train.method, LoraMethod)
    assert cfg.train.method.r == 16
    assert cfg.train.flash_attention.backend == "ck"
    assert cfg.train.env["PYTORCH_ROCM_ARCH"] == "gfx942"
    assert "mmlu" in cfg.eval.harness.tasks
    assert cfg.quantize.scheme == "quark_fp8"
    assert cfg.quantize.ptpc is True
    assert cfg.serve.backend == "vllm-rocm"
    assert cfg.serve.reasoning_parser == "qwen3"


def test_instella_template_validates(tmp_path):
    yaml_text = render_recipe("instella_3b_lora")
    cfg_path = tmp_path / "run.yaml"
    cfg_path.write_text(yaml_text)
    cfg = load_config(cfg_path)
    assert cfg.model.name == "amd/Instella-3B-Instruct"
    assert cfg.data.max_samples == 5000


def test_demo_qwen3_8b_sft_example_validates():
    """The hero config copied verbatim from the production blueprint must load."""
    cfg = load_config("examples/demo_qwen3_8b_sft.yaml")
    assert cfg.meta.run_name == "qwen3_8b_sft_demo"
    assert isinstance(cfg.train.method, LoraMethod)
    assert cfg.train.method.alpha == 32
    assert cfg.publish.enabled is True
    assert cfg.publish.billing.x402.network == "algorand"
    assert cfg.publish.billing.x402.asset == "USDC"


def test_all_recipes_validate():
    """Every shipped recipe must round-trip against XTrainConfig."""
    for name in list_recipes():
        text = render_recipe(name)
        cfg = XTrainConfig.model_validate(yaml.safe_load(text))
        assert cfg.meta.run_name


def test_unknown_template_raises():
    with pytest.raises(FileNotFoundError):
        render_recipe("does_not_exist")


def test_xgmi_2gpu_rejected(tmp_path):
    """hardware.gpus must be 1 or 8 — xGMI asymmetry on 2/4-GPU MI300X."""
    bad = yaml.safe_load(render_recipe("qwen3_32b_full_fsdp"))
    bad["hardware"]["gpus"] = 2
    cfg_path = tmp_path / "bad.yaml"
    cfg_path.write_text(yaml.safe_dump(bad))
    with pytest.raises(ValidationError):
        load_config(cfg_path)


def test_extra_field_forbidden(tmp_path):
    bad = yaml.safe_load(render_recipe("qwen3_8b_sft_lora"))
    bad["meta"]["nonexistent"] = "foo"
    cfg_path = tmp_path / "bad.yaml"
    cfg_path.write_text(yaml.safe_dump(bad))
    with pytest.raises(ValidationError):
        load_config(cfg_path)


def test_method_discriminator_rejects_unknown_kind(tmp_path):
    bad = yaml.safe_load(render_recipe("qwen3_8b_sft_lora"))
    bad["train"]["method"] = {"kind": "not_a_real_method"}
    cfg_path = tmp_path / "bad.yaml"
    cfg_path.write_text(yaml.safe_dump(bad))
    with pytest.raises(ValidationError):
        load_config(cfg_path)


def test_dpo_method_round_trip():
    text = render_recipe("qwen3_32b_dpo")
    cfg = XTrainConfig.model_validate(yaml.safe_load(text))
    assert cfg.train.method.kind == "dpo"
    assert cfg.train.method.beta == 0.1
    assert cfg.hardware.gpus == 8


def test_grpo_method_round_trip():
    text = render_recipe("qwen3_32b_grpo")
    cfg = XTrainConfig.model_validate(yaml.safe_load(text))
    assert cfg.train.method.kind == "grpo"
    assert cfg.train.method.num_generations == 4


def test_round_trip_via_model_dump(tmp_path):
    cfg_path = tmp_path / "run.yaml"
    cfg_path.write_text(render_recipe("qwen3_8b_sft_lora"))
    cfg = load_config(cfg_path)
    redumped = yaml.safe_dump(cfg.model_dump(mode="json"))
    cfg2 = XTrainConfig.model_validate(yaml.safe_load(redumped))
    assert cfg2 == cfg