File size: 5,745 Bytes
eca5751
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Verify architecture + counting tham số - chạy nhanh
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import torch

from nexus.config import NexusConfig, print_config_summary
from nexus.model.nexus_coder import NexusCoderForCausalLM


def test_tiny_model():
    """Test với model nhỏ."""
    print("\n[Test 1] Tiny model forward pass...")
    tiny_config = NexusConfig(
        vocab_size=1000,
        hidden_size=128,
        num_hidden_layers=2,
        num_attention_heads=4,
        num_kv_heads=2,
        head_dim=32,
        intermediate_size=256,
        num_experts=4,
        num_active_experts=2,
        max_position_embeddings=512,
    )
    model = NexusCoderForCausalLM(tiny_config)

    input_ids = torch.randint(0, 1000, (2, 16))
    labels = input_ids.clone()

    outputs = model(input_ids=input_ids, labels=labels)
    assert outputs["loss"] is not None
    assert outputs["logits"].shape == (2, 16, 1000)
    print(f"  ✓ Loss: {outputs['loss'].item():.4f}")
    print(f"  ✓ Logits shape: {outputs['logits'].shape}")

    # Generate
    generated = model.generate(
        input_ids=torch.randint(0, 1000, (1, 4)),
        max_new_tokens=10,
        do_sample=False,
    )
    assert generated.shape[1] > 4
    print(f"  ✓ Generated shape: {generated.shape}")
    print("  ✓ PASSED!")


def test_param_count():
    """Test đếm tham số theo config."""
    print("\n[Test 2] Param count theo config...")
    config = NexusConfig()
    stats = config.estimated_total_params()
    print(f"  Total: {stats['total_params']:,} ({stats['total_params_billion']:.2f}B)")
    print(f"  Active: {stats['active_params']:,} ({stats['active_params_billion']:.2f}B)")
    assert 9.5e9 < stats["total_params"] < 11e9
    assert 1.3e9 < stats["active_params"] < 1.7e9
    print("  ✓ PASSED!")


def test_tokenizer():
    """Test tokenizer cơ bản."""
    print("\n[Test 3] Tokenizer...")
    from nexus.tokenizer.tokenizer import NexusTokenizer
    from nexus.training.dataset import AUTHOR_TRAINING_DATA

    tokenizer = NexusTokenizer(vocab_size=2000)
    corpus = [f"{d['system']} {d['user']} {d['assistant']}" for d in AUTHOR_TRAINING_DATA]
    tokenizer.train(corpus)

    text = "Xin chào, tôi là Nexus Coder do Hieu Louis tạo ra."
    ids = tokenizer.encode(text, add_special=True)
    decoded = tokenizer.decode(ids)

    assert len(ids) > 0
    assert "Nexus" in decoded or "nexus" in decoded
    print(f"  ✓ Encoded {len(text)} chars -> {len(ids)} tokens")
    print(f"  ✓ Decoded (partial): {decoded[:100]}...")
    print("  ✓ PASSED!")


def test_dataset():
    """Test dataset với author info."""
    print("\n[Test 4] Dataset (author info)...")
    from nexus.tokenizer.tokenizer import NexusTokenizer
    from nexus.training.dataset import NexusDataset, AUTHOR_TRAINING_DATA, get_author_info

    info = get_author_info()
    assert info["name"] == "Hieu Louis"
    assert info["github"] == "mhieuhonda"
    assert info["year"] == "2026"
    print(f"  ✓ Author: {info['name']}")
    print(f"  ✓ GitHub: {info['github']}")
    print(f"  ✓ Year: {info['year']}")
    print(f"  ✓ Training samples: {len(AUTHOR_TRAINING_DATA)}")

    tokenizer = NexusTokenizer(vocab_size=2000)
    corpus = [f"{d['system']} {d['user']} {d['assistant']}" for d in AUTHOR_TRAINING_DATA]
    tokenizer.train(corpus)

    dataset = NexusDataset(tokenizer, max_length=128)
    assert len(dataset) > 0
    sample = dataset[0]
    assert "input_ids" in sample
    assert "labels" in sample
    assert sample["input_ids"].shape[0] == 128
    print(f"  ✓ Dataset size: {len(dataset)}")
    print(f"  ✓ Sample shape: {sample['input_ids'].shape}")
    print("  ✓ PASSED!")


def test_full_pipeline():
    """Test pipeline end-to-end với tiny config."""
    print("\n[Test 5] End-to-end pipeline (tiny)...")
    from nexus.tokenizer.tokenizer import NexusTokenizer
    from nexus.training.dataset import NexusDataset, AUTHOR_TRAINING_DATA
    from nexus.model.nexus_coder import NexusCoderForCausalLM

    config = NexusConfig(
        vocab_size=500,
        hidden_size=64,
        num_hidden_layers=2,
        num_attention_heads=4,
        num_kv_heads=2,
        head_dim=16,
        intermediate_size=128,
        num_experts=4,
        num_active_experts=2,
        max_position_embeddings=128,
    )

    tokenizer = NexusTokenizer(vocab_size=500)
    corpus = [f"{d['system']} {d['user']} {d['assistant']}" for d in AUTHOR_TRAINING_DATA]
    tokenizer.train(corpus)

    dataset = NexusDataset(tokenizer, max_length=64)

    model = NexusCoderForCausalLM(config)

    # Train 1 step
    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)
    batch = torch.utils.data.DataLoader(dataset, batch_size=2).__iter__().__next__()
    outputs = model(
        input_ids=batch["input_ids"],
        attention_mask=batch["attention_mask"],
        labels=batch["labels"],
    )
    loss = outputs["loss"]
    loss.backward()
    optimizer.step()
    print(f"  ✓ Loss sau 1 step: {loss.item():.4f}")

    # Generate
    generated = model.generate(
        input_ids=torch.tensor([[1, 5, 10, 20]], dtype=torch.long),
        max_new_tokens=5,
        do_sample=False,
    )
    print(f"  ✓ Generated: {generated.shape}")
    print("  ✓ PASSED!")


if __name__ == "__main__":
    print("=" * 60)
    print("  NEXUS CODER v0.1 - TEST SUITE")
    print("  Tác giả: Hieu Louis (2026)")
    print("=" * 60)

    print_config_summary()

    test_tiny_model()
    test_param_count()
    test_tokenizer()
    test_dataset()
    test_full_pipeline()

    print("\n" + "=" * 60)
    print("✅ TẤT CẢ TESTS PASSED!")
    print("=" * 60)