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
@@ -1,679 +0,0 @@
1
- """
2
- SOAP Note Wizard Router - AI Nurse Florence
3
- Following Wizard Pattern Implementation from SBAR and Shift Handoff wizards
4
-
5
- SOAP (Subjective, Objective, Assessment, Plan) note documentation wizard
6
- for comprehensive clinical progress notes. Evidence-based format for
7
- interprofessional communication and continuity of care.
8
-
9
- Version: 2.0 - 5-step wizard with review/approval (Jan 2025)
10
- Users must preview and explicitly approve documents before finalization.
11
- """
12
-
13
- import logging
14
- from datetime import datetime
15
- from typing import Any
16
- from uuid import uuid4
17
-
18
- from fastapi import APIRouter, HTTPException, status
19
- from src.services import get_service
20
- from src.utils.api_responses import create_success_response
21
- from src.utils.config import get_settings
22
-
23
- logger = logging.getLogger(__name__)
24
-
25
- # Conditional translation import
26
- try:
27
- from src.services.translation_service import translate_text
28
-
29
- _has_translation = True
30
- except ImportError:
31
- _has_translation = False
32
-
33
- async def translate_text(
34
- text: str,
35
- target_language: str,
36
- source_language: str = "en",
37
- context: str = "medical",
38
- ):
39
- return {"translated_text": text, "success": False}
40
-
41
-
42
- # Settings following coding instructions
43
- settings = get_settings()
44
-
45
- router = APIRouter(
46
- prefix="/wizards/soap-note",
47
- tags=["clinical-wizards"],
48
- responses={
49
- 404: {"description": "Wizard session not found"},
50
- 422: {"description": "Invalid wizard step data"},
51
- 500: {"description": "Wizard processing error"},
52
- },
53
- )
54
-
55
- # Session storage (Redis in production, memory for development)
56
- try:
57
- from src.utils.redis_cache import get_redis_client
58
-
59
- _has_redis = True
60
- except ImportError:
61
- _has_redis = False
62
-
63
- _wizard_sessions: dict[str, dict[str, Any]] = {}
64
-
65
-
66
- # SOAP note wizard steps
67
- SOAP_NOTE_STEPS = {
68
- 1: {
69
- "step": 1,
70
- "title": "Subjective - Patient's Experience",
71
- "prompt": "Document the patient's subjective experience and reported symptoms",
72
- "fields": [
73
- "chief_complaint",
74
- "history_present_illness",
75
- "patient_reported_symptoms",
76
- "pain_description",
77
- "functional_status",
78
- "patient_concerns",
79
- ],
80
- "help_text": "Record what the patient tells you in their own words. Include chief complaint, symptoms, pain, and any concerns they express.",
81
- },
82
- 2: {
83
- "step": 2,
84
- "title": "Objective - Clinical Observations",
85
- "prompt": "Record objective clinical findings and measurements",
86
- "fields": [
87
- "vital_signs",
88
- "physical_exam_findings",
89
- "lab_results",
90
- "imaging_results",
91
- "medication_administration",
92
- "procedures_performed",
93
- ],
94
- "help_text": "Document measurable data: vital signs, physical exam findings, lab/imaging results, medications given, procedures done.",
95
- },
96
- 3: {
97
- "step": 3,
98
- "title": "Assessment - Clinical Analysis",
99
- "prompt": "Provide your clinical assessment and interpretation",
100
- "fields": [
101
- "primary_diagnosis",
102
- "differential_diagnoses",
103
- "problem_list",
104
- "progress_evaluation",
105
- "response_to_treatment",
106
- "complications_concerns",
107
- ],
108
- "help_text": "Analyze the data: primary diagnosis, other possibilities, current problems, patient's progress, treatment response, any concerns.",
109
- },
110
- 4: {
111
- "step": 4,
112
- "title": "Plan - Care Plan and Next Steps",
113
- "prompt": "Outline the care plan and next steps",
114
- "fields": [
115
- "diagnostic_plan",
116
- "therapeutic_plan",
117
- "patient_education",
118
- "monitoring_plan",
119
- "follow_up",
120
- "consultations_needed",
121
- ],
122
- "help_text": "Detail the plan: further diagnostics needed, treatment changes, patient education, monitoring requirements, follow-up, consultations.",
123
- },
124
- 5: {
125
- "step": 5,
126
- "title": "Review & Finalize",
127
- "prompt": "Review your SOAP note and finalize the document",
128
- "fields": ["review_complete", "user_approved"],
129
- "help_text": "Review all sections of your SOAP note. Click 'Generate Preview' to see the formatted note. You can go back to edit any section before finalizing.",
130
- "is_review_step": True,
131
- },
132
- }
133
-
134
-
135
- # Educational banner for all SOAP note outputs
136
- EDU_BANNER = """
137
- ⚕️ EDUCATIONAL TOOL NOTICE ⚕️
138
- This SOAP note wizard is an educational tool for healthcare professionals.
139
- All clinical documentation should be reviewed and validated by qualified providers.
140
- Never rely solely on automated tools for clinical decision-making or documentation.
141
- """
142
-
143
-
144
- async def _store_wizard_session(wizard_id: str, session_data: dict[str, Any]) -> bool:
145
- """Store wizard session in Redis (preferred) or memory (fallback)"""
146
- try:
147
- if _has_redis:
148
- redis_client = await get_redis_client()
149
- if redis_client:
150
- import json
151
-
152
- cache_key = f"wizard:soap_note:{wizard_id}"
153
- await redis_client.setex(
154
- cache_key, 7200, json.dumps(session_data) # 2 hour TTL - FIXED: use JSON
155
- )
156
- logger.info(f"Stored SOAP note wizard session {wizard_id} in Redis")
157
- return True
158
- except Exception as e:
159
- logger.warning(f"Failed to store session in Redis: {e}, using memory fallback")
160
-
161
- # Memory fallback
162
- _wizard_sessions[wizard_id] = session_data
163
- logger.info(f"Stored SOAP note wizard session {wizard_id} in memory")
164
- return True
165
-
166
-
167
- async def _get_wizard_session(wizard_id: str) -> dict[str, Any] | None:
168
- """Retrieve wizard session from Redis (preferred) or memory (fallback)"""
169
- try:
170
- if _has_redis:
171
- redis_client = await get_redis_client()
172
- if redis_client:
173
- import json
174
-
175
- cache_key = f"wizard:soap_note:{wizard_id}"
176
- session_str = await redis_client.get(cache_key)
177
- if session_str:
178
- # SECURITY FIX: Use json.loads() instead of ast.literal_eval()
179
- return json.loads(session_str)
180
- except Exception as e:
181
- logger.warning(f"Failed to retrieve session from Redis: {e}, checking memory")
182
-
183
- # Memory fallback
184
- return _wizard_sessions.get(wizard_id)
185
-
186
-
187
- def _get_step_data(step: int) -> dict[str, Any]:
188
- """Get step configuration data"""
189
- if step not in SOAP_NOTE_STEPS:
190
- raise HTTPException(
191
- status_code=status.HTTP_400_BAD_REQUEST,
192
- detail=f"Invalid step number: {step}",
193
- )
194
-
195
- step_config = SOAP_NOTE_STEPS[step]
196
- return {
197
- "step": step,
198
- "title": step_config["title"],
199
- "prompt": step_config["prompt"],
200
- "fields": step_config["fields"],
201
- "help_text": step_config["help_text"],
202
- }
203
-
204
-
205
- def _generate_soap_note_report(collected_data: dict[str, Any]) -> dict[str, Any]:
206
- """Generate final SOAP note report from collected data"""
207
-
208
- # Extract data by section
209
- subjective = {
210
- "chief_complaint": collected_data.get("chief_complaint", "Not documented"),
211
- "history_present_illness": collected_data.get("history_present_illness", "Not documented"),
212
- "patient_reported_symptoms": collected_data.get(
213
- "patient_reported_symptoms", "Not documented"
214
- ),
215
- "pain_description": collected_data.get("pain_description", "Not documented"),
216
- "functional_status": collected_data.get("functional_status", "Not documented"),
217
- "patient_concerns": collected_data.get("patient_concerns", "None reported"),
218
- }
219
-
220
- objective = {
221
- "vital_signs": collected_data.get("vital_signs", "Not documented"),
222
- "physical_exam_findings": collected_data.get("physical_exam_findings", "Not documented"),
223
- "lab_results": collected_data.get("lab_results", "Pending or not available"),
224
- "imaging_results": collected_data.get("imaging_results", "Pending or not available"),
225
- "medication_administration": collected_data.get(
226
- "medication_administration", "None documented"
227
- ),
228
- "procedures_performed": collected_data.get("procedures_performed", "None documented"),
229
- }
230
-
231
- assessment = {
232
- "primary_diagnosis": collected_data.get("primary_diagnosis", "Not documented"),
233
- "differential_diagnoses": collected_data.get("differential_diagnoses", "Not documented"),
234
- "problem_list": collected_data.get("problem_list", "Not documented"),
235
- "progress_evaluation": collected_data.get("progress_evaluation", "Not documented"),
236
- "response_to_treatment": collected_data.get("response_to_treatment", "Not documented"),
237
- "complications_concerns": collected_data.get("complications_concerns", "None identified"),
238
- }
239
-
240
- plan = {
241
- "diagnostic_plan": collected_data.get("diagnostic_plan", "Not documented"),
242
- "therapeutic_plan": collected_data.get("therapeutic_plan", "Not documented"),
243
- "patient_education": collected_data.get("patient_education", "Not documented"),
244
- "monitoring_plan": collected_data.get("monitoring_plan", "Not documented"),
245
- "follow_up": collected_data.get("follow_up", "Not documented"),
246
- "consultations_needed": collected_data.get("consultations_needed", "None at this time"),
247
- }
248
-
249
- # Generate formatted narrative
250
- narrative = f"""
251
- SOAP NOTE
252
- Date: {collected_data.get('note_date', datetime.now().strftime('%Y-%m-%d %H:%M'))}
253
-
254
- SUBJECTIVE:
255
- Chief Complaint: {subjective['chief_complaint']}
256
-
257
- History of Present Illness: {subjective['history_present_illness']}
258
-
259
- Patient-Reported Symptoms: {subjective['patient_reported_symptoms']}
260
-
261
- Pain: {subjective['pain_description']}
262
-
263
- Functional Status: {subjective['functional_status']}
264
-
265
- Patient Concerns: {subjective['patient_concerns']}
266
-
267
-
268
- OBJECTIVE:
269
- Vital Signs: {objective['vital_signs']}
270
-
271
- Physical Examination: {objective['physical_exam_findings']}
272
-
273
- Laboratory Results: {objective['lab_results']}
274
-
275
- Imaging Results: {objective['imaging_results']}
276
-
277
- Medications Administered: {objective['medication_administration']}
278
-
279
- Procedures Performed: {objective['procedures_performed']}
280
-
281
-
282
- ASSESSMENT:
283
- Primary Diagnosis: {assessment['primary_diagnosis']}
284
-
285
- Differential Diagnoses: {assessment['differential_diagnoses']}
286
-
287
- Problem List: {assessment['problem_list']}
288
-
289
- Progress Evaluation: {assessment['progress_evaluation']}
290
-
291
- Response to Treatment: {assessment['response_to_treatment']}
292
-
293
- Complications/Concerns: {assessment['complications_concerns']}
294
-
295
-
296
- PLAN:
297
- Diagnostic Plan: {plan['diagnostic_plan']}
298
-
299
- Therapeutic Plan: {plan['therapeutic_plan']}
300
-
301
- Patient Education: {plan['patient_education']}
302
-
303
- Monitoring Plan: {plan['monitoring_plan']}
304
-
305
- Follow-up: {plan['follow_up']}
306
-
307
- Consultations Needed: {plan['consultations_needed']}
308
- """
309
-
310
- return {
311
- "soap_note": {
312
- "subjective": subjective,
313
- "objective": objective,
314
- "assessment": assessment,
315
- "plan": plan,
316
- },
317
- "narrative": narrative.strip(),
318
- "metadata": {
319
- "generated_at": datetime.now().isoformat(),
320
- "note_date": collected_data.get("note_date", datetime.now().strftime("%Y-%m-%d %H:%M")),
321
- "wizard_type": "soap_note",
322
- },
323
- "banner": EDU_BANNER,
324
- }
325
-
326
-
327
- @router.post("/start", summary="Start SOAP Note Wizard")
328
- async def start_soap_note_wizard():
329
- """
330
- Initialize a new SOAP note documentation wizard session.
331
-
332
- SOAP (Subjective, Objective, Assessment, Plan) is the standard format
333
- for clinical progress notes used across healthcare settings.
334
-
335
- Returns a wizard session ID and first step configuration.
336
- """
337
- try:
338
- wizard_id = str(uuid4())
339
-
340
- session_data = {
341
- "wizard_id": wizard_id,
342
- "wizard_type": "soap_note",
343
- "current_step": 1,
344
- "total_steps": 5,
345
- "collected_data": {},
346
- "created_at": datetime.now().isoformat(),
347
- "updated_at": datetime.now().isoformat(),
348
- }
349
-
350
- # Store session
351
- await _store_wizard_session(wizard_id, session_data)
352
-
353
- # Get first step data
354
- step_data = _get_step_data(1)
355
-
356
- response_data = {
357
- "wizard_session": session_data,
358
- "current_step": step_data,
359
- "progress": {"current": 1, "total": 4, "percentage": 25},
360
- "banner": EDU_BANNER,
361
- }
362
-
363
- return create_success_response(
364
- data=response_data, message="SOAP note wizard started successfully"
365
- )
366
-
367
- except Exception as e:
368
- logger.error(f"Failed to start SOAP note wizard: {e}")
369
- raise HTTPException(
370
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
371
- detail=f"Failed to start SOAP note wizard: {str(e)}",
372
- )
373
-
374
-
375
- @router.post("/{wizard_id}/step", summary="Submit SOAP note wizard step")
376
- async def submit_soap_note_step(wizard_id: str, step_data: dict[str, Any]):
377
- """
378
- Submit data for current step and advance to next step.
379
-
380
- The wizard will validate the data, store it, and either:
381
- - Return the next step configuration (if more steps remain)
382
- - Return the complete SOAP note report (if all steps completed)
383
- """
384
- try:
385
- # Retrieve session
386
- session = await _get_wizard_session(wizard_id)
387
- if not session:
388
- raise HTTPException(
389
- status_code=status.HTTP_404_NOT_FOUND,
390
- detail=f"Wizard session {wizard_id} not found",
391
- )
392
-
393
- current_step = session["current_step"]
394
- total_steps = session["total_steps"]
395
-
396
- # Validate step number
397
- submitted_step = step_data.get("step", current_step)
398
- if submitted_step != current_step:
399
- raise HTTPException(
400
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
401
- detail=f"Expected step {current_step}, got step {submitted_step}",
402
- )
403
-
404
- # Store submitted data
405
- session["collected_data"].update(step_data.get("data", {}))
406
- session["updated_at"] = datetime.now().isoformat()
407
-
408
- # Advance to next step (but don't auto-complete, even on final step)
409
- if current_step < total_steps:
410
- next_step = current_step + 1
411
- session["current_step"] = next_step
412
- else:
413
- # On review step - stay on same step, don't auto-complete
414
- next_step = current_step
415
-
416
- await _store_wizard_session(wizard_id, session)
417
-
418
- # Get next step configuration
419
- next_step_data = _get_step_data(next_step)
420
-
421
- response_data = {
422
- "wizard_session": session,
423
- "current_step": next_step_data,
424
- "progress": {
425
- "current": next_step,
426
- "total": total_steps,
427
- "percentage": int((next_step / total_steps) * 100),
428
- },
429
- }
430
-
431
- return create_success_response(
432
- data=response_data,
433
- message=f"Step {current_step} completed, moved to step {next_step}",
434
- )
435
-
436
- except HTTPException:
437
- raise
438
- except Exception as e:
439
- logger.error(f"Failed to process SOAP note wizard step: {e}")
440
- raise HTTPException(
441
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
442
- detail=f"Failed to process wizard step: {str(e)}",
443
- )
444
-
445
-
446
- @router.post("/{wizard_id}/enhance", summary="Enhance SOAP note text with AI")
447
- async def enhance_soap_note_text(wizard_id: str, text_data: dict[str, Any]):
448
- """
449
- Enhance user-provided text with AI to improve clinical documentation quality.
450
-
451
- Uses the chat service to refine language, add clinical terminology,
452
- and improve clarity while preserving the original meaning.
453
- """
454
- try:
455
- # Verify wizard session exists
456
- session = await _get_wizard_session(wizard_id)
457
- if not session:
458
- raise HTTPException(
459
- status_code=status.HTTP_404_NOT_FOUND,
460
- detail=f"Wizard session {wizard_id} not found",
461
- )
462
-
463
- original_text = text_data.get("text", "")
464
- field_name = text_data.get("field", "text")
465
-
466
- if not original_text:
467
- raise HTTPException(
468
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
469
- detail="No text provided for enhancement",
470
- )
471
-
472
- # Get chat service
473
- chat_service = get_service("chat")
474
-
475
- # Create enhancement prompt
476
- enhancement_prompt = f"""
477
- You are assisting with SOAP note documentation. Please enhance the following text
478
- to be more professional and clinically appropriate while preserving the original meaning:
479
-
480
- Field: {field_name}
481
- Original text: {original_text}
482
-
483
- Please provide an enhanced version that:
484
- - Uses appropriate clinical terminology
485
- - Is clear and concise
486
- - Maintains professional tone
487
- - Preserves all key information
488
- - Follows SOAP note documentation standards
489
-
490
- Enhanced text:"""
491
-
492
- # Call chat service for enhancement
493
- chat_response = await chat_service.chat(
494
- message=enhancement_prompt,
495
- conversation_id=f"soap_enhance_{wizard_id}",
496
- context={"wizard_id": wizard_id, "field": field_name},
497
- )
498
-
499
- enhanced_text = chat_response.get("response", original_text)
500
-
501
- response_data = {
502
- "original_text": original_text,
503
- "enhanced_text": enhanced_text,
504
- "field": field_name,
505
- }
506
-
507
- return create_success_response(data=response_data, message="Text enhanced successfully")
508
-
509
- except HTTPException:
510
- raise
511
- except Exception as e:
512
- logger.error(f"Failed to enhance SOAP note text: {e}")
513
- raise HTTPException(
514
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
515
- detail=f"Failed to enhance text: {str(e)}",
516
- )
517
-
518
-
519
- @router.post("/{wizard_id}/preview", summary="Preview SOAP note report")
520
- async def preview_soap_note_report(wizard_id: str):
521
- """
522
- Generate preview of SOAP note report without finalizing.
523
-
524
- This endpoint allows users to see the formatted SOAP note before
525
- finalizing it. The report is NOT marked as complete. Users can
526
- still go back and edit data after previewing.
527
- """
528
- try:
529
- # Retrieve session
530
- session = await _get_wizard_session(wizard_id)
531
- if not session:
532
- raise HTTPException(
533
- status_code=status.HTTP_404_NOT_FOUND,
534
- detail=f"Wizard session {wizard_id} not found",
535
- )
536
-
537
- # Verify user is on review step
538
- if session["current_step"] != session["total_steps"]:
539
- raise HTTPException(
540
- status_code=status.HTTP_400_BAD_REQUEST,
541
- detail=f"Not on review step. Complete steps 1-{session['total_steps']-1} first.",
542
- )
543
-
544
- # Generate preview report (does NOT mark as complete)
545
- preview_report = _generate_soap_note_report(session["collected_data"])
546
-
547
- # Store preview in session
548
- session["preview_report"] = preview_report
549
- session["preview_generated_at"] = datetime.now().isoformat()
550
- await _store_wizard_session(wizard_id, session)
551
-
552
- response_data = {
553
- "preview": preview_report,
554
- "wizard_session": session,
555
- "message": "Review the SOAP note above. Click 'Finalize Report' to save, or go back to edit any section.",
556
- }
557
-
558
- return create_success_response(data=response_data, message="Preview generated successfully")
559
-
560
- except HTTPException:
561
- raise
562
- except Exception as e:
563
- logger.error(f"Failed to generate SOAP note preview: {e}")
564
- raise HTTPException(
565
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
566
- detail=f"Failed to generate preview: {str(e)}",
567
- )
568
-
569
-
570
- @router.post("/{wizard_id}/save", summary="Finalize SOAP note report")
571
- async def save_soap_note_report(wizard_id: str, approval_data: dict[str, Any]):
572
- """
573
- Finalize and save the SOAP note report after user review and approval.
574
-
575
- Requires that the user has:
576
- 1. Generated a preview first (/preview endpoint)
577
- 2. Explicitly approved the report (user_approved: true)
578
-
579
- Only after calling this endpoint is the report marked as complete.
580
- """
581
- try:
582
- # Retrieve session
583
- session = await _get_wizard_session(wizard_id)
584
- if not session:
585
- raise HTTPException(
586
- status_code=status.HTTP_404_NOT_FOUND,
587
- detail=f"Wizard session {wizard_id} not found",
588
- )
589
-
590
- # Verify preview was generated
591
- if "preview_report" not in session:
592
- raise HTTPException(
593
- status_code=status.HTTP_400_BAD_REQUEST,
594
- detail="Must generate preview before saving. Call /preview endpoint first.",
595
- )
596
-
597
- # Verify user explicitly approved
598
- if not approval_data.get("user_approved", False):
599
- raise HTTPException(
600
- status_code=status.HTTP_400_BAD_REQUEST,
601
- detail="User approval required. Set 'user_approved': true in request body.",
602
- )
603
-
604
- # NOW we mark as complete
605
- session["completed"] = True
606
- session["completed_at"] = datetime.now().isoformat()
607
- session["final_report"] = session["preview_report"]
608
- session["user_approved"] = True
609
-
610
- await _store_wizard_session(wizard_id, session)
611
-
612
- response_data = {
613
- "wizard_session": session,
614
- "report": session["final_report"],
615
- "completed": True,
616
- }
617
-
618
- return create_success_response(
619
- data=response_data, message="SOAP note finalized successfully"
620
- )
621
-
622
- except HTTPException:
623
- raise
624
- except Exception as e:
625
- logger.error(f"Failed to save SOAP note: {e}")
626
- raise HTTPException(
627
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
628
- detail=f"Failed to finalize SOAP note: {str(e)}",
629
- )
630
-
631
-
632
- @router.get("/{wizard_id}/report", summary="Get SOAP note report")
633
- async def get_soap_note_report(wizard_id: str):
634
- """
635
- Retrieve the completed SOAP note report.
636
-
637
- Only available after all wizard steps have been completed.
638
- Returns the structured SOAP note data and formatted narrative.
639
- """
640
- try:
641
- # Retrieve session
642
- session = await _get_wizard_session(wizard_id)
643
- if not session:
644
- raise HTTPException(
645
- status_code=status.HTTP_404_NOT_FOUND,
646
- detail=f"Wizard session {wizard_id} not found",
647
- )
648
-
649
- # Check if completed
650
- if not session.get("completed", False):
651
- raise HTTPException(
652
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
653
- detail="SOAP note wizard not yet completed. Complete all steps first.",
654
- )
655
-
656
- # Generate report
657
- report = _generate_soap_note_report(session["collected_data"])
658
-
659
- response_data = {
660
- "wizard_session": session,
661
- "report": report,
662
- }
663
-
664
- return create_success_response(
665
- data=response_data, message="SOAP note report retrieved successfully"
666
- )
667
-
668
- except HTTPException:
669
- raise
670
- except Exception as e:
671
- logger.error(f"Failed to retrieve SOAP note report: {e}")
672
- raise HTTPException(
673
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
674
- detail=f"Failed to retrieve report: {str(e)}",
675
- )
676
-
677
-
678
- # Export router
679
- __all__ = ["router"]
wizards/treatment_plan.py DELETED
@@ -1,15 +0,0 @@
1
- """
2
- Shim module to expose the existing treatment_plan router under the `src` package.
3
- Some routers live at `routers.wizards.*` (top-level); tests and src loader expect `src.routers.wizards.*`.
4
- This shim imports the real router and exposes it for the src-based loader.
5
- """
6
-
7
- try:
8
- from routers.wizards.treatment_plan import router # type: ignore
9
- except Exception:
10
- # Fallback: expose an empty APIRouter so imports don't fail
11
- from fastapi import APIRouter
12
-
13
- router = APIRouter()
14
-
15
- __all__ = ["router"]