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