from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse, JSONResponse from fastapi.staticfiles import StaticFiles from pathlib import Path from datetime import datetime from agents.manager import plan_mission from agents.specialized import run_specialized_agent app = FastAPI(title="Mission Control Aetheris") # ───────────────────────────── # STATIC # ───────────────────────────── templates_dir = Path("templates") docs_dir = Path("data/docs") if docs_dir.exists(): app.mount("/docs", StaticFiles(directory=docs_dir), name="docs") @app.get("/", response_class=HTMLResponse) async def root(): return (templates_dir / "code_new.html").read_text(encoding="utf-8") # ───────────────────────────── # HEALTH # ───────────────────────────── @app.get("/health") async def health(): return {"status": "ok"} # ───────────────────────────── # DIAGNOSTICS # ───────────────────────────── @app.get("/api/diagnostics") async def diagnostics(): try: import psutil, time # CPU cpu_percent = psutil.cpu_percent(interval=0.3) cpu_count = psutil.cpu_count() # RAM ram = psutil.virtual_memory() ram_total_gb = round(ram.total / (1024**3), 2) ram_used_gb = round(ram.used / (1024**3), 2) ram_percent = ram.percent # Disco disk = psutil.disk_usage("/") disk_total_gb = round(disk.total / (1024**3), 2) disk_used_gb = round(disk.used / (1024**3), 2) disk_percent = disk.percent # Red — delta en 0.5s para calcular velocidad actual net1 = psutil.net_io_counters() time.sleep(0.5) net2 = psutil.net_io_counters() net_in_kb = round((net2.bytes_recv - net1.bytes_recv) / 512, 2) # KB/s net_out_kb = round((net2.bytes_sent - net1.bytes_sent) / 512, 2) net_in_total_mb = round(net2.bytes_recv / (1024**2), 2) net_out_total_mb = round(net2.bytes_sent / (1024**2), 2) return { "timestamp": datetime.now().isoformat(), "cpu": { "percent": cpu_percent, "cores": cpu_count, }, "ram": { "percent": ram_percent, "used_gb": ram_used_gb, "total_gb": ram_total_gb, }, "disk": { "percent": disk_percent, "used_gb": disk_used_gb, "total_gb": disk_total_gb, }, "network": { "in_kb_s": net_in_kb, "out_kb_s": net_out_kb, "in_total_mb": net_in_total_mb, "out_total_mb": net_out_total_mb, } } except Exception as e: return JSONResponse({"error": str(e)}, status_code=500) # ───────────────────────────── # MANAGER (CHAT) # ───────────────────────────── @app.post("/api/manager") async def manager_only(request: Request): data = await request.json() task = data.get("task", "").strip() if not task: return JSONResponse({"error": "No task"}, status_code=400) plan = await plan_mission(task) return { "response": plan.get("reasoning"), "delegate": plan.get("delegate"), "raw": plan.get("raw"), "provider": "HuggingFace Router", "model": "Qwen/Qwen2.5-7B-Instruct" } # ───────────────────────────── # MISIÓN COMPLETA # ───────────────────────────── @app.post("/api/mission") async def create_mission(request: Request): data = await request.json() task = data.get("task", "").strip() if not task: return JSONResponse({"error": "No task provided"}, status_code=400) plan = await plan_mission(task) results = [] shared_context = {} for agent_key in plan.get("delegate", []): result = await run_specialized_agent(agent_key, task, context=shared_context) results.append(result) if result.get("success"): shared_context[agent_key] = result.get("response", "") file_path = None for r in results: if r.get("file_path"): file_path = r["file_path"] break file_url = f"/docs/{file_path}" if file_path else None return { "success": True, "task": task, "plan": plan, "results": results, "file_url": file_url }