VibeGame-8B / code /tests /test_training_export.py
khursheed's picture
Release pipeline source, tests, notebook, and licenses
ae36cfb verified
Raw History Blame Contribute Delete
6.08 kB
"""CPU checks for leakage guards, whole-project token masks, and streamed LoRA math."""
import importlib.util
import json
import tempfile
import unittest
from pathlib import Path
from unittest import mock
ROOT = Path(__file__).resolve().parents[1]
def module(name):
spec = importlib.util.spec_from_file_location(name, ROOT / 'scripts' / (name + '.py'))
obj = importlib.util.module_from_spec(spec)
spec.loader.exec_module(obj)
return obj
train = module('train')
export = module('export_model')
class FakeTokenizer:
def apply_chat_template(self, messages, tokenize, add_generation_prompt):
prefix = [1, 2, 3]
return prefix if add_generation_prompt else prefix + [4, 5, 6, 7]
class TrainingTests(unittest.TestCase):
def row(self, instruction='one', marker='one'):
return {'instruction': instruction, 'output': json.dumps({'files': {'project.godot': marker, 'main.gd': 'extends Node'}})}
def test_assistant_mask_and_whole_project_rejection(self):
rows, rejected = train.tokenize_rows([self.row()], FakeTokenizer(), 7, 'train')
self.assertEqual(rows[0]['labels'], [-100, -100, -100, 4, 5, 6, 7])
self.assertEqual(rejected, [])
with self.assertRaisesRegex(ValueError, 'No complete'):
train.tokenize_rows([self.row()], FakeTokenizer(), 6, 'train')
def test_blind_and_output_leakage(self):
blind = [{'instruction': f'blind {i}'} for i in range(500)]
train.check_splits([self.row('training')], [self.row('development', 'two')], blind)
with self.assertRaisesRegex(ValueError, 'contamination'):
train.check_splits([self.row('blind 1')], [self.row('development', 'two')], blind)
with self.assertRaisesRegex(ValueError, 'Identical project'):
train.check_splits([self.row('training')], [self.row('development')], blind)
with self.assertRaisesRegex(ValueError, '500'):
train.check_splits([self.row()], [self.row('dev', 'other')], blind[:-1])
def test_only_intact_complete_checkpoint_resumes(self):
with tempfile.TemporaryDirectory() as d:
root = Path(d)
old = root / 'checkpoint-1'; old.mkdir()
new = root / 'checkpoint-2'; new.mkdir()
(old / 'adapter').write_text('complete')
train.atomic_json(old / 'COMPLETE.json', {'sha256': {'adapter': train.digest(old / 'adapter')}})
self.assertEqual(train.latest_complete_checkpoint(root), old)
(new / 'adapter').write_text('complete')
train.atomic_json(new / 'COMPLETE.json', {'sha256': {'adapter': train.digest(new / 'adapter')}})
(new / 'adapter').write_text('interrupted')
self.assertEqual(train.latest_complete_checkpoint(root), old)
class ExportTests(unittest.TestCase):
def test_converter_installs_only_into_overlay_and_preserves_torch(self):
versions = {'python': '3.12.12', 'packages': {'torch': '2.11.0+cu130'}}
with tempfile.TemporaryDirectory() as d:
workspace = Path(d)
with mock.patch('importlib.metadata.version', return_value='2.11.0+cu130'), \
mock.patch.object(export, 'run') as run, \
mock.patch.object(export.subprocess, 'check_output', return_value=json.dumps(versions)):
python, env, actual = export.converter_environment(workspace / 'llama.cpp', workspace)
commands = [call.args[0] for call in run.call_args_list]
self.assertIn('--system-site-packages', commands[0])
self.assertIn('--without-pip', commands[0])
self.assertEqual(commands[1][:2], [python, '-c'])
self.assertIn('sys.prefix != sys.base_prefix', commands[1][2])
self.assertIn('sysconfig.get_path', commands[1][2])
self.assertIn("find_spec('pip')", commands[1][2])
self.assertEqual(commands[2][:4], [python, '-m', 'pip', 'install'])
self.assertEqual(python, workspace / 'converter-venv/bin/python')
self.assertEqual((workspace / 'converter-constraints.txt').read_text(), 'torch===2.11.0+cu130\n')
self.assertEqual(env['PIP_REQUIRE_VIRTUALENV'], 'true')
self.assertEqual(env['OMP_NUM_THREADS'], '1')
self.assertEqual(actual, versions)
with mock.patch('importlib.metadata.version', return_value='2.10.0'), \
mock.patch.object(export, 'run') as run:
with self.assertRaisesRegex(RuntimeError, 'requires installed torch'):
export.converter_environment(Path('/unused'), Path('/unused'))
run.assert_not_called()
def test_row_chunked_merge_matches_dense_math(self):
import torch
torch.manual_seed(3)
w = torch.randn(513, 257, dtype=torch.bfloat16)
a = torch.randn(32, 257) * .01
b = torch.randn(513, 32) * .01
merged = export.merge_tensor(w, {'A': a, 'B': b}, 2, row_chunk=41)
expected = (w.float() + (b @ a) * 2).half()
torch.testing.assert_close(merged, expected, rtol=.001, atol=.001)
self.assertEqual(merged.dtype, torch.float16)
def test_adapter_names_and_nonfinite_are_rejected(self):
import torch
from safetensors.torch import save_file
with tempfile.TemporaryDirectory() as d:
path = Path(d) / 'adapter.safetensors'
save_file({'base_model.model.model.layers.0.self_attn.q_proj.lora_A.weight': torch.zeros(32, 3),
'base_model.model.model.layers.0.self_attn.q_proj.lora_B.weight': torch.zeros(4, 32)}, path)
self.assertIn('model.layers.0.self_attn.q_proj.weight', export.lora_pairs(path))
save_file({'unknown': torch.zeros(2)}, path)
with self.assertRaisesRegex(ValueError, 'Unsupported'):
export.lora_pairs(path)
with self.assertRaisesRegex(ValueError, 'Nonfinite'):
export.merge_tensor(torch.zeros(3, 3), {'A': torch.ones(1, 3), 'B': torch.full((3, 1), float('inf'))}, 2)
if __name__ == '__main__':
unittest.main()