FerrellSyntheticIntelligence commited on
Commit
da9493e
Β·
verified Β·
1 Parent(s): a2dff57

Upload nanobot_tools.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. nanobot_tools.py +509 -0
nanobot_tools.py ADDED
@@ -0,0 +1,509 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ NANOBOT TOOL SWARM
4
+ Each nanobot is a specialized tool. No cloud. No APIs. Local execution only.
5
+ Coordinator routes tasks to specialist nanobots. They run in parallel.
6
+ """
7
+ import os, sys, json, subprocess, re, hashlib, time
8
+ from concurrent.futures import ThreadPoolExecutor, as_completed
9
+ from pathlib import Path
10
+ from datetime import datetime
11
+
12
+ # ═══════════════════════════════════════════════════════════════
13
+ # BASE NANOBOT CLASS
14
+ # ═══════════════════════════════════════════════════════════════
15
+ class Nanobot:
16
+ def __init__(self, name, specialty):
17
+ self.name = name
18
+ self.specialty = specialty
19
+ self.calls = 0
20
+ self.errors = 0
21
+ self.last_result = None
22
+
23
+ def execute(self, task):
24
+ """Override in subclasses. Return (success, result, error)."""
25
+ raise NotImplementedError
26
+
27
+ def run(self, task):
28
+ self.calls += 1
29
+ try:
30
+ success, result, error = self.execute(task)
31
+ self.last_result = result
32
+ if not success:
33
+ self.errors += 1
34
+ return success, result, error
35
+ except Exception as e:
36
+ self.errors += 1
37
+ return False, None, str(e)
38
+
39
+ # ═══════════════════════════════════════════════════════════════
40
+ # SPECIALIST NANOBOTS
41
+ # ═══════════════════════════════════════════════════════════════
42
+
43
+ class FileSystemNanobot(Nanobot):
44
+ """Handles file read/write/move/delete/list."""
45
+ def __init__(self):
46
+ super().__init__("fs_bot", "filesystem")
47
+
48
+ def execute(self, task):
49
+ action = task.get("action")
50
+ path = task.get("path", "")
51
+
52
+ if action == "read":
53
+ if not os.path.exists(path):
54
+ return False, None, f"File not found: {path}"
55
+ with open(path, 'r', errors='ignore') as f:
56
+ return True, f.read(), None
57
+
58
+ elif action == "write":
59
+ content = task.get("content", "")
60
+ os.makedirs(os.path.dirname(path), exist_ok=True)
61
+ with open(path, 'w') as f:
62
+ f.write(content)
63
+ return True, f"Wrote {len(content)} chars to {path}", None
64
+
65
+ elif action == "list":
66
+ if not os.path.exists(path):
67
+ return False, None, f"Path not found: {path}"
68
+ items = []
69
+ for entry in sorted(os.listdir(path)):
70
+ full = os.path.join(path, entry)
71
+ items.append({
72
+ "name": entry,
73
+ "is_dir": os.path.isdir(full),
74
+ "size": os.path.getsize(full) if os.path.isfile(full) else 0
75
+ })
76
+ return True, items, None
77
+
78
+ elif action == "exists":
79
+ return True, os.path.exists(path), None
80
+
81
+ elif action == "delete":
82
+ if os.path.isfile(path):
83
+ os.remove(path)
84
+ return True, f"Deleted {path}", None
85
+ elif os.path.isdir(path):
86
+ import shutil
87
+ shutil.rmtree(path)
88
+ return True, f"Deleted dir {path}", None
89
+ return False, None, f"Not found: {path}"
90
+
91
+ elif action == "mkdir":
92
+ os.makedirs(path, exist_ok=True)
93
+ return True, f"Created {path}", None
94
+
95
+ else:
96
+ return False, None, f"Unknown action: {action}"
97
+
98
+ class GitNanobot(Nanobot):
99
+ """Handles git operations: status, diff, commit, branch, log."""
100
+ def __init__(self):
101
+ super().__init__("git_bot", "git")
102
+
103
+ def _git(self, args, cwd=None):
104
+ cmd = ['git'] + args
105
+ result = subprocess.run(cmd, capture_output=True, text=True, cwd=cwd, timeout=30)
106
+ return result.returncode == 0, result.stdout, result.stderr
107
+
108
+ def execute(self, task):
109
+ action = task.get("action")
110
+ cwd = task.get("cwd", "/tmp/fsi_felon")
111
+
112
+ if action == "status":
113
+ ok, out, err = self._git(['status', '--short'], cwd)
114
+ return ok, out, err
115
+
116
+ elif action == "diff":
117
+ ok, out, err = self._git(['diff'], cwd)
118
+ return ok, out, err
119
+
120
+ elif action == "add":
121
+ files = task.get("files", ['.'])
122
+ ok, out, err = self._git(['add'] + files, cwd)
123
+ return ok, out, err
124
+
125
+ elif action == "commit":
126
+ msg = task.get("message", "FSI auto-commit")
127
+ ok, out, err = self._git(['commit', '-m', msg], cwd)
128
+ return ok, out, err
129
+
130
+ elif action == "log":
131
+ n = task.get("n", 5)
132
+ ok, out, err = self._git(['log', f'-n{n}', '--oneline'], cwd)
133
+ return ok, out, err
134
+
135
+ elif action == "branch":
136
+ ok, out, err = self._git(['branch'], cwd)
137
+ return ok, out, err
138
+
139
+ elif action == "checkout":
140
+ branch = task.get("branch", "main")
141
+ ok, out, err = self._git(['checkout', branch], cwd)
142
+ return ok, out, err
143
+
144
+ else:
145
+ return False, None, f"Unknown git action: {action}"
146
+
147
+ class CompileNanobot(Nanobot):
148
+ """Compile-checks Python code. Returns pass/fail with error details."""
149
+ def __init__(self):
150
+ super().__init__("compile_bot", "compile")
151
+
152
+ def execute(self, task):
153
+ code = task.get("code", "")
154
+ path = task.get("path", "")
155
+
156
+ if path and os.path.exists(path):
157
+ # Compile existing file
158
+ result = subprocess.run(
159
+ ['python3', '-m', 'py_compile', path],
160
+ capture_output=True, timeout=10
161
+ )
162
+ elif code:
163
+ # Compile string
164
+ test_path = f"/tmp/compile_bot_{hashlib.md5(code[:200].encode()).hexdigest()[:8]}.py"
165
+ with open(test_path, 'w') as f:
166
+ f.write(code)
167
+ result = subprocess.run(
168
+ ['python3', '-m', 'py_compile', test_path],
169
+ capture_output=True, timeout=10
170
+ )
171
+ if os.path.exists(test_path):
172
+ os.remove(test_path)
173
+ else:
174
+ return False, None, "No code or path provided"
175
+
176
+ if result.returncode == 0:
177
+ return True, "COMPILE PASS", None
178
+ else:
179
+ err = result.stderr.decode()[:200]
180
+ return False, "COMPILE FAIL", err
181
+
182
+ class TestNanobot(Nanobot):
183
+ """Runs pytest and returns results."""
184
+ def __init__(self):
185
+ super().__init__("test_bot", "test")
186
+
187
+ def execute(self, task):
188
+ path = task.get("path", "/tmp/fsi_felon")
189
+ try:
190
+ result = subprocess.run(
191
+ ['python3', '-m', 'pytest', path, '-q', '--tb=short'],
192
+ capture_output=True, text=True, timeout=60, cwd=path
193
+ )
194
+ passed = result.returncode == 0
195
+ summary = self._parse_summary(result.stdout + result.stderr)
196
+ return passed, summary, None if passed else result.stderr[:300]
197
+ except Exception as e:
198
+ return False, None, str(e)
199
+
200
+ def _parse_summary(self, output):
201
+ # Extract "X passed, Y failed" or similar
202
+ match = re.search(r'(\d+)\s+passed.*?(\d+)\s+failed', output, re.DOTALL)
203
+ if match:
204
+ return f"{match.group(1)} passed, {match.group(2)} failed"
205
+ if "passed" in output:
206
+ return "All passed"
207
+ return output[:200]
208
+
209
+ class SearchNanobot(Nanobot):
210
+ """Local code search. Greps files, finds patterns. No web, no cloud."""
211
+ def __init__(self):
212
+ super().__init__("search_bot", "search")
213
+
214
+ def execute(self, task):
215
+ query = task.get("query", "")
216
+ path = task.get("path", "/tmp/fsi_felon")
217
+ ext = task.get("ext", ".py")
218
+
219
+ matches = []
220
+ for root, dirs, files in os.walk(path):
221
+ # Skip hidden and cache
222
+ dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__']
223
+ for f in files:
224
+ if f.endswith(ext):
225
+ fpath = os.path.join(root, f)
226
+ try:
227
+ with open(fpath, 'r', errors='ignore') as fh:
228
+ for i, line in enumerate(fh, 1):
229
+ if query.lower() in line.lower():
230
+ matches.append({
231
+ "file": fpath,
232
+ "line": i,
233
+ "content": line.strip()[:100]
234
+ })
235
+ if len(matches) >= 20:
236
+ break
237
+ except:
238
+ pass
239
+ if len(matches) >= 20:
240
+ break
241
+ if len(matches) >= 20:
242
+ break
243
+
244
+ return True, matches, None
245
+
246
+ class ShellNanobot(Nanobot):
247
+ """Runs shell commands. Sandboxed to /tmp/fsi_felon by default."""
248
+ def __init__(self):
249
+ super().__init__("shell_bot", "shell")
250
+
251
+ def execute(self, task):
252
+ cmd = task.get("command", "")
253
+ cwd = task.get("cwd", "/tmp/fsi_felon")
254
+ timeout = task.get("timeout", 30)
255
+
256
+ # Safety: block dangerous commands
257
+ dangerous = ['rm -rf /', 'mkfs', 'dd if=', '>:', 'shutdown', 'reboot']
258
+ for d in dangerous:
259
+ if d in cmd:
260
+ return False, None, f"Blocked dangerous command: {cmd}"
261
+
262
+ try:
263
+ result = subprocess.run(
264
+ cmd, shell=True, capture_output=True, text=True,
265
+ timeout=timeout, cwd=cwd
266
+ )
267
+ return result.returncode == 0, result.stdout, result.stderr
268
+ except Exception as e:
269
+ return False, None, str(e)
270
+
271
+
272
+ # ═══════════════════════════════════════════════════════════════
273
+ # KNOWLEDGE LIBRARY NANOBOT β€” RAG for SE textbooks
274
+ # ═══════════════════════════════════════════════════════════════
275
+ class LibraryNanobot(Nanobot):
276
+ """Query the SE knowledge library. Model learns from results."""
277
+
278
+ def __init__(self):
279
+ super().__init__("library", "se_knowledge_query")
280
+ from knowledge_library import KnowledgeLibrary
281
+ self.lib = KnowledgeLibrary()
282
+ self.lib.load()
283
+
284
+ def execute(self, task):
285
+ query = task.get("query", "")
286
+ top_k = task.get("top_k", 3)
287
+ if not query:
288
+ return False, None, "Missing 'query' parameter"
289
+
290
+ results = self.lib.query(query, top_k=top_k)
291
+ if not results:
292
+ return True, "No matching knowledge found", ""
293
+
294
+ output = []
295
+ for r in results:
296
+ output.append(f"[{r['topic']}] {r['content'][:500]}")
297
+ return True, "\n\n".join(output), ""
298
+
299
+
300
+ class WebSearchNanobot(Nanobot):
301
+ """Controlled web search β€” allowlisted domains only."""
302
+
303
+ ALLOWED_DOMAINS = [
304
+ "docs.python.org",
305
+ "stackoverflow.com",
306
+ "github.com",
307
+ "pypi.org",
308
+ "realpython.com",
309
+ "w3schools.com/python",
310
+ "geeksforgeeks.org/python",
311
+ ]
312
+
313
+ def __init__(self):
314
+ super().__init__("web_search", "controlled_internet_search")
315
+
316
+ def execute(self, task):
317
+ query = task.get("query", "")
318
+ if not query:
319
+ return False, None, "Missing 'query' parameter"
320
+
321
+ import urllib.request, urllib.parse
322
+ results = []
323
+
324
+ for domain in self.ALLOWED_DOMAINS:
325
+ try:
326
+ encoded = urllib.parse.quote(f"site:{domain} python {query}")
327
+ url = f"https://www.google.com/search?q={encoded}"
328
+ results.append(f"[{domain}] Search URL: {url}")
329
+ except Exception as e:
330
+ results.append(f"[{domain}] Error: {e}")
331
+
332
+ return True, "\n".join(results[:5]), ""
333
+
334
+
335
+ # ═══════════════════════════════════════════════════════════════
336
+ # SWARM COORDINATOR
337
+ # ═══════════════════════════════════════════════════════════════
338
+ class SwarmCoordinator:
339
+ """Routes tasks to specialist nanobots. Runs parallel when possible."""
340
+
341
+ def __init__(self):
342
+ self.bots = {
343
+ "fs": FileSystemNanobot(),
344
+ "git": GitNanobot(),
345
+ "compile": CompileNanobot(),
346
+ "test": TestNanobot(),
347
+ "search": SearchNanobot(),
348
+ "shell": ShellNanobot(),
349
+ "library": LibraryNanobot(),
350
+ "web_search": WebSearchNanobot(),
351
+ }
352
+ self.executor = ThreadPoolExecutor(max_workers=8)
353
+ self.history = []
354
+
355
+ def dispatch(self, bot_name, task):
356
+ """Send task to one nanobot. Return result."""
357
+ bot = self.bots.get(bot_name)
358
+ if not bot:
359
+ return False, None, f"Unknown bot: {bot_name}"
360
+
361
+ success, result, error = bot.run(task)
362
+ self.history.append({
363
+ "bot": bot_name,
364
+ "task": task,
365
+ "success": success,
366
+ "time": time.time()
367
+ })
368
+ return success, result, error
369
+
370
+ def dispatch_parallel(self, tasks):
371
+ """
372
+ tasks = [("bot_name", {"action": "...", ...}), ...]
373
+ Returns list of (success, result, error) in same order.
374
+ """
375
+ futures = []
376
+ for bot_name, task in tasks:
377
+ bot = self.bots.get(bot_name)
378
+ if bot:
379
+ future = self.executor.submit(bot.run, task)
380
+ futures.append(future)
381
+ else:
382
+ futures.append(None)
383
+
384
+ results = []
385
+ for future in futures:
386
+ if future is None:
387
+ results.append((False, None, "Unknown bot"))
388
+ else:
389
+ results.append(future.result())
390
+ return results
391
+
392
+ def swarm_status(self):
393
+ """Report on all nanobots."""
394
+ status = {}
395
+ for name, bot in self.bots.items():
396
+ status[name] = {
397
+ "specialty": bot.specialty,
398
+ "calls": bot.calls,
399
+ "errors": bot.errors,
400
+ "health": "HEALTHY" if bot.errors < bot.calls * 0.5 else "DEGRADED" if bot.calls > 0 else "IDLE"
401
+ }
402
+ return status
403
+
404
+ # ═══════════════════════════════════════════════════════════════
405
+ # INTEGRATION WITH FELON
406
+ # ═══════════════════════════════════════════════════════════════
407
+ class FELONSwarmInterface:
408
+ """High-level interface: FELON agent calls this, swarm handles execution."""
409
+
410
+ def __init__(self):
411
+ self.coordinator = SwarmCoordinator()
412
+
413
+ def generate_and_verify(self, prompt, app_type):
414
+ """
415
+ Full pipeline: generate app β†’ compile β†’ test β†’ git commit.
416
+ All via nanobot swarm.
417
+ """
418
+ print(f"\n[SWARM] Generating {app_type}...")
419
+
420
+ # Step 1: Generate (via FELON agent for now, Keli later)
421
+ # This would call agent.generate_app() β€” keeping it simple
422
+ from agent.agent import FelonAgent
423
+ from quantum.engine import QNFREConfig, QNFREEngine
424
+
425
+ agent = FelonAgent(QNFREEngine(QNFREConfig.full_config()))
426
+ result = agent.generate_app(prompt, app_type=app_type)
427
+ project_dir = result.get("project_dir", "")
428
+
429
+ if not project_dir or not os.path.exists(project_dir):
430
+ return False, "Generation failed", None
431
+
432
+ print(f"[SWARM] Project: {project_dir}")
433
+
434
+ # Step 2: Compile check (parallel with test discovery)
435
+ py_files = list(Path(project_dir).rglob("*.py"))
436
+ compile_tasks = [("compile", {"path": str(f)}) for f in py_files[:5]]
437
+ compile_results = self.coordinator.dispatch_parallel(compile_tasks)
438
+
439
+ compile_pass = sum(1 for s, r, e in compile_results if s)
440
+ compile_fail = len(compile_results) - compile_pass
441
+
442
+ print(f"[SWARM] Compile: {compile_pass}/{len(compile_results)} pass")
443
+
444
+ # Step 3: Run tests
445
+ ok, test_result, test_err = self.coordinator.dispatch("test", {"path": project_dir})
446
+ print(f"[SWARM] Tests: {test_result if ok else 'FAIL'}")
447
+
448
+ # Step 4: Git commit if all pass
449
+ if compile_pass == len(compile_results) and ok:
450
+ self.coordinator.dispatch("git", {"action": "add", "files": ['.'], "cwd": project_dir})
451
+ self.coordinator.dispatch("git", {
452
+ "action": "commit",
453
+ "message": f"FSI: {app_type} generated and verified",
454
+ "cwd": project_dir
455
+ })
456
+ print(f"[SWARM] Committed to git")
457
+ return True, result, None
458
+
459
+ return False, result, f"Compile: {compile_fail} fail, Tests: {test_result if ok else test_err}"
460
+
461
+ # ═══════════════════════════════════════════════════════════════
462
+ # CLI INTERFACE
463
+ # ═══════════════════════════════════════════════════════════════
464
+ def main():
465
+ import argparse
466
+ parser = argparse.ArgumentParser(description="FSI Nanobot Tool Swarm")
467
+ parser.add_argument("command", choices=["status", "test", "generate", "search", "git"])
468
+ parser.add_argument("--path", default="/tmp/fsi_felon")
469
+ parser.add_argument("--query", default="")
470
+ parser.add_argument("--app-type", default="web_app")
471
+ parser.add_argument("--prompt", default="build a web app")
472
+ args = parser.parse_args()
473
+
474
+ swarm = SwarmCoordinator()
475
+
476
+ if args.command == "status":
477
+ status = swarm.swarm_status()
478
+ print("\n ╔══════════════════════════════════════╗")
479
+ print(" β•‘ NANOBOT SWARM STATUS β•‘")
480
+ print(" β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n")
481
+ for name, info in status.items():
482
+ health_color = "\x1b[32m" if info["health"] == "HEALTHY" else "\x1b[33m" if info["health"] == "DEGRADED" else "\x1b[90m"
483
+ print(f" {name:12s} | {info['specialty']:15s} | calls: {info['calls']:4d} | errors: {info['errors']:3d} | {health_color}{info['health']}\x1b[0m")
484
+ print()
485
+
486
+ elif args.command == "test":
487
+ ok, result, err = swarm.dispatch("test", {"path": args.path})
488
+ print(f"Tests: {result if ok else err}")
489
+
490
+ elif args.command == "search":
491
+ ok, result, err = swarm.dispatch("search", {"query": args.query, "path": args.path})
492
+ if ok:
493
+ for match in result[:10]:
494
+ print(f" {match['file']}:{match['line']} | {match['content']}")
495
+
496
+ elif args.command == "git":
497
+ ok, result, err = swarm.dispatch("git", {"action": "status", "cwd": args.path})
498
+ print(result if ok else err)
499
+
500
+ elif args.command == "generate":
501
+ interface = FELONSwarmInterface()
502
+ ok, result, err = interface.generate_and_verify(args.prompt, args.app_type)
503
+ if ok:
504
+ print(f"\nβœ“ Generated and verified: {result.get('project_name')}")
505
+ else:
506
+ print(f"\nβœ— Failed: {err}")
507
+
508
+ if __name__ == "__main__":
509
+ main()