import os from datetime import datetime from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse from pydantic import BaseModel from typing import Optional, List from openai import OpenAI # ── App Setup ────────────────────────────────────────────────────────────── app = FastAPI(title="HERMES AGENT", version="1.0.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) # ── Config ───────────────────────────────────────────────────────────────── NVIDIA_API_KEY = os.getenv("OPENAI_API_KEY", "") SUPABASE_URL = os.getenv("SUPABASE_URL", "") SUPABASE_KEY = os.getenv("SUPABASE_KEY", "") HERMES_NAME = os.getenv("HERMES_NAME", "HERMES") NVIDIA_MODEL = os.getenv("NVIDIA_MODEL", "meta/llama-3.1-70b-instruct") HERMES_SYSTEM_PROMPT = os.getenv( "HERMES_SYSTEM_PROMPT", "Eres HERMES, un agente de inteligencia artificial avanzado, leal y preciso. " "Tienes memoria persistente. Respondes en el mismo idioma que el usuario." ) # ── NVIDIA NIM Client (OpenAI-compatible) ────────────────────────────────── client: Optional[OpenAI] = None if NVIDIA_API_KEY: client = OpenAI( base_url="https://integrate.api.nvidia.com/v1", api_key=NVIDIA_API_KEY ) print(f"[HERMES] NVIDIA NIM client initialized. Model: {NVIDIA_MODEL}") else: print("[HERMES] WARNING: OPENAI_API_KEY not set!") # ── Supabase memory ──────────────────────────────────────────────────────── supabase = None if SUPABASE_URL and SUPABASE_KEY: try: from supabase import create_client supabase = create_client(SUPABASE_URL, SUPABASE_KEY) print("[HERMES] Supabase connected.") except Exception as e: print(f"[HERMES] Supabase error: {e}") # In-memory fallback when Supabase not configured _mem_fallback: dict = {} def get_memory(session_id: str, limit: int = 20) -> List[dict]: if supabase: try: r = (supabase.table("hermes_memory") .select("role,content") .eq("session_id", session_id) .order("created_at", desc=False) .limit(limit) .execute()) return r.data or [] except Exception as e: print(f"[Memory] read error: {e}") return _mem_fallback.get(session_id, [])[-limit:] def save_memory(session_id: str, role: str, content: str, user_id: str = "anon"): if supabase: try: supabase.table("hermes_memory").insert({ "session_id": session_id, "user_id": user_id, "role": role, "content": content, "created_at": datetime.utcnow().isoformat() }).execute() return except Exception as e: print(f"[Memory] save error: {e}") # fallback if session_id not in _mem_fallback: _mem_fallback[session_id] = [] _mem_fallback[session_id].append({"role": role, "content": content}) # ── Models ───────────────────────────────────────────────────────────────── class ChatRequest(BaseModel): message: str session_id: str = "default" user_id: str = "anonymous" class ChatResponse(BaseModel): reply: str session_id: str timestamp: str model: str # ── Routes ───────────────────────────────────────────────────────────────── @app.get("/", response_class=HTMLResponse) async def root(): return """ HERMES AGENT

⚡ HERMES AGENT

v1.0 NVIDIA NIM
Listo. Escribe tu mensaje.
""" @app.get("/health") async def health(): return { "status": "ok", "agent": HERMES_NAME, "timestamp": datetime.utcnow().isoformat(), "nvidia_key_set": bool(NVIDIA_API_KEY), "model": NVIDIA_MODEL, "supabase_connected": supabase is not None } @app.post("/chat", response_model=ChatResponse) async def chat(req: ChatRequest): if not client: raise HTTPException( status_code=503, detail="NVIDIA API Key no configurada. Ve a Settings > Variables and secrets > agrega OPENAI_API_KEY con tu nvapi-... key." ) history = get_memory(req.session_id) messages = [{"role": "system", "content": HERMES_SYSTEM_PROMPT}] for entry in history: messages.append({"role": entry["role"], "content": entry["content"]}) messages.append({"role": "user", "content": req.message}) try: response = client.chat.completions.create( model=NVIDIA_MODEL, messages=messages, max_tokens=1024, temperature=0.7, stream=False ) reply = response.choices[0].message.content except Exception as e: raise HTTPException(status_code=500, detail=str(e)) save_memory(req.session_id, "user", req.message, req.user_id) save_memory(req.session_id, "assistant", reply, req.user_id) return ChatResponse( reply=reply, session_id=req.session_id, timestamp=datetime.utcnow().strftime("%H:%M:%S UTC"), model=NVIDIA_MODEL ) @app.get("/sessions/{session_id}/history") async def get_history(session_id: str): history = get_memory(session_id, limit=100) return {"session_id": session_id, "messages": history, "count": len(history)} @app.delete("/sessions/{session_id}") async def clear_session(session_id: str): if supabase: try: supabase.table("hermes_memory").delete().eq("session_id", session_id).execute() except Exception as e: raise HTTPException(status_code=500, detail=str(e)) elif session_id in _mem_fallback: del _mem_fallback[session_id] return {"status": "cleared", "session_id": session_id}