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