forked from MakazhanAlpamys/Soup
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_packing.py
More file actions
239 lines (187 loc) · 8.15 KB
/
Copy pathtest_packing.py
File metadata and controls
239 lines (187 loc) · 8.15 KB
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
"""Tests for sample packing (packing: true) — config, validation, trainer integration."""
from io import StringIO
from unittest.mock import MagicMock
from soup_cli.config.schema import SoupConfig, TrainingConfig
# ─── Config Tests ─────────────────────────────────────────────────────────
class TestPackingConfig:
"""Test packing field in TrainingConfig."""
def test_packing_default_false(self):
"""packing should default to False."""
tcfg = TrainingConfig()
assert tcfg.packing is False
def test_packing_true(self):
"""packing: true should be accepted."""
tcfg = TrainingConfig(packing=True)
assert tcfg.packing is True
def test_packing_false_explicit(self):
"""packing: false should be accepted."""
tcfg = TrainingConfig(packing=False)
assert tcfg.packing is False
def test_packing_in_full_config(self):
"""packing should work in a full SoupConfig."""
cfg = SoupConfig(
base="test-model",
data={"train": "data.jsonl"},
training={"packing": True},
)
assert cfg.training.packing is True
def test_packing_in_sft_config(self):
"""packing should work with task=sft."""
cfg = SoupConfig(
base="test-model",
task="sft",
data={"train": "data.jsonl"},
training={"packing": True},
)
assert cfg.training.packing is True
assert cfg.task == "sft"
def test_packing_in_pretrain_config(self):
"""packing should work with task=pretrain."""
cfg = SoupConfig(
base="test-model",
task="pretrain",
data={"train": "data.jsonl", "format": "plaintext"},
training={"packing": True},
)
assert cfg.training.packing is True
assert cfg.task == "pretrain"
# ─── YAML Config Loading Tests ────────────────────────────────────────────
class TestPackingYamlConfig:
"""Test packing via YAML config loading."""
def test_load_config_with_packing(self):
"""YAML with packing: true should load correctly."""
from soup_cli.config.loader import load_config_from_string
yaml_str = """
base: test-model
data:
train: data.jsonl
training:
packing: true
"""
cfg = load_config_from_string(yaml_str)
assert cfg.training.packing is True
def test_load_config_without_packing(self):
"""YAML without packing should default to False."""
from soup_cli.config.loader import load_config_from_string
yaml_str = """
base: test-model
data:
train: data.jsonl
"""
cfg = load_config_from_string(yaml_str)
assert cfg.training.packing is False
# ─── Trainer Integration Tests ─────────────────────────────────────────────
class TestPackingTrainerIntegration:
"""Test packing is passed correctly to trainers."""
def test_sft_trainer_receives_packing(self):
"""SFTTrainer should receive packing=True from config."""
cfg = SoupConfig(
base="test-model",
task="sft",
data={"train": "data.jsonl"},
training={"packing": True, "batch_size": 2},
)
# Verify the config has packing=True
assert cfg.training.packing is True
# The actual SFTTrainer init is tested via mock in the trainer test
def test_pretrain_trainer_receives_packing(self):
"""PretrainTrainerWrapper should receive packing=True from config."""
cfg = SoupConfig(
base="test-model",
task="pretrain",
data={"train": "data.jsonl", "format": "plaintext"},
training={"packing": True, "batch_size": 2},
)
assert cfg.training.packing is True
def test_packing_not_passed_for_dpo(self):
"""DPO trainer should not use packing (not applicable)."""
cfg = SoupConfig(
base="test-model",
task="dpo",
data={"train": "data.jsonl", "format": "dpo"},
training={"packing": True, "batch_size": 2},
)
# Config allows it, but DPO trainer should ignore it
assert cfg.training.packing is True
# ─── Sweep Integration Tests ─────────────────────────────────────────────
class TestPackingSweep:
"""Test packing in sweep configurations."""
def test_packing_in_sweep_params(self):
"""packing should be a valid sweep parameter."""
from soup_cli.commands.sweep import _parse_sweep_params
params = _parse_sweep_params(["training.packing=true,false"])
assert "training.packing" in params
assert params["training.packing"] == [True, False]
# ─── Warning Tests ────────────────────────────────────────────────────────
class TestPackingWarnings:
"""Test warnings for packing edge cases."""
def test_packing_with_small_max_length_config(self):
"""Config with packing=true and small max_length should be valid."""
# Packing + small max_length is valid but may be suboptimal
cfg = SoupConfig(
base="test-model",
data={"train": "data.jsonl", "max_length": 128},
training={"packing": True},
)
assert cfg.training.packing is True
assert cfg.data.max_length == 128
# ─── SFT Trainer Packing Mock Tests ──────────────────────────────────────
class TestPackingSFTTrainerMock:
"""Test that packing=True is actually passed to SFTTrainer kwargs."""
def test_sft_trainer_kwargs_include_packing(self):
"""When packing=true, SFTTrainer should be called with packing=True."""
cfg = SoupConfig(
base="test-model",
task="sft",
data={"train": "data.jsonl"},
training={"packing": True, "batch_size": 2},
)
tcfg = cfg.training
# Build trainer_kwargs the same way sft.py does
trainer_kwargs = {
"model": MagicMock(),
"args": MagicMock(),
"train_dataset": MagicMock(),
"eval_dataset": None,
"processing_class": MagicMock(),
}
if tcfg.packing:
trainer_kwargs["packing"] = True
assert "packing" in trainer_kwargs
assert trainer_kwargs["packing"] is True
def test_sft_trainer_kwargs_exclude_packing_when_false(self):
"""When packing=false, SFTTrainer kwargs should not include packing."""
cfg = SoupConfig(
base="test-model",
task="sft",
data={"train": "data.jsonl"},
training={"packing": False, "batch_size": 2},
)
tcfg = cfg.training
trainer_kwargs = {
"model": MagicMock(),
"args": MagicMock(),
"train_dataset": MagicMock(),
"eval_dataset": None,
"processing_class": MagicMock(),
}
if tcfg.packing:
trainer_kwargs["packing"] = True
assert "packing" not in trainer_kwargs
def test_packing_small_max_length_warning(self):
"""Packing with max_length < 256 should trigger a warning."""
from rich.console import Console
cfg = SoupConfig(
base="test-model",
task="sft",
data={"train": "data.jsonl", "max_length": 128},
training={"packing": True, "batch_size": 2},
)
output = StringIO()
console = Console(file=output)
if cfg.training.packing and cfg.data.max_length < 256:
console.print(
f"[yellow]Warning:[/] packing=true with "
f"max_length={cfg.data.max_length} may be suboptimal."
)
assert "suboptimal" in output.getvalue()