# Jarvis-Cognitive/api/api_server.py import uvicorn from fastapi import FastAPI from pydantic import BaseModel import os import sys import uuid from contextlib import asynccontextmanager # --- MODIFICA CHIAVE: Integrazione con il Kernel di Jarvis --- # 1. Aggiungiamo il percorso principale del progetto al sys.path # Questo permette all'API server di trovare e importare il modulo del Kernel. # Calcoliamo il percorso della root del progetto (due livelli sopra la cartella 'api') project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, project_root) # --- MODIFICA FONDAMENTALE: Caricamento delle API Keys --- # Questo blocco, che avevamo nel vecchio `jarvis.py`, mancava qui. # Carica le variabili dal file `data/.env` (es. GOOGLE_API_KEY) # rendendole disponibili al CognitiveService quando inizializza LangChain. # La sua assenza era la causa dell'errore. from dotenv import load_dotenv env_path = os.path.join(project_root, 'data', '.env') if os.path.exists(env_path): print(f"[API SERVER] Caricamento variabili d'ambiente da: {env_path}") load_dotenv(dotenv_path=env_path) else: print(f"[API SERVER] ATTENZIONE: File .env non trovato. I servizi che richiedono API keys falliranno.") from core.kernel import JarvisKernel # 2. Prepariamo le variabili globali per il Kernel e la configurazione kernel: JarvisKernel = None config_path = os.path.join(project_root, 'config', 'config.yaml') # --- NUOVA FUNZIONE DI LOGGING PER IL DEBUG --- def console_logger(log_data): """Callback per stampare i log del kernel sulla console dell'API.""" message = log_data.get('message', '') print(f"{message}") # --- FINE NUOVA FUNZIONE --- # --- FINE MODIFICA --- # --- Modelli di Dati (Pydantic) --- # Definiscono la "forma" dei dati che la nostra API si aspetta in entrata # e che restituisce in uscita. Questo garantisce validazione e chiarezza. class QueryRequest(BaseModel): """Modello per una richiesta di query dall'interfaccia utente.""" query: str session_id: str = "default" # Opzionale, per future sessioni multiple class QueryResponse(BaseModel): """Modello per una risposta dal backend.""" answer: str request_id: str # --- MODIFICA CHIAVE: Gestione del ciclo di vita con Lifespan --- # Questo è il modo moderno e raccomandato da FastAPI per gestire # le operazioni di avvio e arresto. Risolve i DeprecationWarning. @asynccontextmanager async def lifespan(app: FastAPI): """ Gestore del ciclo di vita. Viene eseguito all'avvio di FastAPI, Inizializza e avvia il Kernel di Jarvis. """ global kernel print("[API SERVER] Evento di avvio: Inizializzazione del Kernel di Jarvis...") # Leggi profilo da variabile d'ambiente (default: 'aurelio') profile_name = os.getenv('AGENT_PROFILE', 'aurelio') kernel = JarvisKernel(config_path=config_path, profile_name=profile_name) # Iscriviamo il nostro logger della console PRIMA di avviare il kernel. # Questo ci permetterà di vedere eventuali errori che si verificano # durante il caricamento e l'avvio dei servizi. kernel.subscribe_to_event("log:new_message", console_logger) kernel.start() print("[API SERVER] Kernel avviato e pronto a ricevere richieste.") yield # L'applicazione è in esecuzione # Codice eseguito all'arresto if kernel: print("[API SERVER] Evento di arresto: Fermo del Kernel di Jarvis...") kernel.stop() print("[API SERVER] Kernel fermato.") # --- Inizializzazione dell'Applicazione FastAPI --- app = FastAPI( title="Jarvis Cognitive API", description="API per interagire con il backend cognitivo di Jarvis.", version="1.0.0", lifespan=lifespan # Registriamo il nuovo gestore del ciclo di vita ) # --- Endpoint dell'API --- @app.get("/", summary="Endpoint di controllo stato") def read_root(): """Endpoint di base per verificare che il server sia attivo e funzionante.""" return {"status": "ok", "message": "Jarvis Cognitive API è attiva."} @app.post("/ask", response_model=QueryResponse, summary="Invia una query a Jarvis") def ask_jarvis(request: QueryRequest) -> QueryResponse: """ Endpoint principale per la comunicazione. Riceve una query, la passa al CognitiveService e restituisce la sua risposta. """ request_id = str(uuid.uuid4()) print(f"[API SERVER] Ricevuta query (ID: {request_id}): '{request.query}'") # --- MODIFICA CHIAVE: Chiamata al servizio cognitivo --- if not kernel: return QueryResponse(answer="ERRORE: Il Kernel non è inizializzato.", request_id=request_id) cognitive_service = kernel.get_service("CognitiveService") if not cognitive_service: return QueryResponse(answer="ERRORE: Il CognitiveService non è attivo nel Kernel.", request_id=request_id) try: # NOTA: Stiamo assumendo che il CognitiveService abbia un metodo 'ask'. # Questo metodo dovrà essere implementato nel file del servizio. answer = cognitive_service.ask(request.query, request.session_id) print(f"[API SERVER] Risposta generata per ID {request_id}") return QueryResponse(answer=answer, request_id=request_id) except Exception as e: print(f"[API SERVER] ERRORE durante l'elaborazione della richiesta {request_id}: {e}") return QueryResponse(answer=f"Si è verificato un errore interno: {e}", request_id=request_id) # --- FINE MODIFICA --- # --- Blocco di Avvio --- if __name__ == "__main__": host = os.getenv('HOST', '127.0.0.1') try: port = int(os.getenv('PORT', '8000')) except ValueError: port = 8000 print(f"[API SERVER] Avvio del server Uvicorn su http://{host}:{port}") # NOTA: uvicorn.run() ora avvia l'app, che a sua volta gestisce il Kernel # tramite gli eventi di startup/shutdown. uvicorn.run("api_server:app", host=host, port=port, reload=False)