Spaces:
Configuration error
Configuration error
| #!/usr/bin/env python3 | |
| # build_vortex.py – Génère tous les modules VORTEX v5.1 (23 innovations) | |
| import os | |
| from pathlib import Path | |
| BASE = Path("/app") | |
| def write_file(path, content): | |
| filepath = BASE / path | |
| filepath.parent.mkdir(parents=True, exist_ok=True) | |
| filepath.write_text(content, encoding='utf-8') | |
| print(f"✅ {filepath}") | |
| def build_all(): | |
| # ============================================================ | |
| # 1. agents/cognitive_engine.py | |
| # ============================================================ | |
| write_file("agents/cognitive_engine.py", ''' | |
| import requests, hashlib, json, logging, os | |
| log = logging.getLogger("vortex.llm") | |
| OLLAMA_URL = os.environ.get("OLLAMA_URL","http://localhost:11434") | |
| MODEL_MAIN = os.environ.get("LLM_MODEL","gemma4:e4b") | |
| MODEL_FAST = os.environ.get("LLM_MODEL_FAST","phi3:mini") | |
| TEMP_PROFILES = {"reasoning":0.1,"code_gen":0.35,"creative":0.65,"summary":0.15} | |
| class MultiLLMEngine: | |
| def __init__(self, memory=None): | |
| self.memory=memory; self._cache={}; self.tools={}; self.call_stats={"main":0,"fast":0,"errors":0} | |
| def register_tool(self, name, fn, desc, params): | |
| self.tools[name] = {"fn":fn,"schema":{"type":"function","function":{"name":name,"description":desc,"parameters":{"type":"object","properties":params,"required":list(params.keys())}}}} | |
| async def call(self, agent, system, user, max_tokens=512, temperature=0.3, use_cache=True, use_tools=False, fast=False, profile=None, **kw): | |
| if profile and profile in TEMP_PROFILES: temperature = TEMP_PROFILES[profile] | |
| model = MODEL_FAST if fast else MODEL_MAIN | |
| key = hashlib.sha256(f"{model}|{system}|{user}|{max_tokens}|{temperature}".encode()).hexdigest()[:20] | |
| if use_cache and key in self._cache: return type("R",(),{"content":self._cache[key],"tokens":0,"cached":True})() | |
| payload = {"model":model,"messages":[{"role":"system","content":system or "Tu es VORTEX."},{"role":"user","content":user}],"options":{"num_predict":max_tokens,"temperature":temperature},"stream":False} | |
| if use_tools and self.tools: payload["tools"] = [t["schema"] for t in self.tools.values()] | |
| try: | |
| resp = requests.post(f"{OLLAMA_URL}/api/chat", json=payload, timeout=180); msg = resp.json().get("message",{}) | |
| if msg.get("tool_calls"): | |
| for tc in msg["tool_calls"]: | |
| fname = tc["function"]["name"]; fargs = tc["function"]["arguments"] | |
| if isinstance(fargs, str): fargs = json.loads(fargs) | |
| if fname in self.tools: | |
| result = self.tools[fname]["fn"](**fargs) | |
| payload["messages"] += [msg, {"role":"tool","content":str(result)}] | |
| resp = requests.post(f"{OLLAMA_URL}/api/chat", json=payload, timeout=180); msg = resp.json().get("message",{}) | |
| content = msg.get("content","") | |
| if use_cache: self._cache[key] = content | |
| self.call_stats["fast" if fast else "main"] += 1 | |
| return type("R",(),{"content":content,"tokens":len(content.split()),"cached":False}) | |
| except Exception as e: | |
| self.call_stats["errors"] += 1; log.error(f"LLM error: {e}") | |
| return type("R",(),{"content":f"[Erreur LLM: {e}]","tokens":0,"cached":False}) | |
| ''') | |
| # ============================================================ | |
| # 2. core/cybershield.py | |
| # ============================================================ | |
| write_file("core/cybershield.py", ''' | |
| import ast, re, logging | |
| log = logging.getLogger("vortex.cybershield") | |
| CRITICAL_PATTERNS = { | |
| "shell_injection": (r"os\\.system|os\\.popen|subprocess\\.(call|Popen|run)", -40), | |
| "code_injection": (r"\\beval\\s*\\(|\\bexec\\s*\\(", -35), | |
| "pickle_exploit": (r"\\bpickle\\.(loads|load)\\b", -30), | |
| "import_hijack": (r"__import__\\s*\\(", -25), | |
| "file_overwrite": (r"open\\s*\\([^)]*['\\\"w]['\\\"\\)]", -15), | |
| "network_exfil": (r"(requests|urllib|aiohttp|socket)\\.", -10), | |
| } | |
| WARNING_PATTERNS = { | |
| "bare_except": (r"except\\s*:", -8), | |
| "infinite_loop": (r"while\\s+True(?!.*break)", -12), | |
| "global_var": (r"\\bglobal\\s+\\w+", -5), | |
| "hardcoded_secret": (r"(password|secret|token|api_key)\\s*=\\s*['\\\"][^'\\\"]{4,}", -15), | |
| } | |
| class CyberShield: | |
| def __init__(self, llm_engine=None): | |
| self.llm = llm_engine; self.scan_history = [] | |
| def scan_ast(self, code): | |
| issues = [] | |
| try: tree = ast.parse(code) | |
| except SyntaxError as e: return 0.0, [f"SyntaxError: {e}"] | |
| for node in ast.walk(tree): | |
| if isinstance(node, (ast.Import, ast.ImportFrom)): | |
| mods = [a.name.split(".")[0] for a in node.names] if isinstance(node, ast.Import) else ([node.module.split(".")[0]] if node.module else []) | |
| for m in mods: | |
| if m in {"os","subprocess","socket","pickle","ctypes","importlib","shutil","signal","resource","mmap","cffi"}: | |
| issues.append(f"Import risqué : {m}") | |
| if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id in {"eval","exec","compile","__import__"}: | |
| issues.append(f"Appel dangereux : {node.func.id}()") | |
| return len(issues), issues | |
| def scan_patterns(self, code): | |
| score, issues = 100.0, [] | |
| for name, (pattern, penalty) in {**CRITICAL_PATTERNS, **WARNING_PATTERNS}.items(): | |
| if re.search(pattern, code, re.MULTILINE): | |
| issues.append(f"{name.replace('_',' ').title()} (−{abs(penalty)}pts)") | |
| score += penalty | |
| return max(0, score), issues | |
| async def full_scan(self, code, context=""): | |
| ast_cnt, ast_issues = self.scan_ast(code) | |
| pat_score, pat_issues = self.scan_patterns(code) | |
| base_score = max(0, pat_score - min(ast_cnt * 20, 60)) | |
| llm_score, llm_note = base_score, "" | |
| if self.llm and base_score > 20: | |
| try: | |
| resp = await self.llm.call(None, "Expert sécurité Python.", f"Analyse sécurité : {code[:1000]}\\nScore actuel : {base_score}\\nRép : SCORE=XX NOTE=...", max_tokens=60, temperature=0.1, fast=True) | |
| m_s = re.search(r"SCORE=([0-9]+)", resp.content) | |
| if m_s: llm_score = (float(m_s.group(1)) + base_score) / 2 | |
| m_n = re.search(r"NOTE=(.+)", resp.content) | |
| if m_n: llm_note = m_n.group(1).strip() | |
| except: pass | |
| final = round(llm_score, 1); level = "CRITICAL" if final < 30 else ("WARNING" if final < 65 else "SAFE") | |
| result = {"score":final,"level":level,"passed":final>=60,"ast_issues":ast_issues,"pattern_issues":pat_issues,"llm_note":llm_note,"all_issues":list(set(ast_issues+pat_issues))[:8]} | |
| self.scan_history.append(result); log.info(f"CyberShield: {level} ({final}/100)"); return result | |
| def quick_scan(self, code): | |
| _, ast_issues = self.scan_ast(code) | |
| _, pat_issues = self.scan_patterns(code) | |
| critical = [i for i in pat_issues if any(k in i.lower() for k in ["shell","injection","pickle","import hijack"])] | |
| return len(ast_issues) == 0 and len(critical) == 0 | |
| def stats(self): | |
| if not self.scan_history: return {"total":0} | |
| passed = sum(1 for s in self.scan_history if s["passed"]) | |
| return {"total":len(self.scan_history),"passed":passed,"blocked":len(self.scan_history)-passed,"avg_score":round(sum(s["score"] for s in self.scan_history)/len(self.scan_history),1)} | |
| ''') | |
| # ============================================================ | |
| # 3. core/web_search.py | |
| # ============================================================ | |
| write_file("core/web_search.py", ''' | |
| import asyncio, hashlib, json, logging, time, aiohttp | |
| from pathlib import Path | |
| log = logging.getLogger("vortex.websearch") | |
| CACHE_DIR = Path("data/web_cache"); CACHE_DIR.mkdir(parents=True, exist_ok=True) | |
| class WebSearchAgent: | |
| def __init__(self, llm_engine=None): self.llm = llm_engine; self.search_log = [] | |
| def _cache_key(self, query): return CACHE_DIR / (hashlib.sha256(query.encode()).hexdigest()[:16] + ".json") | |
| def _load_cache(self, query): | |
| k = self._cache_key(query) | |
| if k.exists(): | |
| data = json.loads(k.read_text()) | |
| if time.time() - data.get("ts", 0) < 3600: return data["results"] | |
| return None | |
| def _save_cache(self, query, results): | |
| self._cache_key(query).write_text(json.dumps({"results": results, "ts": time.time()}, ensure_ascii=False)) | |
| async def search_duckduckgo(self, query, max_results=5): | |
| cached = self._load_cache(query) | |
| if cached: return cached | |
| results = [] | |
| try: | |
| url = f"https://api.duckduckgo.com/?q={query}&format=json&no_html=1&skip_disambig=1" | |
| async with aiohttp.ClientSession() as session: | |
| async with session.get(url, timeout=aiohttp.ClientTimeout(total=8)) as resp: | |
| data = await resp.json(content_type=None) | |
| for item in data.get("RelatedTopics", [])[:max_results]: | |
| if isinstance(item, dict) and "Text" in item: | |
| results.append({"title": item.get("Text","")[:100], "snippet": item.get("Text","")[:300], "url": item.get("FirstURL","")}) | |
| if data.get("Abstract"): results.insert(0, {"title": data.get("Heading",""), "snippet": data.get("Abstract","")[:400], "url": data.get("AbstractURL","")}) | |
| except Exception as e: | |
| log.warning(f"DuckDuckGo unavailable: {e}") | |
| results = [{"title": f"Recherche: {query}", "snippet": "Web indisponible.", "url": ""}] | |
| self._save_cache(query, results); return results | |
| async def research_for_rsi(self, domain): | |
| queries = [f"{domain} optimization algorithm Python", f"best {domain} techniques 2024", f"{domain} benchmark comparison"] | |
| all_snippets = [] | |
| for q in queries[:2]: | |
| results = await self.search_duckduckgo(q, max_results=3) | |
| all_snippets.extend([r["snippet"] for r in results if r["snippet"]]) | |
| if not all_snippets: return f"Aucune information web pour : {domain}" | |
| raw_context = "\\n".join(all_snippets[:6])[:2000] | |
| if self.llm: | |
| try: | |
| resp = await self.llm.call(None, "Tu résumes des résultats de recherche.", f"Résume en 3 points clés les techniques de {domain} :\\n{raw_context}\\n\\nRésumé :", max_tokens=150, temperature=0.15, fast=True, profile="summary") | |
| summary = resp.content.strip() | |
| if len(summary) > 20: | |
| self.search_log.append({"domain":domain,"summary":summary,"ts":time.time()}) | |
| return f"[WebSearch] {domain} :\\n{summary}" | |
| except: pass | |
| return f"[WebSearch] {domain} :\\n" + raw_context[:500] | |
| ''') | |
| # ============================================================ | |
| # 4. core/gitautofix.py | |
| # ============================================================ | |
| write_file("core/gitautofix.py", ''' | |
| import subprocess, sys, tempfile, shutil, ast, re, logging, time | |
| from pathlib import Path | |
| log = logging.getLogger("vortex.gitautofix") | |
| PATCHES_DIR = Path("data/git_patches"); PATCHES_DIR.mkdir(parents=True, exist_ok=True) | |
| class GitAutoFix: | |
| def __init__(self, llm_engine, cybershield): | |
| self.llm=llm_engine; self.shield=cybershield; self.patch_log=[]; self.fix_count=0 | |
| def detect_bugs(self, code): | |
| bugs = [] | |
| try: ast.parse(code) | |
| except SyntaxError as e: bugs.append(f"SyntaxError ligne {e.lineno}: {e.msg}") | |
| patterns = [(r"(\\w+)\\s*=\\s*\\1\\s*\\+\\s*1", "Auto-incrémentation sans déclaration"), (r"for\\s+\\w+\\s+in\\s+range\\([^)]*\\):\\s*$", "Boucle sans corps"), (r"def\\s+\\w+\\([^)]*\\):\\s*$", "Fonction sans corps"), (r"if\\s+\\w+\\s*=\\s*\\w+", "Assignation dans condition (=)")] | |
| for pattern, msg in patterns: | |
| if re.search(pattern, code, re.MULTILINE): bugs.append(msg) | |
| return bugs | |
| def run_tests(self, code): | |
| with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: | |
| f.write(code); tmp = Path(f.name) | |
| try: | |
| proc = subprocess.run([sys.executable, str(tmp)], capture_output=True, text=True, timeout=8) | |
| return {"passed": proc.returncode == 0, "stdout": proc.stdout[:300], "stderr": proc.stderr[:300]} | |
| except subprocess.TimeoutExpired: return {"passed": False, "stderr": "Timeout"} | |
| except Exception as e: return {"passed": False, "stderr": str(e)} | |
| finally: tmp.unlink(missing_ok=True) | |
| async def fix_code(self, code, error_context=""): | |
| bugs = self.detect_bugs(code) | |
| if not bugs and not error_context: return {"fixed":False,"reason":"Aucun bug","code":code} | |
| prompt = (f"ÉTAPE 1 — Identifie les bugs.\\nÉTAPE 2 — Cause racine.\\nÉTAPE 3 — Code corrigé.\\n\\nBugs: {bugs}\\nErreur: {error_context[:300]}\\n\\nCODE:\\n{code[:2000]}\\n\\nCODE CORRIGÉ (uniquement):") | |
| try: | |
| resp = await self.llm.call(None, "Expert Python.", prompt, max_tokens=800, temperature=0.2, profile="reasoning") | |
| fixed_code = resp.content.strip().replace("```python","").replace("```","").strip() | |
| try: ast.parse(fixed_code) | |
| except SyntaxError as e: return {"fixed":False,"reason":f"Patch invalide: {e}","code":code} | |
| if not self.shield.quick_scan(fixed_code): return {"fixed":False,"reason":"Blocage CyberShield","code":code} | |
| test_result = self.run_tests(fixed_code) | |
| patch_id = f"fix_{int(time.time())}" | |
| (PATCHES_DIR / f"{patch_id}.py").write_text(fixed_code) | |
| self.fix_count += 1 | |
| return {"fixed":True,"patch_id":patch_id,"bugs_fixed":bugs,"test_passed":test_result["passed"],"code":fixed_code} | |
| except Exception as e: return {"fixed":False,"reason":str(e),"code":code} | |
| async def devin_deploy(self, filepath, auto_fix=True): | |
| path = Path(filepath) | |
| if not path.exists(): return {"status":"error","reason":"fichier introuvable"} | |
| original = path.read_text(); backup = path.with_suffix(".py.bak"); shutil.copy(path, backup) | |
| orig_test = self.run_tests(original) | |
| if orig_test["passed"] and not self.detect_bugs(original): return {"status":"ok","reason":"Aucun bug"} | |
| if auto_fix: | |
| fix_result = await self.fix_code(original, orig_test.get("stderr","")) | |
| if fix_result["fixed"]: | |
| final_test = self.run_tests(fix_result["code"]) | |
| if final_test["passed"]: | |
| path.write_text(fix_result["code"]); return {"status":"deployed","file":filepath,"bugs_fixed":fix_result["bugs_fixed"]} | |
| else: | |
| shutil.copy(backup, path); return {"status":"rollback","reason":"Tests échoués après correction"} | |
| return {"status":"unchanged","bugs":self.detect_bugs(original)} | |
| ''') | |
| # ============================================================ | |
| # 5. federation/nexus.py | |
| # ============================================================ | |
| write_file("federation/nexus.py", ''' | |
| import asyncio, aiohttp, logging | |
| log = logging.getLogger("vortex.nexus") | |
| class NexusCore: | |
| def __init__(self, base_url="http://localhost:7861"): self.base_url = base_url; self.peers = [] | |
| async def register_peer(self, url): | |
| try: | |
| async with aiohttp.ClientSession() as s: | |
| async with s.get(f"{url}/health", timeout=3) as r: | |
| if r.status == 200: | |
| if url not in self.peers: self.peers.append(url); log.info(f"Pair: {url}") | |
| return True | |
| except: pass | |
| return False | |
| async def get_agents(self, url): | |
| try: | |
| async with aiohttp.ClientSession() as s: | |
| async with s.get(f"{url}/api/agents", timeout=5) as r: | |
| if r.status == 200: return (await r.json()).get("agents", []) | |
| except: return [] | |
| async def broadcast_task(self, task, agent_type=None): | |
| results = {} | |
| for peer in self.peers: | |
| try: | |
| async with aiohttp.ClientSession() as s: | |
| payload = {"task": task, "agent_type": agent_type or "all"} | |
| async with s.post(f"{peer}/api/execute", json=payload, timeout=10) as r: | |
| if r.status == 200: | |
| results[peer] = await r.json() | |
| except: pass | |
| return results | |
| nexus = NexusCore() | |
| ''') | |
| # ============================================================ | |
| # 6. core/nexus_server.py | |
| # ============================================================ | |
| write_file("core/nexus_server.py", ''' | |
| import asyncio, json, time, logging | |
| from fastapi import FastAPI, HTTPException | |
| from pydantic import BaseModel | |
| import uvicorn | |
| log = logging.getLogger("vortex.nexus.server") | |
| app = FastAPI(title="Nexus Core", version="1.0") | |
| class AgentData(BaseModel): id: str; code: str; score: float; ts: float | |
| class TaskRequest(BaseModel): task: str; agent_type: str = "all" | |
| agents_db = {} | |
| @app.get("/health") | |
| async def health(): return {"status": "ok", "ts": time.time()} | |
| @app.get("/api/agents") | |
| async def list_agents(): return {"agents": list(agents_db.values())} | |
| @app.post("/api/agents") | |
| async def add_agent(agent: AgentData): | |
| agents_db[agent.id] = agent.dict() | |
| log.info(f"Agent reçu: {agent.id} (score {agent.score})") | |
| return {"status": "ok", "id": agent.id} | |
| @app.post("/api/execute") | |
| async def execute_task(req: TaskRequest): | |
| # Simule l'exécution d'une tâche | |
| return {"status": "ok", "result": f"Task '{req.task}' executed on {req.agent_type}"} | |
| def run_nexus_server(port=7861): | |
| log.info(f"Démarrage Nexus Server sur le port {port}") | |
| uvicorn.run(app, host="0.0.0.0", port=port, log_level="warning") | |
| ''') | |
| # ============================================================ | |
| # 7. benchmarks/swe_bench_adapter.py | |
| # ============================================================ | |
| write_file("benchmarks/swe_bench_adapter.py", ''' | |
| import subprocess, sys, tempfile, json, random | |
| from pathlib import Path | |
| PROBLEMS = [ | |
| {"id": "fib", "desc": "Fibonacci", "code": "def fib(n): return n if n<2 else fib(n-1)+fib(n-2)", "test": "assert fib(10)==55"}, | |
| {"id": "rev", "desc": "Reverse list", "code": "def rev(l): return l[::-1]", "test": "assert rev([1,2,3])==[3,2,1]"}, | |
| {"id": "pal", "desc": "Palindrome", "code": "def pal(s): return s==s[::-1]", "test": "assert pal('radar') and not pal('hello')"}, | |
| {"id": "vowels", "desc": "Count vowels", "code": "def count(s): return sum(1 for c in s if c in 'aeiou')", "test": "assert count('hello')==2"}, | |
| {"id": "max_list", "desc": "Max", "code": "def max_l(l): return max(l)", "test": "assert max_l([1,5,3])==5"}, | |
| ] | |
| def run_swe_bench(agent_code, max_instances=4): | |
| passed = 0 | |
| for p in PROBLEMS[:max_instances]: | |
| full = agent_code + "\\n" + p["code"] + "\\n" + p["test"] | |
| with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: | |
| f.write(full); tmp = Path(f.name) | |
| try: | |
| r = subprocess.run([sys.executable, str(tmp)], capture_output=True, timeout=4) | |
| if r.returncode == 0: passed += 1 | |
| except: pass | |
| finally: tmp.unlink(missing_ok=True) | |
| return {"resolved": passed, "total": max_instances, "rate": round(passed/max_instances*100, 1)} | |
| ''') | |
| # ============================================================ | |
| # 8. core/rsi_loop.py | |
| # ============================================================ | |
| write_file("core/rsi_loop.py", ''' | |
| import asyncio, importlib.util, shutil, time, math, json, logging | |
| from pathlib import Path | |
| log = logging.getLogger("vortex.rsi") | |
| ARCHIVE_DIR = Path("data/rsi_archive"); ARCHIVE_DIR.mkdir(parents=True, exist_ok=True) | |
| DREAM_PATH = Path("data/dream_state.json") | |
| BENCH_FUNCS = [ | |
| ("sphere", lambda p: sum(x**2 for x in p), [(-5,5)]*5, 0.0), | |
| ("rastrigin", lambda p: 10*len(p)+sum(xi**2-10*math.cos(2*math.pi*xi) for xi in p), [(-5.12,5.12)]*5, 0.0), | |
| ("rosenbrock", lambda p: sum(100*(p[i+1]-p[i]**2)**2+(1-p[i])**2 for i in range(len(p)-1)), [(-2,2)]*5, 0.0), | |
| ] | |
| class RSILoop: | |
| def __init__(self, llm, opt_path, shield=None, web=None, gitfix=None): | |
| self.llm=llm; self.path=Path(opt_path); self.backup=self.path.with_suffix(".py.bak") | |
| self.shield=shield; self.web=web; self.gitfix=gitfix | |
| self.generation=0; self.best_score=self._bench_file(self.path); self.archive=self._load_archive() | |
| log.info(f"RSI v2 init — score={self.best_score:.4f} arch={len(self.archive)}") | |
| def _load_archive(self): | |
| idx = ARCHIVE_DIR / "index.json" | |
| return json.loads(idx.read_text()) if idx.exists() else [] | |
| def _save_archive(self): | |
| (ARCHIVE_DIR/"index.json").write_text(json.dumps(self.archive, indent=2)) | |
| def _bench_module(self, mod): | |
| try: Opt = getattr(mod, "FellowOptimizer") | |
| except: return 0.0 | |
| scores = [] | |
| for _, fn, bounds, opt_v in BENCH_FUNCS: | |
| try: | |
| o = Opt(fn, bounds, max_evals=100) | |
| best, _ = o.optimize() | |
| scores.append(1.0 - math.tanh(abs(fn(best)-opt_v))) | |
| except: scores.append(0.0) | |
| return sum(scores)/len(scores) if scores else 0.0 | |
| def _bench_file(self, path): | |
| try: | |
| spec = importlib.util.spec_from_file_location("_b", path) | |
| mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod) | |
| return self._bench_module(mod) | |
| except: return 0.0 | |
| def _load_dream(self): | |
| if DREAM_PATH.exists(): | |
| try: | |
| state = json.loads(DREAM_PATH.read_text()) | |
| summary = state.get("summary","") | |
| if summary and len(summary)>20: return f"\\n\\nCONTEXTE MÉMORIEL:\\n{summary[:400]}\\n" | |
| except: pass | |
| return "" | |
| async def _gen_variant(self): | |
| if not self.llm: return None | |
| current = self.path.read_text() | |
| dream_ctx = self._load_dream() | |
| top_arc = sorted(self.archive, key=lambda x:x["score"], reverse=True)[:2] | |
| arc_ctx = "\\n".join([f"# Archive score={a['score']:.3f}\\n{a['snippet']}" for a in top_arc]) | |
| web_ctx = "" | |
| if self.web: | |
| try: web_ctx = await asyncio.wait_for(self.web.research_for_rsi("evolutionary optimization Python"), timeout=6.0) | |
| except: web_ctx = "" | |
| prompt = (f"ÉTAPE 1 — Analyse les forces/faiblesses de ce code :\\n{current[:1500]}\\n\\n" | |
| f"ÉTAPE 2 — Identifie la meilleure technique (DE, PSO, CMA-ES).\\n" | |
| f"Archives: {arc_ctx[:400] if arc_ctx else 'aucune'}\\n" | |
| f"Web: {web_ctx[:400] if web_ctx else 'non disponible'}" | |
| f"{dream_ctx}" | |
| f"ÉTAPE 3 — Écris la nouvelle version de FellowOptimizer.\\n" | |
| f"CODE UNIQUEMENT (pas de markdown) :") | |
| try: | |
| resp = await self.llm.call(None, "Expert optimisation.", prompt, max_tokens=900, temperature=0.4, profile="code_gen", use_cache=False) | |
| code = resp.content.strip().replace("```python","").replace("```","").strip() | |
| return code if "class FellowOptimizer" in code else None | |
| except: return None | |
| async def run_generation(self): | |
| self.generation += 1 | |
| code = await self._gen_variant() | |
| if not code: return {"gen":self.generation,"action":"skip","reason":"bad_code","score":self.best_score} | |
| if self.shield and not self.shield.quick_scan(code): | |
| return {"gen":self.generation,"action":"blocked","reason":"CyberShield","score":self.best_score} | |
| if self.gitfix: | |
| bugs = self.gitfix.detect_bugs(code) | |
| if bugs: | |
| fix = await self.gitfix.fix_code(code, f"RSI gen {self.generation}") | |
| if fix["fixed"]: code = fix["code"] | |
| tmp = self.path.with_suffix(".py.new") | |
| try: | |
| ast.parse(code) | |
| tmp.write_text(code) | |
| spec = importlib.util.spec_from_file_location("_n", tmp) | |
| mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod) | |
| new_score = self._bench_module(mod) | |
| if new_score > self.best_score: | |
| shutil.copy(self.path, self.backup) | |
| shutil.move(str(tmp), str(self.path)) | |
| old = self.best_score; self.best_score = new_score | |
| self.archive.append({"gen":self.generation,"score":round(new_score,4),"snippet":code[:300],"ts":time.time()}) | |
| self._save_archive() | |
| return {"gen":self.generation,"action":"deployed","old":old,"new":new_score} | |
| self.archive.append({"gen":self.generation,"score":self.best_score*0.85,"snippet":code[:300],"ts":time.time()}) | |
| self._save_archive() | |
| return {"gen":self.generation,"action":"archived","score":self.best_score} | |
| except Exception as e: return {"gen":self.generation,"action":"error","reason":str(e)} | |
| finally: | |
| if tmp.exists(): tmp.unlink(missing_ok=True) | |
| def status(self): | |
| return {"generation":self.generation,"best_score":round(self.best_score,4),"archive_size":len(self.archive)} | |
| ''') | |
| # ============================================================ | |
| # 9. core/agent_registry.py | |
| # ============================================================ | |
| write_file("core/agent_registry.py", ''' | |
| import logging | |
| log = logging.getLogger("vortex.registry") | |
| class AgentRegistry: | |
| def __init__(self): | |
| self.agents = {} | |
| def register(self, name, instance, description, capabilities=None): | |
| self.agents[name] = {"instance": instance, "description": description, "capabilities": capabilities or []} | |
| log.info(f"Agent enregistré : {name}") | |
| def list(self): | |
| return {name: info["description"] for name, info in self.agents.items()} | |
| def get(self, name): | |
| return self.agents.get(name, {}).get("instance") | |
| def get_capabilities(self, name): | |
| return self.agents.get(name, {}).get("capabilities", []) | |
| def filter_by_capability(self, cap): | |
| return [n for n, info in self.agents.items() if cap in info.get("capabilities", [])] | |
| registry = AgentRegistry() | |
| ''') | |
| # ============================================================ | |
| # 10. core/judge_layer.py | |
| # ============================================================ | |
| write_file("core/judge_layer.py", ''' | |
| import asyncio, json, logging, re | |
| log = logging.getLogger("vortex.judge") | |
| class JudgeLayer: | |
| def __init__(self, llm_engine, registry=None): | |
| self.llm = llm_engine; self.registry = registry; self.history = [] | |
| async def evaluate_candidates(self, task, candidates): | |
| if not candidates: return {"error": "Aucun candidat"} | |
| if len(candidates) == 1: | |
| name = list(candidates.keys())[0]; return {"winner": name, "scores": {name: 1.0}, "confidence": 1.0} | |
| prompt = f"Juge expert. Évalue les réponses pour la tâche : \\"{task}\\"\\n" | |
| for name, content in candidates.items(): | |
| prompt += f"\\n--- Agent {name} ---\\n{content[:1000]}\\n" | |
| prompt += """Attribue un score 0-1 pour chaque (précision, exhaustivité, qualité). Réponds UNIQUEMENT en JSON : {"scores": {"agent1": 0.9, ...}, "winner": "agent1", "confidence": 0.85}""" | |
| try: | |
| resp = await self.llm.call(None, "Expert évaluateur.", prompt, max_tokens=200, temperature=0.1, fast=True) | |
| match = re.search(r"\\{.*\\}", resp.content, re.DOTALL) | |
| if match: | |
| result = json.loads(match.group()) | |
| if "winner" in result and result["winner"] not in candidates: | |
| if "scores" in result: | |
| best = max(result["scores"], key=lambda k: result["scores"][k]) | |
| result["winner"] = best | |
| return result | |
| else: | |
| best_name = max(candidates, key=lambda k: len(candidates[k])) | |
| return {"winner": best_name, "scores": {k: 0.5 for k in candidates}, "confidence": 0.5} | |
| except Exception as e: | |
| log.error(f"Judge error: {e}") | |
| best_name = max(candidates, key=lambda k: len(candidates[k])) | |
| return {"winner": best_name, "scores": {k: 0.5 for k in candidates}, "confidence": 0.3} | |
| async def dispatch(self, task, agent_names=None): | |
| if self.registry is None: return {"error": "AgentRegistry manquant"} | |
| if agent_names is None: agent_names = list(self.registry.agents.keys()) | |
| results = {}; tasks = {} | |
| for name in agent_names: | |
| agent = self.registry.get(name) | |
| if agent is None: | |
| results[name] = "Agent non trouvé"; continue | |
| if hasattr(agent, "run"): tasks[name] = asyncio.create_task(agent.run(task)) | |
| elif hasattr(agent, "evaluate"): tasks[name] = asyncio.create_task(agent.evaluate(task)) | |
| elif hasattr(agent, "research_for_rsi"): tasks[name] = asyncio.create_task(agent.research_for_rsi(task)) | |
| else: results[name] = "Agent sans méthode" | |
| for name, future in tasks.items(): | |
| try: | |
| result = await future | |
| if hasattr(result, "content"): results[name] = result.content | |
| elif isinstance(result, dict): results[name] = json.dumps(result, ensure_ascii=False) | |
| else: results[name] = str(result) | |
| except Exception as e: results[name] = f"Erreur: {e}" | |
| judge_result = await self.evaluate_candidates(task, results) | |
| judge_result["all_responses"] = results | |
| self.history.append({"task": task, "judge_result": judge_result}) | |
| return judge_result | |
| judge = None | |
| ''') | |
| # ============================================================ | |
| # 11. core/orchestrator.py | |
| # ============================================================ | |
| write_file("core/orchestrator.py", ''' | |
| import asyncio, json, logging, re | |
| log = logging.getLogger("vortex.orchestrator") | |
| class Orchestrator: | |
| def __init__(self, llm_engine, judge_layer, registry): | |
| self.llm = llm_engine; self.judge = judge_layer; self.registry = registry | |
| self.roles = {"planner": "Planifier l'architecture", "writer": "Écrire le code", "reviewer": "Vérifier sécurité", "optimizer": "Optimiser performances"} | |
| async def decompose(self, task): | |
| prompt = f"Décompose cette tâche en 2-4 sous-tâches claires. Tâche : {task}\\nRetourne UNIQUEMENT une liste JSON : [\\"sous-tâche 1\\", ...]" | |
| try: | |
| resp = await self.llm.call(None, "Planificateur.", prompt, max_tokens=200, temperature=0.3, fast=True) | |
| match = re.search(r"\\[.*\\]", resp.content, re.DOTALL) | |
| if match: | |
| subtasks = json.loads(match.group()); return [s.strip() for s in subtasks if s.strip()] | |
| else: return [task] | |
| except Exception as e: log.error(f"Decomp error: {e}"); return [task] | |
| async def run(self, task, roles=None): | |
| if roles is None: roles = self.roles | |
| subtasks = await self.decompose(task) | |
| if not subtasks: return {"error": "Impossible de décomposer"} | |
| assigned = {} | |
| role_names = list(roles.keys()) | |
| for i, st in enumerate(subtasks): | |
| role = role_names[i % len(role_names)] | |
| assigned[role] = st | |
| results = {} | |
| for role, st in assigned.items(): | |
| log.info(f"Orchestrator: {role} → {st}") | |
| judge_result = await self.judge.dispatch(st) | |
| results[role] = { | |
| "task": st, | |
| "winner": judge_result.get("winner"), | |
| "score": judge_result.get("confidence", 0), | |
| "content": judge_result.get("all_responses", {}).get(judge_result.get("winner"), "") | |
| } | |
| synth_prompt = f"Tâche initiale : {task}\\nSynthétise les résultats :\\n" | |
| for role, data in results.items(): | |
| synth_prompt += f"\\n--- {role} ---\\n{data['content'][:500]}\\n" | |
| synth_prompt += "\\nDonne la solution finale complète." | |
| try: | |
| synth_resp = await self.llm.call(None, "Synthétiseur.", synth_prompt, max_tokens=800, temperature=0.2, profile="reasoning") | |
| synthesis = synth_resp.content.strip() | |
| except Exception as e: synthesis = f"Erreur synthèse: {e}" | |
| return {"task": task, "subtasks": assigned, "results": results, "synthesis": synthesis} | |
| orchestrator = None | |
| ''') | |
| # ============================================================ | |
| # 12. core/agent_manager.py | |
| # ============================================================ | |
| write_file("core/agent_manager.py", ''' | |
| import logging | |
| log = logging.getLogger("vortex.agent_manager") | |
| class AgentManager: | |
| def __init__(self, registry, judge, orchestrator): | |
| self.registry = registry; self.judge = judge; self.orchestrator = orchestrator; self.default_agent = None | |
| async def multi(self, task, agent_names=None): return await self.judge.dispatch(task, agent_names) | |
| async def judge_task(self, task): return await self.judge.dispatch(task) | |
| async def orchestrate(self, task, roles=None): return await self.orchestrator.run(task, roles) | |
| async def switch_agent(self, name): | |
| if name in self.registry.agents: | |
| self.default_agent = name; return True | |
| return False | |
| def list_agents(self): return self.registry.list() | |
| ''') | |
| # ============================================================ | |
| # 13. core/optimizer.py | |
| # ============================================================ | |
| write_file("core/optimizer.py", ''' | |
| import random, math | |
| class FellowOptimizer: | |
| def __init__(self, func, bounds, max_evals=200): | |
| self.func=func; self.bounds=bounds; self.max_evals=max_evals | |
| def optimize(self): | |
| dim=len(self.bounds); F,CR=0.8,0.9 | |
| pop=[[random.uniform(l,u) for l,u in self.bounds] for _ in range(15)] | |
| vals=[self.func(p) for p in pop] | |
| best=pop[vals.index(min(vals))][:]; best_val=min(vals); evals=15 | |
| while evals < self.max_evals: | |
| for i in range(len(pop)): | |
| a,b,c=random.sample([j for j in range(len(pop)) if j!=i],3) | |
| trial=[max(l,min(u,pop[a][d]+F*(pop[b][d]-pop[c][d]))) if random.random()<CR else pop[i][d] for d,(l,u) in enumerate(self.bounds)] | |
| v=self.func(trial); evals+=1 | |
| if v<vals[i]: | |
| pop[i]=trial; vals[i]=v | |
| if v<best_val: | |
| best=trial[:]; best_val=v | |
| if evals>=self.max_evals: break | |
| return best,[best_val] | |
| ''') | |
| # ============================================================ | |
| # 14. core/peer_review.py | |
| # ============================================================ | |
| write_file("core/peer_review.py", ''' | |
| import re | |
| class PeerReview: | |
| def __init__(self, llm, shield=None): self.llm=llm; self.shield=shield | |
| async def evaluate(self, code, context=""): | |
| if not code: return {"score":0,"passed":False} | |
| if self.shield: | |
| r=await self.shield.full_scan(code,context) | |
| if not r["passed"]: return {"score":0,"passed":False,"comment":"CyberShield bloque","issues":r["all_issues"]} | |
| if not self.llm: return {"score":0.7,"passed":True} | |
| try: | |
| resp=await self.llm.call(None,"Expert revue.",f"Note ce code (0-1):\\n{code[:1200]}\\nRéponds: SCORE=0.XX", max_tokens=60, fast=True) | |
| m=re.search(r"SCORE=([0-9.]+)", resp.content); s=float(m.group(1)) if m else 0.7 | |
| return {"score":round(s,3),"passed":s>=0.6} | |
| except: return {"score":0.65,"passed":True} | |
| ''') | |
| # ============================================================ | |
| # 15. core/dream_consolidator.py | |
| # ============================================================ | |
| write_file("core/dream_consolidator.py", ''' | |
| import json, time, logging; from pathlib import Path | |
| log=logging.getLogger("vortex.dream"); DREAM_PATH=Path("data/dream_state.json") | |
| class DreamConsolidator: | |
| def __init__(self, mem, llm): | |
| self.mem=mem; self.llm=llm | |
| self.state = {} | |
| if DREAM_PATH.exists(): | |
| try: self.state = json.loads(DREAM_PATH.read_text()) | |
| except: self.state = {"summary":"","last_ts":0,"count":0} | |
| else: self.state = {"summary":"","last_ts":0,"count":0} | |
| def _save(self): DREAM_PATH.write_text(json.dumps(self.state, indent=2)) | |
| async def consolidate(self): | |
| entries = self.mem.retrieve("", top_k=30) if self.mem else [] | |
| ctx = "\\n".join([e.content[:200] for e in entries if hasattr(e,"content")]) | |
| if not ctx: | |
| self.state["summary"] = "Aucun apprentissage récent." | |
| self._save() | |
| return self.state | |
| try: | |
| resp = await self.llm.call(None, "Résumeur.", f"Résume en 5 points les apprentissages :\\n{ctx[:3000]}\\nRésumé :", max_tokens=200, fast=True, profile="summary") | |
| self.state.update({"summary":resp.content.strip(),"last_ts":time.time(),"count":self.state.get("count",0)+1}) | |
| self._save() | |
| except Exception as e: | |
| log.error(f"Dream consolidation failed: {e}") | |
| return self.state | |
| ''') | |
| # ============================================================ | |
| # 16. core/autodev.py | |
| # ============================================================ | |
| write_file("core/autodev.py", ''' | |
| import shutil, ast | |
| class AutoDev: | |
| def __init__(self, llm): self.llm=llm | |
| async def improve(self, filepath): | |
| from pathlib import Path | |
| path=Path(filepath) | |
| if not path.exists(): return {"error":"File not found"} | |
| orig=path.read_text() | |
| try: | |
| resp=await self.llm.call(None,"Expert Python.",f"Améliore ce code:\\n{orig[:2000]}\\nCode uniquement:", max_tokens=900, temperature=0.4) | |
| new=resp.content.strip().replace("```python","").replace("```","").strip() | |
| try: | |
| ast.parse(new) | |
| except SyntaxError as e: | |
| return {"error":f"SyntaxError dans le patch: {e}"} | |
| backup=path.with_suffix(".py.bak"); shutil.copy(path,backup); path.write_text(new) | |
| return {"status":"deployed","backup":str(backup)} | |
| except Exception as e: return {"error":str(e)} | |
| ''') | |
| # ============================================================ | |
| # 17. sandbox/secure_sandbox.py | |
| # ============================================================ | |
| write_file("sandbox/secure_sandbox.py", ''' | |
| import subprocess, sys, tempfile, resource; from pathlib import Path | |
| def run_in_sandbox(code, timeout=5, memory_mb=128): | |
| with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: | |
| f.write(code); tmp=Path(f.name) | |
| try: | |
| def _lim(): | |
| try: resource.setrlimit(resource.RLIMIT_AS,(memory_mb*1024*1024,memory_mb*1024*1024)); resource.setrlimit(resource.RLIMIT_CPU,(timeout,timeout+1)) | |
| except: pass | |
| proc=subprocess.Popen([sys.executable,str(tmp)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=_lim) | |
| out,err=proc.communicate(timeout=timeout+1) | |
| return {"success":proc.returncode==0,"result":out.decode()[:2000],"error":err.decode()[:500]} | |
| except subprocess.TimeoutExpired: proc.kill(); return {"success":False,"error":"Timeout"} | |
| except Exception as e: return {"success":False,"error":str(e)} | |
| finally: tmp.unlink(missing_ok=True) | |
| ''') | |
| # ============================================================ | |
| # 18. benchmarks/humaneval_adapter.py | |
| # ============================================================ | |
| write_file("benchmarks/humaneval_adapter.py", ''' | |
| import subprocess, sys, tempfile; from pathlib import Path | |
| CASES=[("def add(a,b): return a+b","assert add(2,3)==5"),("def is_even(n): return n%2==0","assert is_even(4)"),("def fact(n): return 1 if n<=1 else n*fact(n-1)","assert fact(5)==120"),("def rev(s): return s[::-1]","assert rev('abc')=='cba'"),("def fib(n): return n if n<2 else fib(n-1)+fib(n-2)","assert fib(10)==55")] | |
| def run_humaneval(preamble="",max_cases=5): | |
| passed=0 | |
| for sol,test in CASES[:max_cases]: | |
| full=(preamble+"\\n"+sol+"\\n"+test) if preamble else sol+"\\n"+test | |
| with tempfile.NamedTemporaryFile(mode="w",suffix=".py",delete=False) as f: f.write(full); tmp=Path(f.name) | |
| try: | |
| r=subprocess.run([sys.executable,str(tmp)],capture_output=True,timeout=3) | |
| if r.returncode==0: passed+=1 | |
| except: pass | |
| finally: tmp.unlink(missing_ok=True) | |
| return {"pass@1":round(passed/max_cases*100,1),"passed":passed,"total":max_cases} | |
| ''') | |
| # ============================================================ | |
| # 19. core/hf_mcp.py – Intégration Hugging Face MCP | |
| # ============================================================ | |
| write_file("core/hf_mcp.py", ''' | |
| import asyncio, json, logging, os, aiohttp | |
| from typing import Dict, List, Optional | |
| log = logging.getLogger("vortex.hf_mcp") | |
| HF_MCP_URL = os.environ.get("HF_MCP_URL", "https://huggingface.co/api") | |
| class HFMCPClient: | |
| def __init__(self, use_mcp=False): | |
| self.use_mcp = use_mcp | |
| self.hub_api = "https://huggingface.co/api" | |
| self.history = [] | |
| async def _hub_api_request(self, method: str, params: Dict) -> Dict: | |
| if method == "search_models": | |
| query = params.get("query", "") | |
| url = f"{self.hub_api}/models?search={query}&limit=10" | |
| async with aiohttp.ClientSession() as session: | |
| async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp: | |
| if resp.status != 200: | |
| return {"error": f"API error {resp.status}"} | |
| data = await resp.json() | |
| return {"models": [{"id": m["id"], "downloads": m.get("downloads", 0)} for m in data[:5]]} | |
| elif method == "search_datasets": | |
| query = params.get("query", "") | |
| url = f"{self.hub_api}/datasets?search={query}&limit=10" | |
| async with aiohttp.ClientSession() as session: | |
| async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp: | |
| if resp.status != 200: | |
| return {"error": f"API error {resp.status}"} | |
| data = await resp.json() | |
| return {"datasets": [{"id": d["id"]} for d in data[:5]]} | |
| elif method == "get_space": | |
| space_name = params.get("name", "") | |
| url = f"{self.hub_api}/spaces/{space_name}" | |
| async with aiohttp.ClientSession() as session: | |
| async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp: | |
| if resp.status != 200: | |
| return {"error": f"Space not found or API error {resp.status}"} | |
| data = await resp.json() | |
| return {"space": {"id": data.get("id"), "likes": data.get("likes", 0)}} | |
| elif method == "search_papers": | |
| query = params.get("query", "") | |
| url = f"http://export.arxiv.org/api/query?search_query=all:{query}&start=0&max_results=5" | |
| async with aiohttp.ClientSession() as session: | |
| try: | |
| async with session.get(url, timeout=aiohttp.ClientTimeout(total=15)) as resp: | |
| if resp.status != 200: | |
| return {"error": f"arXiv error {resp.status}"} | |
| text = await resp.text() | |
| import xml.etree.ElementTree as ET | |
| root = ET.fromstring(text) | |
| papers = [] | |
| for entry in root.findall("{http://www.w3.org/2005/Atom}entry"): | |
| title = entry.find("{http://www.w3.org/2005/Atom}title") | |
| if title is not None: | |
| papers.append({"title": title.text.strip(), "id": "N/A"}) | |
| return {"papers": papers[:3]} | |
| except Exception as e: | |
| log.warning(f"arXiv API error: {e}") | |
| return {"papers": [{"title": f"Recherche simulée pour '{query}'", "id": "arxiv"}]} | |
| return {"error": "Méthode non supportée"} | |
| async def search_models(self, query: str, limit: int = 5) -> List[Dict]: | |
| result = await self._hub_api_request("search_models", {"query": query, "limit": limit}) | |
| return result.get("models", []) | |
| async def search_datasets(self, query: str, limit: int = 5) -> List[Dict]: | |
| result = await self._hub_api_request("search_datasets", {"query": query, "limit": limit}) | |
| return result.get("datasets", []) | |
| async def get_space_info(self, space_name: str) -> Dict: | |
| result = await self._hub_api_request("get_space", {"name": space_name}) | |
| return result.get("space", {}) | |
| async def search_papers(self, query: str) -> List[Dict]: | |
| result = await self._hub_api_request("search_papers", {"query": query}) | |
| return result.get("papers", []) | |
| def stats(self) -> Dict: | |
| return {"history": len(self.history)} | |
| hf_mcp = HFMCPClient(use_mcp=False) | |
| ''') | |
| # ============================================================ | |
| # 20. app.py – VERSION HEADLESS FASTAPI (déjà fournie) | |
| # ============================================================ | |
| write_file("app.py", ''' | |
| #!/usr/bin/env python3 | |
| import os, sys, asyncio, json, logging, time | |
| from pathlib import Path | |
| from fastapi import FastAPI, HTTPException | |
| from pydantic import BaseModel | |
| import uvicorn | |
| import nest_asyncio | |
| nest_asyncio.apply() | |
| BASE = Path("/app") | |
| os.chdir(BASE); sys.path.insert(0, str(BASE)) | |
| DATA_DIR = Path(os.environ.get("DATA_DIR", "/data")) | |
| DATA_DIR.mkdir(parents=True, exist_ok=True) | |
| logging.basicConfig(level=logging.INFO, format="%(levelname)s [%(name)s] %(message)s") | |
| log = logging.getLogger("vortex.headless") | |
| from agents.cognitive_engine import MultiLLMEngine | |
| from core.cybershield import CyberShield | |
| from core.web_search import WebSearchAgent | |
| from core.gitautofix import GitAutoFix | |
| from core.rsi_loop import RSILoop | |
| from core.peer_review import PeerReview | |
| from core.autodev import AutoDev | |
| from core.dream_consolidator import DreamConsolidator | |
| from federation.nexus import nexus | |
| from sandbox.secure_sandbox import run_in_sandbox | |
| from benchmarks.humaneval_adapter import run_humaneval | |
| from benchmarks.swe_bench_adapter import run_swe_bench | |
| from core.agent_registry import registry | |
| from core.judge_layer import JudgeLayer | |
| from core.orchestrator import Orchestrator | |
| from core.agent_manager import AgentManager | |
| from core.hf_mcp import hf_mcp | |
| class _Mem: | |
| def __init__(self): self.store=[] | |
| def ingest(self,c,**k): self.store.append({"content":c}) | |
| def retrieve(self,q,top_k=5): return [type("E",(),{"content":e["content"]})() for e in self.store[-top_k:]] | |
| class _K: | |
| def run_code(self,code,timeout=10): return run_in_sandbox(code,timeout=int(timeout)) | |
| def get_health(self): | |
| try: import psutil; cpu=psutil.cpu_percent(0.1); mem=psutil.virtual_memory().percent | |
| except: cpu,mem=0.0,0.0 | |
| return {"status":"healthy","cpu_percent":cpu,"memory_percent":mem} | |
| KERNEL=_K(); mem=_Mem(); llm=MultiLLMEngine(mem); shield=CyberShield(llm); web=WebSearchAgent(llm) | |
| gitfix=GitAutoFix(llm, shield); pr=PeerReview(llm, shield) | |
| rsi=RSILoop(llm, "core/optimizer.py", shield, web, gitfix) | |
| autodev=AutoDev(llm); dreamer=DreamConsolidator(mem, llm) | |
| registry.register("rsi", rsi, "Auto-amélioration récursive", ["code_gen","optimization"]) | |
| registry.register("shield", shield, "Analyse de sécurité CyberShield", ["security","audit"]) | |
| registry.register("gitfix", gitfix, "Correction automatique de bugs", ["code_fix","deployment"]) | |
| registry.register("web", web, "Recherche web structurée", ["research","search"]) | |
| registry.register("dreamer", dreamer, "Consolidation mémoire", ["memory"]) | |
| registry.register("nexus", nexus, "Fédération P2P", ["federation"]) | |
| judge = JudgeLayer(llm, registry) | |
| orchestrator = Orchestrator(llm, judge, registry) | |
| manager = AgentManager(registry, judge, orchestrator) | |
| # Scheduler | |
| try: | |
| from apscheduler.schedulers.background import BackgroundScheduler | |
| sched=BackgroundScheduler(timezone="UTC") | |
| def _j(coro): | |
| loop=asyncio.new_event_loop() | |
| try: loop.run_until_complete(coro) | |
| finally: loop.close() | |
| sched.add_job(lambda:_j(rsi.run_generation()), "interval", hours=2, id="rsi") | |
| sched.add_job(lambda:_j(dreamer.consolidate()), "cron", hour=3, id="dream") | |
| sched.start() | |
| log.info("✅ Scheduler: RSI/2h, Dream/3h") | |
| except Exception as e: | |
| log.warning(f"Scheduler non démarré: {e}") | |
| # ---- FastAPI ---- | |
| app = FastAPI(title="VORTEX v5.1 API", version="5.1") | |
| class CommandRequest(BaseModel): | |
| command: str | |
| args: dict = {} | |
| class CodeRequest(BaseModel): | |
| code: str | |
| timeout: int = 10 | |
| @app.get("/") | |
| async def root(): | |
| return {"message": "VORTEX v5.1 Headless API"} | |
| @app.post("/command") | |
| async def execute_command(req: CommandRequest): | |
| msg = req.command | |
| if msg.startswith("@health"): | |
| h = KERNEL.get_health() | |
| return f"CPU {h['cpu_percent']:.0f}% · RAM {h['memory_percent']:.0f}% · RSI gen {rsi.generation}" | |
| elif msg.startswith("@rsi"): | |
| r = await rsi.run_generation() | |
| return f"RSI gen={r['gen']} score={r.get('new', r.get('score',0))}" | |
| elif msg.startswith("@shield"): | |
| code = msg.replace("@shield","").strip() | |
| if not code: return "Syntaxe: @shield <code>" | |
| r = await shield.full_scan(code) | |
| return f"{r['level']} {r['score']}/100 · Issues: {r['all_issues']}" | |
| elif msg.startswith("@gitfix"): | |
| path = msg.replace("@gitfix","").strip() or "core/optimizer.py" | |
| r = await gitfix.devin_deploy(path) | |
| return f"GitAutoFix: {r.get('status','?')} - {r.get('reason','')}" | |
| elif msg.startswith("@web"): | |
| q = msg.replace("@web","").strip() or "optimization" | |
| r = await web.research_for_rsi(q) | |
| return r[:600] | |
| elif msg.startswith("@multi"): | |
| parts = msg.split("--agents") | |
| task = parts[0].replace("@multi","").strip() | |
| agents = None | |
| if len(parts)>1: agents = [a.strip() for a in parts[1].strip().split(",")] | |
| result = await manager.multi(task, agents) | |
| return f"Gagnant: {result.get('winner','?')} (confiance {result.get('confidence',0)})" | |
| elif msg.startswith("@judge"): | |
| task = msg.replace("@judge","").strip() | |
| if not task: return "Syntaxe: @judge <tâche>" | |
| result = await manager.judge_task(task) | |
| return f"Meilleur agent: {result.get('winner','?')}" | |
| elif msg.startswith("@orchestrate"): | |
| task = msg.replace("@orchestrate","").strip() | |
| if not task: return "Syntaxe: @orchestrate <tâche>" | |
| result = await manager.orchestrate(task) | |
| return f"Synthèse: {result.get('synthesis','')[:500]}" | |
| elif msg.startswith("@hfsearch"): | |
| query = msg.replace("@hfsearch","").strip() | |
| if not query: return "Syntaxe: @hfsearch <requête>" | |
| models = await hf_mcp.search_models(query) | |
| return "\\n".join([f"- {m['id']}" for m in models[:5]]) if models else "Aucun modèle" | |
| elif msg.startswith("@nexus"): | |
| parts = msg.replace("@nexus","").strip().split() | |
| if not parts: return "Syntaxe: @nexus add <url> | list | broadcast <tâche>" | |
| if parts[0] == "add": | |
| ok = await nexus.register_peer(parts[1]) | |
| return f"Pair {parts[1]} {'ajouté' if ok else 'inaccessible'}" | |
| elif parts[0] == "list": | |
| return f"Pairs: {', '.join(nexus.peers) if nexus.peers else 'Aucun'}" | |
| elif parts[0] == "broadcast": | |
| task = " ".join(parts[1:]) | |
| results = await nexus.broadcast_task(task) | |
| return "\\n".join([f"{p}: {r}" for p,r in results.items()]) | |
| else: return "Commande invalide" | |
| else: | |
| return "Commande non reconnue" | |
| @app.post("/sandbox") | |
| async def sandbox(req: CodeRequest): | |
| result = KERNEL.run_code(req.code, timeout=req.timeout) | |
| return result | |
| @app.get("/health") | |
| async def health(): | |
| h = KERNEL.get_health() | |
| return {"status": "ok", **h} | |
| if __name__ == "__main__": | |
| log.info("🚀 VORTEX v5.1 Headless API démarre sur le port 7860") | |
| uvicorn.run(app, host="0.0.0.0", port=7860) | |
| ''') | |
| # ============================================================ | |
| # 21. requirements.txt (explicit) | |
| # ============================================================ | |
| write_file("requirements.txt", ''' | |
| gradio>=4.0.0 | |
| nest_asyncio>=1.5.0 | |
| psutil>=5.9.0 | |
| apscheduler>=3.10.0 | |
| aiohttp>=3.9.0 | |
| fastapi>=0.100.0 | |
| uvicorn>=0.23.0 | |
| requests>=2.31.0 | |
| pydantic>=2.0.0 | |
| ''') | |
| # ============================================================ | |
| # 22. Dockerfile (pour HF Spaces) | |
| # ============================================================ | |
| write_file("Dockerfile", ''' | |
| FROM python:3.11-slim | |
| ENV PYTHONDONTWRITEBYTECODE=1 | |
| ENV PYTHONUNBUFFERED=1 | |
| WORKDIR /app | |
| RUN apt-get update && apt-get install -y curl git && rm -rf /var/lib/apt/lists/* | |
| COPY build_vortex.py . | |
| COPY requirements.txt . | |
| COPY app.py . | |
| RUN python build_vortex.py | |
| RUN pip install --no-cache-dir -r requirements.txt | |
| CMD ["python", "app.py"] | |
| ''') | |
| print("✅ Tous les fichiers VORTEX v5.1 ont été générés.") | |
| if __name__ == "__main__": | |
| build_all() |