punsaisuwan commited on
Commit
46f8a2f
·
verified ·
1 Parent(s): c13d0c1

Remove legacy dispatcher (superseded by moe_orchestrator.py)

Browse files
Files changed (1) hide show
  1. frankenmoe_dispatcher.py +0 -161
frankenmoe_dispatcher.py DELETED
@@ -1,161 +0,0 @@
1
- """
2
- FrankenMoE Dispatcher — Rebuild-on-Swap Architecture (v4: Handle MoE Switch Layers)
3
- แก้ปัญหา 'Can't convert layer of type LoRASwitchLinear to LoRA'
4
- โดยตรวจจับทั้ง LoRALinear (Attention) และ LoRASwitchLinear (MoE Expert MLP)
5
- """
6
- import json
7
- from mlx.utils import tree_unflatten
8
- from mlx_lm import load, generate
9
- from mlx_lm.tuner.utils import linear_to_lora_layers
10
-
11
- # 🔑 Import ทั้ง 2 Class ที่ mlx_lm ใช้ห่อ LoRA
12
- # LoRALinear -> ใช้กับ Attention Layer ปกติ (q,k,v,o_proj)
13
- # LoRASwitchLinear -> ใช้กับ MoE Switch/Expert MLP Layer โดยเฉพาะ
14
- LORA_WRAPPER_TYPES = []
15
- try:
16
- from mlx_lm.tuner.lora import LoRALinear
17
- LORA_WRAPPER_TYPES.append(LoRALinear)
18
- except ImportError:
19
- pass
20
- try:
21
- from mlx_lm.tuner.lora import LoRASwitchLinear
22
- LORA_WRAPPER_TYPES.append(LoRASwitchLinear)
23
- except ImportError:
24
- pass
25
-
26
- LORA_WRAPPER_TYPES = tuple(LORA_WRAPPER_TYPES)
27
- print(f"🔎 ตรวจพบ LoRA Wrapper Types ที่ต้องจัดการ: {[t.__name__ for t in LORA_WRAPPER_TYPES]}")
28
-
29
-
30
- class FrankenMoEDispatcher:
31
- def __init__(self, base_model_path="output-moe-mlx-4bit", max_num_layers=16):
32
- print(f"🔄 กำลังโหลด Base Model: {base_model_path} ...")
33
- self.model, self.tokenizer = load(base_model_path)
34
- self.max_num_layers = max_num_layers
35
- self.current_expert = None
36
-
37
- self.experts = {
38
- "python": {
39
- "config_path": "adapters/expert-1-python-v2/adapter_config.json",
40
- "weights_path": "adapters/expert-1-python-v2/0000500_adapters.safetensors",
41
- },
42
- "typescript": {
43
- "config_path": "adapters/expert-2-typescript/adapter_config.json",
44
- "weights_path": "adapters/expert-2-typescript/0000100_adapters.safetensors",
45
- },
46
- }
47
-
48
- # Snapshot ทุก Module ใน Layer เป้าหมาย (Attention + MoE Switch + อะไรก็ตาม)
49
- self._original_layers = {}
50
- total_layers = len(self.model.layers)
51
- self._layer_range = range(total_layers - max_num_layers, total_layers)
52
- for real_idx in self._layer_range:
53
- l = self.model.layers[real_idx]
54
- for k, m in l.named_modules():
55
- self._original_layers[(real_idx, k)] = m
56
-
57
- print(f"✅ Base Model พร้อมใช้งาน (Snapshot {len(self._original_layers)} modules)\n")
58
-
59
- def _restore_original_layers(self):
60
- """สแกนหา Module ที่เป็น LoRALinear หรือ LoRASwitchLinear แล้ว Restore กลับเป็นของเดิม"""
61
- restored_count = 0
62
- restored_types = {}
63
- for real_idx in self._layer_range:
64
- l = self.model.layers[real_idx]
65
- restore_pairs = []
66
- for k, m in l.named_modules():
67
- if isinstance(m, LORA_WRAPPER_TYPES):
68
- orig = self._original_layers.get((real_idx, k))
69
- if orig is not None:
70
- restore_pairs.append((k, orig))
71
- restored_count += 1
72
- type_name = type(m).__name__
73
- restored_types[type_name] = restored_types.get(type_name, 0) + 1
74
- if restore_pairs:
75
- l.update_modules(tree_unflatten(restore_pairs))
76
- return restored_count, restored_types
77
-
78
- def load_expert(self, expert_name: str):
79
- if expert_name == self.current_expert:
80
- return
81
-
82
- if expert_name not in self.experts:
83
- raise ValueError(f"ไม่รู้จัก Expert: {expert_name}")
84
-
85
- info = self.experts[expert_name]
86
- with open(info["config_path"]) as f:
87
- adapter_config = json.load(f)
88
-
89
- num_layers = adapter_config.get("num_layers", 16)
90
- lora_parameters = adapter_config.get("lora_parameters", {})
91
-
92
- print(f"🔧 กำลังสลับไปใช้ Expert: '{expert_name}' "
93
- f"(rank={lora_parameters.get('rank')}, "
94
- f"scale={lora_parameters.get('scale')}, "
95
- f"keys={lora_parameters.get('keys', 'default')})")
96
-
97
- n, types_breakdown = self._restore_original_layers()
98
- print(f" ↳ Restored {n} module(s) กลับเป็นของเดิม | breakdown: {types_breakdown}")
99
-
100
- linear_to_lora_layers(self.model, num_layers, lora_parameters, use_dora=False)
101
- self.model.load_weights(info["weights_path"], strict=False)
102
- self.model.eval()
103
-
104
- self.current_expert = expert_name
105
- print(f"✅ พร้อมใช้งาน Expert: '{expert_name}'\n")
106
-
107
- def route(self, prompt: str) -> str:
108
- ts_signals = [
109
- "typescript", "interface", "type guard", "generic", ": string",
110
- ": number", "utility type", "conditional type", "mapped type",
111
- ]
112
- py_signals = [
113
- "python", "def ", "decorator", "list comprehension",
114
- "generator", "context manager",
115
- ]
116
- prompt_lower = prompt.lower()
117
- ts_score = sum(1 for kw in ts_signals if kw in prompt_lower)
118
- py_score = sum(1 for kw in py_signals if kw in prompt_lower)
119
- return "typescript" if ts_score > py_score else "python"
120
-
121
- def generate_response(self, prompt: str, max_tokens: int = 300):
122
- expert_name = self.route(prompt)
123
- self.load_expert(expert_name)
124
-
125
- messages = [{"role": "user", "content": prompt}]
126
- formatted_prompt = self.tokenizer.apply_chat_template(
127
- messages, add_generation_prompt=True, tokenize=False
128
- )
129
- response = generate(
130
- self.model, self.tokenizer,
131
- prompt=formatted_prompt, max_tokens=max_tokens, verbose=False
132
- )
133
- return expert_name, response
134
-
135
-
136
- if __name__ == "__main__":
137
- dispatcher = FrankenMoEDispatcher()
138
-
139
- test_cases = [
140
- "Write a Python function to check if a number is prime.",
141
- "Write a TypeScript type guard function called isArray.",
142
- "Write a Python decorator that logs execution time.",
143
- "Create a TypeScript utility type Readonly that makes properties readonly recursively.",
144
- "Write a Python generator function that yields Fibonacci numbers.",
145
- "Write a TypeScript function isString that acts as a type guard.",
146
- "Write a Python function using list comprehension to flatten a nested list.",
147
- "Write a TypeScript debounce function using closures.",
148
- ]
149
-
150
- for i, prompt in enumerate(test_cases, 1):
151
- print("=" * 70)
152
- print(f"โจทย์ที่ {i}: {prompt}")
153
- print("=" * 70)
154
- expert_used, response = dispatcher.generate_response(prompt)
155
- print(f"[Router เลือก Expert: {expert_used}]")
156
- print(response)
157
- print()
158
-
159
- print("=" * 70)
160
- print("🎉 สลับ Expert สำเร็จทั้งหมด 8 รอบ โดยไม่มี Error")
161
- print("=" * 70)