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.
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/METADATA +7 -6
- empathy_framework-4.7.0.dist-info/RECORD +354 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/top_level.txt +0 -2
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
- empathy_llm_toolkit/agent_factory/__init__.py +6 -6
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
- empathy_llm_toolkit/agents_md/__init__.py +22 -0
- empathy_llm_toolkit/agents_md/loader.py +218 -0
- empathy_llm_toolkit/agents_md/parser.py +271 -0
- empathy_llm_toolkit/agents_md/registry.py +307 -0
- empathy_llm_toolkit/commands/__init__.py +51 -0
- empathy_llm_toolkit/commands/context.py +375 -0
- empathy_llm_toolkit/commands/loader.py +301 -0
- empathy_llm_toolkit/commands/models.py +231 -0
- empathy_llm_toolkit/commands/parser.py +371 -0
- empathy_llm_toolkit/commands/registry.py +429 -0
- empathy_llm_toolkit/config/__init__.py +8 -8
- empathy_llm_toolkit/config/unified.py +3 -7
- empathy_llm_toolkit/context/__init__.py +22 -0
- empathy_llm_toolkit/context/compaction.py +455 -0
- empathy_llm_toolkit/context/manager.py +434 -0
- empathy_llm_toolkit/hooks/__init__.py +24 -0
- empathy_llm_toolkit/hooks/config.py +306 -0
- empathy_llm_toolkit/hooks/executor.py +289 -0
- empathy_llm_toolkit/hooks/registry.py +302 -0
- empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
- empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
- empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
- empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
- empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
- empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
- empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
- empathy_llm_toolkit/learning/__init__.py +30 -0
- empathy_llm_toolkit/learning/evaluator.py +438 -0
- empathy_llm_toolkit/learning/extractor.py +514 -0
- empathy_llm_toolkit/learning/storage.py +560 -0
- empathy_llm_toolkit/providers.py +4 -11
- empathy_llm_toolkit/security/__init__.py +17 -17
- empathy_llm_toolkit/utils/tokens.py +2 -5
- empathy_os/__init__.py +202 -70
- empathy_os/cache_monitor.py +5 -3
- empathy_os/cli/__init__.py +11 -55
- empathy_os/cli/__main__.py +29 -15
- empathy_os/cli/commands/inspection.py +21 -12
- empathy_os/cli/commands/memory.py +4 -12
- empathy_os/cli/commands/profiling.py +198 -0
- empathy_os/cli/commands/utilities.py +27 -7
- empathy_os/cli.py +28 -57
- empathy_os/cli_unified.py +525 -1164
- empathy_os/cost_tracker.py +9 -3
- empathy_os/dashboard/server.py +200 -2
- empathy_os/hot_reload/__init__.py +7 -7
- empathy_os/hot_reload/config.py +6 -7
- empathy_os/hot_reload/integration.py +35 -35
- empathy_os/hot_reload/reloader.py +57 -57
- empathy_os/hot_reload/watcher.py +28 -28
- empathy_os/hot_reload/websocket.py +2 -2
- empathy_os/memory/__init__.py +11 -4
- empathy_os/memory/claude_memory.py +1 -1
- empathy_os/memory/cross_session.py +8 -12
- empathy_os/memory/edges.py +6 -6
- empathy_os/memory/file_session.py +770 -0
- empathy_os/memory/graph.py +30 -30
- empathy_os/memory/nodes.py +6 -6
- empathy_os/memory/short_term.py +15 -9
- empathy_os/memory/unified.py +606 -140
- empathy_os/meta_workflows/agent_creator.py +3 -9
- empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
- empathy_os/meta_workflows/form_engine.py +6 -18
- empathy_os/meta_workflows/intent_detector.py +64 -24
- empathy_os/meta_workflows/models.py +3 -1
- empathy_os/meta_workflows/pattern_learner.py +13 -31
- empathy_os/meta_workflows/plan_generator.py +55 -47
- empathy_os/meta_workflows/session_context.py +2 -3
- empathy_os/meta_workflows/workflow.py +20 -51
- empathy_os/models/cli.py +2 -2
- empathy_os/models/tasks.py +1 -2
- empathy_os/models/telemetry.py +4 -1
- empathy_os/models/token_estimator.py +3 -1
- empathy_os/monitoring/alerts.py +938 -9
- empathy_os/monitoring/alerts_cli.py +346 -183
- empathy_os/orchestration/execution_strategies.py +12 -29
- empathy_os/orchestration/pattern_learner.py +20 -26
- empathy_os/orchestration/real_tools.py +6 -15
- empathy_os/platform_utils.py +2 -1
- empathy_os/plugins/__init__.py +2 -2
- empathy_os/plugins/base.py +64 -64
- empathy_os/plugins/registry.py +32 -32
- empathy_os/project_index/index.py +49 -15
- empathy_os/project_index/models.py +1 -2
- empathy_os/project_index/reports.py +1 -1
- empathy_os/project_index/scanner.py +1 -0
- empathy_os/redis_memory.py +10 -7
- empathy_os/resilience/__init__.py +1 -1
- empathy_os/resilience/health.py +10 -10
- empathy_os/routing/__init__.py +7 -7
- empathy_os/routing/chain_executor.py +37 -37
- empathy_os/routing/classifier.py +36 -36
- empathy_os/routing/smart_router.py +40 -40
- empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
- empathy_os/scaffolding/__init__.py +8 -8
- empathy_os/scaffolding/__main__.py +1 -1
- empathy_os/scaffolding/cli.py +28 -28
- empathy_os/socratic/__init__.py +3 -19
- empathy_os/socratic/ab_testing.py +25 -36
- empathy_os/socratic/blueprint.py +38 -38
- empathy_os/socratic/cli.py +34 -20
- empathy_os/socratic/collaboration.py +30 -28
- empathy_os/socratic/domain_templates.py +9 -1
- empathy_os/socratic/embeddings.py +17 -13
- empathy_os/socratic/engine.py +135 -70
- empathy_os/socratic/explainer.py +70 -60
- empathy_os/socratic/feedback.py +24 -19
- empathy_os/socratic/forms.py +15 -10
- empathy_os/socratic/generator.py +51 -35
- empathy_os/socratic/llm_analyzer.py +25 -23
- empathy_os/socratic/mcp_server.py +99 -159
- empathy_os/socratic/session.py +19 -13
- empathy_os/socratic/storage.py +98 -67
- empathy_os/socratic/success.py +38 -27
- empathy_os/socratic/visual_editor.py +51 -39
- empathy_os/socratic/web_ui.py +99 -66
- empathy_os/telemetry/cli.py +3 -1
- empathy_os/telemetry/usage_tracker.py +1 -3
- empathy_os/test_generator/__init__.py +3 -3
- empathy_os/test_generator/cli.py +28 -28
- empathy_os/test_generator/generator.py +64 -66
- empathy_os/test_generator/risk_analyzer.py +11 -11
- empathy_os/vscode_bridge.py +173 -0
- empathy_os/workflows/__init__.py +212 -120
- empathy_os/workflows/batch_processing.py +8 -24
- empathy_os/workflows/bug_predict.py +1 -1
- empathy_os/workflows/code_review.py +20 -5
- empathy_os/workflows/code_review_pipeline.py +13 -8
- empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
- empathy_os/workflows/manage_documentation.py +1 -0
- empathy_os/workflows/orchestrated_health_check.py +6 -11
- empathy_os/workflows/orchestrated_release_prep.py +3 -3
- empathy_os/workflows/pr_review.py +18 -10
- empathy_os/workflows/progressive/__init__.py +2 -12
- empathy_os/workflows/progressive/cli.py +14 -37
- empathy_os/workflows/progressive/core.py +12 -12
- empathy_os/workflows/progressive/orchestrator.py +166 -144
- empathy_os/workflows/progressive/reports.py +22 -31
- empathy_os/workflows/progressive/telemetry.py +8 -14
- empathy_os/workflows/progressive/test_gen.py +29 -48
- empathy_os/workflows/progressive/workflow.py +31 -70
- empathy_os/workflows/release_prep.py +21 -6
- empathy_os/workflows/release_prep_crew.py +1 -0
- empathy_os/workflows/secure_release.py +13 -6
- empathy_os/workflows/security_audit.py +8 -3
- empathy_os/workflows/test_coverage_boost_crew.py +3 -2
- empathy_os/workflows/test_maintenance_crew.py +1 -0
- empathy_os/workflows/test_runner.py +16 -12
- empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
- empathy_software_plugin/cli.py +0 -122
- coach_wizards/__init__.py +0 -45
- coach_wizards/accessibility_wizard.py +0 -91
- coach_wizards/api_wizard.py +0 -91
- coach_wizards/base_wizard.py +0 -209
- coach_wizards/cicd_wizard.py +0 -91
- coach_wizards/code_reviewer_README.md +0 -60
- coach_wizards/code_reviewer_wizard.py +0 -180
- coach_wizards/compliance_wizard.py +0 -91
- coach_wizards/database_wizard.py +0 -91
- coach_wizards/debugging_wizard.py +0 -91
- coach_wizards/documentation_wizard.py +0 -91
- coach_wizards/generate_wizards.py +0 -347
- coach_wizards/localization_wizard.py +0 -173
- coach_wizards/migration_wizard.py +0 -91
- coach_wizards/monitoring_wizard.py +0 -91
- coach_wizards/observability_wizard.py +0 -91
- coach_wizards/performance_wizard.py +0 -91
- coach_wizards/prompt_engineering_wizard.py +0 -661
- coach_wizards/refactoring_wizard.py +0 -91
- coach_wizards/scaling_wizard.py +0 -90
- coach_wizards/security_wizard.py +0 -92
- coach_wizards/testing_wizard.py +0 -91
- empathy_framework-4.6.6.dist-info/RECORD +0 -410
- empathy_llm_toolkit/wizards/__init__.py +0 -43
- empathy_llm_toolkit/wizards/base_wizard.py +0 -364
- empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
- empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
- empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
- empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
- empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
- empathy_os/wizard_factory_cli.py +0 -170
- empathy_software_plugin/wizards/__init__.py +0 -42
- empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
- empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
- empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
- empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
- empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
- empathy_software_plugin/wizards/base_wizard.py +0 -288
- empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
- empathy_software_plugin/wizards/code_review_wizard.py +0 -604
- empathy_software_plugin/wizards/debugging/__init__.py +0 -50
- empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
- empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
- empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
- empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
- empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
- empathy_software_plugin/wizards/debugging/verification.py +0 -369
- empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
- empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
- empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
- empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
- empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
- empathy_software_plugin/wizards/performance/__init__.py +0 -9
- empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
- empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
- empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
- empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
- empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
- empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
- empathy_software_plugin/wizards/security/__init__.py +0 -32
- empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
- empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
- empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
- empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
- empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
- empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
- empathy_software_plugin/wizards/testing/__init__.py +0 -27
- empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
- empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
- empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
- empathy_software_plugin/wizards/testing_wizard.py +0 -274
- wizards/__init__.py +0 -82
- wizards/admission_assessment_wizard.py +0 -644
- wizards/care_plan.py +0 -321
- wizards/clinical_assessment.py +0 -769
- wizards/discharge_planning.py +0 -77
- wizards/discharge_summary_wizard.py +0 -468
- wizards/dosage_calculation.py +0 -497
- wizards/incident_report_wizard.py +0 -454
- wizards/medication_reconciliation.py +0 -85
- wizards/nursing_assessment.py +0 -171
- wizards/patient_education.py +0 -654
- wizards/quality_improvement.py +0 -705
- wizards/sbar_report.py +0 -324
- wizards/sbar_wizard.py +0 -608
- wizards/shift_handoff_wizard.py +0 -535
- wizards/soap_note_wizard.py +0 -679
- wizards/treatment_plan.py +0 -15
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/WHEEL +0 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/entry_points.txt +0 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/licenses/LICENSE +0 -0
wizards/shift_handoff_wizard.py
DELETED
|
@@ -1,535 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Shift Handoff Wizard Router - AI Nurse Florence
|
|
3
|
-
Following Wizard Pattern Implementation from SBAR wizard
|
|
4
|
-
|
|
5
|
-
Nurse-to-nurse shift handoff documentation wizard for safe patient transitions.
|
|
6
|
-
Based on best practices for bedside handoff communication.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import logging
|
|
11
|
-
from datetime import datetime
|
|
12
|
-
from typing import Any
|
|
13
|
-
from uuid import uuid4
|
|
14
|
-
|
|
15
|
-
from fastapi import APIRouter, HTTPException, status
|
|
16
|
-
from src.services import get_service
|
|
17
|
-
from src.utils.api_responses import create_success_response
|
|
18
|
-
from src.utils.config import get_settings
|
|
19
|
-
|
|
20
|
-
logger = logging.getLogger(__name__)
|
|
21
|
-
|
|
22
|
-
# Conditional translation import
|
|
23
|
-
try:
|
|
24
|
-
from src.services.translation_service import translate_text
|
|
25
|
-
|
|
26
|
-
_has_translation = True
|
|
27
|
-
except ImportError:
|
|
28
|
-
_has_translation = False
|
|
29
|
-
|
|
30
|
-
async def translate_text(
|
|
31
|
-
text: str,
|
|
32
|
-
target_language: str,
|
|
33
|
-
source_language: str = "en",
|
|
34
|
-
context: str = "medical",
|
|
35
|
-
):
|
|
36
|
-
return {"translated_text": text, "success": False}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# Settings following coding instructions
|
|
40
|
-
settings = get_settings()
|
|
41
|
-
|
|
42
|
-
router = APIRouter(
|
|
43
|
-
prefix="/wizards/shift-handoff",
|
|
44
|
-
tags=["clinical-wizards"],
|
|
45
|
-
responses={
|
|
46
|
-
404: {"description": "Wizard session not found"},
|
|
47
|
-
422: {"description": "Invalid wizard step data"},
|
|
48
|
-
500: {"description": "Wizard processing error"},
|
|
49
|
-
},
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Session storage (Redis in production, memory for development)
|
|
53
|
-
try:
|
|
54
|
-
from src.utils.redis_cache import get_redis_client
|
|
55
|
-
|
|
56
|
-
_has_redis = True
|
|
57
|
-
except ImportError:
|
|
58
|
-
_has_redis = False
|
|
59
|
-
|
|
60
|
-
_wizard_sessions: dict[str, dict[str, Any]] = {}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# Shift handoff wizard steps
|
|
64
|
-
SHIFT_HANDOFF_STEPS = {
|
|
65
|
-
1: {
|
|
66
|
-
"step": 1,
|
|
67
|
-
"title": "Patient Identification & Status",
|
|
68
|
-
"prompt": "Identify the patient and current clinical status",
|
|
69
|
-
"fields": [
|
|
70
|
-
"patient_name",
|
|
71
|
-
"room_bed",
|
|
72
|
-
"age",
|
|
73
|
-
"diagnosis",
|
|
74
|
-
"admission_date",
|
|
75
|
-
"code_status",
|
|
76
|
-
],
|
|
77
|
-
"help_text": "Start with patient basics: name, location, age, diagnosis, and code status",
|
|
78
|
-
},
|
|
79
|
-
2: {
|
|
80
|
-
"step": 2,
|
|
81
|
-
"title": "Current Condition & Vital Signs",
|
|
82
|
-
"prompt": "Describe current condition and most recent vital signs",
|
|
83
|
-
"fields": ["current_condition", "vital_signs", "pain_level", "mental_status"],
|
|
84
|
-
"help_text": "Include vital signs, pain level, mental status, and overall condition",
|
|
85
|
-
},
|
|
86
|
-
3: {
|
|
87
|
-
"step": 3,
|
|
88
|
-
"title": "Treatments & Interventions",
|
|
89
|
-
"prompt": "Detail current treatments, medications, and interventions",
|
|
90
|
-
"fields": [
|
|
91
|
-
"iv_lines",
|
|
92
|
-
"medications",
|
|
93
|
-
"recent_procedures",
|
|
94
|
-
"pending_labs",
|
|
95
|
-
"pending_orders",
|
|
96
|
-
],
|
|
97
|
-
"help_text": "IV access, medications given, procedures done, pending orders/labs",
|
|
98
|
-
},
|
|
99
|
-
4: {
|
|
100
|
-
"step": 4,
|
|
101
|
-
"title": "Plan & Priorities",
|
|
102
|
-
"prompt": "Outline the care plan and priorities for the next shift",
|
|
103
|
-
"fields": [
|
|
104
|
-
"care_priorities",
|
|
105
|
-
"scheduled_tasks",
|
|
106
|
-
"patient_concerns",
|
|
107
|
-
"family_involvement",
|
|
108
|
-
],
|
|
109
|
-
"help_text": "What needs to be done this shift? Patient/family concerns? Priorities?",
|
|
110
|
-
},
|
|
111
|
-
5: {
|
|
112
|
-
"step": 5,
|
|
113
|
-
"title": "Safety & Special Considerations",
|
|
114
|
-
"prompt": "Note any safety concerns or special considerations",
|
|
115
|
-
"fields": [
|
|
116
|
-
"fall_risk",
|
|
117
|
-
"isolation_precautions",
|
|
118
|
-
"allergies",
|
|
119
|
-
"special_equipment",
|
|
120
|
-
"other_concerns",
|
|
121
|
-
],
|
|
122
|
-
"help_text": "Falls risk, isolation, allergies, special equipment, or other safety concerns",
|
|
123
|
-
},
|
|
124
|
-
6: {
|
|
125
|
-
"step": 6,
|
|
126
|
-
"title": "Review & Finalize",
|
|
127
|
-
"prompt": "Review your shift handoff and finalize the document",
|
|
128
|
-
"fields": ["review_complete", "user_approved"],
|
|
129
|
-
"help_text": "Review all sections of the shift handoff. Click 'Generate Preview' to see the formatted handoff report. You can go back to edit any section before finalizing.",
|
|
130
|
-
"is_review_step": True,
|
|
131
|
-
},
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
async def _store_wizard_session(wizard_id: str, session_data: dict[str, Any]):
|
|
136
|
-
"""Store wizard session in Redis or memory."""
|
|
137
|
-
if _has_redis:
|
|
138
|
-
try:
|
|
139
|
-
redis_client = await get_redis_client()
|
|
140
|
-
if redis_client:
|
|
141
|
-
# Use JSON for safe serialization
|
|
142
|
-
await redis_client.setex(
|
|
143
|
-
f"wizard_session:{wizard_id}",
|
|
144
|
-
3600, # 1 hour expiry
|
|
145
|
-
json.dumps(session_data),
|
|
146
|
-
)
|
|
147
|
-
return
|
|
148
|
-
except Exception as e:
|
|
149
|
-
logger.warning(f"Failed to store session in Redis: {e}")
|
|
150
|
-
|
|
151
|
-
# Fallback to memory
|
|
152
|
-
_wizard_sessions[wizard_id] = session_data
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
async def _get_wizard_session(wizard_id: str) -> dict[str, Any] | None:
|
|
156
|
-
"""Retrieve wizard session from Redis or memory."""
|
|
157
|
-
if _has_redis:
|
|
158
|
-
try:
|
|
159
|
-
redis_client = await get_redis_client()
|
|
160
|
-
if redis_client:
|
|
161
|
-
data = await redis_client.get(f"wizard_session:{wizard_id}")
|
|
162
|
-
if data:
|
|
163
|
-
# SECURITY FIX: Use json.loads() instead of eval()
|
|
164
|
-
return json.loads(data)
|
|
165
|
-
except Exception as e:
|
|
166
|
-
logger.warning(f"Failed to retrieve session from Redis: {e}")
|
|
167
|
-
|
|
168
|
-
# Fallback to memory
|
|
169
|
-
return _wizard_sessions.get(wizard_id)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
def _get_step_data(step_number: int) -> dict[str, Any]:
|
|
173
|
-
"""Get step configuration data."""
|
|
174
|
-
if step_number not in SHIFT_HANDOFF_STEPS:
|
|
175
|
-
raise ValueError(f"Invalid step number: {step_number}")
|
|
176
|
-
|
|
177
|
-
return SHIFT_HANDOFF_STEPS[step_number]
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
@router.post(
|
|
181
|
-
"/start",
|
|
182
|
-
summary="Start Shift Handoff Wizard",
|
|
183
|
-
description="Initialize a new shift handoff documentation workflow for nurse-to-nurse handoff.",
|
|
184
|
-
)
|
|
185
|
-
async def start_shift_handoff_wizard():
|
|
186
|
-
"""Start shift handoff wizard following Wizard Pattern Implementation."""
|
|
187
|
-
try:
|
|
188
|
-
wizard_id = str(uuid4())
|
|
189
|
-
|
|
190
|
-
session_data = {
|
|
191
|
-
"wizard_id": wizard_id,
|
|
192
|
-
"wizard_type": "shift_handoff",
|
|
193
|
-
"current_step": 1,
|
|
194
|
-
"total_steps": 6,
|
|
195
|
-
"collected_data": {},
|
|
196
|
-
"created_at": datetime.now().isoformat(),
|
|
197
|
-
"updated_at": datetime.now().isoformat(),
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
# Store session
|
|
201
|
-
await _store_wizard_session(wizard_id, session_data)
|
|
202
|
-
|
|
203
|
-
# Get first step prompt
|
|
204
|
-
step_data = _get_step_data(1)
|
|
205
|
-
|
|
206
|
-
response_data = {
|
|
207
|
-
"wizard_session": session_data,
|
|
208
|
-
"current_step": step_data,
|
|
209
|
-
"progress": {"current": 1, "total": 5, "percentage": 20},
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return create_success_response(
|
|
213
|
-
data=response_data, message="Shift handoff wizard started successfully"
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
except Exception as e:
|
|
217
|
-
raise HTTPException(
|
|
218
|
-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
219
|
-
detail=f"Failed to start shift handoff wizard: {str(e)}",
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
@router.post(
|
|
224
|
-
"/{wizard_id}/step",
|
|
225
|
-
summary="Submit shift handoff wizard step",
|
|
226
|
-
description="Submit data for current step and advance to next step in shift handoff workflow.",
|
|
227
|
-
)
|
|
228
|
-
async def submit_shift_handoff_step(wizard_id: str, step_data: dict[str, Any]):
|
|
229
|
-
"""Submit step data and advance wizard."""
|
|
230
|
-
try:
|
|
231
|
-
# Get session
|
|
232
|
-
session = await _get_wizard_session(wizard_id)
|
|
233
|
-
if not session:
|
|
234
|
-
raise HTTPException(
|
|
235
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
236
|
-
detail=f"Wizard session {wizard_id} not found",
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
# Validate step number
|
|
240
|
-
current_step = session["current_step"]
|
|
241
|
-
if step_data.get("step") != current_step:
|
|
242
|
-
raise HTTPException(
|
|
243
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
244
|
-
detail=f"Expected step {current_step}, got {step_data.get('step')}",
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
# Store collected data
|
|
248
|
-
session["collected_data"][f"step_{current_step}"] = step_data.get("data", {})
|
|
249
|
-
session["updated_at"] = datetime.now().isoformat()
|
|
250
|
-
|
|
251
|
-
# Advance to next step (but don't auto-complete, even on final step)
|
|
252
|
-
if current_step < session["total_steps"]:
|
|
253
|
-
next_step = current_step + 1
|
|
254
|
-
session["current_step"] = next_step
|
|
255
|
-
else:
|
|
256
|
-
# On review step - stay on same step, don't auto-complete
|
|
257
|
-
next_step = current_step
|
|
258
|
-
|
|
259
|
-
await _store_wizard_session(wizard_id, session)
|
|
260
|
-
|
|
261
|
-
next_step_data = _get_step_data(next_step)
|
|
262
|
-
|
|
263
|
-
return create_success_response(
|
|
264
|
-
data={
|
|
265
|
-
"wizard_session": session,
|
|
266
|
-
"current_step": next_step_data,
|
|
267
|
-
"progress": {
|
|
268
|
-
"current": next_step,
|
|
269
|
-
"total": session["total_steps"],
|
|
270
|
-
"percentage": (next_step / session["total_steps"]) * 100,
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
message=f"Step {current_step} completed",
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
except HTTPException:
|
|
277
|
-
raise
|
|
278
|
-
except Exception as e:
|
|
279
|
-
raise HTTPException(
|
|
280
|
-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
281
|
-
detail=f"Failed to process wizard step: {str(e)}",
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
@router.post("/{wizard_id}/preview", summary="Preview shift handoff report")
|
|
286
|
-
async def preview_shift_handoff_report(wizard_id: str):
|
|
287
|
-
"""
|
|
288
|
-
Generate preview of shift handoff report without finalizing.
|
|
289
|
-
|
|
290
|
-
This endpoint allows users to see the formatted shift handoff before
|
|
291
|
-
finalizing it. The report is NOT marked as complete. Users can
|
|
292
|
-
still go back and edit data after previewing.
|
|
293
|
-
"""
|
|
294
|
-
try:
|
|
295
|
-
session = await _get_wizard_session(wizard_id)
|
|
296
|
-
if not session:
|
|
297
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
|
|
298
|
-
|
|
299
|
-
# Verify user is on review step
|
|
300
|
-
if session["current_step"] != session["total_steps"]:
|
|
301
|
-
raise HTTPException(
|
|
302
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
303
|
-
detail=f"Not on review step. Complete steps 1-{session['total_steps']-1} first.",
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
# Generate preview report (does NOT mark as complete)
|
|
307
|
-
preview_report = await _generate_handoff_report(session["collected_data"])
|
|
308
|
-
|
|
309
|
-
# Store preview in session
|
|
310
|
-
session["preview_report"] = preview_report
|
|
311
|
-
session["preview_generated_at"] = datetime.now().isoformat()
|
|
312
|
-
await _store_wizard_session(wizard_id, session)
|
|
313
|
-
|
|
314
|
-
response_data = {
|
|
315
|
-
"preview": preview_report,
|
|
316
|
-
"wizard_session": session,
|
|
317
|
-
"message": "Review the shift handoff above. Click 'Finalize Report' to save, or go back to edit any section.",
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return create_success_response(data=response_data, message="Preview generated successfully")
|
|
321
|
-
|
|
322
|
-
except HTTPException:
|
|
323
|
-
raise
|
|
324
|
-
except Exception as e:
|
|
325
|
-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
@router.post("/{wizard_id}/save", summary="Finalize shift handoff report")
|
|
329
|
-
async def save_shift_handoff_report(wizard_id: str, approval_data: dict[str, Any]):
|
|
330
|
-
"""
|
|
331
|
-
Finalize and save the shift handoff report after user review and approval.
|
|
332
|
-
|
|
333
|
-
Requires that the user has:
|
|
334
|
-
1. Generated a preview first (/preview endpoint)
|
|
335
|
-
2. Explicitly approved the report (user_approved: true)
|
|
336
|
-
|
|
337
|
-
Only after calling this endpoint is the report marked as complete.
|
|
338
|
-
"""
|
|
339
|
-
try:
|
|
340
|
-
session = await _get_wizard_session(wizard_id)
|
|
341
|
-
if not session:
|
|
342
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
|
|
343
|
-
|
|
344
|
-
# Verify preview was generated
|
|
345
|
-
if "preview_report" not in session:
|
|
346
|
-
raise HTTPException(
|
|
347
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
348
|
-
detail="Must generate preview before saving. Call /preview endpoint first.",
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
# Verify user explicitly approved
|
|
352
|
-
if not approval_data.get("user_approved", False):
|
|
353
|
-
raise HTTPException(
|
|
354
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
355
|
-
detail="User approval required. Set 'user_approved': true in request body.",
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
# NOW we mark as complete
|
|
359
|
-
session["completed"] = True
|
|
360
|
-
session["completed_at"] = datetime.now().isoformat()
|
|
361
|
-
session["final_report"] = session["preview_report"]
|
|
362
|
-
session["user_approved"] = True
|
|
363
|
-
|
|
364
|
-
await _store_wizard_session(wizard_id, session)
|
|
365
|
-
|
|
366
|
-
response_data = {
|
|
367
|
-
"wizard_session": session,
|
|
368
|
-
"report": session["final_report"],
|
|
369
|
-
"completed": True,
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
return create_success_response(
|
|
373
|
-
data=response_data, message="Shift handoff finalized successfully"
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
except HTTPException:
|
|
377
|
-
raise
|
|
378
|
-
except Exception as e:
|
|
379
|
-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
@router.post(
|
|
383
|
-
"/{wizard_id}/enhance",
|
|
384
|
-
summary="Enhance shift handoff text with AI",
|
|
385
|
-
description="Use AI to professionalize and enhance shift handoff notes.",
|
|
386
|
-
)
|
|
387
|
-
async def enhance_shift_handoff_text(wizard_id: str, text_data: dict[str, Any]):
|
|
388
|
-
"""Enhance text using AI service."""
|
|
389
|
-
try:
|
|
390
|
-
# Get AI service
|
|
391
|
-
ai_service = get_service("ai")
|
|
392
|
-
|
|
393
|
-
# Enhance text with medical context
|
|
394
|
-
enhanced = await ai_service.enhance_clinical_text(
|
|
395
|
-
text=text_data.get("text", ""),
|
|
396
|
-
context="shift_handoff",
|
|
397
|
-
specialty=text_data.get("specialty"),
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
return create_success_response(
|
|
401
|
-
data={"original": text_data.get("text"), "enhanced": enhanced},
|
|
402
|
-
message="Text enhanced successfully",
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
except Exception as e:
|
|
406
|
-
raise HTTPException(
|
|
407
|
-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
408
|
-
detail=f"Failed to enhance text: {str(e)}",
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
@router.get(
|
|
413
|
-
"/{wizard_id}/report",
|
|
414
|
-
summary="Get shift handoff report",
|
|
415
|
-
description="Retrieve the completed shift handoff report.",
|
|
416
|
-
)
|
|
417
|
-
async def get_shift_handoff_report(wizard_id: str):
|
|
418
|
-
"""Get completed shift handoff report."""
|
|
419
|
-
try:
|
|
420
|
-
session = await _get_wizard_session(wizard_id)
|
|
421
|
-
if not session:
|
|
422
|
-
raise HTTPException(
|
|
423
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
424
|
-
detail=f"Wizard session {wizard_id} not found",
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
if "final_report" not in session:
|
|
428
|
-
raise HTTPException(
|
|
429
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
430
|
-
detail="Shift handoff not yet completed",
|
|
431
|
-
)
|
|
432
|
-
|
|
433
|
-
return create_success_response(
|
|
434
|
-
data={"handoff_report": session["final_report"]},
|
|
435
|
-
message="Shift handoff report retrieved",
|
|
436
|
-
)
|
|
437
|
-
|
|
438
|
-
except HTTPException:
|
|
439
|
-
raise
|
|
440
|
-
except Exception as e:
|
|
441
|
-
raise HTTPException(
|
|
442
|
-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
443
|
-
detail=f"Failed to retrieve report: {str(e)}",
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
async def _generate_handoff_report(collected_data: dict[str, Any]) -> dict[str, Any]:
|
|
448
|
-
"""Generate final shift handoff report from collected data."""
|
|
449
|
-
# Compile all steps into structured handoff report
|
|
450
|
-
report = {
|
|
451
|
-
"report_type": "shift_handoff",
|
|
452
|
-
"generated_at": datetime.now().isoformat(),
|
|
453
|
-
"patient_identification": collected_data.get("step_1", {}),
|
|
454
|
-
"current_condition": collected_data.get("step_2", {}),
|
|
455
|
-
"treatments_interventions": collected_data.get("step_3", {}),
|
|
456
|
-
"plan_priorities": collected_data.get("step_4", {}),
|
|
457
|
-
"safety_considerations": collected_data.get("step_5", {}),
|
|
458
|
-
"formatted_report": _format_handoff_narrative(collected_data),
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
return report
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
def _format_handoff_narrative(collected_data: dict[str, Any]) -> str:
|
|
465
|
-
"""Format collected data into narrative handoff report."""
|
|
466
|
-
sections = []
|
|
467
|
-
|
|
468
|
-
# Section 1: Patient ID & Status
|
|
469
|
-
step1 = collected_data.get("step_1", {})
|
|
470
|
-
sections.append("PATIENT IDENTIFICATION & STATUS")
|
|
471
|
-
sections.append("=" * 50)
|
|
472
|
-
if step1:
|
|
473
|
-
sections.append(f"Patient: {step1.get('patient_name', 'N/A')}")
|
|
474
|
-
sections.append(f"Location: {step1.get('room_bed', 'N/A')}")
|
|
475
|
-
sections.append(f"Age: {step1.get('age', 'N/A')}")
|
|
476
|
-
sections.append(f"Diagnosis: {step1.get('diagnosis', 'N/A')}")
|
|
477
|
-
sections.append(f"Admitted: {step1.get('admission_date', 'N/A')}")
|
|
478
|
-
sections.append(f"Code Status: {step1.get('code_status', 'N/A')}")
|
|
479
|
-
sections.append("")
|
|
480
|
-
|
|
481
|
-
# Section 2: Current Condition
|
|
482
|
-
step2 = collected_data.get("step_2", {})
|
|
483
|
-
sections.append("CURRENT CONDITION")
|
|
484
|
-
sections.append("=" * 50)
|
|
485
|
-
if step2:
|
|
486
|
-
sections.append(f"Condition: {step2.get('current_condition', 'N/A')}")
|
|
487
|
-
sections.append(f"Vital Signs: {step2.get('vital_signs', 'N/A')}")
|
|
488
|
-
sections.append(f"Pain Level: {step2.get('pain_level', 'N/A')}")
|
|
489
|
-
sections.append(f"Mental Status: {step2.get('mental_status', 'N/A')}")
|
|
490
|
-
sections.append("")
|
|
491
|
-
|
|
492
|
-
# Section 3: Treatments & Interventions
|
|
493
|
-
step3 = collected_data.get("step_3", {})
|
|
494
|
-
sections.append("TREATMENTS & INTERVENTIONS")
|
|
495
|
-
sections.append("=" * 50)
|
|
496
|
-
if step3:
|
|
497
|
-
sections.append(f"IV Access: {step3.get('iv_lines', 'N/A')}")
|
|
498
|
-
sections.append(f"Medications: {step3.get('medications', 'N/A')}")
|
|
499
|
-
sections.append(f"Recent Procedures: {step3.get('recent_procedures', 'N/A')}")
|
|
500
|
-
sections.append(f"Pending Labs: {step3.get('pending_labs', 'N/A')}")
|
|
501
|
-
sections.append(f"Pending Orders: {step3.get('pending_orders', 'N/A')}")
|
|
502
|
-
sections.append("")
|
|
503
|
-
|
|
504
|
-
# Section 4: Plan & Priorities
|
|
505
|
-
step4 = collected_data.get("step_4", {})
|
|
506
|
-
sections.append("PLAN & PRIORITIES")
|
|
507
|
-
sections.append("=" * 50)
|
|
508
|
-
if step4:
|
|
509
|
-
sections.append(f"Care Priorities: {step4.get('care_priorities', 'N/A')}")
|
|
510
|
-
sections.append(f"Scheduled Tasks: {step4.get('scheduled_tasks', 'N/A')}")
|
|
511
|
-
sections.append(f"Patient Concerns: {step4.get('patient_concerns', 'N/A')}")
|
|
512
|
-
sections.append(f"Family Involvement: {step4.get('family_involvement', 'N/A')}")
|
|
513
|
-
sections.append("")
|
|
514
|
-
|
|
515
|
-
# Section 5: Safety & Special Considerations
|
|
516
|
-
step5 = collected_data.get("step_5", {})
|
|
517
|
-
sections.append("SAFETY & SPECIAL CONSIDERATIONS")
|
|
518
|
-
sections.append("=" * 50)
|
|
519
|
-
if step5:
|
|
520
|
-
sections.append(f"Fall Risk: {step5.get('fall_risk', 'N/A')}")
|
|
521
|
-
sections.append(f"Isolation: {step5.get('isolation_precautions', 'N/A')}")
|
|
522
|
-
sections.append(f"Allergies: {step5.get('allergies', 'N/A')}")
|
|
523
|
-
sections.append(f"Special Equipment: {step5.get('special_equipment', 'N/A')}")
|
|
524
|
-
sections.append(f"Other Concerns: {step5.get('other_concerns', 'N/A')}")
|
|
525
|
-
sections.append("")
|
|
526
|
-
|
|
527
|
-
sections.append("=" * 50)
|
|
528
|
-
sections.append("Educational use only — not medical advice. No PHI stored.")
|
|
529
|
-
sections.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
530
|
-
|
|
531
|
-
return "\n".join(sections)
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
# Export router
|
|
535
|
-
__all__ = ["router"]
|