empathy-framework 3.5.6__py3-none-any.whl → 3.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agents/compliance_anticipation_agent.py +113 -118
- agents/compliance_db.py +339 -0
- agents/epic_integration_wizard.py +37 -48
- agents/notifications.py +291 -0
- agents/trust_building_behaviors.py +66 -85
- coach_wizards/__init__.py +11 -12
- coach_wizards/accessibility_wizard.py +12 -12
- coach_wizards/api_wizard.py +12 -12
- coach_wizards/base_wizard.py +26 -20
- coach_wizards/cicd_wizard.py +15 -13
- coach_wizards/compliance_wizard.py +12 -12
- coach_wizards/database_wizard.py +12 -12
- coach_wizards/debugging_wizard.py +12 -12
- coach_wizards/documentation_wizard.py +12 -12
- coach_wizards/generate_wizards.py +1 -2
- coach_wizards/localization_wizard.py +21 -14
- coach_wizards/migration_wizard.py +12 -12
- coach_wizards/monitoring_wizard.py +12 -12
- coach_wizards/observability_wizard.py +12 -12
- coach_wizards/performance_wizard.py +12 -12
- coach_wizards/prompt_engineering_wizard.py +22 -25
- coach_wizards/refactoring_wizard.py +12 -12
- coach_wizards/scaling_wizard.py +12 -12
- coach_wizards/security_wizard.py +12 -12
- coach_wizards/testing_wizard.py +12 -12
- {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/METADATA +234 -30
- empathy_framework-3.7.0.dist-info/RECORD +105 -0
- empathy_healthcare_plugin/__init__.py +1 -2
- empathy_llm_toolkit/__init__.py +5 -6
- empathy_llm_toolkit/claude_memory.py +14 -15
- empathy_llm_toolkit/code_health.py +27 -19
- empathy_llm_toolkit/contextual_patterns.py +11 -12
- empathy_llm_toolkit/core.py +43 -49
- empathy_llm_toolkit/git_pattern_extractor.py +16 -12
- empathy_llm_toolkit/levels.py +6 -13
- empathy_llm_toolkit/pattern_confidence.py +14 -18
- empathy_llm_toolkit/pattern_resolver.py +10 -12
- empathy_llm_toolkit/pattern_summary.py +13 -11
- empathy_llm_toolkit/providers.py +27 -38
- empathy_llm_toolkit/session_status.py +18 -20
- empathy_llm_toolkit/state.py +20 -21
- empathy_os/__init__.py +72 -73
- empathy_os/cli.py +193 -98
- empathy_os/cli_unified.py +68 -41
- empathy_os/config.py +31 -31
- empathy_os/coordination.py +48 -54
- empathy_os/core.py +90 -99
- empathy_os/cost_tracker.py +20 -23
- empathy_os/discovery.py +9 -11
- empathy_os/emergence.py +20 -21
- empathy_os/exceptions.py +18 -30
- empathy_os/feedback_loops.py +27 -30
- empathy_os/levels.py +31 -34
- empathy_os/leverage_points.py +27 -28
- empathy_os/logging_config.py +11 -12
- empathy_os/monitoring.py +27 -27
- empathy_os/pattern_library.py +29 -28
- empathy_os/persistence.py +30 -34
- empathy_os/platform_utils.py +46 -47
- empathy_os/redis_config.py +14 -15
- empathy_os/redis_memory.py +53 -56
- empathy_os/templates.py +12 -11
- empathy_os/trust_building.py +44 -36
- empathy_os/workflow_commands.py +123 -31
- empathy_software_plugin/__init__.py +1 -2
- empathy_software_plugin/cli.py +32 -25
- empathy_software_plugin/plugin.py +4 -8
- empathy_framework-3.5.6.dist-info/RECORD +0 -103
- {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/WHEEL +0 -0
- {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/entry_points.txt +0 -0
- {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/licenses/LICENSE +0 -0
- {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Compliance Anticipation Agent - Level 4 Anticipatory Empathy
|
|
1
|
+
"""Compliance Anticipation Agent - Level 4 Anticipatory Empathy
|
|
3
2
|
|
|
4
3
|
Multi-step LangGraph agent that predicts regulatory audits, identifies compliance
|
|
5
4
|
gaps, and prepares documentation proactively to achieve positive outcomes.
|
|
@@ -33,8 +32,7 @@ logger = logging.getLogger(__name__)
|
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
class ComplianceAgentState(TypedDict):
|
|
36
|
-
"""
|
|
37
|
-
Level 4 Anticipatory Agent State
|
|
35
|
+
"""Level 4 Anticipatory Agent State
|
|
38
36
|
|
|
39
37
|
Follows Principle #13: "Agent State as Clinical Flowsheet"
|
|
40
38
|
Every field answers a compliance question with clear audit trail.
|
|
@@ -195,10 +193,10 @@ class ComplianceAgentState(TypedDict):
|
|
|
195
193
|
|
|
196
194
|
|
|
197
195
|
def create_initial_state(
|
|
198
|
-
hospital_id: str,
|
|
196
|
+
hospital_id: str,
|
|
197
|
+
audit_type: str = "joint_commission",
|
|
199
198
|
) -> ComplianceAgentState:
|
|
200
|
-
"""
|
|
201
|
-
Create initial agent state
|
|
199
|
+
"""Create initial agent state
|
|
202
200
|
|
|
203
201
|
Args:
|
|
204
202
|
hospital_id: Unique identifier for hospital/facility
|
|
@@ -206,6 +204,7 @@ def create_initial_state(
|
|
|
206
204
|
|
|
207
205
|
Returns:
|
|
208
206
|
Initialized ComplianceAgentState
|
|
207
|
+
|
|
209
208
|
"""
|
|
210
209
|
now = datetime.now()
|
|
211
210
|
execution_id = f"compliance_{now.strftime('%Y%m%d_%H%M%S')}_{hospital_id}"
|
|
@@ -281,8 +280,7 @@ def create_initial_state(
|
|
|
281
280
|
|
|
282
281
|
|
|
283
282
|
def create_compliance_agent() -> StateGraph:
|
|
284
|
-
"""
|
|
285
|
-
Create Level 4 Anticipatory Compliance Agent
|
|
283
|
+
"""Create Level 4 Anticipatory Compliance Agent
|
|
286
284
|
|
|
287
285
|
Workflow:
|
|
288
286
|
1. Predict Audit Timeline → When will next audit occur?
|
|
@@ -299,7 +297,6 @@ def create_compliance_agent() -> StateGraph:
|
|
|
299
297
|
- Actionable (assigns specific tasks with deadlines)
|
|
300
298
|
- Transparent (explains reasoning, provides confidence scores)
|
|
301
299
|
"""
|
|
302
|
-
|
|
303
300
|
workflow = StateGraph(ComplianceAgentState)
|
|
304
301
|
|
|
305
302
|
# Step 1: Audit Prediction
|
|
@@ -350,8 +347,7 @@ def create_compliance_agent() -> StateGraph:
|
|
|
350
347
|
|
|
351
348
|
|
|
352
349
|
def predict_next_audit(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
353
|
-
"""
|
|
354
|
-
Step 1: Predict when next audit will occur
|
|
350
|
+
"""Step 1: Predict when next audit will occur
|
|
355
351
|
|
|
356
352
|
Methods (in order of preference):
|
|
357
353
|
1. Regulatory schedule (published audit windows) - confidence: 0.95
|
|
@@ -364,7 +360,6 @@ def predict_next_audit(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
|
364
360
|
- Reduces stress and scrambling
|
|
365
361
|
- Allows time to fix gaps without pressure
|
|
366
362
|
"""
|
|
367
|
-
|
|
368
363
|
logger.info(f"[Step 1] Predicting {state['audit_type']} audit for {state['hospital_id']}")
|
|
369
364
|
|
|
370
365
|
# Add to audit trail
|
|
@@ -375,7 +370,7 @@ def predict_next_audit(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
|
375
370
|
"action": "Starting audit prediction",
|
|
376
371
|
"details": {"audit_type": state["audit_type"]},
|
|
377
372
|
"user": "system",
|
|
378
|
-
}
|
|
373
|
+
},
|
|
379
374
|
)
|
|
380
375
|
|
|
381
376
|
# Get audit cycle for this type
|
|
@@ -389,8 +384,10 @@ def predict_next_audit(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
|
389
384
|
cycle_months = audit_cycles.get(state["audit_type"], 36)
|
|
390
385
|
state["audit_cycle_months"] = cycle_months
|
|
391
386
|
|
|
392
|
-
# TODO:
|
|
393
|
-
#
|
|
387
|
+
# TODO: Integration point - Connect to real compliance database
|
|
388
|
+
# IMPLEMENTATION AVAILABLE: See agents/compliance_db.py ComplianceDatabase class
|
|
389
|
+
# Use: compliance_db.get_last_audit(audit_type=state["audit_type"])
|
|
390
|
+
# For now, this example uses simulated data to demonstrate the workflow
|
|
394
391
|
# last_audit = get_last_audit_date(state["hospital_id"], state["audit_type"])
|
|
395
392
|
|
|
396
393
|
# Example: Last Joint Commission audit was 2023-04-15
|
|
@@ -426,15 +423,15 @@ def predict_next_audit(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
|
426
423
|
# Log prediction
|
|
427
424
|
logger.info(
|
|
428
425
|
f"Predicted audit: {predicted_date.strftime('%Y-%m-%d')} "
|
|
429
|
-
f"({days_until} days away, {confidence:.0%} confidence)"
|
|
426
|
+
f"({days_until} days away, {confidence:.0%} confidence)",
|
|
430
427
|
)
|
|
431
428
|
|
|
432
429
|
# Add message
|
|
433
430
|
state["messages"].append(
|
|
434
431
|
AIMessage(
|
|
435
432
|
content=f"Predicted {state['audit_type']} audit on {predicted_date.strftime('%Y-%m-%d')} "
|
|
436
|
-
f"(in {days_until} days) with {confidence:.0%} confidence"
|
|
437
|
-
)
|
|
433
|
+
f"(in {days_until} days) with {confidence:.0%} confidence",
|
|
434
|
+
),
|
|
438
435
|
)
|
|
439
436
|
|
|
440
437
|
# Audit trail
|
|
@@ -450,15 +447,14 @@ def predict_next_audit(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
|
450
447
|
"method": "historical_cycle",
|
|
451
448
|
},
|
|
452
449
|
"user": "system",
|
|
453
|
-
}
|
|
450
|
+
},
|
|
454
451
|
)
|
|
455
452
|
|
|
456
453
|
return state
|
|
457
454
|
|
|
458
455
|
|
|
459
456
|
def check_anticipation_window(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
460
|
-
"""
|
|
461
|
-
Step 2: Check if we're within optimal anticipation window
|
|
457
|
+
"""Step 2: Check if we're within optimal anticipation window
|
|
462
458
|
|
|
463
459
|
Level 4 Guardrail:
|
|
464
460
|
- Too early (>120 days): Preparation may become outdated, waste effort
|
|
@@ -470,7 +466,6 @@ def check_anticipation_window(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
470
466
|
- Acting at right time maximizes effectiveness
|
|
471
467
|
- Avoids wasted effort (too early) or crisis mode (too late)
|
|
472
468
|
"""
|
|
473
|
-
|
|
474
469
|
logger.info(f"[Step 2] Checking anticipation window ({state['days_until_audit']} days)")
|
|
475
470
|
|
|
476
471
|
days_until = state["days_until_audit"]
|
|
@@ -491,7 +486,7 @@ def check_anticipation_window(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
491
486
|
state["warnings"].append(
|
|
492
487
|
f"Audit predicted in {days_until} days. "
|
|
493
488
|
f"Optimal anticipation window is 60-120 days. "
|
|
494
|
-
f"Will schedule re-check for {(datetime.now() + timedelta(days=days_until - 120)).strftime('%Y-%m-%d')}"
|
|
489
|
+
f"Will schedule re-check for {(datetime.now() + timedelta(days=days_until - 120)).strftime('%Y-%m-%d')}",
|
|
495
490
|
)
|
|
496
491
|
|
|
497
492
|
elif 30 <= days_until < 60:
|
|
@@ -502,7 +497,7 @@ def check_anticipation_window(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
502
497
|
state["warnings"].append(
|
|
503
498
|
"Less than 60 days until audit. "
|
|
504
499
|
"Limited time for comprehensive remediation. "
|
|
505
|
-
"Recommend expedited action."
|
|
500
|
+
"Recommend expedited action.",
|
|
506
501
|
)
|
|
507
502
|
|
|
508
503
|
else: # < 30 days
|
|
@@ -512,7 +507,7 @@ def check_anticipation_window(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
512
507
|
message = f"🚨 URGENT: Only {days_until} days until audit. Focus on critical gaps only."
|
|
513
508
|
state["warnings"].append(
|
|
514
509
|
"Less than 30 days until audit. "
|
|
515
|
-
"Very limited time. Focus on critical compliance gaps only."
|
|
510
|
+
"Very limited time. Focus on critical compliance gaps only.",
|
|
516
511
|
)
|
|
517
512
|
|
|
518
513
|
state["messages"].append(AIMessage(content=message))
|
|
@@ -532,7 +527,7 @@ def check_anticipation_window(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
532
527
|
"within_window": state["is_within_anticipation_window"],
|
|
533
528
|
},
|
|
534
529
|
"user": "system",
|
|
535
|
-
}
|
|
530
|
+
},
|
|
536
531
|
)
|
|
537
532
|
|
|
538
533
|
logger.info(f"Anticipation window: {state['time_to_act']}")
|
|
@@ -541,28 +536,25 @@ def check_anticipation_window(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
541
536
|
|
|
542
537
|
|
|
543
538
|
def should_anticipate(state: ComplianceAgentState) -> str:
|
|
544
|
-
"""
|
|
545
|
-
Routing function: Decide whether to proceed with anticipation
|
|
539
|
+
"""Routing function: Decide whether to proceed with anticipation
|
|
546
540
|
|
|
547
541
|
Returns:
|
|
548
542
|
"anticipate" - Within optimal window, proceed
|
|
549
543
|
"too_early" - Too far out, schedule for later
|
|
550
544
|
"proceed_anyway" - Urgent, proceed despite non-optimal timing
|
|
551
|
-
"""
|
|
552
545
|
|
|
546
|
+
"""
|
|
553
547
|
time_to_act = state["time_to_act"]
|
|
554
548
|
|
|
555
549
|
if time_to_act == "too_early":
|
|
556
550
|
return "too_early"
|
|
557
|
-
|
|
551
|
+
if time_to_act in ["urgent", "too_late"]:
|
|
558
552
|
return "proceed_anyway"
|
|
559
|
-
|
|
560
|
-
return "anticipate"
|
|
553
|
+
return "anticipate"
|
|
561
554
|
|
|
562
555
|
|
|
563
556
|
def assess_current_compliance(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
564
|
-
"""
|
|
565
|
-
Step 3A: Assess current compliance status
|
|
557
|
+
"""Step 3A: Assess current compliance status
|
|
566
558
|
|
|
567
559
|
Scans all compliance requirements for the audit type and determines
|
|
568
560
|
current compliance percentage.
|
|
@@ -572,11 +564,12 @@ def assess_current_compliance(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
572
564
|
- Categorization helps prioritize remediation
|
|
573
565
|
- Baseline measurement enables tracking improvement
|
|
574
566
|
"""
|
|
575
|
-
|
|
576
567
|
logger.info(f"[Step 3A] Assessing compliance for {state['audit_type']}")
|
|
577
568
|
|
|
578
|
-
# TODO: Connect to real compliance
|
|
579
|
-
#
|
|
569
|
+
# TODO: Integration point - Connect to real compliance assessment system
|
|
570
|
+
# IMPLEMENTATION AVAILABLE: See agents/compliance_db.py for status tracking
|
|
571
|
+
# Use: compliance_db.get_current_compliance_status(compliance_framework)
|
|
572
|
+
# For now, this example simulates assessment to demonstrate the workflow
|
|
580
573
|
|
|
581
574
|
# Get requirements for this audit type
|
|
582
575
|
requirements = get_audit_requirements(state["audit_type"])
|
|
@@ -591,7 +584,10 @@ def assess_current_compliance(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
591
584
|
category_scores = {}
|
|
592
585
|
|
|
593
586
|
for req in requirements:
|
|
594
|
-
# TODO: Check actual compliance
|
|
587
|
+
# TODO: Integration point - Check actual compliance against requirements
|
|
588
|
+
# IMPLEMENTATION AVAILABLE: Query from compliance_db.get_active_gaps()
|
|
589
|
+
# Filter gaps by framework and severity to determine compliance status
|
|
590
|
+
# For now, this example simulates compliance checks to demonstrate scoring logic
|
|
595
591
|
# is_compliant = check_requirement_compliance(state["hospital_id"], req)
|
|
596
592
|
|
|
597
593
|
# Simulated: 90% compliant
|
|
@@ -645,8 +641,8 @@ def assess_current_compliance(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
645
641
|
state["messages"].append(
|
|
646
642
|
AIMessage(
|
|
647
643
|
content=f"{status_emoji} Compliance Assessment: {compliance_pct:.1f}% "
|
|
648
|
-
f"({compliant}/{total_items} items compliant)"
|
|
649
|
-
)
|
|
644
|
+
f"({compliant}/{total_items} items compliant)",
|
|
645
|
+
),
|
|
650
646
|
)
|
|
651
647
|
|
|
652
648
|
# Audit trail
|
|
@@ -662,7 +658,7 @@ def assess_current_compliance(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
662
658
|
"categories": category_pct,
|
|
663
659
|
},
|
|
664
660
|
"user": "system",
|
|
665
|
-
}
|
|
661
|
+
},
|
|
666
662
|
)
|
|
667
663
|
|
|
668
664
|
logger.info(f"Compliance: {compliance_pct:.1f}% ({compliant}/{total_items})")
|
|
@@ -671,8 +667,7 @@ def assess_current_compliance(state: ComplianceAgentState) -> ComplianceAgentSta
|
|
|
671
667
|
|
|
672
668
|
|
|
673
669
|
def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
674
|
-
"""
|
|
675
|
-
Step 3B: Identify specific compliance gaps with actionable details
|
|
670
|
+
"""Step 3B: Identify specific compliance gaps with actionable details
|
|
676
671
|
|
|
677
672
|
For positive outcomes:
|
|
678
673
|
- Specific patient IDs enable targeted remediation
|
|
@@ -680,11 +675,12 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
|
|
|
680
675
|
- Time estimates enable resource planning
|
|
681
676
|
- Retroactive fix capability determines urgency
|
|
682
677
|
"""
|
|
683
|
-
|
|
684
678
|
logger.info("[Step 3B] Identifying compliance gaps")
|
|
685
679
|
|
|
686
|
-
# TODO: Connect to real gap detection system
|
|
687
|
-
#
|
|
680
|
+
# TODO: Integration point - Connect to real gap detection system
|
|
681
|
+
# IMPLEMENTATION AVAILABLE: See agents/compliance_db.py ComplianceDatabase.record_gap()
|
|
682
|
+
# Use: compliance_db.get_active_gaps(severity, framework) to retrieve detected gaps
|
|
683
|
+
# For now, this example simulates gap detection to demonstrate remediation planning
|
|
688
684
|
|
|
689
685
|
gaps = []
|
|
690
686
|
gap_id_counter = 1
|
|
@@ -710,7 +706,7 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
|
|
|
710
706
|
"estimated_time_to_fix": "25 minutes",
|
|
711
707
|
"can_fix_retroactively": True,
|
|
712
708
|
"legal_risk": "medium",
|
|
713
|
-
}
|
|
709
|
+
},
|
|
714
710
|
)
|
|
715
711
|
gap_id_counter += 1
|
|
716
712
|
|
|
@@ -729,7 +725,7 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
|
|
|
729
725
|
"estimated_time_to_fix": "45 minutes",
|
|
730
726
|
"can_fix_retroactively": False, # Can document review but can't undo administration
|
|
731
727
|
"legal_risk": "high",
|
|
732
|
-
}
|
|
728
|
+
},
|
|
733
729
|
)
|
|
734
730
|
gap_id_counter += 1
|
|
735
731
|
|
|
@@ -748,7 +744,7 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
|
|
|
748
744
|
"estimated_time_to_fix": "15 minutes",
|
|
749
745
|
"can_fix_retroactively": False,
|
|
750
746
|
"legal_risk": "high",
|
|
751
|
-
}
|
|
747
|
+
},
|
|
752
748
|
)
|
|
753
749
|
gap_id_counter += 1
|
|
754
750
|
|
|
@@ -768,7 +764,7 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
|
|
|
768
764
|
# Message
|
|
769
765
|
if len(gaps) == 0:
|
|
770
766
|
state["messages"].append(
|
|
771
|
-
AIMessage(content="🎉 No compliance gaps identified. Excellent work!")
|
|
767
|
+
AIMessage(content="🎉 No compliance gaps identified. Excellent work!"),
|
|
772
768
|
)
|
|
773
769
|
else:
|
|
774
770
|
state["messages"].append(
|
|
@@ -777,8 +773,8 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
|
|
|
777
773
|
f"{severity_dist['critical']} critical, "
|
|
778
774
|
f"{severity_dist['high']} high, "
|
|
779
775
|
f"{severity_dist['medium']} medium, "
|
|
780
|
-
f"{severity_dist['low']} low"
|
|
781
|
-
)
|
|
776
|
+
f"{severity_dist['low']} low",
|
|
777
|
+
),
|
|
782
778
|
)
|
|
783
779
|
|
|
784
780
|
# Audit trail
|
|
@@ -793,7 +789,7 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
|
|
|
793
789
|
"gaps": [{"id": g["gap_id"], "description": g["description"]} for g in gaps],
|
|
794
790
|
},
|
|
795
791
|
"user": "system",
|
|
796
|
-
}
|
|
792
|
+
},
|
|
797
793
|
)
|
|
798
794
|
|
|
799
795
|
logger.info(f"Identified {len(gaps)} gaps: {severity_dist}")
|
|
@@ -802,8 +798,7 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
|
|
|
802
798
|
|
|
803
799
|
|
|
804
800
|
def prepare_audit_documentation(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
805
|
-
"""
|
|
806
|
-
Step 4: Prepare comprehensive audit documentation package
|
|
801
|
+
"""Step 4: Prepare comprehensive audit documentation package
|
|
807
802
|
|
|
808
803
|
For positive outcomes:
|
|
809
804
|
- Pre-prepared documentation reduces audit day stress
|
|
@@ -811,7 +806,6 @@ def prepare_audit_documentation(state: ComplianceAgentState) -> ComplianceAgentS
|
|
|
811
806
|
- Gap remediation plan demonstrates proactive approach
|
|
812
807
|
- Audit readiness score provides confidence metric
|
|
813
808
|
"""
|
|
814
|
-
|
|
815
809
|
logger.info("[Step 4] Preparing audit documentation")
|
|
816
810
|
|
|
817
811
|
# Generate documentation package
|
|
@@ -861,7 +855,13 @@ def prepare_audit_documentation(state: ComplianceAgentState) -> ComplianceAgentS
|
|
|
861
855
|
}
|
|
862
856
|
|
|
863
857
|
# Simulate storing documentation
|
|
864
|
-
# TODO:
|
|
858
|
+
# TODO: Integration point - Secure document storage system
|
|
859
|
+
# RECOMMENDATION: Use cloud storage (AWS S3, Azure Blob, SharePoint) with:
|
|
860
|
+
# - Encryption at rest (AES-256) and in transit (TLS 1.2+)
|
|
861
|
+
# - HIPAA-compliant storage (BAA required)
|
|
862
|
+
# - Audit logging for all access
|
|
863
|
+
# - Retention policies per regulatory requirements
|
|
864
|
+
# For now, this example generates simulated URLs to demonstrate the workflow
|
|
865
865
|
doc_url = f"https://secure-docs.hospital.com/compliance/{state['execution_id']}"
|
|
866
866
|
doc_files = [
|
|
867
867
|
"compliance_summary_report.pdf",
|
|
@@ -879,8 +879,8 @@ def prepare_audit_documentation(state: ComplianceAgentState) -> ComplianceAgentS
|
|
|
879
879
|
# Message
|
|
880
880
|
state["messages"].append(
|
|
881
881
|
AIMessage(
|
|
882
|
-
content=f"📄 Documentation package prepared: {len(doc_files)} files ready at {doc_url}"
|
|
883
|
-
)
|
|
882
|
+
content=f"📄 Documentation package prepared: {len(doc_files)} files ready at {doc_url}",
|
|
883
|
+
),
|
|
884
884
|
)
|
|
885
885
|
|
|
886
886
|
# Audit trail
|
|
@@ -895,7 +895,7 @@ def prepare_audit_documentation(state: ComplianceAgentState) -> ComplianceAgentS
|
|
|
895
895
|
"readiness_score": doc_package["audit_readiness_score"],
|
|
896
896
|
},
|
|
897
897
|
"user": "system",
|
|
898
|
-
}
|
|
898
|
+
},
|
|
899
899
|
)
|
|
900
900
|
|
|
901
901
|
logger.info(f"Documentation prepared: {doc_url}")
|
|
@@ -906,8 +906,7 @@ def prepare_audit_documentation(state: ComplianceAgentState) -> ComplianceAgentS
|
|
|
906
906
|
def send_anticipatory_notifications(
|
|
907
907
|
state: ComplianceAgentState,
|
|
908
908
|
) -> ComplianceAgentState:
|
|
909
|
-
"""
|
|
910
|
-
Step 5: Send notifications to stakeholders with actionable information
|
|
909
|
+
"""Step 5: Send notifications to stakeholders with actionable information
|
|
911
910
|
|
|
912
911
|
For positive outcomes:
|
|
913
912
|
- Early notification enables calm, planned response
|
|
@@ -915,7 +914,6 @@ def send_anticipatory_notifications(
|
|
|
915
914
|
- Deadlines provide urgency without panic
|
|
916
915
|
- Transparent reasoning builds trust in AI system
|
|
917
916
|
"""
|
|
918
|
-
|
|
919
917
|
logger.info("[Step 5] Sending notifications to stakeholders")
|
|
920
918
|
|
|
921
919
|
# Create action items from gaps
|
|
@@ -939,7 +937,7 @@ def send_anticipatory_notifications(
|
|
|
939
937
|
"estimated_time": gap["estimated_time_to_fix"],
|
|
940
938
|
"status": "pending",
|
|
941
939
|
"created_at": datetime.now().isoformat(),
|
|
942
|
-
}
|
|
940
|
+
},
|
|
943
941
|
)
|
|
944
942
|
action_id += 1
|
|
945
943
|
|
|
@@ -956,7 +954,11 @@ def send_anticipatory_notifications(
|
|
|
956
954
|
notification = compose_notification(state, action_items)
|
|
957
955
|
|
|
958
956
|
# Send notification
|
|
959
|
-
# TODO:
|
|
957
|
+
# TODO: Integration point - Multi-channel notification delivery
|
|
958
|
+
# IMPLEMENTATION AVAILABLE: See agents/notifications.py NotificationService class
|
|
959
|
+
# Use: notification_service.send_compliance_alert(severity, title, description, recipients)
|
|
960
|
+
# Supports Email (SMTP), Slack (webhooks), and SMS (Twilio) with graceful fallback
|
|
961
|
+
# For now, this example simulates notification to demonstrate stakeholder communication
|
|
960
962
|
send_notification_to_recipients(notification, recipients, state["hospital_id"])
|
|
961
963
|
|
|
962
964
|
state["notification_sent"] = True
|
|
@@ -967,8 +969,8 @@ def send_anticipatory_notifications(
|
|
|
967
969
|
# Message
|
|
968
970
|
state["messages"].append(
|
|
969
971
|
AIMessage(
|
|
970
|
-
content=f"📧 Notifications sent to {len(recipients)} recipients: {', '.join(recipients)}"
|
|
971
|
-
)
|
|
972
|
+
content=f"📧 Notifications sent to {len(recipients)} recipients: {', '.join(recipients)}",
|
|
973
|
+
),
|
|
972
974
|
)
|
|
973
975
|
|
|
974
976
|
# Audit trail
|
|
@@ -982,7 +984,7 @@ def send_anticipatory_notifications(
|
|
|
982
984
|
"action_item_count": len(action_items),
|
|
983
985
|
},
|
|
984
986
|
"user": "system",
|
|
985
|
-
}
|
|
987
|
+
},
|
|
986
988
|
)
|
|
987
989
|
|
|
988
990
|
logger.info(f"Notifications sent to: {recipients}")
|
|
@@ -991,8 +993,7 @@ def send_anticipatory_notifications(
|
|
|
991
993
|
|
|
992
994
|
|
|
993
995
|
def schedule_continuous_monitoring(state: ComplianceAgentState) -> ComplianceAgentState:
|
|
994
|
-
"""
|
|
995
|
-
Step 6: Schedule periodic re-checks until audit
|
|
996
|
+
"""Step 6: Schedule periodic re-checks until audit
|
|
996
997
|
|
|
997
998
|
For positive outcomes:
|
|
998
999
|
- Regular monitoring tracks progress toward compliance
|
|
@@ -1000,7 +1001,6 @@ def schedule_continuous_monitoring(state: ComplianceAgentState) -> ComplianceAge
|
|
|
1000
1001
|
- Trend analysis predicts audit readiness
|
|
1001
1002
|
- Automated reminders keep team accountable
|
|
1002
1003
|
"""
|
|
1003
|
-
|
|
1004
1004
|
logger.info("[Step 6] Scheduling continuous monitoring")
|
|
1005
1005
|
|
|
1006
1006
|
days_until = state["days_until_audit"]
|
|
@@ -1031,8 +1031,8 @@ def schedule_continuous_monitoring(state: ComplianceAgentState) -> ComplianceAge
|
|
|
1031
1031
|
state["messages"].append(
|
|
1032
1032
|
AIMessage(
|
|
1033
1033
|
content=f"⏰ Scheduled {frequency} monitoring until {state['next_audit_date']} "
|
|
1034
|
-
f"(next check: {next_check_date[:10]})"
|
|
1035
|
-
)
|
|
1034
|
+
f"(next check: {next_check_date[:10]})",
|
|
1035
|
+
),
|
|
1036
1036
|
)
|
|
1037
1037
|
|
|
1038
1038
|
# Audit trail
|
|
@@ -1047,7 +1047,7 @@ def schedule_continuous_monitoring(state: ComplianceAgentState) -> ComplianceAge
|
|
|
1047
1047
|
"until_date": state["next_audit_date"],
|
|
1048
1048
|
},
|
|
1049
1049
|
"user": "system",
|
|
1050
|
-
}
|
|
1050
|
+
},
|
|
1051
1051
|
)
|
|
1052
1052
|
|
|
1053
1053
|
logger.info(f"Monitoring scheduled: {frequency} until {state['next_audit_date']}")
|
|
@@ -1061,12 +1061,10 @@ def schedule_continuous_monitoring(state: ComplianceAgentState) -> ComplianceAge
|
|
|
1061
1061
|
|
|
1062
1062
|
|
|
1063
1063
|
def get_audit_requirements(audit_type: str) -> list[dict]:
|
|
1064
|
-
"""
|
|
1065
|
-
Get compliance requirements for audit type
|
|
1064
|
+
"""Get compliance requirements for audit type
|
|
1066
1065
|
|
|
1067
1066
|
TODO: Load from database or configuration file
|
|
1068
1067
|
"""
|
|
1069
|
-
|
|
1070
1068
|
# Example requirements for Joint Commission
|
|
1071
1069
|
if audit_type == "joint_commission":
|
|
1072
1070
|
return [
|
|
@@ -1118,15 +1116,13 @@ def get_audit_requirements(audit_type: str) -> list[dict]:
|
|
|
1118
1116
|
|
|
1119
1117
|
|
|
1120
1118
|
def calculate_audit_readiness_score(state: ComplianceAgentState) -> float:
|
|
1121
|
-
"""
|
|
1122
|
-
Calculate overall audit readiness score (0-100)
|
|
1119
|
+
"""Calculate overall audit readiness score (0-100)
|
|
1123
1120
|
|
|
1124
1121
|
Factors:
|
|
1125
1122
|
- Compliance percentage (60% weight)
|
|
1126
1123
|
- Gap severity (20% weight)
|
|
1127
1124
|
- Time remaining (20% weight)
|
|
1128
1125
|
"""
|
|
1129
|
-
|
|
1130
1126
|
# Factor 1: Compliance percentage
|
|
1131
1127
|
compliance_score = state["compliance_percentage"]
|
|
1132
1128
|
|
|
@@ -1163,47 +1159,40 @@ def calculate_audit_readiness_score(state: ComplianceAgentState) -> float:
|
|
|
1163
1159
|
|
|
1164
1160
|
|
|
1165
1161
|
def determine_assignee(gap: dict) -> str:
|
|
1166
|
-
"""
|
|
1167
|
-
Determine who should be assigned to fix this gap
|
|
1162
|
+
"""Determine who should be assigned to fix this gap
|
|
1168
1163
|
|
|
1169
1164
|
Based on category and severity
|
|
1170
1165
|
"""
|
|
1171
|
-
|
|
1172
1166
|
category = gap["category"]
|
|
1173
1167
|
severity = gap["severity"]
|
|
1174
1168
|
|
|
1175
1169
|
if category == "medication_safety":
|
|
1176
1170
|
if severity == "critical":
|
|
1177
1171
|
return "nurse_manager" # Manager handles critical safety issues
|
|
1178
|
-
|
|
1179
|
-
return "charge_nurse"
|
|
1172
|
+
return "charge_nurse"
|
|
1180
1173
|
|
|
1181
|
-
|
|
1174
|
+
if category == "documentation":
|
|
1182
1175
|
return "charge_nurse" # Charge nurse coordinates documentation fixes
|
|
1183
1176
|
|
|
1184
|
-
|
|
1177
|
+
if category == "patient_safety":
|
|
1185
1178
|
if "restraint" in gap["item"].lower():
|
|
1186
1179
|
return "provider" # Restraint orders require provider
|
|
1187
|
-
|
|
1188
|
-
return "charge_nurse"
|
|
1180
|
+
return "charge_nurse"
|
|
1189
1181
|
|
|
1190
|
-
|
|
1182
|
+
if category == "infection_control":
|
|
1191
1183
|
return "infection_control_nurse"
|
|
1192
1184
|
|
|
1193
|
-
|
|
1194
|
-
return "charge_nurse" # Default
|
|
1185
|
+
return "charge_nurse" # Default
|
|
1195
1186
|
|
|
1196
1187
|
|
|
1197
1188
|
def calculate_deadline(gap: dict, days_until_audit: int, audit_date: str) -> str:
|
|
1198
|
-
"""
|
|
1199
|
-
Calculate appropriate deadline for fixing gap
|
|
1189
|
+
"""Calculate appropriate deadline for fixing gap
|
|
1200
1190
|
|
|
1201
1191
|
- Critical: 1 week or 25% of time remaining (whichever is sooner)
|
|
1202
1192
|
- High: 2 weeks or 50% of time remaining
|
|
1203
1193
|
- Medium: 1 month or 75% of time remaining
|
|
1204
1194
|
- Low: 2 weeks before audit
|
|
1205
1195
|
"""
|
|
1206
|
-
|
|
1207
1196
|
severity = gap["severity"]
|
|
1208
1197
|
|
|
1209
1198
|
if severity == "critical":
|
|
@@ -1228,12 +1217,10 @@ def calculate_deadline(gap: dict, days_until_audit: int, audit_date: str) -> str
|
|
|
1228
1217
|
|
|
1229
1218
|
|
|
1230
1219
|
def get_assignee_email(assignee: str, hospital_id: str) -> str:
|
|
1231
|
-
"""
|
|
1232
|
-
Get email address for assignee
|
|
1220
|
+
"""Get email address for assignee
|
|
1233
1221
|
|
|
1234
1222
|
TODO: Look up from hospital staff database
|
|
1235
1223
|
"""
|
|
1236
|
-
|
|
1237
1224
|
# Example mapping
|
|
1238
1225
|
email_map = {
|
|
1239
1226
|
"charge_nurse": f"charge.nurse@{hospital_id}.hospital.com",
|
|
@@ -1247,10 +1234,7 @@ def get_assignee_email(assignee: str, hospital_id: str) -> str:
|
|
|
1247
1234
|
|
|
1248
1235
|
|
|
1249
1236
|
def compose_notification(state: ComplianceAgentState, action_items: list[dict]) -> dict:
|
|
1250
|
-
"""
|
|
1251
|
-
Compose notification with all relevant information
|
|
1252
|
-
"""
|
|
1253
|
-
|
|
1237
|
+
"""Compose notification with all relevant information"""
|
|
1254
1238
|
days_until = state["days_until_audit"]
|
|
1255
1239
|
compliance_pct = state["compliance_percentage"]
|
|
1256
1240
|
gaps = state["compliance_gaps"]
|
|
@@ -1340,16 +1324,26 @@ without stress or rushed work during audit week.
|
|
|
1340
1324
|
|
|
1341
1325
|
|
|
1342
1326
|
def send_notification_to_recipients(notification: dict, recipients: list[str], hospital_id: str):
|
|
1343
|
-
"""
|
|
1344
|
-
|
|
1327
|
+
"""Send notification via configured channels
|
|
1328
|
+
|
|
1329
|
+
TODO: Integration point - Multi-channel notification delivery
|
|
1330
|
+
IMPLEMENTATION AVAILABLE: See agents/notifications.py NotificationService class
|
|
1331
|
+
|
|
1332
|
+
Supported channels (already implemented):
|
|
1333
|
+
- Email (SMTP) - agents/notifications.py:send_email()
|
|
1334
|
+
- SMS (Twilio) - agents/notifications.py:send_sms()
|
|
1335
|
+
- Slack/Teams (webhooks) - agents/notifications.py:send_slack()
|
|
1336
|
+
|
|
1337
|
+
For compliance alerts, use:
|
|
1338
|
+
notification_service.send_compliance_alert(
|
|
1339
|
+
severity=notification["severity"],
|
|
1340
|
+
title=notification["title"],
|
|
1341
|
+
description=notification["summary"],
|
|
1342
|
+
recipients={"email": [...], "phone": [...]},
|
|
1343
|
+
)
|
|
1345
1344
|
|
|
1346
|
-
|
|
1347
|
-
- Email (SMTP)
|
|
1348
|
-
- SMS (Twilio)
|
|
1349
|
-
- Slack/Teams (webhooks)
|
|
1350
|
-
- In-app notifications
|
|
1345
|
+
This example simulates notifications to demonstrate the workflow.
|
|
1351
1346
|
"""
|
|
1352
|
-
|
|
1353
1347
|
logger.info(f"Sending notification to: {recipients}")
|
|
1354
1348
|
|
|
1355
1349
|
# Simulate sending
|
|
@@ -1369,10 +1363,10 @@ def send_notification_to_recipients(notification: dict, recipients: list[str], h
|
|
|
1369
1363
|
|
|
1370
1364
|
|
|
1371
1365
|
async def run_compliance_agent(
|
|
1372
|
-
hospital_id: str,
|
|
1366
|
+
hospital_id: str,
|
|
1367
|
+
audit_type: str = "joint_commission",
|
|
1373
1368
|
) -> ComplianceAgentState:
|
|
1374
|
-
"""
|
|
1375
|
-
Run the compliance anticipation agent
|
|
1369
|
+
"""Run the compliance anticipation agent
|
|
1376
1370
|
|
|
1377
1371
|
Args:
|
|
1378
1372
|
hospital_id: Hospital/facility identifier
|
|
@@ -1380,8 +1374,8 @@ async def run_compliance_agent(
|
|
|
1380
1374
|
|
|
1381
1375
|
Returns:
|
|
1382
1376
|
Final agent state with all results
|
|
1383
|
-
"""
|
|
1384
1377
|
|
|
1378
|
+
"""
|
|
1385
1379
|
logger.info(f"Starting Compliance Anticipation Agent for {hospital_id} ({audit_type})")
|
|
1386
1380
|
|
|
1387
1381
|
# Create agent
|
|
@@ -1395,7 +1389,7 @@ async def run_compliance_agent(
|
|
|
1395
1389
|
|
|
1396
1390
|
logger.info(
|
|
1397
1391
|
f"Agent completed. Audit readiness score: "
|
|
1398
|
-
f"{final_state.get('documentation_package', {}).get('audit_readiness_score', 0):.1f}"
|
|
1392
|
+
f"{final_state.get('documentation_package', {}).get('audit_readiness_score', 0):.1f}",
|
|
1399
1393
|
)
|
|
1400
1394
|
|
|
1401
1395
|
return final_state
|
|
@@ -1407,7 +1401,8 @@ if __name__ == "__main__":
|
|
|
1407
1401
|
# Example usage
|
|
1408
1402
|
async def main():
|
|
1409
1403
|
result = await run_compliance_agent(
|
|
1410
|
-
hospital_id="example_hospital_123",
|
|
1404
|
+
hospital_id="example_hospital_123",
|
|
1405
|
+
audit_type="joint_commission",
|
|
1411
1406
|
)
|
|
1412
1407
|
|
|
1413
1408
|
print("\n" + "=" * 80)
|
|
@@ -1420,7 +1415,7 @@ if __name__ == "__main__":
|
|
|
1420
1415
|
print(f"Action Items: {len(result['action_items'])}")
|
|
1421
1416
|
print(f"Documentation: {result['documentation_url']}")
|
|
1422
1417
|
print(
|
|
1423
|
-
f"Audit Readiness: {result['documentation_package']['audit_readiness_score']:.1f}/100"
|
|
1418
|
+
f"Audit Readiness: {result['documentation_package']['audit_readiness_score']:.1f}/100",
|
|
1424
1419
|
)
|
|
1425
1420
|
print("=" * 80)
|
|
1426
1421
|
|