gsingh24 commited on
Commit
29a6fbf
Β·
verified Β·
1 Parent(s): fa772c5

Upload 5 files

Browse files
Files changed (5) hide show
  1. careloop_main.py +1143 -0
  2. config.py +160 -0
  3. demo_scenarios.py +216 -0
  4. gradio_app.py +622 -0
  5. requirements-space.txt +10 -0
careloop_main.py ADDED
@@ -0,0 +1,1143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CareLoop Hackathon Submission - Main Application
2
+ # AI-Powered Family Caregiving Assistant
3
+
4
+ import asyncio
5
+ import json
6
+ from datetime import datetime, timedelta
7
+ from typing import Dict, List, Any, TypedDict, Optional
8
+ from dataclasses import dataclass, asdict
9
+ from enum import Enum
10
+ import random
11
+ import uuid
12
+
13
+ # LangGraph and LangChain imports
14
+ from langgraph.graph import StateGraph, END
15
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
16
+ from langchain_openai import ChatOpenAI
17
+ from langchain_core.tools import tool
18
+
19
+ # FastAPI for web interface
20
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
21
+ from fastapi.staticfiles import StaticFiles
22
+ from fastapi.responses import HTMLResponse
23
+ import uvicorn
24
+
25
+ # ============== MOCK DATA MODELS ==============
26
+
27
+ class AlertLevel(Enum):
28
+ LOW = "low"
29
+ MEDIUM = "medium"
30
+ HIGH = "high"
31
+ URGENT = "urgent"
32
+
33
+ @dataclass
34
+ class Parent:
35
+ id: str
36
+ name: str
37
+ age: int
38
+ conditions: List[str]
39
+ medications: List[Dict]
40
+ emergency_contacts: List[Dict]
41
+ preferences: Dict
42
+
43
+ @dataclass
44
+ class FamilyMember:
45
+ id: str
46
+ name: str
47
+ relationship: str
48
+ phone: str
49
+ email: str
50
+ role: str
51
+ notification_preferences: Dict
52
+
53
+ @dataclass
54
+ class HealthMetric:
55
+ timestamp: datetime
56
+ metric_type: str
57
+ value: float
58
+ unit: str
59
+ source: str
60
+ normal_range: tuple
61
+
62
+ # ============== COMPREHENSIVE MOCK DATA ==============
63
+
64
+ class MockDataGenerator:
65
+ def __init__(self):
66
+ self.parents = {}
67
+ self.families = {}
68
+ self.health_data = {}
69
+ self.medication_data = {}
70
+ self.setup_families()
71
+
72
+ def setup_families(self):
73
+ # Family 1: Margaret Chen (active, multiple conditions)
74
+ margaret = Parent(
75
+ id="parent_001",
76
+ name="Margaret Chen",
77
+ age=78,
78
+ conditions=["Type 2 Diabetes", "Hypertension", "Mild Cognitive Impairment"],
79
+ medications=[
80
+ {"name": "Metformin", "dosage": "500mg", "frequency": "2x daily", "times": ["08:00", "20:00"]},
81
+ {"name": "Lisinopril", "dosage": "10mg", "frequency": "1x daily", "times": ["08:00"]},
82
+ {"name": "Donepezil", "dosage": "5mg", "frequency": "1x daily", "times": ["20:00"]}
83
+ ],
84
+ emergency_contacts=[
85
+ {"name": "Dr. Sarah Kim", "phone": "555-MED-1234", "type": "primary_care"},
86
+ {"name": "Mercy General Hospital", "phone": "555-911-HELP", "type": "emergency"}
87
+ ],
88
+ preferences={
89
+ "preferred_contact_time": "morning",
90
+ "communication_style": "gentle",
91
+ "daily_check_in_time": "19:00"
92
+ }
93
+ )
94
+
95
+ margaret_family = [
96
+ FamilyMember(
97
+ id="family_001",
98
+ name="David Chen",
99
+ relationship="son",
100
+ phone="555-123-4567",
101
+ email="david.chen@email.com",
102
+ role="primary_caregiver",
103
+ notification_preferences={
104
+ "urgent_alerts": True,
105
+ "daily_summary": True,
106
+ "medication_reminders": True,
107
+ "health_trends": True
108
+ }
109
+ ),
110
+ FamilyMember(
111
+ id="family_002",
112
+ name="Lisa Chen-Rodriguez",
113
+ relationship="daughter",
114
+ phone="555-987-6543",
115
+ email="lisa.rodriguez@email.com",
116
+ role="backup_caregiver",
117
+ notification_preferences={
118
+ "urgent_alerts": True,
119
+ "daily_summary": False,
120
+ "medication_reminders": False,
121
+ "health_trends": True
122
+ }
123
+ ),
124
+ FamilyMember(
125
+ id="family_003",
126
+ name="Jennifer Chen",
127
+ relationship="daughter-in-law",
128
+ phone="555-456-7890",
129
+ email="jen.chen@email.com",
130
+ role="support",
131
+ notification_preferences={
132
+ "urgent_alerts": True,
133
+ "daily_summary": False,
134
+ "medication_reminders": False,
135
+ "health_trends": False
136
+ }
137
+ )
138
+ ]
139
+
140
+ # Family 2: Robert Johnson (recent stroke recovery)
141
+ robert = Parent(
142
+ id="parent_002",
143
+ name="Robert Johnson",
144
+ age=72,
145
+ conditions=["Stroke Recovery", "Atrial Fibrillation", "Arthritis"],
146
+ medications=[
147
+ {"name": "Warfarin", "dosage": "5mg", "frequency": "1x daily", "times": ["18:00"]},
148
+ {"name": "Atorvastatin", "dosage": "20mg", "frequency": "1x daily", "times": ["20:00"]},
149
+ {"name": "Aspirin", "dosage": "81mg", "frequency": "1x daily", "times": ["08:00"]}
150
+ ],
151
+ emergency_contacts=[
152
+ {"name": "Dr. James Miller", "phone": "555-MED-8765", "type": "primary_care"},
153
+ {"name": "Central Hospital", "phone": "555-911-9999", "type": "emergency"}
154
+ ],
155
+ preferences={
156
+ "preferred_contact_time": "afternoon",
157
+ "communication_style": "direct",
158
+ "daily_check_in_time": "17:00"
159
+ }
160
+ )
161
+
162
+ robert_family = [
163
+ FamilyMember(
164
+ id="family_101",
165
+ name="Michael Johnson",
166
+ relationship="son",
167
+ phone="555-333-4444",
168
+ email="michael.johnson@email.com",
169
+ role="primary_caregiver",
170
+ notification_preferences={
171
+ "urgent_alerts": True,
172
+ "daily_summary": True,
173
+ "medication_reminders": True,
174
+ "health_trends": True
175
+ }
176
+ ),
177
+ FamilyMember(
178
+ id="family_102",
179
+ name="Susan Johnson",
180
+ relationship="daughter",
181
+ phone="555-555-6666",
182
+ email="susan.johnson@email.com",
183
+ role="backup_caregiver",
184
+ notification_preferences={
185
+ "urgent_alerts": True,
186
+ "daily_summary": True,
187
+ "medication_reminders": True,
188
+ "health_trends": True
189
+ }
190
+ )
191
+ ]
192
+
193
+ # Family 3: Elena Gonzalez (early Alzheimer's)
194
+ elena = Parent(
195
+ id="parent_003",
196
+ name="Elena Gonzalez",
197
+ age=81,
198
+ conditions=["Early Alzheimer's", "Osteoporosis", "COPD"],
199
+ medications=[
200
+ {"name": "Aricept", "dosage": "10mg", "frequency": "1x daily", "times": ["20:00"]},
201
+ {"name": "Alendronate", "dosage": "70mg", "frequency": "1x weekly", "times": ["08:00"]},
202
+ {"name": "Albuterol", "dosage": "90mcg", "frequency": "as needed", "times": ["08:00", "20:00"]}
203
+ ],
204
+ emergency_contacts=[
205
+ {"name": "Dr. Maria Lopez", "phone": "555-MED-9876", "type": "primary_care"},
206
+ {"name": "Sunset Medical Center", "phone": "555-911-4321", "type": "emergency"}
207
+ ],
208
+ preferences={
209
+ "preferred_contact_time": "morning",
210
+ "communication_style": "simple",
211
+ "daily_check_in_time": "10:00"
212
+ }
213
+ )
214
+
215
+ elena_family = [
216
+ FamilyMember(
217
+ id="family_201",
218
+ name="Carlos Gonzalez",
219
+ relationship="son",
220
+ phone="555-777-8888",
221
+ email="carlos.gonzalez@email.com",
222
+ role="primary_caregiver",
223
+ notification_preferences={
224
+ "urgent_alerts": True,
225
+ "daily_summary": True,
226
+ "medication_reminders": True,
227
+ "health_trends": True
228
+ }
229
+ ),
230
+ FamilyMember(
231
+ id="family_202",
232
+ name="Isabella Martinez",
233
+ relationship="granddaughter",
234
+ phone="555-999-0000",
235
+ email="isabella.martinez@email.com",
236
+ role="support",
237
+ notification_preferences={
238
+ "urgent_alerts": True,
239
+ "daily_summary": False,
240
+ "medication_reminders": False,
241
+ "health_trends": True
242
+ }
243
+ )
244
+ ]
245
+
246
+ # Store in parent dictionary
247
+ self.parents = {
248
+ "parent_001": margaret,
249
+ "parent_002": robert,
250
+ "parent_003": elena
251
+ }
252
+
253
+ # Store in family dictionary
254
+ self.families = {
255
+ "parent_001": margaret_family,
256
+ "parent_002": robert_family,
257
+ "parent_003": elena_family
258
+ }
259
+
260
+ # Generate health data for each parent
261
+ for parent_id in self.parents:
262
+ self.health_data[parent_id] = self.generate_health_timeline(parent_id)
263
+ self.medication_data[parent_id] = self.generate_medication_timeline(parent_id)
264
+
265
+ # For backward compatibility
266
+ self.margaret = self.parents["parent_001"]
267
+ self.margaret_family = self.families["parent_001"]
268
+
269
+ def get_parent_by_id(self, parent_id: str) -> Optional[Parent]:
270
+ """Get parent data by ID"""
271
+ return self.parents.get(parent_id)
272
+
273
+ def get_family_by_parent_id(self, parent_id: str) -> List[FamilyMember]:
274
+ """Get family members for a specific parent"""
275
+ return self.families.get(parent_id, [])
276
+
277
+ def get_health_data_by_parent_id(self, parent_id: str) -> List[HealthMetric]:
278
+ """Get health data for a specific parent"""
279
+ return self.health_data.get(parent_id, [])
280
+
281
+ def get_medication_data_by_parent_id(self, parent_id: str) -> List[Dict]:
282
+ """Get medication data for a specific parent"""
283
+ return self.medication_data.get(parent_id, [])
284
+
285
+ def generate_health_timeline(self, parent_id: str) -> List[HealthMetric]:
286
+ """Generate realistic health data for the past week"""
287
+ metrics = []
288
+ base_date = datetime.now() - timedelta(days=7)
289
+ parent = self.parents.get(parent_id)
290
+
291
+ if not parent:
292
+ return []
293
+
294
+ for day in range(7):
295
+ current_date = base_date + timedelta(days=day)
296
+
297
+ # Morning metrics
298
+ morning = current_date.replace(hour=8, minute=0)
299
+
300
+ # Customize metrics based on parent conditions
301
+ if parent_id == "parent_001": # Margaret (diabetes, hypertension)
302
+ metrics.extend([
303
+ HealthMetric(morning, "heart_rate", 72 + random.randint(-8, 8), "bpm", "apple_watch", (60, 90)),
304
+ HealthMetric(morning, "blood_pressure_systolic", 135 + random.randint(-15, 15), "mmHg", "omron_cuff", (90, 140)),
305
+ HealthMetric(morning, "blood_pressure_diastolic", 82 + random.randint(-10, 10), "mmHg", "omron_cuff", (60, 90)),
306
+ HealthMetric(morning, "weight", 145.2 + random.uniform(-1, 1), "lbs", "smart_scale", (140, 150)),
307
+ ])
308
+
309
+ # Blood glucose (Type 2 diabetes)
310
+ for hour in [8, 12, 18]:
311
+ glucose_time = current_date.replace(hour=hour, minute=random.randint(0, 30))
312
+ base_glucose = 140 if hour == 12 else 120 # Higher after lunch
313
+ metrics.append(
314
+ HealthMetric(glucose_time, "blood_glucose", base_glucose + random.randint(-20, 30), "mg/dL", "glucose_meter", (80, 130))
315
+ )
316
+
317
+ elif parent_id == "parent_002": # Robert (stroke recovery, AFib)
318
+ metrics.extend([
319
+ HealthMetric(morning, "heart_rate", 78 + random.randint(-5, 12), "bpm", "apple_watch", (60, 90)),
320
+ HealthMetric(morning, "blood_pressure_systolic", 142 + random.randint(-10, 20), "mmHg", "omron_cuff", (90, 140)),
321
+ HealthMetric(morning, "blood_pressure_diastolic", 88 + random.randint(-5, 10), "mmHg", "omron_cuff", (60, 90)),
322
+ HealthMetric(morning, "weight", 190.5 + random.uniform(-1, 1.5), "lbs", "smart_scale", (185, 195)),
323
+ HealthMetric(morning, "heart_rhythm", 1 if random.random() < 0.2 else 0, "irregularity", "ecg_monitor", (0, 0)),
324
+ ])
325
+
326
+ elif parent_id == "parent_003": # Elena (Alzheimer's, osteoporosis, COPD)
327
+ metrics.extend([
328
+ HealthMetric(morning, "heart_rate", 75 + random.randint(-8, 10), "bpm", "apple_watch", (60, 90)),
329
+ HealthMetric(morning, "blood_pressure_systolic", 128 + random.randint(-10, 15), "mmHg", "omron_cuff", (90, 140)),
330
+ HealthMetric(morning, "blood_pressure_diastolic", 78 + random.randint(-8, 8), "mmHg", "omron_cuff", (60, 90)),
331
+ HealthMetric(morning, "weight", 118.7 + random.uniform(-0.5, 0.5), "lbs", "smart_scale", (115, 120)),
332
+ HealthMetric(morning, "oxygen_saturation", 94 + random.randint(-3, 2), "%", "pulse_oximeter", (95, 100)),
333
+ ])
334
+
335
+ # Cognitive function test (for Alzheimer's)
336
+ if day % 2 == 0: # Every other day
337
+ test_time = current_date.replace(hour=11, minute=random.randint(0, 30))
338
+ metrics.append(
339
+ HealthMetric(test_time, "cognitive_score", 78 + random.randint(-8, 5), "points", "cognitive_test", (85, 100))
340
+ )
341
+
342
+ # Daily activity metrics - common for all
343
+ metrics.extend([
344
+ HealthMetric(current_date.replace(hour=23, minute=59), "steps", 2800 + random.randint(-800, 1200), "steps", "apple_watch", (2000, 8000)),
345
+ HealthMetric(current_date.replace(hour=23, minute=59), "sleep_hours", 6.5 + random.uniform(-1, 1.5), "hours", "apple_watch", (6, 9)),
346
+ ])
347
+
348
+ return sorted(metrics, key=lambda x: x.timestamp)
349
+
350
+ def generate_medication_timeline(self, parent_id: str) -> List[Dict]:
351
+ """Generate medication compliance data"""
352
+ compliance_data = []
353
+ base_date = datetime.now() - timedelta(days=7)
354
+ parent = self.parents.get(parent_id)
355
+
356
+ if not parent:
357
+ return []
358
+
359
+ for day in range(7):
360
+ current_date = base_date + timedelta(days=day)
361
+
362
+ for med in parent.medications:
363
+ for time_str in med["times"]:
364
+ hour, minute = map(int, time_str.split(':'))
365
+ scheduled_time = current_date.replace(hour=hour, minute=minute)
366
+
367
+ # Simulate realistic compliance patterns
368
+ compliance_rate = 0.9 # Default rate
369
+
370
+ if parent_id == "parent_001": # Margaret
371
+ if med["name"] == "Metformin":
372
+ compliance_rate = 0.95 if hour < 12 else 0.85
373
+ elif med["name"] == "Lisinopril":
374
+ compliance_rate = 0.92
375
+ else: # Donepezil
376
+ compliance_rate = 0.88
377
+ elif parent_id == "parent_002": # Robert
378
+ if med["name"] == "Warfarin":
379
+ compliance_rate = 0.97 # Critical medication
380
+ else:
381
+ compliance_rate = 0.9
382
+ elif parent_id == "parent_003": # Elena (Alzheimer's)
383
+ # More forgetful due to cognitive issues
384
+ compliance_rate = 0.75 if hour >= 18 else 0.85
385
+
386
+ was_taken = random.random() < compliance_rate
387
+ actual_time = None
388
+
389
+ if was_taken:
390
+ # Add realistic delay (5-30 minutes)
391
+ delay_minutes = random.randint(5, 30)
392
+ actual_time = scheduled_time + timedelta(minutes=delay_minutes)
393
+
394
+ compliance_data.append({
395
+ "medication": med["name"],
396
+ "scheduled_time": scheduled_time,
397
+ "actual_time": actual_time,
398
+ "was_taken": was_taken,
399
+ "source": "pill_dispenser" if was_taken else "missed"
400
+ })
401
+
402
+ return compliance_data
403
+
404
+ # ============== LANGGRAPH STATE DEFINITION ==============
405
+
406
+ class CareState(TypedDict):
407
+ parent_id: str
408
+ date: str
409
+ health_metrics: List[Dict]
410
+ medication_status: List[Dict]
411
+ concerns: List[str]
412
+ alerts: List[Dict]
413
+ daily_summary: str
414
+ action_items: List[str]
415
+ family_notifications: List[Dict]
416
+ emergency_level: str
417
+
418
+ # ============== SPECIALIZED CARE AGENTS ==============
419
+
420
+ class HealthMonitorAgent:
421
+ def __init__(self, llm):
422
+ self.llm = llm
423
+ self.mock_data = MockDataGenerator()
424
+
425
+ def analyze_health_patterns(self, state: CareState) -> CareState:
426
+ """Analyze health trends and identify concerns"""
427
+ recent_metrics = [m for m in self.mock_data.health_data[state["parent_id"]]
428
+ if m.timestamp >= datetime.now() - timedelta(days=3)]
429
+
430
+ concerns = []
431
+ alerts = []
432
+
433
+ # Analyze blood glucose trends (diabetes management)
434
+ glucose_readings = [m for m in recent_metrics if m.metric_type == "blood_glucose"]
435
+ if glucose_readings:
436
+ avg_glucose = sum(m.value for m in glucose_readings) / len(glucose_readings)
437
+ if avg_glucose > 160:
438
+ concerns.append("Blood glucose levels elevated - average 165 mg/dL over 3 days")
439
+ alerts.append({
440
+ "type": "health_concern",
441
+ "severity": AlertLevel.HIGH.value,
442
+ "message": "Diabetes management needs attention - glucose levels trending high",
443
+ "recommended_action": "Contact diabetes care team"
444
+ })
445
+
446
+ # Analyze activity levels
447
+ step_data = [m for m in recent_metrics if m.metric_type == "steps"]
448
+ if step_data:
449
+ avg_steps = sum(m.value for m in step_data) / len(step_data)
450
+ if avg_steps < 2000:
451
+ concerns.append(f"Low activity level - averaging {int(avg_steps)} steps/day")
452
+ alerts.append({
453
+ "type": "activity_concern",
454
+ "severity": AlertLevel.MEDIUM.value,
455
+ "message": "Activity levels below recommended for health maintenance",
456
+ "recommended_action": "Encourage gentle walks or physical therapy"
457
+ })
458
+
459
+ # Analyze sleep patterns
460
+ sleep_data = [m for m in recent_metrics if m.metric_type == "sleep_hours"]
461
+ if sleep_data:
462
+ avg_sleep = sum(m.value for m in sleep_data) / len(sleep_data)
463
+ if avg_sleep < 6:
464
+ concerns.append(f"Insufficient sleep - averaging {avg_sleep:.1f} hours/night")
465
+
466
+ # Blood pressure monitoring
467
+ bp_systolic = [m for m in recent_metrics if m.metric_type == "blood_pressure_systolic"]
468
+ if bp_systolic:
469
+ recent_bp = bp_systolic[-1].value
470
+ if recent_bp > 150:
471
+ concerns.append(f"Blood pressure elevated - last reading {recent_bp} mmHg")
472
+ alerts.append({
473
+ "type": "vital_concern",
474
+ "severity": AlertLevel.HIGH.value,
475
+ "message": "Blood pressure significantly elevated",
476
+ "recommended_action": "Schedule urgent medical appointment"
477
+ })
478
+
479
+ state["health_metrics"] = [asdict(m) for m in recent_metrics]
480
+ state["concerns"].extend(concerns)
481
+ state["alerts"].extend(alerts)
482
+
483
+ return state
484
+
485
+ class MedicationAgent:
486
+ def __init__(self, llm):
487
+ self.llm = llm
488
+ self.mock_data = MockDataGenerator()
489
+
490
+ def check_medication_compliance(self, state: CareState) -> CareState:
491
+ """Monitor medication adherence and identify issues"""
492
+ recent_meds = [m for m in self.mock_data.medication_data[state["parent_id"]]
493
+ if m["scheduled_time"] >= datetime.now() - timedelta(days=3)]
494
+
495
+ # Calculate compliance rates
496
+ compliance_summary = {}
497
+ for med_record in recent_meds:
498
+ med_name = med_record["medication"]
499
+ if med_name not in compliance_summary:
500
+ compliance_summary[med_name] = {"total": 0, "taken": 0, "missed_times": []}
501
+
502
+ compliance_summary[med_name]["total"] += 1
503
+ if med_record["was_taken"]:
504
+ compliance_summary[med_name]["taken"] += 1
505
+ else:
506
+ compliance_summary[med_name]["missed_times"].append(med_record["scheduled_time"])
507
+
508
+ # Generate alerts for poor compliance
509
+ alerts = []
510
+ concerns = []
511
+
512
+ for med_name, data in compliance_summary.items():
513
+ compliance_rate = data["taken"] / data["total"]
514
+
515
+ if compliance_rate < 0.8:
516
+ concerns.append(f"Poor medication compliance: {med_name} - {compliance_rate:.0%} over 3 days")
517
+ alerts.append({
518
+ "type": "medication_compliance",
519
+ "severity": AlertLevel.HIGH.value,
520
+ "message": f"{med_name} missed multiple doses - compliance at {compliance_rate:.0%}",
521
+ "recommended_action": "Review medication routine with family"
522
+ })
523
+ elif compliance_rate < 0.9:
524
+ concerns.append(f"Medication adherence concern: {med_name} - {compliance_rate:.0%}")
525
+
526
+ # Check for recent missed doses
527
+ recent_missed = [m for m in recent_meds
528
+ if not m["was_taken"] and
529
+ m["scheduled_time"] >= datetime.now() - timedelta(hours=24)]
530
+
531
+ if recent_missed:
532
+ for missed in recent_missed:
533
+ alerts.append({
534
+ "type": "missed_medication",
535
+ "severity": AlertLevel.MEDIUM.value,
536
+ "message": f"Missed {missed['medication']} at {missed['scheduled_time'].strftime('%I:%M %p')}",
537
+ "recommended_action": "Remind about missed dose"
538
+ })
539
+
540
+ state["medication_status"] = recent_meds
541
+ state["concerns"].extend(concerns)
542
+ state["alerts"].extend(alerts)
543
+
544
+ return state
545
+
546
+ class FamilyCommunicationAgent:
547
+ def __init__(self, llm):
548
+ self.llm = llm
549
+ self.mock_data = MockDataGenerator()
550
+
551
+ def generate_family_updates(self, state: CareState) -> CareState:
552
+ """Create personalized updates for each family member"""
553
+ notifications = []
554
+
555
+ # Get urgency level
556
+ alert_levels = [alert["severity"] for alert in state["alerts"]]
557
+ max_severity = AlertLevel.LOW.value
558
+ if AlertLevel.URGENT.value in alert_levels:
559
+ max_severity = AlertLevel.URGENT.value
560
+ elif AlertLevel.HIGH.value in alert_levels:
561
+ max_severity = AlertLevel.HIGH.value
562
+ elif AlertLevel.MEDIUM.value in alert_levels:
563
+ max_severity = AlertLevel.MEDIUM.value
564
+
565
+ # Generate notifications for each family member
566
+ for family_member in self.mock_data.get_family_by_parent_id(state["parent_id"]):
567
+ prefs = family_member.notification_preferences
568
+
569
+ # Determine what to include based on preferences and severity
570
+ should_send_immediate = False
571
+ message_parts = []
572
+
573
+ if max_severity in [AlertLevel.URGENT.value, AlertLevel.HIGH.value] and prefs["urgent_alerts"]:
574
+ should_send_immediate = True
575
+ urgent_alerts = [a for a in state["alerts"] if a["severity"] in [AlertLevel.URGENT.value, AlertLevel.HIGH.value]]
576
+ message_parts.append("🚨 **Urgent Update Needed**")
577
+ for alert in urgent_alerts[:2]: # Limit to top 2 urgent items
578
+ message_parts.append(f"β€’ {alert['message']}")
579
+
580
+ if prefs["daily_summary"]:
581
+ message_parts.append(f"\nπŸ“‹ **Daily Summary for {self.mock_data.get_parent_by_id(state['parent_id']).name}**")
582
+
583
+ # Health highlights
584
+ if state["health_metrics"]:
585
+ latest_metrics = {}
586
+ for metric in state["health_metrics"]:
587
+ if metric["metric_type"] not in latest_metrics or metric["timestamp"] > latest_metrics[metric["metric_type"]]["timestamp"]:
588
+ latest_metrics[metric["metric_type"]] = metric
589
+
590
+ message_parts.append("πŸ“Š **Today's Health Data:**")
591
+ if "steps" in latest_metrics:
592
+ steps = int(latest_metrics["steps"]["value"])
593
+ message_parts.append(f"β€’ Activity: {steps:,} steps")
594
+ if "blood_glucose" in latest_metrics:
595
+ glucose = latest_metrics["blood_glucose"]["value"]
596
+ message_parts.append(f"β€’ Blood Sugar: {glucose} mg/dL")
597
+
598
+ # Medication status
599
+ if prefs["medication_reminders"] and state["medication_status"]:
600
+ today_meds = [m for m in state["medication_status"]
601
+ if m["scheduled_time"].date() == datetime.now().date()]
602
+ taken_count = sum(1 for m in today_meds if m["was_taken"])
603
+ total_count = len(today_meds)
604
+
605
+ message_parts.append(f"πŸ’Š **Medications:** {taken_count}/{total_count} doses taken today")
606
+
607
+ if message_parts:
608
+ notifications.append({
609
+ "recipient": family_member.name,
610
+ "recipient_id": family_member.id,
611
+ "phone": family_member.phone,
612
+ "email": family_member.email,
613
+ "message": "\n".join(message_parts),
614
+ "urgency": max_severity,
615
+ "send_immediately": should_send_immediate,
616
+ "channels": ["sms"] if should_send_immediate else ["email"]
617
+ })
618
+
619
+ state["family_notifications"] = notifications
620
+ return state
621
+
622
+ def create_daily_summary(self, state: CareState) -> CareState:
623
+ """Generate comprehensive daily summary"""
624
+ summary_parts = []
625
+
626
+ # Header with date and overall status
627
+ today = datetime.now().strftime("%A, %B %d, %Y")
628
+ summary_parts.append(f"# Daily Care Report - {today}")
629
+ summary_parts.append(f"**Parent:** {self.mock_data.get_parent_by_id(state['parent_id']).name}")
630
+
631
+ # Overall status indicator
632
+ if any(alert["severity"] == AlertLevel.URGENT.value for alert in state["alerts"]):
633
+ summary_parts.append("πŸ”΄ **Status: Needs Immediate Attention**")
634
+ elif any(alert["severity"] == AlertLevel.HIGH.value for alert in state["alerts"]):
635
+ summary_parts.append("🟑 **Status: Some Concerns**")
636
+ elif state["concerns"]:
637
+ summary_parts.append("🟒 **Status: Monitoring**")
638
+ else:
639
+ summary_parts.append("βœ… **Status: All Good**")
640
+
641
+ # Key metrics summary
642
+ summary_parts.append("\n## Today's Highlights")
643
+
644
+ if state["health_metrics"]:
645
+ # Get today's key metrics
646
+ today_metrics = [m for m in state["health_metrics"]
647
+ if m["timestamp"].date() == datetime.now().date()]
648
+
649
+ if today_metrics:
650
+ latest_by_type = {}
651
+ for metric in today_metrics:
652
+ metric_type = metric["metric_type"]
653
+ if metric_type not in latest_by_type:
654
+ latest_by_type[metric_type] = metric
655
+
656
+ summary_parts.append("### Health Metrics")
657
+ for metric_type, data in latest_by_type.items():
658
+ value = data["value"]
659
+ unit = data["unit"]
660
+
661
+ if metric_type == "steps":
662
+ summary_parts.append(f"β€’ **Activity:** {int(value):,} {unit}")
663
+ elif metric_type == "blood_glucose":
664
+ status = "πŸ”΄ High" if value > 150 else "🟑 Elevated" if value > 130 else "βœ… Normal"
665
+ summary_parts.append(f"β€’ **Blood Sugar:** {value} {unit} {status}")
666
+ elif metric_type == "blood_pressure_systolic":
667
+ status = "πŸ”΄ High" if value > 140 else "🟑 Elevated" if value > 130 else "βœ… Normal"
668
+ summary_parts.append(f"β€’ **Blood Pressure:** {value} {unit} {status}")
669
+
670
+ # Medication summary
671
+ if state["medication_status"]:
672
+ today_meds = [m for m in state["medication_status"]
673
+ if m["scheduled_time"].date() == datetime.now().date()]
674
+
675
+ if today_meds:
676
+ taken_count = sum(1 for m in today_meds if m["was_taken"])
677
+ total_count = len(today_meds)
678
+
679
+ summary_parts.append(f"\n### Medication Compliance")
680
+ summary_parts.append(f"β€’ **Today:** {taken_count}/{total_count} doses taken")
681
+
682
+ if taken_count < total_count:
683
+ missed = [m for m in today_meds if not m["was_taken"]]
684
+ summary_parts.append("β€’ **Missed doses:**")
685
+ for m in missed:
686
+ time_str = m["scheduled_time"].strftime("%I:%M %p")
687
+ summary_parts.append(f" - {m['medication']} at {time_str}")
688
+
689
+ # Concerns and alerts
690
+ if state["concerns"]:
691
+ summary_parts.append(f"\n## Areas of Attention ({len(state['concerns'])})")
692
+ for concern in state["concerns"]:
693
+ summary_parts.append(f"β€’ {concern}")
694
+
695
+ # Action items
696
+ if state["action_items"]:
697
+ summary_parts.append(f"\n## Recommended Actions")
698
+ for action in state["action_items"]:
699
+ summary_parts.append(f"β€’ {action}")
700
+
701
+ state["daily_summary"] = "\n".join(summary_parts)
702
+ return state
703
+
704
+ class ActionPlannerAgent:
705
+ def __init__(self, llm):
706
+ self.llm = llm
707
+
708
+ def generate_action_items(self, state: CareState) -> CareState:
709
+ """Generate prioritized action items based on concerns and alerts"""
710
+ actions = []
711
+
712
+ # Process alerts to create specific actions
713
+ urgent_alerts = [a for a in state["alerts"] if a["severity"] == AlertLevel.URGENT.value]
714
+ high_alerts = [a for a in state["alerts"] if a["severity"] == AlertLevel.HIGH.value]
715
+
716
+ # Urgent actions first
717
+ for alert in urgent_alerts:
718
+ if alert["type"] == "vital_concern":
719
+ actions.append("🚨 URGENT: Schedule emergency medical appointment for blood pressure management")
720
+ elif alert["type"] == "medication_compliance":
721
+ actions.append(f"🚨 URGENT: Call parent immediately about missed {alert['message'].split()[0]} medications")
722
+
723
+ # High priority actions
724
+ for alert in high_alerts:
725
+ if alert["type"] == "health_concern":
726
+ actions.append("πŸ“ž HIGH: Contact diabetes care team about elevated glucose levels")
727
+ elif alert["type"] == "medication_compliance":
728
+ actions.append("πŸ“ž HIGH: Review medication routine - consider pill dispenser or reminder system")
729
+
730
+ # Medium priority actions based on concerns
731
+ concern_keywords = {
732
+ "low activity": "πŸ’ͺ Encourage gentle exercise - suggest short walks or chair exercises",
733
+ "insufficient sleep": "😴 Discuss sleep hygiene - check for pain or anxiety issues",
734
+ "blood pressure": "🩺 Monitor blood pressure daily and log readings for doctor",
735
+ "medication": "πŸ’Š Set up automated medication reminders or family check-ins"
736
+ }
737
+
738
+ for concern in state["concerns"]:
739
+ concern_lower = concern.lower()
740
+ for keyword, action in concern_keywords.items():
741
+ if keyword in concern_lower and action not in actions:
742
+ actions.append(action)
743
+
744
+ # Always include routine actions
745
+ routine_actions = [
746
+ "πŸ“‹ Review tomorrow's medication schedule",
747
+ "πŸ“± Confirm evening check-in call is scheduled",
748
+ "πŸ“Š Check health metrics sync from devices"
749
+ ]
750
+
751
+ # Only add routine actions if no urgent/high priority actions
752
+ if not urgent_alerts and not high_alerts:
753
+ actions.extend(routine_actions)
754
+
755
+ # Limit to top 5 actions for manageability
756
+ state["action_items"] = actions[:5]
757
+ return state
758
+
759
+ # ============== MAIN LANGGRAPH WORKFLOW ==============
760
+
761
+ class CareLoopOrchestrator:
762
+ def __init__(self):
763
+ # For demo purposes, we'll mock the LLM
764
+ self.llm = None # ChatOpenAI would go here in production
765
+
766
+ # Initialize mock data
767
+ self.mock_data = MockDataGenerator()
768
+
769
+ # Initialize specialized agents
770
+ self.health_monitor = HealthMonitorAgent(self.llm)
771
+ self.medication_agent = MedicationAgent(self.llm)
772
+ self.family_communicator = FamilyCommunicationAgent(self.llm)
773
+ self.action_planner = ActionPlannerAgent(self.llm)
774
+
775
+ # Build the workflow graph
776
+ self.graph = self._build_workflow()
777
+
778
+ def _build_workflow(self) -> StateGraph:
779
+ """Build the LangGraph workflow for daily care monitoring"""
780
+ workflow = StateGraph(CareState)
781
+
782
+ # Add nodes for each step
783
+ workflow.add_node("health_analysis", self._health_analysis_node)
784
+ workflow.add_node("medication_check", self._medication_check_node)
785
+ workflow.add_node("generate_summary", self._generate_summary_node)
786
+ workflow.add_node("send_family_notifications", self._family_notifications_node)
787
+ workflow.add_node("action_planning", self._action_planning_node)
788
+ workflow.add_node("emergency_check", self._emergency_check_node)
789
+
790
+ # Define the workflow
791
+ workflow.set_entry_point("health_analysis")
792
+ workflow.add_edge("health_analysis", "medication_check")
793
+ workflow.add_edge("medication_check", "emergency_check")
794
+ workflow.add_conditional_edges(
795
+ "emergency_check",
796
+ self._should_trigger_emergency,
797
+ {
798
+ "emergency": "send_family_notifications",
799
+ "continue": "generate_summary"
800
+ }
801
+ )
802
+ workflow.add_edge("generate_summary", "send_family_notifications")
803
+ workflow.add_edge("send_family_notifications", "action_planning")
804
+ workflow.add_edge("action_planning", END)
805
+
806
+ return workflow.compile()
807
+
808
+ def _health_analysis_node(self, state: CareState) -> CareState:
809
+ return self.health_monitor.analyze_health_patterns(state)
810
+
811
+ def _medication_check_node(self, state: CareState) -> CareState:
812
+ return self.medication_agent.check_medication_compliance(state)
813
+
814
+ def _generate_summary_node(self, state: CareState) -> CareState:
815
+ return self.family_communicator.create_daily_summary(state)
816
+
817
+ def _family_notifications_node(self, state: CareState) -> CareState:
818
+ return self.family_communicator.generate_family_updates(state)
819
+
820
+ def _action_planning_node(self, state: CareState) -> CareState:
821
+ return self.action_planner.generate_action_items(state)
822
+
823
+ def _emergency_check_node(self, state: CareState) -> CareState:
824
+ """Check if emergency response is needed"""
825
+ urgent_alerts = [a for a in state["alerts"] if a["severity"] == AlertLevel.URGENT.value]
826
+ if urgent_alerts:
827
+ state["emergency_level"] = "urgent"
828
+ else:
829
+ state["emergency_level"] = "normal"
830
+ return state
831
+
832
+ def _should_trigger_emergency(self, state: CareState) -> str:
833
+ """Decide whether to trigger emergency protocol"""
834
+ return "emergency" if state["emergency_level"] == "urgent" else "continue"
835
+
836
+ def run_daily_care_cycle(self, parent_id: str) -> Dict[str, Any]:
837
+ """Execute the complete daily care monitoring workflow"""
838
+
839
+ # Validate parent_id exists
840
+ if parent_id not in self.mock_data.parents:
841
+ raise ValueError(f"Parent ID {parent_id} not found")
842
+
843
+ initial_state = {
844
+ "parent_id": parent_id,
845
+ "date": datetime.now().strftime("%Y-%m-%d"),
846
+ "health_metrics": [],
847
+ "medication_status": [],
848
+ "concerns": [],
849
+ "alerts": [],
850
+ "daily_summary": "",
851
+ "action_items": [],
852
+ "family_notifications": [],
853
+ "emergency_level": "normal"
854
+ }
855
+
856
+ # Run the workflow
857
+ result = self.graph.invoke(initial_state)
858
+
859
+ # Add timestamp and processing info
860
+ result["processed_at"] = datetime.now().isoformat()
861
+ result["next_check"] = (datetime.now() + timedelta(hours=24)).isoformat()
862
+
863
+ return result
864
+
865
+ # ============== WEB INTERFACE ==============
866
+
867
+ # Initialize FastAPI app
868
+ app = FastAPI(title="CareLoop - AI-Powered Family Caregiving")
869
+
870
+ # Initialize the care orchestrator
871
+ care_system = CareLoopOrchestrator()
872
+
873
+ # Store for demo data
874
+ demo_reports = []
875
+
876
+ @app.get("/", response_class=HTMLResponse)
877
+ async def get_dashboard():
878
+ """Serve the main dashboard"""
879
+ html_content = """
880
+ <!DOCTYPE html>
881
+ <html lang="en">
882
+ <head>
883
+ <meta charset="UTF-8">
884
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
885
+ <title>CareLoop Dashboard</title>
886
+ <script src="https://cdn.tailwindcss.com"></script>
887
+ <script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
888
+ </head>
889
+ <body class="bg-gray-50">
890
+ <div x-data="careloop()" class="min-h-screen">
891
+ <!-- Header -->
892
+ <header class="bg-blue-600 text-white p-4">
893
+ <div class="max-w-6xl mx-auto flex justify-between items-center">
894
+ <h1 class="text-2xl font-bold">🏠 CareLoop</h1>
895
+ <p class="text-blue-100">AI-Powered Family Caregiving</p>
896
+ </div>
897
+ </header>
898
+
899
+ <!-- Main Content -->
900
+ <main class="max-w-6xl mx-auto p-6">
901
+ <!-- Demo Controls -->
902
+ <div class="bg-white rounded-lg shadow p-6 mb-6">
903
+ <h2 class="text-xl font-semibold mb-4">Hackathon Demo</h2>
904
+ <div class="flex flex-col md:flex-row md:items-center mb-6 space-y-4 md:space-y-0 md:space-x-4">
905
+ <div class="flex-1">
906
+ <label for="parent-select" class="block text-sm font-medium text-gray-700 mb-1">Select Parent:</label>
907
+ <select
908
+ id="parent-select"
909
+ x-model="selectedParent"
910
+ @change="loadParentInfo()"
911
+ class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500">
912
+ <option value="parent_001">Margaret Chen (78) - Diabetes/Hypertension</option>
913
+ <option value="parent_002">Robert Johnson (72) - Stroke Recovery</option>
914
+ <option value="parent_003">Elena Gonzalez (81) - Early Alzheimer's</option>
915
+ </select>
916
+ </div>
917
+ <div class="flex-none">
918
+ <button @click="runCareCheck()"
919
+ class="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 w-full md:w-auto"
920
+ :disabled="loading">
921
+ <span x-show="!loading">πŸ”„ Run Daily Care Check</span>
922
+ <span x-show="loading">πŸ”„ Analyzing...</span>
923
+ </button>
924
+ </div>
925
+ </div>
926
+
927
+ <div x-show="parentInfo" class="bg-blue-50 p-4 rounded-lg text-sm">
928
+ <h3 class="font-medium text-blue-800" x-text="parentInfo.parent.name + ' (' + parentInfo.parent.age + ')'"></h3>
929
+ <div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-2">
930
+ <div>
931
+ <p class="text-blue-600 font-medium">Health Conditions:</p>
932
+ <ul class="list-disc list-inside text-blue-700 pl-2">
933
+ <template x-for="condition in parentInfo.parent.conditions">
934
+ <li x-text="condition"></li>
935
+ </template>
936
+ </ul>
937
+ </div>
938
+ <div>
939
+ <p class="text-blue-600 font-medium">Family Caregivers:</p>
940
+ <ul class="list-disc list-inside text-blue-700 pl-2">
941
+ <template x-for="member in parentInfo.family">
942
+ <li x-text="member.name + ' (' + member.relationship + ')'"></li>
943
+ </template>
944
+ </ul>
945
+ </div>
946
+ </div>
947
+ </div>
948
+ </div>
949
+
950
+ <!-- Results -->
951
+ <div x-show="report" class="space-y-6">
952
+ <!-- Status Overview -->
953
+ <div class="bg-white rounded-lg shadow p-6">
954
+ <h3 class="text-lg font-semibold mb-4">πŸ“Š Care Status Overview</h3>
955
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
956
+ <div class="bg-green-50 p-4 rounded-lg">
957
+ <h4 class="font-medium text-green-800">Health Metrics</h4>
958
+ <p class="text-green-600" x-text="report?.health_metrics?.length + ' data points collected'"></p>
959
+ </div>
960
+ <div class="bg-blue-50 p-4 rounded-lg">
961
+ <h4 class="font-medium text-blue-800">Medication Status</h4>
962
+ <p class="text-blue-600" x-text="report?.medication_status?.length + ' medication events'"></p>
963
+ </div>
964
+ <div class="bg-orange-50 p-4 rounded-lg">
965
+ <h4 class="font-medium text-orange-800">Alerts Generated</h4>
966
+ <p class="text-orange-600" x-text="report?.alerts?.length + ' alerts for family'"></p>
967
+ </div>
968
+ </div>
969
+ </div>
970
+
971
+ <!-- Daily Summary -->
972
+ <div class="bg-white rounded-lg shadow p-6">
973
+ <h3 class="text-lg font-semibold mb-4">πŸ“‹ Daily Summary</h3>
974
+ <div class="prose max-w-none">
975
+ <pre x-text="report?.daily_summary" class="whitespace-pre-wrap font-sans text-sm bg-gray-50 p-4 rounded"></pre>
976
+ </div>
977
+ </div>
978
+
979
+ <!-- Action Items -->
980
+ <div class="bg-white rounded-lg shadow p-6">
981
+ <h3 class="text-lg font-semibold mb-4">βœ… Recommended Actions</h3>
982
+ <ul class="space-y-2">
983
+ <template x-for="action in report?.action_items">
984
+ <li class="flex items-start space-x-2">
985
+ <span class="text-blue-600">β€’</span>
986
+ <span x-text="action"></span>
987
+ </li>
988
+ </template>
989
+ </ul>
990
+ </div>
991
+
992
+ <!-- Family Notifications -->
993
+ <div class="bg-white rounded-lg shadow p-6">
994
+ <h3 class="text-lg font-semibold mb-4">πŸ“± Family Notifications</h3>
995
+ <div class="space-y-4">
996
+ <template x-for="notification in report?.family_notifications">
997
+ <div class="border-l-4 border-blue-500 pl-4 py-2">
998
+ <h4 class="font-medium" x-text="'To: ' + notification.recipient + ' (' + notification.channels.join(', ') + ')'"></h4>
999
+ <pre x-text="notification.message" class="whitespace-pre-wrap text-sm text-gray-600 mt-2"></pre>
1000
+ </div>
1001
+ </template>
1002
+ </div>
1003
+ </div>
1004
+
1005
+ <!-- Alerts Detail -->
1006
+ <div x-show="report?.alerts?.length > 0" class="bg-white rounded-lg shadow p-6">
1007
+ <h3 class="text-lg font-semibold mb-4">⚠️ Care Alerts</h3>
1008
+ <div class="space-y-3">
1009
+ <template x-for="alert in report?.alerts">
1010
+ <div class="border-l-4 pl-4 py-2"
1011
+ :class="{
1012
+ 'border-red-500 bg-red-50': alert.severity === 'urgent',
1013
+ 'border-orange-500 bg-orange-50': alert.severity === 'high',
1014
+ 'border-yellow-500 bg-yellow-50': alert.severity === 'medium',
1015
+ 'border-blue-500 bg-blue-50': alert.severity === 'low'
1016
+ }">
1017
+ <div class="flex justify-between items-start">
1018
+ <div>
1019
+ <h4 class="font-medium" x-text="alert.message"></h4>
1020
+ <p class="text-sm text-gray-600" x-text="'Type: ' + alert.type + ' | Severity: ' + alert.severity"></p>
1021
+ <p class="text-sm mt-1" x-text="'Recommended: ' + alert.recommended_action"></p>
1022
+ </div>
1023
+ </div>
1024
+ </div>
1025
+ </template>
1026
+ </div>
1027
+ </div>
1028
+ </div>
1029
+ </main>
1030
+ </div>
1031
+
1032
+ <script>
1033
+ function careloop() {
1034
+ return {
1035
+ loading: false,
1036
+ report: null,
1037
+ parentInfo: null,
1038
+ selectedParent: "parent_001",
1039
+
1040
+ init() {
1041
+ this.loadParentInfo();
1042
+ },
1043
+
1044
+ async loadParentInfo() {
1045
+ try {
1046
+ const response = await fetch(`/api/parent/${this.selectedParent}`);
1047
+ this.parentInfo = await response.json();
1048
+ // Clear previous report when changing parents
1049
+ this.report = null;
1050
+ } catch (error) {
1051
+ console.error('Error loading parent info:', error);
1052
+ }
1053
+ },
1054
+
1055
+ async runCareCheck() {
1056
+ this.loading = true;
1057
+ try {
1058
+ const response = await fetch(`/api/care-check/${this.selectedParent}`, {
1059
+ method: 'POST'
1060
+ });
1061
+ this.report = await response.json();
1062
+ } catch (error) {
1063
+ console.error('Error running care check:', error);
1064
+ } finally {
1065
+ this.loading = false;
1066
+ }
1067
+ }
1068
+ }
1069
+ }
1070
+ </script>
1071
+ </body>
1072
+ </html>
1073
+ """
1074
+ return HTMLResponse(content=html_content)
1075
+
1076
+ @app.post("/api/care-check/{parent_id}")
1077
+ async def run_care_check(parent_id: str):
1078
+ """Run the daily care check for a parent"""
1079
+ try:
1080
+ result = care_system.run_daily_care_cycle(parent_id)
1081
+ demo_reports.append(result)
1082
+ return result
1083
+ except Exception as e:
1084
+ return {"error": str(e)}
1085
+
1086
+ @app.get("/api/reports")
1087
+ async def get_reports():
1088
+ """Get all demo reports"""
1089
+ return {"reports": demo_reports}
1090
+
1091
+ @app.get("/api/parent/{parent_id}")
1092
+ async def get_parent_info(parent_id: str):
1093
+ """Get parent information"""
1094
+ mock_data = MockDataGenerator()
1095
+ return {
1096
+ "parent": asdict(mock_data.get_parent_by_id(parent_id)),
1097
+ "family": [asdict(fm) for fm in mock_data.get_family_by_parent_id(parent_id)]
1098
+ }
1099
+
1100
+ # ============== MAIN EXECUTION ==============
1101
+
1102
+ if __name__ == "__main__":
1103
+ print("πŸš€ Starting CareLoop Hackathon Demo")
1104
+ print("=" * 50)
1105
+
1106
+ # Load mock data
1107
+ mock_data = MockDataGenerator()
1108
+
1109
+ # Display available parents
1110
+ print("🏠 Available Parent Profiles:")
1111
+ for parent_id, parent in mock_data.parents.items():
1112
+ family_count = len(mock_data.families.get(parent_id, []))
1113
+ conditions = ", ".join(parent.conditions[:2]) + ("..." if len(parent.conditions) > 2 else "")
1114
+ print(f" β€’ {parent.name} ({parent.age}) - ID: {parent_id}")
1115
+ print(f" Health: {conditions}")
1116
+ print(f" Family caregivers: {family_count}")
1117
+
1118
+ # Run a sample care check for demo
1119
+ print("\nπŸ”„ Running sample care analysis for Margaret Chen...")
1120
+ result = care_system.run_daily_care_cycle("parent_001")
1121
+
1122
+ print(f"\nπŸ“‹ Daily Summary:")
1123
+ print(result["daily_summary"])
1124
+
1125
+ print(f"\nβœ… Action Items ({len(result['action_items'])}):")
1126
+ for action in result["action_items"]:
1127
+ print(f" β€’ {action}")
1128
+
1129
+ print(f"\nπŸ“± Family Notifications ({len(result['family_notifications'])}):")
1130
+ for notification in result["family_notifications"]:
1131
+ print(f" β†’ {notification['recipient']}: {notification['urgency']} priority")
1132
+
1133
+ print(f"\n⚠️ Alerts Generated ({len(result['alerts'])}):")
1134
+ for alert in result["alerts"]:
1135
+ print(f" β€’ {alert['severity'].upper()}: {alert['message']}")
1136
+
1137
+ print("\n" + "=" * 50)
1138
+ print("🌐 Starting web interface...")
1139
+ print("Open your browser to: http://localhost:8000")
1140
+ print("Select a parent and click 'Run Daily Care Check' to see the AI agents in action!")
1141
+
1142
+ # Start the web server
1143
+ uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info")
config.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # config.py
2
+ # CareLoop Configuration Management
3
+
4
+ import os
5
+ from dataclasses import dataclass
6
+ from typing import Dict, List, Optional
7
+
8
+ @dataclass
9
+ class DatabaseConfig:
10
+ """Database configuration settings"""
11
+ url: str = "sqlite:///careloop_demo.db"
12
+ echo: bool = False
13
+ pool_size: int = 10
14
+ max_overflow: int = 20
15
+
16
+ @dataclass
17
+ class AIConfig:
18
+ """AI and LLM configuration"""
19
+ openai_api_key: str = "demo-key-not-required"
20
+ model_name: str = "gpt-4"
21
+ temperature: float = 0.1
22
+ max_tokens: int = 2000
23
+ # For hackathon demo, we mock AI responses
24
+ use_mock_responses: bool = True
25
+
26
+ @dataclass
27
+ class NotificationConfig:
28
+ """Notification service configuration"""
29
+ twilio_account_sid: str = "demo-account"
30
+ twilio_auth_token: str = "demo-token"
31
+ twilio_phone_number: str = "+1-555-CARELOOP"
32
+ email_service: str = "sendgrid"
33
+ email_api_key: str = "demo-email-key"
34
+
35
+ @dataclass
36
+ class HealthIntegrationConfig:
37
+ """Health device and service integrations"""
38
+ apple_health_enabled: bool = True
39
+ fitbit_enabled: bool = True
40
+ omron_enabled: bool = True # Blood pressure monitors
41
+ dexcom_enabled: bool = False # Continuous glucose monitoring
42
+ epic_fhir_enabled: bool = False # Electronic health records
43
+
44
+ @dataclass
45
+ class SecurityConfig:
46
+ """Security and privacy settings"""
47
+ encryption_key: str = "demo-encryption-key-32-chars-long"
48
+ jwt_secret: str = "demo-jwt-secret-key"
49
+ session_timeout_hours: int = 24
50
+ max_login_attempts: int = 5
51
+ require_2fa: bool = False # For demo
52
+
53
+ @dataclass
54
+ class CareLoopConfig:
55
+ """Main application configuration"""
56
+ # Subsystem configs - non-default parameters must come first
57
+ database: DatabaseConfig
58
+ ai: AIConfig
59
+ notifications: NotificationConfig
60
+ health_integrations: HealthIntegrationConfig
61
+ security: SecurityConfig
62
+
63
+ # Environment
64
+ environment: str = "development"
65
+ debug: bool = True
66
+
67
+ # Web server
68
+ host: str = "0.0.0.0"
69
+ port: int = 8000
70
+ workers: int = 1
71
+
72
+ # Feature flags
73
+ enable_web_interface: bool = True
74
+ enable_api: bool = True
75
+ enable_websockets: bool = True
76
+ enable_background_tasks: bool = True
77
+
78
+ # Monitoring intervals
79
+ health_check_interval_hours: int = 4
80
+ medication_check_interval_hours: int = 2
81
+ emergency_check_interval_minutes: int = 15
82
+
83
+ def __init__(self):
84
+ self.database = DatabaseConfig()
85
+ self.ai = AIConfig()
86
+ self.notifications = NotificationConfig()
87
+ self.health_integrations = HealthIntegrationConfig()
88
+ self.security = SecurityConfig()
89
+
90
+ def load_config() -> CareLoopConfig:
91
+ """Load configuration from environment variables"""
92
+ config = CareLoopConfig()
93
+
94
+ # Override with environment variables if present
95
+ config.environment = os.getenv("CARELOOP_ENV", "development")
96
+ config.debug = os.getenv("CARELOOP_DEBUG", "true").lower() == "true"
97
+ config.host = os.getenv("CARELOOP_HOST", "0.0.0.0")
98
+ config.port = int(os.getenv("CARELOOP_PORT", "8000"))
99
+
100
+ # Database
101
+ config.database.url = os.getenv("DATABASE_URL", "sqlite:///careloop_demo.db")
102
+
103
+ # AI Configuration
104
+ config.ai.openai_api_key = os.getenv("OPENAI_API_KEY", "demo-key")
105
+ config.ai.use_mock_responses = os.getenv("USE_MOCK_AI", "true").lower() == "true"
106
+
107
+ # Notifications
108
+ config.notifications.twilio_account_sid = os.getenv("TWILIO_ACCOUNT_SID", "demo")
109
+ config.notifications.twilio_auth_token = os.getenv("TWILIO_AUTH_TOKEN", "demo")
110
+ config.notifications.email_api_key = os.getenv("EMAIL_API_KEY", "demo")
111
+
112
+ # Security
113
+ config.security.encryption_key = os.getenv("ENCRYPTION_KEY", "demo-key-32-chars-very-secure!!")
114
+ config.security.jwt_secret = os.getenv("JWT_SECRET", "demo-jwt-secret")
115
+
116
+ return config
117
+
118
+ # Global config instance
119
+ CONFIG = load_config()
120
+
121
+ # Environment-specific configurations
122
+ ENVIRONMENT_CONFIGS = {
123
+ "development": {
124
+ "debug": True,
125
+ "log_level": "DEBUG",
126
+ "database_echo": True,
127
+ "use_mock_integrations": True,
128
+ "enable_hot_reload": True
129
+ },
130
+ "staging": {
131
+ "debug": False,
132
+ "log_level": "INFO",
133
+ "database_echo": False,
134
+ "use_mock_integrations": True,
135
+ "enable_hot_reload": False
136
+ },
137
+ "production": {
138
+ "debug": False,
139
+ "log_level": "WARNING",
140
+ "database_echo": False,
141
+ "use_mock_integrations": False,
142
+ "enable_hot_reload": False,
143
+ "require_https": True,
144
+ "enable_rate_limiting": True
145
+ }
146
+ }
147
+
148
+ def get_environment_config(env: str) -> Dict:
149
+ """Get configuration for specific environment"""
150
+ return ENVIRONMENT_CONFIGS.get(env, ENVIRONMENT_CONFIGS["development"])
151
+
152
+ if __name__ == "__main__":
153
+ print("CareLoop Configuration")
154
+ print("=" * 30)
155
+ print(f"Environment: {CONFIG.environment}")
156
+ print(f"Debug mode: {CONFIG.debug}")
157
+ print(f"Database: {CONFIG.database.url}")
158
+ print(f"AI Mock mode: {CONFIG.ai.use_mock_responses}")
159
+ print(f"Web interface: {CONFIG.enable_web_interface}")
160
+ print(f"Host: {CONFIG.host}:{CONFIG.port}")
demo_scenarios.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CareLoop Demo Script - Run Different Scenarios
2
+ # This script demonstrates various AI agent capabilities
3
+
4
+ import sys
5
+ import os
6
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
7
+
8
+ from careloop_main import CareLoopOrchestrator, MockDataGenerator, AlertLevel
9
+ from datetime import datetime, timedelta
10
+ import random
11
+
12
+ class CareLoopDemo:
13
+ def __init__(self):
14
+ self.care_system = CareLoopOrchestrator()
15
+ self.mock_data = MockDataGenerator()
16
+
17
+ def print_banner(self, title: str):
18
+ """Print a nice banner for demo sections"""
19
+ print("\n" + "="*60)
20
+ print(f"🎯 {title}")
21
+ print("="*60)
22
+
23
+ def print_section(self, title: str):
24
+ """Print a section header"""
25
+ print(f"\nπŸ“‹ {title}")
26
+ print("-" * 40)
27
+
28
+ def run_normal_day_scenario(self):
29
+ """Scenario 1: Normal day with good health metrics"""
30
+ self.print_banner("SCENARIO 1: Normal Day - Everything Going Well")
31
+
32
+ print("Margaret had a good day:")
33
+ print("β€’ Took all medications on time")
34
+ print("β€’ Blood glucose levels stable (110-125 mg/dL)")
35
+ print("β€’ Good activity level (3,200 steps)")
36
+ print("β€’ Slept well (7.5 hours)")
37
+ print("β€’ Blood pressure normal (128/78)")
38
+
39
+ # Run the care check
40
+ result = self.care_system.run_daily_care_cycle("parent_001")
41
+
42
+ self.print_section("AI Agent Analysis")
43
+ print(f"Concerns detected: {len(result['concerns'])}")
44
+ print(f"Alerts generated: {len(result['alerts'])}")
45
+ print(f"Action items: {len(result['action_items'])}")
46
+
47
+ if result['concerns']:
48
+ print("\nConcerns:")
49
+ for concern in result['concerns']:
50
+ print(f" β€’ {concern}")
51
+ else:
52
+ print("βœ… No concerns detected - everything looks good!")
53
+
54
+ self.print_section("Family Notifications")
55
+ for notification in result['family_notifications']:
56
+ print(f"β†’ {notification['recipient']} ({notification['urgency']} priority)")
57
+ print(f" Channels: {', '.join(notification['channels'])}")
58
+
59
+ return result
60
+
61
+ def run_medication_issues_scenario(self):
62
+ """Scenario 2: Medication compliance problems"""
63
+ self.print_banner("SCENARIO 2: Medication Compliance Issues")
64
+
65
+ print("Margaret had medication troubles:")
66
+ print("β€’ Missed evening Metformin dose yesterday")
67
+ print("β€’ Forgot Lisinopril this morning")
68
+ print("β€’ Late with Donepezil twice this week")
69
+ print("β€’ Blood glucose elevated due to missed Metformin")
70
+
71
+ result = self.care_system.run_daily_care_cycle("parent_001")
72
+
73
+ self.print_section("AI Agent Response")
74
+ high_alerts = [a for a in result['alerts'] if a['severity'] == 'high']
75
+ print(f"🚨 High priority alerts: {len(high_alerts)}")
76
+ print(f"πŸ“‹ Action items generated: {len(result['action_items'])}")
77
+
78
+ for alert in high_alerts:
79
+ print(f"\nπŸ”΄ {alert['severity'].upper()}: {alert['message']}")
80
+ print(f" Recommended: {alert['recommended_action']}")
81
+
82
+ return result
83
+
84
+ def run_family_coordination_scenario(self):
85
+ """Scenario 3: Complex family coordination"""
86
+ self.print_banner("SCENARIO 3: Multi-Family Member Coordination")
87
+
88
+ print("Family dynamics in action:")
89
+ print("β€’ David (son): Primary caregiver, wants all details")
90
+ print("β€’ Lisa (daughter): Backup caregiver, weekly summaries only")
91
+ print("β€’ Jennifer (daughter-in-law): Support role, emergencies only")
92
+ print("β€’ Different notification preferences and access levels")
93
+
94
+ result = self.care_system.run_daily_care_cycle("parent_001")
95
+
96
+ self.print_section("Personalized Communication Strategy")
97
+
98
+ for notification in result['family_notifications']:
99
+ print(f"\nπŸ‘€ {notification['recipient']}")
100
+ print(f" Role: {notification.get('recipient_id', 'unknown')}")
101
+ print(f" Channels: {', '.join(notification['channels'])}")
102
+ print(f" Priority: {notification['urgency']}")
103
+ print(f" Send immediately: {notification.get('send_immediately', False)}")
104
+
105
+ # Show first few lines of message
106
+ message_lines = notification['message'].split('\n')[:3]
107
+ for line in message_lines:
108
+ if line.strip():
109
+ print(f" Preview: {line.strip()}")
110
+ break
111
+
112
+ return result
113
+
114
+ def show_daily_summary(self, result):
115
+ """Display the complete daily summary"""
116
+ self.print_section("Complete Daily Summary")
117
+ print(result['daily_summary'])
118
+
119
+ self.print_section("Action Items")
120
+ for i, action in enumerate(result['action_items'], 1):
121
+ print(f"{i}. {action}")
122
+
123
+ def run_interactive_demo(self):
124
+ """Run an interactive demo session"""
125
+ self.print_banner("CARELOOP INTERACTIVE DEMO")
126
+
127
+ print("Welcome to the CareLoop AI Caregiving Platform Demo!")
128
+ print("This demo shows how multiple AI agents work together to")
129
+ print("monitor Margaret Chen (78) and coordinate her family care.")
130
+ print("\nMargaret's Profile:")
131
+ print("β€’ Age: 78")
132
+ print("β€’ Conditions: Type 2 Diabetes, Hypertension, Mild Cognitive Impairment")
133
+ print("β€’ Medications: Metformin, Lisinopril, Donepezil")
134
+ print("β€’ Family: David (son/primary), Lisa (daughter), Jennifer (daughter-in-law)")
135
+
136
+ while True:
137
+ print(f"\n{'='*50}")
138
+ print("Choose a scenario to demonstrate:")
139
+ print("1. Normal Day - Everything Going Well")
140
+ print("2. Medication Compliance Issues")
141
+ print("3. Multi-Family Coordination")
142
+ print("4. Show Complete Daily Summary")
143
+ print("5. Exit demo")
144
+
145
+ try:
146
+ choice = input("\nEnter choice (1-5): ").strip()
147
+
148
+ if choice == "1":
149
+ result = self.run_normal_day_scenario()
150
+ input("\nPress Enter to continue...")
151
+
152
+ elif choice == "2":
153
+ result = self.run_medication_issues_scenario()
154
+ input("\nPress Enter to continue...")
155
+
156
+ elif choice == "3":
157
+ result = self.run_family_coordination_scenario()
158
+ input("\nPress Enter to continue...")
159
+
160
+ elif choice == "4":
161
+ print("Running care analysis to generate summary...")
162
+ result = self.care_system.run_daily_care_cycle("parent_001")
163
+ self.show_daily_summary(result)
164
+ input("\nPress Enter to continue...")
165
+
166
+ elif choice == "5":
167
+ break
168
+
169
+ else:
170
+ print("❌ Invalid choice. Please enter 1-5.")
171
+
172
+ except KeyboardInterrupt:
173
+ print("\nπŸ‘‹ Demo interrupted. Thanks for trying CareLoop!")
174
+ break
175
+ except Exception as e:
176
+ print(f"❌ Error: {e}")
177
+
178
+ self.print_banner("DEMO COMPLETE")
179
+ print("🎯 Key Takeaways:")
180
+ print("β€’ AI agents work 24/7 to monitor health patterns")
181
+ print("β€’ Automated family communication reduces caregiver burden")
182
+ print("β€’ Predictive analytics prevent emergencies before they happen")
183
+ print("β€’ Personalized care coordination scales to any family size")
184
+ print("\nπŸ’‘ CareLoop: Making family caregiving smarter, not harder.")
185
+
186
+ def run_quick_demo():
187
+ """Run a quick non-interactive demo for presentations"""
188
+ demo = CareLoopDemo()
189
+
190
+ print("πŸš€ CARELOOP QUICK DEMO")
191
+ print("=" * 40)
192
+ print("Running AI-powered care analysis for Margaret Chen...")
193
+
194
+ # Quick run through key scenarios
195
+ result1 = demo.run_normal_day_scenario()
196
+ print("\nPress Enter for next scenario...")
197
+ input()
198
+
199
+ result2 = demo.run_medication_issues_scenario()
200
+ print("\nPress Enter for next scenario...")
201
+ input()
202
+
203
+ result3 = demo.run_family_coordination_scenario()
204
+
205
+ print("\n🎯 DEMO SUMMARY:")
206
+ print("βœ… Normal day: Routine monitoring and family updates")
207
+ print("⚠️ Medication issues: Proactive alerts and action items")
208
+ print("πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ Family coordination: Personalized communication for each member")
209
+ print("\nπŸ’‘ Ready for production deployment!")
210
+
211
+ if __name__ == "__main__":
212
+ if len(sys.argv) > 1 and sys.argv[1] == "--quick":
213
+ run_quick_demo()
214
+ else:
215
+ demo = CareLoopDemo()
216
+ demo.run_interactive_demo()
gradio_app.py ADDED
@@ -0,0 +1,622 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # gradio_app.py - Gradio interface for CareLoop
3
+
4
+ import os
5
+ import json
6
+ import gradio as gr
7
+ from datetime import datetime, timedelta
8
+ from dataclasses import asdict
9
+ from typing import Dict, List, Any, Optional
10
+ import random
11
+ import pandas as pd
12
+ import numpy as np
13
+ import matplotlib.pyplot as plt
14
+
15
+ from openai import OpenAI
16
+ from langchain_core.messages import HumanMessage, AIMessage
17
+
18
+ # Import CareLoop components
19
+ from careloop_main import (
20
+ MockDataGenerator,
21
+ CareState,
22
+ HealthMonitorAgent,
23
+ MedicationAgent,
24
+ FamilyCommunicationAgent,
25
+ ActionPlannerAgent,
26
+ AlertLevel
27
+ )
28
+
29
+ # Initialize the OpenAI client for Nebius API
30
+ client = OpenAI(
31
+ base_url="https://api.studio.nebius.com/v1/",
32
+ api_key=os.environ.get("NEBIUS_API_KEY", "demo-key-for-hackathon")
33
+ )
34
+
35
+ class NebiusLLM:
36
+ """LLM wrapper for Nebius API"""
37
+
38
+ def __init__(self, model_name="meta-llama/Meta-Llama-3.1-70B-Instruct"):
39
+ self.model_name = model_name
40
+ self.client = client
41
+
42
+ def generate(self, prompt: str) -> str:
43
+ """Generate text using Nebius API"""
44
+ try:
45
+ response = self.client.chat.completions.create(
46
+ model=self.model_name,
47
+ max_tokens=512,
48
+ temperature=0.6,
49
+ top_p=0.9,
50
+ extra_body={
51
+ "top_k": 50
52
+ },
53
+ messages=[{"role": "user", "content": prompt}]
54
+ )
55
+ return response.choices[0].message.content
56
+ except Exception as e:
57
+ print(f"Error calling Nebius API: {e}")
58
+ # Fall back to mock responses in case of API error
59
+ return self._generate_mock_response(prompt)
60
+
61
+ def _generate_mock_response(self, prompt: str) -> str:
62
+ """Generate a mock response for demo purposes"""
63
+ if "health" in prompt.lower():
64
+ return "The patient's health metrics show stable vital signs with glucose levels within acceptable range."
65
+ elif "medication" in prompt.lower():
66
+ return "Medication compliance has been good this week with only one missed dose of evening medication."
67
+ elif "summary" in prompt.lower():
68
+ return "Overall, the patient is doing well with stable health metrics and good medication compliance."
69
+ else:
70
+ return "The care system is monitoring the patient's condition and no significant issues have been detected."
71
+
72
+ class CareLoopSystem:
73
+ """CareLoop system with Nebius LLM integration"""
74
+
75
+ def __init__(self):
76
+ self.mock_data = MockDataGenerator()
77
+ self.llm = NebiusLLM()
78
+
79
+ # Initialize specialized agents
80
+ self.health_monitor = HealthMonitorAgent(self.llm)
81
+ self.medication_agent = MedicationAgent(self.llm)
82
+ self.family_communicator = FamilyCommunicationAgent(self.llm)
83
+ self.action_planner = ActionPlannerAgent(self.llm)
84
+
85
+ # Store chat history for each parent
86
+ self.chat_history = {}
87
+
88
+ # Generate historical data for timelines
89
+ self.historical_data = self._generate_historical_data()
90
+
91
+ # Generate medication compliance data
92
+ self.medication_compliance = self._generate_medication_compliance()
93
+
94
+ def _generate_historical_data(self) -> Dict[str, Dict[str, List]]:
95
+ """Generate 7 days of historical health data for each parent"""
96
+ data = {}
97
+ today = datetime.now()
98
+
99
+ for parent_id, parent in self.mock_data.parents.items():
100
+ parent_data = {
101
+ "dates": [],
102
+ "blood_pressure_systolic": [],
103
+ "blood_pressure_diastolic": [],
104
+ "heart_rate": [],
105
+ "blood_glucose": [],
106
+ "weight": [],
107
+ "temperature": [],
108
+ "sleep_hours": []
109
+ }
110
+
111
+ # Set default weight based on parent
112
+ if parent.name == "Margaret Chen":
113
+ base_weight = 145.0 # lbs
114
+ elif parent.name == "Robert Johnson":
115
+ base_weight = 175.0 # lbs
116
+ elif parent.name == "Elena Gonzalez":
117
+ base_weight = 130.0 # lbs
118
+ else:
119
+ base_weight = 150.0 # default weight
120
+
121
+ # Generate data for the last 7 days
122
+ for i in range(7, 0, -1):
123
+ date = today - timedelta(days=i)
124
+ parent_data["dates"].append(date.strftime("%Y-%m-%d"))
125
+
126
+ # Generate values based on parent's conditions
127
+ if "Diabetes" in parent.conditions:
128
+ glucose_base = 130
129
+ glucose_var = 30
130
+ else:
131
+ glucose_base = 95
132
+ glucose_var = 15
133
+
134
+ if "Hypertension" in parent.conditions:
135
+ bp_sys_base = 145
136
+ bp_sys_var = 15
137
+ bp_dia_base = 90
138
+ bp_dia_var = 10
139
+ else:
140
+ bp_sys_base = 125
141
+ bp_sys_var = 10
142
+ bp_dia_base = 80
143
+ bp_dia_var = 5
144
+
145
+ # Add some random variation to simulate real-world data
146
+ parent_data["blood_pressure_systolic"].append(bp_sys_base + random.randint(-bp_sys_var, bp_sys_var))
147
+ parent_data["blood_pressure_diastolic"].append(bp_dia_base + random.randint(-bp_dia_var, bp_dia_var))
148
+ parent_data["heart_rate"].append(75 + random.randint(-10, 10))
149
+ parent_data["blood_glucose"].append(glucose_base + random.randint(-glucose_var, glucose_var))
150
+ parent_data["weight"].append(base_weight + random.uniform(-0.5, 0.5))
151
+ parent_data["temperature"].append(round(98.2 + random.uniform(-0.5, 0.8), 1))
152
+ parent_data["sleep_hours"].append(round(6.5 + random.uniform(-1.5, 1.5), 1))
153
+
154
+ data[parent_id] = parent_data
155
+
156
+ return data
157
+
158
+ def _generate_medication_compliance(self) -> Dict[str, Dict[str, List]]:
159
+ """Generate medication compliance data for each parent"""
160
+ compliance = {}
161
+
162
+ for parent_id, parent in self.mock_data.parents.items():
163
+ parent_compliance = {}
164
+
165
+ for med in parent.medications:
166
+ med_name = med["name"]
167
+ med_compliance = []
168
+
169
+ # Generate 7 days of compliance data
170
+ for _ in range(7):
171
+ # 85% chance of taking medication
172
+ took_med = random.random() < 0.85
173
+ med_compliance.append(took_med)
174
+
175
+ parent_compliance[med_name] = med_compliance
176
+
177
+ compliance[parent_id] = parent_compliance
178
+
179
+ return compliance
180
+
181
+ def run_care_check(self, parent_id: str) -> Dict[str, Any]:
182
+ """Run a full care check for a specific parent"""
183
+ # Validate parent_id exists
184
+ if parent_id not in self.mock_data.parents:
185
+ raise ValueError(f"Parent ID {parent_id} not found")
186
+
187
+ # Initialize state
188
+ state = CareState(
189
+ parent_id=parent_id,
190
+ date=datetime.now().strftime("%Y-%m-%d"),
191
+ health_metrics=[],
192
+ medication_status=[],
193
+ concerns=[],
194
+ alerts=[],
195
+ daily_summary="",
196
+ action_items=[],
197
+ family_notifications=[],
198
+ emergency_level="normal"
199
+ )
200
+
201
+ # Run the analysis pipeline manually since we're not using langgraph here
202
+ state = self.health_monitor.analyze_health_patterns(state)
203
+ state = self.medication_agent.check_medication_compliance(state)
204
+
205
+ # Check for emergency
206
+ urgent_alerts = [a for a in state["alerts"] if a["severity"] == AlertLevel.URGENT.value]
207
+ state["emergency_level"] = "urgent" if urgent_alerts else "normal"
208
+
209
+ # Generate daily summary
210
+ state = self.family_communicator.create_daily_summary(state)
211
+
212
+ # Generate family notifications
213
+ state = self.family_communicator.generate_family_updates(state)
214
+
215
+ # Generate action items
216
+ state = self.action_planner.generate_action_items(state)
217
+
218
+ # Add timestamp
219
+ result = dict(state)
220
+ result["processed_at"] = datetime.now().isoformat()
221
+ result["next_check"] = (datetime.now() + timedelta(hours=24)).isoformat()
222
+
223
+ return result
224
+
225
+ def get_parent_info(self, parent_id: str) -> Dict[str, Any]:
226
+ """Get information about a specific parent"""
227
+ parent = self.mock_data.get_parent_by_id(parent_id)
228
+ family = self.mock_data.get_family_by_parent_id(parent_id)
229
+
230
+ if not parent:
231
+ return {"error": f"Parent ID {parent_id} not found"}
232
+
233
+ return {
234
+ "parent": asdict(parent),
235
+ "family": [asdict(fm) for fm in family]
236
+ }
237
+
238
+ def get_health_timeline(self, parent_id: str) -> Dict[str, Any]:
239
+ """Get historical health data for a specific parent"""
240
+ if parent_id not in self.historical_data:
241
+ return {"error": f"No historical data for parent ID {parent_id}"}
242
+
243
+ return self.historical_data[parent_id]
244
+
245
+ def get_medication_compliance(self, parent_id: str) -> Dict[str, Any]:
246
+ """Get medication compliance data for a specific parent"""
247
+ if parent_id not in self.medication_compliance:
248
+ return {"error": f"No medication data for parent ID {parent_id}"}
249
+
250
+ return self.medication_compliance[parent_id]
251
+
252
+ def chat_with_system(self, parent_id: str, message: str) -> str:
253
+ """Chat with the CareLoop system about a specific parent"""
254
+ if parent_id not in self.chat_history:
255
+ self.chat_history[parent_id] = []
256
+
257
+ self.chat_history[parent_id].append({"role": "user", "content": message})
258
+
259
+ # Generate context about the parent
260
+ parent = self.mock_data.get_parent_by_id(parent_id)
261
+ if not parent:
262
+ response = "I couldn't find information about this parent."
263
+ self.chat_history[parent_id].append({"role": "assistant", "content": response})
264
+ return response
265
+
266
+ # Create a prompt for the LLM
267
+ prompt = f"""
268
+ You are CareLoop, an AI assistant for family caregivers.
269
+
270
+ Parent information:
271
+ - Name: {parent.name}
272
+ - Age: {parent.age}
273
+ - Health conditions: {', '.join(parent.conditions)}
274
+ - Medications: {', '.join(med['name'] for med in parent.medications)}
275
+
276
+ The caregiver has asked: {message}
277
+
278
+ Provide a helpful, compassionate response focused on elderly care.
279
+ """
280
+
281
+ # Get response from LLM
282
+ response = self.llm.generate(prompt)
283
+ self.chat_history[parent_id].append({"role": "assistant", "content": response})
284
+
285
+ return response
286
+
287
+ def get_chat_history(self, parent_id: str) -> List[Dict[str, str]]:
288
+ """Get chat history for a specific parent"""
289
+ return self.chat_history.get(parent_id, [])
290
+
291
+ # Initialize the CareLoop system
292
+ care_system = CareLoopSystem()
293
+
294
+ def format_markdown_report(report: Dict[str, Any]) -> str:
295
+ """Format the care report as markdown for Gradio display"""
296
+ if not report:
297
+ return ""
298
+
299
+ md = []
300
+ md.append(f"# {report['daily_summary']}")
301
+
302
+ md.append("\n## Action Items")
303
+ for item in report["action_items"]:
304
+ md.append(f"- {item}")
305
+
306
+ md.append("\n## Family Notifications")
307
+ for notif in report["family_notifications"]:
308
+ md.append(f"**To: {notif['recipient']}** ({notif['urgency']} priority)")
309
+ md.append(f"```\n{notif['message']}\n```")
310
+
311
+ if report["alerts"]:
312
+ md.append("\n## Alerts")
313
+ for alert in report["alerts"]:
314
+ md.append(f"- **{alert['severity'].upper()}**: {alert['message']}")
315
+ md.append(f" - *Recommended Action:* {alert['recommended_action']}")
316
+
317
+ return "\n".join(md)
318
+
319
+ def format_parent_info(parent_info: Dict[str, Any]) -> str:
320
+ """Format parent info as markdown for Gradio display"""
321
+ if not parent_info or "error" in parent_info:
322
+ return "No parent information available"
323
+
324
+ parent = parent_info["parent"]
325
+ family = parent_info["family"]
326
+
327
+ md = []
328
+ md.append(f"# {parent['name']} ({parent['age']})")
329
+
330
+ md.append("\n## Health Conditions")
331
+ for condition in parent["conditions"]:
332
+ md.append(f"- {condition}")
333
+
334
+ md.append("\n## Medications")
335
+ for med in parent["medications"]:
336
+ times = ", ".join(med["times"])
337
+ md.append(f"- {med['name']} ({med['dosage']}) - {med['frequency']} at {times}")
338
+
339
+ md.append("\n## Family Caregivers")
340
+ for member in family:
341
+ md.append(f"- {member['name']} ({member['relationship']}) - {member['role']}")
342
+ md.append(f" - Contact: {member['phone']} | {member['email']}")
343
+
344
+ return "\n".join(md)
345
+
346
+ def run_care_check(parent_id: str) -> tuple:
347
+ """Run care check and return formatted results"""
348
+ try:
349
+ # Get parent info
350
+ parent_info = care_system.get_parent_info(parent_id)
351
+ parent_md = format_parent_info(parent_info)
352
+
353
+ # Run care check
354
+ report = care_system.run_care_check(parent_id)
355
+ report_md = format_markdown_report(report)
356
+
357
+ # Create metrics display
358
+ health_metrics = len(report["health_metrics"])
359
+ medication_events = len(report["medication_status"])
360
+ alerts = len(report["alerts"])
361
+ actions = len(report["action_items"])
362
+ notifications = len(report["family_notifications"])
363
+
364
+ # Get health timeline data
365
+ timeline_data = care_system.get_health_timeline(parent_id)
366
+ timeline_plot = create_health_timeline_plot(timeline_data)
367
+
368
+ # Get medication compliance data
369
+ compliance_data = care_system.get_medication_compliance(parent_id)
370
+ compliance_plot = create_medication_compliance_plot(compliance_data)
371
+
372
+ return parent_md, report_md, health_metrics, medication_events, alerts, actions, notifications, timeline_plot, compliance_plot
373
+ except Exception as e:
374
+ return f"Error: {str(e)}", "", 0, 0, 0, 0, 0, None, None
375
+
376
+ def create_health_timeline_plot(timeline_data: Dict[str, List]) -> gr.Plot:
377
+ """Create a plot of health metrics over time"""
378
+ if "error" in timeline_data:
379
+ fig, ax = plt.subplots(figsize=(10, 6))
380
+ ax.text(0.5, 0.5, "No timeline data available", ha='center', va='center')
381
+ return fig
382
+
383
+ # Create a figure with multiple subplots
384
+ fig, axs = plt.subplots(3, 1, figsize=(10, 10), sharex=True)
385
+ fig.suptitle("Health Metrics Over Time", fontsize=16)
386
+
387
+ # Plot blood pressure and heart rate
388
+ ax1 = axs[0]
389
+ dates = timeline_data["dates"]
390
+ ax1.plot(dates, timeline_data["blood_pressure_systolic"], 'r-', label='Systolic BP')
391
+ ax1.plot(dates, timeline_data["blood_pressure_diastolic"], 'b-', label='Diastolic BP')
392
+ ax1.set_ylabel('Blood Pressure (mmHg)')
393
+ ax1.grid(True)
394
+ ax1.legend(loc='upper left')
395
+
396
+ # Add heart rate on secondary y-axis
397
+ ax1_hr = ax1.twinx()
398
+ ax1_hr.plot(dates, timeline_data["heart_rate"], 'g-', label='Heart Rate')
399
+ ax1_hr.set_ylabel('Heart Rate (bpm)')
400
+ ax1_hr.legend(loc='upper right')
401
+
402
+ # Plot blood glucose
403
+ ax2 = axs[1]
404
+ ax2.plot(dates, timeline_data["blood_glucose"], 'm-', label='Blood Glucose')
405
+ ax2.set_ylabel('Blood Glucose (mg/dL)')
406
+ ax2.grid(True)
407
+ ax2.legend()
408
+
409
+ # Plot weight and temperature
410
+ ax3 = axs[2]
411
+ ax3.plot(dates, timeline_data["weight"], 'k-', label='Weight')
412
+ ax3.set_xlabel('Date')
413
+ ax3.set_ylabel('Weight (lbs)')
414
+ ax3.grid(True)
415
+ ax3.legend(loc='upper left')
416
+
417
+ # Add temperature on secondary y-axis
418
+ ax3_temp = ax3.twinx()
419
+ ax3_temp.plot(dates, timeline_data["temperature"], 'c-', label='Temperature')
420
+ ax3_temp.set_ylabel('Temperature (Β°F)')
421
+ ax3_temp.legend(loc='upper right')
422
+
423
+ plt.tight_layout()
424
+ return fig
425
+
426
+ def create_medication_compliance_plot(compliance_data: Dict[str, List]) -> gr.Plot:
427
+ """Create a plot of medication compliance"""
428
+ if "error" in compliance_data:
429
+ fig, ax = plt.subplots(figsize=(10, 6))
430
+ ax.text(0.5, 0.5, "No medication compliance data available", ha='center', va='center')
431
+ return fig
432
+
433
+ # Create a figure
434
+ fig, ax = plt.subplots(figsize=(10, 6))
435
+ fig.suptitle("7-Day Medication Compliance", fontsize=16)
436
+
437
+ # Set up data
438
+ medications = list(compliance_data.keys())
439
+ dates = [f"Day {i+1}" for i in range(7)]
440
+
441
+ # Create a matrix of compliance data
442
+ compliance_matrix = np.zeros((len(medications), 7))
443
+ for i, med in enumerate(medications):
444
+ for j in range(7):
445
+ compliance_matrix[i, j] = 1 if compliance_data[med][j] else 0
446
+
447
+ # Create heatmap
448
+ im = ax.imshow(compliance_matrix, cmap='RdYlGn', aspect='auto', vmin=0, vmax=1)
449
+
450
+ # Configure axes
451
+ ax.set_xticks(np.arange(len(dates)))
452
+ ax.set_yticks(np.arange(len(medications)))
453
+ ax.set_xticklabels(dates)
454
+ ax.set_yticklabels(medications)
455
+
456
+ # Add text annotations
457
+ for i in range(len(medications)):
458
+ for j in range(len(dates)):
459
+ text = "βœ“" if compliance_matrix[i, j] == 1 else "βœ—"
460
+ color = "black" if compliance_matrix[i, j] == 1 else "white"
461
+ ax.text(j, i, text, ha="center", va="center", color=color, fontweight="bold")
462
+
463
+ ax.set_xlabel("Day")
464
+ ax.set_title("βœ“ = Taken, βœ— = Missed")
465
+ plt.tight_layout()
466
+
467
+ return fig
468
+
469
+ # Create Gradio interface
470
+ with gr.Blocks(title="CareLoop AI - Family Caregiving Platform", theme=gr.themes.Soft()) as demo:
471
+ gr.Markdown("# 🏠 CareLoop - AI-Powered Family Caregiving")
472
+ gr.Markdown("Select a parent and run a care check to see AI analysis of their health and care needs.")
473
+
474
+ # Store parent_id as a state variable
475
+ current_parent_id = gr.State("parent_001")
476
+
477
+ with gr.Row():
478
+ with gr.Column(scale=1):
479
+ parent_dropdown = gr.Dropdown(
480
+ choices=[
481
+ "Margaret Chen (78) - Diabetes/Hypertension",
482
+ "Robert Johnson (72) - Stroke Recovery",
483
+ "Elena Gonzalez (81) - Early Alzheimer's"
484
+ ],
485
+ value="Margaret Chen (78) - Diabetes/Hypertension",
486
+ label="Select Parent"
487
+ )
488
+
489
+ run_button = gr.Button("πŸ”„ Run Care Check", variant="primary")
490
+
491
+ with gr.Accordion("About CareLoop", open=False):
492
+ gr.Markdown("""
493
+ ## About CareLoop
494
+
495
+ CareLoop is an AI-powered platform that helps families care for elderly relatives by:
496
+
497
+ - Monitoring health metrics and medication compliance
498
+ - Generating personalized care insights and recommendations
499
+ - Coordinating communication between family caregivers
500
+ - Detecting potential health issues early
501
+
502
+ This demo showcases how AI can transform elderly care by analyzing complex health data
503
+ and generating actionable insights for family caregivers.
504
+ """)
505
+
506
+ with gr.Column(scale=2):
507
+ with gr.Tabs():
508
+ with gr.Tab("Care Analysis"):
509
+ with gr.Row():
510
+ with gr.Column(scale=1):
511
+ parent_info = gr.Markdown("Select a parent to view their information")
512
+
513
+ with gr.Column(scale=2):
514
+ care_report = gr.Markdown("Run a care check to see the AI analysis")
515
+
516
+ with gr.Tab("Health Timeline"):
517
+ timeline_plot = gr.Plot(label="Health Metrics Over Time")
518
+
519
+ with gr.Tab("Medication Tracker"):
520
+ compliance_plot = gr.Plot(label="Medication Compliance")
521
+
522
+ with gr.Tab("Care Metrics"):
523
+ with gr.Row():
524
+ metric1 = gr.Number(label="Health Data Points", value=0)
525
+ metric2 = gr.Number(label="Medication Events", value=0)
526
+ metric3 = gr.Number(label="Alerts Generated", value=0)
527
+ metric4 = gr.Number(label="Action Items", value=0)
528
+ metric5 = gr.Number(label="Family Notifications", value=0)
529
+
530
+ with gr.Tab("Care Assistant"):
531
+ chatbot = gr.Chatbot(label="Chat with CareLoop")
532
+ msg = gr.Textbox(
533
+ placeholder="Ask a question about the patient's care...",
534
+ show_label=False
535
+ )
536
+ clear = gr.Button("Clear")
537
+
538
+ # Set up the event handlers
539
+ parent_map = {
540
+ "Margaret Chen (78) - Diabetes/Hypertension": "parent_001",
541
+ "Robert Johnson (72) - Stroke Recovery": "parent_002",
542
+ "Elena Gonzalez (81) - Early Alzheimer's": "parent_003"
543
+ }
544
+
545
+ def get_parent_id(display_name):
546
+ return parent_map.get(display_name, "parent_001")
547
+
548
+ def update_parent_id(display_name):
549
+ parent_id = get_parent_id(display_name)
550
+ return parent_id
551
+
552
+ parent_dropdown.change(
553
+ fn=update_parent_id,
554
+ inputs=[parent_dropdown],
555
+ outputs=[current_parent_id]
556
+ )
557
+
558
+ run_button.click(
559
+ fn=lambda p_id: run_care_check(p_id),
560
+ inputs=[current_parent_id],
561
+ outputs=[parent_info, care_report, metric1, metric2, metric3, metric4, metric5, timeline_plot, compliance_plot]
562
+ )
563
+
564
+ # Also update parent info when dropdown changes
565
+ parent_dropdown.change(
566
+ fn=lambda p_id: (format_parent_info(care_system.get_parent_info(p_id)), "", 0, 0, 0, 0, 0,
567
+ create_health_timeline_plot(care_system.get_health_timeline(p_id)),
568
+ create_medication_compliance_plot(care_system.get_medication_compliance(p_id))),
569
+ inputs=[current_parent_id],
570
+ outputs=[parent_info, care_report, metric1, metric2, metric3, metric4, metric5, timeline_plot, compliance_plot]
571
+ )
572
+
573
+ # Chat functionality - modified to avoid Gradio version compatibility issues
574
+ def submit_message(p_id, message, history):
575
+ if not message or message.strip() == "":
576
+ return "", history
577
+ response = care_system.chat_with_system(p_id, message)
578
+ new_history = history + [[message, response]]
579
+ return "", new_history
580
+
581
+ def clear_chat():
582
+ return []
583
+
584
+ msg.submit(
585
+ fn=submit_message,
586
+ inputs=[current_parent_id, msg, chatbot],
587
+ outputs=[msg, chatbot]
588
+ )
589
+
590
+ clear.click(fn=clear_chat, inputs=[], outputs=[chatbot])
591
+
592
+ if __name__ == "__main__":
593
+ print("πŸš€ Starting CareLoop Gradio Interface")
594
+ print("=" * 50)
595
+
596
+ # Display available parents
597
+ mock_data = MockDataGenerator()
598
+ print("🏠 Available Parent Profiles:")
599
+ for parent_id, parent in mock_data.parents.items():
600
+ family_count = len(mock_data.families.get(parent_id, []))
601
+ conditions = ", ".join(parent.conditions[:2]) + ("..." if len(parent.conditions) > 2 else "")
602
+ print(f" β€’ {parent.name} ({parent.age}) - ID: {parent_id}")
603
+ print(f" Health: {conditions}")
604
+ print(f" Family caregivers: {family_count}")
605
+
606
+ print("\n" + "=" * 50)
607
+ print("πŸ’‘ Note: Using Nebius API for LLM. Set NEBIUS_API_KEY environment variable for production use.")
608
+ print("=" * 50)
609
+
610
+ # Launch the Gradio app
611
+ try:
612
+ # Launch with standard options for Gradio 4.16.0
613
+ demo.launch(share=False, show_error=True)
614
+ except Exception as e:
615
+ print(f"Error launching Gradio app: {e}")
616
+ print("\nTrying alternative launch method...")
617
+ try:
618
+ # Alternative launch approach with minimal options
619
+ demo.queue(False).launch(share=False)
620
+ except Exception as e2:
621
+ print(f"Alternative launch also failed: {e2}")
622
+ print("\nPlease try updating Gradio with: pip install --upgrade gradio")
requirements-space.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio==4.16.0
2
+ langchain==0.1.17
3
+ langgraph==0.0.27
4
+ langchain-openai==0.1.7
5
+ langchain-core>=0.1.48,<0.2.0
6
+ pandas==2.2.2
7
+ numpy==1.26.4
8
+ matplotlib==3.8.2
9
+ openai==1.84.0
10
+ python-dateutil==2.9.0