empathy-framework 4.6.6__py3-none-any.whl → 4.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 (247) hide show
  1. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/METADATA +7 -6
  2. empathy_framework-4.7.0.dist-info/RECORD +354 -0
  3. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/top_level.txt +0 -2
  4. empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
  5. empathy_llm_toolkit/agent_factory/__init__.py +6 -6
  6. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
  7. empathy_llm_toolkit/agents_md/__init__.py +22 -0
  8. empathy_llm_toolkit/agents_md/loader.py +218 -0
  9. empathy_llm_toolkit/agents_md/parser.py +271 -0
  10. empathy_llm_toolkit/agents_md/registry.py +307 -0
  11. empathy_llm_toolkit/commands/__init__.py +51 -0
  12. empathy_llm_toolkit/commands/context.py +375 -0
  13. empathy_llm_toolkit/commands/loader.py +301 -0
  14. empathy_llm_toolkit/commands/models.py +231 -0
  15. empathy_llm_toolkit/commands/parser.py +371 -0
  16. empathy_llm_toolkit/commands/registry.py +429 -0
  17. empathy_llm_toolkit/config/__init__.py +8 -8
  18. empathy_llm_toolkit/config/unified.py +3 -7
  19. empathy_llm_toolkit/context/__init__.py +22 -0
  20. empathy_llm_toolkit/context/compaction.py +455 -0
  21. empathy_llm_toolkit/context/manager.py +434 -0
  22. empathy_llm_toolkit/hooks/__init__.py +24 -0
  23. empathy_llm_toolkit/hooks/config.py +306 -0
  24. empathy_llm_toolkit/hooks/executor.py +289 -0
  25. empathy_llm_toolkit/hooks/registry.py +302 -0
  26. empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
  27. empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
  28. empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
  29. empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
  30. empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
  31. empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
  32. empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
  33. empathy_llm_toolkit/learning/__init__.py +30 -0
  34. empathy_llm_toolkit/learning/evaluator.py +438 -0
  35. empathy_llm_toolkit/learning/extractor.py +514 -0
  36. empathy_llm_toolkit/learning/storage.py +560 -0
  37. empathy_llm_toolkit/providers.py +4 -11
  38. empathy_llm_toolkit/security/__init__.py +17 -17
  39. empathy_llm_toolkit/utils/tokens.py +2 -5
  40. empathy_os/__init__.py +202 -70
  41. empathy_os/cache_monitor.py +5 -3
  42. empathy_os/cli/__init__.py +11 -55
  43. empathy_os/cli/__main__.py +29 -15
  44. empathy_os/cli/commands/inspection.py +21 -12
  45. empathy_os/cli/commands/memory.py +4 -12
  46. empathy_os/cli/commands/profiling.py +198 -0
  47. empathy_os/cli/commands/utilities.py +27 -7
  48. empathy_os/cli.py +28 -57
  49. empathy_os/cli_unified.py +525 -1164
  50. empathy_os/cost_tracker.py +9 -3
  51. empathy_os/dashboard/server.py +200 -2
  52. empathy_os/hot_reload/__init__.py +7 -7
  53. empathy_os/hot_reload/config.py +6 -7
  54. empathy_os/hot_reload/integration.py +35 -35
  55. empathy_os/hot_reload/reloader.py +57 -57
  56. empathy_os/hot_reload/watcher.py +28 -28
  57. empathy_os/hot_reload/websocket.py +2 -2
  58. empathy_os/memory/__init__.py +11 -4
  59. empathy_os/memory/claude_memory.py +1 -1
  60. empathy_os/memory/cross_session.py +8 -12
  61. empathy_os/memory/edges.py +6 -6
  62. empathy_os/memory/file_session.py +770 -0
  63. empathy_os/memory/graph.py +30 -30
  64. empathy_os/memory/nodes.py +6 -6
  65. empathy_os/memory/short_term.py +15 -9
  66. empathy_os/memory/unified.py +606 -140
  67. empathy_os/meta_workflows/agent_creator.py +3 -9
  68. empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
  69. empathy_os/meta_workflows/form_engine.py +6 -18
  70. empathy_os/meta_workflows/intent_detector.py +64 -24
  71. empathy_os/meta_workflows/models.py +3 -1
  72. empathy_os/meta_workflows/pattern_learner.py +13 -31
  73. empathy_os/meta_workflows/plan_generator.py +55 -47
  74. empathy_os/meta_workflows/session_context.py +2 -3
  75. empathy_os/meta_workflows/workflow.py +20 -51
  76. empathy_os/models/cli.py +2 -2
  77. empathy_os/models/tasks.py +1 -2
  78. empathy_os/models/telemetry.py +4 -1
  79. empathy_os/models/token_estimator.py +3 -1
  80. empathy_os/monitoring/alerts.py +938 -9
  81. empathy_os/monitoring/alerts_cli.py +346 -183
  82. empathy_os/orchestration/execution_strategies.py +12 -29
  83. empathy_os/orchestration/pattern_learner.py +20 -26
  84. empathy_os/orchestration/real_tools.py +6 -15
  85. empathy_os/platform_utils.py +2 -1
  86. empathy_os/plugins/__init__.py +2 -2
  87. empathy_os/plugins/base.py +64 -64
  88. empathy_os/plugins/registry.py +32 -32
  89. empathy_os/project_index/index.py +49 -15
  90. empathy_os/project_index/models.py +1 -2
  91. empathy_os/project_index/reports.py +1 -1
  92. empathy_os/project_index/scanner.py +1 -0
  93. empathy_os/redis_memory.py +10 -7
  94. empathy_os/resilience/__init__.py +1 -1
  95. empathy_os/resilience/health.py +10 -10
  96. empathy_os/routing/__init__.py +7 -7
  97. empathy_os/routing/chain_executor.py +37 -37
  98. empathy_os/routing/classifier.py +36 -36
  99. empathy_os/routing/smart_router.py +40 -40
  100. empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
  101. empathy_os/scaffolding/__init__.py +8 -8
  102. empathy_os/scaffolding/__main__.py +1 -1
  103. empathy_os/scaffolding/cli.py +28 -28
  104. empathy_os/socratic/__init__.py +3 -19
  105. empathy_os/socratic/ab_testing.py +25 -36
  106. empathy_os/socratic/blueprint.py +38 -38
  107. empathy_os/socratic/cli.py +34 -20
  108. empathy_os/socratic/collaboration.py +30 -28
  109. empathy_os/socratic/domain_templates.py +9 -1
  110. empathy_os/socratic/embeddings.py +17 -13
  111. empathy_os/socratic/engine.py +135 -70
  112. empathy_os/socratic/explainer.py +70 -60
  113. empathy_os/socratic/feedback.py +24 -19
  114. empathy_os/socratic/forms.py +15 -10
  115. empathy_os/socratic/generator.py +51 -35
  116. empathy_os/socratic/llm_analyzer.py +25 -23
  117. empathy_os/socratic/mcp_server.py +99 -159
  118. empathy_os/socratic/session.py +19 -13
  119. empathy_os/socratic/storage.py +98 -67
  120. empathy_os/socratic/success.py +38 -27
  121. empathy_os/socratic/visual_editor.py +51 -39
  122. empathy_os/socratic/web_ui.py +99 -66
  123. empathy_os/telemetry/cli.py +3 -1
  124. empathy_os/telemetry/usage_tracker.py +1 -3
  125. empathy_os/test_generator/__init__.py +3 -3
  126. empathy_os/test_generator/cli.py +28 -28
  127. empathy_os/test_generator/generator.py +64 -66
  128. empathy_os/test_generator/risk_analyzer.py +11 -11
  129. empathy_os/vscode_bridge.py +173 -0
  130. empathy_os/workflows/__init__.py +212 -120
  131. empathy_os/workflows/batch_processing.py +8 -24
  132. empathy_os/workflows/bug_predict.py +1 -1
  133. empathy_os/workflows/code_review.py +20 -5
  134. empathy_os/workflows/code_review_pipeline.py +13 -8
  135. empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
  136. empathy_os/workflows/manage_documentation.py +1 -0
  137. empathy_os/workflows/orchestrated_health_check.py +6 -11
  138. empathy_os/workflows/orchestrated_release_prep.py +3 -3
  139. empathy_os/workflows/pr_review.py +18 -10
  140. empathy_os/workflows/progressive/__init__.py +2 -12
  141. empathy_os/workflows/progressive/cli.py +14 -37
  142. empathy_os/workflows/progressive/core.py +12 -12
  143. empathy_os/workflows/progressive/orchestrator.py +166 -144
  144. empathy_os/workflows/progressive/reports.py +22 -31
  145. empathy_os/workflows/progressive/telemetry.py +8 -14
  146. empathy_os/workflows/progressive/test_gen.py +29 -48
  147. empathy_os/workflows/progressive/workflow.py +31 -70
  148. empathy_os/workflows/release_prep.py +21 -6
  149. empathy_os/workflows/release_prep_crew.py +1 -0
  150. empathy_os/workflows/secure_release.py +13 -6
  151. empathy_os/workflows/security_audit.py +8 -3
  152. empathy_os/workflows/test_coverage_boost_crew.py +3 -2
  153. empathy_os/workflows/test_maintenance_crew.py +1 -0
  154. empathy_os/workflows/test_runner.py +16 -12
  155. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
  156. empathy_software_plugin/cli.py +0 -122
  157. coach_wizards/__init__.py +0 -45
  158. coach_wizards/accessibility_wizard.py +0 -91
  159. coach_wizards/api_wizard.py +0 -91
  160. coach_wizards/base_wizard.py +0 -209
  161. coach_wizards/cicd_wizard.py +0 -91
  162. coach_wizards/code_reviewer_README.md +0 -60
  163. coach_wizards/code_reviewer_wizard.py +0 -180
  164. coach_wizards/compliance_wizard.py +0 -91
  165. coach_wizards/database_wizard.py +0 -91
  166. coach_wizards/debugging_wizard.py +0 -91
  167. coach_wizards/documentation_wizard.py +0 -91
  168. coach_wizards/generate_wizards.py +0 -347
  169. coach_wizards/localization_wizard.py +0 -173
  170. coach_wizards/migration_wizard.py +0 -91
  171. coach_wizards/monitoring_wizard.py +0 -91
  172. coach_wizards/observability_wizard.py +0 -91
  173. coach_wizards/performance_wizard.py +0 -91
  174. coach_wizards/prompt_engineering_wizard.py +0 -661
  175. coach_wizards/refactoring_wizard.py +0 -91
  176. coach_wizards/scaling_wizard.py +0 -90
  177. coach_wizards/security_wizard.py +0 -92
  178. coach_wizards/testing_wizard.py +0 -91
  179. empathy_framework-4.6.6.dist-info/RECORD +0 -410
  180. empathy_llm_toolkit/wizards/__init__.py +0 -43
  181. empathy_llm_toolkit/wizards/base_wizard.py +0 -364
  182. empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
  183. empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
  184. empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
  185. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
  186. empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
  187. empathy_os/wizard_factory_cli.py +0 -170
  188. empathy_software_plugin/wizards/__init__.py +0 -42
  189. empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
  190. empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
  191. empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
  192. empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
  193. empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
  194. empathy_software_plugin/wizards/base_wizard.py +0 -288
  195. empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
  196. empathy_software_plugin/wizards/code_review_wizard.py +0 -604
  197. empathy_software_plugin/wizards/debugging/__init__.py +0 -50
  198. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
  199. empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
  200. empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
  201. empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
  202. empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
  203. empathy_software_plugin/wizards/debugging/verification.py +0 -369
  204. empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
  205. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
  206. empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
  207. empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
  208. empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
  209. empathy_software_plugin/wizards/performance/__init__.py +0 -9
  210. empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
  211. empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
  212. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
  213. empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
  214. empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
  215. empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
  216. empathy_software_plugin/wizards/security/__init__.py +0 -32
  217. empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
  218. empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
  219. empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
  220. empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
  221. empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
  222. empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
  223. empathy_software_plugin/wizards/testing/__init__.py +0 -27
  224. empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
  225. empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
  226. empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
  227. empathy_software_plugin/wizards/testing_wizard.py +0 -274
  228. wizards/__init__.py +0 -82
  229. wizards/admission_assessment_wizard.py +0 -644
  230. wizards/care_plan.py +0 -321
  231. wizards/clinical_assessment.py +0 -769
  232. wizards/discharge_planning.py +0 -77
  233. wizards/discharge_summary_wizard.py +0 -468
  234. wizards/dosage_calculation.py +0 -497
  235. wizards/incident_report_wizard.py +0 -454
  236. wizards/medication_reconciliation.py +0 -85
  237. wizards/nursing_assessment.py +0 -171
  238. wizards/patient_education.py +0 -654
  239. wizards/quality_improvement.py +0 -705
  240. wizards/sbar_report.py +0 -324
  241. wizards/sbar_wizard.py +0 -608
  242. wizards/shift_handoff_wizard.py +0 -535
  243. wizards/soap_note_wizard.py +0 -679
  244. wizards/treatment_plan.py +0 -15
  245. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/WHEEL +0 -0
  246. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/entry_points.txt +0 -0
  247. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/licenses/LICENSE +0 -0
wizards/sbar_wizard.py DELETED
@@ -1,608 +0,0 @@
1
- """
2
- SBAR Wizard Router - AI Nurse Florence
3
- Following Wizard Pattern Implementation
4
-
5
- SBAR (Situation, Background, Assessment, Recommendation) documentation wizard
6
- for clinical handoff communication and escalation to physicians.
7
-
8
- This wizard includes a review step where users can preview the AI-enhanced report
9
- before saving it.
10
-
11
- Version: 2.0 - Nov 2025 - 5-step wizard with auto-preview and dictation support
12
- """
13
-
14
- import json
15
- import logging
16
- import os
17
- from datetime import datetime
18
- from typing import Any
19
- from uuid import uuid4
20
-
21
- from fastapi import APIRouter, HTTPException, status
22
- from src.services import get_service
23
- from src.utils.api_responses import create_success_response
24
- from src.utils.config import get_settings
25
-
26
- logger = logging.getLogger(__name__)
27
-
28
- # Settings following coding instructions
29
- settings = get_settings()
30
-
31
- router = APIRouter(
32
- prefix="/wizards/sbar",
33
- tags=["clinical-wizards"],
34
- responses={
35
- 404: {"description": "Wizard session not found"},
36
- 422: {"description": "Invalid wizard step data"},
37
- 500: {"description": "Wizard processing error"},
38
- },
39
- )
40
-
41
- # Session storage (Redis in production, memory for development)
42
- try:
43
- from src.utils.redis_cache import get_redis_client
44
-
45
- _has_redis = True
46
- logger.info("✅ Redis client module imported for SBAR wizard sessions")
47
- except ImportError:
48
- _has_redis = False
49
- logger.warning("⚠️ Redis unavailable for SBAR wizard - sessions will use memory only")
50
-
51
- _wizard_sessions: dict[str, dict[str, Any]] = {}
52
-
53
- # Log Redis configuration at module load
54
- redis_url = os.getenv("REDIS_URL")
55
- if redis_url:
56
- logger.info(f"🔧 REDIS_URL environment variable detected (starts with: {redis_url[:15]}...)")
57
- else:
58
- logger.warning(
59
- "⚠️ REDIS_URL environment variable NOT SET - sessions will be lost between workers"
60
- )
61
-
62
-
63
- # SBAR wizard steps - 5 steps total
64
- SBAR_STEPS = {
65
- 1: {
66
- "step": 1,
67
- "title": "Situation",
68
- "prompt": "Describe the current patient situation",
69
- "fields": [
70
- "patient_condition",
71
- "immediate_concerns",
72
- "vital_signs",
73
- ],
74
- "help_text": "What is happening right now with the patient? Include current condition, vital signs, and immediate concerns.",
75
- },
76
- 2: {
77
- "step": 2,
78
- "title": "Background",
79
- "prompt": "Provide relevant clinical background",
80
- "fields": [
81
- "medical_history",
82
- "current_treatments",
83
- "baseline_condition",
84
- ],
85
- "help_text": "What is the clinical context? Include pertinent medical history, current treatments, and baseline condition.",
86
- },
87
- 3: {
88
- "step": 3,
89
- "title": "Assessment",
90
- "prompt": "Your professional clinical assessment",
91
- "fields": [
92
- "clinical_assessment",
93
- "primary_concerns",
94
- "risk_factors",
95
- ],
96
- "help_text": "What do you think is happening? Include your nursing assessment, primary concerns, and risk factors.",
97
- },
98
- 4: {
99
- "step": 4,
100
- "title": "Recommendation",
101
- "prompt": "What actions do you recommend?",
102
- "fields": [
103
- "recommendations",
104
- "requested_actions",
105
- "timeline",
106
- ],
107
- "help_text": "What needs to be done? Include specific recommendations, requested actions, and urgency timeline.",
108
- },
109
- 5: {
110
- "step": 5,
111
- "title": "Review & Enhance",
112
- "prompt": "Review and enhance your SBAR report with AI",
113
- "fields": ["review_complete", "generate_enhanced"],
114
- "help_text": "Review all sections. Click 'Generate Enhanced Report' to see AI suggestions before saving.",
115
- "is_review_step": True,
116
- },
117
- }
118
-
119
-
120
- async def _store_wizard_session(wizard_id: str, session_data: dict[str, Any]):
121
- """Store wizard session in Redis or memory."""
122
- if _has_redis:
123
- try:
124
- redis_client = await get_redis_client()
125
- if redis_client:
126
- await redis_client.setex(
127
- f"sbar_wizard:{wizard_id}",
128
- 3600, # 1 hour expiry
129
- json.dumps(session_data),
130
- )
131
- return
132
- except Exception as e:
133
- logger.warning(f"Failed to store session in Redis: {e}")
134
-
135
- # Fallback to memory
136
- _wizard_sessions[wizard_id] = session_data
137
-
138
-
139
- async def _get_wizard_session(wizard_id: str) -> dict[str, Any] | None:
140
- """Retrieve wizard session from Redis or memory."""
141
- if _has_redis:
142
- try:
143
- redis_client = await get_redis_client()
144
- if redis_client:
145
- data = await redis_client.get(f"sbar_wizard:{wizard_id}")
146
- if data:
147
- return json.loads(data)
148
- except Exception as e:
149
- logger.warning(f"Failed to retrieve session from Redis: {e}")
150
-
151
- # Fallback to memory
152
- return _wizard_sessions.get(wizard_id)
153
-
154
-
155
- def _get_step_data(step_number: int) -> dict[str, Any]:
156
- """Get step configuration data."""
157
- if step_number not in SBAR_STEPS:
158
- raise ValueError(f"Invalid step number: {step_number}")
159
-
160
- return SBAR_STEPS[step_number]
161
-
162
-
163
- @router.post(
164
- "/start",
165
- summary="Start SBAR Wizard",
166
- description="Initialize a new SBAR documentation workflow for clinical handoff communication.",
167
- )
168
- async def start_sbar_wizard():
169
- """Start SBAR wizard following Wizard Pattern Implementation."""
170
- try:
171
- wizard_id = str(uuid4())
172
-
173
- session_data = {
174
- "wizard_id": wizard_id,
175
- "wizard_type": "sbar",
176
- "current_step": 1,
177
- "total_steps": 5,
178
- "collected_data": {},
179
- "created_at": datetime.now().isoformat(),
180
- "updated_at": datetime.now().isoformat(),
181
- }
182
-
183
- # Store session
184
- await _store_wizard_session(wizard_id, session_data)
185
-
186
- # Get first step prompt
187
- step_data = _get_step_data(1)
188
-
189
- response_data = {
190
- "wizard_session": session_data,
191
- "current_step": step_data,
192
- "progress": {"current": 1, "total": 5, "percentage": 20},
193
- }
194
-
195
- return create_success_response(
196
- data=response_data, message="SBAR wizard started successfully"
197
- )
198
-
199
- except Exception as e:
200
- logger.error(f"Failed to start SBAR wizard: {e}", exc_info=True)
201
- raise HTTPException(
202
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
203
- detail=f"Failed to start SBAR wizard: {str(e)}",
204
- )
205
-
206
-
207
- @router.post(
208
- "/{wizard_id}/step",
209
- summary="Submit SBAR wizard step",
210
- description="Submit data for current step and advance to next step in SBAR workflow.",
211
- )
212
- async def submit_sbar_step(wizard_id: str, step_data: dict[str, Any]):
213
- """Submit step data and advance wizard."""
214
- try:
215
- # Get session
216
- session = await _get_wizard_session(wizard_id)
217
- if not session:
218
- raise HTTPException(
219
- status_code=status.HTTP_404_NOT_FOUND,
220
- detail=f"Wizard session {wizard_id} not found",
221
- )
222
-
223
- # Validate step number
224
- current_step = session["current_step"]
225
- if step_data.get("step") != current_step:
226
- raise HTTPException(
227
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
228
- detail=f"Expected step {current_step}, got {step_data.get('step')}",
229
- )
230
-
231
- # Store collected data
232
- session["collected_data"][f"step_{current_step}"] = step_data.get("data", {})
233
- session["updated_at"] = datetime.now().isoformat()
234
-
235
- # Advance to next step or complete
236
- if current_step < session["total_steps"]:
237
- session["current_step"] += 1
238
- await _store_wizard_session(wizard_id, session)
239
-
240
- next_step_data = _get_step_data(session["current_step"])
241
-
242
- # If next step is review step (step 5), generate and include the SBAR report
243
- if session["current_step"] == 5:
244
- # Auto-generate SBAR report for preview
245
- sbar_report = await _generate_sbar_report(wizard_id, session["collected_data"])
246
- next_step_data["sbar_report"] = sbar_report
247
- next_step_data["review_data"] = {
248
- "situation": session["collected_data"].get("step_1", {}),
249
- "background": session["collected_data"].get("step_2", {}),
250
- "assessment": session["collected_data"].get("step_3", {}),
251
- "recommendation": session["collected_data"].get("step_4", {}),
252
- }
253
-
254
- return create_success_response(
255
- data={
256
- "wizard_session": session,
257
- "current_step": next_step_data,
258
- "progress": {
259
- "current": session["current_step"],
260
- "total": session["total_steps"],
261
- "percentage": (session["current_step"] / session["total_steps"]) * 100,
262
- },
263
- },
264
- message=f"Step {current_step} completed",
265
- )
266
- else:
267
- # Wizard complete on step 5 - mark as complete and return report
268
- sbar_report = await _generate_sbar_report(wizard_id, session["collected_data"])
269
-
270
- session["completed_at"] = datetime.now().isoformat()
271
- session["final_report"] = sbar_report
272
- await _store_wizard_session(wizard_id, session)
273
-
274
- return create_success_response(
275
- data={
276
- "wizard_session": session,
277
- "sbar_report": sbar_report,
278
- "completed": True,
279
- },
280
- message="SBAR documentation completed successfully",
281
- )
282
-
283
- except HTTPException:
284
- raise
285
- except Exception as e:
286
- logger.error(f"Failed to process wizard step: {e}", exc_info=True)
287
- raise HTTPException(
288
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
289
- detail=f"Failed to process wizard step: {str(e)}",
290
- )
291
-
292
-
293
- @router.post(
294
- "/{wizard_id}/enhance",
295
- summary="Generate enhanced SBAR report for review",
296
- description="Generate AI-enhanced SBAR report for user to review before saving.",
297
- )
298
- async def enhance_sbar_report(wizard_id: str):
299
- """
300
- Generate enhanced SBAR report with AI for review.
301
- This does NOT save the report - user must call /save endpoint after reviewing.
302
- """
303
- try:
304
- session = await _get_wizard_session(wizard_id)
305
- if not session:
306
- raise HTTPException(
307
- status_code=status.HTTP_404_NOT_FOUND,
308
- detail=f"Wizard session {wizard_id} not found",
309
- )
310
-
311
- # Verify we're on step 5
312
- if session["current_step"] != 5:
313
- raise HTTPException(
314
- status_code=status.HTTP_400_BAD_REQUEST,
315
- detail=f"Can only enhance report on final step. Current step: {session['current_step']}",
316
- )
317
-
318
- # Generate SBAR report with AI enhancement
319
- sbar_report = await _generate_sbar_report(wizard_id, session["collected_data"])
320
-
321
- # Store enhanced report in session for review (but don't mark as completed yet)
322
- session["enhanced_report"] = sbar_report
323
- session["enhanced_at"] = datetime.now().isoformat()
324
- await _store_wizard_session(wizard_id, session)
325
-
326
- return create_success_response(
327
- data={
328
- "wizard_session": session,
329
- "sbar_report": sbar_report,
330
- "message": "Review the enhanced report below. Click 'Save Report' to finalize.",
331
- },
332
- message="Enhanced SBAR report generated for review",
333
- )
334
-
335
- except HTTPException:
336
- raise
337
- except Exception as e:
338
- logger.error(f"Failed to enhance SBAR report: {e}", exc_info=True)
339
- raise HTTPException(
340
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
341
- detail=f"Failed to enhance SBAR report: {str(e)}",
342
- )
343
-
344
-
345
- @router.post(
346
- "/{wizard_id}/save",
347
- summary="Save reviewed SBAR report",
348
- description="Save the enhanced SBAR report after user has reviewed it.",
349
- )
350
- async def save_sbar_report(wizard_id: str):
351
- """
352
- Save the enhanced SBAR report after user review.
353
- """
354
- try:
355
- session = await _get_wizard_session(wizard_id)
356
- if not session:
357
- raise HTTPException(
358
- status_code=status.HTTP_404_NOT_FOUND,
359
- detail=f"Wizard session {wizard_id} not found",
360
- )
361
-
362
- # Verify enhanced report exists
363
- if "enhanced_report" not in session:
364
- raise HTTPException(
365
- status_code=status.HTTP_400_BAD_REQUEST,
366
- detail="No enhanced report found. Generate enhanced report first.",
367
- )
368
-
369
- # Mark as completed
370
- session["completed_at"] = datetime.now().isoformat()
371
- session["final_report"] = session["enhanced_report"]
372
- await _store_wizard_session(wizard_id, session)
373
-
374
- return create_success_response(
375
- data={
376
- "wizard_session": session,
377
- "sbar_report": session["final_report"],
378
- "completed": True,
379
- },
380
- message="SBAR report saved successfully",
381
- )
382
-
383
- except HTTPException:
384
- raise
385
- except Exception as e:
386
- logger.error(f"Failed to save SBAR report: {e}", exc_info=True)
387
- raise HTTPException(
388
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
389
- detail=f"Failed to save SBAR report: {str(e)}",
390
- )
391
-
392
-
393
- @router.get(
394
- "/{wizard_id}/report",
395
- summary="Get SBAR report",
396
- description="Retrieve the completed SBAR report.",
397
- )
398
- async def get_sbar_report(wizard_id: str):
399
- """Get completed SBAR report."""
400
- try:
401
- session = await _get_wizard_session(wizard_id)
402
- if not session:
403
- raise HTTPException(
404
- status_code=status.HTTP_404_NOT_FOUND,
405
- detail=f"Wizard session {wizard_id} not found",
406
- )
407
-
408
- if "final_report" not in session:
409
- raise HTTPException(
410
- status_code=status.HTTP_400_BAD_REQUEST,
411
- detail="SBAR report not yet saved",
412
- )
413
-
414
- return create_success_response(
415
- data={"sbar_report": session["final_report"]},
416
- message="SBAR report retrieved",
417
- )
418
-
419
- except HTTPException:
420
- raise
421
- except Exception as e:
422
- logger.error(f"Failed to retrieve report: {e}", exc_info=True)
423
- raise HTTPException(
424
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
425
- detail=f"Failed to retrieve report: {str(e)}",
426
- )
427
-
428
-
429
- async def _generate_sbar_report(wizard_id: str, collected_data: dict[str, Any]) -> dict[str, Any]:
430
- """Generate SBAR report from collected data with AI enhancement."""
431
- logger.info(f"Generating SBAR report for wizard {wizard_id}")
432
-
433
- # Extract collected data
434
- step1 = collected_data.get("step_1", {})
435
- step2 = collected_data.get("step_2", {})
436
- step3 = collected_data.get("step_3", {})
437
- step4 = collected_data.get("step_4", {})
438
-
439
- logger.debug(f"SBAR data - Steps collected: {list(collected_data.keys())}")
440
-
441
- # Compile all steps into structured SBAR report
442
- report = {
443
- "report_type": "sbar",
444
- "generated_at": datetime.now().isoformat(),
445
- "sections": {
446
- "situation": step1,
447
- "background": step2,
448
- "assessment": step3,
449
- "recommendation": step4,
450
- },
451
- "situation": step1,
452
- "background": step2,
453
- "assessment": step3,
454
- "recommendation": step4,
455
- "formatted_report": _format_sbar_narrative(collected_data),
456
- }
457
-
458
- logger.debug(f"Formatted report length: {len(report['formatted_report'])} chars")
459
-
460
- # Try to enhance with AI
461
- try:
462
- sbar_service = get_service("sbar")
463
- if sbar_service:
464
- logger.info(f"Enhancing SBAR report for wizard {wizard_id}")
465
-
466
- enhancement_result = await sbar_service.enhance_sbar_report(report)
467
-
468
- if enhancement_result.get("enhanced"):
469
- report["ai_enhanced"] = True
470
- report["enhanced_report"] = enhancement_result.get("enhanced_report")
471
- report["model_used"] = enhancement_result.get("model")
472
- report["ai_usage"] = enhancement_result.get("usage")
473
- logger.info(f"SBAR report enhanced with {enhancement_result.get('model')}")
474
- else:
475
- report["ai_enhanced"] = False
476
- report["enhancement_note"] = enhancement_result.get(
477
- "note", "Enhancement unavailable"
478
- )
479
- logger.warning(f"SBAR enhancement unavailable: {enhancement_result.get('note')}")
480
- else:
481
- logger.warning("SBAR service not available")
482
- report["ai_enhanced"] = False
483
- report["enhancement_note"] = "SBAR service not available"
484
-
485
- except Exception as e:
486
- logger.error(f"Failed to enhance SBAR report: {e}", exc_info=True)
487
- report["ai_enhanced"] = False
488
- report["enhancement_error"] = str(e)
489
-
490
- return report
491
-
492
-
493
- def _format_sbar_narrative(collected_data: dict[str, Any]) -> str:
494
- """Format collected data into narrative SBAR report."""
495
- sections = []
496
-
497
- # Header
498
- sections.append("=" * 60)
499
- sections.append("SBAR CLINICAL COMMUNICATION REPORT")
500
- sections.append("=" * 60)
501
- sections.append("")
502
-
503
- # Situation
504
- step1 = collected_data.get("step_1", {})
505
- sections.append("SITUATION")
506
- sections.append("-" * 60)
507
- if step1:
508
- patient_condition = step1.get("patient_condition", "")
509
- immediate_concerns = step1.get("immediate_concerns", "")
510
- vital_signs = step1.get("vital_signs", "")
511
-
512
- if patient_condition or immediate_concerns:
513
- narrative = []
514
- if patient_condition:
515
- narrative.append(f"The patient is currently {patient_condition.strip()}.")
516
- if immediate_concerns:
517
- narrative.append(f"Immediate concerns include: {immediate_concerns.strip()}.")
518
- if vital_signs:
519
- narrative.append(f"Current vital signs: {vital_signs.strip()}.")
520
- sections.append(" ".join(narrative))
521
- else:
522
- sections.append("No situation data provided.")
523
- else:
524
- sections.append("No situation data provided.")
525
- sections.append("")
526
-
527
- # Background
528
- step2 = collected_data.get("step_2", {})
529
- sections.append("BACKGROUND")
530
- sections.append("-" * 60)
531
- if step2:
532
- medical_history = step2.get("medical_history", "")
533
- current_treatments = step2.get("current_treatments", "")
534
- baseline_condition = step2.get("baseline_condition", "")
535
-
536
- if medical_history or current_treatments or baseline_condition:
537
- narrative = []
538
- if medical_history:
539
- narrative.append(f"Patient medical history: {medical_history.strip()}.")
540
- if baseline_condition:
541
- narrative.append(f"Baseline condition: {baseline_condition.strip()}.")
542
- if current_treatments:
543
- narrative.append(f"Current treatments include: {current_treatments.strip()}.")
544
- sections.append(" ".join(narrative))
545
- else:
546
- sections.append("No background data provided.")
547
- else:
548
- sections.append("No background data provided.")
549
- sections.append("")
550
-
551
- # Assessment
552
- step3 = collected_data.get("step_3", {})
553
- sections.append("ASSESSMENT")
554
- sections.append("-" * 60)
555
- if step3:
556
- clinical_assessment = step3.get("clinical_assessment", "")
557
- primary_concerns = step3.get("primary_concerns", "")
558
- risk_factors = step3.get("risk_factors", "")
559
-
560
- if clinical_assessment or primary_concerns or risk_factors:
561
- narrative = []
562
- if clinical_assessment:
563
- narrative.append(f"Clinical assessment: {clinical_assessment.strip()}.")
564
- if primary_concerns:
565
- narrative.append(f"Primary concerns: {primary_concerns.strip()}.")
566
- if risk_factors:
567
- narrative.append(f"Risk factors identified: {risk_factors.strip()}.")
568
- sections.append(" ".join(narrative))
569
- else:
570
- sections.append("No assessment data provided.")
571
- else:
572
- sections.append("No assessment data provided.")
573
- sections.append("")
574
-
575
- # Recommendation
576
- step4 = collected_data.get("step_4", {})
577
- sections.append("RECOMMENDATION")
578
- sections.append("-" * 60)
579
- if step4:
580
- recommendations = step4.get("recommendations", "")
581
- requested_actions = step4.get("requested_actions", "")
582
- timeline = step4.get("timeline", "")
583
-
584
- if recommendations or requested_actions or timeline:
585
- narrative = []
586
- if recommendations:
587
- narrative.append(f"Recommended interventions: {recommendations.strip()}.")
588
- if requested_actions:
589
- narrative.append(f"Requested actions: {requested_actions.strip()}.")
590
- if timeline:
591
- narrative.append(f"Timeline: {timeline.strip()}.")
592
- sections.append(" ".join(narrative))
593
- else:
594
- sections.append("No recommendations provided.")
595
- else:
596
- sections.append("No recommendations provided.")
597
- sections.append("")
598
-
599
- sections.append("=" * 60)
600
- sections.append("Educational use only — not medical advice. No PHI stored.")
601
- sections.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
602
- sections.append("=" * 60)
603
-
604
- return "\n".join(sections)
605
-
606
-
607
- # Export router
608
- __all__ = ["router"]