import os from fastapi import FastAPI, HTTPException from huggingface_hub import InferenceClient from rdflib import Graph from pydantic import BaseModel import logging # Configurazione logging logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) # Configurazione API Hugging Face API_KEY = os.getenv("HF_API_KEY") client = InferenceClient(api_key=API_KEY) # File RDF RDF_FILE = "Ontologia.rdf" # Carica un riassunto del file RDF def load_rdf_summary(): if os.path.exists(RDF_FILE): try: g = Graph() g.parse(RDF_FILE, format="xml") classes = set() properties = set() for s, _, o in g.triples((None, None, None)): if "Class" in str(o) or "rdfs:Class" in str(o): classes.add(s) if "Property" in str(o): properties.add(s) classes_summary = "\n".join([f"- Classe: {cls}" for cls in classes]) properties_summary = "\n".join([f"- Proprietà: {prop}" for prop in properties]) return f"Classi:\n{classes_summary}\n\nProprietà:\n{properties_summary}" except Exception as e: logger.error(f"Errore durante il parsing del file RDF: {e}") return "Errore nel caricamento del file RDF." return "Nessun dato RDF trovato." rdf_context = load_rdf_summary() logger.info("RDF Summary: %s", rdf_context) # Valida le query SPARQL def validate_sparql_query(query, rdf_file_path): try: g = Graph() g.parse(rdf_file_path, format="xml") g.query(query) # Prova ad eseguire la query return True except Exception as e: logger.error(f"Errore durante la validazione della query SPARQL: {e}") return False # FastAPI app app = FastAPI() # Modello di input per richieste POST class QueryRequest(BaseModel): message: str max_tokens: int = 2048 temperature: float = 0.7 # Messaggio di sistema con RDF incluso def create_system_message(rdf_context): return f""" Sei un assistente esperto nella generazione di query SPARQL basate su ontologie RDF. Ecco un riassunto dell'ontologia su cui devi lavorare: {rdf_context} Il tuo compito: - Genera esclusivamente query SPARQL valide in UNA SOLA RIGA. - Rispondi solo se la domanda è pertinente alle classi e proprietà fornite. - Se non puoi rispondere, di': "Non posso generare una query SPARQL per questa richiesta." """ async def generate_response(message, max_tokens, temperature): system_message = create_system_message(rdf_context) logger.debug("System Message: %s", system_message) logger.info("User Message: %s", message) messages = [ {"role": "system", "content": system_message}, {"role": "user", "content": message} ] try: response = client.chat.completions.create( model="Qwen/Qwen2.5-72B-Instruct", messages=messages, temperature=temperature, max_tokens=max_tokens, top_p=0.7, stream=False, timeout=60 # Aumenta il timeout ) logger.info("Raw Response: %s", response) return response['choices'][0]['message']['content'].replace("\n", " ").strip() except Exception as e: logger.error(f"Errore nell'elaborazione: {str(e)}") raise HTTPException(status_code=500, detail=f"Errore nell'elaborazione: {str(e)}") # Endpoint per generare query SPARQL @app.post("/generate-query/") async def generate_query(request: QueryRequest): response = await generate_response(request.message, request.max_tokens, request.temperature) logger.info("Risposta generata dal modello: %s", response) if not (response.startswith("SELECT") or response.startswith("ASK")): return { "query": None, "explanation": "Non posso generare una query SPARQL per questa richiesta. Assicurati che la domanda sia coerente con i dati RDF forniti." } if not validate_sparql_query(response, RDF_FILE): return { "query": None, "explanation": "La query generata non è valida rispetto alla base di conoscenza RDF. Assicurati di chiedere informazioni che siano presenti nell'ontologia." } return {"query": response, "explanation": "Ecco la query generata correttamente in una riga pronta per GraphDB."} # Endpoint di test @app.get("/") async def root(): return {"message": "Il server è attivo e pronto a generare query SPARQL!"}