Anshereina commited on
Commit
fa1944f
Β·
verified Β·
1 Parent(s): c6a7a70

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +436 -0
app.py ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import datetime
3
+ import json
4
+ import os
5
+ from transformers import pipeline
6
+
7
+ # Load an emotion detection model
8
+ # top_k=None to get all emotions and their scores
9
+ emotion_pipeline = pipeline("text-classification", model="SamLowe/roberta-base-go_emotions", top_k=None)
10
+
11
+ # Custom motivational responses based on various emotions
12
+ def get_motivation(label):
13
+ messages = {
14
+ "joy": "That's wonderful! Keep embracing that positive energy! 🌟",
15
+ "optimism": "It's great to see your hopeful outlook! Keep believing in yourself! ✨",
16
+ "love": "Feeling the love is a beautiful thing! Cherish these moments. ❀️",
17
+ "sadness": "I'm here for you. It's okay to feel sad, and remember that tough times don't last. 🌧️",
18
+ "anger": "It sounds like you're feeling angry. Acknowledge your feelings, and remember to be kind to yourself. 😀",
19
+ "fear": "It's natural to feel scared sometimes. Take a deep breath; you're stronger than you think. πŸ›‘οΈ",
20
+ "surprise": "Oh, what an unexpected turn! Hope it's a good one! 😲",
21
+ "neutral": "It's okay to have neutral days. Keep going, one step at a time. 🌱",
22
+ "disappointment": "It's tough when things don't go as planned. Allow yourself to feel it, and remember there's always tomorrow.πŸ˜”",
23
+ "excitement": "Awesome! Your excitement is contagious! Keep that energy flowing! πŸŽ‰",
24
+ "gratitude": "Feeling thankful is a wonderful practice. What a lovely perspective! πŸ™",
25
+ "desire": "It's good to have aspirations! What steps can you take towards your goals? 🌠",
26
+ "confusion": "Feeling a bit lost? It's okay to not have all the answers. Take your time to figure things out. πŸ€”",
27
+ "admiration": "It's inspiring to look up to someone! What qualities do you admire most? 🀩",
28
+ "amusement": "Laughter is the best medicine! Glad to hear you're finding joy. πŸ˜‚"
29
+ }
30
+ return messages.get(label.lower(), "Thanks for sharing. Keep expressing yourself. πŸ’¬")
31
+
32
+ # Function to get suggestions based on emotion
33
+ def get_suggestions(emotion_label):
34
+ suggestions = {
35
+ "sadness": [
36
+ "Relax and listen to calming music.",
37
+ "Take a warm bath or shower.",
38
+ "Connect with a trusted friend or family member.",
39
+ "Go for a gentle walk in nature.",
40
+ "Watch a comforting movie or read a book.",
41
+ "Allow yourself to cry if you need to, it can be a healthy release.",
42
+ "Engage in a favorite hobby that brings you a sense of calm."
43
+ ],
44
+ "anger": [
45
+ "Take a few deep breaths to calm your nervous system.",
46
+ "Step away from the situation if possible.",
47
+ "Engage in physical activity, like a brisk walk or some stretches, to release tension.",
48
+ "Express your feelings in a non-confrontational way, perhaps by writing them down.",
49
+ "Listen to music that helps you relax or channel your energy.",
50
+ "Squeeze a stress ball or do something with your hands like crafting."
51
+ ],
52
+ "fear": [
53
+ "Practice deep breathing exercises (e.g., 4-7-8 breathing).",
54
+ "Identify 5 things you can see, 4 you can touch, 3 you can hear, 2 you can smell, and 1 you can taste (grounding technique).",
55
+ "Talk to someone you trust about your worries.",
56
+ "Engage in a calming activity like meditation or gentle yoga.",
57
+ "Remind yourself that fear is a natural emotion and it will pass.",
58
+ "Challenge anxious thoughts by asking if they are truly factual."
59
+ ],
60
+ "joy": [
61
+ "Savor the moment and appreciate what made you feel this way.",
62
+ "Share your happiness with loved ones.",
63
+ "Engage in activities that bring you more joy, like a favorite hobby.",
64
+ "Practice gratitude: list things you are thankful for.",
65
+ "Help someone else – giving back can amplify joyful feelings.",
66
+ "Capture the moment (e.g., take a photo, write it down) to revisit later."
67
+ ],
68
+ "optimism": [
69
+ "Keep focusing on your goals and take small steps towards them.",
70
+ "Share your positive outlook with others to inspire them.",
71
+ "Continue practicing gratitude to reinforce positive thinking.",
72
+ "Reflect on your strengths and past successes.",
73
+ "Consider setting new positive intentions for your future."
74
+ ],
75
+ "disappointment": [
76
+ "Acknowledge your feelings and allow yourself to feel disappointed.",
77
+ "Reflect on what you can learn from the situation.",
78
+ "Focus on what you can control moving forward.",
79
+ "Talk to someone supportive about how you feel.",
80
+ "Engage in a self-care activity to uplift your spirits.",
81
+ "Remember that it's okay for things not to go as planned sometimes."
82
+ ],
83
+ "gratitude": [
84
+ "Keep a gratitude journal to consistently note things you're thankful for.",
85
+ "Express your thanks to someone in your life.",
86
+ "Perform a random act of kindness for someone else.",
87
+ "Mindfully appreciate the small blessings in your day.",
88
+ "Reflect on how far you've come and what you've overcome."
89
+ ],
90
+ # Add more emotions and their corresponding suggestions here
91
+ }
92
+
93
+ # Default suggestions if no specific emotion is matched or for neutral
94
+ default_suggestions = [
95
+ "Take a moment to reflect on your day.",
96
+ "Engage in a relaxing activity like reading or listening to music.",
97
+ "Practice deep breathing exercises.",
98
+ "Connect with a friend or family member.",
99
+ "Consider going for a short walk.",
100
+ "Hydrate and consider a healthy snack."
101
+ ]
102
+
103
+ chosen_suggestions = suggestions.get(emotion_label.lower(), default_suggestions)
104
+
105
+ return chosen_suggestions
106
+
107
+ # Main journaling function
108
+ def analyze_journal(entry, confidence_threshold=0.1): # Added confidence_threshold
109
+ if not entry.strip():
110
+ # When clearing the input, also clear the suggestions output and state
111
+ return "Please write something in your journal entry.", "I'm ready to listen when you are!", "", []
112
+
113
+ results = emotion_pipeline(entry)
114
+
115
+ detected_emotions_display = ""
116
+ suggestions_output_list = []
117
+
118
+ # Sort results by score in descending order
119
+ # results is typically a list containing a list of dictionaries, so results[0]
120
+ if not results or not results[0]: # Handle case where results might be empty
121
+ detected_emotions_display = "No emotions detected."
122
+ suggestions_output_list.extend(get_suggestions("neutral"))
123
+ return f"🧠 Detected Emotions:\n{detected_emotions_display}", "Thanks for sharing. Keep expressing yourself. πŸ’¬", \
124
+ "\n\n**πŸ’‘ Here are some suggestions for you:**\n" + "\n".join([f"- {s}" for s in suggestions_output_list]), \
125
+ suggestions_output_list
126
+
127
+ sorted_results = sorted(results[0], key=lambda x: x['score'], reverse=True)
128
+
129
+ for emotion_data in sorted_results:
130
+ emotion_label = emotion_data['label']
131
+ emotion_score = emotion_data['score']
132
+
133
+ if emotion_score >= confidence_threshold:
134
+ detected_emotions_display += f"- **{emotion_label.replace('_', ' ').title()}** (Confidence: {emotion_score:.2f})\n"
135
+ suggestions_output_list.extend(get_suggestions(emotion_label))
136
+
137
+ if not detected_emotions_display:
138
+ detected_emotions_display = "No specific emotions detected with high confidence."
139
+ suggestions_output_list.extend(get_suggestions("neutral")) # Fallback to neutral suggestions
140
+
141
+ # Remove duplicates from suggestions while preserving order
142
+ unique_suggestions = []
143
+ seen = set()
144
+ for suggestion in suggestions_output_list:
145
+ if suggestion not in seen:
146
+ unique_suggestions.append(suggestion)
147
+ seen.add(suggestion)
148
+
149
+ suggestion_text = "\n\n**πŸ’‘ Here are some suggestions for you:**\n"
150
+ if unique_suggestions:
151
+ for suggestion in unique_suggestions:
152
+ suggestion_text += f"- {suggestion}\n"
153
+ else:
154
+ suggestion_text += "No specific suggestions generated based on your entry."
155
+
156
+ # Get a primary emotion for the motivational response (e.g., the top one)
157
+ primary_emotion = sorted_results[0]['label'] if sorted_results else "neutral"
158
+ response = get_motivation(primary_emotion)
159
+
160
+ return f"🧠 Detected Emotions:\n{detected_emotions_display}", response, suggestion_text, unique_suggestions
161
+
162
+
163
+ # --- Calendar To-Do List Feature Setup ---
164
+ TASKS_FILE = "calendar_tasks.json"
165
+
166
+ def load_calendar_tasks():
167
+ if os.path.exists(TASKS_FILE):
168
+ try:
169
+ with open(TASKS_FILE, 'r') as f:
170
+ return json.load(f)
171
+ except json.JSONDecodeError:
172
+ print(f"Warning: {TASKS_FILE} is corrupted or empty. Starting with empty tasks.")
173
+ return {}
174
+ return {} # { "YYYY-MM-DD": [{"item": "...", "completed": False}, ...] }
175
+
176
+ def save_calendar_tasks(tasks_data):
177
+ with open(TASKS_FILE, 'w') as f:
178
+ json.dump(tasks_data, f, indent=4)
179
+
180
+ # Load tasks when the app starts
181
+ global_calendar_tasks = load_calendar_tasks()
182
+
183
+ def format_date_input(selected_date):
184
+ """Ensures the date is in "YYYY-MM-DD" format from various Gradio date inputs."""
185
+ if isinstance(selected_date, datetime.date):
186
+ return selected_date.strftime("%Y-%m-%d")
187
+ elif isinstance(selected_date, datetime.datetime):
188
+ return selected_date.strftime("%Y-%m-%d")
189
+ elif isinstance(selected_date, str):
190
+ try:
191
+ # Handle potential ISO format strings from gr.DateTime
192
+ # Gradio's gr.DateTime often returns ISO formatted strings like "YYYY-MM-DDTHH:MM:SS.sssZ"
193
+ dt_obj = datetime.datetime.fromisoformat(selected_date.replace('Z', '+00:00'))
194
+ return dt_obj.strftime("%Y-%m-%d")
195
+ except ValueError:
196
+ # Assume it's already "YYYY-MM-DD" if other conversions fail
197
+ return selected_date
198
+ return datetime.date.today().strftime("%Y-%m-%d") # Default fallback
199
+
200
+ def get_tasks_for_date(selected_date):
201
+ selected_date_str = format_date_input(selected_date)
202
+ return global_calendar_tasks.get(selected_date_str, [])
203
+
204
+ def display_calendar_tasks(selected_date):
205
+ selected_date_str = format_date_input(selected_date)
206
+ tasks = get_tasks_for_date(selected_date_str)
207
+
208
+ display_title = f"### To-Do List for {selected_date_str}:"
209
+ if not tasks:
210
+ display_title += "\nNo tasks for this date. Add one!"
211
+
212
+ # We will no longer generate HTML directly.
213
+ # Instead, we will configure the CheckboxGroup to reflect the current tasks and their completion status.
214
+
215
+ task_labels = [task['item'] for task in tasks]
216
+ completed_task_labels = [task['item'] for task in tasks if task['completed']]
217
+
218
+ # The CheckboxGroup's choices are all task items.
219
+ # Its value is the list of items that are completed.
220
+ return (
221
+ display_title,
222
+ gr.CheckboxGroup(choices=task_labels, value=completed_task_labels, visible=True, label="Tasks"),
223
+ "" # Clear the new item textbox
224
+ )
225
+
226
+
227
+ def add_calendar_todo_item(selected_date, item):
228
+ selected_date_str = format_date_input(selected_date)
229
+
230
+ if not item.strip():
231
+ # Re-display tasks without adding empty item, clear input
232
+ title, checkbox_group_output, _ = display_calendar_tasks(selected_date_str)
233
+ return title, checkbox_group_output, "" # Clear input
234
+
235
+ tasks_for_today = global_calendar_tasks.get(selected_date_str, [])
236
+ # Check if the item already exists to prevent duplicates if you want to.
237
+ # For now, we'll allow duplicates to simplify.
238
+
239
+ tasks_for_today.append({"item": item.strip(), "completed": False})
240
+ global_calendar_tasks[selected_date_str] = tasks_for_today
241
+ save_calendar_tasks(global_calendar_tasks)
242
+
243
+ # Re-display tasks and clear input
244
+ title, checkbox_group_output, _ = display_calendar_tasks(selected_date_str)
245
+ return title, checkbox_group_output, "" # Clear input
246
+
247
+ def toggle_calendar_todo_item(selected_date, selected_tasks_labels_from_checkbox_group):
248
+ selected_date_str = format_date_input(selected_date)
249
+ tasks = global_calendar_tasks.get(selected_date_str, [])
250
+
251
+ # Update the completed status for each task based on the CheckboxGroup's value
252
+ for task in tasks:
253
+ task["completed"] = (task["item"] in selected_tasks_labels_from_checkbox_group)
254
+
255
+ save_calendar_tasks(global_calendar_tasks)
256
+
257
+ # Re-display the tasks to update the visual representation (e.g., strikethrough)
258
+ # The display_calendar_tasks function will now return a new gr.CheckboxGroup
259
+ # with updated choices and values.
260
+ return display_calendar_tasks(selected_date)
261
+
262
+
263
+ def clear_completed_tasks_today(selected_date):
264
+ selected_date_str = format_date_input(selected_date)
265
+
266
+ if selected_date_str in global_calendar_tasks:
267
+ # Filter out completed tasks
268
+ global_calendar_tasks[selected_date_str] = [
269
+ task for task in global_calendar_tasks[selected_date_str] if not task['completed']
270
+ ]
271
+ save_calendar_tasks(global_calendar_tasks)
272
+
273
+ # Re-display the tasks for today after clearing completed ones
274
+ # This will now correctly return a new gr.CheckboxGroup with fewer items if tasks were cleared
275
+ return display_calendar_tasks(selected_date)
276
+
277
+
278
+ # This function is called to update the choices of the gr.Radio component
279
+ def populate_suggestions_radio(suggestions_list):
280
+ """
281
+ Populates the gr.Radio component with suggestions.
282
+ This function should be directly mapped to the outputs of an event.
283
+ """
284
+ if suggestions_list:
285
+ # If there are suggestions, make it visible and populate choices
286
+ return gr.Radio(choices=suggestions_list, value=None, visible=True)
287
+ else:
288
+ # If no suggestions, ensure choices are empty and it's hidden
289
+ return gr.Radio(choices=[], value=None, visible=False)
290
+
291
+ def add_suggestion_to_todo(selected_date, suggestion_to_add):
292
+ if suggestion_to_add:
293
+ # Call the existing add_calendar_todo_item function
294
+ title, checkbox_group_output, new_item_textbox_val = add_calendar_todo_item(selected_date, suggestion_to_add)
295
+ # Clear the selected radio button after adding
296
+ return title, checkbox_group_output, new_item_textbox_val, gr.Radio(value=None, visible=True) # Reset radio selection
297
+ else:
298
+ # If no suggestion selected, just refresh the display and clear input field
299
+ title, checkbox_group_output, _ = display_calendar_tasks(selected_date)
300
+ return title, checkbox_group_output, gr.Textbox(value="", interactive=True), gr.Radio(value=None, visible=True)
301
+
302
+
303
+ # --- Combined Gradio Interface (All in one frame) ---
304
+ with gr.Blocks(title="✨ Your Multi-purpose AI Companion") as demo:
305
+ gr.Markdown("# ✨ Your Multi-purpose AI Companion")
306
+ gr.Markdown("Welcome! This AI buddy offers journaling and a to-do list all in one place.")
307
+
308
+ # --- Journal Buddy Section ---
309
+ gr.HTML("<hr>")
310
+ gr.Markdown("## πŸ“ Journal Buddy")
311
+ gr.Markdown("Reflect, vent, or write freely. Get feedback based on your emotional tone.")
312
+ with gr.Column():
313
+ journal_input = gr.Textbox(label="Your Journal Entry", placeholder="Write how your day went or what's on your mind...", lines=5)
314
+ journal_analyze_button = gr.Button("Analyze Journal")
315
+ journal_output_mood = gr.Markdown(label="Detected Mood")
316
+ journal_output_response = gr.Text(label="Journal Buddy's Response")
317
+ journal_output_suggestions_markdown = gr.Markdown(label="Suggestions for You") # Markdown for display
318
+
319
+ # Hidden state to pass suggestions to the calendar tab
320
+ journal_suggestions_list_state = gr.State(value=[])
321
+
322
+ journal_analyze_result = journal_analyze_button.click( # Store the event object
323
+ fn=analyze_journal,
324
+ inputs=journal_input,
325
+ outputs=[
326
+ journal_output_mood,
327
+ journal_output_response,
328
+ journal_output_suggestions_markdown,
329
+ journal_suggestions_list_state # This updates the gr.State
330
+ ]
331
+ )
332
+ gr.Examples(
333
+ examples=[
334
+ ["I had a fantastic day, everything went perfectly and I feel so happy!"],
335
+ ["I'm really struggling today, nothing seems to be going right and I feel so sad and frustrated."],
336
+ ["I'm so angry about what happened at work, I can't believe it. I also feel a bit disappointed."],
337
+ ["Today was just a regular day, nothing special happened."],
338
+ ["I am so grateful for the sunny weather and a good cup of coffee this morning. Feeling very content."]
339
+ ],
340
+ inputs=journal_input
341
+ )
342
+
343
+ # --- Calendar To-Do List Section ---
344
+ gr.HTML("<hr>")
345
+ gr.Markdown("## πŸ“… Calendar To-Do List")
346
+ gr.Markdown("This To-Do list is automatically set to today's date.")
347
+
348
+ with gr.Tabs() as calendar_tabs: # Use gr.Tabs for multiple sections within the To-Do list
349
+ with gr.TabItem("Manage To-Dos", id="manage_todos_tab"): # Added ID for clarity
350
+ with gr.Column():
351
+ # Automatically set to today's date and make it non-interactive
352
+ calendar_date_picker = gr.DateTime(
353
+ label="Today's Date",
354
+ value=datetime.datetime.now(),
355
+ type="date",
356
+ info="Tasks are managed for today's date.",
357
+ interactive=False # Key change: Make it non-interactive
358
+ )
359
+
360
+ calendar_todo_title = gr.Markdown() # To display the date title
361
+
362
+ # IMPORTANT CHANGE: Use gr.CheckboxGroup directly for displaying tasks
363
+ calendar_todo_checkboxes_display = gr.CheckboxGroup(
364
+ label="Your Tasks",
365
+ choices=[], # Will be populated by display_calendar_tasks
366
+ value=[], # Will be populated by display_calendar_tasks
367
+ interactive=True # Allow users to check/uncheck
368
+ )
369
+
370
+ with gr.Row():
371
+ new_item_textbox_calendar = gr.Textbox(label="New To-Do Item", placeholder="e.g., Team meeting", scale=4)
372
+ add_button_calendar = gr.Button("Add Item", scale=1)
373
+
374
+ clear_completed_button_calendar = gr.Button("Clear Done Tasks Today", variant="secondary")
375
+
376
+
377
+ # This ensures tasks are displayed when the app initially loads, for today's date
378
+ demo.load(
379
+ fn=lambda: display_calendar_tasks(datetime.date.today()),
380
+ outputs=[calendar_todo_title, calendar_todo_checkboxes_display, new_item_textbox_calendar]
381
+ )
382
+
383
+ add_button_calendar.click(
384
+ fn=add_calendar_todo_item,
385
+ inputs=[calendar_date_picker, new_item_textbox_calendar],
386
+ outputs=[calendar_todo_title, calendar_todo_checkboxes_display, new_item_textbox_calendar]
387
+ )
388
+
389
+ # IMPORTANT CHANGE: Wire the `change` event of the visible CheckboxGroup
390
+ calendar_todo_checkboxes_display.change(
391
+ fn=toggle_calendar_todo_item,
392
+ inputs=[calendar_date_picker, calendar_todo_checkboxes_display],
393
+ outputs=[calendar_todo_title, calendar_todo_checkboxes_display, new_item_textbox_calendar] # Ensure all outputs are updated
394
+ )
395
+
396
+ clear_completed_button_calendar.click(
397
+ fn=clear_completed_tasks_today,
398
+ inputs=calendar_date_picker,
399
+ outputs=[calendar_todo_title, calendar_todo_checkboxes_display, new_item_textbox_calendar]
400
+ )
401
+
402
+ with gr.TabItem("AI Suggestions", id="ai_suggestions_tab"): # Added ID for clarity
403
+ gr.Markdown("### Add Journal Suggestions to your To-Do List")
404
+ gr.Markdown("Select a suggestion from your last journal entry to add it directly to the To-Do list for today.")
405
+
406
+ # Display suggestions as radio buttons for selection
407
+ ai_suggestions_radio = gr.Radio(label="Choose a suggestion to add:", choices=[], value=None, visible=False)
408
+ add_suggestion_button = gr.Button("Add Selected Suggestion to To-Do List")
409
+
410
+ # Corrected logic: Use the .then() method to ensure sequential updates
411
+ # First, analyze journal and update state. Then, use the state to populate the radio.
412
+ journal_analyze_result.then( # Use the stored event object from journal_analyze_button.click
413
+ fn=populate_suggestions_radio,
414
+ inputs=journal_suggestions_list_state, # Input the state that was just updated
415
+ outputs=ai_suggestions_radio # Output to the radio button to update its choices
416
+ ).then( # Chain another .then() to switch tabs
417
+ fn=lambda: gr.Tabs(selected="ai_suggestions_tab"),
418
+ outputs=calendar_tabs
419
+ )
420
+
421
+ # Action to add selected suggestion to the to-do list
422
+ add_suggestion_button.click(
423
+ fn=add_suggestion_to_todo,
424
+ inputs=[calendar_date_picker, ai_suggestions_radio],
425
+ outputs=[calendar_todo_title, calendar_todo_checkboxes_display, new_item_textbox_calendar, ai_suggestions_radio]
426
+ )
427
+
428
+ # Clear suggestions when switching back to the "Manage To-Dos" tab
429
+ # We don't need to clear suggestions on date_picker.change since it's non-interactive
430
+ calendar_tabs.select(
431
+ fn=lambda selected_tab: gr.Radio(choices=[], value=None, visible=False) if selected_tab == "manage_todos_tab" else gr.Noop(),
432
+ inputs=calendar_tabs,
433
+ outputs=ai_suggestions_radio
434
+ )
435
+
436
+ demo.launch()