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
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Incident Report Wizard Router - AI Nurse Florence
|
|
3
|
-
Following Wizard Pattern Implementation
|
|
4
|
-
|
|
5
|
-
Structured safety event documentation for incident/variance reporting.
|
|
6
|
-
HIPAA-compliant, objective language, based on patient safety best practices.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import logging
|
|
10
|
-
from datetime import datetime
|
|
11
|
-
from typing import Any
|
|
12
|
-
from uuid import uuid4
|
|
13
|
-
|
|
14
|
-
from fastapi import APIRouter, HTTPException, status
|
|
15
|
-
from src.services import get_service
|
|
16
|
-
from src.utils.api_responses import create_success_response
|
|
17
|
-
from src.utils.config import get_settings
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
try:
|
|
22
|
-
from src.services.translation_service import translate_text
|
|
23
|
-
|
|
24
|
-
_has_translation = True
|
|
25
|
-
except ImportError:
|
|
26
|
-
_has_translation = False
|
|
27
|
-
|
|
28
|
-
async def translate_text(
|
|
29
|
-
text: str,
|
|
30
|
-
target_language: str,
|
|
31
|
-
source_language: str = "en",
|
|
32
|
-
context: str = "medical",
|
|
33
|
-
):
|
|
34
|
-
return {"translated_text": text, "success": False}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
settings = get_settings()
|
|
38
|
-
|
|
39
|
-
router = APIRouter(
|
|
40
|
-
prefix="/wizards/incident-report",
|
|
41
|
-
tags=["clinical-wizards"],
|
|
42
|
-
responses={
|
|
43
|
-
404: {"description": "Wizard session not found"},
|
|
44
|
-
422: {"description": "Invalid wizard step data"},
|
|
45
|
-
500: {"description": "Wizard processing error"},
|
|
46
|
-
},
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
from src.utils.redis_cache import get_redis_client
|
|
51
|
-
|
|
52
|
-
_has_redis = True
|
|
53
|
-
except ImportError:
|
|
54
|
-
_has_redis = False
|
|
55
|
-
|
|
56
|
-
_wizard_sessions: dict[str, dict[str, Any]] = {}
|
|
57
|
-
|
|
58
|
-
INCIDENT_REPORT_STEPS = {
|
|
59
|
-
1: {
|
|
60
|
-
"step": 1,
|
|
61
|
-
"title": "Incident Overview",
|
|
62
|
-
"prompt": "Document basic incident information",
|
|
63
|
-
"fields": [
|
|
64
|
-
"incident_date_time",
|
|
65
|
-
"incident_location",
|
|
66
|
-
"incident_type",
|
|
67
|
-
"patient_involved",
|
|
68
|
-
"reporter_name",
|
|
69
|
-
"reporter_role",
|
|
70
|
-
],
|
|
71
|
-
"help_text": "Record when and where the incident occurred, type of incident, and reporting information",
|
|
72
|
-
},
|
|
73
|
-
2: {
|
|
74
|
-
"step": 2,
|
|
75
|
-
"title": "Incident Description",
|
|
76
|
-
"prompt": "Describe what happened in objective, factual terms",
|
|
77
|
-
"fields": [
|
|
78
|
-
"incident_description",
|
|
79
|
-
"sequence_of_events",
|
|
80
|
-
"witnesses",
|
|
81
|
-
"patient_condition_before",
|
|
82
|
-
"patient_condition_after",
|
|
83
|
-
"immediate_actions_taken",
|
|
84
|
-
],
|
|
85
|
-
"help_text": "Use objective language. Describe facts, not opinions or blame. Include timeline and witnesses.",
|
|
86
|
-
},
|
|
87
|
-
3: {
|
|
88
|
-
"step": 3,
|
|
89
|
-
"title": "Contributing Factors",
|
|
90
|
-
"prompt": "Identify potential contributing factors",
|
|
91
|
-
"fields": [
|
|
92
|
-
"environmental_factors",
|
|
93
|
-
"equipment_involved",
|
|
94
|
-
"staffing_factors",
|
|
95
|
-
"communication_issues",
|
|
96
|
-
"policy_procedure_gaps",
|
|
97
|
-
"other_factors",
|
|
98
|
-
],
|
|
99
|
-
"help_text": "Note environmental, equipment, staffing, or system factors. Focus on improvement, not blame.",
|
|
100
|
-
},
|
|
101
|
-
4: {
|
|
102
|
-
"step": 4,
|
|
103
|
-
"title": "Response & Follow-up",
|
|
104
|
-
"prompt": "Document response actions and follow-up",
|
|
105
|
-
"fields": [
|
|
106
|
-
"notifications_made",
|
|
107
|
-
"medical_response",
|
|
108
|
-
"family_notification",
|
|
109
|
-
"documentation_completed",
|
|
110
|
-
"follow_up_required",
|
|
111
|
-
"prevention_recommendations",
|
|
112
|
-
],
|
|
113
|
-
"help_text": "Who was notified, what medical response occurred, family communication, and recommendations for prevention",
|
|
114
|
-
},
|
|
115
|
-
5: {
|
|
116
|
-
"step": 5,
|
|
117
|
-
"title": "Review & Finalize",
|
|
118
|
-
"prompt": "Review your incident report and finalize the document",
|
|
119
|
-
"fields": ["review_complete", "user_approved"],
|
|
120
|
-
"help_text": "Review all sections of the incident report. Click 'Generate Preview' to see the formatted document. You can go back to edit any section before finalizing.",
|
|
121
|
-
"is_review_step": True,
|
|
122
|
-
},
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
EDU_BANNER = """
|
|
126
|
-
⚠️ INCIDENT REPORTING NOTICE ⚠️
|
|
127
|
-
This incident report wizard is for educational and quality improvement purposes.
|
|
128
|
-
Incident reports are confidential peer review documents. Use objective,
|
|
129
|
-
non-judgmental language. Focus on facts and system improvement, not individual blame.
|
|
130
|
-
"""
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
async def _store_wizard_session(wizard_id: str, session_data: dict[str, Any]) -> bool:
|
|
134
|
-
try:
|
|
135
|
-
if _has_redis:
|
|
136
|
-
import json
|
|
137
|
-
|
|
138
|
-
redis_client = await get_redis_client()
|
|
139
|
-
if redis_client:
|
|
140
|
-
await redis_client.setex(
|
|
141
|
-
f"wizard:incident_report:{wizard_id}",
|
|
142
|
-
7200,
|
|
143
|
-
json.dumps(session_data), # FIXED: use JSON
|
|
144
|
-
)
|
|
145
|
-
return True
|
|
146
|
-
except Exception: # noqa: BLE001
|
|
147
|
-
# INTENTIONAL: Graceful degradation - fall back to in-memory storage if Redis fails
|
|
148
|
-
pass
|
|
149
|
-
_wizard_sessions[wizard_id] = session_data
|
|
150
|
-
return True
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
async def _get_wizard_session(wizard_id: str) -> dict[str, Any] | None:
|
|
154
|
-
try:
|
|
155
|
-
if _has_redis:
|
|
156
|
-
import json
|
|
157
|
-
|
|
158
|
-
redis_client = await get_redis_client()
|
|
159
|
-
if redis_client:
|
|
160
|
-
session_str = await redis_client.get(f"wizard:incident_report:{wizard_id}")
|
|
161
|
-
if session_str:
|
|
162
|
-
# SECURITY FIX: Use json.loads() instead of ast.literal_eval()
|
|
163
|
-
return json.loads(session_str)
|
|
164
|
-
except Exception: # noqa: BLE001
|
|
165
|
-
# INTENTIONAL: Graceful degradation - fall back to in-memory storage if Redis fails
|
|
166
|
-
pass
|
|
167
|
-
return _wizard_sessions.get(wizard_id)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def _get_step_data(step: int) -> dict[str, Any]:
|
|
171
|
-
if step not in INCIDENT_REPORT_STEPS:
|
|
172
|
-
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Invalid step: {step}")
|
|
173
|
-
step_config = INCIDENT_REPORT_STEPS[step]
|
|
174
|
-
return {
|
|
175
|
-
"step": step,
|
|
176
|
-
"title": step_config["title"],
|
|
177
|
-
"prompt": step_config["prompt"],
|
|
178
|
-
"fields": step_config["fields"],
|
|
179
|
-
"help_text": step_config["help_text"],
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def _generate_incident_report(collected_data: dict[str, Any]) -> dict[str, Any]:
|
|
184
|
-
narrative = f"""
|
|
185
|
-
INCIDENT REPORT - CONFIDENTIAL QUALITY IMPROVEMENT DOCUMENT
|
|
186
|
-
|
|
187
|
-
INCIDENT INFORMATION
|
|
188
|
-
Date/Time of Incident: {collected_data.get('incident_date_time', 'Not documented')}
|
|
189
|
-
Location: {collected_data.get('incident_location', 'Not documented')}
|
|
190
|
-
Type of Incident: {collected_data.get('incident_type', 'Not documented')}
|
|
191
|
-
Patient Involved: {collected_data.get('patient_involved', 'Not applicable')}
|
|
192
|
-
Reported By: {collected_data.get('reporter_name', 'Not documented')} ({collected_data.get('reporter_role', 'Not documented')})
|
|
193
|
-
|
|
194
|
-
INCIDENT DESCRIPTION (Objective, Factual Account)
|
|
195
|
-
Description: {collected_data.get('incident_description', 'Not documented')}
|
|
196
|
-
|
|
197
|
-
Sequence of Events: {collected_data.get('sequence_of_events', 'Not documented')}
|
|
198
|
-
|
|
199
|
-
Witnesses: {collected_data.get('witnesses', 'None identified')}
|
|
200
|
-
|
|
201
|
-
Patient Condition Before Incident: {collected_data.get('patient_condition_before', 'Not applicable')}
|
|
202
|
-
|
|
203
|
-
Patient Condition After Incident: {collected_data.get('patient_condition_after', 'Not applicable')}
|
|
204
|
-
|
|
205
|
-
Immediate Actions Taken: {collected_data.get('immediate_actions_taken', 'Not documented')}
|
|
206
|
-
|
|
207
|
-
CONTRIBUTING FACTORS
|
|
208
|
-
Environmental Factors: {collected_data.get('environmental_factors', 'None identified')}
|
|
209
|
-
Equipment Involved: {collected_data.get('equipment_involved', 'None')}
|
|
210
|
-
Staffing Factors: {collected_data.get('staffing_factors', 'None identified')}
|
|
211
|
-
Communication Issues: {collected_data.get('communication_issues', 'None identified')}
|
|
212
|
-
Policy/Procedure Gaps: {collected_data.get('policy_procedure_gaps', 'None identified')}
|
|
213
|
-
Other Contributing Factors: {collected_data.get('other_factors', 'None identified')}
|
|
214
|
-
|
|
215
|
-
RESPONSE & FOLLOW-UP
|
|
216
|
-
Notifications Made: {collected_data.get('notifications_made', 'Not documented')}
|
|
217
|
-
Medical Response: {collected_data.get('medical_response', 'Not applicable')}
|
|
218
|
-
Family Notification: {collected_data.get('family_notification', 'Not applicable')}
|
|
219
|
-
Documentation Completed: {collected_data.get('documentation_completed', 'Medical record updated')}
|
|
220
|
-
Follow-up Required: {collected_data.get('follow_up_required', 'Not documented')}
|
|
221
|
-
|
|
222
|
-
RECOMMENDATIONS FOR PREVENTION
|
|
223
|
-
{collected_data.get('prevention_recommendations', 'Not documented')}
|
|
224
|
-
|
|
225
|
-
Report Submitted: {datetime.now().strftime('%Y-%m-%d %H:%M')}
|
|
226
|
-
|
|
227
|
-
This report is a confidential quality improvement document protected under peer review statutes.
|
|
228
|
-
Use for quality improvement and risk management purposes only.
|
|
229
|
-
"""
|
|
230
|
-
return {
|
|
231
|
-
"incident_report": collected_data,
|
|
232
|
-
"narrative": narrative.strip(),
|
|
233
|
-
"metadata": {
|
|
234
|
-
"generated_at": datetime.now().isoformat(),
|
|
235
|
-
"wizard_type": "incident_report",
|
|
236
|
-
},
|
|
237
|
-
"banner": EDU_BANNER,
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
@router.post("/start", summary="Start Incident Report Wizard")
|
|
242
|
-
async def start_incident_report_wizard():
|
|
243
|
-
try:
|
|
244
|
-
wizard_id = str(uuid4())
|
|
245
|
-
session_data = {
|
|
246
|
-
"wizard_id": wizard_id,
|
|
247
|
-
"wizard_type": "incident_report",
|
|
248
|
-
"current_step": 1,
|
|
249
|
-
"total_steps": 5,
|
|
250
|
-
"collected_data": {},
|
|
251
|
-
"created_at": datetime.now().isoformat(),
|
|
252
|
-
"updated_at": datetime.now().isoformat(),
|
|
253
|
-
}
|
|
254
|
-
await _store_wizard_session(wizard_id, session_data)
|
|
255
|
-
step_data = _get_step_data(1)
|
|
256
|
-
response_data = {
|
|
257
|
-
"wizard_session": session_data,
|
|
258
|
-
"current_step": step_data,
|
|
259
|
-
"progress": {"current": 1, "total": 4, "percentage": 25},
|
|
260
|
-
"banner": EDU_BANNER,
|
|
261
|
-
}
|
|
262
|
-
return create_success_response(
|
|
263
|
-
data=response_data, message="Incident report wizard started successfully"
|
|
264
|
-
)
|
|
265
|
-
except Exception as e:
|
|
266
|
-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
@router.post("/{wizard_id}/step", summary="Submit incident report step")
|
|
270
|
-
async def submit_incident_report_step(wizard_id: str, step_data: dict[str, Any]):
|
|
271
|
-
try:
|
|
272
|
-
session = await _get_wizard_session(wizard_id)
|
|
273
|
-
if not session:
|
|
274
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
|
|
275
|
-
current_step = session["current_step"]
|
|
276
|
-
session["collected_data"].update(step_data.get("data", {}))
|
|
277
|
-
session["updated_at"] = datetime.now().isoformat()
|
|
278
|
-
|
|
279
|
-
# Advance to next step (but don't auto-complete, even on final step)
|
|
280
|
-
if current_step < session["total_steps"]:
|
|
281
|
-
next_step = current_step + 1
|
|
282
|
-
session["current_step"] = next_step
|
|
283
|
-
else:
|
|
284
|
-
# On review step - stay on same step, don't auto-complete
|
|
285
|
-
next_step = current_step
|
|
286
|
-
|
|
287
|
-
await _store_wizard_session(wizard_id, session)
|
|
288
|
-
return create_success_response(
|
|
289
|
-
data={
|
|
290
|
-
"wizard_session": session,
|
|
291
|
-
"current_step": _get_step_data(next_step),
|
|
292
|
-
"progress": {
|
|
293
|
-
"current": next_step,
|
|
294
|
-
"total": session["total_steps"],
|
|
295
|
-
"percentage": int((next_step / session["total_steps"]) * 100),
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
message=f"Step {current_step} completed",
|
|
299
|
-
)
|
|
300
|
-
except Exception as e:
|
|
301
|
-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
@router.post("/{wizard_id}/preview", summary="Preview incident report")
|
|
305
|
-
async def preview_incident_report(wizard_id: str):
|
|
306
|
-
"""
|
|
307
|
-
Generate preview of incident report without finalizing.
|
|
308
|
-
|
|
309
|
-
This endpoint allows users to see the formatted incident report before
|
|
310
|
-
finalizing it. The report is NOT marked as complete. Users can
|
|
311
|
-
still go back and edit data after previewing.
|
|
312
|
-
"""
|
|
313
|
-
try:
|
|
314
|
-
session = await _get_wizard_session(wizard_id)
|
|
315
|
-
if not session:
|
|
316
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
|
|
317
|
-
|
|
318
|
-
# Verify user is on review step
|
|
319
|
-
if session["current_step"] != session["total_steps"]:
|
|
320
|
-
raise HTTPException(
|
|
321
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
322
|
-
detail=f"Not on review step. Complete steps 1-{session['total_steps']-1} first.",
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
# Generate preview report (does NOT mark as complete)
|
|
326
|
-
preview_report = _generate_incident_report(session["collected_data"])
|
|
327
|
-
|
|
328
|
-
# Store preview in session
|
|
329
|
-
session["preview_report"] = preview_report
|
|
330
|
-
session["preview_generated_at"] = datetime.now().isoformat()
|
|
331
|
-
await _store_wizard_session(wizard_id, session)
|
|
332
|
-
|
|
333
|
-
response_data = {
|
|
334
|
-
"preview": preview_report,
|
|
335
|
-
"wizard_session": session,
|
|
336
|
-
"message": "Review the incident report above. Click 'Finalize Report' to save, or go back to edit any section.",
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return create_success_response(data=response_data, message="Preview generated successfully")
|
|
340
|
-
|
|
341
|
-
except HTTPException:
|
|
342
|
-
raise
|
|
343
|
-
except Exception as e:
|
|
344
|
-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
@router.post("/{wizard_id}/save", summary="Finalize incident report")
|
|
348
|
-
async def save_incident_report(wizard_id: str, approval_data: dict[str, Any]):
|
|
349
|
-
"""
|
|
350
|
-
Finalize and save the incident report after user review and approval.
|
|
351
|
-
|
|
352
|
-
Requires that the user has:
|
|
353
|
-
1. Generated a preview first (/preview endpoint)
|
|
354
|
-
2. Explicitly approved the report (user_approved: true)
|
|
355
|
-
|
|
356
|
-
Only after calling this endpoint is the report marked as complete.
|
|
357
|
-
"""
|
|
358
|
-
try:
|
|
359
|
-
session = await _get_wizard_session(wizard_id)
|
|
360
|
-
if not session:
|
|
361
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
|
|
362
|
-
|
|
363
|
-
# Verify preview was generated
|
|
364
|
-
if "preview_report" not in session:
|
|
365
|
-
raise HTTPException(
|
|
366
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
367
|
-
detail="Must generate preview before saving. Call /preview endpoint first.",
|
|
368
|
-
)
|
|
369
|
-
|
|
370
|
-
# Verify user explicitly approved
|
|
371
|
-
if not approval_data.get("user_approved", False):
|
|
372
|
-
raise HTTPException(
|
|
373
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
374
|
-
detail="User approval required. Set 'user_approved': true in request body.",
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
# NOW we mark as complete
|
|
378
|
-
session["completed"] = True
|
|
379
|
-
session["completed_at"] = datetime.now().isoformat()
|
|
380
|
-
session["final_report"] = session["preview_report"]
|
|
381
|
-
session["user_approved"] = True
|
|
382
|
-
|
|
383
|
-
await _store_wizard_session(wizard_id, session)
|
|
384
|
-
|
|
385
|
-
response_data = {
|
|
386
|
-
"wizard_session": session,
|
|
387
|
-
"report": session["final_report"],
|
|
388
|
-
"completed": True,
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return create_success_response(
|
|
392
|
-
data=response_data, message="Incident report finalized successfully"
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
except HTTPException:
|
|
396
|
-
raise
|
|
397
|
-
except Exception as e:
|
|
398
|
-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
@router.post("/{wizard_id}/enhance", summary="Enhance incident report text")
|
|
402
|
-
async def enhance_incident_report_text(wizard_id: str, text_data: dict[str, Any]):
|
|
403
|
-
try:
|
|
404
|
-
session = await _get_wizard_session(wizard_id)
|
|
405
|
-
if not session:
|
|
406
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
|
|
407
|
-
original_text = text_data.get("text", "")
|
|
408
|
-
field_name = text_data.get("field", "text")
|
|
409
|
-
if not original_text:
|
|
410
|
-
raise HTTPException(
|
|
411
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
412
|
-
detail="No text provided",
|
|
413
|
-
)
|
|
414
|
-
chat_service = get_service("chat")
|
|
415
|
-
enhancement_prompt = f"Enhance this incident report field ({field_name}) using objective, non-judgmental language. Focus on facts, not blame: {original_text}"
|
|
416
|
-
chat_response = await chat_service.chat(
|
|
417
|
-
message=enhancement_prompt,
|
|
418
|
-
conversation_id=f"incident_enhance_{wizard_id}",
|
|
419
|
-
context={"wizard_id": wizard_id, "field": field_name},
|
|
420
|
-
)
|
|
421
|
-
enhanced_text = chat_response.get("response", original_text)
|
|
422
|
-
return create_success_response(
|
|
423
|
-
data={
|
|
424
|
-
"original_text": original_text,
|
|
425
|
-
"enhanced_text": enhanced_text,
|
|
426
|
-
"field": field_name,
|
|
427
|
-
},
|
|
428
|
-
message="Text enhanced successfully",
|
|
429
|
-
)
|
|
430
|
-
except Exception as e:
|
|
431
|
-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
@router.get("/{wizard_id}/report", summary="Get incident report")
|
|
435
|
-
async def get_incident_report(wizard_id: str):
|
|
436
|
-
try:
|
|
437
|
-
session = await _get_wizard_session(wizard_id)
|
|
438
|
-
if not session:
|
|
439
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found")
|
|
440
|
-
if not session.get("completed", False):
|
|
441
|
-
raise HTTPException(
|
|
442
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
443
|
-
detail="Wizard not completed",
|
|
444
|
-
)
|
|
445
|
-
report = _generate_incident_report(session["collected_data"])
|
|
446
|
-
return create_success_response(
|
|
447
|
-
data={"wizard_session": session, "report": report},
|
|
448
|
-
message="Report retrieved successfully",
|
|
449
|
-
)
|
|
450
|
-
except Exception as e:
|
|
451
|
-
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
__all__ = ["router"]
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Medication Reconciliation Wizard - AI Nurse Florence
|
|
3
|
-
Following Wizard Pattern Implementation from coding instructions
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from datetime import datetime
|
|
7
|
-
from typing import Any
|
|
8
|
-
from uuid import uuid4
|
|
9
|
-
|
|
10
|
-
from fastapi import APIRouter, HTTPException
|
|
11
|
-
from pydantic import BaseModel, Field
|
|
12
|
-
|
|
13
|
-
from ...utils.config import get_educational_banner
|
|
14
|
-
|
|
15
|
-
router = APIRouter(
|
|
16
|
-
prefix="/wizard/medication-reconciliation",
|
|
17
|
-
tags=["wizards", "medication-reconciliation"],
|
|
18
|
-
responses={
|
|
19
|
-
404: {"description": "Wizard session not found"},
|
|
20
|
-
422: {"description": "Invalid step data"},
|
|
21
|
-
},
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
# Wizard session storage (Redis in production)
|
|
25
|
-
_wizard_sessions: dict[str, dict[str, Any]] = {}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class MedicationItem(BaseModel):
|
|
29
|
-
name: str = Field(..., description="Medication name")
|
|
30
|
-
dosage: str = Field(..., description="Dosage information")
|
|
31
|
-
frequency: str = Field(..., description="Frequency of administration")
|
|
32
|
-
route: str = Field(..., description="Route of administration")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@router.post("/start")
|
|
36
|
-
async def start_medication_reconciliation():
|
|
37
|
-
"""Start medication reconciliation wizard following Wizard Pattern Implementation."""
|
|
38
|
-
wizard_id = str(uuid4())
|
|
39
|
-
|
|
40
|
-
session_data = {
|
|
41
|
-
"wizard_id": wizard_id,
|
|
42
|
-
"wizard_type": "medication_reconciliation",
|
|
43
|
-
"created_at": datetime.now().isoformat(),
|
|
44
|
-
"current_step": 1,
|
|
45
|
-
"total_steps": 4,
|
|
46
|
-
"completed_steps": [],
|
|
47
|
-
"data": {},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
_wizard_sessions[wizard_id] = session_data
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
"banner": get_educational_banner(),
|
|
54
|
-
"wizard_id": wizard_id,
|
|
55
|
-
"wizard_type": "medication_reconciliation",
|
|
56
|
-
"current_step": 1,
|
|
57
|
-
"total_steps": 4,
|
|
58
|
-
"step_title": "Patient Information",
|
|
59
|
-
"educational_note": "Medication reconciliation prevents medication errors during care transitions.",
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@router.get("/{wizard_id}/status")
|
|
64
|
-
async def get_wizard_status(wizard_id: str):
|
|
65
|
-
"""Get current wizard status following Wizard Pattern Implementation."""
|
|
66
|
-
|
|
67
|
-
if wizard_id not in _wizard_sessions:
|
|
68
|
-
raise HTTPException(status_code=404, detail="Wizard session not found")
|
|
69
|
-
|
|
70
|
-
session = _wizard_sessions[wizard_id]
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
"banner": get_educational_banner(),
|
|
74
|
-
"wizard_id": wizard_id,
|
|
75
|
-
"wizard_type": session["wizard_type"],
|
|
76
|
-
"current_step": session["current_step"],
|
|
77
|
-
"total_steps": session["total_steps"],
|
|
78
|
-
"completed_steps": session["completed_steps"],
|
|
79
|
-
"progress": len(session["completed_steps"]) / session["total_steps"] * 100,
|
|
80
|
-
"status": (
|
|
81
|
-
"completed"
|
|
82
|
-
if len(session["completed_steps"]) == session["total_steps"]
|
|
83
|
-
else "in_progress"
|
|
84
|
-
),
|
|
85
|
-
}
|