Spaces:
Running
Running
Create agentic_handlers.py
Browse files- services/agentic_handlers.py +278 -0
services/agentic_handlers.py
ADDED
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# handlers/agentic_handlers.py
|
2 |
+
import gradio as gr
|
3 |
+
import logging
|
4 |
+
from collections import defaultdict
|
5 |
+
|
6 |
+
# Attempt to import agentic pipeline functions and UI formatters
|
7 |
+
try:
|
8 |
+
from run_agentic_pipeline import run_full_analytics_orchestration
|
9 |
+
from ui.insights_ui_generator import (
|
10 |
+
format_report_to_markdown,
|
11 |
+
extract_key_results_for_selection,
|
12 |
+
format_single_okr_for_display
|
13 |
+
)
|
14 |
+
AGENTIC_MODULES_LOADED = True
|
15 |
+
except ImportError as e:
|
16 |
+
logging.error(f"Could not import agentic pipeline modules for AgenticHandlers: {e}.")
|
17 |
+
AGENTIC_MODULES_LOADED = False
|
18 |
+
# Define placeholder functions if modules are not loaded to avoid NameErrors during class definition
|
19 |
+
async def run_full_analytics_orchestration(*args, **kwargs): return None
|
20 |
+
def format_report_to_markdown(report_string): return "Agentic modules not loaded. Report unavailable."
|
21 |
+
def extract_key_results_for_selection(okrs_dict): return []
|
22 |
+
def format_single_okr_for_display(okr_data, **kwargs): return "Agentic modules not loaded. OKR display unavailable."
|
23 |
+
|
24 |
+
|
25 |
+
class AgenticHandlers:
|
26 |
+
def __init__(self, agentic_report_components, agentic_okrs_components,
|
27 |
+
token_state_ref, orchestration_raw_results_st_ref,
|
28 |
+
key_results_for_selection_st_ref, selected_key_result_ids_st_ref):
|
29 |
+
|
30 |
+
self.report_components = agentic_report_components
|
31 |
+
self.okrs_components = agentic_okrs_components
|
32 |
+
|
33 |
+
# References to global states
|
34 |
+
self.token_state = token_state_ref
|
35 |
+
self.orchestration_raw_results_st = orchestration_raw_results_st_ref
|
36 |
+
self.key_results_for_selection_st = key_results_for_selection_st_ref
|
37 |
+
self.selected_key_result_ids_st = selected_key_result_ids_st_ref # Though this is updated by CBG, might be read
|
38 |
+
|
39 |
+
self.agentic_modules_really_loaded = AGENTIC_MODULES_LOADED # Store the flag
|
40 |
+
logging.info(f"AgenticHandlers initialized. Modules loaded: {self.agentic_modules_really_loaded}")
|
41 |
+
|
42 |
+
async def run_agentic_pipeline_autonomously_on_update(self, current_token_state_val):
|
43 |
+
"""
|
44 |
+
This function is intended to be triggered by changes in token_state
|
45 |
+
(e.g., after initial load or after sync).
|
46 |
+
It yields updates for the agentic report and OKR tabs.
|
47 |
+
"""
|
48 |
+
logging.info(f"Agentic pipeline auto-trigger. Token: {'Set' if current_token_state_val.get('token') else 'Not Set'}")
|
49 |
+
|
50 |
+
# Initial "waiting" status updates
|
51 |
+
initial_report_status = "Pipeline AI: In attesa dei dati necessari..."
|
52 |
+
initial_okr_choices = []
|
53 |
+
initial_okr_value = []
|
54 |
+
initial_okr_interactive = False
|
55 |
+
initial_okr_details = "Pipeline AI: In attesa dei dati necessari..."
|
56 |
+
initial_orchestration_results = self.orchestration_raw_results_st.value # Preserve if re-running
|
57 |
+
initial_selected_krs = self.selected_key_result_ids_st.value
|
58 |
+
initial_krs_for_selection = self.key_results_for_selection_st.value
|
59 |
+
|
60 |
+
# Check if components exist (in case tabs were conditionally not fully rendered)
|
61 |
+
report_status_md_update = gr.update(value=initial_report_status) if self.report_components.get("agentic_pipeline_status_md") else gr.update()
|
62 |
+
report_display_md_update = gr.update() # No change to report display yet
|
63 |
+
|
64 |
+
okrs_cbg_update = gr.update(choices=initial_okr_choices, value=initial_okr_value, interactive=initial_okr_interactive) if self.okrs_components.get("key_results_cbg") else gr.update()
|
65 |
+
okrs_detail_md_update = gr.update(value=initial_okr_details) if self.okrs_components.get("okr_detail_display_md") else gr.update()
|
66 |
+
|
67 |
+
if not current_token_state_val or not current_token_state_val.get("token"):
|
68 |
+
logging.info("Agentic pipeline: Token not available in token_state. Skipping actual run.")
|
69 |
+
yield (
|
70 |
+
report_status_md_update, # For agentic_pipeline_status_md
|
71 |
+
report_display_md_update, # For agentic_report_display_md (no change yet)
|
72 |
+
okrs_cbg_update, # For key_results_cbg
|
73 |
+
okrs_detail_md_update, # For okr_detail_display_md
|
74 |
+
initial_orchestration_results, # For orchestration_raw_results_st
|
75 |
+
initial_selected_krs, # For selected_key_result_ids_st
|
76 |
+
initial_krs_for_selection # For key_results_for_selection_st
|
77 |
+
)
|
78 |
+
return
|
79 |
+
|
80 |
+
# Update status to "in progress"
|
81 |
+
in_progress_status = "Analisi AI (Sempre) in corso..."
|
82 |
+
if self.report_components.get("agentic_pipeline_status_md"):
|
83 |
+
report_status_md_update = gr.update(value=in_progress_status)
|
84 |
+
if self.okrs_components.get("okr_detail_display_md"): # Also update OKR detail placeholder
|
85 |
+
okrs_detail_md_update = gr.update(value="Dettagli OKR (Sempre) in corso di generazione...")
|
86 |
+
|
87 |
+
yield (
|
88 |
+
report_status_md_update, report_display_md_update, okrs_cbg_update, okrs_detail_md_update,
|
89 |
+
initial_orchestration_results, initial_selected_krs, initial_krs_for_selection
|
90 |
+
)
|
91 |
+
|
92 |
+
if not self.agentic_modules_really_loaded:
|
93 |
+
logging.warning("Agentic modules not loaded. Skipping autonomous pipeline actual run.")
|
94 |
+
error_status = "Moduli AI non caricati. Operazione non disponibile."
|
95 |
+
if self.report_components.get("agentic_pipeline_status_md"):
|
96 |
+
report_status_md_update = gr.update(value=error_status)
|
97 |
+
if self.report_components.get("agentic_report_display_md"):
|
98 |
+
report_display_md_update = gr.update(value=error_status) # Update report display too
|
99 |
+
if self.okrs_components.get("okr_detail_display_md"):
|
100 |
+
okrs_detail_md_update = gr.update(value=error_status) # Update OKR detail too
|
101 |
+
|
102 |
+
yield (
|
103 |
+
report_status_md_update, report_display_md_update, okrs_cbg_update, okrs_detail_md_update,
|
104 |
+
None, [], [] # Reset states
|
105 |
+
)
|
106 |
+
return
|
107 |
+
|
108 |
+
try:
|
109 |
+
# Always use "Sempre" filter for the autonomous agentic pipeline
|
110 |
+
date_filter_val_agentic = "Sempre"
|
111 |
+
custom_start_val_agentic = None
|
112 |
+
custom_end_val_agentic = None
|
113 |
+
|
114 |
+
logging.info("Agentic pipeline: Calling run_full_analytics_orchestration...")
|
115 |
+
orchestration_output = await run_full_analytics_orchestration(
|
116 |
+
current_token_state_val,
|
117 |
+
date_filter_val_agentic,
|
118 |
+
custom_start_val_agentic,
|
119 |
+
custom_end_val_agentic
|
120 |
+
)
|
121 |
+
|
122 |
+
final_status_text = "Pipeline AI (Sempre) completata."
|
123 |
+
logging.info(f"Autonomous agentic pipeline finished. Output keys: {orchestration_output.keys() if orchestration_output else 'None'}")
|
124 |
+
|
125 |
+
orchestration_results_update_val = None
|
126 |
+
selected_krs_update_val = []
|
127 |
+
krs_for_selection_update_val = []
|
128 |
+
|
129 |
+
if orchestration_output:
|
130 |
+
orchestration_results_update_val = orchestration_output # Store the raw output
|
131 |
+
|
132 |
+
report_str = orchestration_output.get('comprehensive_analysis_report', "Nessun report testuale fornito.")
|
133 |
+
if self.report_components.get("agentic_report_display_md"):
|
134 |
+
report_display_md_update = gr.update(value=format_report_to_markdown(report_str))
|
135 |
+
|
136 |
+
actionable_okrs = orchestration_output.get('actionable_okrs_and_tasks')
|
137 |
+
krs_for_ui_selection_list = extract_key_results_for_selection(actionable_okrs)
|
138 |
+
krs_for_selection_update_val = krs_for_ui_selection_list # Store for CBG change handler
|
139 |
+
|
140 |
+
kr_choices_for_cbg = [(kr['kr_description'], kr['unique_kr_id']) for kr in krs_for_ui_selection_list]
|
141 |
+
if self.okrs_components.get("key_results_cbg"):
|
142 |
+
okrs_cbg_update = gr.update(choices=kr_choices_for_cbg, value=[], interactive=True)
|
143 |
+
|
144 |
+
# Display all OKRs initially (before any KR selection)
|
145 |
+
all_okrs_md_parts = []
|
146 |
+
if actionable_okrs and isinstance(actionable_okrs.get("okrs"), list):
|
147 |
+
for okr_idx, okr_item in enumerate(actionable_okrs["okrs"]):
|
148 |
+
all_okrs_md_parts.append(format_single_okr_for_display(okr_item, accepted_kr_indices=None, okr_main_index=okr_idx))
|
149 |
+
|
150 |
+
if not all_okrs_md_parts:
|
151 |
+
if self.okrs_components.get("okr_detail_display_md"):
|
152 |
+
okrs_detail_md_update = gr.update(value="Nessun OKR generato o trovato (Sempre).")
|
153 |
+
else:
|
154 |
+
if self.okrs_components.get("okr_detail_display_md"):
|
155 |
+
okrs_detail_md_update = gr.update(value="\n\n---\n\n".join(all_okrs_md_parts))
|
156 |
+
|
157 |
+
selected_krs_update_val = [] # Reset selection
|
158 |
+
else: # No output from pipeline
|
159 |
+
final_status_text = "Pipeline AI (Sempre): Nessun risultato prodotto."
|
160 |
+
if self.report_components.get("agentic_report_display_md"):
|
161 |
+
report_display_md_update = gr.update(value="Nessun report generato dalla pipeline AI (Sempre).")
|
162 |
+
if self.okrs_components.get("key_results_cbg"):
|
163 |
+
okrs_cbg_update = gr.update(choices=[], value=[], interactive=False)
|
164 |
+
if self.okrs_components.get("okr_detail_display_md"):
|
165 |
+
okrs_detail_md_update = gr.update(value="Nessun OKR generato o errore nella pipeline AI (Sempre).")
|
166 |
+
|
167 |
+
if self.report_components.get("agentic_pipeline_status_md"):
|
168 |
+
report_status_md_update = gr.update(value=final_status_text)
|
169 |
+
|
170 |
+
yield (
|
171 |
+
report_status_md_update, report_display_md_update, okrs_cbg_update, okrs_detail_md_update,
|
172 |
+
orchestration_results_update_val, selected_krs_update_val, krs_for_selection_update_val
|
173 |
+
)
|
174 |
+
|
175 |
+
except Exception as e:
|
176 |
+
logging.error(f"Error during autonomous agentic pipeline execution: {e}", exc_info=True)
|
177 |
+
error_status_text = f"Errore pipeline AI (Sempre): {str(e)}"
|
178 |
+
if self.report_components.get("agentic_pipeline_status_md"):
|
179 |
+
report_status_md_update = gr.update(value=error_status_text)
|
180 |
+
if self.report_components.get("agentic_report_display_md"):
|
181 |
+
report_display_md_update = gr.update(value=f"Errore generazione report AI (Sempre): {str(e)}")
|
182 |
+
if self.okrs_components.get("key_results_cbg"):
|
183 |
+
okrs_cbg_update = gr.update(choices=[], value=[], interactive=False)
|
184 |
+
if self.okrs_components.get("okr_detail_display_md"):
|
185 |
+
okrs_detail_md_update = gr.update(value=f"Errore generazione OKR AI (Sempre): {str(e)}")
|
186 |
+
|
187 |
+
yield (
|
188 |
+
report_status_md_update, report_display_md_update, okrs_cbg_update, okrs_detail_md_update,
|
189 |
+
None, [], [] # Reset states on error
|
190 |
+
)
|
191 |
+
|
192 |
+
def update_okr_display_on_kr_selection(self, selected_kr_unique_ids: list,
|
193 |
+
raw_orchestration_results: dict,
|
194 |
+
all_krs_for_selection_list: list):
|
195 |
+
"""
|
196 |
+
Updates the OKR detail display when Key Results are selected in the CheckboxGroup.
|
197 |
+
"""
|
198 |
+
if not self.agentic_modules_really_loaded:
|
199 |
+
return gr.update(value="Moduli AI non caricati. Impossibile visualizzare i dettagli OKR.")
|
200 |
+
if not raw_orchestration_results:
|
201 |
+
return gr.update(value="Nessun dato dalla pipeline AI o moduli non caricati.")
|
202 |
+
|
203 |
+
actionable_okrs_dict = raw_orchestration_results.get("actionable_okrs_and_tasks")
|
204 |
+
if not actionable_okrs_dict or not isinstance(actionable_okrs_dict.get("okrs"), list):
|
205 |
+
return gr.update(value="Nessun OKR trovato nei risultati della pipeline.")
|
206 |
+
|
207 |
+
okrs_list = actionable_okrs_dict["okrs"]
|
208 |
+
if not okrs_list: # Should be caught by above, but defensive
|
209 |
+
return gr.update(value="Nessun OKR generato.")
|
210 |
+
|
211 |
+
# Create a mapping from unique_kr_id to its (okr_index, kr_index_within_okr)
|
212 |
+
kr_id_to_indices = {}
|
213 |
+
if all_krs_for_selection_list: # This list comes from extract_key_results_for_selection
|
214 |
+
for kr_info in all_krs_for_selection_list:
|
215 |
+
kr_id_to_indices[kr_info['unique_kr_id']] = (kr_info['okr_index'], kr_info['kr_index'])
|
216 |
+
|
217 |
+
# Group selected KR indices by their parent OKR index
|
218 |
+
selected_krs_by_okr_idx = defaultdict(list)
|
219 |
+
if selected_kr_unique_ids: # This is the list of unique_kr_ids from the checkboxgroup
|
220 |
+
for kr_unique_id in selected_kr_unique_ids:
|
221 |
+
if kr_unique_id in kr_id_to_indices:
|
222 |
+
okr_idx, kr_idx_in_okr = kr_id_to_indices[kr_unique_id]
|
223 |
+
selected_krs_by_okr_idx[okr_idx].append(kr_idx_in_okr)
|
224 |
+
|
225 |
+
output_md_parts = []
|
226 |
+
for okr_idx, okr_data in enumerate(okrs_list):
|
227 |
+
# If specific KRs are selected, only show OKRs that have at least one selected KR,
|
228 |
+
# and then format that OKR to only show its selected KRs.
|
229 |
+
# If no KRs are selected, show all OKRs with all their KRs.
|
230 |
+
|
231 |
+
accepted_indices_for_this_okr = selected_krs_by_okr_idx.get(okr_idx) # This will be a list of KR indices or None
|
232 |
+
|
233 |
+
if selected_kr_unique_ids: # If there's any selection active
|
234 |
+
if accepted_indices_for_this_okr is not None: # Only format/display if this OKR has selected KRs
|
235 |
+
formatted_okr_md = format_single_okr_for_display(
|
236 |
+
okr_data,
|
237 |
+
accepted_kr_indices=accepted_indices_for_this_okr,
|
238 |
+
okr_main_index=okr_idx
|
239 |
+
)
|
240 |
+
output_md_parts.append(formatted_okr_md)
|
241 |
+
else: # No KRs selected, display all OKRs fully
|
242 |
+
formatted_okr_md = format_single_okr_for_display(
|
243 |
+
okr_data,
|
244 |
+
accepted_kr_indices=None, # None means show all KRs for this OKR
|
245 |
+
okr_main_index=okr_idx
|
246 |
+
)
|
247 |
+
output_md_parts.append(formatted_okr_md)
|
248 |
+
|
249 |
+
if not output_md_parts and selected_kr_unique_ids:
|
250 |
+
final_md = "Nessun OKR corrisponde alla selezione corrente o i KR selezionati non hanno task dettagliati."
|
251 |
+
elif not output_md_parts and not selected_kr_unique_ids: # Should mean okrs_list was empty
|
252 |
+
final_md = "Nessun OKR generato."
|
253 |
+
else:
|
254 |
+
final_md = "\n\n---\n\n".join(output_md_parts)
|
255 |
+
|
256 |
+
return gr.update(value=final_md)
|
257 |
+
|
258 |
+
def setup_event_handlers(self):
|
259 |
+
"""Sets up event handlers for the agentic OKRs tab."""
|
260 |
+
if not self.agentic_modules_really_loaded:
|
261 |
+
logging.warning("Agentic modules not loaded. Skipping agentic event handler setup.")
|
262 |
+
return
|
263 |
+
|
264 |
+
if self.okrs_components.get("key_results_cbg"):
|
265 |
+
self.okrs_components['key_results_cbg'].change(
|
266 |
+
fn=self.update_okr_display_on_kr_selection,
|
267 |
+
inputs=[
|
268 |
+
self.okrs_components['key_results_cbg'], # Current value of checkbox group
|
269 |
+
self.orchestration_raw_results_st, # State
|
270 |
+
self.key_results_for_selection_st # State
|
271 |
+
],
|
272 |
+
outputs=[self.okrs_components['okr_detail_display_md']],
|
273 |
+
api_name="update_okr_display_on_kr_selection"
|
274 |
+
)
|
275 |
+
logging.info("Agentic OKR selection handler setup complete.")
|
276 |
+
else:
|
277 |
+
logging.warning("key_results_cbg component not found for agentic OKR handler setup.")
|
278 |
+
|