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.
Files changed (72) hide show
  1. agents/compliance_anticipation_agent.py +113 -118
  2. agents/compliance_db.py +339 -0
  3. agents/epic_integration_wizard.py +37 -48
  4. agents/notifications.py +291 -0
  5. agents/trust_building_behaviors.py +66 -85
  6. coach_wizards/__init__.py +11 -12
  7. coach_wizards/accessibility_wizard.py +12 -12
  8. coach_wizards/api_wizard.py +12 -12
  9. coach_wizards/base_wizard.py +26 -20
  10. coach_wizards/cicd_wizard.py +15 -13
  11. coach_wizards/compliance_wizard.py +12 -12
  12. coach_wizards/database_wizard.py +12 -12
  13. coach_wizards/debugging_wizard.py +12 -12
  14. coach_wizards/documentation_wizard.py +12 -12
  15. coach_wizards/generate_wizards.py +1 -2
  16. coach_wizards/localization_wizard.py +21 -14
  17. coach_wizards/migration_wizard.py +12 -12
  18. coach_wizards/monitoring_wizard.py +12 -12
  19. coach_wizards/observability_wizard.py +12 -12
  20. coach_wizards/performance_wizard.py +12 -12
  21. coach_wizards/prompt_engineering_wizard.py +22 -25
  22. coach_wizards/refactoring_wizard.py +12 -12
  23. coach_wizards/scaling_wizard.py +12 -12
  24. coach_wizards/security_wizard.py +12 -12
  25. coach_wizards/testing_wizard.py +12 -12
  26. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/METADATA +234 -30
  27. empathy_framework-3.7.0.dist-info/RECORD +105 -0
  28. empathy_healthcare_plugin/__init__.py +1 -2
  29. empathy_llm_toolkit/__init__.py +5 -6
  30. empathy_llm_toolkit/claude_memory.py +14 -15
  31. empathy_llm_toolkit/code_health.py +27 -19
  32. empathy_llm_toolkit/contextual_patterns.py +11 -12
  33. empathy_llm_toolkit/core.py +43 -49
  34. empathy_llm_toolkit/git_pattern_extractor.py +16 -12
  35. empathy_llm_toolkit/levels.py +6 -13
  36. empathy_llm_toolkit/pattern_confidence.py +14 -18
  37. empathy_llm_toolkit/pattern_resolver.py +10 -12
  38. empathy_llm_toolkit/pattern_summary.py +13 -11
  39. empathy_llm_toolkit/providers.py +27 -38
  40. empathy_llm_toolkit/session_status.py +18 -20
  41. empathy_llm_toolkit/state.py +20 -21
  42. empathy_os/__init__.py +72 -73
  43. empathy_os/cli.py +193 -98
  44. empathy_os/cli_unified.py +68 -41
  45. empathy_os/config.py +31 -31
  46. empathy_os/coordination.py +48 -54
  47. empathy_os/core.py +90 -99
  48. empathy_os/cost_tracker.py +20 -23
  49. empathy_os/discovery.py +9 -11
  50. empathy_os/emergence.py +20 -21
  51. empathy_os/exceptions.py +18 -30
  52. empathy_os/feedback_loops.py +27 -30
  53. empathy_os/levels.py +31 -34
  54. empathy_os/leverage_points.py +27 -28
  55. empathy_os/logging_config.py +11 -12
  56. empathy_os/monitoring.py +27 -27
  57. empathy_os/pattern_library.py +29 -28
  58. empathy_os/persistence.py +30 -34
  59. empathy_os/platform_utils.py +46 -47
  60. empathy_os/redis_config.py +14 -15
  61. empathy_os/redis_memory.py +53 -56
  62. empathy_os/templates.py +12 -11
  63. empathy_os/trust_building.py +44 -36
  64. empathy_os/workflow_commands.py +123 -31
  65. empathy_software_plugin/__init__.py +1 -2
  66. empathy_software_plugin/cli.py +32 -25
  67. empathy_software_plugin/plugin.py +4 -8
  68. empathy_framework-3.5.6.dist-info/RECORD +0 -103
  69. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/WHEEL +0 -0
  70. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/entry_points.txt +0 -0
  71. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/licenses/LICENSE +0 -0
  72. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,339 @@
1
+ """Compliance Database with Append-Only Architecture.
2
+
3
+ Provides immutable audit trail for healthcare compliance tracking.
4
+ Supports INSERT operations only (no UPDATE/DELETE) for regulatory compliance.
5
+
6
+ Copyright 2025 Smart-AI-Memory
7
+ Licensed under Fair Source License 0.9
8
+ """
9
+
10
+ import sqlite3
11
+ from collections.abc import Generator
12
+ from contextlib import contextmanager
13
+ from datetime import datetime
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+
18
+ class ComplianceDatabase:
19
+ """SQLite database for compliance tracking with append-only operations.
20
+
21
+ Features:
22
+ - Immutable audit trail (INSERT only, no UPDATE/DELETE)
23
+ - Audit date tracking
24
+ - Compliance status monitoring
25
+ - Gap detection and recording
26
+ - Thread-safe operations
27
+
28
+ Regulatory Compliance:
29
+ - Append-only design satisfies HIPAA audit log requirements
30
+ - No modification of historical records
31
+ - Complete audit trail preservation
32
+ """
33
+
34
+ def __init__(self, db_path: str | None = None):
35
+ """Initialize compliance database.
36
+
37
+ Args:
38
+ db_path: Path to SQLite database file.
39
+ Defaults to agents/data/compliance.db
40
+ """
41
+ if db_path is None:
42
+ # Default to agents/data/compliance.db
43
+ agents_dir = Path(__file__).parent
44
+ data_dir = agents_dir / "data"
45
+ data_dir.mkdir(exist_ok=True)
46
+ db_path = str(data_dir / "compliance.db")
47
+
48
+ self.db_path = db_path
49
+ self._init_schema()
50
+
51
+ @contextmanager
52
+ def _get_connection(self) -> Generator[sqlite3.Connection, None, None]:
53
+ """Get database connection with automatic cleanup."""
54
+ conn = sqlite3.connect(self.db_path, check_same_thread=False)
55
+ conn.row_factory = sqlite3.Row # Enable dict-like access
56
+ try:
57
+ yield conn
58
+ conn.commit()
59
+ except Exception:
60
+ conn.rollback()
61
+ raise
62
+ finally:
63
+ conn.close()
64
+
65
+ def _init_schema(self) -> None:
66
+ """Initialize database schema if not exists."""
67
+ with self._get_connection() as conn:
68
+ conn.executescript(
69
+ """
70
+ CREATE TABLE IF NOT EXISTS compliance_audits (
71
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
72
+ audit_date TIMESTAMP NOT NULL,
73
+ audit_type TEXT NOT NULL, -- 'HIPAA', 'GDPR', 'SOC2', etc.
74
+ findings TEXT, -- JSON string of findings
75
+ risk_score INTEGER, -- 0-100
76
+ auditor TEXT, -- Who performed the audit
77
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
78
+ -- No updated_at field (immutable records)
79
+ );
80
+
81
+ CREATE TABLE IF NOT EXISTS compliance_gaps (
82
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
83
+ gap_type TEXT NOT NULL, -- 'missing_policy', 'expired_cert', etc.
84
+ severity TEXT NOT NULL, -- 'critical', 'high', 'medium', 'low'
85
+ detected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
86
+ description TEXT,
87
+ affected_systems TEXT, -- JSON string of affected systems
88
+ compliance_framework TEXT, -- 'HIPAA', 'GDPR', etc.
89
+ detection_source TEXT -- 'automated_scan', 'manual_review', etc.
90
+ -- No status field (can't mark as "fixed", only add new record showing fix)
91
+ );
92
+
93
+ CREATE TABLE IF NOT EXISTS compliance_status (
94
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
95
+ compliance_framework TEXT NOT NULL, -- 'HIPAA', 'GDPR', 'SOC2', etc.
96
+ status TEXT NOT NULL, -- 'compliant', 'non_compliant', 'pending'
97
+ effective_date TIMESTAMP NOT NULL,
98
+ notes TEXT,
99
+ recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
100
+ );
101
+
102
+ CREATE INDEX IF NOT EXISTS idx_audits_date ON compliance_audits(audit_date DESC);
103
+ CREATE INDEX IF NOT EXISTS idx_gaps_severity ON compliance_gaps(severity, detected_at DESC);
104
+ CREATE INDEX IF NOT EXISTS idx_status_framework ON compliance_status(compliance_framework, effective_date DESC);
105
+ """
106
+ )
107
+
108
+ def record_audit(
109
+ self,
110
+ audit_date: datetime,
111
+ audit_type: str,
112
+ findings: str | None = None,
113
+ risk_score: int | None = None,
114
+ auditor: str | None = None,
115
+ ) -> int:
116
+ """Record a compliance audit (append-only).
117
+
118
+ Args:
119
+ audit_date: When the audit was performed
120
+ audit_type: Type of audit ('HIPAA', 'GDPR', 'SOC2', etc.)
121
+ findings: JSON string of audit findings
122
+ risk_score: Risk score 0-100
123
+ auditor: Who performed the audit
124
+
125
+ Returns:
126
+ Audit record ID
127
+
128
+ Note:
129
+ This is an append-only operation. Cannot modify existing audits.
130
+ """
131
+ with self._get_connection() as conn:
132
+ cursor = conn.execute(
133
+ """
134
+ INSERT INTO compliance_audits (audit_date, audit_type, findings, risk_score, auditor)
135
+ VALUES (?, ?, ?, ?, ?)
136
+ """,
137
+ (audit_date, audit_type, findings, risk_score, auditor),
138
+ )
139
+ return cursor.lastrowid
140
+
141
+ def get_last_audit(self, audit_type: str | None = None) -> dict[str, Any] | None:
142
+ """Get most recent audit record (read-only).
143
+
144
+ Args:
145
+ audit_type: Optional filter by audit type
146
+
147
+ Returns:
148
+ Audit record dict or None if no audits found
149
+ """
150
+ with self._get_connection() as conn:
151
+ if audit_type:
152
+ cursor = conn.execute(
153
+ """
154
+ SELECT * FROM compliance_audits
155
+ WHERE audit_type = ?
156
+ ORDER BY audit_date DESC
157
+ LIMIT 1
158
+ """,
159
+ (audit_type,),
160
+ )
161
+ else:
162
+ cursor = conn.execute(
163
+ """
164
+ SELECT * FROM compliance_audits
165
+ ORDER BY audit_date DESC
166
+ LIMIT 1
167
+ """
168
+ )
169
+
170
+ row = cursor.fetchone()
171
+ if row is None:
172
+ return None
173
+
174
+ return {
175
+ "id": row["id"],
176
+ "audit_date": row["audit_date"],
177
+ "audit_type": row["audit_type"],
178
+ "findings": row["findings"],
179
+ "risk_score": row["risk_score"],
180
+ "auditor": row["auditor"],
181
+ "created_at": row["created_at"],
182
+ }
183
+
184
+ def record_gap(
185
+ self,
186
+ gap_type: str,
187
+ severity: str,
188
+ description: str | None = None,
189
+ affected_systems: str | None = None,
190
+ compliance_framework: str | None = None,
191
+ detection_source: str = "automated_scan",
192
+ ) -> int:
193
+ """Record a compliance gap (append-only).
194
+
195
+ Args:
196
+ gap_type: Type of gap ('missing_policy', 'expired_cert', etc.)
197
+ severity: Severity level ('critical', 'high', 'medium', 'low')
198
+ description: Human-readable description
199
+ affected_systems: JSON string of affected systems
200
+ compliance_framework: Related framework ('HIPAA', 'GDPR', etc.)
201
+ detection_source: How gap was detected
202
+
203
+ Returns:
204
+ Gap record ID
205
+
206
+ Note:
207
+ This is an append-only operation. To mark a gap as fixed,
208
+ add a new status record, don't modify this one.
209
+ """
210
+ with self._get_connection() as conn:
211
+ cursor = conn.execute(
212
+ """
213
+ INSERT INTO compliance_gaps (
214
+ gap_type, severity, description, affected_systems,
215
+ compliance_framework, detection_source
216
+ )
217
+ VALUES (?, ?, ?, ?, ?, ?)
218
+ """,
219
+ (
220
+ gap_type,
221
+ severity,
222
+ description,
223
+ affected_systems,
224
+ compliance_framework,
225
+ detection_source,
226
+ ),
227
+ )
228
+ return cursor.lastrowid
229
+
230
+ def get_active_gaps(
231
+ self, severity: str | None = None, framework: str | None = None
232
+ ) -> list[dict[str, Any]]:
233
+ """Get all recorded gaps (read-only).
234
+
235
+ Args:
236
+ severity: Optional filter by severity
237
+ framework: Optional filter by compliance framework
238
+
239
+ Returns:
240
+ List of gap records
241
+
242
+ Note:
243
+ Returns all gaps. In append-only design, gaps are never deleted.
244
+ To track fixes, use separate status records.
245
+ """
246
+ with self._get_connection() as conn:
247
+ query = "SELECT * FROM compliance_gaps WHERE 1=1"
248
+ params: list[Any] = []
249
+
250
+ if severity:
251
+ query += " AND severity = ?"
252
+ params.append(severity)
253
+
254
+ if framework:
255
+ query += " AND compliance_framework = ?"
256
+ params.append(framework)
257
+
258
+ query += " ORDER BY detected_at DESC"
259
+
260
+ cursor = conn.execute(query, params)
261
+ rows = cursor.fetchall()
262
+
263
+ return [
264
+ {
265
+ "id": row["id"],
266
+ "gap_type": row["gap_type"],
267
+ "severity": row["severity"],
268
+ "detected_at": row["detected_at"],
269
+ "description": row["description"],
270
+ "affected_systems": row["affected_systems"],
271
+ "compliance_framework": row["compliance_framework"],
272
+ "detection_source": row["detection_source"],
273
+ }
274
+ for row in rows
275
+ ]
276
+
277
+ def record_compliance_status(
278
+ self,
279
+ compliance_framework: str,
280
+ status: str,
281
+ effective_date: datetime,
282
+ notes: str | None = None,
283
+ ) -> int:
284
+ """Record compliance status change (append-only).
285
+
286
+ Args:
287
+ compliance_framework: Framework name ('HIPAA', 'GDPR', 'SOC2', etc.)
288
+ status: Status ('compliant', 'non_compliant', 'pending')
289
+ effective_date: When this status became effective
290
+ notes: Additional notes
291
+
292
+ Returns:
293
+ Status record ID
294
+
295
+ Note:
296
+ This is an append-only operation. Status history is preserved.
297
+ """
298
+ with self._get_connection() as conn:
299
+ cursor = conn.execute(
300
+ """
301
+ INSERT INTO compliance_status (compliance_framework, status, effective_date, notes)
302
+ VALUES (?, ?, ?, ?)
303
+ """,
304
+ (compliance_framework, status, effective_date, notes),
305
+ )
306
+ return cursor.lastrowid
307
+
308
+ def get_current_compliance_status(self, compliance_framework: str) -> dict[str, Any] | None:
309
+ """Get most recent compliance status (read-only).
310
+
311
+ Args:
312
+ compliance_framework: Framework name
313
+
314
+ Returns:
315
+ Status record or None
316
+ """
317
+ with self._get_connection() as conn:
318
+ cursor = conn.execute(
319
+ """
320
+ SELECT * FROM compliance_status
321
+ WHERE compliance_framework = ?
322
+ ORDER BY effective_date DESC, recorded_at DESC
323
+ LIMIT 1
324
+ """,
325
+ (compliance_framework,),
326
+ )
327
+
328
+ row = cursor.fetchone()
329
+ if row is None:
330
+ return None
331
+
332
+ return {
333
+ "id": row["id"],
334
+ "compliance_framework": row["compliance_framework"],
335
+ "status": row["status"],
336
+ "effective_date": row["effective_date"],
337
+ "notes": row["notes"],
338
+ "recorded_at": row["recorded_at"],
339
+ }
@@ -1,5 +1,4 @@
1
- """
2
- Epic Integration Wizard - LangChain Agent
1
+ """Epic Integration Wizard - LangChain Agent
3
2
  Multi-step wizard for configuring and testing Epic FHIR integration
4
3
 
5
4
  Copyright 2025 Smart AI Memory, LLC
@@ -26,8 +25,7 @@ logger = logging.getLogger(__name__)
26
25
 
27
26
 
28
27
  class WizardState(TypedDict):
29
- """
30
- State for Epic Integration Wizard
28
+ """State for Epic Integration Wizard
31
29
 
32
30
  Tracks all wizard steps, user inputs, validation results, and progress.
33
31
  Microsoft-style linear progression with validation at each step.
@@ -114,8 +112,7 @@ def create_initial_state() -> WizardState:
114
112
 
115
113
 
116
114
  async def step_1_prerequisites(state: WizardState) -> WizardState:
117
- """
118
- Step 1: Check Prerequisites
115
+ """Step 1: Check Prerequisites
119
116
 
120
117
  Validates system readiness:
121
118
  - Python version
@@ -142,7 +139,7 @@ async def step_1_prerequisites(state: WizardState) -> WizardState:
142
139
  if not settings.database_url:
143
140
  missing.append("Database configuration")
144
141
  except Exception as e:
145
- missing.append(f"Configuration error: {str(e)}")
142
+ missing.append(f"Configuration error: {e!s}")
146
143
 
147
144
  state["prerequisites_checked"] = True
148
145
  state["prerequisites_passed"] = len(missing) == 0
@@ -153,7 +150,7 @@ async def step_1_prerequisites(state: WizardState) -> WizardState:
153
150
  # Even if automated prerequisites fail, user can still configure Epic manually
154
151
  if state["prerequisites_passed"]:
155
152
  state["messages"].append(
156
- AIMessage(content="✅ All prerequisites met. Ready to configure Epic integration.")
153
+ AIMessage(content="✅ All prerequisites met. Ready to configure Epic integration."),
157
154
  )
158
155
  else:
159
156
  # Don't block progression - just warn user
@@ -164,8 +161,7 @@ async def step_1_prerequisites(state: WizardState) -> WizardState:
164
161
 
165
162
 
166
163
  async def step_2_credentials(state: WizardState) -> WizardState:
167
- """
168
- Step 2: Epic Credentials Input
164
+ """Step 2: Epic Credentials Input
169
165
 
170
166
  Collects and validates:
171
167
  - Epic Client ID
@@ -192,15 +188,14 @@ async def step_2_credentials(state: WizardState) -> WizardState:
192
188
  state["completed_steps"].append(2)
193
189
  state["current_step"] = 3
194
190
  state["messages"].append(
195
- AIMessage(content="✅ Epic credentials collected. Ready to test connection.")
191
+ AIMessage(content="✅ Epic credentials collected. Ready to test connection."),
196
192
  )
197
193
 
198
194
  return state
199
195
 
200
196
 
201
197
  async def step_3_connection_test(state: WizardState) -> WizardState:
202
- """
203
- Step 3: Test Epic FHIR Connection
198
+ """Step 3: Test Epic FHIR Connection
204
199
 
205
200
  Validates:
206
201
  - OAuth token can be obtained
@@ -226,13 +221,13 @@ async def step_3_connection_test(state: WizardState) -> WizardState:
226
221
  state["oauth_token_obtained"] = True
227
222
  state["connection_test_passed"] = True
228
223
  state["messages"].append(
229
- AIMessage(content="✅ Successfully connected to Epic FHIR API")
224
+ AIMessage(content="✅ Successfully connected to Epic FHIR API"),
230
225
  )
231
226
  except Exception as e:
232
227
  state["connection_test_error"] = str(e)
233
228
  state["connection_test_passed"] = False
234
- state["errors"].append(f"OAuth token request failed: {str(e)}")
235
- state["messages"].append(AIMessage(content=f"❌ Connection failed: {str(e)}"))
229
+ state["errors"].append(f"OAuth token request failed: {e!s}")
230
+ state["messages"].append(AIMessage(content=f"❌ Connection failed: {e!s}"))
236
231
  return state
237
232
 
238
233
  state["completed_steps"].append(3)
@@ -242,14 +237,13 @@ async def step_3_connection_test(state: WizardState) -> WizardState:
242
237
  logger.error(f"Connection test failed: {e}", exc_info=True)
243
238
  state["connection_test_error"] = str(e)
244
239
  state["connection_test_passed"] = False
245
- state["errors"].append(f"Connection test error: {str(e)}")
240
+ state["errors"].append(f"Connection test error: {e!s}")
246
241
 
247
242
  return state
248
243
 
249
244
 
250
245
  async def step_4_resource_permissions(state: WizardState) -> WizardState:
251
- """
252
- Step 4: Select FHIR Resources and Scopes
246
+ """Step 4: Select FHIR Resources and Scopes
253
247
 
254
248
  User selects which FHIR resources to enable:
255
249
  - Patient (demographics)
@@ -284,15 +278,14 @@ async def step_4_resource_permissions(state: WizardState) -> WizardState:
284
278
  state["completed_steps"].append(4)
285
279
  state["current_step"] = 5
286
280
  state["messages"].append(
287
- AIMessage(content=f"✅ Configured {len(scopes)} resource permissions: {', '.join(scopes)}")
281
+ AIMessage(content=f"✅ Configured {len(scopes)} resource permissions: {', '.join(scopes)}"),
288
282
  )
289
283
 
290
284
  return state
291
285
 
292
286
 
293
287
  async def step_5_test_patient_lookup(state: WizardState) -> WizardState:
294
- """
295
- Step 5: Test Patient Lookup
288
+ """Step 5: Test Patient Lookup
296
289
 
297
290
  Validates end-to-end functionality:
298
291
  - Search patient by MRN
@@ -316,7 +309,8 @@ async def step_5_test_patient_lookup(state: WizardState) -> WizardState:
316
309
  )
317
310
 
318
311
  epic_client = EpicFHIRClient(
319
- base_url=state["epic_fhir_base_url"], oauth_manager=oauth_manager
312
+ base_url=state["epic_fhir_base_url"],
313
+ oauth_manager=oauth_manager,
320
314
  )
321
315
 
322
316
  # Search for patient
@@ -332,13 +326,13 @@ async def step_5_test_patient_lookup(state: WizardState) -> WizardState:
332
326
  state["patient_data_retrieved"] = True
333
327
  state["retrieved_patient_name"] = patient_name
334
328
  state["messages"].append(
335
- AIMessage(content=f"✅ Successfully retrieved patient: {patient_name}")
329
+ AIMessage(content=f"✅ Successfully retrieved patient: {patient_name}"),
336
330
  )
337
331
  else:
338
332
  state["patient_test_error"] = "Patient not found"
339
333
  state["errors"].append(f"Patient with MRN {state['test_mrn']} not found")
340
334
  state["messages"].append(
341
- AIMessage(content=f"❌ Patient not found: MRN {state['test_mrn']}")
335
+ AIMessage(content=f"❌ Patient not found: MRN {state['test_mrn']}"),
342
336
  )
343
337
  return state
344
338
 
@@ -348,15 +342,14 @@ async def step_5_test_patient_lookup(state: WizardState) -> WizardState:
348
342
  except Exception as e:
349
343
  logger.error(f"Patient lookup failed: {e}", exc_info=True)
350
344
  state["patient_test_error"] = str(e)
351
- state["errors"].append(f"Patient lookup error: {str(e)}")
352
- state["messages"].append(AIMessage(content=f"❌ Patient lookup failed: {str(e)}"))
345
+ state["errors"].append(f"Patient lookup error: {e!s}")
346
+ state["messages"].append(AIMessage(content=f"❌ Patient lookup failed: {e!s}"))
353
347
 
354
348
  return state
355
349
 
356
350
 
357
351
  async def step_6_review_confirm(state: WizardState) -> WizardState:
358
- """
359
- Step 6: Review and Confirm Configuration
352
+ """Step 6: Review and Confirm Configuration
360
353
 
361
354
  Shows summary of all settings for final review:
362
355
  - Epic endpoint and credentials
@@ -390,15 +383,14 @@ Please confirm to activate integration.
390
383
  state["completed_steps"].append(6)
391
384
  state["current_step"] = 7
392
385
  state["messages"].append(
393
- AIMessage(content="✅ Configuration confirmed. Activating integration...")
386
+ AIMessage(content="✅ Configuration confirmed. Activating integration..."),
394
387
  )
395
388
 
396
389
  return state
397
390
 
398
391
 
399
392
  async def step_7_complete(state: WizardState) -> WizardState:
400
- """
401
- Step 7: Complete Integration Setup
393
+ """Step 7: Complete Integration Setup
402
394
 
403
395
  Finalizes configuration:
404
396
  - Saves settings to database/config
@@ -442,14 +434,14 @@ Next steps:
442
434
  1. Train your staff on the Epic integration workflow
443
435
  2. Review HIPAA compliance documentation
444
436
  3. Monitor integration logs for any issues
445
- """
446
- )
437
+ """,
438
+ ),
447
439
  )
448
440
 
449
441
  except Exception as e:
450
442
  logger.error(f"Integration activation failed: {e}", exc_info=True)
451
- state["errors"].append(f"Activation error: {str(e)}")
452
- state["messages"].append(AIMessage(content=f"❌ Activation failed: {str(e)}"))
443
+ state["errors"].append(f"Activation error: {e!s}")
444
+ state["messages"].append(AIMessage(content=f"❌ Activation failed: {e!s}"))
453
445
 
454
446
  return state
455
447
 
@@ -460,8 +452,7 @@ Next steps:
460
452
 
461
453
 
462
454
  def should_continue(state: WizardState) -> str:
463
- """
464
- Determine next step based on current state
455
+ """Determine next step based on current state
465
456
 
466
457
  Microsoft wizard pattern: linear progression with error handling
467
458
  """
@@ -482,20 +473,19 @@ def should_continue(state: WizardState) -> str:
482
473
 
483
474
  if current_step == 1:
484
475
  return "prerequisites"
485
- elif current_step == 2:
476
+ if current_step == 2:
486
477
  return "credentials"
487
- elif current_step == 3:
478
+ if current_step == 3:
488
479
  return "connection_test"
489
- elif current_step == 4:
480
+ if current_step == 4:
490
481
  return "resource_permissions"
491
- elif current_step == 5:
482
+ if current_step == 5:
492
483
  return "test_patient_lookup"
493
- elif current_step == 6:
484
+ if current_step == 6:
494
485
  return "review_confirm"
495
- elif current_step == 7:
486
+ if current_step == 7:
496
487
  return "complete"
497
- else:
498
- return END
488
+ return END
499
489
 
500
490
 
501
491
  # =============================================================================
@@ -504,8 +494,7 @@ def should_continue(state: WizardState) -> str:
504
494
 
505
495
 
506
496
  def create_epic_wizard_graph():
507
- """
508
- Create LangGraph workflow for Epic Integration Wizard
497
+ """Create LangGraph workflow for Epic Integration Wizard
509
498
 
510
499
  Microsoft-style multi-step wizard:
511
500
  Prerequisites → Credentials → Connection Test → Permissions → Patient Test → Review → Complete