AshenClock commited on
Commit
bfa70d6
·
verified ·
1 Parent(s): e250196

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -39
app.py CHANGED
@@ -27,10 +27,11 @@ HF_MODEL = "Qwen/Qwen2.5-72B-Instruct"
27
  MAX_CLASSES = 30
28
  MAX_PROPERTIES = 30
29
 
 
30
  def extract_classes_and_properties(rdf_file:str) -> str:
31
  """
32
- Carica l'ontologia e crea un 'sunto' solo di Classi e Proprietà
33
- (senza riportare NamedIndividuals o triple).
34
  """
35
  if not os.path.exists(rdf_file):
36
  return "NO_RDF_FILE"
@@ -74,41 +75,51 @@ def extract_classes_and_properties(rdf_file:str) -> str:
74
  """
75
  return summary
76
 
 
77
  knowledge_text = extract_classes_and_properties(RDF_FILE)
78
 
 
79
  def create_system_message(ont_text:str)->str:
80
  """
81
- Prompt di sistema con regole stringenti e SENZA NamedIndividuals.
 
 
82
  """
83
  return f"""
84
- Sei un assistente museale. Hai un elenco di Classi e Proprietà dell'ontologia:
85
 
86
  --- ONTOLOGIA ---
87
  {ont_text}
88
  --- FINE ---
89
 
90
- Regole Fondamentali:
91
- 1) Se l'utente fa una domanda correlata a queste Classi/Proprietà, genera SEMPRE una query SPARQL
92
- in UNA SOLA RIGA, con prefix:
93
- PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/progettoMuseo#>
94
- 2) Se la query produce 0 risultati o è invalida, devi fare un secondo tentativo (magari con FILTER).
95
- 3) Se è una domanda generica (es. come stai?), rispondi breve.
96
- 4) Se trovi risultati, la risposta finale è la query SPARQL su una singola riga.
97
- 5) Se non trovi nulla, di' "Nessuna info".
98
- 6) Non scrivere risposte multiline per la query. UNA SOLA RIGA.
 
 
 
 
99
 
100
  FINE REGOLE
101
  """
102
 
 
103
  def create_explanation_prompt(results_str:str)->str:
104
  return f"""
105
  Ho ottenuto questi risultati SPARQL:
106
  {results_str}
107
 
108
- Fornisci una breve spiegazione museale (massimo 10 righe), coerente e senza inventare.
109
  """
110
 
111
- async def call_hf_model(messages, temperature=0.7, max_tokens=1024)->str:
 
112
  logger.debug("Chiamo HF con i seguenti messaggi:")
113
  for m in messages:
114
  logger.debug(f"ROLE={m['role']} => {m['content'][:300]}")
@@ -121,11 +132,15 @@ async def call_hf_model(messages, temperature=0.7, max_tokens=1024)->str:
121
  top_p=0.9
122
  )
123
  raw=resp["choices"][0]["message"]["content"]
124
- return raw.replace("\n"," ").strip()
 
 
 
125
  except Exception as e:
126
  logger.error(f"HuggingFace error: {e}")
127
  raise HTTPException(status_code=500, detail=str(e))
128
 
 
129
  from fastapi import FastAPI
130
 
131
  app=FastAPI()
@@ -145,57 +160,59 @@ async def generate_response(req:QueryRequest):
145
  {"role":"system","content":sys_msg},
146
  {"role":"user","content":user_input}
147
  ]
148
- first=await call_hf_model(msgs, req.temperature, req.max_tokens)
149
- logger.info(f"PRIMA RISPOSTA:\n{first}")
 
150
 
151
- if not first.startswith("PREFIX base:"):
152
- second_msg=f"Non hai fatto query SPARQL su una riga. Ritenta. Domanda: {user_input}"
 
153
  msgs2=[
154
  {"role":"system","content":sys_msg},
155
- {"role":"assistant","content":first},
156
- {"role":"user","content":second_msg}
157
  ]
158
- second=await call_hf_model(msgs2, req.temperature, req.max_tokens)
159
- logger.info(f"SECONDA RISPOSTA:\n{second}")
160
- if second.startswith("PREFIX base:"):
161
- sparql_query=second
162
  else:
163
- return {"type":"NATURAL","response": second}
164
  else:
165
- sparql_query=first
166
 
167
- # Eseguiamo la query
168
  import rdflib
169
  g=rdflib.Graph()
170
  try:
171
  g.parse(RDF_FILE,format="xml")
172
  except Exception as e:
173
- logger.error(f"Parse error: {e}")
174
- return {"type":"ERROR","response":"Parsing RDF error"}
175
 
176
  try:
177
  results=g.query(sparql_query)
178
  except Exception as e:
179
- # fallback
180
- fallback=f"Query fallita. Riprova. Domanda: {user_input}"
181
  msgs3=[
182
  {"role":"system","content":sys_msg},
183
  {"role":"assistant","content":sparql_query},
184
  {"role":"user","content":fallback}
185
  ]
186
- res3=await call_hf_model(msgs3,req.temperature,req.max_tokens)
187
- if res3.startswith("PREFIX base:"):
188
- sparql_query=res3
189
  try:
190
  results=g.query(sparql_query)
191
  except Exception as e2:
192
- return {"type":"ERROR","response":f"Query fallita ancora: {e2}"}
193
  else:
194
- return {"type":"NATURAL","response":res3}
195
 
196
  if len(results)==0:
197
  return {"type":"NATURAL","sparql_query":sparql_query,"response":"Nessun risultato."}
198
 
 
199
  row_list=[]
200
  for row in results:
201
  row_str=", ".join([f"{k}:{v}" for k,v in row.asdict().items()])
@@ -219,4 +236,4 @@ async def generate_response(req:QueryRequest):
219
 
220
  @app.get("/")
221
  def home():
222
- return {"message":"Ok con sole classi e proprietà. Se l'utente cerca istanze, non le trova."}
 
27
  MAX_CLASSES = 30
28
  MAX_PROPERTIES = 30
29
 
30
+
31
  def extract_classes_and_properties(rdf_file:str) -> str:
32
  """
33
+ Carica l'ontologia e crea un 'sunto' di Classi e Proprietà
34
+ (senza NamedIndividuals) per ridurre i token.
35
  """
36
  if not os.path.exists(rdf_file):
37
  return "NO_RDF_FILE"
 
75
  """
76
  return summary
77
 
78
+
79
  knowledge_text = extract_classes_and_properties(RDF_FILE)
80
 
81
+
82
  def create_system_message(ont_text:str)->str:
83
  """
84
+ Prompt di sistema robusto, con regole su query in una riga.
85
+ Lasciamo un 'accenno' che, per parlare di 'materiale', potrebbe esserci
86
+ una proprietà simile a 'base:materialeOpera' o analoga, ma NON tassativo.
87
  """
88
  return f"""
89
+ Sei un assistente museale. Ecco un estratto di CLASSI e PROPRIETA' dell'ontologia (senza NamedIndividuals):
90
 
91
  --- ONTOLOGIA ---
92
  {ont_text}
93
  --- FINE ---
94
 
95
+ Suggerimento: se l'utente chiede il 'materiale' di un'opera, potresti usare qualcosa come
96
+ 'base:materialeOpera' o un'altra proprietà simile (se esiste). Non è tassativo: usa
97
+ la proprietà che ritieni più affine se ci sono riferimenti in ontologia.
98
+
99
+ REGOLE STRINGENTI:
100
+ 1) Se l'utente chiede info su questa ontologia, genera SEMPRE una query SPARQL in UNA SOLA RIGA,
101
+ con prefix:
102
+ PREFIX base: <http://www.semanticweb.org/lucreziamosca/ontologies/progettoMuseo#>
103
+ 2) Se la query produce 0 risultati o fallisce, ritenta con un secondo tentativo.
104
+ 3) Se la domanda è generica (tipo 'Ciao, come stai?'), rispondi breve.
105
+ 4) Se trovi risultati, risposta finale = la query SPARQL (una sola riga).
106
+ 5) Se non trovi nulla, di' 'Nessuna info.'
107
+ 6) Non multiline. Esempio: PREFIX base: <...> SELECT ?x WHERE {{ ... }}.
108
 
109
  FINE REGOLE
110
  """
111
 
112
+
113
  def create_explanation_prompt(results_str:str)->str:
114
  return f"""
115
  Ho ottenuto questi risultati SPARQL:
116
  {results_str}
117
 
118
+ Ora fornisci una breve spiegazione museale (massimo ~10 righe), senza inventare oltre i risultati.
119
  """
120
 
121
+
122
+ async def call_hf_model(messages, temperature=0.5, max_tokens=1024)->str:
123
  logger.debug("Chiamo HF con i seguenti messaggi:")
124
  for m in messages:
125
  logger.debug(f"ROLE={m['role']} => {m['content'][:300]}")
 
132
  top_p=0.9
133
  )
134
  raw=resp["choices"][0]["message"]["content"]
135
+ # Forziamo la query su linea singola se multiline
136
+ single_line = " ".join(raw.splitlines())
137
+ logger.debug(f"Risposta HF single-line: {single_line}")
138
+ return single_line.strip()
139
  except Exception as e:
140
  logger.error(f"HuggingFace error: {e}")
141
  raise HTTPException(status_code=500, detail=str(e))
142
 
143
+
144
  from fastapi import FastAPI
145
 
146
  app=FastAPI()
 
160
  {"role":"system","content":sys_msg},
161
  {"role":"user","content":user_input}
162
  ]
163
+ # Primo tentativo
164
+ r1=await call_hf_model(msgs, req.temperature, req.max_tokens)
165
+ logger.info(f"PRIMA RISPOSTA:\n{r1}")
166
 
167
+ # Se non parte con "PREFIX base:"
168
+ if not r1.startswith("PREFIX base:"):
169
+ sc=f"Non hai risposto con query SPARQL su una sola riga. Riprova. Domanda: {user_input}"
170
  msgs2=[
171
  {"role":"system","content":sys_msg},
172
+ {"role":"assistant","content":r1},
173
+ {"role":"user","content":sc}
174
  ]
175
+ r2=await call_hf_model(msgs2,req.temperature,req.max_tokens)
176
+ logger.info(f"SECONDA RISPOSTA:\n{r2}")
177
+ if r2.startswith("PREFIX base:"):
178
+ sparql_query=r2
179
  else:
180
+ return {"type":"NATURAL","response": r2}
181
  else:
182
+ sparql_query=r1
183
 
184
+ # Esegui la query con rdflib
185
  import rdflib
186
  g=rdflib.Graph()
187
  try:
188
  g.parse(RDF_FILE,format="xml")
189
  except Exception as e:
190
+ logger.error(f"Parsing RDF error: {e}")
191
+ return {"type":"ERROR","response":f"Parsing RDF error: {e}"}
192
 
193
  try:
194
  results=g.query(sparql_query)
195
  except Exception as e:
196
+ fallback=f"La query SPARQL ha fallito. Riprova. Domanda: {user_input}"
 
197
  msgs3=[
198
  {"role":"system","content":sys_msg},
199
  {"role":"assistant","content":sparql_query},
200
  {"role":"user","content":fallback}
201
  ]
202
+ r3=await call_hf_model(msgs3,req.temperature,req.max_tokens)
203
+ if r3.startswith("PREFIX base:"):
204
+ sparql_query=r3
205
  try:
206
  results=g.query(sparql_query)
207
  except Exception as e2:
208
+ return {"type":"ERROR","response":f"Query fallita di nuovo: {e2}"}
209
  else:
210
+ return {"type":"NATURAL","response":r3}
211
 
212
  if len(results)==0:
213
  return {"type":"NATURAL","sparql_query":sparql_query,"response":"Nessun risultato."}
214
 
215
+ # Confeziona risultati
216
  row_list=[]
217
  for row in results:
218
  row_str=", ".join([f"{k}:{v}" for k,v in row.asdict().items()])
 
236
 
237
  @app.get("/")
238
  def home():
239
+ return {"message":"Prompt lascia libertà su come chiamare la proprietà del materiale, ma suggerisce un possibile 'materialeOpera'."}