IA2_model / app.py
AshenClock's picture
Create app.py
ac52c4d verified
raw
history blame
7.46 kB
import os
import logging
from rdflib import Graph
from pydantic import BaseModel
from fastapi import FastAPI, HTTPException
from huggingface_hub import InferenceClient
from typing import Optional
# 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")
if not API_KEY:
logger.error("HF_API_KEY non impostata nell'ambiente.")
raise EnvironmentError("HF_API_KEY non impostata nell'ambiente.")
client = InferenceClient(api_key=API_KEY)
# File RDF
RDF_FILE = "Ontologia.rdf"
# Caricamento RDF (riassunto)
def load_rdf_summary():
"""
Carica un riassunto dell'ontologia dal file RDF.
Estrae le classi e le proprietà presenti nell'ontologia.
"""
if not os.path.exists(RDF_FILE):
logger.error("Nessun file RDF trovato.")
return "Nessun file RDF trovato."
try:
g = Graph()
g.parse(RDF_FILE, format="xml")
# Estrazione semplificata di classi e proprietà
classes = set()
properties = set()
for s, p, o in g.triples((None, None, None)):
if "Class" in str(o):
classes.add(s)
if "Property" in str(o):
properties.add(s)
class_summary = "\n".join([f"- Classe: {cls}" for cls in classes])
prop_summary = "\n".join([f"- Proprietà: {prop}" for prop in properties])
return f"Classi:\n{class_summary}\n\nProprietà:\n{prop_summary}"
except Exception as e:
logger.error(f"Errore durante il parsing del file RDF: {e}")
return "Errore nel caricamento del file RDF."
rdf_context = load_rdf_summary()
logger.info("RDF Summary: %s", rdf_context)
# Validazione SPARQL
def validate_sparql_query(query: str, rdf_file_path: str) -> bool:
"""
Verifica la validità della query SPARQL.
"""
g = Graph()
try:
g.parse(rdf_file_path, format="xml")
g.query(query) # Solleva un'eccezione se la query non è valida
return True
except Exception as e:
logger.error(f"Errore durante la validazione della query SPARQL: {e}")
return False
# Prompt di Sistema
def create_system_message(rdf_context: str) -> str:
return f"""
Sei un assistente esperto nella generazione di query SPARQL basate su un'ontologia RDF, nell'interpretazione dei risultati delle query SPARQL in risposte naturali, e nel fare chatting minimale con i visitatori. In base alla domanda dell'utente, devi decidere se:
1. Generare una query SPARQL per interrogare la base di conoscenza.
2. Fornire una risposta naturale basata sui risultati di una query SPARQL.
3. Rispondere con una risposta di chat minimale.
Ecco un riassunto dell'ontologia su cui devi lavorare:
{rdf_context}
Regole TASSATIVE:
1. Se la domanda richiede una query SPARQL, restituisci la query SPARQL come testo semplice.
2. Se la domanda richiede l'interpretazione di risultati SPARQL, restituisci una risposta naturale basata sui risultati.
3. Se la domanda è una chat minimale, restituisci una risposta di chat.
4. DEVI usare ESCLUSIVAMENTE questo prefisso di base (e NON modificarlo in nessun modo):
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/>
5. NON generare alcun altro prefisso o URI inventato.
6. Se non puoi rispondere con una query SPARQL valida, interpretare i risultati o fare chatting, scrivi:
"Non posso generare una query SPARQL, interpretare i risultati o fare una risposta di chat per questa richiesta."
Esempi:
- Domanda: "Quali sono le statue esposte del periodo medievale?"
Risposta:
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/> SELECT ?statua WHERE { ?statua a base:Statua . ?statua base:Periodo_Storico "Medioevo" . }
- Domanda: "La query ha restituito 5 statue. Puoi descriverle?"
Risposta:
Ecco le 5 statue medievali trovate: Statua1, Statua2, Statua3, Statua4, Statua5.
- Domanda: "Ciao!"
Risposta:
Ciao! Come posso aiutarti oggi?
RISPONDI ESCLUSIVAMENTE CON IL FORMATO SPECIFICATO.
"""
# Funzione per chiamare il modello
async def call_model(messages, temperature=0.7, max_tokens=2048):
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
)
raw_text = response["choices"][0]["message"]["content"]
# Rimuoviamo eventuali newline per forzare la singola riga
return raw_text.replace("\n", " ").strip()
except Exception as e:
logger.error(f"Errore nel modello: {e}")
raise HTTPException(status_code=500, detail=str(e))
# Funzione di Interpretazione dei Risultati SPARQL
def interpret_sparql_results(results):
"""
Trasforma i risultati di una query SPARQL in una risposta naturale.
"""
if not results:
return "Non sono state trovate informazioni corrispondenti alla tua richiesta."
# Esempio semplice: elenca gli elementi trovati
interpreted = "Ecco i risultati trovati: "
items = []
for row in results:
items.append(", ".join([f"{k}: {v}" for k, v in row.asdict().items()]))
interpreted += "; ".join(items) + "."
return interpreted
# FastAPI
app = FastAPI()
class QueryRequest(BaseModel):
message: str
max_tokens: int = 2048
temperature: float = 0.7
@app.post("/generate-response/")
async def generate_response(request: QueryRequest):
user_msg = request.message
# 1) Generazione della risposta (SPARQL, INTERPRET o CHAT)
system_msg = create_system_message(rdf_context)
messages = [
{"role": "system", "content": system_msg},
{"role": "user", "content": user_msg}
]
response_text = await call_model(messages, request.temperature, request.max_tokens)
logger.info(f"Risposta generata dal modello: {response_text}")
# 2) Determinazione se la risposta è una query SPARQL
if response_text.startswith("PREFIX base:"):
sparql_query = response_text
# Validazione della query SPARQL
if validate_sparql_query(sparql_query, RDF_FILE):
# Esegui la query su GraphDB
try:
g = Graph()
g.parse(RDF_FILE, format="xml")
results = g.query(sparql_query)
# Interpreta i risultati in una risposta naturale
interpreted_response = interpret_sparql_results(results)
return {"type": "SPARQL", "response": interpreted_response}
except Exception as e:
logger.error(f"Errore durante l'esecuzione della query SPARQL: {e}")
return {"type": "SPARQL", "response": "Errore nell'esecuzione della query SPARQL."}
else:
return {"type": "SPARQL", "response": "Query SPARQL non valida."}
elif "Non posso generare una query SPARQL" in response_text:
# Risposta di errore dal modello
return {"type": "ERROR", "response": response_text}
else:
# Presumiamo che sia una risposta naturale o una chat
return {"type": "NATURAL", "response": response_text}
@app.get("/")
async def root():
return {"message": "Server attivo e pronto a generare risposte!"}