GuglielmoTor commited on
Commit
fce9f7a
·
verified ·
1 Parent(s): 05c9662

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +246 -103
app.py CHANGED
@@ -18,14 +18,14 @@ from config import (
18
  BUBBLE_API_ENDPOINT_ENV_VAR
19
  )
20
 
 
 
21
  # UPDATED: Using the new data loading function from the refactored state manager
22
  from services.state_manager import load_data_from_bubble
23
-
24
- # Import UI generator functions (these are now passed to build_main_app_ui)
25
  from ui.ui_generators import (
26
  build_analytics_tab_plot_area,
27
- build_home_tab_ui,
28
- create_enhanced_report_tab,
29
  BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON
30
  )
31
  # NEW: Import the new OKR UI functions
@@ -33,9 +33,6 @@ from ui.okr_ui_generator import create_enhanced_okr_tab, format_okrs_for_enhance
33
  from ui.analytics_plot_generator import update_analytics_plots_figures, create_placeholder_plot
34
  from formulas import PLOT_FORMULAS
35
 
36
- # NEW: Import UI enhancements from the new module
37
- from ui.ui_main_page_enhancements import build_main_app_ui, update_report_display_enhanced
38
-
39
  # --- CHATBOT MODULE IMPORTS ---
40
  from features.chatbot.chatbot_prompts import get_initial_insight_prompt_and_suggestions
41
  from features.chatbot.chatbot_handler import generate_llm_response
@@ -49,6 +46,8 @@ try:
49
  # UI formatting functions
50
  from ui.insights_ui_generator import (
51
  format_report_for_display, # This will now return header HTML and body Markdown
 
 
52
  )
53
  AGENTIC_MODULES_LOADED = True
54
  except ImportError as e:
@@ -77,15 +76,15 @@ except ImportError as e:
77
  # matching the `outputs` in the .then() call later.
78
  return (
79
  gr.update(value="Modules not loaded."), # agentic_pipeline_status_md (0)
80
- gr.update(choices=[], value=None), # report_selector_dd (1)
81
- gr.update(choices=[], value=[]), # key_results_cbg (2) - KEPT HIDDEN for compatibility
82
- gr.update(value="Modules not loaded."), # okr_detail_display_md (3) - KEPT HIDDEN for compatibility
83
- None, # orchestration_raw_results_st (4)
84
- [], # selected_key_result_ids_st (5) - KEPT HIDDEN for compatibility
85
- [], # key_results_for_selection_st (6) - KEPT HIDDEN for compatibility
86
- gr.update(value=empty_header_html), # report_header_html_display (7)
87
- gr.update(value=empty_body_markdown), # report_body_markdown_display (8)
88
- {}, # reconstruction_cache_st (9)
89
  gr.update(value=get_initial_okr_display()), # NEW: enhanced_okr_display_html (10)
90
  gr.update(value={}) # NEW: actionable_okrs_data_st (11)
91
  )
@@ -96,97 +95,241 @@ except ImportError as e:
96
  return {'header_html': '<h1>Agentic modules not loaded.</h1>', 'body_markdown': 'Report display unavailable.'}
97
 
98
 
99
- # --- Initial data load sequence function (remains in app.py as it uses service functions) ---
100
- def initial_data_load_sequence(url_token, org_urn_val, current_state):
101
- """
102
- Handles the initial data loading from Bubble.
103
- No longer generates dashboard HTML as the Home tab is now static.
104
- """
105
- status_msg, new_state = load_data_from_bubble(url_token, org_urn_val, current_state)
106
- # Add status icons based on success/failure
107
- if "successfully" in status_msg.lower() or "loaded" in status_msg.lower():
108
- status_msg = f" {status_msg}"
109
- elif "error" in status_msg.lower() or "failed" in status_msg.lower():
110
- status_msg = f" {status_msg}"
111
- else:
112
- status_msg = f"🔄 {status_msg}"
113
- return status_msg, new_state
114
-
115
- # Build the main UI using the function from ui_enhancements
116
- # This function now also sets up the main event listeners.
117
- (app, url_user_token_display, org_urn_display, status_box,
118
- token_state, reconstruction_cache_st, enhanced_okr_display_html,
119
- tabs, report_selector_dd, agentic_display_outputs,
120
- analytics_tab_instance, chat_histories_st_returned,
121
- current_chat_plot_id_st_returned, plot_data_for_chatbot_st_returned, # Receive these returned states
122
- format_report_for_display_func_passed) = \
123
- build_main_app_ui(
124
- PLOT_ID_TO_FORMULA_KEY_MAP=PLOT_ID_TO_FORMULA_KEY_MAP,
125
- PLOT_FORMULAS=PLOT_FORMULAS,
126
- BOMB_ICON=BOMB_ICON, EXPLORE_ICON=EXPLORE_ICON, FORMULA_ICON=FORMULA_ICON, ACTIVE_ICON=ACTIVE_ICON,
127
- build_analytics_tab_plot_area_func=build_analytics_tab_plot_area,
128
- update_analytics_plots_figures_func=update_analytics_plots_figures,
129
- create_placeholder_plot_func=create_placeholder_plot,
130
- get_initial_insight_prompt_and_suggestions_func=get_initial_insight_prompt_and_suggestions,
131
- generate_llm_response_func=generate_llm_response,
132
- build_home_tab_ui_func=build_home_tab_ui,
133
- create_enhanced_report_tab_func=create_enhanced_report_tab,
134
- create_enhanced_okr_tab_func=create_enhanced_okr_tab,
135
- format_report_for_display_func=format_report_for_display, # Pass the imported function
136
- AGENTIC_MODULES_LOADED=AGENTIC_MODULES_LOADED, # ADD THIS PARAMETER
137
- get_initial_okr_display_func=get_initial_okr_display if AGENTIC_MODULES_LOADED else None, # ADD THIS TOO
138
- # Pass the initial data load sequence function and others that need to be bound
139
- # within the Gradio Blocks context.
140
- initial_data_load_sequence_func=initial_data_load_sequence,
141
- get_url_user_token_func=get_url_user_token,
142
- load_and_display_agentic_results_func=load_and_display_agentic_results,
143
- format_okrs_for_enhanced_display_func=format_okrs_for_enhanced_display,
144
- update_report_display_enhanced_func=update_report_display_enhanced # Pass this as well
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  )
146
 
147
- # The event handlers are now set up *inside* build_main_app_ui,
148
- # so these calls are no longer needed here.
149
 
150
  if __name__ == "__main__":
151
- # Enhanced startup logging
152
- print("🚀 Starting LinkedIn Organization Dashboard...")
153
-
154
- # Environment variable checks with better logging
155
- missing_vars = []
156
-
157
  if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR):
158
- missing_vars.append(LINKEDIN_CLIENT_ID_ENV_VAR)
159
- logging.warning(f"⚠️ WARNING: '{LINKEDIN_CLIENT_ID_ENV_VAR}' is not set.")
160
-
161
- bubble_vars = [BUBBLE_APP_NAME_ENV_VAR, BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR]
162
- if not all(os.environ.get(var) for var in bubble_vars):
163
- missing_vars.extend([var for var in bubble_vars if not os.environ.get(var)])
164
- logging.warning("⚠️ WARNING: One or more Bubble environment variables are not set.")
165
-
166
- if not os.environ.get("GEMINI_API_KEY"):
167
- missing_vars.append("GEMINI_API_KEY")
168
- logging.warning("⚠️ WARNING: 'GEMINI_API_KEY' is not set.")
169
-
170
  if not AGENTIC_MODULES_LOADED:
171
- logging.warning("🔴 CRITICAL: Agentic modules failed to load.")
172
-
173
- if missing_vars:
174
- print(f"⚠️ Missing environment variables: {', '.join(missing_vars)}")
175
- print("🔧 Please set these variables for full functionality.")
176
- else:
177
- print("✅ All environment variables are properly configured.")
178
-
179
- print("🌐 Launching dashboard on http://0.0.0.0:7860")
180
- print("🎯 Dashboard features:")
181
- print(" • 📊 Advanced LinkedIn Analytics")
182
- print(" • 🤖 AI-Powered Insights")
183
- print(" • 🎯 OKR Generation & Tracking")
184
- print(" • ☁️ Bubble.io Integration")
185
-
186
- app.launch(
187
- server_name="0.0.0.0",
188
- server_port=int(os.environ.get("PORT", 7860)),
189
- debug=True,
190
- show_error=True
191
- )
192
 
 
 
18
  BUBBLE_API_ENDPOINT_ENV_VAR
19
  )
20
 
21
+ from services.analytics_tab_module import AnalyticsTab
22
+
23
  # UPDATED: Using the new data loading function from the refactored state manager
24
  from services.state_manager import load_data_from_bubble
 
 
25
  from ui.ui_generators import (
26
  build_analytics_tab_plot_area,
27
+ build_home_tab_ui, # NEW: Import the function to build the Home tab UI
28
+ create_enhanced_report_tab, # NEW: Import the function to build the enhanced Report tab UI
29
  BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON
30
  )
31
  # NEW: Import the new OKR UI functions
 
33
  from ui.analytics_plot_generator import update_analytics_plots_figures, create_placeholder_plot
34
  from formulas import PLOT_FORMULAS
35
 
 
 
 
36
  # --- CHATBOT MODULE IMPORTS ---
37
  from features.chatbot.chatbot_prompts import get_initial_insight_prompt_and_suggestions
38
  from features.chatbot.chatbot_handler import generate_llm_response
 
46
  # UI formatting functions
47
  from ui.insights_ui_generator import (
48
  format_report_for_display, # This will now return header HTML and body Markdown
49
+ # REMOVED: extract_key_results_for_selection, - Moved to okr_ui_generator (implicitly)
50
+ # REMOVED: format_single_okr_for_display - Moved to okr_ui_generator (implicitly)
51
  )
52
  AGENTIC_MODULES_LOADED = True
53
  except ImportError as e:
 
76
  # matching the `outputs` in the .then() call later.
77
  return (
78
  gr.update(value="Modules not loaded."), # agentic_pipeline_status_md (0)
79
+ gr.update(choices=[], value=None), # report_selector_dd (1)
80
+ gr.update(choices=[], value=[]), # key_results_cbg (2) - KEPT HIDDEN for compatibility
81
+ gr.update(value="Modules not loaded."), # okr_detail_display_md (3) - KEPT HIDDEN for compatibility
82
+ None, # orchestration_raw_results_st (4)
83
+ [], # selected_key_result_ids_st (5) - KEPT HIDDEN for compatibility
84
+ [], # key_results_for_selection_st (6) - KEPT HIDDEN for compatibility
85
+ gr.update(value=empty_header_html), # report_header_html_display (7)
86
+ gr.update(value=empty_body_markdown), # report_body_markdown_display (8)
87
+ {}, # reconstruction_cache_st (9)
88
  gr.update(value=get_initial_okr_display()), # NEW: enhanced_okr_display_html (10)
89
  gr.update(value={}) # NEW: actionable_okrs_data_st (11)
90
  )
 
95
  return {'header_html': '<h1>Agentic modules not loaded.</h1>', 'body_markdown': 'Report display unavailable.'}
96
 
97
 
98
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
99
+ title="LinkedIn Organization Dashboard") as app:
100
+ # --- STATE MANAGEMENT ---
101
+ token_state = gr.State(value={
102
+ "token": None, "client_id": None, "org_urn": None,
103
+ "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
104
+ "bubble_mentions_df": pd.DataFrame(), "bubble_follower_stats_df": pd.DataFrame(),
105
+ "bubble_agentic_analysis_data": pd.DataFrame(), # To store agentic results from Bubble
106
+ "url_user_token_temp_storage": None,
107
+ "config_date_col_posts": "published_at", "config_date_col_mentions": "date",
108
+ "config_date_col_followers": "date", "config_media_type_col": "media_type",
109
+ "config_eb_labels_col": "li_eb_label"
110
+ })
111
+
112
+ # States for analytics tab chatbot
113
+ chat_histories_st = gr.State({})
114
+ current_chat_plot_id_st = gr.State(None)
115
+ plot_data_for_chatbot_st = gr.State({})
116
+
117
+ # States for agentic results display
118
+ orchestration_raw_results_st = gr.State(None)
119
+ # KEPT for compatibility with load_and_display_agentic_results signature
120
+ key_results_for_selection_st = gr.State([])
121
+ selected_key_result_ids_st = gr.State([])
122
+
123
+ # --- NEW: Session-specific cache for reconstructed OKR data ---
124
+ reconstruction_cache_st = gr.State({})
125
+ # NEW: State to hold the actionable_okrs dictionary explicitly
126
+ actionable_okrs_data_st = gr.State({})
127
+
128
+
129
+ # --- UI LAYOUT ---
130
+ gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
131
+ url_user_token_display = gr.Textbox(label="User Token (Hidden)", interactive=False, visible=False)
132
+ org_urn_display = gr.Textbox(label="Org URN (Hidden)", interactive=False, visible=False)
133
+ status_box = gr.Textbox(label="Status", interactive=False, value="Initializing...")
134
+
135
+ app.load(fn=get_url_user_token, inputs=None, outputs=[url_user_token_display, org_urn_display], api_name="get_url_params", show_progress=False)
136
+
137
+ def initial_data_load_sequence(url_token, org_urn_val, current_state):
138
+ """
139
+ Handles the initial data loading from Bubble.
140
+ No longer generates dashboard HTML as the Home tab is now static.
141
+ """
142
+ status_msg, new_state = load_data_from_bubble(url_token, org_urn_val, current_state)
143
+ return status_msg, new_state
144
+
145
+ analytics_icons = {'bomb': BOMB_ICON, 'explore': EXPLORE_ICON, 'formula': FORMULA_ICON, 'active': ACTIVE_ICON}
146
+ analytics_tab_instance = AnalyticsTab(
147
+ token_state=token_state,
148
+ chat_histories_st=chat_histories_st,
149
+ current_chat_plot_id_st=current_chat_plot_id_st,
150
+ plot_data_for_chatbot_st=plot_data_for_chatbot_st,
151
+ plot_id_to_formula_map=PLOT_ID_TO_FORMULA_KEY_MAP,
152
+ plot_formulas_data=PLOT_FORMULAS,
153
+ icons=analytics_icons,
154
+ fn_build_plot_area=build_analytics_tab_plot_area,
155
+ fn_update_plot_figures=update_analytics_plots_figures,
156
+ fn_create_placeholder_plot=create_placeholder_plot,
157
+ fn_get_initial_insight=get_initial_insight_prompt_and_suggestions,
158
+ fn_generate_llm_response=generate_llm_response
159
+ )
160
+
161
+ def update_report_display(selected_report_id: str, current_token_state: dict):
162
+ """
163
+ Updates the report header and body display when a new report is selected.
164
+ This function now expects format_report_for_display to return a dict with
165
+ 'header_html' and 'body_markdown'.
166
+ """
167
+ # Define empty states for header and body
168
+ empty_header_html = """
169
+ <div class="report-title">📊 Comprehensive Analysis Report</div>
170
+ <div class="report-subtitle">AI-Generated Insights from Your LinkedIn Data</div>
171
+ <div class="status-badge">Generated from Bubble.io</div>
172
+ """
173
+ empty_body_markdown_no_selection = """
174
+ <div class="empty-state">
175
+ <div class="empty-state-icon">📋</div>
176
+ <div class="empty-state-title">Select a Report</div>
177
+ <div class="empty-state-description">
178
+ Choose a report from the dropdown above to view its detailed analysis and insights.
179
+ </div>
180
+ </div>
181
+ """
182
+ empty_body_markdown_no_data = """
183
+ <div class="empty-state">
184
+ <div class="empty-state-icon">⚠️</div>
185
+ <div class="empty-state-title">Data Not Available</div>
186
+ <div class="empty-state-description">
187
+ Analysis data is not loaded or is empty. Please try refreshing the page.
188
+ </div>
189
+ </div>
190
+ """
191
+ empty_body_markdown_not_found = lambda _id: f"""
192
+ <div class="empty-state">
193
+ <div class="empty-state-icon">❌</div>
194
+ <div class="empty-state-title">Report Not Found</div>
195
+ <div class="empty-state-description">
196
+ Report with ID '{_id}' was not found in the database.
197
+ </div>
198
+ </div>
199
+ """
200
+
201
+ if not selected_report_id:
202
+ # When no report is selected, update both header and body
203
+ return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_no_selection)
204
+
205
+ agentic_df = current_token_state.get("bubble_agentic_analysis_data")
206
+ if agentic_df is None or agentic_df.empty:
207
+ # When no data is available, update both header and body
208
+ return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_no_data)
209
+
210
+ selected_report_series_df = agentic_df[agentic_df['_id'] == selected_report_id]
211
+ if selected_report_series_df.empty:
212
+ # When report is not found, update both header and body
213
+ return gr.update(value=empty_header_html), gr.update(value=empty_body_markdown_not_found(selected_report_id))
214
+
215
+ selected_report_series = selected_report_series_df.iloc[0]
216
+
217
+ # Call the format_report_for_display, which now returns a dict
218
+ formatted_content_parts = format_report_for_display(selected_report_series)
219
+
220
+ # Update the two separate Gradio components
221
+ return (
222
+ gr.update(value=formatted_content_parts['header_html']),
223
+ gr.update(value=formatted_content_parts['body_markdown'])
224
+ )
225
+
226
+
227
+ with gr.Tabs() as tabs:
228
+ # --- NEW HOME TAB ---
229
+ with gr.TabItem("1️⃣ Home", id="tab_home"):
230
+ # Call the new function from ui_generators to build the Home tab content
231
+ btn_graphs, btn_reports, btn_okr, btn_help = build_home_tab_ui()
232
+
233
+ # Link buttons to tab selection
234
+ btn_graphs.click(fn=lambda: gr.update(selected="tab_analytics_module"), outputs=tabs)
235
+ btn_reports.click(fn=lambda: gr.update(selected="tab_agentic_report"), outputs=tabs)
236
+ btn_okr.click(fn=lambda: gr.update(selected="tab_agentic_okrs"), outputs=tabs)
237
+ # btn_help.click(fn=lambda: gr.update(selected="tab_help"), outputs=tabs) # Uncomment if you add a help tab
238
+
239
+
240
+ analytics_tab_instance.create_tab_ui() # This is the "Graphs" tab, assuming its ID is "tab_analytics"
241
+
242
+ # --- REPLACED: Agentic Analysis Report Tab with enhanced UI ---
243
+ # The create_enhanced_report_tab function now builds this entire tab's UI.
244
+ # It also returns the relevant Gradio components needed for callbacks.
245
+ with gr.TabItem("3️⃣ Agentic Analysis Report", id="tab_agentic_report", visible=AGENTIC_MODULES_LOADED):
246
+ # The create_enhanced_report_tab function handles the CSS and HTML structure
247
+ # MODIFIED: Unpacked 4 values instead of 3
248
+ agentic_pipeline_status_md, report_selector_dd, report_header_html_display, report_body_markdown_display = \
249
+ create_enhanced_report_tab(AGENTIC_MODULES_LOADED)
250
+
251
+
252
+ with gr.TabItem("4️⃣ Agentic OKRs & Tasks", id="tab_agentic_okrs", visible=AGENTIC_MODULES_LOADED):
253
+ gr.Markdown("## 🎯 AI Generated OKRs and Actionable Tasks (from Bubble.io)")
254
+ gr.Markdown("Basato sull'analisi AI, l'agente ha proposto i seguenti OKR.")
255
+
256
+ if not AGENTIC_MODULES_LOADED:
257
+ gr.Markdown("🔴 **Error:** Agentic modules could not be loaded.")
258
+
259
+ # Keep the old components but make them invisible to maintain load_and_display_agentic_results signature
260
+ with gr.Column(visible=False):
261
+ gr.Markdown("### Suggested Key Results (OLD UI - HIDDEN)")
262
+ key_results_cbg = gr.CheckboxGroup(label="Select Key Results", choices=[], value=[], interactive=True)
263
+ gr.Markdown("### Detailed OKRs and Tasks (OLD UI - HIDDEN)")
264
+ okr_detail_display_md = gr.Markdown("I dettagli OKR appariranno qui.")
265
+
266
+ # NEW: Add the enhanced OKR display HTML component
267
+ enhanced_okr_display_html = create_enhanced_okr_tab()
268
+
269
+ # REMOVED: The old update_okr_display_on_selection function and its change event
270
+ # as the new UI handles display dynamically from raw_results_st
271
+
272
+ if AGENTIC_MODULES_LOADED:
273
+ report_selector_dd.change(
274
+ fn=update_report_display, # This now calls the enhanced function
275
+ # MODIFIED: Updated outputs to match the two new display components
276
+ inputs=[report_selector_dd, token_state],
277
+ outputs=[report_header_html_display, report_body_markdown_display],
278
+ show_progress="minimal"
279
+ )
280
+
281
+ # Ensure agentic_display_outputs correctly maps to the newly created components
282
+ # This list must match the outputs of load_and_display_agentic_results
283
+ agentic_display_outputs = [
284
+ agentic_pipeline_status_md, # 0: Status Markdown (hidden)
285
+ report_selector_dd, # 1: Dropdown for selecting reports
286
+ key_results_cbg, # 2: Checkbox group for OKRs (kept hidden)
287
+ okr_detail_display_md, # 3: Markdown for detailed OKR display (kept hidden)
288
+ orchestration_raw_results_st, # 4: Raw results state
289
+ selected_key_result_ids_st, # 5: Selected KR IDs state (kept hidden)
290
+ key_results_for_selection_st, # 6: All KRs for selection state (kept hidden)
291
+ report_header_html_display, # 7: New HTML output for header
292
+ report_body_markdown_display, # 8: New Markdown output for body
293
+ reconstruction_cache_st, # 9: Reconstruction cache state
294
+ enhanced_okr_display_html, # 10: NEW: The enhanced HTML display for OKRs
295
+ actionable_okrs_data_st # 11: NEW: The actionable_okrs dictionary state
296
+ ]
297
+
298
+ initial_load_event = org_urn_display.change(
299
+ fn=initial_data_load_sequence,
300
+ inputs=[url_user_token_display, org_urn_display, token_state],
301
+ outputs=[status_box, token_state],
302
+ show_progress="full"
303
+ )
304
+
305
+ initial_load_event.then(
306
+ fn=analytics_tab_instance._refresh_analytics_graphs_ui,
307
+ inputs=[token_state, analytics_tab_instance.date_filter_selector, analytics_tab_instance.custom_start_date_picker,
308
+ analytics_tab_instance.custom_end_date_picker, chat_histories_st],
309
+ outputs=analytics_tab_instance.graph_refresh_outputs_list,
310
+ show_progress="full"
311
+ ).then(
312
+ fn=load_and_display_agentic_results,
313
+ inputs=[token_state, reconstruction_cache_st],
314
+ # MODIFIED: Updated outputs to match all components returned by load_and_display_agentic_results (now 12)
315
+ outputs=agentic_display_outputs,
316
+ show_progress="minimal"
317
+ ).then( # NEW CHAIN: Update the enhanced OKR display after load_and_display_agentic_results runs
318
+ fn=format_okrs_for_enhanced_display,
319
+ inputs=[reconstruction_cache_st], # Change from actionable_okrs_data_st to reconstruction_cache_st
320
+ outputs=[enhanced_okr_display_html],
321
+ show_progress="minimal"
322
  )
323
 
 
 
324
 
325
  if __name__ == "__main__":
 
 
 
 
 
 
326
  if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR):
327
+ logging.warning(f"WARNING: '{LINKEDIN_CLIENT_ID_ENV_VAR}' is not set.")
328
+ if not all(os.environ.get(var) for var in [BUBBLE_APP_NAME_ENV_VAR, BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR]):
329
+ logging.warning("WARNING: One or more Bubble environment variables are not set.")
 
 
 
 
 
 
 
 
 
330
  if not AGENTIC_MODULES_LOADED:
331
+ logging.warning("CRITICAL: Agentic modules failed to load.")
332
+ if not os.environ.get("GEMINI_API_KEY"):
333
+ logging.warning("WARNING: 'GEMINI_API_KEY' is not set.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
+ app.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)), debug=True)