File size: 27,311 Bytes
10fcca6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
"""

Task Coordinator Agent (Communicator Agent) for CareFlow Nexus

Agent 3: Assigns tasks to staff and orchestrates workflows



This agent is 50% rule-based (staff selection, task creation) and 50% AI (reasoning, escalation)

"""

import logging
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional

from base_agent import BaseAgent
from prompts.prompt_templates import TaskCoordinatorPrompts
from services.firebase_service import FirebaseService
from services.gemini_service import GeminiService
from utils.response_parser import ResponseParser

logger = logging.getLogger(__name__)


class CommunicatorAgent(BaseAgent):
    """

    Task Coordinator Agent - Assigns tasks and orchestrates workflows



    Responsibilities:

    - Create tasks for bed assignments, cleaning, etc.

    - Assign tasks to optimal staff (rule-based + AI)

    - Orchestrate multi-step workflows

    - Monitor task progress

    - Handle delays and escalations

    """

    # Workflow templates
    WORKFLOWS = {
        "bed_assignment": [
            {
                "task_type": "cleaning",
                "role": "cleaner",
                "priority": "high",
                "estimated_duration": 30,
                "description_template": "Clean and sanitize bed {bed_number} in {ward}",
            },
            {
                "task_type": "bed_prep",
                "role": "nurse",
                "priority": "high",
                "estimated_duration": 15,
                "description_template": "Prepare bed {bed_number} for patient {patient_name}",
                "depends_on": "cleaning",
            },
            {
                "task_type": "patient_transfer",
                "role": "nurse",
                "priority": "high",
                "estimated_duration": 20,
                "description_template": "Transfer patient {patient_name} to bed {bed_number}",
                "depends_on": "bed_prep",
            },
        ],
        "discharge": [
            {
                "task_type": "patient_discharge",
                "role": "nurse",
                "priority": "normal",
                "estimated_duration": 30,
                "description_template": "Process discharge for patient {patient_name} from bed {bed_number}",
            },
            {
                "task_type": "cleaning",
                "role": "cleaner",
                "priority": "high",
                "estimated_duration": 30,
                "description_template": "Deep clean bed {bed_number} after discharge",
                "depends_on": "patient_discharge",
            },
            {
                "task_type": "bed_prep",
                "role": "nurse",
                "priority": "normal",
                "estimated_duration": 15,
                "description_template": "Prepare bed {bed_number} for next patient",
                "depends_on": "cleaning",
            },
        ],
        "bed_cleaning": [
            {
                "task_type": "cleaning",
                "role": "cleaner",
                "priority": "high",
                "estimated_duration": 30,
                "description_template": "Clean bed {bed_number} in {ward}",
            }
        ],
    }

    def __init__(

        self,

        firebase_service: FirebaseService,

        gemini_service: GeminiService,

        memory_agent,

        max_staff_workload: int = 5,

    ):
        """

        Initialize Task Coordinator Agent



        Args:

            firebase_service: Firebase service instance

            gemini_service: Gemini AI service instance

            memory_agent: Memory agent for state queries

            max_staff_workload: Maximum tasks per staff member

        """
        super().__init__(
            agent_id="task_coordinator_001",
            agent_type="task_coordinator",
            firebase_service=firebase_service,
            gemini_service=gemini_service,
        )

        self.memory_agent = memory_agent
        self.max_staff_workload = max_staff_workload

        self.logger.info("Task Coordinator Agent initialized")

    async def process(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
        """

        Process task coordination requests



        Args:

            request_data: Request with 'type' and parameters



        Returns:

            Response dictionary

        """
        try:
            request_type = request_data.get("type", "")

            if request_type == "initiate_workflow":
                workflow_type = request_data.get("workflow_type")
                context = request_data.get("context", {})
                result = await self.initiate_workflow(workflow_type, context)
                return self.format_response(True, result, "Workflow initiated")

            elif request_type == "create_task":
                task_data = request_data.get("task_data", {})
                result = await self.create_and_assign_task(task_data)
                return self.format_response(True, result, "Task created")

            elif request_type == "assign_staff":
                task_data = request_data.get("task_data", {})
                result = await self.assign_optimal_staff(task_data)
                return self.format_response(True, result, "Staff assigned")

            elif request_type == "check_task_progress":
                result = await self.check_task_progress()
                return self.format_response(True, result, "Task progress checked")

            elif request_type == "handle_delayed_task":
                task_id = request_data.get("task_id")
                result = await self.handle_delayed_task(task_id)
                return self.format_response(True, result, "Delayed task handled")

            else:
                return self.format_response(
                    False,
                    None,
                    f"Unknown request type: {request_type}",
                    "invalid_request",
                )

        except Exception as e:
            self.logger.error(f"Error processing request: {e}")
            await self.log_error(str(e), request_data, "process_error")
            return self.format_response(False, None, str(e), "processing_error")

    # ==================== WORKFLOW ORCHESTRATION ====================

    async def initiate_workflow(

        self, workflow_type: str, context: Dict[str, Any]

    ) -> Dict[str, Any]:
        """

        Initiate a multi-step workflow



        Args:

            workflow_type: Type of workflow (bed_assignment, discharge, etc.)

            context: Context data (patient_id, bed_id, etc.)



        Returns:

            Result with created task IDs

        """
        try:
            self.logger.info(f"Initiating workflow: {workflow_type}")

            if workflow_type not in self.WORKFLOWS:
                raise ValueError(f"Unknown workflow type: {workflow_type}")

            workflow_template = self.WORKFLOWS[workflow_type]

            # Get patient and bed info for descriptions
            patient_id = context.get("patient_id")
            bed_id = context.get("bed_id")

            patient = None
            bed = None

            if patient_id:
                patient = await self.firebase.get_patient(patient_id)

            if bed_id:
                bed = await self.firebase.get_bed(bed_id)

            # Create tasks
            created_tasks = []
            for task_template in workflow_template:
                # Check if task has dependencies
                depends_on = task_template.get("depends_on")
                if depends_on:
                    # For now, just create all tasks immediately
                    # In production, implement dependency checking
                    pass

                # Format description
                description = task_template["description_template"].format(
                    patient_name=patient.get("name", "Patient")
                    if patient
                    else "Patient",
                    bed_number=bed.get("bed_number", "N/A") if bed else "N/A",
                    ward=bed.get("ward", "N/A") if bed else "N/A",
                )

                # Create task data
                task_data = {
                    "task_type": task_template["task_type"],
                    "description": description,
                    "priority": task_template["priority"],
                    "estimated_duration": task_template["estimated_duration"],
                    "bed_id": bed_id,
                    "patient_id": patient_id,
                    "workflow_type": workflow_type,
                    "assigned_by": "AI",
                }

                # Create and assign task
                task_result = await self.create_and_assign_task(task_data)
                created_tasks.append(task_result)

            # Log workflow initiation
            await self.log_decision(
                action="initiate_workflow",
                input_data={"workflow_type": workflow_type, "context": context},
                output_data={"tasks_created": len(created_tasks)},
                reasoning=f"Initiated {workflow_type} workflow with {len(created_tasks)} tasks",
            )

            return {
                "workflow_type": workflow_type,
                "tasks_created": created_tasks,
                "total_tasks": len(created_tasks),
            }

        except Exception as e:
            self.logger.error(f"Error initiating workflow: {e}")
            raise

    # ==================== TASK CREATION & ASSIGNMENT ====================

    async def create_and_assign_task(self, task_data: Dict[str, Any]) -> Dict[str, Any]:
        """

        Create task and assign to optimal staff member



        Args:

            task_data: Task information



        Returns:

            Created task with assignment details

        """
        try:
            # Get required role
            task_type = task_data.get("task_type")
            required_role = self._get_required_role(task_type)

            # Get bed info for ward context
            bed_id = task_data.get("bed_id")
            ward = None
            if bed_id:
                bed = await self.firebase.get_bed(bed_id)
                if bed:
                    ward = bed.get("ward")

            # Assign optimal staff (50% rule-based, 50% AI)
            assignment = await self.assign_optimal_staff(
                {
                    "task_type": task_type,
                    "required_role": required_role,
                    "ward": ward,
                    "priority": task_data.get("priority", "normal"),
                    "description": task_data.get("description", ""),
                }
            )

            if not assignment.get("staff_id"):
                # No staff available
                self.logger.warning(f"No staff available for {task_type}")
                task_data["assigned_to"] = None
                task_data["status"] = "pending"
            else:
                task_data["assigned_to"] = assignment["staff_id"]
                task_data["status"] = "pending"

            # Create task in Firebase
            task_id = await self.firebase.create_task(task_data)

            if not task_id:
                raise Exception("Failed to create task in Firebase")

            result = {
                "task_id": task_id,
                "task_type": task_type,
                "assigned_to": assignment.get("staff_id"),
                "staff_name": assignment.get("staff_name"),
                "reasoning": assignment.get("reasoning", ""),
                "priority": task_data.get("priority"),
                "description": task_data.get("description"),
            }

            self.logger.info(
                f"Created task {task_id} and assigned to {assignment.get('staff_name', 'No one (pending)')}"
            )

            # Log decision
            await self.log_decision(
                action="create_task",
                input_data=task_data,
                output_data=result,
                reasoning=assignment.get("reasoning", "Task created"),
            )

            return result

        except Exception as e:
            self.logger.error(f"Error creating and assigning task: {e}")
            raise

    # ==================== STAFF ASSIGNMENT (HYBRID 50/50) ====================

    async def assign_optimal_staff(self, task_info: Dict[str, Any]) -> Dict[str, Any]:
        """

        Assign optimal staff member to task using hybrid approach



        Args:

            task_info: Task information



        Returns:

            Assignment dictionary with staff_id and reasoning

        """
        try:
            required_role = task_info.get("required_role")
            ward = task_info.get("ward")

            # Step 1: Get available staff from memory agent (rule-based)
            staff_response = await self.memory_agent.process(
                {"type": "get_staff_availability", "role": required_role, "ward": ward}
            )
            available_staff = staff_response.get("data", [])

            if not available_staff:
                self.logger.warning(f"No available {required_role} staff")
                return {
                    "staff_id": None,
                    "staff_name": None,
                    "reasoning": f"No available {required_role} staff",
                    "confidence": 0,
                }

            # Step 2: Rule-based scoring
            scored_staff = self._score_staff_rule_based(available_staff, task_info)

            # Step 3: Get AI recommendation (top 5 candidates)
            top_candidates = scored_staff[:5]
            ai_recommendation = await self._get_ai_staff_recommendation(
                task_info, top_candidates
            )

            # Step 4: Combine rule-based and AI decision
            final_assignment = self._combine_staff_assignment(
                scored_staff, ai_recommendation
            )

            return final_assignment

        except Exception as e:
            self.logger.error(f"Error assigning staff: {e}")
            # Fallback to first available staff
            if available_staff:
                return {
                    "staff_id": available_staff[0].get("id"),
                    "staff_name": available_staff[0].get("name"),
                    "reasoning": "Fallback assignment to first available staff",
                    "confidence": 50,
                }
            return {
                "staff_id": None,
                "staff_name": None,
                "reasoning": "No staff available",
                "confidence": 0,
            }

    def _score_staff_rule_based(

        self, staff_list: List[Dict], task_info: Dict

    ) -> List[Dict]:
        """

        Score staff members using rule-based criteria



        Scoring:

        - Workload (0-5 tasks): 40 points (fewer tasks = higher score)

        - Ward match: 30 points

        - Recent activity: 20 points

        - Bonus: 10 points



        Args:

            staff_list: List of available staff

            task_info: Task information



        Returns:

            Sorted list of staff with scores

        """
        task_ward = task_info.get("ward")
        scored_staff = []

        for staff in staff_list:
            score = 0

            # 1. Workload score (40 points max)
            current_load = staff.get("current_load", 0)
            workload_score = max(0, 40 - (current_load * 8))  # 8 points per task
            score += workload_score

            # 2. Ward match (30 points)
            staff_ward = staff.get("assigned_ward")
            if task_ward and staff_ward == task_ward:
                score += 30
            elif task_ward and staff_ward:
                score += 10  # Different ward but still assigned somewhere
            else:
                score += 15  # No ward assignment

            # 3. Recent activity (20 points)
            # For now, give everyone 15 points (would need task history)
            score += 15

            # 4. Bonus points (10 points)
            score += 10

            scored_staff.append(
                {
                    **staff,
                    "rule_score": score,
                }
            )

        # Sort by score (highest first)
        scored_staff.sort(key=lambda x: x["rule_score"], reverse=True)

        return scored_staff

    async def _get_ai_staff_recommendation(

        self, task_info: Dict, candidates: List[Dict]

    ) -> Dict[str, Any]:
        """

        Get AI recommendation for staff assignment



        Args:

            task_info: Task information

            candidates: Top staff candidates from rule-based scoring



        Returns:

            AI recommendation dictionary

        """
        try:
            # Prepare candidates for AI
            candidates_for_ai = []
            for staff in candidates:
                candidates_for_ai.append(
                    {
                        "staff_id": staff.get("id"),
                        "name": staff.get("name"),
                        "role": staff.get("role"),
                        "current_load": staff.get("current_load", 0),
                        "assigned_ward": staff.get("assigned_ward"),
                        "rule_score": staff.get("rule_score"),
                    }
                )

            # Get system state for context
            state_response = await self.memory_agent.process(
                {"type": "get_system_state"}
            )
            state = state_response.get("data", {})

            # Build prompt
            prompt = TaskCoordinatorPrompts.STAFF_ASSIGNMENT.format(
                task_id="TBD",
                task_type=task_info.get("task_type"),
                description=task_info.get("description", ""),
                priority=task_info.get("priority", "normal"),
                ward=task_info.get("ward", "Unknown"),
                bed_number="TBD",
                duration=task_info.get("estimated_duration", 30),
                patient_name="Patient",
                staff_json=self._format_staff_for_prompt(candidates_for_ai),
                required_role=task_info.get("required_role"),
                current_time=datetime.now().strftime("%H:%M"),
                activity_level="normal",
                pending_tasks_count=state.get("tasks", {}).get("pending", 0),
            )

            # Call Gemini AI
            response = await self.gemini.generate_json_response(prompt, temperature=0.5)

            if response:
                parsed = ResponseParser.parse_staff_assignment_response(response)
                self.logger.info(
                    f"AI recommended staff: {parsed.get('staff_name')} with {parsed.get('confidence')}% confidence"
                )
                return parsed

            return {}

        except Exception as e:
            self.logger.error(f"Error getting AI staff recommendation: {e}")
            return {}

    def _format_staff_for_prompt(self, staff_list: List[Dict]) -> str:
        """Format staff list for AI prompt"""
        lines = []
        for i, staff in enumerate(staff_list, 1):
            lines.append(
                f"{i}. {staff.get('name')} ({staff.get('role')})\n"
                f"   Current Workload: {staff.get('current_load', 0)} tasks\n"
                f"   Ward: {staff.get('assigned_ward', 'Any')}\n"
                f"   Rule Score: {staff.get('rule_score', 0)}/100"
            )
        return "\n\n".join(lines)

    def _combine_staff_assignment(

        self, rule_based_staff: List[Dict], ai_recommendation: Dict

    ) -> Dict[str, Any]:
        """

        Combine rule-based and AI staff assignment (50/50 approach)



        Args:

            rule_based_staff: Staff sorted by rule-based score

            ai_recommendation: AI recommendation



        Returns:

            Final assignment decision

        """
        # Get AI recommended staff ID
        ai_staff_id = ai_recommendation.get("recommended_staff_id")

        # If AI has a recommendation, use it
        if ai_staff_id:
            # Find the staff member
            for staff in rule_based_staff:
                if staff.get("id") == ai_staff_id:
                    return {
                        "staff_id": staff.get("id"),
                        "staff_name": staff.get("name"),
                        "reasoning": ai_recommendation.get(
                            "reasoning", "AI recommendation"
                        ),
                        "workload_impact": ai_recommendation.get("workload_impact", ""),
                        "confidence": ai_recommendation.get("confidence", 75),
                        "method": "AI-selected",
                    }

        # Fallback to rule-based top choice
        if rule_based_staff:
            top_staff = rule_based_staff[0]
            return {
                "staff_id": top_staff.get("id"),
                "staff_name": top_staff.get("name"),
                "reasoning": f"Selected based on lowest workload ({top_staff.get('current_load', 0)} tasks) and ward proximity",
                "workload_impact": f"Workload will increase from {top_staff.get('current_load', 0)} to {top_staff.get('current_load', 0) + 1} tasks",
                "confidence": 70,
                "method": "Rule-based",
            }

        return {
            "staff_id": None,
            "staff_name": None,
            "reasoning": "No staff available",
            "confidence": 0,
            "method": "None",
        }

    # ==================== TASK MONITORING ====================

    async def check_task_progress(self) -> Dict[str, Any]:
        """

        Check progress of all active tasks



        Returns:

            Task progress summary with delays

        """
        try:
            # Get all active tasks
            tasks = await self.firebase.get_tasks(
                {"status": ["pending", "in_progress"]}
            )

            delayed_tasks = []
            on_track_tasks = []
            current_time = datetime.now()

            for task in tasks:
                created_at = task.get("created_at")
                estimated_duration = task.get("estimated_duration", 30)

                # Calculate expected completion time
                if created_at:
                    # Convert Firestore timestamp if needed
                    if hasattr(created_at, "timestamp"):
                        created_at = datetime.fromtimestamp(created_at.timestamp())

                    expected_completion = created_at + timedelta(
                        minutes=estimated_duration
                    )

                    if current_time > expected_completion:
                        delay_minutes = (
                            current_time - expected_completion
                        ).total_seconds() / 60
                        delayed_tasks.append(
                            {
                                "task_id": task.get("id"),
                                "task_type": task.get("task_type"),
                                "delay_minutes": int(delay_minutes),
                                "priority": task.get("priority"),
                                "assigned_to": task.get("assigned_to"),
                            }
                        )
                    else:
                        on_track_tasks.append(task.get("id"))

            result = {
                "total_active_tasks": len(tasks),
                "on_track": len(on_track_tasks),
                "delayed": len(delayed_tasks),
                "delayed_tasks": delayed_tasks,
            }

            self.logger.info(
                f"Task progress: {len(on_track_tasks)} on track, {len(delayed_tasks)} delayed"
            )

            return result

        except Exception as e:
            self.logger.error(f"Error checking task progress: {e}")
            return {"error": str(e)}

    async def handle_delayed_task(self, task_id: str) -> Dict[str, Any]:
        """

        Handle a delayed task (escalation logic)



        Args:

            task_id: Task ID



        Returns:

            Action taken

        """
        try:
            task = await self.firebase.get_task(task_id)

            if not task:
                return {"action": "none", "reason": "Task not found"}

            # Simple escalation logic
            priority = task.get("priority", "normal")

            if priority == "high" or priority == "urgent":
                # Escalate to supervisor
                action = "escalated"
                message = f"High priority task {task_id} is delayed"

                await self.firebase.log_event(
                    {
                        "entity_type": "task_escalation",
                        "entity_id": task_id,
                        "action": "escalate_to_supervisor",
                        "triggered_by": self.agent_type,
                        "details": {"task": task, "reason": "delayed"},
                    }
                )

            else:
                # Increase priority
                action = "priority_increased"
                new_priority = "high" if priority == "normal" else "urgent"
                await self.firebase.update_task_status(
                    task_id, task.get("status"), f"Priority increased to {new_priority}"
                )
                message = f"Task priority increased to {new_priority}"

            return {"action": action, "message": message, "task_id": task_id}

        except Exception as e:
            self.logger.error(f"Error handling delayed task: {e}")
            return {"action": "error", "reason": str(e)}

    # ==================== HELPER METHODS ====================

    def _get_required_role(self, task_type: str) -> str:
        """Get required staff role for task type"""
        role_map = {
            "cleaning": "cleaner",
            "bed_prep": "nurse",
            "patient_transfer": "nurse",
            "patient_discharge": "nurse",
            "medication": "nurse",
            "examination": "doctor",
        }
        return role_map.get(task_type, "nurse")

    def get_capabilities(self) -> List[str]:
        """Get agent capabilities"""
        return [
            "initiate_workflow",
            "create_task",
            "assign_staff",
            "check_task_progress",
            "handle_delayed_task",
            "orchestrate_workflows",
        ]