FSI commited on
Commit
931659a
·
0 Parent(s):

FSI_ECHO v2 space

Browse files
Files changed (4) hide show
  1. README.md +12 -0
  2. app.py +51 -0
  3. fsi_echo.py +486 -0
  4. requirements.txt +3 -0
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: FSI_ECHO
3
+ emoji: 🧬
4
+ colorFrom: purple
5
+ colorTo: indigo
6
+ sdk: gradio
7
+ sdk_version: "5.0.0"
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+ # FSI_ECHO - 2.6M Param Code AI
app.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys, os, json, torch, requests, time
2
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
3
+ from fsi_echo import FSIEchoModel, CodeTokenizer, ClosedLoopDebugger
4
+ import gradio as gr
5
+
6
+ @torch.no_grad()
7
+ def load():
8
+ MODEL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'prod2_final.pt')
9
+ if not os.path.exists(MODEL_PATH):
10
+ url = "https://huggingface.co/FerrellSyntheticIntelligence/FSI_ECHO/resolve/main/prod2_final.pt"
11
+ r = requests.get(url, stream=True, timeout=300)
12
+ r.raise_for_status()
13
+ with open(MODEL_PATH, 'wb') as f:
14
+ for chunk in r.iter_content(chunk_size=8192):
15
+ f.write(chunk)
16
+ ckpt = torch.load(MODEL_PATH, map_location='cpu', weights_only=True)
17
+ m = FSIEchoModel()
18
+ m.load_state_dict(ckpt['model'])
19
+ m.eval()
20
+ return m, CodeTokenizer()
21
+
22
+ model, tok = load()
23
+ debugger = ClosedLoopDebugger(model, tok)
24
+
25
+ def generate(prompt, temp, max_t):
26
+ t0 = time.time()
27
+ r = model.generate(tok, prompt, max_tokens=int(max_t), temperature=float(temp), top_k=5, top_p=0.95)
28
+ return r['generated'], f"{r['tokens']}t | conf:{r['confidence']:.2f} | {time.time()-t0:.1f}s"
29
+
30
+ def debug(code):
31
+ r = debugger.debug(code)
32
+ return r.get('code', 'Could not fix')
33
+
34
+ with gr.Blocks(title="FSI_ECHO", theme=gr.themes.Monochrome()) as demo:
35
+ gr.Markdown("# 🧬 FSI_ECHO — 2.6M Param Code AI\n*Morphing Code Swarm*")
36
+ with gr.Tab("Generate"):
37
+ p = gr.Textbox(label="Prompt", value="def is_even")
38
+ t = gr.Slider(0.1, 1.0, 0.1, label="Temperature")
39
+ m = gr.Slider(10, 200, 50, label="Max Tokens")
40
+ b = gr.Button("Generate")
41
+ o = gr.Textbox(label="Output", lines=8)
42
+ s = gr.Textbox(label="Stats")
43
+ b.click(generate, [p, t, m], [o, s])
44
+ with gr.Tab("Debug"):
45
+ c = gr.Textbox(label="Buggy Code", value="def add(a, b):\n a + b", lines=6)
46
+ b2 = gr.Button("Debug")
47
+ o2 = gr.Textbox(label="Fixed Code", lines=8)
48
+ b2.click(debug, [c], [o2])
49
+
50
+ if __name__ == '__main__':
51
+ demo.launch(server_name='0.0.0.0', server_port=7860)
fsi_echo.py ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ FSI_ECHO - Morphing Code Swarm
4
+ Novel architecture: token morph embedding + nanobot swarm + assembly blocks + self-verification.
5
+ 2.6M params — fits in 1.3MB at q4, runs on any phone.
6
+ """
7
+ import os, sys, json, time, math, random, re, struct
8
+ from typing import List, Dict, Optional, Tuple
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+
13
+ # =============================================================================
14
+ # 1. TOKENIZER
15
+ # =============================================================================
16
+ class CodeTokenizer:
17
+ SPECIAL = {
18
+ '<PAD>': 0, '<EOS>': 1, '<BOS>': 2, '<UNK>': 3,
19
+ '<BUG>': 4, '<FIX>': 5, '<CODE>': 6, '<EXPLAIN>': 7,
20
+ '<MORPH>': 8, '<ASSEMBLE>': 9, '<SCOUT>': 10, '<COMBAT>': 11,
21
+ }
22
+ def __init__(self, vocab_size: int = 4096):
23
+ self.vocab_size = vocab_size
24
+ self.vocab = dict(self.SPECIAL)
25
+ self.inverse = {v: k for k, v in self.SPECIAL.items()}
26
+ self.next_id = len(self.SPECIAL)
27
+ self._build()
28
+ def _build(self):
29
+ for i in range(32, 127):
30
+ self._add(chr(i))
31
+ for t in ['def','class','return','if','else','elif','for','while','in','not',
32
+ 'and','or','import','from','as','try','except','finally','raise','with',
33
+ 'pass','break','continue','yield','lambda','self','None','True','False',
34
+ 'async','await','global','nonlocal','assert','del','print','len','range',
35
+ 'int','str','float','list','dict','set','tuple','type','is','isinstance',
36
+ 'hasattr','getattr','setattr','super','open','Exception','ValueError',
37
+ 'TypeError','KeyError','IndexError','AttributeError','ImportError',
38
+ 'Error','Warning','property','staticmethod','classmethod']:
39
+ self._add(t)
40
+ for s in ['==','!=','<=','>=','->','+=','-=','*=','/=','//=','**=','%=',
41
+ '<<','>>','**','//','::','=>','++','--','...']:
42
+ self._add(s)
43
+ for t in ['fn','func','function','const','let','var','this','typeof','void',
44
+ 'null','undefined','prototype','module','exports','require','new','delete',
45
+ 'throw','catch','switch','case','default','do','while','interface','enum',
46
+ 'implements','private','public','protected','abstract','final','static',
47
+ 'package','boolean','byte','char','double','float','int','long','short',
48
+ 'printf','scanf','malloc','free','sizeof','typedef','struct','union',
49
+ 'include','define','template','typename','namespace','using','virtual',
50
+ 'override','friend','operator','inline','explicit','string','vector',
51
+ 'map','set','auto','decltype','noexcept','constexpr','std','cout','cin',
52
+ 'endl','printf','scanf','NULL','nullptr','true','false','bool']:
53
+ self._add(t)
54
+ while self.next_id < self.vocab_size:
55
+ self._add(f'v{self.next_id}')
56
+ def _add(self, t):
57
+ if t not in self.vocab and self.next_id < self.vocab_size:
58
+ self.vocab[t] = self.next_id
59
+ self.inverse[self.next_id] = t
60
+ self.next_id += 1
61
+ def encode(self, text: str, bos: bool = True, eos: bool = False) -> List[int]:
62
+ ids = []
63
+ if bos:
64
+ ids.append(2)
65
+ for token in re.findall(r'<[^>]+>|[A-Za-z_][A-Za-z0-9_]*|\.\.\.|==|!=|<=|>=|->|\*\*|//|::|=>|\d+\.\d*|\d+|\S', text):
66
+ if token in self.vocab:
67
+ ids.append(self.vocab[token])
68
+ elif token.lower() in self.vocab:
69
+ ids.append(self.vocab[token.lower()])
70
+ else:
71
+ for ch in token:
72
+ if ch in self.vocab:
73
+ ids.append(self.vocab[ch])
74
+ else:
75
+ ids.append(3)
76
+ if eos:
77
+ ids.append(1)
78
+ return ids[:2048]
79
+ def decode(self, ids: List[int], skip_special: bool = True) -> str:
80
+ tokens = []
81
+ for i in ids:
82
+ if i in self.inverse:
83
+ t = self.inverse[i]
84
+ if skip_special and t.startswith('<') and t.endswith('>'):
85
+ continue
86
+ tokens.append(t)
87
+ else:
88
+ tokens.append(' ')
89
+ return ''.join(tokens)
90
+ @property
91
+ def pad_id(self): return 0
92
+ @property
93
+ def eos_id(self): return 1
94
+ @property
95
+ def bos_id(self): return 2
96
+ @property
97
+ def vocab_size_(self): return len(self.vocab)
98
+
99
+ # =============================================================================
100
+ # 2. MORPH EMBEDDING
101
+ # =============================================================================
102
+ class MorphEmbedding(nn.Module):
103
+ def __init__(self, vocab_size: int, d_model: int, morph_width: int = 3):
104
+ super().__init__()
105
+ self.d_model = d_model
106
+ self.morph_width = morph_width
107
+ self.base_embed = nn.Embedding(vocab_size, d_model)
108
+ # Causal morph: only current + past (morph_width tokens, no future leakage)
109
+ self.morph_net = nn.Sequential(
110
+ nn.Linear(d_model * morph_width, d_model), nn.Tanh(),
111
+ nn.Linear(d_model, d_model),
112
+ )
113
+ self.gate = nn.Linear(d_model * 2, d_model)
114
+
115
+ def forward(self, tokens: torch.Tensor) -> torch.Tensor:
116
+ B, T = tokens.shape
117
+ base = self.base_embed(tokens)
118
+ # Causal sliding window: each position only sees itself and preceding tokens
119
+ padded = F.pad(base, (0, 0, self.morph_width - 1, 0), mode='replicate')
120
+ contexts = []
121
+ for i in range(self.morph_width):
122
+ contexts.append(padded[:, i:i+T, :])
123
+ context = torch.cat(contexts, dim=-1)
124
+ morph = self.morph_net(context)
125
+ gate = torch.sigmoid(self.gate(torch.cat([base, morph], dim=-1)))
126
+ return base + gate * morph
127
+
128
+ # =============================================================================
129
+ # 3. NANOBOT SWARM
130
+ # =============================================================================
131
+ class NanobotSwarm(nn.Module):
132
+ def __init__(self, d_model: int, n_nanobots: int = 512, scout_dim: int = 64, combat_dim: int = 128):
133
+ super().__init__()
134
+ self.d_model = d_model
135
+ self.n_nanobots = n_nanobots
136
+ self.nano_keys = nn.Parameter(torch.randn(n_nanobots, d_model // 4))
137
+ self.nano_vals = nn.Parameter(torch.randn(n_nanobots, d_model))
138
+ self.scout_ffn = nn.Sequential(
139
+ nn.Linear(d_model, scout_dim), nn.GELU(), nn.Linear(scout_dim, d_model),
140
+ )
141
+ self.combat_ffn = nn.Sequential(
142
+ nn.Linear(d_model, combat_dim), nn.GELU(), nn.Linear(combat_dim, d_model),
143
+ )
144
+ self.mode_router = nn.Linear(d_model, 2)
145
+ self.assembly = nn.Linear(d_model, d_model)
146
+ self.norm = nn.LayerNorm(d_model)
147
+ self.dropout = nn.Dropout(0.1)
148
+
149
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
150
+ B, T, D = x.shape
151
+ residual = x
152
+ x = self.norm(x)
153
+ router_query = x[..., :self.d_model // 4]
154
+ nano_scores = torch.matmul(router_query, self.nano_keys.T)
155
+ nano_weights = F.softmax(nano_scores / math.sqrt(self.d_model // 4), dim=-1)
156
+ mode_logits = self.mode_router(x)
157
+ mode_w = F.softmax(mode_logits, dim=-1)
158
+ scout_out = self.scout_ffn(x)
159
+ combat_out = self.combat_ffn(x)
160
+ mode_out = mode_w[:, :, 0:1] * scout_out + mode_w[:, :, 1:2] * combat_out
161
+ nano_out = torch.matmul(nano_weights, self.nano_vals)
162
+ out = residual + self.dropout(self.assembly(mode_out + nano_out))
163
+ return out
164
+
165
+ # =============================================================================
166
+ # 4. ASSEMBLY BLOCK
167
+ # =============================================================================
168
+ class AssemblyBlock(nn.Module):
169
+ def __init__(self, d_model: int, n_heads: int = 4, dropout: float = 0.1):
170
+ super().__init__()
171
+ self.d_model = d_model
172
+ self.n_heads = n_heads
173
+ self.head_dim = d_model // n_heads
174
+ self.qkv = nn.Linear(d_model, 3 * d_model, bias=False)
175
+ self.proj = nn.Linear(d_model, d_model, bias=False)
176
+ self.ffn = nn.Sequential(
177
+ nn.Linear(d_model, d_model * 2), nn.GELU(),
178
+ nn.Linear(d_model * 2, d_model), nn.Dropout(dropout),
179
+ )
180
+ self.adapt_gate = nn.Linear(d_model, 1)
181
+ self.norm1 = nn.LayerNorm(d_model)
182
+ self.norm2 = nn.LayerNorm(d_model)
183
+ self.dropout = nn.Dropout(dropout)
184
+
185
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
186
+ B, T, D = x.shape
187
+ qkv = self.qkv(self.norm1(x))
188
+ qkv = qkv.reshape(B, T, 3, self.n_heads, self.head_dim)
189
+ q, k, v = qkv[:, :, 0], qkv[:, :, 1], qkv[:, :, 2]
190
+ q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)
191
+ scale = math.sqrt(self.head_dim)
192
+ attn = torch.matmul(q, k.transpose(-2, -1)) / scale
193
+ mask = torch.triu(torch.ones(T, T, device=x.device) * float('-inf'), diagonal=1)
194
+ attn = attn + mask.unsqueeze(0)
195
+ attn = F.softmax(attn, dim=-1)
196
+ out = torch.matmul(attn, v).transpose(1, 2).reshape(B, T, D)
197
+ out = self.proj(out)
198
+ x = x + self.dropout(out)
199
+ gate = torch.sigmoid(self.adapt_gate(self.norm2(x)))
200
+ x = x + gate * self.dropout(self.ffn(self.norm2(x)))
201
+ return x
202
+
203
+ # =============================================================================
204
+ # 5. FSI_ECHO MODEL
205
+ # =============================================================================
206
+ class FSIEchoModel(nn.Module):
207
+ def __init__(self, vocab_size: int = 4096, d_model: int = 192,
208
+ n_swarm_layers: int = 3, n_assembly_layers: int = 3,
209
+ n_nanobots: int = 512):
210
+ super().__init__()
211
+ self.vocab_size = vocab_size
212
+ self.d_model = d_model
213
+ self.morph_embed = MorphEmbedding(vocab_size, d_model)
214
+ self.swarm_layers = nn.ModuleList([
215
+ NanobotSwarm(d_model, n_nanobots) for _ in range(n_swarm_layers)
216
+ ])
217
+ self.assembly_layers = nn.ModuleList([
218
+ AssemblyBlock(d_model) for _ in range(n_assembly_layers)
219
+ ])
220
+ self.norm = nn.LayerNorm(d_model)
221
+ self.verify = nn.Sequential(
222
+ nn.Linear(d_model, d_model // 2), nn.GELU(), nn.Linear(d_model // 2, 1),
223
+ )
224
+ self.lm_head = nn.Linear(d_model, vocab_size, bias=False)
225
+ self.lm_head.weight = self.morph_embed.base_embed.weight
226
+ self._init_weights()
227
+
228
+ def _init_weights(self):
229
+ for m in self.modules():
230
+ if isinstance(m, (nn.Linear, nn.Embedding)):
231
+ nn.init.normal_(m.weight, mean=0.0, std=0.02)
232
+ if hasattr(m, 'bias') and m.bias is not None:
233
+ nn.init.zeros_(m.bias)
234
+ elif isinstance(m, nn.LayerNorm):
235
+ nn.init.ones_(m.weight)
236
+ nn.init.zeros_(m.bias)
237
+
238
+ def forward(self, tokens: torch.Tensor, targets: Optional[torch.Tensor] = None) -> Dict:
239
+ x = self.morph_embed(tokens)
240
+ for layer in self.swarm_layers:
241
+ x = layer(x)
242
+ for layer in self.assembly_layers:
243
+ x = layer(x)
244
+ x = self.norm(x)
245
+ logits = self.lm_head(x)
246
+ confidence = torch.sigmoid(self.verify(x)).squeeze(-1)
247
+ loss = None
248
+ if targets is not None:
249
+ loss = F.cross_entropy(logits.view(-1, self.vocab_size), targets.view(-1), ignore_index=0)
250
+ return {'logits': logits, 'confidence': confidence, 'loss': loss}
251
+
252
+ @torch.no_grad()
253
+ def generate(self, tokenizer, prompt: str, max_tokens: int = 512,
254
+ temperature: float = 0.3, top_k: int = 5, top_p: float = 0.9) -> Dict:
255
+ self.eval()
256
+ device = next(self.parameters()).device
257
+ toks = torch.tensor([tokenizer.encode(prompt)], dtype=torch.long, device=device)
258
+ generated_ids = []
259
+ confs = []
260
+ for _ in range(min(max_tokens, 2048 - toks.shape[1])):
261
+ out = self.forward(toks)
262
+ logits = out['logits'][0, -1, :] / max(temperature, 0.01)
263
+ if top_k > 0:
264
+ vals, _ = torch.topk(logits, min(top_k, logits.size(-1)))
265
+ logits[logits < vals[-1]] = float('-inf')
266
+ if top_p < 1.0:
267
+ sorted_lg, sorted_idx = torch.sort(logits, descending=True)
268
+ cum = torch.cumsum(F.softmax(sorted_lg, dim=-1), dim=-1)
269
+ rm = cum > top_p
270
+ rm[1:] = rm[:-1].clone()
271
+ rm[0] = False
272
+ logits[sorted_idx[rm]] = float('-inf')
273
+ logits = torch.nan_to_num(logits, nan=-100.0, posinf=100.0, neginf=-100.0)
274
+ logits[0] = float('-inf')
275
+ for rid in range(300, logits.size(-1)):
276
+ logits[rid] = float('-inf')
277
+ if (logits > float(-1e9)).sum() == 0:
278
+ logits[tokenizer.eos_id] = 0.0
279
+ nxt = logits.argmax().unsqueeze(0)
280
+ confs.append(out['confidence'][0, -1].item())
281
+ if nxt.item() == tokenizer.eos_id:
282
+ break
283
+ generated_ids.append(nxt.item())
284
+ toks = torch.cat([toks, nxt.unsqueeze(0)], dim=1)
285
+ generated = tokenizer.decode(generated_ids, skip_special=True)
286
+ avg_conf = sum(confs) / max(len(confs), 1)
287
+ return {'generated': generated, 'confidence': avg_conf, 'tokens': len(generated_ids)}
288
+
289
+ def param_count(self) -> int:
290
+ return sum(p.numel() for p in self.parameters())
291
+
292
+ # =============================================================================
293
+ # 6. TRAINER
294
+ # =============================================================================
295
+ class Trainer:
296
+ def __init__(self, model: FSIEchoModel, tokenizer: CodeTokenizer, lr: float = 3e-4):
297
+ self.model = model
298
+ self.tokenizer = tokenizer
299
+ self.optimizer = torch.optim.AdamW(model.parameters(), lr=lr, betas=(0.9, 0.95), weight_decay=0.1)
300
+ self.scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(self.optimizer, T_max=5000, eta_min=1e-5)
301
+
302
+ def step(self, texts: List[str], batch_size: int = 2, device: str = 'cpu') -> float:
303
+ self.model.train()
304
+ batch = random.sample(texts, min(batch_size, len(texts)))
305
+ encoded = []
306
+ max_len = 0
307
+ for t in batch:
308
+ toks = self.tokenizer.encode(t)
309
+ if 5 < len(toks) <= 2048:
310
+ encoded.append(toks)
311
+ max_len = max(max_len, len(toks))
312
+ if not encoded:
313
+ return 0.0
314
+ padded = torch.zeros(len(encoded), max_len, dtype=torch.long)
315
+ targets = torch.full((len(encoded), max_len), 0, dtype=torch.long)
316
+ for i, toks in enumerate(encoded):
317
+ padded[i, :len(toks)] = torch.tensor(toks)
318
+ targets[i, :len(toks)-1] = torch.tensor(toks[1:])
319
+ targets[i, len(toks)-1] = 1
320
+ padded, targets = padded.to(device), targets.to(device)
321
+ out = self.model(padded, targets)
322
+ self.optimizer.zero_grad()
323
+ out['loss'].backward()
324
+ torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0)
325
+ self.optimizer.step()
326
+ self.scheduler.step()
327
+ return out['loss'].item()
328
+
329
+ def save(self, path: str):
330
+ os.makedirs(os.path.dirname(path), exist_ok=True)
331
+ torch.save({
332
+ 'model': self.model.state_dict(),
333
+ 'optimizer': self.optimizer.state_dict(),
334
+ 'config': {'vocab_size': self.model.vocab_size, 'd_model': self.model.d_model,
335
+ 'n_swarm': len(self.model.swarm_layers), 'n_assembly': len(self.model.assembly_layers),
336
+ 'n_nanobots': self.model.swarm_layers[0].n_nanobots if self.model.swarm_layers else 512},
337
+ }, path)
338
+
339
+ @classmethod
340
+ def load(cls, path: str, device: str = 'cpu') -> 'Trainer':
341
+ data = torch.load(path, map_location=device, weights_only=True)
342
+ cfg = data.get('config', {})
343
+ model = FSIEchoModel(
344
+ vocab_size=cfg.get('vocab_size', 4096), d_model=cfg.get('d_model', 192),
345
+ n_swarm_layers=cfg.get('n_swarm', 3), n_assembly_layers=cfg.get('n_assembly', 3),
346
+ n_nanobots=cfg.get('n_nanobots', 512),
347
+ )
348
+ model.load_state_dict(data['model'])
349
+ tokenizer = CodeTokenizer(vocab_size=cfg.get('vocab_size', 4096))
350
+ t = cls(model, tokenizer)
351
+ if 'optimizer' in data:
352
+ t.optimizer.load_state_dict(data['optimizer'])
353
+ model.to(device)
354
+ return t
355
+
356
+ # =============================================================================
357
+ # 7. CLOSED-LOOP DEBUG
358
+ # =============================================================================
359
+ class CodeVerifier:
360
+ @staticmethod
361
+ def check_syntax(code: str) -> Tuple[bool, str]:
362
+ try:
363
+ compile(code, '<debug>', 'exec')
364
+ return True, ""
365
+ except SyntaxError as e:
366
+ return False, f"Line {e.lineno}: {e.msg}"
367
+ @staticmethod
368
+ def find_issues(code: str) -> List[str]:
369
+ issues = []
370
+ for i, line in enumerate(code.split('\n'), 1):
371
+ s = line.strip()
372
+ if s == 'except:':
373
+ issues.append(f"L{i}: Bare except — specify exception")
374
+ return issues
375
+ def verify(self, code: str) -> Dict:
376
+ ok, err = self.check_syntax(code)
377
+ issues = self.find_issues(code)
378
+ return {'valid': ok and not issues, 'syntax_ok': ok, 'error': err, 'issues': issues, 'code': code}
379
+
380
+ class ClosedLoopDebugger:
381
+ def __init__(self, model: FSIEchoModel, tokenizer: CodeTokenizer, max_iters: int = 3):
382
+ self.model = model
383
+ self.tokenizer = tokenizer
384
+ self.max_iters = max_iters
385
+ self.verifier = CodeVerifier()
386
+ def debug(self, code: str, requirement: str = "", max_iterations: int = None) -> Dict:
387
+ iters = max_iterations or self.max_iters
388
+ result = self._extract_code(code)
389
+ if not result:
390
+ return {'code': code, 'error': 'Could not parse code', 'iterations': 0, 'confidence': 0.0}
391
+ buggy = result
392
+ prompt = f"Fix this code:\n```python\n{buggy}\n```\nFixed:\n```python\n"
393
+ best_code, best_v = buggy, self.verifier.verify(buggy)
394
+ for i in range(iters):
395
+ gen = self.model.generate(self.tokenizer, prompt, max_tokens=256, temperature=0.3, top_k=5)
396
+ fixed = self._extract_code(gen['generated'])
397
+ if not fixed:
398
+ prompt += "\n```python\n"
399
+ continue
400
+ v = self.verifier.verify(fixed)
401
+ if v['valid']:
402
+ return {'code': fixed, 'verification': v, 'iterations': i+1, 'confidence': gen['confidence']}
403
+ if len(v['issues']) < len(best_v['issues']):
404
+ best_code, best_v = fixed, v
405
+ if v['issues']:
406
+ prompt += f"\nIssues: {'; '.join(v['issues'])}\nFixed:\n```python\n"
407
+ elif not v['syntax_ok']:
408
+ prompt += f"\n{v['error']}\nFixed:\n```python\n"
409
+ else:
410
+ break
411
+ return {'code': best_code, 'verification': best_v, 'iterations': iters, 'confidence': 0.0}
412
+ def _extract_code(self, text: str) -> str:
413
+ m = re.search(r'```(?:python)?\n(.*?)```', text, re.DOTALL)
414
+ if m: return m.group(1).strip()
415
+ lines = text.split('\n')
416
+ code = []
417
+ in_code = False
418
+ for line in lines:
419
+ if line.startswith('```'): in_code = not in_code; continue
420
+ if in_code: code.append(line)
421
+ return '\n'.join(code).strip() if code else ''
422
+
423
+ # =============================================================================
424
+ # 8. GGUF EXPORT
425
+ # =============================================================================
426
+ def export_gguf(model: FSIEchoModel, tokenizer: CodeTokenizer, path: str):
427
+ sd = model.state_dict()
428
+ keys = sorted(sd.keys())
429
+ meta = {
430
+ 'general.name': 'FSI_ECHO', 'general.architecture': 'fsi_echo',
431
+ 'general.description': 'Morphing Code Swarm',
432
+ 'general.file_type': 0, 'general.vocab_size': model.vocab_size,
433
+ 'general.context_length': 2048, 'general.parameter_count': model.param_count(),
434
+ 'fsi_echo.block_count': len(model.swarm_layers) + len(model.assembly_layers),
435
+ 'fsi_echo.embedding_length': model.d_model,
436
+ 'fsi_echo.feed_forward_length': model.d_model * 2,
437
+ 'fsi_echo.attention.head_count': 4,
438
+ 'fsi_echo.nanobot_count': model.swarm_layers[0].n_nanobots if model.swarm_layers else 512,
439
+ }
440
+ with open(path, 'wb') as f:
441
+ f.write(b'GGUF' + struct.pack('<I', 3) + struct.pack('<Q', len(keys)) + struct.pack('<Q', len(meta)))
442
+ for k, v in meta.items():
443
+ f.write(struct.pack('<I', len(k)) + k.encode())
444
+ if isinstance(v, str):
445
+ f.write(struct.pack('<I', 8) + struct.pack('<Q', len(v)) + v.encode())
446
+ elif isinstance(v, int):
447
+ f.write(struct.pack('<I', 4) + struct.pack('<I', v))
448
+ elif isinstance(v, float):
449
+ f.write(struct.pack('<I', 6) + struct.pack('<f', v))
450
+ offset = 0
451
+ for name in keys:
452
+ nb = len(name)
453
+ f.write(struct.pack('<Q', nb) + name.encode() +
454
+ struct.pack('<I', len(sd[name].shape)) +
455
+ b''.join(struct.pack('<Q', d) for d in sd[name].shape) +
456
+ struct.pack('<I', 0) + struct.pack('<Q', offset))
457
+ offset += sd[name].numel() * 4
458
+ for name in keys:
459
+ f.write(sd[name].float().numpy().tobytes())
460
+
461
+ # =============================================================================
462
+ # 9. MAIN
463
+ # =============================================================================
464
+ if __name__ == '__main__':
465
+ import argparse
466
+ p = argparse.ArgumentParser(description='FSI_ECHO')
467
+ p.add_argument('--train', action='store_true')
468
+ p.add_argument('--gen', type=str, help='Generate from prompt')
469
+ p.add_argument('--gguf', type=str, help='Export to GGUF')
470
+ p.add_argument('--load', type=str, help='Load checkpoint')
471
+ p.add_argument('--steps', type=int, default=5000)
472
+ args = p.parse_args()
473
+ device = 'cpu'
474
+ if args.load and os.path.exists(args.load):
475
+ trainer = Trainer.load(args.load, device)
476
+ else:
477
+ model = FSIEchoModel()
478
+ model.to(device)
479
+ tok = CodeTokenizer()
480
+ print(f"New model: {model.param_count():,} params")
481
+ trainer = Trainer(model, tok)
482
+ if args.gen:
483
+ r = trainer.model.generate(trainer.tokenizer, args.gen, max_tokens=256)
484
+ print(r['generated'])
485
+ if args.gguf:
486
+ export_gguf(trainer.model, trainer.tokenizer, args.gguf)
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ torch>=2.0.0
2
+ gradio>=5.0.0
3
+ requests>=2.0.0