StefanoDUrso commited on
Commit
f88de80
Β·
1 Parent(s): 6e5b27a

handling contexts

Browse files
__pycache__/config.cpython-312.pyc CHANGED
Binary files a/__pycache__/config.cpython-312.pyc and b/__pycache__/config.cpython-312.pyc differ
 
app.backup DELETED
@@ -1,126 +0,0 @@
1
- import sys
2
- import time
3
- import gradio as gr
4
-
5
- from config import initialize, check_user
6
-
7
-
8
- llm_manager, qdrant_manager = initialize()
9
- if llm_manager is None:
10
- print("Error: Failed to initialize configuration: llm_manager. Exiting application.", flush=True)
11
- sys.exit(1)
12
- if qdrant_manager is None:
13
- print("Error: Failed to initialize configuration: qdrant_manager. Exiting application.", flush=True)
14
- sys.exit(1)
15
-
16
- def reset_textbox():
17
- """Clears the textbox after sending a message."""
18
- return gr.update(value="")
19
-
20
- def slow_echo(message, history):
21
- if history is None:
22
- history = [] # Ensure history is initialized
23
-
24
- # Append user message with role "user"
25
- history.append({"role": "user", "content": message})
26
-
27
- # Placeholder for assistant response
28
- bot_entry = {"role": "assistant", "content": ""}
29
- history.append(bot_entry)
30
-
31
- response = "You typed: "
32
-
33
- for i in range(len(message)):
34
- time.sleep(0.05)
35
- response += message[i]
36
- bot_entry["content"] = response # Update assistant's response progressively
37
- yield history # Yield updated history in the correct format
38
-
39
- yield history # Final yield with full message
40
-
41
- def llm_send_message(message, history):
42
- if history is None:
43
- history = []
44
-
45
- # Append user message to history
46
- history.append({"role": "user", "content": message})
47
-
48
- yield history
49
-
50
- # Placeholder for assistant response
51
- bot_entry = {"role": "assistant", "content": ""}
52
- history.append(bot_entry)
53
-
54
- # Send message to LLM and stream response
55
- response = ""
56
- for chunk in llm_manager.send_message(message): # Streaming response
57
- time.sleep(0.01) # Simulate gradual output
58
- response += chunk
59
- bot_entry["content"] = response # Update assistant response progressively
60
- yield history # Yield updated history
61
-
62
- yield history # Final yield
63
-
64
- def authenticate(username, password):
65
- if check_user(username, password):
66
- print("πŸ”‘ Login successful!")
67
- return gr.update(visible=False), gr.update(visible=True), gr.update(value="", visible=False) # Hide login, show chatbot, clear error
68
- else:
69
- print("❌ Incorrect username or password")
70
- return gr.update(visible=True), gr.update(visible=True), gr.update(value="❌ Incorrect username or password", visible=True) # Show error
71
-
72
- with gr.Blocks(fill_height=True) as demo:
73
-
74
- with gr.Column(visible=True) as login_section:
75
- gr.Markdown("### πŸ”’ Login Required")
76
- username_input = gr.Textbox(label="Username")
77
- password_input = gr.Textbox(label="Password", type="password")
78
- login_button = gr.Button("Login")
79
- error_message = gr.Text("", visible=False)
80
-
81
- with gr.Column(visible=False) as chat_section:
82
-
83
- chat_configuration = gr.Markdown("")
84
-
85
- chat = gr.Chatbot(
86
- label="Video Helper",
87
- type="messages"
88
- )
89
-
90
- input = gr.Textbox(
91
- label="Input",
92
- placeholder="Type something here..."
93
- )
94
-
95
- stored_message = gr.State()
96
-
97
- input.submit(
98
- fn=lambda text: (text, ""),
99
- inputs=[input],
100
- outputs=[stored_message, input]
101
- ).then(
102
- #fn=llm_send_message,
103
- fn=slow_echo,
104
- inputs=[stored_message, chat],
105
- outputs=chat
106
- )
107
-
108
- send_btn = gr.Button("Send")
109
- send_btn.click(
110
- fn=lambda text: (text, ""),
111
- inputs=[input],
112
- outputs=[stored_message, input]
113
- ).then(
114
- fn=llm_send_message,
115
- inputs=[stored_message, chat],
116
- outputs=chat
117
- )
118
-
119
- login_button.click(
120
- authenticate,
121
- [username_input, password_input],
122
- [login_section, chat_section, error_message]
123
- )
124
-
125
- if __name__ == "__main__":
126
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -33,27 +33,81 @@ def set_interactive_state(interactive: bool):
33
  gr.update(interactive=interactive) # send_btn
34
  )
35
 
36
- def _add_trust_icon(text, level):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  icons = {
38
- "high": "🟒", # alta affidabilità
39
- "medium": "🟑", # simile ma non perfetto
40
- "summary": "🟠", # riassunto usato
41
- "low": "πŸ”΄", # basato solo sulla chat
42
- "no_context": "βšͺ️"
43
  }
44
- label = {
45
- "high": "Reliable",
46
- "medium": "Moderate Similarity",
47
- "summary": "Summary Used",
48
- "low": "No Source Match",
49
- "no_context": "No Context"
50
  }
51
 
52
- icon = icons.get(level, "βšͺ️")
53
- tooltip = label.get(level, "Unknown")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
- # Prepend semaforo + etichetta (puoi anche usare HTML per Gradio futuro)
56
- return f"{icon} *{tooltip}*\n\n{text}"
57
 
58
  def get_summary(summary_type, history):
59
  if history is None:
@@ -70,12 +124,40 @@ def get_summary(summary_type, history):
70
  label = "❓ Unknown Summary"
71
 
72
  if content:
 
73
  history.append({"role": "assistant", "content": f"{label}\n\n{content}"})
74
  else:
75
  history.append({"role": "assistant", "content": f"⚠️ No {label.lower()} available."})
76
 
77
  return history
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  def send_chat_message(message, history):
80
  if history is None:
81
  history = []
@@ -85,21 +167,60 @@ def send_chat_message(message, history):
85
  history.append(bot_entry)
86
 
87
  response = ""
88
- context_level = "high" # default in caso non venga restituito
 
89
 
90
- # Supporta nuova struttura restituita da stream_message
91
- stream = llm_manager.stream_message(message, contextualize=True)
92
 
93
  for chunk in stream:
94
- if isinstance(chunk, dict): # nuova versione con chunk + livello
95
- response += chunk["content"]
96
- context_level = chunk.get("context_level", "high")
 
 
 
 
 
 
 
97
  else: # retrocompatibilitΓ 
98
  response += chunk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
- # aggiorna contenuto in tempo reale
101
- bot_entry["content"] = _add_trust_icon(response, context_level)
102
- yield history
 
103
 
104
  yield history
105
 
@@ -116,6 +237,7 @@ def authenticate(username, password):
116
  return (
117
  gr.update(visible=False), # Hide login
118
  gr.update(visible=True), # Show chat
 
119
  gr.update(value="", visible=False), # Clear error
120
  assistant_msgs # Initial chat history
121
  )
@@ -129,6 +251,7 @@ def authenticate(username, password):
129
 
130
  with gr.Blocks(fill_height=True) as demo:
131
 
 
132
  with gr.Column(visible=True) as login_section:
133
  gr.Markdown("### πŸ”’ Login Required")
134
  username_input = gr.Textbox(label="Username")
@@ -136,83 +259,155 @@ with gr.Blocks(fill_height=True) as demo:
136
  login_button = gr.Button("Login")
137
  error_message = gr.Text("", visible=False)
138
 
 
139
  with gr.Column(visible=False) as chat_section:
140
-
141
  chat_configuration = gr.Markdown("")
142
-
143
  spinner = gr.Markdown("⏳ Sto pensando...", visible=False)
144
 
145
- with gr.Row():
146
- map_btn = gr.Button("🧾 Map Summary")
147
- stuff_btn = gr.Button("πŸ“š Stuff Summary")
148
-
149
- chat = gr.Chatbot(
150
- label="Video Helper",
151
- type="messages"
152
- )
153
-
154
- input = gr.Textbox(
155
- label="Input",
156
- placeholder="Type something here..."
157
- )
158
  send_btn = gr.Button("Send")
159
 
160
  stored_message = gr.State()
161
  chat_history = gr.State(value=[])
162
 
163
- map_btn.click(
164
- fn=lambda history: get_summary("map", history),
165
- inputs=[chat_history],
166
- outputs=[chat]
167
- )
168
 
169
- stuff_btn.click(
170
- fn=lambda history: get_summary("stuff", history),
171
- inputs=[chat_history],
172
- outputs=[chat]
173
- )
174
 
175
- input.submit(
176
- fn=lambda x: (x, ""),
177
- inputs=input,
178
- outputs=[stored_message, input]
179
- ).then(
180
- fn=show_spinner,
181
- outputs=[spinner]
182
- ).then(
183
- fn=send_chat_message,
184
- inputs=[stored_message, chat_history],
185
- outputs=chat
186
- ).then(
187
- fn=hide_spinner,
188
- outputs=[spinner]
189
- )
190
-
191
- send_btn.click(
192
- fn=lambda x: (x, ""),
193
- inputs=input,
194
- outputs=[stored_message, input]
195
- ).then(
196
- fn=show_spinner,
197
- outputs=[spinner]
198
- ).then(
199
- fn=send_chat_message,
200
- inputs=[stored_message, chat_history],
201
- outputs=chat
202
- ).then(
203
- fn=hide_spinner,
204
- outputs=[spinner]
205
- )
206
 
 
207
  login_button.click(
208
  authenticate,
209
  [username_input, password_input],
210
- [login_section, chat_section, error_message, chat_history]
211
  ).then(
212
  fn=lambda history: history,
213
  inputs=[chat_history],
214
  outputs=[chat]
215
  )
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  if __name__ == "__main__":
218
  demo.launch()
 
33
  gr.update(interactive=interactive) # send_btn
34
  )
35
 
36
+ # def _add_trust_icon(text, level):
37
+ # icons = {
38
+ # "high": "🟒", # alta affidabilità
39
+ # "medium": "🟑", # simile ma non perfetto
40
+ # "summary": "🟠", # riassunto usato
41
+ # "low": "πŸ”΄", # basato solo sulla chat
42
+ # "no_context": "βšͺ️"
43
+ # }
44
+ # label = {
45
+ # "high": "Reliable",
46
+ # "medium": "Moderate Similarity",
47
+ # "summary": "Summary Used",
48
+ # "low": "No Source Match",
49
+ # "no_context": "No Context"
50
+ # }
51
+
52
+ # icon = icons.get(level, "βšͺ️")
53
+ # tooltip = label.get(level, "Unknown")
54
+
55
+ # # Prepend semaforo + etichetta (puoi anche usare HTML per Gradio futuro)
56
+ # return f"{icon} *{tooltip}*\n\n{text}"
57
+
58
+ def _add_trust_icon(text, support_level="unknown"):
59
  icons = {
60
+ "green": "🟒",
61
+ "yellow": "🟑",
62
+ "red": "πŸ”΄",
63
+ "unknown": "βšͺ️"
 
64
  }
65
+
66
+ labels = {
67
+ "green": "Supported by context",
68
+ "yellow": "Partially supported",
69
+ "red": "Not supported",
70
+ "unknown": "Support level unknown"
71
  }
72
 
73
+ icon = icons.get(support_level, "βšͺ️")
74
+ label = labels.get(support_level, "Support level unknown")
75
+
76
+ return f"{icon} *{label}*\n\n{text}"
77
+
78
+
79
+
80
+ def toggle_study_mode(is_study, index, history):
81
+ if is_study:
82
+ # πŸ”™ Uscita dalla modalitΓ  studio β†’ torna alla modalitΓ  standard
83
+ return (
84
+ "πŸŽ“ Study", # bottone torna Studio
85
+ gr.update(visible=True), # chat
86
+ gr.update(visible=True), # input
87
+ gr.update(visible=True), # send_btn
88
+ gr.update(visible=False), # study_chat
89
+ gr.update(visible=True), # summary_mode_btn
90
+ gr.update(visible=False), # chunk_nav
91
+ index,
92
+ history,
93
+ False
94
+ )
95
+ else:
96
+ # πŸŽ“ Entrata in modalitΓ  studio
97
+ return (
98
+ "❌ Studio off",
99
+ gr.update(visible=False), # chat
100
+ gr.update(visible=False), # input
101
+ gr.update(visible=False), # send_btn
102
+ gr.update(visible=True), # study_chat
103
+ gr.update(visible=False), # summary_mode_btn
104
+ gr.update(visible=True), # chunk_nav
105
+ 0,
106
+ [], # nuova chat studio
107
+ True
108
+ )
109
+
110
 
 
 
111
 
112
  def get_summary(summary_type, history):
113
  if history is None:
 
124
  label = "❓ Unknown Summary"
125
 
126
  if content:
127
+ history.append({"role": "user", "content": "Summary requested."})
128
  history.append({"role": "assistant", "content": f"{label}\n\n{content}"})
129
  else:
130
  history.append({"role": "assistant", "content": f"⚠️ No {label.lower()} available."})
131
 
132
  return history
133
 
134
+ # def send_chat_message(message, history):
135
+ # if history is None:
136
+ # history = []
137
+
138
+ # history.append({"role": "user", "content": message})
139
+ # bot_entry = {"role": "assistant", "content": ""}
140
+ # history.append(bot_entry)
141
+
142
+ # response = ""
143
+ # context_level = "high" # default
144
+
145
+ # # βœ… Usa la nuova funzione standard che restituisce dict con context_level
146
+ # stream = llm_manager.stream_message_standard(message)
147
+
148
+ # for chunk in stream:
149
+ # if isinstance(chunk, dict): # nuova struttura
150
+ # response += chunk["content"]
151
+ # context_level = chunk.get("context_level", "high")
152
+ # else: # retrocompatibilitΓ , se mai usato
153
+ # response += chunk
154
+
155
+ # # aggiornamento in tempo reale
156
+ # bot_entry["content"] = _add_trust_icon(response, context_level)
157
+ # yield history
158
+
159
+ # yield history
160
+
161
  def send_chat_message(message, history):
162
  if history is None:
163
  history = []
 
167
  history.append(bot_entry)
168
 
169
  response = ""
170
+ context_level = "high" # default
171
+ support_level = "green" # default (safe fallback)
172
 
173
+ # βœ… Usa la nuova funzione standard che restituisce dict con context_level e support_level
174
+ stream = llm_manager.stream_message_standard(message)
175
 
176
  for chunk in stream:
177
+ if isinstance(chunk, dict): # nuova struttura
178
+ content = chunk.get("content")
179
+ if content:
180
+ response += content
181
+
182
+ support_level = chunk.get("support_level", support_level)
183
+ #print(f"πŸ” SUPPORT LEVEL RECEIVED: {support_level}")
184
+ bot_entry["content"] = _add_trust_icon(response, support_level)
185
+ yield history
186
+
187
  else: # retrocompatibilitΓ 
188
  response += chunk
189
+ bot_entry["content"] = _add_trust_icon(response, support_level)
190
+ yield history
191
+
192
+
193
+ def load_chunk(index, history):
194
+ chunk = qdrant_manager.get_chunk_by_index(index)
195
+ if chunk:
196
+ llm_manager.set_focus_on_chunk(chunk)
197
+
198
+ # Aggiungi messaggio assistant con il contenuto del chunk
199
+ history.append({"role": "assistant", "content": f"πŸ“š **Chunk {index + 1}**\n\n{chunk}"})
200
+ return history, gr.update(visible=index > 0), gr.update(visible=True)
201
+ else:
202
+ history.append({"role": "assistant", "content": "⚠️ Chunk non trovato."})
203
+ return history, gr.update(visible=False), gr.update(visible=False)
204
+
205
+ def handle_study_message(msg, index, history):
206
+ if history is None:
207
+ history = []
208
+
209
+ history.append({"role": "user", "content": msg})
210
+ bot_entry = {"role": "assistant", "content": ""}
211
+ history.append(bot_entry)
212
+
213
+ response = ""
214
+ context_level = None
215
+
216
+ for partial in llm_manager.stream_message_study(msg):
217
+ content = partial.get("content")
218
+ support_level = partial.get("support_level", "unknown")
219
 
220
+ if content:
221
+ response += content
222
+ bot_entry["content"] = _add_trust_icon(response, support_level)
223
+ yield history
224
 
225
  yield history
226
 
 
237
  return (
238
  gr.update(visible=False), # Hide login
239
  gr.update(visible=True), # Show chat
240
+ gr.update(visible=True), # Show nav
241
  gr.update(value="", visible=False), # Clear error
242
  assistant_msgs # Initial chat history
243
  )
 
251
 
252
  with gr.Blocks(fill_height=True) as demo:
253
 
254
+ # === LOGIN ===
255
  with gr.Column(visible=True) as login_section:
256
  gr.Markdown("### πŸ”’ Login Required")
257
  username_input = gr.Textbox(label="Username")
 
259
  login_button = gr.Button("Login")
260
  error_message = gr.Text("", visible=False)
261
 
262
+ # === SEZIONE CHAT (visibile per default) ===
263
  with gr.Column(visible=False) as chat_section:
 
264
  chat_configuration = gr.Markdown("")
 
265
  spinner = gr.Markdown("⏳ Sto pensando...", visible=False)
266
 
267
+ input = gr.Textbox(label="Input", placeholder="Type something here...")
 
 
 
 
 
 
 
 
 
 
 
 
268
  send_btn = gr.Button("Send")
269
 
270
  stored_message = gr.State()
271
  chat_history = gr.State(value=[])
272
 
273
+ # === SEZIONE STUDIO (mostrata solo quando attiva) ===
274
+ with gr.Column(visible=False) as study_chat:
275
+ study_chatbox = gr.Chatbot(label="Study Moxde", type="messages")
276
+ study_input = gr.Textbox(placeholder="Ask something about the current chunk...")
277
+ study_send = gr.Button("Send")
278
 
279
+ # === NAVIGAZIONE MODALITΓ€ ===
280
+ with gr.Row(visible=False) as nav_section:
281
+ study_mode_btn = gr.Button("πŸŽ“ Study")
282
+ summary_mode_btn = gr.Button("🧠 Summary")
 
283
 
284
+ # === NAVIGAZIONE CHUNKS (mostrata solo in modalitΓ  studio) ===
285
+ with gr.Row(visible=False) as chunk_nav:
286
+ prev_chunk_btn = gr.Button("⬅️ Previous Chunk")
287
+ next_chunk_btn = gr.Button("➑️ Next Chunk")
288
+
289
+ # === STATE ===
290
+ study_chunk_index = gr.State(value=0)
291
+ study_history = gr.State(value=[])
292
+ is_study_mode = gr.State(value=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
+ # === AUTENTICAZIONE ===
295
  login_button.click(
296
  authenticate,
297
  [username_input, password_input],
298
+ [login_section, chat_section, nav_section, error_message, chat_history]
299
  ).then(
300
  fn=lambda history: history,
301
  inputs=[chat_history],
302
  outputs=[chat]
303
  )
304
 
305
+ # === CHAT STANDARD ===
306
+ input.submit(
307
+ fn=lambda x: (x, ""),
308
+ inputs=input,
309
+ outputs=[stored_message, input]
310
+ ).then(
311
+ fn=show_spinner,
312
+ outputs=[spinner]
313
+ ).then(
314
+ fn=send_chat_message,
315
+ inputs=[stored_message, chat_history],
316
+ outputs=chat
317
+ ).then(
318
+ fn=hide_spinner,
319
+ outputs=[spinner]
320
+ )
321
+
322
+ send_btn.click(
323
+ fn=lambda x: (x, ""),
324
+ inputs=input,
325
+ outputs=[stored_message, input]
326
+ ).then(
327
+ fn=show_spinner,
328
+ outputs=[spinner]
329
+ ).then(
330
+ fn=send_chat_message,
331
+ inputs=[stored_message, chat_history],
332
+ outputs=chat
333
+ ).then(
334
+ fn=hide_spinner,
335
+ outputs=[spinner]
336
+ )
337
+
338
+ # === CHAT STUDIO ===
339
+ study_input.submit(
340
+ fn=lambda x: (x, ""),
341
+ inputs=study_input,
342
+ outputs=[stored_message, study_input]
343
+ ).then(
344
+ fn=handle_study_message,
345
+ inputs=[stored_message, study_chunk_index, study_history],
346
+ outputs=study_chatbox
347
+ )
348
+
349
+ study_send.click(
350
+ fn=lambda x: (x, ""),
351
+ inputs=study_input,
352
+ outputs=[stored_message, study_input]
353
+ ).then(
354
+ fn=handle_study_message,
355
+ inputs=[stored_message, study_chunk_index, study_history],
356
+ outputs=study_chatbox
357
+ )
358
+
359
+ # === MODALITΓ€ STUDIO ON/OFF ===
360
+ study_mode_btn.click(
361
+ fn=toggle_study_mode,
362
+ inputs=[is_study_mode, study_chunk_index, study_history],
363
+ outputs=[
364
+ study_mode_btn,
365
+ chat,
366
+ input,
367
+ send_btn,
368
+ study_chat,
369
+ summary_mode_btn,
370
+ chunk_nav,
371
+ study_chunk_index,
372
+ study_history,
373
+ is_study_mode
374
+ ]
375
+ ).then(
376
+ fn=load_chunk,
377
+ inputs=[study_chunk_index, study_history],
378
+ outputs=[study_chatbox, prev_chunk_btn, next_chunk_btn]
379
+ )
380
+
381
+ # === MOSTRA SUMMARY (solo modalitΓ  standard) ===
382
+ summary_mode_btn.click(
383
+ fn=get_summary,
384
+ inputs=[gr.State("map"), chat_history],
385
+ outputs=[chat]
386
+ )
387
+
388
+ # === NAVIGAZIONE CHUNKS ===
389
+ next_chunk_btn.click(
390
+ fn=lambda idx: idx + 1,
391
+ inputs=study_chunk_index,
392
+ outputs=study_chunk_index
393
+ ).then(
394
+ fn=load_chunk,
395
+ inputs=[study_chunk_index, study_history],
396
+ outputs=[study_chatbox, prev_chunk_btn, next_chunk_btn]
397
+ )
398
+
399
+ prev_chunk_btn.click(
400
+ fn=lambda idx: max(idx - 1, 0),
401
+ inputs=study_chunk_index,
402
+ outputs=study_chunk_index
403
+ ).then(
404
+ fn=load_chunk,
405
+ inputs=[study_chunk_index, study_history],
406
+ outputs=[study_chatbox, prev_chunk_btn, next_chunk_btn]
407
+ )
408
+
409
+
410
+
411
+
412
  if __name__ == "__main__":
413
  demo.launch()
config.py CHANGED
@@ -9,6 +9,10 @@ MODEL = "gpt-4o-mini"
9
  LANGUAGE = "en"
10
 
11
  crossencoder_model = None
 
 
 
 
12
  credentials = {}
13
 
14
  def check_user(username, password):
@@ -25,9 +29,9 @@ def initialize(app=None):
25
  try:
26
  load_dotenv()
27
 
28
- if crossencoder_model is None:
29
  print("Loading CrossEncoder model...")
30
- crossencoder_model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
31
  print("CrossEncoder model loaded!")
32
 
33
  credentials[os.getenv("USERNAME")] = os.getenv("PASSWORD")
 
9
  LANGUAGE = "en"
10
 
11
  crossencoder_model = None
12
+ crossencoder_model_name = None
13
+ #crossencoder_model_name = "cross-encoder/ms-marco-MiniLM-L-6-v2"
14
+ #crossencoder_model_name = "cross-encoder/ms-marco-MiniLM-L-12-v2"
15
+
16
  credentials = {}
17
 
18
  def check_user(username, password):
 
29
  try:
30
  load_dotenv()
31
 
32
+ if crossencoder_model_name:
33
  print("Loading CrossEncoder model...")
34
+ crossencoder_model = CrossEncoder(crossencoder_model_name)
35
  print("CrossEncoder model loaded!")
36
 
37
  credentials[os.getenv("USERNAME")] = os.getenv("PASSWORD")
data/txt/Key statisitcs startups.txt CHANGED
@@ -1,5 +1,3 @@
1
- (Transcribed by TurboScribe.ai. Go Unlimited to remove this message.)
2
-
3
  Hi everyone and welcome to this video lecture from the Entrepreneurial Literacy Initiative. I am Martti Wask and today we're going to present to you some brief key statistics about entrepreneurship. Before we go into the mud, let me give you some brief definition of what I understand about a startup.
4
 
5
  So to some extent startups are just like any other business, right, or more specifically small businesses because they're small at the beginning. But there are three characteristics that make them unique. Startups are young, high growth orientated businesses.
 
 
 
1
  Hi everyone and welcome to this video lecture from the Entrepreneurial Literacy Initiative. I am Martti Wask and today we're going to present to you some brief key statistics about entrepreneurship. Before we go into the mud, let me give you some brief definition of what I understand about a startup.
2
 
3
  So to some extent startups are just like any other business, right, or more specifically small businesses because they're small at the beginning. But there are three characteristics that make them unique. Startups are young, high growth orientated businesses.
utilities/llm/LlmManager.py CHANGED
@@ -1,76 +1,139 @@
 
1
  from langchain_openai import ChatOpenAI
2
  from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
3
  from utilities.vectorstore.SummaryManager import SummaryManager
4
 
5
  MAX_MESSAGES = 50
6
 
7
- system_message_it = """
8
- Sei ELI, un assistente che aiuta gli studenti ad analizzare un video e rispondere a domande su di esso.
9
- Basandoti sul contesto fornito, rispondi alla domanda dell'utente.
10
-
11
- - Contesto: {context}
12
- - Domanda: {question}
13
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  system_message_en = """
16
  You are ELI, an assistant that helps students analyze a video and answer questions about it.
17
- Based on the provided context, answer the user's question.
18
 
19
- - Context: {context}
20
- - Question: {question}
21
- """
22
 
23
- def get_system_message(language="en"):
24
- if language == "it":
25
- return system_message_it
26
- else:
27
- return system_message_en
28
 
29
- fallback_prompt_en = """
30
- You are ELI, an assistant that helps students analyze a video and answer questions about it.
31
- Unfortunately, no relevant context could be found in the provided material.
 
 
 
 
 
 
 
32
 
33
- - If possible, try to answer based on the previous conversation.
34
- - Otherwise, inform the user that no verified information is available to answer the question reliably.
35
 
36
- - Question: {question}
 
37
  """
38
 
39
- fallback_prompt_it = """
 
 
 
40
  Sei ELI, un assistente che aiuta gli studenti ad analizzare un video e rispondere a domande su di esso.
41
- Purtroppo, non Γ¨ stato trovato alcun contesto rilevante nei materiali forniti.
42
 
43
- - Se possibile, prova a rispondere basandoti sulla conversazione precedente.
44
- - Altrimenti, informa l’utente che non hai informazioni verificate per rispondere in modo affidabile.
45
 
46
- - Domanda: {question}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  """
48
 
49
- def get_fallback_prompt(language="en"):
50
- fallback_prompts = {
51
- "en": fallback_prompt_en,
52
- "it": fallback_prompt_it
53
- }
54
- return fallback_prompts.get(language, fallback_prompts["en"])
55
-
56
-
57
- def get_disclaimer(context_level, language="en"):
58
- disclaimers = {
59
- "en": {
60
- "medium": "\n\n⚠️ Note: the retrieved context has moderate similarity. The answer may not be fully reliable.",
61
- "summary": "\n\n🟠 Note: the context is based on a general summary of the content. Please verify the information if needed.",
62
- "low": "\n\n⚠️ No reliable information was found in the source material. The answer may rely only on the conversation.",
63
- "no_context": "\n\n⚠️ No context available. The assistant will try to respond based on previous conversation, if possible."
64
- },
65
- "it": {
66
- "medium": "\n\n⚠️ Nota: il contesto recuperato ha una similarità moderata. La risposta potrebbe non essere pienamente affidabile.",
67
- "summary": "\n\n🟠 Nota: il contesto usato è un riassunto generale del contenuto. Verifica le fonti se necessario.",
68
- "low": "\n\n⚠️ Nessuna informazione affidabile trovata nei materiali. La risposta potrebbe basarsi solo sulla conversazione.",
69
- "no_context": "\n\n⚠️ Nessun contesto disponibile. L’assistente proverΓ  a rispondere in base alla conversazione, se possibile."
70
- }
71
- }
72
 
73
- return disclaimers.get(language, {}).get(context_level, "")
 
 
 
 
 
 
 
74
 
75
  class LlmManager():
76
 
@@ -88,48 +151,177 @@ class LlmManager():
88
  def reset_messages(self, context, question):
89
  self.messages = [SystemMessage(content=self.system_message.format(context=context, question=question))]
90
 
91
- def stream_message(self, message, contextualize=False):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  """
93
- Streaming equivalente a send_message, con gestione dinamica del contesto e localizzazione.
 
 
 
 
94
  """
95
- if contextualize:
96
- context = ""
97
- context_level = "no_context"
 
 
 
98
 
99
- if self.qdrant_manager and self.qdrant_manager.is_loaded():
100
- context, context_level = self.qdrant_manager.get_context_for_query(message)
101
- print(f"πŸ“š Contesto recuperato [{context_level}]: {context[:100]}")
102
 
103
- disclaimer = get_disclaimer(context_level, self.language)
104
- base_prompt = get_system_message(self.language)
105
 
106
- # Caso: contesto assente (no_context/low) ➝ prompt fallback
107
- if not context.strip() and context_level in ["low", "no_context"]:
108
- formatted_message = get_fallback_prompt(self.language).format(question=message)
109
- else:
110
- formatted_message = base_prompt.format(context=context, question=message) + disclaimer
 
 
 
 
 
 
 
 
111
 
112
- # Inserisci o aggiorna il SystemMessage
113
- if self.messages and isinstance(self.messages[0], SystemMessage):
114
- self.messages[0] = SystemMessage(content=formatted_message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  else:
116
- self.messages.insert(0, SystemMessage(content=formatted_message))
 
 
117
 
118
- # Aggiungi il messaggio utente
119
- self.messages.append(HumanMessage(content=message))
 
 
 
 
 
 
 
120
 
121
- else:
122
- self.messages.append(HumanMessage(content=message))
123
 
 
 
 
 
 
 
 
124
  self._roll_messages()
125
 
 
126
  response = ""
 
 
 
127
  for chunk in self.llm.stream(self.messages):
128
- response += chunk.content
129
- yield {"content": chunk.content, "context_level": context_level}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
  self.messages.append(AIMessage(content=response))
132
- return response, context_level
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  def send_message(self, message, contextualize=False):
135
  if contextualize:
@@ -203,6 +395,11 @@ class LlmManager():
203
  summary, _, _ = summary_manager.do_summary_stuff()
204
  return summary
205
 
 
 
 
 
 
206
  def _roll_messages(self):
207
  """
208
  Keeps only the last `MAX_MESSAGES` from `messages`, excluding the first (SystemMessage).
 
1
+ import json, re
2
  from langchain_openai import ChatOpenAI
3
  from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
4
  from utilities.vectorstore.SummaryManager import SummaryManager
5
 
6
  MAX_MESSAGES = 50
7
 
8
+ # system_message_it = """
9
+ # Sei ELI, un assistente che aiuta gli studenti ad analizzare un video e rispondere a domande su di esso.
10
+ # Basandoti sul contesto fornito, rispondi alla domanda dell'utente.
11
+
12
+ # - Contesto: {context}
13
+ # - Domanda: {question}
14
+ # """
15
+
16
+ # system_message_en = """
17
+ # You are ELI, an assistant that helps students analyze a video and answer questions about it.
18
+ # Based on the provided context, answer the user's question.
19
+
20
+ # - Context: {context}
21
+ # - Question: {question}
22
+ # """
23
+
24
+ # def get_system_message(language="en"):
25
+ # if language == "it":
26
+ # return system_message_it
27
+ # else:
28
+ # return system_message_en
29
+
30
+ # fallback_prompt_en = """
31
+ # You are ELI, an assistant that helps students analyze a video and answer questions about it.
32
+ # Unfortunately, no relevant context could be found in the provided material.
33
+
34
+ # - If possible, try to answer based on the previous conversation.
35
+ # - Otherwise, inform the user that no verified information is available to answer the question reliably.
36
+
37
+ # - Question: {question}
38
+ # """
39
+
40
+ # fallback_prompt_it = """
41
+ # Sei ELI, un assistente che aiuta gli studenti ad analizzare un video e rispondere a domande su di esso.
42
+ # Purtroppo, non Γ¨ stato trovato alcun contesto rilevante nei materiali forniti.
43
+
44
+ # - Se possibile, prova a rispondere basandoti sulla conversazione precedente.
45
+ # - Altrimenti, informa l’utente che non hai informazioni verificate per rispondere in modo affidabile.
46
+
47
+ # - Domanda: {question}
48
+ # """
49
+
50
+ # def get_fallback_prompt(language="en"):
51
+ # fallback_prompts = {
52
+ # "en": fallback_prompt_en,
53
+ # "it": fallback_prompt_it
54
+ # }
55
+ # return fallback_prompts.get(language, fallback_prompts["en"])
56
+
57
+ # def get_disclaimer(context_level, language="en"):
58
+ # disclaimers = {
59
+ # "en": {
60
+ # "medium": "\n\n⚠️ Note: the retrieved context has moderate similarity. The answer may not be fully reliable.",
61
+ # "summary": "\n\n🟠 Note: the context is based on a general summary of the content. Please verify the information if needed.",
62
+ # "low": "\n\n⚠️ No reliable information was found in the source material. The answer may rely only on the conversation.",
63
+ # "no_context": "\n\n⚠️ No context available. The assistant will try to respond based on previous conversation, if possible."
64
+ # },
65
+ # "it": {
66
+ # "medium": "\n\n⚠️ Nota: il contesto recuperato ha una similarità moderata. La risposta potrebbe non essere pienamente affidabile.",
67
+ # "summary": "\n\n🟠 Nota: il contesto usato è un riassunto generale del contenuto. Verifica le fonti se necessario.",
68
+ # "low": "\n\n⚠️ Nessuna informazione affidabile trovata nei materiali. La risposta potrebbe basarsi solo sulla conversazione.",
69
+ # "no_context": "\n\n⚠️ Nessun contesto disponibile. L’assistente proverΓ  a rispondere in base alla conversazione, se possibile."
70
+ # }
71
+ # }
72
+
73
+ # return disclaimers.get(language, {}).get(context_level, "")
74
 
75
  system_message_en = """
76
  You are ELI, an assistant that helps students analyze a video and answer questions about it.
 
77
 
78
+ Evaluate how well the provided context supports answering the user's question.
 
 
79
 
80
+ If the context is not sufficient, you may use the previous conversation to help answer, if possible.
 
 
 
 
81
 
82
+ Use one of the following support levels:
83
+ - green: clearly supported by the context
84
+ - yellow: partially supported or inferred
85
+ - red: not supported by the context (but you may still answer based on the conversation)
86
+
87
+ At the beginning of your answer, write a line with the support level in the following format:
88
+
89
+ [SUPPORT: green]
90
+
91
+ Then go to the next line and provide the actual answer, with proper formatting.
92
 
93
+ Context:
94
+ {context}
95
 
96
+ Question:
97
+ {question}
98
  """
99
 
100
+
101
+
102
+
103
+ system_message_it = """
104
  Sei ELI, un assistente che aiuta gli studenti ad analizzare un video e rispondere a domande su di esso.
 
105
 
106
+ Valuta quanto il contesto fornito ti permette di rispondere in modo affidabile alla domanda.
 
107
 
108
+ Se il contesto non Γ¨ sufficiente, puoi anche usare la conversazione precedente per rispondere, se disponibile.
109
+
110
+ Usa uno dei seguenti livelli di supporto:
111
+ - verde: supportata chiaramente dal contesto
112
+ - giallo: supportata parzialmente o inferita
113
+ - rosso: non supportata dal contesto (ma potresti rispondere comunque grazie alla conversazione)
114
+
115
+ All'inizio della tua risposta scrivi una riga con il livello di supporto nel formato:
116
+
117
+ [SUPPORT: verde]
118
+
119
+ Poi vai a capo e fornisci la risposta normalmente, con la formattazione corretta.
120
+
121
+ Contesto:
122
+ {context}
123
+
124
+ Domanda:
125
+ {question}
126
  """
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
+
130
+
131
+ def get_system_message(language="en"):
132
+ if language == "it":
133
+ return system_message_it
134
+ else:
135
+ return system_message_en
136
+
137
 
138
  class LlmManager():
139
 
 
151
  def reset_messages(self, context, question):
152
  self.messages = [SystemMessage(content=self.system_message.format(context=context, question=question))]
153
 
154
+ # def stream_message_standard(self, message):
155
+ # """
156
+ # ModalitΓ  STANDARD: recupera il contesto semanticamente da Qdrant.
157
+ # """
158
+ # context = ""
159
+ # context_level = "no_context"
160
+
161
+ # if self.qdrant_manager and self.qdrant_manager.is_loaded():
162
+ # context, context_level = self.qdrant_manager.get_context_for_query(message)
163
+ # print(f"πŸ“š Contesto recuperato [{context_level}]: {context[:100]}")
164
+
165
+ # disclaimer = get_disclaimer(context_level, self.language)
166
+ # base_prompt = get_system_message(self.language)
167
+
168
+ # # Fallback se il contesto Γ¨ debole o assente
169
+ # if not context.strip() and context_level in ["low", "no_context"]:
170
+ # formatted_message = get_fallback_prompt(self.language).format(question=message)
171
+ # else:
172
+ # formatted_message = base_prompt.format(context=context, question=message) + disclaimer
173
+
174
+ # # Aggiorna o imposta il messaggio di sistema
175
+ # if self.messages and isinstance(self.messages[0], SystemMessage):
176
+ # self.messages[0] = SystemMessage(content=formatted_message)
177
+ # else:
178
+ # self.messages.insert(0, SystemMessage(content=formatted_message))
179
+
180
+ # self.messages.append(HumanMessage(content=message))
181
+ # self._roll_messages()
182
+
183
+ # response = ""
184
+ # for chunk in self.llm.stream(self.messages):
185
+ # response += chunk.content
186
+ # yield {"content": chunk.content, "context_level": context_level} # βœ… stream + context_level
187
+
188
+ # self.messages.append(AIMessage(content=response))
189
+
190
+ def stream_message_standard(self, message):
191
  """
192
+ ModalitΓ  STANDARD:
193
+ - Recupera contesto da Qdrant
194
+ - Costruisce prompt con system message
195
+ - Mantiene memoria conversazionale
196
+ - Streamma solo la parte di risposta dopo [SUPPORT: ...]
197
  """
198
+ context = ""
199
+ context_level = "no_context"
200
+
201
+ if self.qdrant_manager and self.qdrant_manager.is_loaded():
202
+ context, context_level = self.qdrant_manager.get_context_for_query(message)
203
+ print(f"πŸ“š Contesto recuperato [{context_level}]: {context[:100]}")
204
 
205
+ base_prompt = get_system_message(self.language)
 
 
206
 
207
+ formatted_message = base_prompt.format(context=context, question=message)
 
208
 
209
+ # Mantieni la cronologia
210
+ if self.messages and isinstance(self.messages[0], SystemMessage):
211
+ self.messages[0] = SystemMessage(content=formatted_message)
212
+ else:
213
+ self.messages.insert(0, SystemMessage(content=formatted_message))
214
+
215
+ self.messages.append(HumanMessage(content=message))
216
+ self._roll_messages()
217
+
218
+ buffer = ""
219
+ response = ""
220
+ support_level = None
221
+ start_streaming = False
222
 
223
+ for chunk in self.llm.stream(self.messages):
224
+ chunk_text = chunk.content
225
+ if not start_streaming:
226
+ buffer += chunk_text
227
+ match = re.search(r"\[SUPPORT:\s*(green|yellow|red|verde|giallo|rosso)\]", buffer, re.IGNORECASE)
228
+ if match:
229
+ raw_level = match.group(1).lower().strip()
230
+ support_level = {
231
+ "verde": "green",
232
+ "giallo": "yellow",
233
+ "rosso": "red"
234
+ }.get(raw_level, raw_level)
235
+
236
+ print(f"βœ… SUPPORT LEVEL: {support_level}")
237
+
238
+ # Inizia lo streaming dal contenuto dopo il tag
239
+ after = buffer[match.end():].lstrip()
240
+ if after:
241
+ response += after
242
+ #yield {"content": after, "context_level": context_level}
243
+ yield {"content": after, "support_level": support_level or "unknown"}
244
+ buffer = ""
245
+ start_streaming = True
246
  else:
247
+ response += chunk_text
248
+ #yield {"content": chunk_text, "context_level": context_level}
249
+ yield {"content": chunk_text, "support_level": support_level or "unknown"}
250
 
251
+ response = response.strip()
252
+ print(f"πŸ€– ELI: {response}")
253
+ self.messages.append(AIMessage(content=response))
254
+
255
+ yield {
256
+ "content": None,
257
+ #"context_level": context_level,
258
+ "support_level": support_level or "unknown"
259
+ }
260
 
 
 
261
 
262
+ def stream_message_study(self, message):
263
+ """
264
+ ModalitΓ  STUDIO: il contesto Γ¨ giΓ  stato fissato tramite set_focus_on_chunk().
265
+ La risposta include anche il livello di supporto ([SUPPORT: ...]).
266
+ """
267
+ context_level = "fixed" # contesto chunk-based
268
+ self.messages.append(HumanMessage(content=message))
269
  self._roll_messages()
270
 
271
+ buffer = ""
272
  response = ""
273
+ support_level = None
274
+ start_streaming = False
275
+
276
  for chunk in self.llm.stream(self.messages):
277
+ chunk_text = chunk.content
278
+ if not start_streaming:
279
+ buffer += chunk_text
280
+ match = re.search(r"\[SUPPORT:\s*(green|yellow|red|verde|giallo|rosso)\]", buffer, re.IGNORECASE)
281
+ if match:
282
+ raw_level = match.group(1).lower().strip()
283
+ support_level = {
284
+ "verde": "green",
285
+ "giallo": "yellow",
286
+ "rosso": "red"
287
+ }.get(raw_level, raw_level)
288
+
289
+ print(f"βœ… SUPPORT LEVEL (study): {support_level}")
290
+
291
+ after = buffer[match.end():].lstrip()
292
+ if after:
293
+ response += after
294
+ yield {"content": after, "support_level": support_level or "unknown"}
295
+ buffer = ""
296
+ start_streaming = True
297
+ else:
298
+ response += chunk_text
299
+ yield {"content": chunk_text, "support_level": support_level or "unknown"}
300
 
301
  self.messages.append(AIMessage(content=response))
302
+
303
+ yield {
304
+ "content": None,
305
+ "support_level": support_level or "unknown"
306
+ }
307
+
308
+
309
+ # def stream_message_study(self, message):
310
+ # """
311
+ # ModalitΓ  STUDIO: il contesto Γ¨ giΓ  stato fissato tramite set_focus_on_chunk().
312
+ # """
313
+ # context_level = "fixed" # πŸ” contesto chunk-based
314
+
315
+ # self.messages.append(HumanMessage(content=message))
316
+ # self._roll_messages()
317
+
318
+ # response = ""
319
+ # for chunk in self.llm.stream(self.messages):
320
+ # response += chunk.content
321
+ # yield {"content": chunk.content, "context_level": context_level}
322
+
323
+ # self.messages.append(AIMessage(content=response))
324
+
325
 
326
  def send_message(self, message, contextualize=False):
327
  if contextualize:
 
395
  summary, _, _ = summary_manager.do_summary_stuff()
396
  return summary
397
 
398
+ def set_focus_on_chunk(self, chunk_text):
399
+ self.messages = [
400
+ SystemMessage(content=self.system_message.format(context=chunk_text, question=""))
401
+ ]
402
+
403
  def _roll_messages(self):
404
  """
405
  Keeps only the last `MAX_MESSAGES` from `messages`, excluding the first (SystemMessage).
utilities/llm/__pycache__/LlmManager.cpython-312.pyc CHANGED
Binary files a/utilities/llm/__pycache__/LlmManager.cpython-312.pyc and b/utilities/llm/__pycache__/LlmManager.cpython-312.pyc differ
 
utilities/vectorstore/QdrantLangchainManager.py CHANGED
@@ -200,6 +200,34 @@ class QdrantLangchainManager:
200
  print(f"Error fetching documents from Qdrant: {e}")
201
  return []
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  def get_context_for_query(self, query, top_k=8):
204
  print("πŸ”Ž get_context_for_query:", query)
205
 
@@ -219,7 +247,14 @@ class QdrantLangchainManager:
219
  print("⚠️ Nessun documento con type='content' trovato.")
220
  return "", "no_context"
221
 
222
- print(f"πŸ“¦ Trovati {len(docs_with_scores)} documenti candidati.")
 
 
 
 
 
 
 
223
 
224
  # Se non c'Γ¨ reranker, usa soglia base
225
  if not self.reranker:
@@ -241,9 +276,9 @@ class QdrantLangchainManager:
241
  high_conf, medium_conf = [], []
242
 
243
  for (doc, _), score in reranked:
244
- if score > 0.7:
245
  high_conf.append(doc.page_content)
246
- elif score > 0.3:
247
  medium_conf.append(doc.page_content)
248
 
249
  if high_conf:
@@ -287,7 +322,6 @@ class QdrantLangchainManager:
287
  print(f"❌ Errore nel recupero del contesto: {e}")
288
  return "", "no_context"
289
 
290
-
291
  def delete_collection(self, collection_name):
292
  try:
293
  self.client.delete_collection(collection_name)
@@ -331,34 +365,25 @@ class QdrantLangchainManager:
331
 
332
  return merged_chunks, merge_performed
333
 
334
- # def _reranking(self, query, docs_with_scores):
335
- # if not self.reranker:
336
- # print("⚠️ Reranker not initialized. Skipping reranking.")
337
- # return docs_with_scores
338
-
339
- # query_pairs = [(query, doc.page_content) for doc, _ in docs_with_scores]
340
- # new_scores = self.reranker.predict(query_pairs)
341
- # return sorted(zip(docs_with_scores, new_scores), key=lambda x: x[1], reverse=True)
342
-
343
  def _reranking(self, query, docs_with_scores):
344
  if not self.reranker:
345
  print("⚠️ Reranker not initialized. Skipping reranking.")
346
  return docs_with_scores
347
-
 
348
  query_pairs = [(query, doc.page_content) for doc, _ in docs_with_scores]
349
-
350
- # Calcola i logit grezzi
351
  raw_scores = self.reranker.predict(query_pairs)
352
-
353
- # Applica sigmoid per ottenere probabilitΓ  tra 0 e 1
354
- prob_scores = sigmoid(torch.tensor(raw_scores)).tolist()
355
 
356
- # Log per debugging
357
- print("πŸ“ˆ Reranking scores (sigmoid-normalized):")
358
- for i, ((doc, old_score), prob) in enumerate(zip(docs_with_scores, prob_scores)):
359
- print(f"{i+1:02d}. chunk_index={doc.metadata.get('chunk_index', '-')}, score={prob:.3f}")
 
 
 
360
 
361
- return sorted(zip(docs_with_scores, prob_scores), key=lambda x: x[1], reverse=True)
362
 
363
  def _calculate_tokens(self, text):
364
  """Calculate the number of tokens in the given text."""
 
200
  print(f"Error fetching documents from Qdrant: {e}")
201
  return []
202
 
203
+ def get_chunk_by_index(self, index: int):
204
+ try:
205
+ results = self.client.scroll(
206
+ collection_name=self.collection_name,
207
+ scroll_filter=Filter(
208
+ must=[
209
+ FieldCondition(
210
+ key="metadata.chunk_index",
211
+ match=MatchValue(value=index)
212
+ ),
213
+ FieldCondition(
214
+ key="metadata.type",
215
+ match=MatchValue(value="content")
216
+ )
217
+ ]
218
+ ),
219
+ limit=1,
220
+ with_payload=True
221
+ )
222
+
223
+ if results[0]:
224
+ return results[0][0].payload.get("page_content", "")
225
+ else:
226
+ return None
227
+ except Exception as e:
228
+ print(f"❌ Error retrieving chunk {index}: {e}")
229
+ return None
230
+
231
  def get_context_for_query(self, query, top_k=8):
232
  print("πŸ”Ž get_context_for_query:", query)
233
 
 
247
  print("⚠️ Nessun documento con type='content' trovato.")
248
  return "", "no_context"
249
 
250
+ # print(f"πŸ“¦ Trovati {len(docs_with_scores)} documenti candidati.")
251
+
252
+ # Log original scores (solo per confronto visivo)
253
+ # print("\nπŸ“„ Original similarity scores (with sigmoid just for print):")
254
+ # for i, (doc, original_score) in enumerate(docs_with_scores):
255
+ # chunk_index = doc.metadata.get("chunk_index", "-")
256
+ # sigmoid_score = sigmoid(torch.tensor(original_score)).item()
257
+ # print(f"{i+1:02d}. chunk_index={chunk_index}, original_score={original_score:.3f}, sigmoid(original)={sigmoid_score:.3f}")
258
 
259
  # Se non c'Γ¨ reranker, usa soglia base
260
  if not self.reranker:
 
276
  high_conf, medium_conf = [], []
277
 
278
  for (doc, _), score in reranked:
279
+ if score > 1:
280
  high_conf.append(doc.page_content)
281
+ elif score > -5:
282
  medium_conf.append(doc.page_content)
283
 
284
  if high_conf:
 
322
  print(f"❌ Errore nel recupero del contesto: {e}")
323
  return "", "no_context"
324
 
 
325
  def delete_collection(self, collection_name):
326
  try:
327
  self.client.delete_collection(collection_name)
 
365
 
366
  return merged_chunks, merge_performed
367
 
 
 
 
 
 
 
 
 
 
368
  def _reranking(self, query, docs_with_scores):
369
  if not self.reranker:
370
  print("⚠️ Reranker not initialized. Skipping reranking.")
371
  return docs_with_scores
372
+
373
+ # Prepara input per reranker
374
  query_pairs = [(query, doc.page_content) for doc, _ in docs_with_scores]
375
+
376
+ # Ottieni i punteggi dal reranker (giΓ  in scala 0–1, ma non da sigmoid)
377
  raw_scores = self.reranker.predict(query_pairs)
 
 
 
378
 
379
+ print("\nπŸ“ˆ Reranker scores (raw):")
380
+ for i, ((doc, _), score) in enumerate(zip(docs_with_scores, raw_scores)):
381
+ chunk_index = doc.metadata.get("chunk_index", "-")
382
+ print(f"{i+1:02d}. chunk_index={chunk_index}, reranked_score={score:.4f}")
383
+
384
+ # Ritorna risultati ordinati per score decrescente
385
+ return sorted(zip(docs_with_scores, raw_scores), key=lambda x: x[1], reverse=True)
386
 
 
387
 
388
  def _calculate_tokens(self, text):
389
  """Calculate the number of tokens in the given text."""
utilities/vectorstore/SummaryManager.py CHANGED
@@ -220,14 +220,24 @@ class SummaryManager:
220
  print("❌ No documents found in collection.")
221
  return None, 0, 0
222
 
223
- print(len(all_documents), flush=True)
 
 
 
 
 
 
 
 
 
 
224
 
225
- # STEP 2: extract vectors & text
226
- embeddings = [doc["vector"] for doc in all_documents]
227
- documents = [doc["payload"]["page_content"] for doc in all_documents]
228
- metadata = [doc["payload"] for doc in all_documents]
229
 
230
- # STEP 3: select up to MAX_SELECTED_DOCS chunks via KMeans
231
  MAX_SELECTED_DOCS = 5
232
  selected_docs = self._select_best_chunks(
233
  documents=documents,
@@ -239,7 +249,7 @@ class SummaryManager:
239
  total_tokens = sum(self.llm.get_num_tokens(doc.page_content) for doc in selected_docs)
240
  print(f"βœ… Selected {len(selected_docs)} docs with total tokens: {total_tokens}")
241
 
242
- # STEP 4: load LangChain prompts
243
  map_prompt_template = PromptTemplate(template=get_map_prompt(self.language), input_variables=["text"])
244
  combine_prompt_template = PromptTemplate(template=get_combine_prompt(self.language), input_variables=["text"])
245
 
@@ -257,7 +267,7 @@ class SummaryManager:
257
  token_count = self.llm.get_num_tokens(doc.page_content)
258
  print(f"Chunk {i+1}: {token_count} tokens")
259
 
260
- # STEP 5: run the chain with token tracking
261
  with get_openai_callback() as cb:
262
  result = summary_chain.invoke({"input_documents": selected_docs})
263
  input_tokens_used = cb.prompt_tokens
@@ -268,7 +278,7 @@ class SummaryManager:
268
  full_summary = result['output_text']
269
  print("βœ… Map-reduce summary generated.")
270
 
271
- # STEP 6: store the final summary
272
  inserted = self.qdrant_manager.insert_text(
273
  text=full_summary,
274
  metadata={
@@ -301,14 +311,22 @@ class SummaryManager:
301
  print("❌ No documents found in collection.")
302
  return None, 0, 0
303
 
304
- #print(len(all_documents), flush=True)
 
 
 
 
305
 
306
- # STEP 2: extract vectors & text
307
- embeddings = [doc["vector"] for doc in all_documents]
308
- documents = [doc["payload"]["page_content"] for doc in all_documents]
309
- metadata = [doc["payload"] for doc in all_documents]
310
 
311
- # STEP 3: selezione intelligente con fallback a clustering
 
 
 
 
 
312
  selected_docs = self._get_chunks_for_stuff(
313
  documents=documents,
314
  metadata=metadata,
@@ -321,7 +339,7 @@ class SummaryManager:
321
  total_tokens = sum(self.llm.get_num_tokens(doc.page_content) for doc in selected_docs)
322
  print(f"βœ… Selected {len(selected_docs)} docs with total tokens: {total_tokens}")
323
 
324
- # STEP 4: load chain
325
  combine_prompt_template = PromptTemplate(
326
  template=get_combine_prompt(self.language),
327
  input_variables=["text"]
@@ -335,7 +353,7 @@ class SummaryManager:
335
  verbose=False
336
  )
337
 
338
- # STEP 5: run the chain with token tracking
339
  with get_openai_callback() as cb:
340
  result = summary_chain.invoke({"input_documents": selected_docs})
341
  input_tokens_used = cb.prompt_tokens
@@ -346,7 +364,7 @@ class SummaryManager:
346
  full_summary = result['output_text']
347
  print("βœ… Stuff summary generated.")
348
 
349
- # STEP 6: store the final summary
350
  inserted = self.qdrant_manager.insert_text(
351
  text=full_summary,
352
  metadata={
@@ -359,7 +377,7 @@ class SummaryManager:
359
  print("πŸ“ Final summary saved to vector store.")
360
 
361
  return full_summary, input_tokens_used, output_tokens_used
362
-
363
  def _find_closest_embeddings(self,vectors, num_clusters, kmeans):
364
  closest_indices = []
365
  for i in range(num_clusters):
 
220
  print("❌ No documents found in collection.")
221
  return None, 0, 0
222
 
223
+ # STEP 2: filtra fuori i documenti di tipo summary
224
+ filtered_docs = [
225
+ doc for doc in all_documents
226
+ if not str(doc["payload"].get("type", "")).endswith("summary")
227
+ ]
228
+
229
+ if not filtered_docs:
230
+ print("❌ No non-summary documents available for summarization.")
231
+ return None, 0, 0
232
+
233
+ print(f"πŸ“„ {len(filtered_docs)} documents after filtering summaries.", flush=True)
234
 
235
+ # STEP 3: extract vectors & text
236
+ embeddings = [doc["vector"] for doc in filtered_docs]
237
+ documents = [doc["payload"]["page_content"] for doc in filtered_docs]
238
+ metadata = [doc["payload"] for doc in filtered_docs]
239
 
240
+ # STEP 4: select up to MAX_SELECTED_DOCS chunks via KMeans
241
  MAX_SELECTED_DOCS = 5
242
  selected_docs = self._select_best_chunks(
243
  documents=documents,
 
249
  total_tokens = sum(self.llm.get_num_tokens(doc.page_content) for doc in selected_docs)
250
  print(f"βœ… Selected {len(selected_docs)} docs with total tokens: {total_tokens}")
251
 
252
+ # STEP 5: load LangChain prompts
253
  map_prompt_template = PromptTemplate(template=get_map_prompt(self.language), input_variables=["text"])
254
  combine_prompt_template = PromptTemplate(template=get_combine_prompt(self.language), input_variables=["text"])
255
 
 
267
  token_count = self.llm.get_num_tokens(doc.page_content)
268
  print(f"Chunk {i+1}: {token_count} tokens")
269
 
270
+ # STEP 6: run the chain with token tracking
271
  with get_openai_callback() as cb:
272
  result = summary_chain.invoke({"input_documents": selected_docs})
273
  input_tokens_used = cb.prompt_tokens
 
278
  full_summary = result['output_text']
279
  print("βœ… Map-reduce summary generated.")
280
 
281
+ # STEP 7: store the final summary
282
  inserted = self.qdrant_manager.insert_text(
283
  text=full_summary,
284
  metadata={
 
311
  print("❌ No documents found in collection.")
312
  return None, 0, 0
313
 
314
+ # STEP 2: filtra fuori i documenti di tipo summary
315
+ filtered_docs = [
316
+ doc for doc in all_documents
317
+ if not str(doc["payload"].get("type", "")).endswith("summary")
318
+ ]
319
 
320
+ if not filtered_docs:
321
+ print("❌ No non-summary documents available for summarization.")
322
+ return None, 0, 0
 
323
 
324
+ # STEP 3: extract vectors & text
325
+ embeddings = [doc["vector"] for doc in filtered_docs]
326
+ documents = [doc["payload"]["page_content"] for doc in filtered_docs]
327
+ metadata = [doc["payload"] for doc in filtered_docs]
328
+
329
+ # STEP 4: selezione intelligente con fallback a clustering
330
  selected_docs = self._get_chunks_for_stuff(
331
  documents=documents,
332
  metadata=metadata,
 
339
  total_tokens = sum(self.llm.get_num_tokens(doc.page_content) for doc in selected_docs)
340
  print(f"βœ… Selected {len(selected_docs)} docs with total tokens: {total_tokens}")
341
 
342
+ # STEP 5: load chain
343
  combine_prompt_template = PromptTemplate(
344
  template=get_combine_prompt(self.language),
345
  input_variables=["text"]
 
353
  verbose=False
354
  )
355
 
356
+ # STEP 6: run the chain with token tracking
357
  with get_openai_callback() as cb:
358
  result = summary_chain.invoke({"input_documents": selected_docs})
359
  input_tokens_used = cb.prompt_tokens
 
364
  full_summary = result['output_text']
365
  print("βœ… Stuff summary generated.")
366
 
367
+ # STEP 7: store the final summary
368
  inserted = self.qdrant_manager.insert_text(
369
  text=full_summary,
370
  metadata={
 
377
  print("πŸ“ Final summary saved to vector store.")
378
 
379
  return full_summary, input_tokens_used, output_tokens_used
380
+
381
  def _find_closest_embeddings(self,vectors, num_clusters, kmeans):
382
  closest_indices = []
383
  for i in range(num_clusters):
utilities/vectorstore/__pycache__/QdrantLangchainManager.cpython-312.pyc CHANGED
Binary files a/utilities/vectorstore/__pycache__/QdrantLangchainManager.cpython-312.pyc and b/utilities/vectorstore/__pycache__/QdrantLangchainManager.cpython-312.pyc differ
 
utilities/vectorstore/__pycache__/SummaryManager.cpython-312.pyc CHANGED
Binary files a/utilities/vectorstore/__pycache__/SummaryManager.cpython-312.pyc and b/utilities/vectorstore/__pycache__/SummaryManager.cpython-312.pyc differ
 
utils.py CHANGED
@@ -78,7 +78,15 @@ def chat_with_bot(llm_manager, contextualize=True):
78
  except Exception as e:
79
  print(f"⚠️ Error: {e}\n")
80
 
 
 
 
 
 
 
 
81
 
 
82
 
83
  llm_manager, qdrant_manager = initialize()
84
 
@@ -86,10 +94,10 @@ llm_manager, qdrant_manager = initialize()
86
  collection_name="key_statistics"
87
 
88
  llm_manager, qdrant_manager = initialize()
89
- if qdrant_manager.get_collection(collection_name):
90
- llm_manager.set_qdrant_manager(qdrant_manager)
91
 
92
- chat_with_bot(llm_manager)
93
 
94
  #manage_collection(file_name, collection_name)
95
  #summary=get_initial_summary(collection_name)
@@ -99,4 +107,7 @@ chat_with_bot(llm_manager)
99
  # if summary:
100
  # print(f"βœ… Summary:\n{summary}")
101
  # else:
102
- # print("⚠️ Nessun riassunto generato.")
 
 
 
 
78
  except Exception as e:
79
  print(f"⚠️ Error: {e}\n")
80
 
81
+ def get_chunk(collection_name, chunk_id):
82
+ """Retrieve a specific chunk from a Qdrant collection."""
83
+
84
+ # Carica la collection se esiste
85
+ if not qdrant_manager.get_collection(collection_name):
86
+ print(f"❌ Collection '{collection_name}' non trovata.")
87
+ return None
88
 
89
+ return qdrant_manager.get_chunk_by_index(chunk_id)
90
 
91
  llm_manager, qdrant_manager = initialize()
92
 
 
94
  collection_name="key_statistics"
95
 
96
  llm_manager, qdrant_manager = initialize()
97
+ # if qdrant_manager.get_collection(collection_name):
98
+ # llm_manager.set_qdrant_manager(qdrant_manager)
99
 
100
+ #chat_with_bot(llm_manager)
101
 
102
  #manage_collection(file_name, collection_name)
103
  #summary=get_initial_summary(collection_name)
 
107
  # if summary:
108
  # print(f"βœ… Summary:\n{summary}")
109
  # else:
110
+ # print("⚠️ Nessun riassunto generato.")
111
+
112
+ text=get_chunk(collection_name, 1)
113
+ print(text)