1717
1818import logging
1919import operator
20+ from collections .abc import Sequence
2021from datetime import datetime , timedelta
21- from typing import Annotated , Dict , List , Sequence , TypedDict
22+ from typing import Annotated , TypedDict
2223
2324from langchain_core .messages import AIMessage , BaseMessage
2425from langgraph .graph import END , StateGraph
@@ -49,7 +50,7 @@ class ComplianceAgentState(TypedDict):
4950 # Progress Tracking
5051 # =========================================================================
5152 current_step : int # 1-5
52- completed_steps : List [int ]
53+ completed_steps : list [int ]
5354 messages : Annotated [Sequence [BaseMessage ], operator .add ]
5455
5556 # =========================================================================
@@ -82,20 +83,14 @@ class ComplianceAgentState(TypedDict):
8283 compliance_percentage : float # 0.0-100.0
8384
8485 # Item-Level Detail
85- compliance_categories : List [
86- str
87- ] # ["medication_safety", "documentation", "patient_safety"]
88- category_scores : Dict [
89- str , float
90- ] # {"medication_safety": 95.0, "documentation": 88.0}
86+ compliance_categories : list [str ] # ["medication_safety", "documentation", "patient_safety"]
87+ category_scores : dict [str , float ] # {"medication_safety": 95.0, "documentation": 88.0}
9188
9289 # =========================================================================
9390 # Gap Identification (Step 3) - Answers: "What needs fixing?"
9491 # =========================================================================
95- compliance_gaps : List [Dict ] # Detailed gap information
96- gap_severity_distribution : Dict [
97- str , int
98- ] # {"critical": 2, "high": 5, "medium": 10}
92+ compliance_gaps : list [dict ] # Detailed gap information
93+ gap_severity_distribution : dict [str , int ] # {"critical": 2, "high": 5, "medium": 10}
9994
10095 # Gap Structure:
10196 # {
@@ -117,10 +112,10 @@ class ComplianceAgentState(TypedDict):
117112 # =========================================================================
118113 documentation_prepared : bool
119114 documentation_url : str # Secure storage location
120- documentation_files : List [str ] # ["compliance_summary.pdf", "gap_analysis.xlsx"]
115+ documentation_files : list [str ] # ["compliance_summary.pdf", "gap_analysis.xlsx"]
121116
122117 # Documentation Package Contents:
123- documentation_package : Dict
118+ documentation_package : dict
124119 # {
125120 # "summary_report": {...},
126121 # "evidence_files": [...],
@@ -133,11 +128,11 @@ class ComplianceAgentState(TypedDict):
133128 # Stakeholder Notification (Step 5) - Answers: "Who needs to act?"
134129 # =========================================================================
135130 notification_sent : bool
136- notification_recipients : List [str ] # ["charge_nurse", "nurse_manager", "cno"]
131+ notification_recipients : list [str ] # ["charge_nurse", "nurse_manager", "cno"]
137132 notification_timestamp : str # ISO timestamp
138133
139134 # Action Items (Assigned Work)
140- action_items : List [ Dict ]
135+ action_items : list [ dict ]
141136 # {
142137 # "action_id": "action_001",
143138 # "gap_id": "gap_001",
@@ -175,11 +170,11 @@ class ComplianceAgentState(TypedDict):
175170 # =========================================================================
176171 # Error Handling & Audit Trail
177172 # =========================================================================
178- errors : List [str ]
179- warnings : List [str ]
173+ errors : list [str ]
174+ warnings : list [str ]
180175
181176 # Full Audit Trail
182- audit_trail : List [ Dict ]
177+ audit_trail : list [ dict ]
183178 # {
184179 # "timestamp": "2025-01-20T10:00:00Z",
185180 # "step": "predict_audit",
@@ -370,9 +365,7 @@ def predict_next_audit(state: ComplianceAgentState) -> ComplianceAgentState:
370365 - Allows time to fix gaps without pressure
371366 """
372367
373- logger .info (
374- f"[Step 1] Predicting { state ['audit_type' ]} audit for { state ['hospital_id' ]} "
375- )
368+ logger .info (f"[Step 1] Predicting { state ['audit_type' ]} audit for { state ['hospital_id' ]} " )
376369
377370 # Add to audit trail
378371 state ["audit_trail" ].append (
@@ -478,17 +471,17 @@ def check_anticipation_window(state: ComplianceAgentState) -> ComplianceAgentSta
478471 - Avoids wasted effort (too early) or crisis mode (too late)
479472 """
480473
481- logger .info (
482- f"[Step 2] Checking anticipation window ({ state ['days_until_audit' ]} days)"
483- )
474+ logger .info (f"[Step 2] Checking anticipation window ({ state ['days_until_audit' ]} days)" )
484475
485476 days_until = state ["days_until_audit" ]
486477
487478 if 60 <= days_until <= 120 :
488479 state ["is_within_anticipation_window" ] = True
489480 state ["time_to_act" ] = "timely"
490481 state ["anticipation_window_days" ] = days_until
491- message = f"✅ Within optimal anticipation window ({ days_until } days). Perfect time to prepare."
482+ message = (
483+ f"✅ Within optimal anticipation window ({ days_until } days). Perfect time to prepare."
484+ )
492485
493486 elif days_until > 120 :
494487 state ["is_within_anticipation_window" ] = False
@@ -626,9 +619,7 @@ def assess_current_compliance(state: ComplianceAgentState) -> ComplianceAgentSta
626619 category_pct = {}
627620 for cat , scores in category_scores .items ():
628621 category_pct [cat ] = (
629- (scores ["compliant" ] / scores ["total" ] * 100 )
630- if scores ["total" ] > 0
631- else 0.0
622+ (scores ["compliant" ] / scores ["total" ] * 100 ) if scores ["total" ] > 0 else 0.0
632623 )
633624
634625 # Update state
@@ -650,9 +641,7 @@ def assess_current_compliance(state: ComplianceAgentState) -> ComplianceAgentSta
650641 state ["last_updated" ] = datetime .now ().isoformat ()
651642
652643 # Message
653- status_emoji = (
654- "✅" if compliance_pct >= 95 else "⚠️" if compliance_pct >= 85 else "🚨"
655- )
644+ status_emoji = "✅" if compliance_pct >= 95 else "⚠️" if compliance_pct >= 85 else "🚨"
656645 state ["messages" ].append (
657646 AIMessage (
658647 content = f"{ status_emoji } Compliance Assessment: { compliance_pct :.1f} % "
@@ -801,9 +790,7 @@ def identify_compliance_gaps(state: ComplianceAgentState) -> ComplianceAgentStat
801790 "details" : {
802791 "gap_count" : len (gaps ),
803792 "severity_distribution" : severity_dist ,
804- "gaps" : [
805- {"id" : g ["gap_id" ], "description" : g ["description" ]} for g in gaps
806- ],
793+ "gaps" : [{"id" : g ["gap_id" ], "description" : g ["description" ]} for g in gaps ],
807794 },
808795 "user" : "system" ,
809796 }
@@ -937,9 +924,7 @@ def send_anticipatory_notifications(
937924
938925 for gap in state ["compliance_gaps" ]:
939926 assignee = determine_assignee (gap )
940- deadline = calculate_deadline (
941- gap , state ["days_until_audit" ], state ["next_audit_date" ]
942- )
927+ deadline = calculate_deadline (gap , state ["days_until_audit" ], state ["next_audit_date" ])
943928
944929 action_items .append (
945930 {
@@ -1075,7 +1060,7 @@ def schedule_continuous_monitoring(state: ComplianceAgentState) -> ComplianceAge
10751060# =============================================================================
10761061
10771062
1078- def get_audit_requirements (audit_type : str ) -> List [ Dict ]:
1063+ def get_audit_requirements (audit_type : str ) -> list [ dict ]:
10791064 """
10801065 Get compliance requirements for audit type
10811066
@@ -1177,7 +1162,7 @@ def calculate_audit_readiness_score(state: ComplianceAgentState) -> float:
11771162 return round (readiness_score , 1 )
11781163
11791164
1180- def determine_assignee (gap : Dict ) -> str :
1165+ def determine_assignee (gap : dict ) -> str :
11811166 """
11821167 Determine who should be assigned to fix this gap
11831168
@@ -1209,7 +1194,7 @@ def determine_assignee(gap: Dict) -> str:
12091194 return "charge_nurse" # Default
12101195
12111196
1212- def calculate_deadline (gap : Dict , days_until_audit : int , audit_date : str ) -> str :
1197+ def calculate_deadline (gap : dict , days_until_audit : int , audit_date : str ) -> str :
12131198 """
12141199 Calculate appropriate deadline for fixing gap
12151200
@@ -1261,7 +1246,7 @@ def get_assignee_email(assignee: str, hospital_id: str) -> str:
12611246 return email_map .get (assignee , f"{ assignee } @{ hospital_id } .hospital.com" )
12621247
12631248
1264- def compose_notification (state : ComplianceAgentState , action_items : List [ Dict ]) -> Dict :
1249+ def compose_notification (state : ComplianceAgentState , action_items : list [ dict ]) -> dict :
12651250 """
12661251 Compose notification with all relevant information
12671252 """
@@ -1290,9 +1275,7 @@ def compose_notification(state: ComplianceAgentState, action_items: List[Dict])
12901275
12911276**Category Breakdown:**
12921277"""
1293- + "\n " .join (
1294- f"- { cat } : { score :.1f} %" for cat , score in state ["category_scores" ].items ()
1295- )
1278+ + "\n " .join (f"- { cat } : { score :.1f} %" for cat , score in state ["category_scores" ].items ())
12961279 + f"""
12971280
12981281---
@@ -1314,11 +1297,7 @@ def compose_notification(state: ComplianceAgentState, action_items: List[Dict])
13141297 f" → Time: { item ['estimated_time' ]} "
13151298 for i , item in enumerate (action_items [:3 ])
13161299 )
1317- + (
1318- f"\n ... and { len (action_items ) - 3 } more action items"
1319- if len (action_items ) > 3
1320- else ""
1321- )
1300+ + (f"\n ... and { len (action_items ) - 3 } more action items" if len (action_items ) > 3 else "" )
13221301 + f"""
13231302
13241303---
@@ -1353,18 +1332,14 @@ def compose_notification(state: ComplianceAgentState, action_items: List[Dict])
13531332 "action_items" : action_items ,
13541333 "documentation_url" : state ["documentation_url" ],
13551334 "priority" : (
1356- "high"
1357- if state ["gap_severity_distribution" ].get ("critical" , 0 ) > 0
1358- else "medium"
1335+ "high" if state ["gap_severity_distribution" ].get ("critical" , 0 ) > 0 else "medium"
13591336 ),
13601337 }
13611338
13621339 return notification
13631340
13641341
1365- def send_notification_to_recipients (
1366- notification : Dict , recipients : List [str ], hospital_id : str
1367- ):
1342+ def send_notification_to_recipients (notification : dict , recipients : list [str ], hospital_id : str ):
13681343 """
13691344 Send notification via configured channels
13701345
@@ -1407,9 +1382,7 @@ async def run_compliance_agent(
14071382 Final agent state with all results
14081383 """
14091384
1410- logger .info (
1411- f"Starting Compliance Anticipation Agent for { hospital_id } ({ audit_type } )"
1412- )
1385+ logger .info (f"Starting Compliance Anticipation Agent for { hospital_id } ({ audit_type } )" )
14131386
14141387 # Create agent
14151388 agent = create_compliance_agent ()
0 commit comments