IA2_model / app.py
AshenClock's picture
Update app.py
89f944e verified
raw
history blame
5.98 kB
import os
import logging
from typing import Optional
from pydantic import BaseModel
from fastapi import FastAPI, HTTPException
from huggingface_hub import InferenceClient
import rdflib
# Configurazione logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("app.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# =========================================
# PARAMETRI BASE
# =========================================
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.")
# Nome del file RDF
RDF_FILE = "Ontologia.rdf"
# Inizializza il client
client = InferenceClient(api_key=API_KEY)
# =========================================
# Carica e Preprocessa l'Ontologia
# =========================================
def load_ontology_as_text(rdf_file: str, max_triples: int = 300) -> str:
"""
Legge l'ontologia dal file RDF e costruisce
una stringa "knowledge_text" con un numero limitato di triple,
per non sforare i limiti di contesto.
max_triples limita quante triple includere.
"""
if not os.path.exists(rdf_file):
return "Nessun file RDF trovato."
g = rdflib.Graph()
try:
g.parse(rdf_file, format="xml")
except Exception as e:
logger.error(f"Errore parsing RDF: {e}")
return "Errore parsing RDF."
# Convertiamo un certo numero di triple in stringa
knowledge_lines = []
count = 0
for s, p, o in g:
# Troncamento se necessario (evita di incollare 10.000 triple)
if count >= max_triples:
break
# Abbreviamo s, p, o se sono lunghi
s_str = str(s)[:200]
p_str = str(p)[:200]
o_str = str(o)[:200]
line = f"- {s_str} | {p_str} | {o_str}"
knowledge_lines.append(line)
count += 1
knowledge_text = "\n".join(knowledge_lines)
return knowledge_text
# Carichiamo e preprocessiamo la knowledge
knowledge_text = load_ontology_as_text(RDF_FILE, max_triples=600) # Aumenta/diminuisci se serve
# =========================================
# Creazione Prompt di Sistema
# =========================================
def create_system_message(knowledge_text: str) -> str:
"""
Crea un prompt di sistema con l'ontologia (o una versione ridotta) inline,
obbligando il modello a cercare le info per la query SPARQL da qui.
"""
# Nota: qui rafforziamo che deve SEMPRE consultare questi triple.
# E deve generare query SPARQL SOLO su questi triple, etc.
system_message = f"""
Sei un assistente per un museo. Qui hai l'estratto dell'ontologia RDF in formato triple:
(EntitaSoggetto, EntitaProprieta, EntitaOggetto).
Devi usare ESCLUSIVAMENTE queste informazioni come knowledge base.
Ecco la knowledge in forma di triple (max 600):
{knowledge_text}
REGOLE TASSATIVE:
1. Se la domanda dell'utente richiede info su entità/opere ecc., DEVI generare una query SPARQL che si basa su questi triple.
2. Se la domanda è chat semplice (saluto, ecc.), puoi rispondere brevemente, ma se serve una knowledge, la cerchi nei triple.
3. Se i triple non contengono l'info, di' che non è disponibile.
4. Il prefisso da usare:
PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/progettoMuseo#>
5. Se hai generato la query e bisogna interpretare i risultati (es. autoreOpera?), puoi fornire la risposta.
Ma l'info deve derivare dai triple sopra.
Attenzione al fatto che le stringhe (es. <nomeOpera> ) possono avere @it o differenze minuscole/maiuscole.
Puoi usare FILTER( STR(?x) = "valore" ) se serve.
Buona fortuna!
"""
return system_message
# =========================================
# Funzione che chiama il modello
# =========================================
async def call_model(messages, temperature=0.7, max_tokens=2048):
logger.info("Chiamata al modello iniziata.")
try:
response = client.chat.completions.create(
model="Qwen/Qwen2.5-72B-Instruct",
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
top_p=0.9, # puoi settare come vuoi
stream=False
)
raw_text = response["choices"][0]["message"]["content"]
logger.debug(f"Risposta del modello: {raw_text}")
# Togli newline
return raw_text.replace("\n", " ").strip()
except Exception as e:
logger.error(f"Errore durante la chiamata al modello: {e}")
raise HTTPException(status_code=500, detail=str(e))
# =========================================
# FASTAPI
# =========================================
from fastapi import FastAPI
app = FastAPI()
class QueryRequest(BaseModel):
message: str
max_tokens: int = 1024
temperature: float = 0.5
@app.post("/generate-response/")
async def generate_response(req: QueryRequest):
user_msg = req.message
logger.info(f"Ricevuta richiesta: {user_msg}")
# 1) Creiamo un system_msg che contiene TUTTI i triple (preprocessati)
system_msg = create_system_message(knowledge_text)
messages = [
{"role": "system", "content": system_msg},
{"role": "user", "content": user_msg}
]
# 2) Chiamiamo il modello
response_text = await call_model(
messages,
temperature=req.temperature,
max_tokens=req.max_tokens
)
logger.info(f"Risposta generata dal modello: {response_text}")
# (Opzionale) Se vuoi, qui potresti controllare se la risposta inizia con "PREFIX base:"
# e se sì, eseguire su un Graph locale la query, interpretarla, ecc.
# Ma in questo esempio, ci fermiamo e inviamo la raw response:
return {"type": "NATURAL", "response": response_text}
@app.get("/")
async def root():
return {"message": "Server con ontologia in prompt di sistema!"}