Rixf123 commited on
Commit
8974101
Β·
verified Β·
1 Parent(s): b88d08f

Create App.py

Browse files
Files changed (1) hide show
  1. App.py +521 -0
App.py ADDED
@@ -0,0 +1,521 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CodeMind AI β€” Pure API Server (No UI)
2
+ # Call from any app using your API Key + URL!
3
+
4
+ import os, re, ast, json, time, random, hashlib
5
+ import warnings; warnings.filterwarnings("ignore")
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+ from transformers import GPT2TokenizerFast
10
+ from dataclasses import dataclass
11
+ from typing import List, Dict, Any
12
+ from collections import deque
13
+ from fastapi import FastAPI, HTTPException, Depends
14
+ from fastapi.middleware.cors import CORSMiddleware
15
+ from fastapi.responses import JSONResponse
16
+ from fastapi.security import APIKeyHeader
17
+ from pydantic import BaseModel
18
+ import uvicorn
19
+
20
+ # ══════════════════════════════════════════════════
21
+ # API KEY β€” set in HF Space β†’ Settings β†’ Secrets
22
+ # Name: CODEMIND_API_KEY
23
+ # ══════════════════════════════════════════════════
24
+ API_KEY = os.environ.get("CODEMIND_API_KEY", "codemind-change-me")
25
+ print("βœ… API Key loaded!" if "change-me" not in API_KEY
26
+ else "⚠️ Set CODEMIND_API_KEY in HF Secrets!")
27
+
28
+ device = "cuda" if torch.cuda.is_available() else "cpu"
29
+ print(f"πŸš€ Device: {device.upper()}")
30
+
31
+ @dataclass
32
+ class Config:
33
+ vocab_size: int = 50304
34
+ n_embd: int = 512
35
+ n_head: int = 8
36
+ n_kv_head: int = 4
37
+ n_layer: int = 8
38
+ block_size: int = 512
39
+ temperature: float = 0.8
40
+ top_k: int = 50
41
+ top_p: float = 0.95
42
+ rep_penalty: float = 1.1
43
+ max_new_tokens: int = 256
44
+
45
+ cfg = Config()
46
+ random.seed(42); torch.manual_seed(42)
47
+
48
+ # ── Tokenizer ─────────────────────────────────────
49
+ tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
50
+ tokenizer.pad_token = tokenizer.eos_token
51
+ _SPECIAL = [
52
+ '<|generate|>','<|complete|>','<|explain|>','<|bugfix|>',
53
+ '<|optimize|>','<|translate|>','<|docstring|>','<|unittest|>',
54
+ '<|review|>','<|refactor|>','<|security|>','<|complexity|>',
55
+ '<|async|>','<|python|>','<|javascript|>','<|java|>',
56
+ '<|cpp|>','<|typescript|>','<|go|>','<|rust|>',
57
+ ]
58
+ tokenizer.add_special_tokens({'additional_special_tokens': _SPECIAL})
59
+ cfg.vocab_size = len(tokenizer)
60
+ print(f"βœ… Vocab: {cfg.vocab_size:,}")
61
+
62
+ # ── Model ─────────────────────────────────────────
63
+ class RMSNorm(nn.Module):
64
+ def __init__(self,d,eps=1e-8):
65
+ super().__init__()
66
+ self.scale=nn.Parameter(torch.ones(d)); self.eps=eps
67
+ def forward(self,x):
68
+ return self.scale*x/(x.pow(2).mean(-1,keepdim=True).add(self.eps).sqrt())
69
+
70
+ class RotaryEmbedding(nn.Module):
71
+ def __init__(self,dim):
72
+ super().__init__()
73
+ self.register_buffer("inv_freq",1.0/(10000**(torch.arange(0,dim,2).float()/dim)))
74
+ def forward(self,T,dev):
75
+ t=torch.arange(T,device=dev).float()
76
+ f=torch.outer(t,self.inv_freq)
77
+ e=torch.cat([f,f],dim=-1)
78
+ return e.cos(),e.sin()
79
+
80
+ def _rot(x):
81
+ a,b=x.chunk(2,dim=-1); return torch.cat([-b,a],dim=-1)
82
+
83
+ def apply_rope(q,k,cos,sin):
84
+ c,s=cos[None,None],sin[None,None]
85
+ return (q*c)+(_rot(q)*s),(k*c)+(_rot(k)*s)
86
+
87
+ class GQA(nn.Module):
88
+ def __init__(self,cfg):
89
+ super().__init__()
90
+ self.nh=cfg.n_head; self.nkv=cfg.n_kv_head; self.hd=cfg.n_embd//cfg.n_head
91
+ self.q=nn.Linear(cfg.n_embd,cfg.n_embd,bias=False)
92
+ self.k=nn.Linear(cfg.n_embd,self.nkv*self.hd,bias=False)
93
+ self.v=nn.Linear(cfg.n_embd,self.nkv*self.hd,bias=False)
94
+ self.o=nn.Linear(cfg.n_embd,cfg.n_embd,bias=False)
95
+ self.rope=RotaryEmbedding(self.hd)
96
+ def forward(self,x,cache=None):
97
+ B,T,C=x.shape; cos,sin=self.rope(T,x.device)
98
+ q=self.q(x).view(B,T,self.nh,self.hd).transpose(1,2)
99
+ k=self.k(x).view(B,T,self.nkv,self.hd).transpose(1,2)
100
+ v=self.v(x).view(B,T,self.nkv,self.hd).transpose(1,2)
101
+ q,k=apply_rope(q,k,cos,sin)
102
+ if cache is not None:
103
+ k=torch.cat([cache[0],k],dim=2); v=torch.cat([cache[1],v],dim=2)
104
+ nc=(k.detach(),v.detach())
105
+ k=k.repeat_interleave(self.nh//self.nkv,dim=1)
106
+ v=v.repeat_interleave(self.nh//self.nkv,dim=1)
107
+ out=F.scaled_dot_product_attention(q,k,v,is_causal=True,dropout_p=0.0)
108
+ return self.o(out.transpose(1,2).contiguous().view(B,T,C)),nc
109
+
110
+ class SwiGLU(nn.Module):
111
+ def __init__(self,cfg):
112
+ super().__init__()
113
+ h=int(cfg.n_embd*8/3)
114
+ self.w1=nn.Linear(cfg.n_embd,h,bias=False)
115
+ self.w2=nn.Linear(h,cfg.n_embd,bias=False)
116
+ self.w3=nn.Linear(cfg.n_embd,h,bias=False)
117
+ def forward(self,x): return self.w2(F.silu(self.w1(x))*self.w3(x))
118
+
119
+ class Block(nn.Module):
120
+ def __init__(self,cfg):
121
+ super().__init__()
122
+ self.n1=RMSNorm(cfg.n_embd); self.n2=RMSNorm(cfg.n_embd)
123
+ self.attn=GQA(cfg); self.mlp=SwiGLU(cfg)
124
+ def forward(self,x,cache=None):
125
+ a,c=self.attn(self.n1(x),cache); x=x+a; x=x+self.mlp(self.n2(x)); return x,c
126
+
127
+ class CodeMindModel(nn.Module):
128
+ def __init__(self,cfg):
129
+ super().__init__()
130
+ self.emb=nn.Embedding(cfg.vocab_size,cfg.n_embd)
131
+ self.blocks=nn.ModuleList([Block(cfg) for _ in range(cfg.n_layer)])
132
+ self.norm=RMSNorm(cfg.n_embd)
133
+ self.head=nn.Linear(cfg.n_embd,cfg.vocab_size,bias=False)
134
+ self.emb.weight=self.head.weight
135
+ self.apply(lambda m:nn.init.normal_(m.weight,0,0.02) if isinstance(m,(nn.Linear,nn.Embedding)) else None)
136
+ print(f"🧠 CodeMind: {sum(p.numel() for p in self.parameters())/1e6:.1f}M params")
137
+
138
+ def forward(self,idx,targets=None,caches=None):
139
+ x=self.emb(idx); nc=[]
140
+ for i,b in enumerate(self.blocks):
141
+ x,c=b(x,caches[i] if caches else None); nc.append(c)
142
+ logits=self.head(self.norm(x))
143
+ loss=(F.cross_entropy(logits.view(-1,logits.size(-1)),targets.view(-1),ignore_index=-1) if targets is not None else None)
144
+ return logits,loss,nc
145
+
146
+ @torch.no_grad()
147
+ def generate(self,ids,max_t=256):
148
+ self.eval(); caches=None; start=ids.shape[1]
149
+ for _ in range(max_t):
150
+ inp=ids[:,-cfg.block_size:]
151
+ logits,_,caches=self(inp,caches=caches)
152
+ logits=logits[:,-1,:].float()/cfg.temperature
153
+ v,_=torch.topk(logits,min(cfg.top_k,logits.size(-1)))
154
+ logits[logits<v[:,[-1]]]=float('-inf')
155
+ probs=F.softmax(logits,dim=-1)
156
+ sp,si=torch.sort(probs,descending=True)
157
+ cp=sp.cumsum(-1); sp[cp-sp>cfg.top_p]=0.0
158
+ probs=torch.zeros_like(probs).scatter_(1,si,sp)
159
+ probs/=probs.sum(-1,keepdim=True).clamp(1e-8)
160
+ for tid in set(ids[0,-20:].tolist()):
161
+ if probs[0,tid]>0: probs[0,tid]/=cfg.rep_penalty
162
+ probs/=probs.sum(-1,keepdim=True).clamp(1e-8)
163
+ nxt=torch.multinomial(probs,1)
164
+ if nxt.item()==tokenizer.eos_token_id: break
165
+ ids=torch.cat([ids,nxt],dim=1)
166
+ return tokenizer.decode(ids[0,start:].tolist(),skip_special_tokens=True).strip()
167
+
168
+ # ── Memory ────────────────────────────────────────
169
+ class Memory:
170
+ def __init__(self): self.cache={}; self.history=[]
171
+ def get(self,c,k): return self.cache.get(f"{hashlib.md5(c.encode()).hexdigest()}_{k}")
172
+ def set(self,c,k,v): self.cache[f"{hashlib.md5(c.encode()).hexdigest()}_{k}"]=v
173
+ def stats(self): return {"requests":len(self.history),"cache":len(self.cache)}
174
+
175
+ # ── 20 Functions ──────────────────────────────────
176
+ class Functions:
177
+ def __init__(self,model,mem): self.model=model; self.mem=mem
178
+
179
+ def _gen(self,prompt,max_t=128):
180
+ ids=tokenizer.encode(prompt,return_tensors="pt").to(device)
181
+ return self.model.generate(ids[:,-cfg.block_size:],max_t)
182
+
183
+ def generate_code(self,prompt,lang="python",max_t=256):
184
+ lt=f"<|{lang}|>" if f"<|{lang}|>" in _SPECIAL else ""
185
+ out=self._gen(f"{lt}<|generate|># Task: {prompt}\n",max_t)
186
+ imp=self.suggest_imports(out)
187
+ return ("\n".join(imp)+"\n\n"+out) if imp else out
188
+
189
+ def complete_code(self,partial,max_t=128):
190
+ return self._gen(f"<|complete|>\n{partial}",max_t)
191
+
192
+ def explain_code(self,code):
193
+ c=self.mem.get(code,"explain")
194
+ if c: return c
195
+ r=self._gen(f"<|explain|>\n{code[:400]}\n# Explanation:",200)
196
+ self.mem.set(code,"explain",r); return r
197
+
198
+ def detect_bugs(self,code):
199
+ bugs=[]
200
+ try: ast.parse(code); ok=True
201
+ except SyntaxError as e: ok=False; bugs.append({"type":"SyntaxError","line":e.lineno,"msg":str(e)})
202
+ rules=[(r'== None',"StyleWarning","Use 'is None'"),(r'!= None',"StyleWarning","Use 'is not None'"),
203
+ (r'except:\s*$',"BestPractice","Bare except"),(r'print\s*\(',"DebugCode","Debug print"),
204
+ (r'TODO|FIXME',"Incomplete","Unresolved TODO")]
205
+ for i,line in enumerate(code.split('\n'),1):
206
+ for pat,kind,msg in rules:
207
+ if re.search(pat,line): bugs.append({"type":kind,"line":i,"msg":msg})
208
+ return {"syntax_ok":ok,"bugs":bugs,"total":len(bugs)}
209
+
210
+ def optimize_code(self,code): return self._gen(f"<|optimize|>\n{code[:400]}\n# Optimized:",256)
211
+ def translate_code(self,code,target="javascript"): return self._gen(f"<|translate|>\n# Python:\n{code[:400]}\n# {target}:",300)
212
+ def generate_docs(self,code): return self._gen(f"<|docstring|>\n{code[:400]}\n# Documented:",300)
213
+ def generate_tests(self,code,fw="pytest"): return self._gen(f"<|unittest|>\n{code[:350]}\n# {fw} tests:",350)
214
+
215
+ def review_code(self,code):
216
+ lines=[l for l in code.split('\n') if l.strip()]
217
+ score,iss=100,[]
218
+ if '"""' not in code: score-=20; iss.append("❌ No docstrings")
219
+ if '->' not in code: score-=10; iss.append("⚠️ No type hints")
220
+ if not any(l.strip().startswith('#') for l in code.split('\n')): score-=10; iss.append("⚠️ No comments")
221
+ if len(lines)>50: score-=15; iss.append("⚠️ Too long")
222
+ g="A" if score>=90 else "B" if score>=75 else "C" if score>=60 else "D"
223
+ return {"score":max(score,0),"grade":g,"issues":iss,"loc":len(lines)}
224
+
225
+ def analyze_complexity(self,code):
226
+ md=0
227
+ for line in code.split('\n'):
228
+ s=line.lstrip()
229
+ if s.startswith(('for ','while ')): md=max(md,(len(line)-len(s))//4+1)
230
+ tm={0:"O(1)",1:"O(n)",2:"O(nΒ²)",3:"O(nΒ³)"}.get(md,f"O(n^{md})")
231
+ sp="O(n)" if re.search(r'\bappend\b|\[\]',code) else "O(1)"
232
+ return {"time":tm,"space":sp,"loop_depth":md}
233
+
234
+ def suggest_imports(self,code):
235
+ MAP={r'\bpd\.': "import pandas as pd",r'\bnp\.': "import numpy as np",
236
+ r'\bplt\.': "import matplotlib.pyplot as plt",r'\btorch\b': "import torch",
237
+ r'\bos\b': "import os",r'\bre\b': "import re",r'\bmath\b': "import math",
238
+ r'\bjson\b': "import json",r'\brandom\b': "import random",r'\bsys\b': "import sys"}
239
+ ex=set(re.findall(r'(?:import|from)\s+(\w+)',code))
240
+ return [s for p,s in MAP.items() if re.search(p,code) and s.split()[-1].split('.')[0] not in ex]
241
+
242
+ def format_code(self,code):
243
+ lines=[]
244
+ for line in code.split('\n'):
245
+ line=re.sub(r'(?<![=!<>])=(?!=)',' = ',line); line=re.sub(r'(?<! ),',', ',line); lines.append(line.rstrip())
246
+ return '\n'.join(lines).rstrip()+'\n'
247
+
248
+ def summarize_code(self,code):
249
+ fns=re.findall(r'def (\w+)',code); cls=re.findall(r'class (\w+)',code)
250
+ lns=[l for l in code.split('\n') if l.strip()]; parts=[]
251
+ if cls: parts.append(f"Classes: {', '.join(cls)}")
252
+ if fns: parts.append(f"Functions: {', '.join(fns)}")
253
+ parts.append(f"{len(lns)} lines"); return " | ".join(parts)
254
+
255
+ def detect_dead_code(self,code):
256
+ dead=[]
257
+ try: tree=ast.parse(code)
258
+ except: return [{"type":"ParseError","msg":"Cannot parse"}]
259
+ assigned,used=set(),set()
260
+ for n in ast.walk(tree):
261
+ if isinstance(n,ast.Assign):
262
+ for t in n.targets:
263
+ if isinstance(t,ast.Name): assigned.add(t.id)
264
+ elif isinstance(n,ast.Name) and not isinstance(n.ctx,ast.Store): used.add(n.id)
265
+ for v in (assigned-used-{'self','_'}):
266
+ dead.append({"type":"UnusedVariable","name":v,"msg":f"'{v}' never used"})
267
+ return dead
268
+
269
+ def scan_security(self,code):
270
+ checks=[(r'\beval\s*\(', "CRITICAL","eval() dangerous"),(r'\bexec\s*\(', "CRITICAL","exec() dangerous"),
271
+ (r'os\.system\s*\(', "HIGH","os.system risk"),(r'pickle\.loads?\s*\(', "HIGH","Unsafe pickle"),
272
+ (r'shell\s*=\s*True', "HIGH","shell=True injection"),(r'password\s*=\s*["\']', "HIGH","Hardcoded password"),
273
+ (r'api_key\s*=\s*["\']', "HIGH","Hardcoded API key"),(r'\bmd5\b', "MEDIUM","MD5 broken"),(r'http://', "LOW","Use HTTPS")]
274
+ vulns=[]
275
+ for i,line in enumerate(code.split('\n'),1):
276
+ for pat,sev,msg in checks:
277
+ if re.search(pat,line,re.I): vulns.append({"line":i,"severity":sev,"msg":msg})
278
+ order={"CRITICAL":0,"HIGH":1,"MEDIUM":2,"LOW":3}
279
+ risk=("CRITICAL" if any(v["severity"]=="CRITICAL" for v in vulns)
280
+ else "HIGH" if any(v["severity"]=="HIGH" for v in vulns)
281
+ else "MEDIUM" if vulns else "SAFE")
282
+ return sorted(vulns,key=lambda x:order.get(x["severity"],9)),risk
283
+
284
+ def generate_type_hints(self,code):
285
+ lines,out=code.split('\n'),[]
286
+ for line in lines:
287
+ m=re.match(r'(\s*def \w+\()(.*)(\):.*)',line)
288
+ if m and '->' not in line:
289
+ typed=[]
290
+ for p in m.group(2).split(','):
291
+ p=p.strip()
292
+ if not p or p=='self': typed.append(p)
293
+ elif any(k in p for k in ('name','text','msg','key')): typed.append(f"{p}: str")
294
+ elif any(k in p for k in ('num','count','n','i')): typed.append(f"{p}: int")
295
+ else: typed.append(f"{p}: Any")
296
+ out.append(f"{m.group(1)}{', '.join(typed)}) -> Any:")
297
+ else: out.append(line)
298
+ return '\n'.join(out)
299
+
300
+ def refactor_code(self,code): return self._gen(f"<|refactor|>\n{code[:400]}\n# Clean:",300)
301
+
302
+ def extract_functions(self,code):
303
+ try: tree=ast.parse(code)
304
+ except Exception as e: return [{"error":str(e)}]
305
+ return [{"name":n.name,"args":[a.arg for a in n.args.args],"line":n.lineno}
306
+ for n in ast.walk(tree) if isinstance(n,ast.FunctionDef)]
307
+
308
+ def convert_to_async(self,code):
309
+ out=re.sub(r'\bdef (\w+)\s*\(',r'async def \1(',code)
310
+ out=re.sub(r'\btime\.sleep\b','await asyncio.sleep',out)
311
+ return "import asyncio\nimport aiohttp\n\n"+out
312
+
313
+ def estimate_cost(self,n_params=70_000_000,n_tokens=5_000_000,gpu="T4"):
314
+ flops=6*n_params*n_tokens; tp={"T4":65e12,"A100":312e12}.get(gpu,65e12)
315
+ pr={"T4":0.35,"A100":3.00}.get(gpu,0.35); h=flops/tp/3600
316
+ return {"gpu":gpu,"est_hours":round(h,2),"est_cost_usd":round(h*pr,2)}
317
+
318
+
319
+ # ── 17 Agents ─────────────────────────────────────
320
+ class Agent:
321
+ def __init__(self,name,fn): self.name=name; self.fn=fn
322
+ def run(self,*a,**k): raise NotImplementedError
323
+ def ok(self,d): return {"agent":self.name,"status":"ok","result":d}
324
+ def err(self,m): return {"agent":self.name,"status":"error","msg":m}
325
+
326
+ class GenAgent(Agent):
327
+ def __init__(self,fn): super().__init__("CodeGenerator",fn)
328
+ def run(self,prompt,lang="python"): return self.ok({"code":self.fn.generate_code(prompt,lang),"lang":lang})
329
+
330
+ class BugAgent(Agent):
331
+ def __init__(self,fn): super().__init__("BugDetector",fn)
332
+ def run(self,code):
333
+ b=self.fn.detect_bugs(code); d=self.fn.detect_dead_code(code)
334
+ return self.ok({"bugs":b,"dead_code":d,"total":b["total"]+len(d),"healthy":b["total"]+len(d)==0})
335
+
336
+ class OptAgent(Agent):
337
+ def __init__(self,fn): super().__init__("Optimizer",fn)
338
+ def run(self,code): return self.ok({"optimized":self.fn.format_code(self.fn.optimize_code(code)),"complexity":self.fn.analyze_complexity(code)})
339
+
340
+ class DocAgent(Agent):
341
+ def __init__(self,fn): super().__init__("Documentation",fn)
342
+ def run(self,code): return self.ok({"docstrings":self.fn.generate_docs(code),"summary":self.fn.summarize_code(code),"functions":self.fn.extract_functions(code)})
343
+
344
+ class TestAgent(Agent):
345
+ def __init__(self,fn): super().__init__("TestGenerator",fn)
346
+ def run(self,code,fw="pytest"): return self.ok({"tests":self.fn.generate_tests(code,fw),"framework":fw})
347
+
348
+ class SecAgent(Agent):
349
+ def __init__(self,fn): super().__init__("SecurityScanner",fn)
350
+ def run(self,code):
351
+ v,r=self.fn.scan_security(code); return self.ok({"vulnerabilities":v,"risk_level":r,"is_safe":not v})
352
+
353
+ class RefAgent(Agent):
354
+ def __init__(self,fn): super().__init__("Refactor",fn)
355
+ def run(self,code):
356
+ r=self.fn.refactor_code(code)
357
+ return self.ok({"refactored":r,"score_before":self.fn.review_code(code)["score"],"score_after":self.fn.review_code(r)["score"]})
358
+
359
+ class TrAgent(Agent):
360
+ SUPPORTED=["javascript","java","cpp","typescript","go","rust","csharp"]
361
+ def __init__(self,fn): super().__init__("Translator",fn)
362
+ def run(self,code,target="javascript"):
363
+ if target not in self.SUPPORTED: return self.err(f"Choose: {self.SUPPORTED}")
364
+ return self.ok({"translated":self.fn.translate_code(code,target),"target":target})
365
+
366
+ class RevAgent(Agent):
367
+ def __init__(self,fn): super().__init__("CodeReviewer",fn)
368
+ def run(self,code):
369
+ r=self.fn.review_code(code); v,_=self.fn.scan_security(code); d=self.fn.detect_dead_code(code)
370
+ s=max(r["score"]-len(v)*5-len(d)*2,0)
371
+ return self.ok({"score":s,"grade":r["grade"],"issues":r["issues"],"recommendation":"βœ… LGTM!" if s>=80 else "❌ Needs work"})
372
+
373
+ class CpxAgent(Agent):
374
+ def __init__(self,fn): super().__init__("ComplexityAnalyzer",fn)
375
+ def run(self,code): return self.ok(self.fn.analyze_complexity(code))
376
+
377
+ class ImpAgent(Agent):
378
+ def __init__(self,fn): super().__init__("ImportManager",fn)
379
+ def run(self,code): return self.ok({"suggested":self.fn.suggest_imports(code)})
380
+
381
+ class FmtAgent(Agent):
382
+ def __init__(self,fn): super().__init__("Formatter",fn)
383
+ def run(self,code): return self.ok({"formatted":self.fn.generate_type_hints(self.fn.format_code(code))})
384
+
385
+ class ExpAgent(Agent):
386
+ def __init__(self,fn): super().__init__("Explainer",fn)
387
+ def run(self,code,level="beginner"):
388
+ pre={"beginner":"Simply: ","expert":"Technical: "}.get(level,"")
389
+ return self.ok({"explanation":pre+self.fn.explain_code(code),"summary":self.fn.summarize_code(code)})
390
+
391
+ class DcdAgent(Agent):
392
+ def __init__(self,fn): super().__init__("DeadCodeDetector",fn)
393
+ def run(self,code): d=self.fn.detect_dead_code(code); return self.ok({"items":d,"total":len(d)})
394
+
395
+ class PerfAgent(Agent):
396
+ def __init__(self,fn): super().__init__("Profiler",fn)
397
+ def run(self,code):
398
+ sug=[]
399
+ if re.search(r'for .+ in .+:\n.*\.append\(',code,re.S): sug.append("Use list comprehension")
400
+ if re.search(r'for .* in range\(len\(',code): sug.append("Use enumerate()")
401
+ if 'global ' in code: sug.append("Remove global variables")
402
+ return self.ok({"suggestions":sug,"perf_score":max(100-len(sug)*15,10)})
403
+
404
+ class AsynAgent(Agent):
405
+ def __init__(self,fn): super().__init__("AsyncConverter",fn)
406
+ def run(self,code): return self.ok({"async_code":self.fn.convert_to_async(code)})
407
+
408
+ class Orchestrator:
409
+ def __init__(self,fn):
410
+ self.fn=fn; self.mem=fn.mem
411
+ self.agents={"generate":GenAgent(fn),"bugs":BugAgent(fn),"optimize":OptAgent(fn),
412
+ "docs":DocAgent(fn),"tests":TestAgent(fn),"security":SecAgent(fn),
413
+ "refactor":RefAgent(fn),"translate":TrAgent(fn),"review":RevAgent(fn),
414
+ "complexity":CpxAgent(fn),"imports":ImpAgent(fn),"format":FmtAgent(fn),
415
+ "explain":ExpAgent(fn),"deadcode":DcdAgent(fn),"performance":PerfAgent(fn),
416
+ "async":AsynAgent(fn)}
417
+ print(f"βœ… {len(self.agents)} agents ready")
418
+
419
+ def run(self,task,data,**kw):
420
+ a=self.agents.get(task)
421
+ if not a: return {"status":"error","msg":f"Unknown task: {task}"}
422
+ self.mem.history.append({"agent":task,"ts":time.time()})
423
+ return a.run(data,**kw)
424
+
425
+ def pipeline(self,code):
426
+ t0,res=time.time(),{}
427
+ for step in ["bugs","security","review","complexity","deadcode","performance"]:
428
+ try: res[step]=self.agents[step].run(code)
429
+ except Exception as e: res[step]={"error":str(e)}
430
+ res["_meta"]={"elapsed_s":round(time.time()-t0,2)}
431
+ return res
432
+
433
+ # ── Build ─────────────────────────────────────────
434
+ print("πŸ”¨ Building system...")
435
+ model=CodeMindModel(cfg).to(device)
436
+ memory=Memory()
437
+ functions=Functions(model,memory)
438
+ orc=Orchestrator(functions)
439
+
440
+ import glob as _g
441
+ _ckpts=sorted(_g.glob("/tmp/*.pt")+_g.glob("*.pt")+_g.glob("checkpoints/*.pt"))
442
+ if _ckpts:
443
+ try:
444
+ ck=torch.load(_ckpts[-1],map_location=device)
445
+ model.load_state_dict(ck["model"]); print(f"βœ… Checkpoint: {_ckpts[-1]}")
446
+ except Exception as e: print(f"⚠️ {e}")
447
+ print("βœ… System ready!\n")
448
+
449
+ # ══════════════════════════════════════════════════
450
+ # FASTAPI β€” Pure REST API
451
+ # ══════════════════════════════════════════════════
452
+ app=FastAPI(title="CodeMind AI API",description="17 Agents Β· 20 Functions Β· Use X-API-Key header",version="3.0",docs_url="/docs")
453
+ app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_methods=["*"],allow_headers=["*"])
454
+
455
+ _kh=APIKeyHeader(name="X-API-Key",auto_error=False)
456
+
457
+ async def require_key(key:str=Depends(_kh)):
458
+ if key!=API_KEY:
459
+ raise HTTPException(status_code=401,detail={
460
+ "error":"❌ Wrong or missing API Key",
461
+ "fix":"Add header: X-API-Key: YOUR_KEY",
462
+ "your_key":"Set CODEMIND_API_KEY in HF Space β†’ Settings β†’ Secrets"})
463
+ return key
464
+
465
+ class Req(BaseModel):
466
+ code:str=""; prompt:str=""; lang:str="python"; target:str="javascript"
467
+ framework:str="pytest"; level:str="beginner"; max_tokens:int=256
468
+
469
+ def _j(d): return JSONResponse(content=d if isinstance(d,dict) else {"result":d})
470
+
471
+ # ── Public ─────────────────────────────────────────
472
+ @app.get("/")
473
+ async def root():
474
+ return {"name":"CodeMind AI","version":"3.0","status":"βœ… Online","agents":len(orc.agents),
475
+ "auth":"Add X-API-Key: YOUR_KEY header to all /api/ requests",
476
+ "swagger_ui":"/docs","health":"/health"}
477
+
478
+ @app.get("/health")
479
+ async def health():
480
+ return {"status":"online","device":device,"agents":len(orc.agents),"memory":memory.stats(),"ts":time.time()}
481
+
482
+ # ── Protected ──────────────────────────────────────
483
+ @app.post("/api/generate", dependencies=[Depends(require_key)])
484
+ async def ep_gen(r:Req): return _j(orc.run("generate", r.prompt,lang=r.lang))
485
+ @app.post("/api/complete", dependencies=[Depends(require_key)])
486
+ async def ep_cmp(r:Req): return _j({"completion":functions.complete_code(r.code,r.max_tokens)})
487
+ @app.post("/api/bugs", dependencies=[Depends(require_key)])
488
+ async def ep_bug(r:Req): return _j(orc.run("bugs", r.code))
489
+ @app.post("/api/optimize", dependencies=[Depends(require_key)])
490
+ async def ep_opt(r:Req): return _j(orc.run("optimize", r.code))
491
+ @app.post("/api/docs", dependencies=[Depends(require_key)])
492
+ async def ep_doc(r:Req): return _j(orc.run("docs", r.code))
493
+ @app.post("/api/tests", dependencies=[Depends(require_key)])
494
+ async def ep_tst(r:Req): return _j(orc.run("tests", r.code,fw=r.framework))
495
+ @app.post("/api/security", dependencies=[Depends(require_key)])
496
+ async def ep_sec(r:Req): return _j(orc.run("security", r.code))
497
+ @app.post("/api/refactor", dependencies=[Depends(require_key)])
498
+ async def ep_ref(r:Req): return _j(orc.run("refactor", r.code))
499
+ @app.post("/api/translate", dependencies=[Depends(require_key)])
500
+ async def ep_tr(r:Req): return _j(orc.run("translate", r.code,target=r.target))
501
+ @app.post("/api/review", dependencies=[Depends(require_key)])
502
+ async def ep_rev(r:Req): return _j(orc.run("review", r.code))
503
+ @app.post("/api/complexity", dependencies=[Depends(require_key)])
504
+ async def ep_cpx(r:Req): return _j(orc.run("complexity", r.code))
505
+ @app.post("/api/imports", dependencies=[Depends(require_key)])
506
+ async def ep_imp(r:Req): return _j(orc.run("imports", r.code))
507
+ @app.post("/api/format", dependencies=[Depends(require_key)])
508
+ async def ep_fmt(r:Req): return _j(orc.run("format", r.code))
509
+ @app.post("/api/explain", dependencies=[Depends(require_key)])
510
+ async def ep_exp(r:Req): return _j(orc.run("explain", r.code,level=r.level))
511
+ @app.post("/api/deadcode", dependencies=[Depends(require_key)])
512
+ async def ep_dcd(r:Req): return _j(orc.run("deadcode", r.code))
513
+ @app.post("/api/performance", dependencies=[Depends(require_key)])
514
+ async def ep_prf(r:Req): return _j(orc.run("performance", r.code))
515
+ @app.post("/api/async", dependencies=[Depends(require_key)])
516
+ async def ep_asn(r:Req): return _j(orc.run("async", r.code))
517
+ @app.post("/api/pipeline", dependencies=[Depends(require_key)])
518
+ async def ep_pip(r:Req): return _j(orc.pipeline(r.code))
519
+
520
+ if __name__=="__main__":
521
+ uvicorn.run(app,host="0.0.0.0",port=7860,log_level="info")