AshenClock commited on
Commit
70bec3c
·
verified ·
1 Parent(s): e623027

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -100
app.py CHANGED
@@ -7,95 +7,59 @@ from pydantic import BaseModel
7
  from fastapi import FastAPI, HTTPException
8
  import logging
9
 
10
- # Configurazione logging
11
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
12
  logger = logging.getLogger(__name__)
13
 
14
- # Configurazione API Hugging Face
15
  API_KEY = os.getenv("HF_API_KEY")
16
  client = InferenceClient(api_key=API_KEY)
17
 
18
- # File RDF
19
  RDF_FILE = "Ontologia.rdf"
20
 
21
- # Carica un riassunto del file RDF
22
- def load_rdf_summary():
23
- if os.path.exists(RDF_FILE):
24
- try:
25
- g = Graph()
26
- g.parse(RDF_FILE, format="xml")
27
-
28
- classes = set()
29
- properties = set()
30
-
31
- for s, _, o in g.triples((None, None, None)):
32
- # Si fa un controllo basilare per capire se l'oggetto
33
- # è una definizione di classe o proprietà
34
- # In RDF/OWL reali si userebbero approcci più rigorosi (rdfs:Class, rdf:Property, etc.)
35
- if "Class" in str(o) or "rdfs:Class" in str(o):
36
- classes.add(s)
37
- if "Property" in str(o):
38
- properties.add(s)
39
-
40
- classes_summary = "\n".join([f"- Classe: {cls}" for cls in classes])
41
- properties_summary = "\n".join([f"- Proprietà: {prop}" for prop in properties])
42
- return f"Classi:\n{classes_summary}\n\nProprietà:\n{properties_summary}"
43
- except Exception as e:
44
- logger.error(f"Errore durante il parsing del file RDF: {e}")
45
- return "Errore nel caricamento del file RDF."
46
- return "Nessun dato RDF trovato."
47
-
48
- rdf_context = load_rdf_summary()
49
- logger.info("RDF Summary: %s", rdf_context)
50
-
51
- # Valida le query SPARQL
52
- def validate_sparql_query(query, rdf_file_path):
53
  try:
54
- g = Graph()
55
  g.parse(rdf_file_path, format="xml")
56
- g.query(query) # Prova ad eseguire la query
57
  return True
58
  except Exception as e:
59
  logger.error(f"Errore durante la validazione della query SPARQL: {e}")
60
  return False
61
 
62
- # FastAPI app
63
- app = FastAPI()
64
-
65
- # Modello di input per richieste POST
66
- class QueryRequest(BaseModel):
67
- message: str
68
- max_tokens: int = 2048
69
- temperature: float = 0.7
70
-
71
- # Messaggio di sistema con RDF incluso
72
- def create_system_message(rdf_context):
73
  return f"""
74
- Sei un assistente esperto nella generazione di query SPARQL basate su ontologie RDF.
75
  Ecco un riassunto dell'ontologia su cui devi lavorare:
76
  {rdf_context}
77
 
78
- Il tuo compito:
79
- - Genera esclusivamente query SPARQL valide in UNA SOLA RIGA.
80
- - Usa SEMPRE il prefisso:
81
- PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/2024/11/untitled-ontology-39/>
82
- e fai seguire la SELECT sulla STESSA riga, per esempio:
83
- PREFIX base: <...> SELECT ...
84
- - Usa i nomi abbreviati con 'base:' invece di URI complete. Esempio: base:Stanza, base:Contiene, ecc.
85
- - Rispondi solo se la domanda è pertinente alle classi e proprietà fornite.
86
- - Se non puoi rispondere, di': "Non posso generare una query SPARQL per questa richiesta."
 
 
 
 
87
  """
88
 
89
- async def generate_response(message, max_tokens, temperature):
90
- system_message = create_system_message(rdf_context)
91
- logger.debug("System Message: %s", system_message)
92
- logger.info("User Message: %s", message)
 
93
 
94
- messages = [
95
- {"role": "system", "content": system_message},
96
- {"role": "user", "content": message}
97
- ]
98
 
 
 
99
  try:
100
  response = client.chat.completions.create(
101
  model="Qwen/Qwen2.5-72B-Instruct",
@@ -105,43 +69,68 @@ async def generate_response(message, max_tokens, temperature):
105
  top_p=0.7,
106
  stream=False
107
  )
108
- logger.info("Raw Response: %s", response)
109
- # Rimuoviamo eventuali a-capo così da avere una singola riga
110
- return response['choices'][0]['message']['content'].replace("\n", " ").strip()
111
  except Exception as e:
112
- logger.error(f"Errore nell'elaborazione: {str(e)}")
113
- raise HTTPException(status_code=500, detail=f"Errore nell'elaborazione: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
- # Endpoint per generare query SPARQL
116
  @app.post("/generate-query/")
117
  async def generate_query(request: QueryRequest):
118
- response = await generate_response(request.message, request.max_tokens, request.temperature)
119
- logger.info("Risposta generata dal modello: %s", response)
120
-
121
- # Controllo basilare: la query deve iniziare con 'PREFIX' (come da istruzioni)
122
- # e successivamente con 'SELECT' o 'ASK'
123
- if not response.startswith("PREFIX"):
124
- return {
125
- "query": None,
126
- "explanation": "Non posso generare una query SPARQL per questa richiesta (manca il PREFIX)."
127
- }
128
-
129
- # Potresti anche voler controllare se c'è 'SELECT' o 'ASK' dopo 'PREFIX ...'
130
- if "SELECT" not in response and "ASK" not in response:
131
- return {
132
- "query": None,
133
- "explanation": "Non posso generare una query SPARQL per questa richiesta (manca SELECT/ASK)."
134
- }
135
-
136
- if not validate_sparql_query(response, RDF_FILE):
137
- return {
138
- "query": None,
139
- "explanation": "La query generata non è valida rispetto alla base di conoscenza RDF. Assicurati di chiedere informazioni che siano presenti nell'ontologia."
140
- }
141
-
142
- return {"query": response, "explanation": "Ecco la query generata correttamente in una riga pronta per GraphDB."}
143
-
144
- # Endpoint di test
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  @app.get("/")
146
  async def root():
147
- return {"message": "Il server è attivo e pronto a generare query SPARQL!"}
 
 
7
  from fastapi import FastAPI, HTTPException
8
  import logging
9
 
 
10
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
11
  logger = logging.getLogger(__name__)
12
 
 
13
  API_KEY = os.getenv("HF_API_KEY")
14
  client = InferenceClient(api_key=API_KEY)
15
 
 
16
  RDF_FILE = "Ontologia.rdf"
17
 
18
+ # Funzione di validazione
19
+ def validate_sparql_query(query: str, rdf_file_path: str) -> bool:
20
+ g = Graph()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  try:
 
22
  g.parse(rdf_file_path, format="xml")
23
+ g.query(query) # Se c'è un errore di sintassi o referenza, solleva eccezione
24
  return True
25
  except Exception as e:
26
  logger.error(f"Errore durante la validazione della query SPARQL: {e}")
27
  return False
28
 
29
+ # Prompt di sistema "stringente"
30
+ def create_system_message(rdf_context: str) -> str:
 
 
 
 
 
 
 
 
 
31
  return f"""
32
+ Sei un assistente esperto nella generazione di query SPARQL basate su un'ontologia RDF.
33
  Ecco un riassunto dell'ontologia su cui devi lavorare:
34
  {rdf_context}
35
 
36
+ Regole tassative:
37
+ 1. La query DEV'ESSERE in una sola riga.
38
+ 2. Usa SEMPRE il prefisso base: se l'ontologia è <http://example.org/onto#>, dovrai scrivere:
39
+ PREFIX base: <http://example.org/onto#> SELECT ...
40
+ TUTTO su un'unica riga, senza andare a capo.
41
+ 3. Se vuoi filtrare per classe, usa: ?entity a base:NomeClasse .
42
+ 4. Se vuoi usare una proprietà, usa: ?entity base:NomeProprieta ?altra .
43
+ 5. NON usare URI lunghe senza parentesi angolate e non inventare prefissi.
44
+ 6. Se la query non rispetta la sintassi SPARQL, verrà rifiutata.
45
+
46
+ OBIETTIVO:
47
+ - Genera una query SPARQL sintatticamente e semanticamente valida rispetto alle classi e proprietà riportate.
48
+ - Se non puoi rispondere, dì: "Non posso generare una query SPARQL per questa richiesta."
49
  """
50
 
51
+ # Prompt di sistema "correttivo" per la seconda iterazione
52
+ def create_correction_message(rdf_context: str, errore: str) -> str:
53
+ return f"""
54
+ La query che hai fornito è risultata NON valida:
55
+ {errore}
56
 
57
+ Ricorda le regole tassative e ritenta la generazione di UNA SOLA query SPARQL valida in una riga.
58
+ Riproponila corretta, sempre rispettando il prefisso base: e la sintassi di SELECT/ASK.
59
+ """
 
60
 
61
+ # Funzione per chiamare il modello su Hugging Face
62
+ async def call_model(messages, temperature=0.7, max_tokens=2048):
63
  try:
64
  response = client.chat.completions.create(
65
  model="Qwen/Qwen2.5-72B-Instruct",
 
69
  top_p=0.7,
70
  stream=False
71
  )
72
+ raw_text = response["choices"][0]["message"]["content"]
73
+ return raw_text.replace("\n", " ").strip()
 
74
  except Exception as e:
75
+ logger.error(f"Errore nel modello: {e}")
76
+ raise HTTPException(status_code=500, detail=str(e))
77
+
78
+ # Caricamento di un riassunto dell'ontologia (semplificato per brevità)
79
+ def load_rdf_summary() -> str:
80
+ # Qui un caricamento minimo o un testo statico
81
+ return "Classi e proprietà dell'ontologia: base:Stanza, base:Contiene, base:Opera, ecc."
82
+
83
+ app = FastAPI()
84
+ rdf_context = load_rdf_summary()
85
+
86
+ class QueryRequest(BaseModel):
87
+ message: str
88
+ max_tokens: int = 2048
89
+ temperature: float = 0.7
90
 
 
91
  @app.post("/generate-query/")
92
  async def generate_query(request: QueryRequest):
93
+ # 1) Prima iterazione
94
+ system_msg = create_system_message(rdf_context)
95
+ user_msg = request.message
96
+ messages = [
97
+ {"role": "system", "content": system_msg},
98
+ {"role": "user", "content": user_msg},
99
+ ]
100
+ response1 = await call_model(messages, request.temperature, request.max_tokens)
101
+ logger.info(f"[Prima iterazione] Risposta generata dal modello: {response1}")
102
+
103
+ # 2) Validazione
104
+ if not (response1.startswith("PREFIX") and ("SELECT" in response1 or "ASK" in response1)):
105
+ # Fallimento immediato
106
+ return {"query": None, "explanation": "La query non rispetta le regole base (PREFIX + SELECT/ASK)."}
107
+
108
+ if validate_sparql_query(response1, RDF_FILE):
109
+ # Query valida! Restituisco la prima
110
+ return {"query": response1, "explanation": "Query valida alla prima iterazione."}
111
+ else:
112
+ # 3) Seconda iterazione “correttiva”
113
+ correction_msg = create_correction_message(rdf_context, "Errore di validazione (prima iterazione).")
114
+ messages2 = [
115
+ {"role": "system", "content": system_msg}, # Sistema invariato
116
+ {"role": "assistant", "content": response1}, # Metti la risposta errata in contesto
117
+ {"role": "system", "content": correction_msg}, # Istruzione di correzione
118
+ ]
119
+ response2 = await call_model(messages2, request.temperature, request.max_tokens)
120
+ logger.info(f"[Seconda iterazione] Risposta generata dal modello: {response2}")
121
+
122
+ if not (response2.startswith("PREFIX") and ("SELECT" in response2 or "ASK" in response2)):
123
+ return {"query": None, "explanation": "Anche la seconda query non rispetta le regole (PREFIX + SELECT/ASK)."}
124
+
125
+ if validate_sparql_query(response2, RDF_FILE):
126
+ return {"query": response2, "explanation": "Query valida alla seconda iterazione (corretta)."}
127
+ else:
128
+ return {
129
+ "query": None,
130
+ "explanation": "Anche la seconda iterazione ha prodotto una query non valida. Interrompo."
131
+ }
132
+
133
  @app.get("/")
134
  async def root():
135
+ return {"message": "Server attivo per generare query SPARQL"}
136
+