empathy-framework 4.6.6__py3-none-any.whl → 4.7.1__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 (273) hide show
  1. empathy_framework-4.7.1.dist-info/METADATA +690 -0
  2. empathy_framework-4.7.1.dist-info/RECORD +379 -0
  3. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/top_level.txt +1 -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 2.py +173 -0
  130. empathy_os/vscode_bridge.py +173 -0
  131. empathy_os/workflows/__init__.py +212 -120
  132. empathy_os/workflows/batch_processing.py +8 -24
  133. empathy_os/workflows/bug_predict.py +1 -1
  134. empathy_os/workflows/code_review.py +20 -5
  135. empathy_os/workflows/code_review_pipeline.py +13 -8
  136. empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
  137. empathy_os/workflows/manage_documentation.py +1 -0
  138. empathy_os/workflows/orchestrated_health_check.py +6 -11
  139. empathy_os/workflows/orchestrated_release_prep.py +3 -3
  140. empathy_os/workflows/pr_review.py +18 -10
  141. empathy_os/workflows/progressive/README 2.md +454 -0
  142. empathy_os/workflows/progressive/__init__ 2.py +92 -0
  143. empathy_os/workflows/progressive/__init__.py +2 -12
  144. empathy_os/workflows/progressive/cli 2.py +242 -0
  145. empathy_os/workflows/progressive/cli.py +14 -37
  146. empathy_os/workflows/progressive/core 2.py +488 -0
  147. empathy_os/workflows/progressive/core.py +12 -12
  148. empathy_os/workflows/progressive/orchestrator 2.py +701 -0
  149. empathy_os/workflows/progressive/orchestrator.py +166 -144
  150. empathy_os/workflows/progressive/reports 2.py +528 -0
  151. empathy_os/workflows/progressive/reports.py +22 -31
  152. empathy_os/workflows/progressive/telemetry 2.py +280 -0
  153. empathy_os/workflows/progressive/telemetry.py +8 -14
  154. empathy_os/workflows/progressive/test_gen 2.py +514 -0
  155. empathy_os/workflows/progressive/test_gen.py +29 -48
  156. empathy_os/workflows/progressive/workflow 2.py +628 -0
  157. empathy_os/workflows/progressive/workflow.py +31 -70
  158. empathy_os/workflows/release_prep.py +21 -6
  159. empathy_os/workflows/release_prep_crew.py +1 -0
  160. empathy_os/workflows/secure_release.py +13 -6
  161. empathy_os/workflows/security_audit.py +8 -3
  162. empathy_os/workflows/test_coverage_boost_crew.py +3 -2
  163. empathy_os/workflows/test_maintenance_crew.py +1 -0
  164. empathy_os/workflows/test_runner.py +16 -12
  165. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
  166. empathy_software_plugin/cli.py +0 -122
  167. patterns/README.md +119 -0
  168. patterns/__init__.py +95 -0
  169. patterns/behavior.py +298 -0
  170. patterns/code_review_memory.json +441 -0
  171. patterns/core.py +97 -0
  172. patterns/debugging.json +3763 -0
  173. patterns/empathy.py +268 -0
  174. patterns/health_check_memory.json +505 -0
  175. patterns/input.py +161 -0
  176. patterns/memory_graph.json +8 -0
  177. patterns/refactoring_memory.json +1113 -0
  178. patterns/registry.py +663 -0
  179. patterns/security_memory.json +8 -0
  180. patterns/structural.py +415 -0
  181. patterns/validation.py +194 -0
  182. coach_wizards/__init__.py +0 -45
  183. coach_wizards/accessibility_wizard.py +0 -91
  184. coach_wizards/api_wizard.py +0 -91
  185. coach_wizards/base_wizard.py +0 -209
  186. coach_wizards/cicd_wizard.py +0 -91
  187. coach_wizards/code_reviewer_README.md +0 -60
  188. coach_wizards/code_reviewer_wizard.py +0 -180
  189. coach_wizards/compliance_wizard.py +0 -91
  190. coach_wizards/database_wizard.py +0 -91
  191. coach_wizards/debugging_wizard.py +0 -91
  192. coach_wizards/documentation_wizard.py +0 -91
  193. coach_wizards/generate_wizards.py +0 -347
  194. coach_wizards/localization_wizard.py +0 -173
  195. coach_wizards/migration_wizard.py +0 -91
  196. coach_wizards/monitoring_wizard.py +0 -91
  197. coach_wizards/observability_wizard.py +0 -91
  198. coach_wizards/performance_wizard.py +0 -91
  199. coach_wizards/prompt_engineering_wizard.py +0 -661
  200. coach_wizards/refactoring_wizard.py +0 -91
  201. coach_wizards/scaling_wizard.py +0 -90
  202. coach_wizards/security_wizard.py +0 -92
  203. coach_wizards/testing_wizard.py +0 -91
  204. empathy_framework-4.6.6.dist-info/METADATA +0 -1597
  205. empathy_framework-4.6.6.dist-info/RECORD +0 -410
  206. empathy_llm_toolkit/wizards/__init__.py +0 -43
  207. empathy_llm_toolkit/wizards/base_wizard.py +0 -364
  208. empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
  209. empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
  210. empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
  211. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
  212. empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
  213. empathy_os/wizard_factory_cli.py +0 -170
  214. empathy_software_plugin/wizards/__init__.py +0 -42
  215. empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
  216. empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
  217. empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
  218. empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
  219. empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
  220. empathy_software_plugin/wizards/base_wizard.py +0 -288
  221. empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
  222. empathy_software_plugin/wizards/code_review_wizard.py +0 -604
  223. empathy_software_plugin/wizards/debugging/__init__.py +0 -50
  224. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
  225. empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
  226. empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
  227. empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
  228. empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
  229. empathy_software_plugin/wizards/debugging/verification.py +0 -369
  230. empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
  231. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
  232. empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
  233. empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
  234. empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
  235. empathy_software_plugin/wizards/performance/__init__.py +0 -9
  236. empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
  237. empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
  238. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
  239. empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
  240. empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
  241. empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
  242. empathy_software_plugin/wizards/security/__init__.py +0 -32
  243. empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
  244. empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
  245. empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
  246. empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
  247. empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
  248. empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
  249. empathy_software_plugin/wizards/testing/__init__.py +0 -27
  250. empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
  251. empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
  252. empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
  253. empathy_software_plugin/wizards/testing_wizard.py +0 -274
  254. wizards/__init__.py +0 -82
  255. wizards/admission_assessment_wizard.py +0 -644
  256. wizards/care_plan.py +0 -321
  257. wizards/clinical_assessment.py +0 -769
  258. wizards/discharge_planning.py +0 -77
  259. wizards/discharge_summary_wizard.py +0 -468
  260. wizards/dosage_calculation.py +0 -497
  261. wizards/incident_report_wizard.py +0 -454
  262. wizards/medication_reconciliation.py +0 -85
  263. wizards/nursing_assessment.py +0 -171
  264. wizards/patient_education.py +0 -654
  265. wizards/quality_improvement.py +0 -705
  266. wizards/sbar_report.py +0 -324
  267. wizards/sbar_wizard.py +0 -608
  268. wizards/shift_handoff_wizard.py +0 -535
  269. wizards/soap_note_wizard.py +0 -679
  270. wizards/treatment_plan.py +0 -15
  271. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/WHEEL +0 -0
  272. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/entry_points.txt +0 -0
  273. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,769 +0,0 @@
1
- """
2
- Clinical Assessment Wizard - AI Nurse Florence
3
- Following Wizard Pattern Implementation from coding instructions
4
- Comprehensive patient assessment workflows with AI-powered clinical analysis
5
- """
6
-
7
- import json
8
- import logging
9
- from datetime import datetime
10
- from typing import Any
11
- from uuid import uuid4
12
-
13
- from fastapi import APIRouter, HTTPException
14
- from pydantic import BaseModel
15
-
16
- from ...services.openai_client import clinical_decision_support
17
- from ...utils.config import get_educational_banner
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
- # Redis import with fallback
22
- try:
23
- from src.utils.redis_cache import get_redis_client
24
-
25
- _has_redis = True
26
- except ImportError:
27
- _has_redis = False
28
-
29
- router = APIRouter(
30
- prefix="/wizard/clinical-assessment",
31
- tags=["wizards", "clinical-assessment"],
32
- responses={
33
- 404: {"description": "Wizard session not found"},
34
- 422: {"description": "Invalid step data"},
35
- },
36
- )
37
-
38
- # Session storage (Redis in production, memory for development)
39
- _wizard_sessions: dict[str, dict[str, Any]] = {}
40
-
41
-
42
- async def _store_wizard_session(wizard_id: str, session_data: dict[str, Any]):
43
- """Store wizard session in Redis or memory."""
44
- if _has_redis:
45
- try:
46
- redis_client = await get_redis_client()
47
- if redis_client:
48
- await redis_client.setex(
49
- f"wizard_session:{wizard_id}",
50
- 3600, # 1 hour expiry
51
- json.dumps(session_data),
52
- )
53
- return
54
- except Exception as e:
55
- logger.warning(f"Failed to store session in Redis: {e}")
56
-
57
- # Fallback to memory
58
- _wizard_sessions[wizard_id] = session_data
59
-
60
-
61
- async def _get_wizard_session(wizard_id: str) -> dict[str, Any] | None:
62
- """Retrieve wizard session from Redis or memory."""
63
- if _has_redis:
64
- try:
65
- redis_client = await get_redis_client()
66
- if redis_client:
67
- data = await redis_client.get(f"wizard_session:{wizard_id}")
68
- if data:
69
- return json.loads(data)
70
- except Exception as e:
71
- logger.warning(f"Failed to retrieve session from Redis: {e}")
72
-
73
- # Fallback to memory
74
- return _wizard_sessions.get(wizard_id)
75
-
76
-
77
- class AssessmentStepData(BaseModel):
78
- """Data model for assessment step submission."""
79
-
80
- step_data: dict[str, Any]
81
-
82
-
83
- @router.post("/start")
84
- async def start_clinical_assessment():
85
- """Start clinical assessment wizard following Wizard Pattern Implementation."""
86
- wizard_id = str(uuid4())
87
-
88
- session_data = {
89
- "wizard_id": wizard_id,
90
- "wizard_type": "clinical_assessment",
91
- "created_at": datetime.now().isoformat(),
92
- "current_step": 1,
93
- "total_steps": 6,
94
- "completed": False,
95
- "completed_steps": [],
96
- "data": {
97
- "vital_signs": {},
98
- "physical_assessment": {},
99
- "systems_review": {},
100
- "pain_assessment": {},
101
- "functional_assessment": {},
102
- "review_approval": {},
103
- },
104
- }
105
-
106
- await _store_wizard_session(wizard_id, session_data)
107
-
108
- return {
109
- "banner": get_educational_banner(),
110
- "wizard_id": wizard_id,
111
- "wizard_type": "clinical_assessment",
112
- "current_step": 1,
113
- "total_steps": 6,
114
- "step_title": "Vital Signs",
115
- "step_description": "Record patient vital signs and initial observations",
116
- "fields": [
117
- {"name": "temperature", "type": "number", "unit": "°F", "required": True},
118
- {"name": "pulse", "type": "number", "unit": "bpm", "required": True},
119
- {"name": "respirations", "type": "number", "unit": "/min", "required": True},
120
- {"name": "blood_pressure_systolic", "type": "number", "unit": "mmHg", "required": True},
121
- {
122
- "name": "blood_pressure_diastolic",
123
- "type": "number",
124
- "unit": "mmHg",
125
- "required": True,
126
- },
127
- {"name": "oxygen_saturation", "type": "number", "unit": "%", "required": True},
128
- {"name": "pain_scale", "type": "number", "min": 0, "max": 10, "required": True},
129
- ],
130
- "educational_note": "Vital signs should be compared to patient's baseline and age-appropriate norms.",
131
- }
132
-
133
-
134
- @router.get("/{wizard_id}/status")
135
- async def get_clinical_assessment_status(wizard_id: str):
136
- """Get clinical assessment wizard status following Wizard Pattern Implementation."""
137
-
138
- session = await _get_wizard_session(wizard_id)
139
- if not session:
140
- raise HTTPException(status_code=404, detail="Wizard session not found")
141
-
142
- return {
143
- "banner": get_educational_banner(),
144
- "wizard_id": wizard_id,
145
- "wizard_type": session["wizard_type"],
146
- "current_step": session["current_step"],
147
- "total_steps": session["total_steps"],
148
- "completed_steps": session["completed_steps"],
149
- "progress": len(session["completed_steps"]) / session["total_steps"] * 100,
150
- "status": (
151
- "completed"
152
- if len(session["completed_steps"]) == session["total_steps"]
153
- else "in_progress"
154
- ),
155
- "data": session["data"],
156
- }
157
-
158
-
159
- @router.post("/{wizard_id}/step/{step_number}")
160
- async def submit_clinical_assessment_step(
161
- wizard_id: str, step_number: int, step_data: AssessmentStepData
162
- ):
163
- """Submit clinical assessment step data following Wizard Pattern Implementation."""
164
-
165
- session = await _get_wizard_session(wizard_id)
166
- if not session:
167
- raise HTTPException(status_code=404, detail="Wizard session not found")
168
-
169
- if step_number != session["current_step"]:
170
- raise HTTPException(
171
- status_code=422,
172
- detail=f"Invalid step. Expected step {session['current_step']}, got step {step_number}",
173
- )
174
-
175
- # Store step data based on step number
176
- step_mapping = {
177
- 1: "vital_signs",
178
- 2: "physical_assessment",
179
- 3: "systems_review",
180
- 4: "pain_assessment",
181
- 5: "functional_assessment",
182
- 6: "review_approval",
183
- }
184
-
185
- if step_number in step_mapping:
186
- session["data"][step_mapping[step_number]] = step_data.step_data
187
-
188
- # Mark step as completed
189
- if step_number not in session["completed_steps"]:
190
- session["completed_steps"].append(step_number)
191
-
192
- # Generate AI analysis for the submitted step (but not for review step)
193
- if step_number < 6:
194
- ai_analysis = await _generate_ai_analysis(step_number, step_data.step_data, session["data"])
195
- else:
196
- ai_analysis = {
197
- "step_name": "Review & Finalize",
198
- "message": "Use /preview to generate report, then /save to finalize with approval",
199
- }
200
-
201
- # Move to next step (no auto-complete - requires explicit approval)
202
- if step_number < session["total_steps"]:
203
- session["current_step"] = step_number + 1
204
- next_step_info = _get_step_info(step_number + 1)
205
- else:
206
- # At step 6 (review) - don't advance, wait for /save
207
- next_step_info = None
208
-
209
- # Store updated session
210
- await _store_wizard_session(wizard_id, session)
211
-
212
- return {
213
- "banner": get_educational_banner(),
214
- "wizard_id": wizard_id,
215
- "step_completed": step_number,
216
- "current_step": session["current_step"],
217
- "total_steps": session["total_steps"],
218
- "progress": len(session["completed_steps"]) / session["total_steps"] * 100,
219
- "status": (
220
- "completed"
221
- if len(session["completed_steps"]) == session["total_steps"]
222
- else "in_progress"
223
- ),
224
- "ai_analysis": ai_analysis,
225
- "next_step": next_step_info,
226
- }
227
-
228
-
229
- @router.get("/{wizard_id}/step/{step_number}")
230
- async def get_clinical_assessment_step(wizard_id: str, step_number: int):
231
- """Get clinical assessment step information."""
232
-
233
- session = await _get_wizard_session(wizard_id)
234
- if not session:
235
- raise HTTPException(status_code=404, detail="Wizard session not found")
236
-
237
- if step_number < 1 or step_number > session["total_steps"]:
238
- raise HTTPException(status_code=422, detail="Invalid step number")
239
-
240
- step_info = _get_step_info(step_number)
241
-
242
- # Get previously entered data if exists
243
- step_mapping = {
244
- 1: "vital_signs",
245
- 2: "physical_assessment",
246
- 3: "systems_review",
247
- 4: "pain_assessment",
248
- 5: "functional_assessment",
249
- 6: "review_approval",
250
- }
251
-
252
- existing_data = session["data"].get(step_mapping.get(step_number, ""), {})
253
-
254
- return {
255
- "banner": get_educational_banner(),
256
- "wizard_id": wizard_id,
257
- "step_number": step_number,
258
- "existing_data": existing_data,
259
- **step_info,
260
- }
261
-
262
-
263
- @router.delete("/{wizard_id}")
264
- async def cancel_clinical_assessment(wizard_id: str):
265
- """Cancel and delete clinical assessment wizard session."""
266
-
267
- session = await _get_wizard_session(wizard_id)
268
- if not session:
269
- raise HTTPException(status_code=404, detail="Wizard session not found")
270
-
271
- # Delete from both Redis and memory
272
- if _has_redis:
273
- try:
274
- redis_client = await get_redis_client()
275
- if redis_client:
276
- await redis_client.delete(f"wizard_session:{wizard_id}")
277
- except Exception as e:
278
- logger.warning(f"Failed to delete session from Redis: {e}")
279
-
280
- _wizard_sessions.pop(wizard_id, None)
281
-
282
- return {
283
- "banner": get_educational_banner(),
284
- "message": "Clinical assessment wizard session cancelled",
285
- "wizard_id": wizard_id,
286
- }
287
-
288
-
289
- @router.post("/{wizard_id}/preview")
290
- async def preview_clinical_assessment(wizard_id: str):
291
- """
292
- Generate preview of clinical assessment report.
293
- This does NOT mark the assessment as completed.
294
- Requires user to call /save endpoint with approval to finalize.
295
- """
296
- session = await _get_wizard_session(wizard_id)
297
- if not session:
298
- raise HTTPException(status_code=404, detail="Wizard session not found")
299
-
300
- if session.get("completed", False):
301
- raise HTTPException(status_code=422, detail="Assessment already completed")
302
-
303
- # Generate assessment report preview
304
- collected_data = session["data"]
305
- preview_report = _generate_clinical_assessment_report(collected_data)
306
-
307
- # Store preview in session (does NOT mark as completed)
308
- session["preview_report"] = preview_report
309
- session["preview_generated_at"] = datetime.now().isoformat()
310
-
311
- # Store updated session
312
- await _store_wizard_session(wizard_id, session)
313
-
314
- return {
315
- "banner": get_educational_banner(),
316
- "success": True,
317
- "wizard_id": wizard_id,
318
- "message": "Clinical assessment preview generated. Please review and use /save endpoint to finalize.",
319
- "data": {"preview": preview_report, "generated_at": session["preview_generated_at"]},
320
- }
321
-
322
-
323
- @router.post("/{wizard_id}/save")
324
- async def save_clinical_assessment(wizard_id: str, approval_data: dict[str, Any]):
325
- """
326
- Finalize and save clinical assessment with user approval.
327
- Requires user_approved: true in request body.
328
- This is the ONLY endpoint that marks the assessment as completed.
329
- """
330
- session = await _get_wizard_session(wizard_id)
331
- if not session:
332
- raise HTTPException(status_code=404, detail="Wizard session not found")
333
-
334
- if session.get("completed", False):
335
- raise HTTPException(status_code=422, detail="Assessment already completed")
336
-
337
- # Require preview before save
338
- if "preview_report" not in session:
339
- raise HTTPException(
340
- status_code=422,
341
- detail="Must generate preview before saving. Call /preview endpoint first.",
342
- )
343
-
344
- # Require explicit user approval
345
- if not approval_data.get("user_approved", False):
346
- raise HTTPException(
347
- status_code=422,
348
- detail="User approval required. Set user_approved: true to finalize assessment.",
349
- )
350
-
351
- # Mark as completed with user approval
352
- session["completed"] = True
353
- session["completed_at"] = datetime.now().isoformat()
354
- session["user_approved"] = True
355
- session["approved_by"] = approval_data.get("approved_by", "Unknown user")
356
-
357
- # Store updated session
358
- await _store_wizard_session(wizard_id, session)
359
-
360
- return {
361
- "banner": get_educational_banner(),
362
- "success": True,
363
- "wizard_id": wizard_id,
364
- "message": "Clinical assessment finalized and saved successfully.",
365
- "data": {
366
- "report": session["preview_report"],
367
- "completed_at": session["completed_at"],
368
- "user_approved": True,
369
- "approved_by": session["approved_by"],
370
- },
371
- }
372
-
373
-
374
- def _generate_clinical_assessment_report(collected_data: dict[str, Any]) -> dict[str, Any]:
375
- """
376
- Generate formatted clinical assessment report from collected data.
377
- Internal function used by /preview and /save endpoints.
378
- """
379
- vital_signs = collected_data.get("vital_signs", {})
380
- physical = collected_data.get("physical_assessment", {})
381
- systems = collected_data.get("systems_review", {})
382
- pain = collected_data.get("pain_assessment", {})
383
- functional = collected_data.get("functional_assessment", {})
384
-
385
- # Format narrative
386
- narrative = f"""
387
- COMPREHENSIVE CLINICAL ASSESSMENT REPORT
388
- {'=' * 80}
389
-
390
- ASSESSMENT DATE: {datetime.now().strftime('%Y-%m-%d %H:%M')}
391
-
392
- VITAL SIGNS
393
- Temperature: {vital_signs.get('temperature', 'Not documented')}°F
394
- Pulse: {vital_signs.get('pulse', 'Not documented')} bpm
395
- Respirations: {vital_signs.get('respirations', 'Not documented')}/min
396
- Blood Pressure: {vital_signs.get('blood_pressure_systolic', 'N/A')}/{vital_signs.get('blood_pressure_diastolic', 'N/A')} mmHg
397
- Oxygen Saturation: {vital_signs.get('oxygen_saturation', 'Not documented')}%
398
- Pain Scale: {vital_signs.get('pain_scale', 'Not assessed')}/10
399
-
400
- PHYSICAL ASSESSMENT
401
- General Appearance: {physical.get('general_appearance', 'Not documented')}
402
- Skin: {physical.get('skin_assessment', 'Not documented')}
403
- HEENT: {physical.get('heent', 'Not documented')}
404
- Cardiovascular: {physical.get('cardiovascular', 'Not documented')}
405
- Respiratory: {physical.get('respiratory', 'Not documented')}
406
- Gastrointestinal: {physical.get('gastrointestinal', 'Not documented')}
407
- Musculoskeletal: {physical.get('musculoskeletal', 'Not documented')}
408
- Neurological: {physical.get('neurological', 'Not documented')}
409
-
410
- SYSTEMS REVIEW
411
- Cardiovascular: {systems.get('cardiovascular_review', 'Not documented')}
412
- Respiratory: {systems.get('respiratory_review', 'Not documented')}
413
- Gastrointestinal: {systems.get('gastrointestinal_review', 'Not documented')}
414
- Genitourinary: {systems.get('genitourinary_review', 'Not documented')}
415
- Musculoskeletal: {systems.get('musculoskeletal_review', 'Not documented')}
416
- Neurological: {systems.get('neurological_review', 'Not documented')}
417
- Integumentary: {systems.get('integumentary_review', 'Not documented')}
418
-
419
- PAIN ASSESSMENT
420
- Location: {pain.get('pain_location', 'Not documented')}
421
- Intensity: {pain.get('pain_intensity', 'Not assessed')}/10
422
- Character: {pain.get('pain_character', 'Not documented')}
423
- Onset: {pain.get('pain_onset', 'Not documented')}
424
- Duration: {pain.get('pain_duration', 'Not documented')}
425
- Aggravating Factors: {pain.get('aggravating_factors', 'Not documented')}
426
- Relieving Factors: {pain.get('relieving_factors', 'Not documented')}
427
- Impact on Activities: {pain.get('pain_impact', 'Not documented')}
428
-
429
- FUNCTIONAL ASSESSMENT
430
- Mobility: {functional.get('mobility', 'Not documented')}
431
- ADL Independence: {functional.get('adl_independence', 'Not documented')}
432
- Fall Risk: {functional.get('fall_risk', 'Not documented')}
433
- Cognitive Status: {functional.get('cognitive_status', 'Not documented')}
434
- Communication: {functional.get('communication_ability', 'Not documented')}
435
- Nutrition: {functional.get('nutritional_status', 'Not documented')}
436
- Elimination: {functional.get('elimination_patterns', 'Not documented')}
437
-
438
- {'=' * 80}
439
- Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
440
- """
441
-
442
- return {
443
- "report_type": "clinical_assessment",
444
- "narrative": narrative.strip(),
445
- "structured_data": {
446
- "vital_signs": vital_signs,
447
- "physical_assessment": physical,
448
- "systems_review": systems,
449
- "pain_assessment": pain,
450
- "functional_assessment": functional,
451
- },
452
- "metadata": {
453
- "generated_at": datetime.now().isoformat(),
454
- "wizard_type": "clinical_assessment",
455
- },
456
- }
457
-
458
-
459
- def _get_step_info(step_number: int) -> dict[str, Any]:
460
- """Get step configuration information."""
461
-
462
- steps = {
463
- 1: {
464
- "step_title": "Vital Signs",
465
- "step_description": "Record patient vital signs and initial observations",
466
- "fields": [
467
- {"name": "temperature", "type": "number", "unit": "°F", "required": True},
468
- {"name": "pulse", "type": "number", "unit": "bpm", "required": True},
469
- {"name": "respirations", "type": "number", "unit": "/min", "required": True},
470
- {
471
- "name": "blood_pressure_systolic",
472
- "type": "number",
473
- "unit": "mmHg",
474
- "required": True,
475
- },
476
- {
477
- "name": "blood_pressure_diastolic",
478
- "type": "number",
479
- "unit": "mmHg",
480
- "required": True,
481
- },
482
- {"name": "oxygen_saturation", "type": "number", "unit": "%", "required": True},
483
- {"name": "pain_scale", "type": "number", "min": 0, "max": 10, "required": True},
484
- ],
485
- "educational_note": "Vital signs should be compared to patient's baseline and age-appropriate norms.",
486
- },
487
- 2: {
488
- "step_title": "Physical Assessment",
489
- "step_description": "Conduct head-to-toe physical examination",
490
- "fields": [
491
- {"name": "general_appearance", "type": "text", "required": True},
492
- {"name": "skin_assessment", "type": "text", "required": True},
493
- {
494
- "name": "heent",
495
- "type": "text",
496
- "label": "HEENT (Head, Eyes, Ears, Nose, Throat)",
497
- "required": True,
498
- },
499
- {"name": "cardiovascular", "type": "text", "required": True},
500
- {"name": "respiratory", "type": "text", "required": True},
501
- {"name": "gastrointestinal", "type": "text", "required": True},
502
- {"name": "musculoskeletal", "type": "text", "required": True},
503
- {"name": "neurological", "type": "text", "required": True},
504
- ],
505
- "educational_note": "Use systematic head-to-toe approach. Document normal and abnormal findings.",
506
- },
507
- 3: {
508
- "step_title": "Systems Review",
509
- "step_description": "Review all body systems for abnormalities",
510
- "fields": [
511
- {"name": "cardiovascular_review", "type": "textarea", "required": True},
512
- {"name": "respiratory_review", "type": "textarea", "required": True},
513
- {"name": "gastrointestinal_review", "type": "textarea", "required": True},
514
- {"name": "genitourinary_review", "type": "textarea", "required": True},
515
- {"name": "musculoskeletal_review", "type": "textarea", "required": True},
516
- {"name": "neurological_review", "type": "textarea", "required": True},
517
- {"name": "integumentary_review", "type": "textarea", "required": True},
518
- {"name": "endocrine_review", "type": "textarea", "required": False},
519
- ],
520
- "educational_note": "Document both positive and negative findings for comprehensive assessment.",
521
- },
522
- 4: {
523
- "step_title": "Pain Assessment",
524
- "step_description": "Comprehensive pain evaluation using standardized tools",
525
- "fields": [
526
- {"name": "pain_location", "type": "text", "required": True},
527
- {"name": "pain_intensity", "type": "number", "min": 0, "max": 10, "required": True},
528
- {
529
- "name": "pain_character",
530
- "type": "select",
531
- "options": ["Sharp", "Dull", "Aching", "Burning", "Stabbing", "Throbbing"],
532
- "required": True,
533
- },
534
- {"name": "pain_onset", "type": "text", "required": True},
535
- {"name": "pain_duration", "type": "text", "required": True},
536
- {"name": "aggravating_factors", "type": "textarea", "required": True},
537
- {"name": "relieving_factors", "type": "textarea", "required": True},
538
- {
539
- "name": "pain_impact",
540
- "type": "textarea",
541
- "label": "Impact on daily activities",
542
- "required": True,
543
- },
544
- ],
545
- "educational_note": "Use PQRST or OLDCARTS mnemonic for comprehensive pain assessment.",
546
- },
547
- 5: {
548
- "step_title": "Functional Assessment",
549
- "step_description": "Evaluate patient's functional status and activities of daily living",
550
- "fields": [
551
- {
552
- "name": "mobility",
553
- "type": "select",
554
- "options": ["Independent", "Assisted", "Limited", "Bedbound"],
555
- "required": True,
556
- },
557
- {
558
- "name": "adl_independence",
559
- "type": "select",
560
- "label": "ADL Independence",
561
- "options": [
562
- "Fully independent",
563
- "Needs minimal assistance",
564
- "Needs moderate assistance",
565
- "Fully dependent",
566
- ],
567
- "required": True,
568
- },
569
- {
570
- "name": "fall_risk",
571
- "type": "select",
572
- "options": ["Low", "Moderate", "High"],
573
- "required": True,
574
- },
575
- {
576
- "name": "cognitive_status",
577
- "type": "select",
578
- "options": [
579
- "Alert and oriented x4",
580
- "Alert and oriented x3",
581
- "Confused",
582
- "Lethargic",
583
- "Unresponsive",
584
- ],
585
- "required": True,
586
- },
587
- {"name": "communication_ability", "type": "text", "required": True},
588
- {"name": "nutritional_status", "type": "textarea", "required": True},
589
- {"name": "elimination_patterns", "type": "textarea", "required": True},
590
- ],
591
- "educational_note": "Functional assessment helps determine care needs and discharge planning.",
592
- },
593
- 6: {
594
- "step_title": "Review & Finalize",
595
- "step_description": "Review complete clinical assessment and finalize with approval",
596
- "fields": [
597
- {
598
- "name": "review_complete",
599
- "type": "boolean",
600
- "label": "I have reviewed the complete assessment",
601
- "required": True,
602
- },
603
- {
604
- "name": "user_approved",
605
- "type": "boolean",
606
- "label": "I approve this assessment for finalization",
607
- "required": True,
608
- },
609
- ],
610
- "educational_note": "Review all collected data before finalizing. Use /preview to generate the report, then /save to complete.",
611
- "is_review_step": True,
612
- },
613
- }
614
-
615
- return steps.get(step_number, {})
616
-
617
-
618
- async def _generate_ai_analysis(
619
- step_number: int, step_data: dict[str, Any], all_data: dict[str, Any]
620
- ) -> dict[str, Any]:
621
- """
622
- Generate AI-powered clinical analysis for assessment step.
623
- Uses OpenAI to provide evidence-based clinical insights and recommendations.
624
- """
625
-
626
- step_names = {
627
- 1: "Vital Signs",
628
- 2: "Physical Assessment",
629
- 3: "Systems Review",
630
- 4: "Pain Assessment",
631
- 5: "Functional Assessment",
632
- }
633
-
634
- step_name = step_names.get(step_number, "Unknown Step")
635
-
636
- try:
637
- # Create clinical analysis prompt based on step
638
- if step_number == 1:
639
- # Vital signs analysis
640
- clinical_question = f"""Analyze the following vital signs and provide clinical assessment:
641
-
642
- Vital Signs:
643
- - Temperature: {step_data.get('temperature', 'N/A')}°F
644
- - Pulse: {step_data.get('pulse', 'N/A')} bpm
645
- - Respirations: {step_data.get('respirations', 'N/A')}/min
646
- - Blood Pressure: {step_data.get('blood_pressure_systolic', 'N/A')}/{step_data.get('blood_pressure_diastolic', 'N/A')} mmHg
647
- - Oxygen Saturation: {step_data.get('oxygen_saturation', 'N/A')}%
648
- - Pain Scale: {step_data.get('pain_scale', 'N/A')}/10
649
-
650
- Please provide:
651
- 1. Assessment of each vital sign (normal/abnormal)
652
- 2. Clinical significance of any abnormalities
653
- 3. Recommended follow-up assessments
654
- 4. Potential urgent concerns requiring escalation
655
- """
656
-
657
- elif step_number == 2:
658
- # Physical assessment analysis
659
- clinical_question = f"""Analyze the following physical assessment findings:
660
-
661
- {step_data}
662
-
663
- Provide:
664
- 1. Key positive and negative findings
665
- 2. Clinical patterns or concerns identified
666
- 3. Differential considerations based on findings
667
- 4. Recommended focused assessments
668
- """
669
-
670
- elif step_number == 3:
671
- # Systems review analysis
672
- clinical_question = f"""Analyze the following systems review:
673
-
674
- {step_data}
675
-
676
- Provide:
677
- 1. Systems with concerning findings
678
- 2. Inter-system correlations or patterns
679
- 3. Priority systems requiring further evaluation
680
- 4. Clinical decision support recommendations
681
- """
682
-
683
- elif step_number == 4:
684
- # Pain assessment analysis
685
- clinical_question = f"""Analyze the following pain assessment:
686
-
687
- Location: {step_data.get('pain_location', 'N/A')}
688
- Intensity: {step_data.get('pain_intensity', 'N/A')}/10
689
- Character: {step_data.get('pain_character', 'N/A')}
690
- Onset: {step_data.get('pain_onset', 'N/A')}
691
- Duration: {step_data.get('pain_duration', 'N/A')}
692
- Aggravating Factors: {step_data.get('aggravating_factors', 'N/A')}
693
- Relieving Factors: {step_data.get('relieving_factors', 'N/A')}
694
- Impact: {step_data.get('pain_impact', 'N/A')}
695
-
696
- Provide:
697
- 1. Pain pattern analysis and likely etiology
698
- 2. Evidence-based pain management recommendations
699
- 3. Red flags requiring immediate attention
700
- 4. Non-pharmacological interventions to consider
701
- """
702
-
703
- elif step_number == 5:
704
- # Functional assessment analysis - final comprehensive summary
705
- clinical_question = f"""Provide comprehensive clinical summary based on complete assessment:
706
-
707
- Vital Signs: {all_data.get('vital_signs', {})}
708
- Physical Assessment: {all_data.get('physical_assessment', {})}
709
- Systems Review: {all_data.get('systems_review', {})}
710
- Pain Assessment: {all_data.get('pain_assessment', {})}
711
- Functional Status: {step_data}
712
-
713
- Provide:
714
- 1. Comprehensive clinical summary
715
- 2. Priority nursing diagnoses
716
- 3. Key safety concerns and interventions
717
- 4. Care planning recommendations
718
- 5. Discharge planning considerations
719
- """
720
-
721
- else:
722
- clinical_question = f"Analyze assessment data for {step_name}: {step_data}"
723
-
724
- # Call AI clinical decision support
725
- ai_response = await clinical_decision_support(
726
- patient_data=step_data,
727
- clinical_question=clinical_question,
728
- context="clinical_assessment",
729
- )
730
-
731
- return {
732
- "step_name": step_name,
733
- "analysis_available": True,
734
- "clinical_insights": ai_response.get("response", "AI analysis temporarily unavailable"),
735
- "recommendations": _extract_recommendations(ai_response),
736
- "disclaimer": "AI-generated clinical insights for educational support. All clinical decisions require professional nursing judgment.",
737
- "ai_model": ai_response.get("model", "gpt-4"),
738
- "service_status": ai_response.get("service_status", "available"),
739
- }
740
-
741
- except Exception as e:
742
- logger.error(f"AI analysis failed for step {step_number}: {e}")
743
- return {
744
- "step_name": step_name,
745
- "analysis_available": False,
746
- "message": "AI analysis temporarily unavailable",
747
- "fallback_note": "Continue with clinical assessment using professional judgment and clinical protocols",
748
- "error": str(e),
749
- }
750
-
751
-
752
- def _extract_recommendations(ai_response: dict[str, Any]) -> list[str]:
753
- """Extract actionable recommendations from AI response."""
754
- response_text = ai_response.get("response", "")
755
-
756
- # Simple extraction - look for numbered or bulleted lists
757
- recommendations = []
758
- lines = response_text.split("\n")
759
-
760
- for line in lines:
761
- line = line.strip()
762
- # Match numbered lists (1., 2., etc.) or bullet points (-, *, •)
763
- if line and (line[0].isdigit() or line.startswith(("-", "*", "•"))):
764
- # Clean up the line
765
- cleaned = line.lstrip("0123456789.-*• ")
766
- if cleaned and len(cleaned) > 10: # Only include substantial recommendations
767
- recommendations.append(cleaned)
768
-
769
- return recommendations[:5] if recommendations else ["Continue systematic clinical assessment"]