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 from core.storage import save_mission, load_recent_missions 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"} # ───────────────────────────── # HISTORY # ───────────────────────────── @app.get("/api/history") async def get_history(): try: missions = load_recent_missions(limit=100) return {"missions": missions} except Exception as e: return JSONResponse({"error": str(e)}, status_code=500) # ───────────────────────────── # DIAGNOSTICS # ───────────────────────────── @app.get("/api/diagnostics") async def diagnostics(): try: import psutil, time cpu_percent = psutil.cpu_percent(interval=0.3) cpu_count = psutil.cpu_count() 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 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 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) 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) started_at = datetime.now().isoformat() plan = await plan_mission(task) # ── Ordenar pipeline: analyst siempre antes que writer ────────────────── delegates = plan.get("delegate", []) PIPELINE_ORDER = ["analyst", "backend_dev", "frontend_dev", "writer", "image_agent"] delegates = sorted(delegates, key=lambda k: PIPELINE_ORDER.index(k) if k in PIPELINE_ORDER else 99) results = [] shared_context = {} for agent_key in delegates: 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 = next((r["file_path"] for r in results if r.get("file_path")), None) file_url = f"/docs/{file_path}" if file_path else None # Serializar results para SQLite (quitar objetos no-JSON) safe_results = [] for r in results: safe_results.append({ "agent": r.get("agent"), "success": r.get("success"), "response": r.get("response", "")[:2000], # limitar tamaño "file_path": r.get("file_path"), "file_type": r.get("file_type"), "image_urls": r.get("image_urls", []), }) save_mission({ "task": task, "started_at": started_at, "ended_at": datetime.now().isoformat(), "plan": plan, "results": safe_results, "doc_file": file_path, "events": [], }) return { "success": True, "task": task, "plan": plan, "results": results, "file_url": file_url }