Download coding_lab_server.py from Chaman1234/cat-v3-coding-agent: direct link, hf CLI and curl.
- Browser
- Download file 48.4 kB
-
https://huggingface.co/Chaman1234/cat-v3-coding-agent/resolve/main/coding_lab_server.py
- Command line
-
hf download hf://Chaman1234/cat-v3-coding-agent/coding_lab_server.py
-
curl -L -o coding_lab_server.py https://huggingface.co/Chaman1234/cat-v3-coding-agent/resolve/main/coding_lab_server.py
48.4 kB
| # coding_lab_server.py | |
| # Antigravity Autonomous Coding Lab Server | |
| # Powered by CAT V3 (Graph-MoE) & Ollama qwen2.5-coder:3b | |
| import os | |
| import sys | |
| import json | |
| import math | |
| import urllib.parse | |
| from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer | |
| from pathlib import Path | |
| import threading | |
| # Ensure workspace root is in python path | |
| sys.path.append(str(Path(__file__).parent)) | |
| try: | |
| import torch | |
| from cat_v3.model import CATV3Model | |
| from agent_executor import OllamaClient, SandboxExecutor, AutonomousCodingAgent | |
| BACKEND_AVAILABLE = True | |
| except ImportError: | |
| BACKEND_AVAILABLE = False | |
| _LOADED_SYSTEM = None | |
| def load_system(): | |
| global _LOADED_SYSTEM | |
| if _LOADED_SYSTEM is not None: | |
| return _LOADED_SYSTEM | |
| if not BACKEND_AVAILABLE: | |
| print("Backend dependencies not available.") | |
| return None | |
| checkpoint_path = "checkpoints/cat_v3/cat_v3_model.pt" | |
| if not os.path.exists(checkpoint_path): | |
| print(f"Checkpoint {checkpoint_path} not found. Run training first.") | |
| return None | |
| try: | |
| device = "cpu" | |
| checkpoint = torch.load(checkpoint_path, map_location=device) | |
| vocab = checkpoint["vocab"] | |
| tokenizer = checkpoint["tokenizer"] | |
| expert_graphs = checkpoint["expert_graphs"] | |
| model = CATV3Model( | |
| num_concepts=vocab.size(), | |
| tokenizer_vocab_size=tokenizer.vocab_size(), | |
| pad_id=tokenizer.pad_id, | |
| eos_id=tokenizer.eos_id, | |
| expert_graphs=expert_graphs, | |
| concept_dim=128, | |
| hidden_size=128, | |
| path_length=8, | |
| top_m=8, | |
| decoder_vocab_size=tokenizer.vocab_size() | |
| ).to(device) | |
| model.load_state_dict(checkpoint["model_state_dict"]) | |
| model.eval() | |
| _LOADED_SYSTEM = { | |
| "model": model, | |
| "vocab": vocab, | |
| "tokenizer": tokenizer, | |
| "expert_graphs": expert_graphs, | |
| "device": device | |
| } | |
| print("CAT V3 model loaded successfully for Coding Lab.") | |
| return _LOADED_SYSTEM | |
| except Exception as e: | |
| print(f"Error loading CAT V3 model: {e}") | |
| return None | |
| def predict_v3_routing(question): | |
| loaded = load_system() | |
| if not loaded: | |
| # Fallback simulated routing if no checkpoint | |
| return { | |
| "reasoning_path": ["data_input", "sort", "syntax", "control_flow"], | |
| "activated_domains": ["coding", "mathematics"], | |
| "domain_probabilities": { | |
| "mechanical": 0.05, "civil": 0.05, "electrical": 0.02, | |
| "physics": 0.08, "mathematics": 0.72, "english": 0.12, "coding": 0.95 | |
| }, | |
| "nodes": [{"name": "syntax", "activation": 0.8}, {"name": "sort", "activation": 0.9}], | |
| "edges": [] | |
| } | |
| model = loaded["model"] | |
| vocab = loaded["vocab"] | |
| tokenizer = loaded["tokenizer"] | |
| device = loaded["device"] | |
| DOMAINS_LIST = ["mechanical", "civil", "electrical", "physics", "mathematics", "english", "coding"] | |
| with torch.no_grad(): | |
| input_ids, attention_mask = tokenizer.encode(question, max_length=32) | |
| input_ids = input_ids.unsqueeze(0).to(device) | |
| attention_mask = attention_mask.unsqueeze(0).to(device) | |
| outputs = model.generate_response( | |
| input_ids=input_ids, | |
| attention_mask=attention_mask, | |
| router_top_k=2, | |
| router_threshold=0.4 | |
| ) | |
| router_probs = outputs["router_probs"][0].cpu().tolist() | |
| active_mask = outputs["router_mask"][0].cpu().tolist() | |
| activated_domains = [DOMAINS_LIST[i] for i, mask in enumerate(active_mask) if mask] | |
| domain_probs = {DOMAINS_LIST[i]: prob for i, prob in enumerate(router_probs)} | |
| fusion_report = model.fusion.get_symbolic_report( | |
| vocab=vocab, | |
| expert_reports=outputs["expert_reports"], | |
| router_mask=outputs["router_mask"], | |
| domain_names=DOMAINS_LIST | |
| )[0] | |
| reasoning_path = [] | |
| if fusion_report["reasoning_paths"]: | |
| reasoning_path = fusion_report["reasoning_paths"][0] | |
| else: | |
| # Fallback path if empty | |
| reasoning_path = ["data_input", "syntax", "control_flow"] | |
| nodes_list = [] | |
| for name in vocab.concepts: | |
| if name not in ("<PAD>", "<EOS>"): | |
| act = 0.95 if name in fusion_report["concepts"] else 0.05 | |
| nodes_list.append({ | |
| "name": name, | |
| "activation": act | |
| }) | |
| edges_list = [] | |
| for d_idx, d_name in enumerate(DOMAINS_LIST): | |
| if active_mask[d_idx]: | |
| expert = model.experts[d_name] | |
| edge_index = expert.edge_index.cpu() | |
| for i in range(edge_index.size(1)): | |
| u = vocab.id_to_concept[edge_index[0, i].item()] | |
| v = vocab.id_to_concept[edge_index[1, i].item()] | |
| if u not in ("<PAD>", "<EOS>") and v not in ("<PAD>", "<EOS>"): | |
| edges_list.append({ | |
| "from": u, | |
| "to": v, | |
| "weight": 1.0 | |
| }) | |
| return { | |
| "reasoning_path": reasoning_path, | |
| "activated_domains": activated_domains, | |
| "domain_probabilities": domain_probs, | |
| "nodes": nodes_list, | |
| "edges": edges_list | |
| } | |
| class CodingLabRequestHandler(BaseHTTPRequestHandler): | |
| def log_message(self, format, *args): | |
| pass | |
| def do_GET(self): | |
| parsed = urllib.parse.urlparse(self.path) | |
| if parsed.path == "/" or parsed.path == "/index.html": | |
| self.send_response(200) | |
| self.send_header("Content-Type", "text/html; charset=utf-8") | |
| self.end_headers() | |
| self.wfile.write(LAB_HTML.encode("utf-8")) | |
| elif parsed.path == "/api/status": | |
| ollama = OllamaClient() | |
| conn_ok, conn_msg = ollama.check_connection() | |
| runtimes = SandboxExecutor.get_supported_runtimes() | |
| status_data = { | |
| "ollama_connected": conn_ok, | |
| "ollama_message": conn_msg, | |
| "runtimes": runtimes, | |
| "cat_v3_loaded": load_system() is not None | |
| } | |
| self.send_response(200) | |
| self.send_header("Content-Type", "application/json") | |
| self.end_headers() | |
| self.wfile.write(json.dumps(status_data).encode("utf-8")) | |
| elif parsed.path == "/api/agent/run": | |
| query_params = urllib.parse.parse_qs(parsed.query) | |
| question = query_params.get("question", [""])[0].strip() | |
| language = query_params.get("language", ["python"])[0].strip().lower() | |
| max_iterations = int(query_params.get("max_iterations", ["3"])[0]) | |
| temperature = float(query_params.get("temperature", ["0.2"])[0]) | |
| try: | |
| if not question: | |
| raise ValueError("Question cannot be empty.") | |
| # Set headers for Server-Sent Events (SSE) | |
| self.send_response(200) | |
| self.send_header("Content-Type", "text/event-stream") | |
| self.send_header("Cache-Control", "no-cache") | |
| self.send_header("Connection", "keep-alive") | |
| self.send_header("Access-Control-Allow-Origin", "*") | |
| self.end_headers() | |
| def send_event(event_type, payload): | |
| event_data = {"event": event_type, "data": payload} | |
| try: | |
| self.wfile.write(f"data: {json.dumps(event_data)}\n\n".encode("utf-8")) | |
| self.wfile.flush() | |
| except Exception: | |
| pass # Connection closed | |
| # Step 1: Run CAT V3 Routing | |
| send_event("status", "Running query through CAT V3 Graph-MoE router...") | |
| routing_res = predict_v3_routing(question) | |
| send_event("routing", { | |
| "reasoning_path": routing_res["reasoning_path"], | |
| "activated_domains": routing_res["activated_domains"], | |
| "domain_probabilities": routing_res["domain_probabilities"], | |
| "nodes": routing_res["nodes"], | |
| "edges": routing_res["edges"] | |
| }) | |
| # Step 2: Initialize Agent | |
| ollama = OllamaClient(model="qwen2.5-coder:3b") | |
| conn_ok, conn_msg = ollama.check_connection() | |
| if not conn_ok: | |
| send_event("error", f"Ollama Error: {conn_msg}") | |
| return | |
| sandbox = SandboxExecutor() | |
| agent = AutonomousCodingAgent(ollama, sandbox) | |
| def agent_callback(msg): | |
| # Direct forwarding of callback logs to SSE client | |
| send_event("agent_log", msg) | |
| # Step 3: Run the loop | |
| run_res = agent.run_agent_loop( | |
| task=question, | |
| language=language, | |
| concept_path=routing_res["reasoning_path"], | |
| max_iterations=max_iterations, | |
| callback=agent_callback | |
| ) | |
| # Step 4: Finished | |
| send_event("done", run_res) | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| try: | |
| self.send_response(500) | |
| self.send_header("Content-Type", "application/json") | |
| self.end_headers() | |
| self.wfile.write(json.dumps({"error": str(e)}).encode("utf-8")) | |
| except Exception: | |
| pass | |
| else: | |
| self.send_response(404) | |
| self.end_headers() | |
| def do_POST(self): | |
| parsed = urllib.parse.urlparse(self.path) | |
| if parsed.path == "/api/agent/run": | |
| content_length = int(self.headers.get("Content-Length", 0)) | |
| body = self.rfile.read(content_length) | |
| try: | |
| data = json.loads(body.decode("utf-8")) | |
| question = data.get("question", "").strip() | |
| language = data.get("language", "python").strip().lower() | |
| max_iterations = int(data.get("max_iterations", 3)) | |
| temperature = float(data.get("temperature", 0.2)) | |
| if not question: | |
| raise ValueError("Question cannot be empty.") | |
| # Set headers for Server-Sent Events (SSE) | |
| self.send_response(200) | |
| self.send_header("Content-Type", "text/event-stream") | |
| self.send_header("Cache-Control", "no-cache") | |
| self.send_header("Connection", "keep-alive") | |
| self.send_header("Access-Control-Allow-Origin", "*") | |
| self.end_headers() | |
| def send_event(event_type, payload): | |
| event_data = {"event": event_type, "data": payload} | |
| try: | |
| self.wfile.write(f"data: {json.dumps(event_data)}\n\n".encode("utf-8")) | |
| self.wfile.flush() | |
| except Exception: | |
| pass # Connection closed | |
| # Step 1: Run CAT V3 Routing | |
| send_event("status", "Running query through CAT V3 Graph-MoE router...") | |
| routing_res = predict_v3_routing(question) | |
| send_event("routing", { | |
| "reasoning_path": routing_res["reasoning_path"], | |
| "activated_domains": routing_res["activated_domains"], | |
| "domain_probabilities": routing_res["domain_probabilities"], | |
| "nodes": routing_res["nodes"], | |
| "edges": routing_res["edges"] | |
| }) | |
| # Step 2: Initialize Agent | |
| ollama = OllamaClient(model="qwen2.5-coder:3b") | |
| conn_ok, conn_msg = ollama.check_connection() | |
| if not conn_ok: | |
| send_event("error", f"Ollama Error: {conn_msg}") | |
| return | |
| sandbox = SandboxExecutor() | |
| agent = AutonomousCodingAgent(ollama, sandbox) | |
| def agent_callback(msg): | |
| # Direct forwarding of callback logs to SSE client | |
| send_event("agent_log", msg) | |
| # Step 3: Run the loop | |
| run_res = agent.run_agent_loop( | |
| task=question, | |
| language=language, | |
| concept_path=routing_res["reasoning_path"], | |
| max_iterations=max_iterations, | |
| callback=agent_callback | |
| ) | |
| # Step 4: Finished | |
| send_event("done", run_res) | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| try: | |
| self.send_response(500) | |
| self.send_header("Content-Type", "application/json") | |
| self.end_headers() | |
| self.wfile.write(json.dumps({"error": str(e)}).encode("utf-8")) | |
| except Exception: | |
| pass | |
| else: | |
| self.send_response(404) | |
| self.end_headers() | |
| # ─── FRONTEND HTML / CSS / JS ─────────────────────────────────────────────── | |
| LAB_HTML = r"""<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Antigravity Autonomous Coding Lab</title> | |
| <meta name="description" content="State of the art multi-language autonomous coding agent visual interface."> | |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=Fira+Code:wght@400;500;600&display=swap" rel="stylesheet"> | |
| <script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script> | |
| <style> | |
| :root { | |
| --bg-deep: #05070a; | |
| --bg-base: #0a0d14; | |
| --bg-surface: #101422; | |
| --bg-elevated: #161c30; | |
| --bg-glass: rgba(10, 13, 20, 0.75); | |
| --border-glow: rgba(99, 102, 241, 0.25); | |
| --border-subtle: rgba(255, 255, 255, 0.06); | |
| --border-medium: rgba(255, 255, 255, 0.15); | |
| --text-primary: #f8fafc; | |
| --text-secondary: #94a3b8; | |
| --text-muted: #475569; | |
| --accent-primary: #6366f1; | |
| --accent-glow: rgba(99, 102, 241, 0.15); | |
| --accent-green: #10b981; | |
| --accent-amber: #f59e0b; | |
| --accent-rose: #f43f5e; | |
| --accent-cyan: #06b6d4; | |
| --font-main: 'Outfit', sans-serif; | |
| --font-code: 'Fira Code', monospace; | |
| --shadow-lg: 0 10px 40px rgba(0, 0, 0, 0.5); | |
| --radius-md: 12px; | |
| --radius-lg: 16px; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| body { | |
| font-family: var(--font-main); | |
| background: var(--bg-deep); | |
| color: var(--text-primary); | |
| height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 20%; | |
| width: 60%; | |
| height: 40%; | |
| background: radial-gradient(ellipse at center, rgba(99, 102, 241, 0.08) 0%, transparent 70%); | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| header { | |
| height: 70px; | |
| background: var(--bg-base); | |
| border-bottom: 1px solid var(--border-subtle); | |
| display: flex; | |
| align-items: center; | |
| justify-content: justify; | |
| padding: 0 2rem; | |
| position: relative; | |
| z-index: 10; | |
| backdrop-filter: blur(10px); | |
| } | |
| .header-left { | |
| display: flex; | |
| align-items: center; | |
| gap: 1rem; | |
| } | |
| .logo { | |
| width: 42px; | |
| height: 42px; | |
| background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%); | |
| border-radius: var(--radius-md); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.3rem; | |
| font-weight: 800; | |
| color: #fff; | |
| box-shadow: 0 0 15px rgba(99,102,241,0.4); | |
| } | |
| .title-group h1 { | |
| font-size: 1.15rem; | |
| font-weight: 700; | |
| letter-spacing: -0.3px; | |
| } | |
| .title-group p { | |
| font-size: 0.75rem; | |
| color: var(--text-secondary); | |
| } | |
| .header-status { | |
| display: flex; | |
| align-items: center; | |
| gap: 1.5rem; | |
| } | |
| .status-pill { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| padding: 0.4rem 0.8rem; | |
| background: rgba(16, 185, 129, 0.08); | |
| border: 1px solid rgba(16, 185, 129, 0.2); | |
| border-radius: 999px; | |
| font-size: 0.72rem; | |
| font-weight: 600; | |
| color: var(--accent-green); | |
| } | |
| .status-dot { | |
| width: 6px; | |
| height: 6px; | |
| background: var(--accent-green); | |
| border-radius: 50%; | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.3; } | |
| } | |
| #main-container { | |
| flex: 1; | |
| display: grid; | |
| grid-template-columns: 360px 1fr 1fr; | |
| overflow: hidden; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| /* Panels layout */ | |
| .panel { | |
| display: flex; | |
| flex-direction: column; | |
| border-right: 1px solid var(--border-subtle); | |
| background: var(--bg-base); | |
| overflow: hidden; | |
| } | |
| .panel:last-child { | |
| border-right: none; | |
| } | |
| .panel-header { | |
| padding: 1.2rem; | |
| border-bottom: 1px solid var(--border-subtle); | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| } | |
| .panel-header h2 { | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| letter-spacing: 0.5px; | |
| text-transform: uppercase; | |
| color: var(--text-secondary); | |
| } | |
| .panel-body { | |
| flex: 1; | |
| padding: 1.5rem; | |
| overflow-y: auto; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1.5rem; | |
| } | |
| /* Controls Form */ | |
| .form-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .form-group label { | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| color: var(--text-secondary); | |
| } | |
| textarea { | |
| width: 100%; | |
| height: 120px; | |
| background: var(--bg-surface); | |
| border: 1px solid var(--border-subtle); | |
| border-radius: var(--radius-md); | |
| padding: 0.8rem; | |
| color: var(--text-primary); | |
| font-family: inherit; | |
| font-size: 0.85rem; | |
| resize: none; | |
| outline: none; | |
| transition: border-color 0.2s; | |
| } | |
| textarea:focus { | |
| border-color: var(--accent-primary); | |
| box-shadow: 0 0 10px rgba(99,102,241,0.1); | |
| } | |
| select { | |
| width: 100%; | |
| background: var(--bg-surface); | |
| border: 1px solid var(--border-subtle); | |
| border-radius: var(--radius-md); | |
| padding: 0.8rem; | |
| color: var(--text-primary); | |
| font-family: inherit; | |
| font-size: 0.85rem; | |
| outline: none; | |
| cursor: pointer; | |
| } | |
| .slider-group { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| font-size: 0.85rem; | |
| } | |
| .slider-group input { | |
| flex: 1; | |
| margin-left: 1rem; | |
| accent-color: var(--accent-primary); | |
| } | |
| .btn-run { | |
| width: 100%; | |
| padding: 1rem; | |
| background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%); | |
| border: none; | |
| border-radius: var(--radius-md); | |
| color: #fff; | |
| font-family: var(--font-main); | |
| font-weight: 700; | |
| font-size: 0.95rem; | |
| cursor: pointer; | |
| transition: transform 0.1s, box-shadow 0.2s; | |
| box-shadow: 0 4px 15px rgba(99,102,241,0.3); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 0.5rem; | |
| } | |
| .btn-run:hover { | |
| box-shadow: 0 6px 20px rgba(99,102,241,0.45); | |
| } | |
| .btn-run:active { | |
| transform: scale(0.98); | |
| } | |
| .btn-run:disabled { | |
| background: var(--bg-elevated); | |
| color: var(--text-secondary); | |
| cursor: not-allowed; | |
| box-shadow: none; | |
| } | |
| /* Compiler check table */ | |
| .system-check-card { | |
| background: var(--bg-surface); | |
| border: 1px solid var(--border-subtle); | |
| border-radius: var(--radius-md); | |
| padding: 1rem; | |
| } | |
| .system-check-card h3 { | |
| font-size: 0.78rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| color: var(--text-secondary); | |
| margin-bottom: 0.8rem; | |
| } | |
| .compiler-row { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| font-size: 0.78rem; | |
| padding: 0.4rem 0; | |
| border-bottom: 1px solid rgba(255,255,255,0.03); | |
| } | |
| .compiler-row:last-child { | |
| border-bottom: none; | |
| } | |
| .badge-ok { | |
| color: var(--accent-green); | |
| font-weight: 600; | |
| } | |
| .badge-warn { | |
| color: var(--accent-amber); | |
| font-weight: 600; | |
| } | |
| /* Visualizer Panel (Middle) */ | |
| #network-canvas { | |
| flex: 1; | |
| background: #07090f; | |
| position: relative; | |
| } | |
| .router-bars { | |
| height: 150px; | |
| border-top: 1px solid var(--border-subtle); | |
| background: var(--bg-surface); | |
| padding: 1rem; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.4rem; | |
| overflow-y: auto; | |
| } | |
| .router-bars h3 { | |
| font-size: 0.75rem; | |
| text-transform: uppercase; | |
| color: var(--text-secondary); | |
| margin-bottom: 0.3rem; | |
| } | |
| .bar-row { | |
| display: flex; | |
| align-items: center; | |
| font-size: 0.7rem; | |
| gap: 0.5rem; | |
| } | |
| .bar-label { | |
| width: 80px; | |
| text-align: right; | |
| font-weight: 600; | |
| } | |
| .bar-container { | |
| flex: 1; | |
| height: 8px; | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 4px; | |
| overflow: hidden; | |
| } | |
| .bar-fill { | |
| height: 100%; | |
| background: var(--accent-primary); | |
| width: 0%; | |
| transition: width 0.4s ease-out; | |
| border-radius: 4px; | |
| } | |
| .bar-val { | |
| width: 35px; | |
| text-align: left; | |
| font-weight: 500; | |
| } | |
| /* Code/Logs Panel (Right) */ | |
| .tabs-header { | |
| display: flex; | |
| border-bottom: 1px solid var(--border-subtle); | |
| background: var(--bg-surface); | |
| } | |
| .tab-btn { | |
| flex: 1; | |
| padding: 1rem 0; | |
| background: none; | |
| border: none; | |
| border-bottom: 2px solid transparent; | |
| color: var(--text-secondary); | |
| font-family: var(--font-main); | |
| font-weight: 600; | |
| font-size: 0.85rem; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .tab-btn:hover { | |
| color: var(--text-primary); | |
| background: rgba(255,255,255,0.02); | |
| } | |
| .tab-btn.active { | |
| color: var(--accent-primary); | |
| border-bottom-color: var(--accent-primary); | |
| background: rgba(99,102,241,0.03); | |
| } | |
| .tab-content { | |
| flex: 1; | |
| display: none; | |
| padding: 1.2rem; | |
| background: #080b12; | |
| overflow: auto; | |
| position: relative; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| /* Logging Terminal */ | |
| .terminal-log { | |
| font-family: var(--font-code); | |
| font-size: 0.78rem; | |
| line-height: 1.5; | |
| color: #d1d5db; | |
| } | |
| .log-entry { | |
| margin-bottom: 0.8rem; | |
| padding-bottom: 0.6rem; | |
| border-bottom: 1px solid rgba(255,255,255,0.02); | |
| } | |
| .log-time { | |
| color: var(--text-muted); | |
| margin-right: 0.5rem; | |
| } | |
| .log-tag { | |
| font-weight: 700; | |
| padding: 0.1rem 0.4rem; | |
| border-radius: 4px; | |
| font-size: 0.68rem; | |
| margin-right: 0.5rem; | |
| } | |
| .tag-status { background: rgba(99,102,241,0.15); color: var(--accent-primary); } | |
| .tag-thought { background: rgba(6,182,212,0.15); color: var(--accent-cyan); } | |
| .tag-code { background: rgba(245,158,11,0.15); color: var(--accent-amber); } | |
| .tag-execution { background: rgba(16,185,129,0.15); color: var(--accent-green); } | |
| .tag-error { background: rgba(244,63,94,0.15); color: var(--accent-rose); } | |
| pre { | |
| background: rgba(0,0,0,0.3); | |
| border: 1px solid var(--border-subtle); | |
| border-radius: 6px; | |
| padding: 0.8rem; | |
| margin-top: 0.4rem; | |
| overflow-x: auto; | |
| font-family: var(--font-code); | |
| color: #f1f5f9; | |
| } | |
| .code-container { | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| } | |
| .code-pre { | |
| flex: 1; | |
| background: #020408; | |
| border: 1px solid var(--border-subtle); | |
| border-radius: 8px; | |
| padding: 1.2rem; | |
| font-family: var(--font-code); | |
| font-size: 0.85rem; | |
| color: #e2e8f0; | |
| white-space: pre-wrap; | |
| overflow-y: auto; | |
| position: relative; | |
| } | |
| .copy-btn { | |
| position: absolute; | |
| top: 1rem; | |
| right: 1rem; | |
| padding: 0.4rem 0.8rem; | |
| background: var(--bg-elevated); | |
| border: 1px solid var(--border-subtle); | |
| border-radius: 4px; | |
| color: var(--text-primary); | |
| font-family: var(--font-main); | |
| font-size: 0.72rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .copy-btn:hover { | |
| background: var(--accent-primary); | |
| border-color: var(--accent-primary); | |
| } | |
| /* Spinner */ | |
| .spinner { | |
| width: 16px; | |
| height: 16px; | |
| border: 2px solid rgba(255,255,255,0.3); | |
| border-top-color: #fff; | |
| border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| display: inline-block; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="header-left"> | |
| <div class="logo">Ag</div> | |
| <div class="title-group"> | |
| <h1>Antigravity Coding Lab</h1> | |
| <p>CAT V3 (Graph-MoE) + Ollama qwen2.5-coder:3b Autonomous Agent Loop</p> | |
| </div> | |
| </div> | |
| <div class="header-status"> | |
| <div class="status-pill" id="ollama-status"> | |
| <div class="status-dot"></div> | |
| Ollama: Checking... | |
| </div> | |
| <div class="status-pill" id="cat-status" style="background: rgba(99,102,241,0.08); border-color: rgba(99,102,241,0.2); color: var(--accent-primary);"> | |
| <div class="status-dot" style="background: var(--accent-primary);"></div> | |
| Model: Checking... | |
| </div> | |
| </div> | |
| </header> | |
| <div id="main-container"> | |
| <!-- Controls Panel --> | |
| <div class="panel"> | |
| <div class="panel-header"> | |
| <h2>Configuration</h2> | |
| </div> | |
| <div class="panel-body"> | |
| <div class="form-group"> | |
| <label for="prompt-input">Coding Task</label> | |
| <textarea id="prompt-input" placeholder="e.g. Write a script to calculate fibonacci values recursively."></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label for="lang-select">Programming Language</label> | |
| <select id="lang-select"> | |
| <option value="python">Python</option> | |
| <option value="javascript">JavaScript (Node.js)</option> | |
| <option value="cpp">C++</option> | |
| <option value="go">Go</option> | |
| <option value="sql">SQL (SQLite3)</option> | |
| <option value="html">HTML/CSS (Validator)</option> | |
| <option value="java">Java</option> | |
| <option value="rust">Rust</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <div class="slider-group"> | |
| <label for="temp-slider">Temperature</label> | |
| <span id="temp-val">0.2</span> | |
| </div> | |
| <input type="range" id="temp-slider" min="0.1" max="1.0" step="0.05" value="0.2" oninput="document.getElementById('temp-val').innerText = this.value"> | |
| </div> | |
| <div class="form-group"> | |
| <div class="slider-group"> | |
| <label for="iter-slider">Max Debug Retries</label> | |
| <span id="iter-val">3</span> | |
| </div> | |
| <input type="range" id="iter-slider" min="1" max="5" step="1" value="3" oninput="document.getElementById('iter-val').innerText = this.value"> | |
| </div> | |
| <button class="btn-run" id="btn-execute"> | |
| <span>Run Coding Agent</span> | |
| </button> | |
| <div class="system-check-card"> | |
| <h3>Runtime Diagnostics</h3> | |
| <div id="diagnostics-list"> | |
| <!-- Filled by JS --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Visualizer Panel --> | |
| <div class="panel"> | |
| <div class="panel-header"> | |
| <h2>Concept Planning Graph (CAT V3)</h2> | |
| </div> | |
| <div id="network-canvas"></div> | |
| <div class="router-bars"> | |
| <h3>MoE Routing Probabilities</h3> | |
| <div id="routing-bars-list"> | |
| <!-- Bars filled dynamically --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Outputs Panel --> | |
| <div class="panel"> | |
| <div class="tabs-header"> | |
| <button class="tab-btn active" onclick="switchTab('trace')">Execution Trace</button> | |
| <button class="tab-btn" onclick="switchTab('code')">Generated Code</button> | |
| <button class="tab-btn" onclick="switchTab('stdout')">Stdout Output</button> | |
| </div> | |
| <!-- Trace content --> | |
| <div class="tab-content active" id="tab-trace"> | |
| <div class="terminal-log" id="log-container"> | |
| <div class="log-entry"><span class="log-time">00:00:00</span><span class="log-tag tag-status">System</span>Agent ready. Enter task details and click run.</div> | |
| </div> | |
| </div> | |
| <!-- Code Content --> | |
| <div class="tab-content" id="tab-code"> | |
| <div class="code-container"> | |
| <div class="code-pre" id="code-output"> | |
| <button class="copy-btn" onclick="copyGeneratedCode()">Copy</button> | |
| <code id="code-content">// Code will be shown here...</code> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Output Content --> | |
| <div class="tab-content" id="tab-stdout"> | |
| <pre id="stdout-output">Execution stdout/stderr output will be displayed here...</pre> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let network = null; | |
| let activeEventSource = null; | |
| // Initialize Diagnostics and Status | |
| async function fetchStatus() { | |
| try { | |
| const response = await fetch('/api/status'); | |
| const data = await response.json(); | |
| // Update Ollama status badge | |
| const ollamaPill = document.getElementById('ollama-status'); | |
| if (data.ollama_connected) { | |
| ollamaPill.innerHTML = '<div class="status-dot"></div>Ollama: Online'; | |
| ollamaPill.style.background = 'rgba(16, 185, 129, 0.08)'; | |
| ollamaPill.style.color = 'var(--accent-green)'; | |
| } else { | |
| ollamaPill.innerHTML = '<div class="status-dot" style="background: var(--accent-rose);"></div>Ollama: Offline'; | |
| ollamaPill.style.background = 'rgba(244, 63, 94, 0.08)'; | |
| ollamaPill.style.color = 'var(--accent-rose)'; | |
| } | |
| // Update CAT V3 badge | |
| const catPill = document.getElementById('cat-status'); | |
| if (data.cat_v3_loaded) { | |
| catPill.innerHTML = '<div class="status-dot"></div>CAT V3: Ready'; | |
| catPill.style.background = 'rgba(99, 102, 241, 0.08)'; | |
| catPill.style.color = 'var(--accent-primary)'; | |
| } else { | |
| catPill.innerHTML = '<div class="status-dot" style="background: var(--accent-amber);"></div>CAT V3: Simulated'; | |
| catPill.style.background = 'rgba(245, 158, 11, 0.08)'; | |
| catPill.style.color = 'var(--accent-amber)'; | |
| } | |
| // Fill compiler rows | |
| const diagList = document.getElementById('diagnostics-list'); | |
| diagList.innerHTML = ''; | |
| for (const [lang, ok] of Object.entries(data.runtimes)) { | |
| const row = document.createElement('div'); | |
| row.className = 'compiler-row'; | |
| row.innerHTML = ` | |
| <span>${lang.toUpperCase()}</span> | |
| <span class="${ok ? 'badge-ok' : 'badge-warn'}">${ok ? 'Executable' : 'Simulated Check'}</span> | |
| `; | |
| diagList.appendChild(row); | |
| } | |
| } catch (e) { | |
| console.error("Failed to load server status: ", e); | |
| } | |
| } | |
| // Tab Switching | |
| function switchTab(tabId) { | |
| document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); | |
| document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); | |
| const idx = ['trace', 'code', 'stdout'].indexOf(tabId); | |
| document.querySelectorAll('.tab-btn')[idx].classList.add('active'); | |
| document.getElementById('tab-' + tabId).classList.add('active'); | |
| } | |
| // Log entry helper | |
| function appendLog(tag, msg, detail = null) { | |
| const container = document.getElementById('log-container'); | |
| const entry = document.createElement('div'); | |
| entry.className = 'log-entry'; | |
| const timeStr = new Date().toTimeString().split(' ')[0]; | |
| let tagClass = 'tag-status'; | |
| if (tag === 'Thought') tagClass = 'tag-thought'; | |
| if (tag === 'Code') tagClass = 'tag-code'; | |
| if (tag === 'Execution') tagClass = 'tag-execution'; | |
| if (tag === 'Error') tagClass = 'tag-error'; | |
| entry.innerHTML = ` | |
| <span class="log-time">${timeStr}</span> | |
| <span class="log-tag ${tagClass}">${tag}</span> | |
| <span>${msg}</span> | |
| `; | |
| if (detail) { | |
| const pre = document.createElement('pre'); | |
| pre.innerText = detail; | |
| entry.appendChild(pre); | |
| } | |
| container.appendChild(entry); | |
| container.scrollTop = container.scrollHeight; | |
| } | |
| function copyGeneratedCode() { | |
| const codeText = document.getElementById('code-content').innerText; | |
| navigator.clipboard.writeText(codeText); | |
| const btn = document.querySelector('.copy-btn'); | |
| btn.innerText = 'Copied!'; | |
| setTimeout(() => btn.innerText = 'Copy', 2000); | |
| } | |
| // Draw Concept Network Graph | |
| function drawGraph(nodesData, edgesData, highlightPath = []) { | |
| const container = document.getElementById('network-canvas'); | |
| // Build Network Nodes | |
| const nodes = new vis.DataSet( | |
| nodesData.map(n => { | |
| const isHighlighted = highlightPath.includes(n.name); | |
| return { | |
| id: n.name, | |
| label: n.name.replace('_', ' '), | |
| color: { | |
| background: isHighlighted ? '#6366f1' : '#161c30', | |
| border: isHighlighted ? '#a5b4fc' : '#334155', | |
| highlight: { background: '#6366f1', border: '#a5b4fc' } | |
| }, | |
| font: { color: '#f8fafc', size: 12, face: 'Outfit' }, | |
| shape: 'box', | |
| borderWidth: isHighlighted ? 2.5 : 1, | |
| margin: 10 | |
| }; | |
| }) | |
| ); | |
| // Deduplicate Edges | |
| const edgeMap = {}; | |
| edgesData.forEach(e => { | |
| const key = `${e.from}->${e.to}`; | |
| edgeMap[key] = e; | |
| }); | |
| // Build Network Edges | |
| const edges = new vis.DataSet( | |
| Object.values(edgeMap).map(e => { | |
| let isHighlighted = false; | |
| for (let i = 0; i < highlightPath.length - 1; i++) { | |
| if (highlightPath[i] === e.from && highlightPath[i+1] === e.to) { | |
| isHighlighted = true; | |
| break; | |
| } | |
| } | |
| return { | |
| from: e.from, | |
| to: e.to, | |
| arrows: 'to', | |
| color: { | |
| color: isHighlighted ? '#10b981' : '#334155', | |
| highlight: isHighlighted ? '#10b981' : '#475569' | |
| }, | |
| width: isHighlighted ? 3 : 1, | |
| smooth: { type: 'curvedCW', roundness: 0.15 } | |
| }; | |
| }) | |
| ); | |
| const data = { nodes, edges }; | |
| const options = { | |
| physics: { | |
| stabilization: true, | |
| barnesHut: { gravitationalConstant: -2000, springLength: 150 } | |
| } | |
| }; | |
| network = new vis.Network(container, data, options); | |
| } | |
| // Run Routing Bars animation | |
| function updateRoutingBars(probs) { | |
| const list = document.getElementById('routing-bars-list'); | |
| list.innerHTML = ''; | |
| const sorted = Object.entries(probs).sort((a,b) => b[1] - a[1]); | |
| for (const [dom, p] of sorted) { | |
| const pct = Math.round(p * 100); | |
| const row = document.createElement('div'); | |
| row.className = 'bar-row'; | |
| // Highlight active experts | |
| const isActive = p >= 0.4; | |
| const fillStyle = isActive ? 'background: linear-gradient(90deg, #6366f1, #8b5cf6)' : 'background: #475569'; | |
| row.innerHTML = ` | |
| <div class="bar-label" style="color: ${isActive ? 'var(--text-primary)' : 'var(--text-secondary)'}">${dom}</div> | |
| <div class="bar-container"> | |
| <div class="bar-fill" style="width: ${pct}%; ${fillStyle}"></div> | |
| </div> | |
| <div class="bar-val" style="color: ${isActive ? 'var(--accent-indigo-light)' : 'var(--text-secondary)'}">${pct}%</div> | |
| `; | |
| list.appendChild(row); | |
| } | |
| } | |
| // Execute Agent | |
| document.getElementById('btn-execute').addEventListener('click', async () => { | |
| const question = document.getElementById('prompt-input').value.trim(); | |
| const language = document.getElementById('lang-select').value; | |
| const maxIterations = document.getElementById('iter-slider').value; | |
| const temperature = document.getElementById('temp-slider').value; | |
| if (!question) { | |
| alert("Please enter a coding task first!"); | |
| return; | |
| } | |
| const btn = document.getElementById('btn-execute'); | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="spinner"></span> <span>Running Agent...</span>'; | |
| // Clear visual logs | |
| document.getElementById('log-container').innerHTML = ''; | |
| document.getElementById('code-content').innerText = '// Initializing generation...'; | |
| document.getElementById('stdout-output').innerText = 'Awaiting run output...'; | |
| switchTab('trace'); | |
| appendLog('System', `Initiating autonomous execution loop for language: ${language.toUpperCase()}`); | |
| if (activeEventSource) { | |
| activeEventSource.close(); | |
| } | |
| // Start SSE request | |
| const eventUrl = `/api/agent/run?` + new URLSearchParams({ | |
| question: question, | |
| language: language, | |
| max_iterations: maxIterations, | |
| temperature: temperature | |
| }).toString(); | |
| activeEventSource = new EventSource(eventUrl); | |
| activeEventSource.onmessage = (event) => { | |
| const res = JSON.parse(event.data); | |
| const type = res.event; | |
| const payload = res.data; | |
| if (type === 'status') { | |
| appendLog('System', payload); | |
| } else if (type === 'thought') { | |
| appendLog('Thought', payload); | |
| } else if (type === 'routing') { | |
| // Update VIS Network and MoE Routing Bars | |
| updateRoutingBars(payload.domain_probabilities); | |
| drawGraph(payload.nodes, payload.edges, payload.reasoning_path); | |
| appendLog('System', `CAT V3 Router selected experts: [${payload.activated_domains.join(', ')}]. Path predicted: ${payload.reasoning_path.join(' -> ')}`); | |
| } else if (type === 'agent_log') { | |
| const logType = payload.type; | |
| if (logType === 'status') { | |
| appendLog('System', `[Iter ${payload.iteration}] ${payload.message}`); | |
| } else if (logType === 'thought') { | |
| appendLog('Thought', `[Iter ${payload.iteration}] ${payload.message}`); | |
| } else if (logType === 'code') { | |
| appendLog('Code', `[Iter ${payload.iteration}] Generated code solution.`, payload.explanation); | |
| document.getElementById('code-content').innerText = payload.code; | |
| } else if (logType === 'execution') { | |
| let execDetail = `Exit Code: ${payload.exit_code}\n`; | |
| if (payload.stdout.trim()) execDetail += `\nSTDOUT:\n${payload.stdout}\n`; | |
| if (payload.stderr.trim()) execDetail += `\nSTDERR:\n${payload.stderr}\n`; | |
| appendLog('Execution', `[Iter ${payload.iteration}] Executed sandbox run (Simulated: ${payload.is_simulated})`, execDetail); | |
| } else if (logType === 'error') { | |
| appendLog('Error', `[Iter ${payload.iteration}] ${payload.message}`); | |
| } | |
| } else if (type === 'done') { | |
| appendLog('System', `Autonomous loop finished. Success: ${payload.success} in ${payload.iterations} iterations.`); | |
| // Display final output in Stdout tab | |
| let stdoutText = `Status: ${payload.success ? 'Success' : 'Failure'}\n`; | |
| stdoutText += `Iterations run: ${payload.iterations}\n`; | |
| stdoutText += `Exit Code: ${payload.exit_code}\n\n`; | |
| stdoutText += `=== STDOUT ===\n${payload.stdout}\n\n`; | |
| stdoutText += `=== STDERR ===\n${payload.stderr}\n`; | |
| document.getElementById('stdout-output').innerText = stdoutText; | |
| document.getElementById('code-content').innerText = payload.code; | |
| // Switch tab to code if success, otherwise stdout | |
| if (payload.success) { | |
| switchTab('code'); | |
| } else { | |
| switchTab('stdout'); | |
| } | |
| btn.disabled = false; | |
| btn.innerHTML = '<span>Run Coding Agent</span>'; | |
| activeEventSource.close(); | |
| } else if (type === 'error') { | |
| appendLog('Error', payload); | |
| btn.disabled = false; | |
| btn.innerHTML = '<span>Run Coding Agent</span>'; | |
| activeEventSource.close(); | |
| } | |
| }; | |
| activeEventSource.onerror = (e) => { | |
| appendLog('Error', "Connection to server EventStream closed unexpectedly."); | |
| btn.disabled = false; | |
| btn.innerHTML = '<span>Run Coding Agent</span>'; | |
| if (activeEventSource) activeEventSource.close(); | |
| }; | |
| }); | |
| // Load baseline status on page show | |
| fetchStatus(); | |
| // Poll status every 10 seconds | |
| setInterval(fetchStatus, 10000); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def run(port=8002): | |
| server_address = ('', port) | |
| httpd = ThreadingHTTPServer(server_address, CodingLabRequestHandler) | |
| print(f"Starting Antigravity Autonomous Coding Lab server on port {port}...") | |
| try: | |
| httpd.serve_forever() | |
| except KeyboardInterrupt: | |
| print("\nShutting down server.") | |
| httpd.server_close() | |
| if __name__ == "__main__": | |
| port = 8002 | |
| if len(sys.argv) > 1: | |
| try: | |
| port = int(sys.argv[1]) | |
| except ValueError: | |
| pass | |
| run(port) | |