MatteoScript commited on
Commit
631c799
1 Parent(s): d906dce

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -35
app.py CHANGED
@@ -8,6 +8,11 @@ import requests
8
  from langchain_community.vectorstores import Chroma
9
  from langchain_community.embeddings import HuggingFaceEmbeddings
10
  import json
 
 
 
 
 
11
 
12
  load_dotenv()
13
  URL_APP_SCRIPT = os.getenv('URL_APP_SCRIPT')
@@ -23,7 +28,7 @@ option_personalizzata = {'Personalizzata': {'systemRole': 'Tu sei BONSI AI, il m
23
  }
24
 
25
  # ----------------------------------------------------------- Interfaccia --------------------------------------------------------------------
26
- st.set_page_config(page_title="Bonsi AI", page_icon="🏫")
27
 
28
  def init_state() :
29
  if "messages" not in st.session_state:
@@ -51,11 +56,29 @@ def init_state() :
51
  st.session_state.split = 30
52
 
53
  if "enable_history" not in st.session_state:
54
- st.session_state.enable_history = False
 
 
 
 
 
 
 
 
 
55
 
56
  if "numero_generazioni" not in st.session_state:
57
  st.session_state.numero_generazioni = 1
58
 
 
 
 
 
 
 
 
 
 
59
  if not st.session_state.loaded_data:
60
  with st.status("Caricamento in corso...", expanded=True) as status:
61
  st.write("Inizializzazione Ambiente")
@@ -88,7 +111,6 @@ def sidebar():
88
  st.session_state.systemStyle = st.session_state.selected_option.get('systemStyle', '')
89
  st.session_state.systemStyle = st.text_area("Stile", st.session_state.systemStyle, help='Descrizione dello stile utilizzato per generare il testo')
90
  st.session_state.rag_enabled = st.session_state.selected_option.get('tipo', '')=='RAG'
91
-
92
  if st.session_state.selected_option_key == 'Decreti':
93
  st.session_state.top_k = st.slider(label="Documenti da ricercare", min_value=1, max_value=20, value=4, disabled=not st.session_state.rag_enabled)
94
  st.session_state.decreti_escludere = st.multiselect(
@@ -96,45 +118,82 @@ def sidebar():
96
  ['23.10.2 destinazione risorse residue pnrr DGR 1051-2023_Destinazione risorse PNRR Duale.pdf', '23.10.25 accompagnatoria Circolare Inail assicurazione.pdf', '23.10.26 circolare Inail assicurazione.pdf', '23.10.3 FAQ in attesa di avviso_.pdf', '23.11.2 avviso 24_24 Decreto 17106-2023 Approvazione Avviso IeFP 2023-2024.pdf', '23.5.15 decreto linee inclusione x enti locali.pdf', '23.6.21 Circolare+esplicativa+DGR+312-2023.pdf', '23.7.3 1° Decreto R.L. 23_24 .pdf', '23.9 Regolamento_prevenzione_bullismo_e_cyberbullismo__Centro_Bonsignori.pdf', '23.9.1 FAQ inizio anno formativo.pdf', '23.9.15 DECRETO VERIFICHE AMMINISTR 15-09-23.pdf', '23.9.4 modifica decreto GRS.pdf', '23.9.8 Budget 23_24.pdf', '24.10.2022 DECRETO loghi N.15176.pdf', 'ALLEGATO C_Scheda Supporti al funzionamento.pdf', 'ALLEGATO_ B_ Linee Guida.pdf', 'ALLEGATO_A1_PEI_INFANZIA.pdf', 'ALLEGATO_A2_PEI_PRIMARIA.pdf', 'ALLEGATO_A3_PEI_SEC_1_GRADO.pdf', 'ALLEGATO_A4_PEI_SEC_2_GRADO.pdf', 'ALLEGATO_C_1_Tabella_Fabbisogni.pdf', 'Brand+Guidelines+FSE+.pdf', 'Decreto 20797 del 22-12-2023_Aggiornamento budget PNRR.pdf', 'Decreto 20874 del 29-12-2023 Avviso IeFP PNRR 2023-2024_file unico.pdf'],
97
  [])
98
  st.markdown("---")
 
 
 
 
 
 
 
99
 
100
- def model_settings() :
101
  st.markdown("# Impostazioni Modello")
102
  st.session_state.chat_bot = st.sidebar.radio('Modello:', [key for key, value in CHAT_BOTS.items() ])
103
  st.session_state.numero_generazioni = st.slider(label="Generazioni", min_value = 1, max_value=10, value=1)
 
104
  st.session_state.temp = st.slider(label="Creatività", min_value=0.0, max_value=1.0, step=0.1, value=0.9)
105
- st.session_state.max_tokens = st.slider(label="Lunghezza Output", min_value = 64, max_value=2048, step= 32, value=1024)
106
- st.session_state.enable_history = st.toggle("Storico Messaggi", value=False)
107
 
108
  with st.sidebar:
109
  retrieval_settings()
110
  model_settings()
111
- st.markdown("""> **Creato da [Matteo Bergamelli] 🔗**""")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  def header() :
114
- st.title("Bonsi AI")
 
 
 
 
115
  with st.expander("Cos'è BonsiAI?"):
116
  st.info("""BonsiAI Chat è un ChatBot personalizzato basato su un database vettoriale, funziona secondo il principio della Generazione potenziata da Recupero (RAG).
117
- La sua funzione principale ruota attorno alla gestione di un ampio repository di documenti BonsiAI e fornisce agli utenti risposte in linea con le loro domande.
118
- Questo approccio garantisce una risposta più precisa sulla base della richiesta degli utenti.""")
119
 
120
  def chat_box() :
121
  for message in st.session_state.messages:
122
  with st.chat_message(message["role"]):
123
  st.markdown(message["content"])
124
 
125
- init_state()
126
- sidebar()
127
- header()
128
- chat_box()
129
-
130
- # ----------------------------------------------------------- Funzioni Varie --------------------------------------------------------------------
131
  def formattaPrompt(prompt, systemRole, systemStyle, instruction):
 
 
132
  input_text = f'''
133
  {{
134
  "input": {{
135
  "role": "system",
136
  "content": "{systemRole}",
137
- "style": "{systemStyle}"
138
  }},
139
  "messages": [
140
  {{
@@ -155,7 +214,6 @@ def gen_augmented_prompt(prompt, top_k) :
155
  embedding = HuggingFaceEmbeddings(model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
156
  db = Chroma(persist_directory='./DB_Decreti', embedding_function=embedding)
157
  docs = db.similarity_search(prompt, k=top_k)
158
-
159
  links = []
160
  context = ''
161
  NomeCartellaOriginariaDB = 'Documenti_2\\'
@@ -164,24 +222,52 @@ def gen_augmented_prompt(prompt, top_k) :
164
  context += testo + '\n\n\n'
165
  reference = doc.metadata["source"].replace(NomeCartellaOriginariaDB, '') + ' (Pag. ' + str(doc.metadata["page"]) + ')'
166
  links.append((reference, testo))
167
- generated_prompt = f"""
168
- A PARTIRE DAL SEGUENTE CONTESTO: {docs},
169
- ----
170
- RISPONDI ALLA SEGUENTE RICHIESTA: {prompt}
171
- """
172
  return context, links
173
 
174
- def generate_chat_stream(prompt, prompt_originale, inst) :
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  links = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  if st.session_state.rag_enabled :
177
  with st.spinner("Ricerca nei Decreti...."):
178
  time.sleep(1)
179
  st.session_state.instruction, links = gen_augmented_prompt(prompt=prompt_originale, top_k=st.session_state.top_k)
180
  with st.spinner("Generazione in corso...") :
181
  time.sleep(1)
182
- chat_stream = chat(prompt, st.session_state.history,chat_client=CHAT_BOTS[st.session_state.chat_bot] ,
183
- temperature=st.session_state.temp, max_new_tokens=st.session_state.max_tokens)
184
- return chat_stream, links, inst
185
 
186
  def stream_handler(chat_stream, placeholder) :
187
  full_response = ''
@@ -198,6 +284,11 @@ def show_source(links) :
198
  reference, testo = link
199
  st.info('##### ' + reference.replace('_', ' ') + '\n\n'+ testo)
200
 
 
 
 
 
 
201
  def split_text(text, chunk_size):
202
  testo_suddiviso = []
203
  if text == '':
@@ -207,29 +298,29 @@ def split_text(text, chunk_size):
207
  for i in range(0, len(text), chunk_size):
208
  testo_suddiviso.append(text[i:i+chunk_size])
209
  return testo_suddiviso
210
-
211
- # -------------------------------------------------------------- Gestione Chat -----------------------------------------------------------------------
212
  if prompt := st.chat_input("Chatta con BonsiAI..."):
213
- instruction_suddivise = split_text(st.session_state.instruction, st.session_state.split*2000)
214
  prompt_originale = prompt
 
 
215
  ruolo_originale = st.session_state.systemRole
216
  ruoli_divisi = ruolo_originale.split("&&")
217
- parte = 1
218
- i = 1
219
  risposta_completa = ''
220
  for ruolo_singolo in ruoli_divisi:
221
  for instruction_singola in instruction_suddivise:
222
  for numgen in range(1, st.session_state.numero_generazioni+1):
223
- prompt = formattaPrompt(prompt_originale, ruolo_singolo, st.session_state.systemStyle, instruction_singola)
224
  if i==1:
225
  st.chat_message("user").markdown(prompt_originale + (': Parte ' + str(parte) if i > 1 else ''))
226
  i+=1
 
227
  st.session_state.messages.append({"role": "user", "content": prompt_originale})
228
- chat_stream, links, inst = generate_chat_stream(prompt, prompt_originale, instruction_singola)
229
  with st.chat_message("assistant"):
230
  placeholder = st.empty()
231
  full_response = stream_handler(chat_stream, placeholder)
232
- if st.session_state.rag_enabled:
233
  show_source(links)
234
  if st.session_state.options.get(st.session_state.selected_option_key, {})["tipo"]=='DOCUMENTO':
235
  with st.expander("Mostra Documento") :
@@ -240,6 +331,8 @@ if prompt := st.chat_input("Chatta con BonsiAI..."):
240
 
241
  if st.session_state.enable_history:
242
  st.session_state.history.append([prompt, full_response])
 
 
243
  st.success('Generazione Completata')
244
  payload = {"domanda": prompt_originale, "risposta": risposta_completa}
245
  json_payload = json.dumps(payload)
 
8
  from langchain_community.vectorstores import Chroma
9
  from langchain_community.embeddings import HuggingFaceEmbeddings
10
  import json
11
+ from audio_recorder_streamlit import audio_recorder
12
+ import speech_recognition as sr
13
+ from googlesearch import search
14
+ from bs4 import BeautifulSoup
15
+
16
 
17
  load_dotenv()
18
  URL_APP_SCRIPT = os.getenv('URL_APP_SCRIPT')
 
28
  }
29
 
30
  # ----------------------------------------------------------- Interfaccia --------------------------------------------------------------------
31
+ st.set_page_config(page_title="Bonsi A.I.", page_icon="🏫")
32
 
33
  def init_state() :
34
  if "messages" not in st.session_state:
 
56
  st.session_state.split = 30
57
 
58
  if "enable_history" not in st.session_state:
59
+ st.session_state.enable_history = True
60
+
61
+ if "audio_bytes" not in st.session_state:
62
+ st.session_state.audio_bytes = False
63
+
64
+ if "cerca_online" not in st.session_state:
65
+ st.session_state.cerca_online = False
66
+
67
+ if "numero_siti" not in st.session_state:
68
+ st.session_state.numero_siti = 3
69
 
70
  if "numero_generazioni" not in st.session_state:
71
  st.session_state.numero_generazioni = 1
72
 
73
+ if "tbs_options" not in st.session_state:
74
+ st.session_state.tbs_options = {
75
+ "Sempre": "0",
76
+ "Ultimo anno": "qdr:y",
77
+ "Ultimo mese": "qdr:m",
78
+ "Ultima settimana": "qdr:w",
79
+ "Ultimo giorno": "qdr:d"
80
+ }
81
+
82
  if not st.session_state.loaded_data:
83
  with st.status("Caricamento in corso...", expanded=True) as status:
84
  st.write("Inizializzazione Ambiente")
 
111
  st.session_state.systemStyle = st.session_state.selected_option.get('systemStyle', '')
112
  st.session_state.systemStyle = st.text_area("Stile", st.session_state.systemStyle, help='Descrizione dello stile utilizzato per generare il testo')
113
  st.session_state.rag_enabled = st.session_state.selected_option.get('tipo', '')=='RAG'
 
114
  if st.session_state.selected_option_key == 'Decreti':
115
  st.session_state.top_k = st.slider(label="Documenti da ricercare", min_value=1, max_value=20, value=4, disabled=not st.session_state.rag_enabled)
116
  st.session_state.decreti_escludere = st.multiselect(
 
118
  ['23.10.2 destinazione risorse residue pnrr DGR 1051-2023_Destinazione risorse PNRR Duale.pdf', '23.10.25 accompagnatoria Circolare Inail assicurazione.pdf', '23.10.26 circolare Inail assicurazione.pdf', '23.10.3 FAQ in attesa di avviso_.pdf', '23.11.2 avviso 24_24 Decreto 17106-2023 Approvazione Avviso IeFP 2023-2024.pdf', '23.5.15 decreto linee inclusione x enti locali.pdf', '23.6.21 Circolare+esplicativa+DGR+312-2023.pdf', '23.7.3 1° Decreto R.L. 23_24 .pdf', '23.9 Regolamento_prevenzione_bullismo_e_cyberbullismo__Centro_Bonsignori.pdf', '23.9.1 FAQ inizio anno formativo.pdf', '23.9.15 DECRETO VERIFICHE AMMINISTR 15-09-23.pdf', '23.9.4 modifica decreto GRS.pdf', '23.9.8 Budget 23_24.pdf', '24.10.2022 DECRETO loghi N.15176.pdf', 'ALLEGATO C_Scheda Supporti al funzionamento.pdf', 'ALLEGATO_ B_ Linee Guida.pdf', 'ALLEGATO_A1_PEI_INFANZIA.pdf', 'ALLEGATO_A2_PEI_PRIMARIA.pdf', 'ALLEGATO_A3_PEI_SEC_1_GRADO.pdf', 'ALLEGATO_A4_PEI_SEC_2_GRADO.pdf', 'ALLEGATO_C_1_Tabella_Fabbisogni.pdf', 'Brand+Guidelines+FSE+.pdf', 'Decreto 20797 del 22-12-2023_Aggiornamento budget PNRR.pdf', 'Decreto 20874 del 29-12-2023 Avviso IeFP PNRR 2023-2024_file unico.pdf'],
119
  [])
120
  st.markdown("---")
121
+ st.markdown("# Ricerca Online")
122
+ st.session_state.cerca_online = st.toggle("Attivata", value=False)
123
+ st.session_state.selected_tbs = st.selectbox("Periodo:", list(st.session_state.tbs_options.keys()), disabled=not st.session_state.cerca_online)
124
+ st.session_state.tbs_value = st.session_state.tbs_options[st.session_state.selected_tbs]
125
+ st.session_state.numero_siti = st.slider(label="Risultati", min_value = 1, max_value=20, value=3, disabled=not st.session_state.cerca_online)
126
+ #st.session_state.suddividi_ricerca = st.toggle("Attivata", value=False)
127
+ st.markdown("---")
128
 
129
+ def model_settings():
130
  st.markdown("# Impostazioni Modello")
131
  st.session_state.chat_bot = st.sidebar.radio('Modello:', [key for key, value in CHAT_BOTS.items() ])
132
  st.session_state.numero_generazioni = st.slider(label="Generazioni", min_value = 1, max_value=10, value=1)
133
+ st.session_state.enable_history = st.toggle("Storico Messaggi", value=True)
134
  st.session_state.temp = st.slider(label="Creatività", min_value=0.0, max_value=1.0, step=0.1, value=0.9)
135
+ st.session_state.max_tokens = st.slider(label="Lunghezza Output", min_value = 2, max_value=2048, step= 32, value=1024)
 
136
 
137
  with st.sidebar:
138
  retrieval_settings()
139
  model_settings()
140
+ st.markdown("""> **Creato da Matteo Bergamelli **""")
141
+
142
+ def audioRec():
143
+ st.session_state.audio_bytes = audio_recorder(text='', icon_size="3x")
144
+ if st.session_state.audio_bytes:
145
+ with open("./AUDIO.wav", "wb") as file:
146
+ file.write(st.session_state.audio_bytes)
147
+ wav = sr.AudioFile("./AUDIO.wav")
148
+ with wav as source:
149
+ recognizer_instance = sr.Recognizer()
150
+ recognizer_instance.pause_threshold = 3.0
151
+ audio = recognizer_instance.listen(source)
152
+ print("Ok! sto ora elaborando il messaggio!")
153
+ try:
154
+ text = recognizer_instance.recognize_google(audio, language="it-IT")
155
+ js = f"""
156
+ <script>
157
+ var chatInput = parent.document.querySelector('textarea[data-testid="stChatInputTextArea"]');
158
+ var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
159
+ nativeInputValueSetter.call(chatInput, "{text}");
160
+ var event = new Event('input', {{ bubbles: true}});
161
+ chatInput.dispatchEvent(event);
162
+ var sendChat = parent.document.querySelector('[data-testid="stChatInputSubmitButton"]');
163
+ sendChat.click();
164
+ var x = parent.document.querySelector('[data-testid="stIFrame"]');
165
+ x.style.display = "none";
166
+ </script>
167
+ """
168
+ st.components.v1.html(js)
169
+ except Exception as e:
170
+ print(e)
171
 
172
  def header() :
173
+ col1, colx, col2 = st.columns([2, 1, 25])
174
+ with col2:
175
+ st.title("Software 2000 A.I.", anchor=False)
176
+ with col1:
177
+ audioRec()
178
  with st.expander("Cos'è BonsiAI?"):
179
  st.info("""BonsiAI Chat è un ChatBot personalizzato basato su un database vettoriale, funziona secondo il principio della Generazione potenziata da Recupero (RAG).
180
+ La sua funzione principale ruota attorno alla gestione di un ampio repository di documenti BonsiAI e fornisce agli utenti risposte in linea con le loro domande.
181
+ Questo approccio garantisce una risposta più precisa sulla base della richiesta degli utenti.""")
182
 
183
  def chat_box() :
184
  for message in st.session_state.messages:
185
  with st.chat_message(message["role"]):
186
  st.markdown(message["content"])
187
 
 
 
 
 
 
 
188
  def formattaPrompt(prompt, systemRole, systemStyle, instruction):
189
+ if st.session_state.cerca_online:
190
+ systemRole += '. Ti ho fornito una lista di materiali nelle instruction. Devi rispondere sulla base delle informazioni fonrnite!'
191
  input_text = f'''
192
  {{
193
  "input": {{
194
  "role": "system",
195
  "content": "{systemRole}",
196
+ "style": "{systemStyle} "
197
  }},
198
  "messages": [
199
  {{
 
214
  embedding = HuggingFaceEmbeddings(model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
215
  db = Chroma(persist_directory='./DB_Decreti', embedding_function=embedding)
216
  docs = db.similarity_search(prompt, k=top_k)
 
217
  links = []
218
  context = ''
219
  NomeCartellaOriginariaDB = 'Documenti_2\\'
 
222
  context += testo + '\n\n\n'
223
  reference = doc.metadata["source"].replace(NomeCartellaOriginariaDB, '') + ' (Pag. ' + str(doc.metadata["page"]) + ')'
224
  links.append((reference, testo))
 
 
 
 
 
225
  return context, links
226
 
227
+ def get_search_results(query, top_k):
228
+ results = []
229
+ for url in search(query, num=top_k, stop=top_k, tbs=st.session_state.tbs_value):
230
+ try:
231
+ response = requests.get(url)
232
+ soup = BeautifulSoup(response.text, 'html.parser')
233
+ title = soup.title.string if soup.title else "N/A"
234
+ description = soup.find('meta', attrs={'name': 'description'})['content'] if soup.find('meta', attrs={'name': 'description'}) else "N/A"
235
+ body_content = soup.find('body').get_text() if soup.find('body') else "N/A"
236
+ results.append({'title': title, 'description': description, 'url': url, 'body': body_content})
237
+ except Exception as e:
238
+ print(f"Error fetching data from {url}: {e}")
239
+ continue
240
+ return results
241
+
242
+ def gen_online_prompt(prompt, top_k) :
243
  links = []
244
+ context = ''
245
+ results = get_search_results(prompt, top_k)
246
+ for i, result in enumerate(results, start=1):
247
+ context += result['title'] + '\n' + result['description'] + '\n' + '\n\n' + result['body'].replace('\n','.') + '\n\n------------------------------------------------------------'
248
+ links.append((str(i) + '. ' + result['title'], result['description'] + '\n\n' + result['url']))
249
+ return context, links
250
+
251
+ def generate_chat_stream(prompt) :
252
+ chat_stream = chat(prompt, st.session_state.history,chat_client=CHAT_BOTS[st.session_state.chat_bot] ,
253
+ temperature=st.session_state.temp, max_new_tokens=st.session_state.max_tokens)
254
+ return chat_stream
255
+
256
+ def inserisci_istruzioni(prompt_originale):
257
+ links = []
258
+ instruction_originale = st.session_state.instruction
259
+ if st.session_state.cerca_online:
260
+ with st.spinner("Ricerca Online...."):
261
+ time.sleep(1)
262
+ st.session_state.instruction, links = gen_online_prompt(prompt=prompt_originale, top_k=st.session_state.numero_siti)
263
  if st.session_state.rag_enabled :
264
  with st.spinner("Ricerca nei Decreti...."):
265
  time.sleep(1)
266
  st.session_state.instruction, links = gen_augmented_prompt(prompt=prompt_originale, top_k=st.session_state.top_k)
267
  with st.spinner("Generazione in corso...") :
268
  time.sleep(1)
269
+ st.session_state.instruction = instruction_originale + '\n----------------------------------------------\n' + st.session_state.instruction
270
+ return links
 
271
 
272
  def stream_handler(chat_stream, placeholder) :
273
  full_response = ''
 
284
  reference, testo = link
285
  st.info('##### ' + reference.replace('_', ' ') + '\n\n'+ testo)
286
 
287
+ init_state()
288
+ sidebar()
289
+ header()
290
+ chat_box()
291
+
292
  def split_text(text, chunk_size):
293
  testo_suddiviso = []
294
  if text == '':
 
298
  for i in range(0, len(text), chunk_size):
299
  testo_suddiviso.append(text[i:i+chunk_size])
300
  return testo_suddiviso
301
+
 
302
  if prompt := st.chat_input("Chatta con BonsiAI..."):
 
303
  prompt_originale = prompt
304
+ links = inserisci_istruzioni(prompt_originale)
305
+ instruction_suddivise = split_text(st.session_state.instruction, st.session_state.split*2000)
306
  ruolo_originale = st.session_state.systemRole
307
  ruoli_divisi = ruolo_originale.split("&&")
308
+ parte=1
309
+ i=1
310
  risposta_completa = ''
311
  for ruolo_singolo in ruoli_divisi:
312
  for instruction_singola in instruction_suddivise:
313
  for numgen in range(1, st.session_state.numero_generazioni+1):
 
314
  if i==1:
315
  st.chat_message("user").markdown(prompt_originale + (': Parte ' + str(parte) if i > 1 else ''))
316
  i+=1
317
+ prompt = formattaPrompt(prompt_originale, ruolo_singolo, st.session_state.systemStyle, instruction_singola)
318
  st.session_state.messages.append({"role": "user", "content": prompt_originale})
319
+ chat_stream = generate_chat_stream(prompt)
320
  with st.chat_message("assistant"):
321
  placeholder = st.empty()
322
  full_response = stream_handler(chat_stream, placeholder)
323
+ if st.session_state.rag_enabled or st.session_state.cerca_online:
324
  show_source(links)
325
  if st.session_state.options.get(st.session_state.selected_option_key, {})["tipo"]=='DOCUMENTO':
326
  with st.expander("Mostra Documento") :
 
331
 
332
  if st.session_state.enable_history:
333
  st.session_state.history.append([prompt, full_response])
334
+ else:
335
+ st.session_state.history.append(['', ''])
336
  st.success('Generazione Completata')
337
  payload = {"domanda": prompt_originale, "risposta": risposta_completa}
338
  json_payload = json.dumps(payload)