empathy-framework 2.4.0__py3-none-any.whl → 3.8.2__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.
- coach_wizards/__init__.py +13 -12
- coach_wizards/accessibility_wizard.py +12 -12
- coach_wizards/api_wizard.py +12 -12
- coach_wizards/base_wizard.py +26 -20
- coach_wizards/cicd_wizard.py +15 -13
- coach_wizards/code_reviewer_README.md +60 -0
- coach_wizards/code_reviewer_wizard.py +180 -0
- coach_wizards/compliance_wizard.py +12 -12
- coach_wizards/database_wizard.py +12 -12
- coach_wizards/debugging_wizard.py +12 -12
- coach_wizards/documentation_wizard.py +12 -12
- coach_wizards/generate_wizards.py +1 -2
- coach_wizards/localization_wizard.py +101 -19
- coach_wizards/migration_wizard.py +12 -12
- coach_wizards/monitoring_wizard.py +12 -12
- coach_wizards/observability_wizard.py +12 -12
- coach_wizards/performance_wizard.py +12 -12
- coach_wizards/prompt_engineering_wizard.py +661 -0
- coach_wizards/refactoring_wizard.py +12 -12
- coach_wizards/scaling_wizard.py +12 -12
- coach_wizards/security_wizard.py +12 -12
- coach_wizards/testing_wizard.py +12 -12
- empathy_framework-3.8.2.dist-info/METADATA +1176 -0
- empathy_framework-3.8.2.dist-info/RECORD +333 -0
- empathy_framework-3.8.2.dist-info/entry_points.txt +22 -0
- {empathy_framework-2.4.0.dist-info → empathy_framework-3.8.2.dist-info}/top_level.txt +5 -1
- empathy_healthcare_plugin/__init__.py +1 -2
- empathy_healthcare_plugin/monitors/__init__.py +9 -0
- empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +315 -0
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +44 -0
- empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +300 -0
- empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +214 -0
- empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +306 -0
- empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +389 -0
- empathy_llm_toolkit/__init__.py +7 -7
- empathy_llm_toolkit/agent_factory/__init__.py +53 -0
- empathy_llm_toolkit/agent_factory/adapters/__init__.py +85 -0
- empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +312 -0
- empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +454 -0
- empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +298 -0
- empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +362 -0
- empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +333 -0
- empathy_llm_toolkit/agent_factory/adapters/native.py +228 -0
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +426 -0
- empathy_llm_toolkit/agent_factory/base.py +305 -0
- empathy_llm_toolkit/agent_factory/crews/__init__.py +67 -0
- empathy_llm_toolkit/agent_factory/crews/code_review.py +1113 -0
- empathy_llm_toolkit/agent_factory/crews/health_check.py +1246 -0
- empathy_llm_toolkit/agent_factory/crews/refactoring.py +1128 -0
- empathy_llm_toolkit/agent_factory/crews/security_audit.py +1018 -0
- empathy_llm_toolkit/agent_factory/decorators.py +286 -0
- empathy_llm_toolkit/agent_factory/factory.py +558 -0
- empathy_llm_toolkit/agent_factory/framework.py +192 -0
- empathy_llm_toolkit/agent_factory/memory_integration.py +324 -0
- empathy_llm_toolkit/agent_factory/resilient.py +320 -0
- empathy_llm_toolkit/claude_memory.py +14 -15
- empathy_llm_toolkit/cli/__init__.py +8 -0
- empathy_llm_toolkit/cli/sync_claude.py +487 -0
- empathy_llm_toolkit/code_health.py +186 -28
- empathy_llm_toolkit/config/__init__.py +29 -0
- empathy_llm_toolkit/config/unified.py +295 -0
- empathy_llm_toolkit/contextual_patterns.py +11 -12
- empathy_llm_toolkit/core.py +168 -53
- empathy_llm_toolkit/git_pattern_extractor.py +17 -13
- empathy_llm_toolkit/levels.py +6 -13
- empathy_llm_toolkit/pattern_confidence.py +14 -18
- empathy_llm_toolkit/pattern_resolver.py +10 -12
- empathy_llm_toolkit/pattern_summary.py +16 -14
- empathy_llm_toolkit/providers.py +194 -28
- empathy_llm_toolkit/routing/__init__.py +32 -0
- empathy_llm_toolkit/routing/model_router.py +362 -0
- empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +413 -0
- empathy_llm_toolkit/security/PHASE2_COMPLETE.md +384 -0
- empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- empathy_llm_toolkit/security/QUICK_REFERENCE.md +316 -0
- empathy_llm_toolkit/security/README.md +262 -0
- empathy_llm_toolkit/security/__init__.py +62 -0
- empathy_llm_toolkit/security/audit_logger.py +929 -0
- empathy_llm_toolkit/security/audit_logger_example.py +152 -0
- empathy_llm_toolkit/security/pii_scrubber.py +640 -0
- empathy_llm_toolkit/security/secrets_detector.py +678 -0
- empathy_llm_toolkit/security/secrets_detector_example.py +304 -0
- empathy_llm_toolkit/security/secure_memdocs.py +1192 -0
- empathy_llm_toolkit/security/secure_memdocs_example.py +278 -0
- empathy_llm_toolkit/session_status.py +20 -22
- empathy_llm_toolkit/state.py +28 -21
- empathy_llm_toolkit/wizards/__init__.py +38 -0
- empathy_llm_toolkit/wizards/base_wizard.py +364 -0
- empathy_llm_toolkit/wizards/customer_support_wizard.py +190 -0
- empathy_llm_toolkit/wizards/healthcare_wizard.py +362 -0
- empathy_llm_toolkit/wizards/patient_assessment_README.md +64 -0
- empathy_llm_toolkit/wizards/patient_assessment_wizard.py +193 -0
- empathy_llm_toolkit/wizards/technology_wizard.py +194 -0
- empathy_os/__init__.py +125 -84
- empathy_os/adaptive/__init__.py +13 -0
- empathy_os/adaptive/task_complexity.py +127 -0
- empathy_os/{monitoring.py → agent_monitoring.py} +28 -28
- empathy_os/cache/__init__.py +117 -0
- empathy_os/cache/base.py +166 -0
- empathy_os/cache/dependency_manager.py +253 -0
- empathy_os/cache/hash_only.py +248 -0
- empathy_os/cache/hybrid.py +390 -0
- empathy_os/cache/storage.py +282 -0
- empathy_os/cli.py +1516 -70
- empathy_os/cli_unified.py +597 -0
- empathy_os/config/__init__.py +63 -0
- empathy_os/config/xml_config.py +239 -0
- empathy_os/config.py +95 -37
- empathy_os/coordination.py +72 -68
- empathy_os/core.py +94 -107
- empathy_os/cost_tracker.py +74 -55
- empathy_os/dashboard/__init__.py +15 -0
- empathy_os/dashboard/server.py +743 -0
- empathy_os/discovery.py +17 -14
- empathy_os/emergence.py +21 -22
- empathy_os/exceptions.py +18 -30
- empathy_os/feedback_loops.py +30 -33
- empathy_os/levels.py +32 -35
- empathy_os/leverage_points.py +31 -32
- empathy_os/logging_config.py +19 -16
- empathy_os/memory/__init__.py +195 -0
- empathy_os/memory/claude_memory.py +466 -0
- empathy_os/memory/config.py +224 -0
- empathy_os/memory/control_panel.py +1298 -0
- empathy_os/memory/edges.py +179 -0
- empathy_os/memory/graph.py +567 -0
- empathy_os/memory/long_term.py +1194 -0
- empathy_os/memory/nodes.py +179 -0
- empathy_os/memory/redis_bootstrap.py +540 -0
- empathy_os/memory/security/__init__.py +31 -0
- empathy_os/memory/security/audit_logger.py +930 -0
- empathy_os/memory/security/pii_scrubber.py +640 -0
- empathy_os/memory/security/secrets_detector.py +678 -0
- empathy_os/memory/short_term.py +2119 -0
- empathy_os/memory/storage/__init__.py +15 -0
- empathy_os/memory/summary_index.py +583 -0
- empathy_os/memory/unified.py +619 -0
- empathy_os/metrics/__init__.py +12 -0
- empathy_os/metrics/prompt_metrics.py +190 -0
- empathy_os/models/__init__.py +136 -0
- empathy_os/models/__main__.py +13 -0
- empathy_os/models/cli.py +655 -0
- empathy_os/models/empathy_executor.py +354 -0
- empathy_os/models/executor.py +252 -0
- empathy_os/models/fallback.py +671 -0
- empathy_os/models/provider_config.py +563 -0
- empathy_os/models/registry.py +382 -0
- empathy_os/models/tasks.py +302 -0
- empathy_os/models/telemetry.py +548 -0
- empathy_os/models/token_estimator.py +378 -0
- empathy_os/models/validation.py +274 -0
- empathy_os/monitoring/__init__.py +52 -0
- empathy_os/monitoring/alerts.py +23 -0
- empathy_os/monitoring/alerts_cli.py +268 -0
- empathy_os/monitoring/multi_backend.py +271 -0
- empathy_os/monitoring/otel_backend.py +363 -0
- empathy_os/optimization/__init__.py +19 -0
- empathy_os/optimization/context_optimizer.py +272 -0
- empathy_os/pattern_library.py +30 -29
- empathy_os/persistence.py +35 -37
- empathy_os/platform_utils.py +261 -0
- empathy_os/plugins/__init__.py +28 -0
- empathy_os/plugins/base.py +361 -0
- empathy_os/plugins/registry.py +268 -0
- empathy_os/project_index/__init__.py +30 -0
- empathy_os/project_index/cli.py +335 -0
- empathy_os/project_index/crew_integration.py +430 -0
- empathy_os/project_index/index.py +425 -0
- empathy_os/project_index/models.py +501 -0
- empathy_os/project_index/reports.py +473 -0
- empathy_os/project_index/scanner.py +538 -0
- empathy_os/prompts/__init__.py +61 -0
- empathy_os/prompts/config.py +77 -0
- empathy_os/prompts/context.py +177 -0
- empathy_os/prompts/parser.py +285 -0
- empathy_os/prompts/registry.py +313 -0
- empathy_os/prompts/templates.py +208 -0
- empathy_os/redis_config.py +144 -58
- empathy_os/redis_memory.py +79 -77
- empathy_os/resilience/__init__.py +56 -0
- empathy_os/resilience/circuit_breaker.py +256 -0
- empathy_os/resilience/fallback.py +179 -0
- empathy_os/resilience/health.py +300 -0
- empathy_os/resilience/retry.py +209 -0
- empathy_os/resilience/timeout.py +135 -0
- empathy_os/routing/__init__.py +43 -0
- empathy_os/routing/chain_executor.py +433 -0
- empathy_os/routing/classifier.py +217 -0
- empathy_os/routing/smart_router.py +234 -0
- empathy_os/routing/wizard_registry.py +307 -0
- empathy_os/templates.py +19 -14
- empathy_os/trust/__init__.py +28 -0
- empathy_os/trust/circuit_breaker.py +579 -0
- empathy_os/trust_building.py +67 -58
- empathy_os/validation/__init__.py +19 -0
- empathy_os/validation/xml_validator.py +281 -0
- empathy_os/wizard_factory_cli.py +170 -0
- empathy_os/{workflows.py → workflow_commands.py} +131 -37
- empathy_os/workflows/__init__.py +360 -0
- empathy_os/workflows/base.py +1660 -0
- empathy_os/workflows/bug_predict.py +962 -0
- empathy_os/workflows/code_review.py +960 -0
- empathy_os/workflows/code_review_adapters.py +310 -0
- empathy_os/workflows/code_review_pipeline.py +720 -0
- empathy_os/workflows/config.py +600 -0
- empathy_os/workflows/dependency_check.py +648 -0
- empathy_os/workflows/document_gen.py +1069 -0
- empathy_os/workflows/documentation_orchestrator.py +1205 -0
- empathy_os/workflows/health_check.py +679 -0
- empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
- empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
- empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
- empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
- empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
- empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
- empathy_os/workflows/manage_documentation.py +804 -0
- empathy_os/workflows/new_sample_workflow1.py +146 -0
- empathy_os/workflows/new_sample_workflow1_README.md +150 -0
- empathy_os/workflows/perf_audit.py +687 -0
- empathy_os/workflows/pr_review.py +748 -0
- empathy_os/workflows/progress.py +445 -0
- empathy_os/workflows/progress_server.py +322 -0
- empathy_os/workflows/refactor_plan.py +693 -0
- empathy_os/workflows/release_prep.py +808 -0
- empathy_os/workflows/research_synthesis.py +404 -0
- empathy_os/workflows/secure_release.py +585 -0
- empathy_os/workflows/security_adapters.py +297 -0
- empathy_os/workflows/security_audit.py +1046 -0
- empathy_os/workflows/step_config.py +234 -0
- empathy_os/workflows/test5.py +125 -0
- empathy_os/workflows/test5_README.md +158 -0
- empathy_os/workflows/test_gen.py +1855 -0
- empathy_os/workflows/test_lifecycle.py +526 -0
- empathy_os/workflows/test_maintenance.py +626 -0
- empathy_os/workflows/test_maintenance_cli.py +590 -0
- empathy_os/workflows/test_maintenance_crew.py +821 -0
- empathy_os/workflows/xml_enhanced_crew.py +285 -0
- empathy_software_plugin/__init__.py +1 -2
- empathy_software_plugin/cli/__init__.py +120 -0
- empathy_software_plugin/cli/inspect.py +362 -0
- empathy_software_plugin/cli.py +49 -27
- empathy_software_plugin/plugin.py +4 -8
- empathy_software_plugin/wizards/__init__.py +42 -0
- empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
- empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
- empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
- empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
- empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
- empathy_software_plugin/wizards/base_wizard.py +288 -0
- empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
- empathy_software_plugin/wizards/code_review_wizard.py +606 -0
- empathy_software_plugin/wizards/debugging/__init__.py +50 -0
- empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
- empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
- empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
- empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
- empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
- empathy_software_plugin/wizards/debugging/verification.py +369 -0
- empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
- empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
- empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
- empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
- empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
- empathy_software_plugin/wizards/performance/__init__.py +9 -0
- empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
- empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
- empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
- empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
- empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
- empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
- empathy_software_plugin/wizards/security/__init__.py +32 -0
- empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
- empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
- empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
- empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
- empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
- empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
- empathy_software_plugin/wizards/testing/__init__.py +27 -0
- empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
- empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
- empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
- empathy_software_plugin/wizards/testing_wizard.py +274 -0
- hot_reload/README.md +473 -0
- hot_reload/__init__.py +62 -0
- hot_reload/config.py +84 -0
- hot_reload/integration.py +228 -0
- hot_reload/reloader.py +298 -0
- hot_reload/watcher.py +179 -0
- hot_reload/websocket.py +176 -0
- scaffolding/README.md +589 -0
- scaffolding/__init__.py +35 -0
- scaffolding/__main__.py +14 -0
- scaffolding/cli.py +240 -0
- test_generator/__init__.py +38 -0
- test_generator/__main__.py +14 -0
- test_generator/cli.py +226 -0
- test_generator/generator.py +325 -0
- test_generator/risk_analyzer.py +216 -0
- workflow_patterns/__init__.py +33 -0
- workflow_patterns/behavior.py +249 -0
- workflow_patterns/core.py +76 -0
- workflow_patterns/output.py +99 -0
- workflow_patterns/registry.py +255 -0
- workflow_patterns/structural.py +288 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
- agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
- agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
- agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
- agents/compliance_anticipation_agent.py +0 -1427
- agents/epic_integration_wizard.py +0 -541
- agents/trust_building_behaviors.py +0 -891
- empathy_framework-2.4.0.dist-info/METADATA +0 -485
- empathy_framework-2.4.0.dist-info/RECORD +0 -102
- empathy_framework-2.4.0.dist-info/entry_points.txt +0 -6
- empathy_llm_toolkit/htmlcov/status.json +0 -1
- empathy_llm_toolkit/security/htmlcov/status.json +0 -1
- {empathy_framework-2.4.0.dist-info → empathy_framework-3.8.2.dist-info}/WHEEL +0 -0
- {empathy_framework-2.4.0.dist-info → empathy_framework-3.8.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Protocol Loader
|
|
2
|
+
|
|
3
|
+
Loads clinical pathway protocols from JSON files.
|
|
4
|
+
|
|
5
|
+
This is like loading linting configs - protocols define the rules.
|
|
6
|
+
|
|
7
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
8
|
+
Licensed under Fair Source 0.9
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ProtocolCriterion:
|
|
19
|
+
"""A single criterion in a protocol"""
|
|
20
|
+
|
|
21
|
+
parameter: str
|
|
22
|
+
condition: str # "<=", ">=", "==", "!=", "altered", etc.
|
|
23
|
+
value: Any | None = None
|
|
24
|
+
points: int = 0
|
|
25
|
+
description: str | None = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class ProtocolIntervention:
|
|
30
|
+
"""A required intervention"""
|
|
31
|
+
|
|
32
|
+
order: int
|
|
33
|
+
action: str
|
|
34
|
+
timing: str
|
|
35
|
+
required: bool = True
|
|
36
|
+
parameters: dict[str, Any] | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class ClinicalProtocol:
|
|
41
|
+
"""Clinical pathway protocol.
|
|
42
|
+
|
|
43
|
+
This is the "linting config" for healthcare - defines the rules.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
name: str
|
|
47
|
+
version: str
|
|
48
|
+
applies_to: list[str]
|
|
49
|
+
|
|
50
|
+
# Screening criteria (when to activate protocol)
|
|
51
|
+
screening_criteria: list[ProtocolCriterion]
|
|
52
|
+
screening_threshold: int
|
|
53
|
+
|
|
54
|
+
# Required interventions (what to do)
|
|
55
|
+
interventions: list[ProtocolIntervention]
|
|
56
|
+
|
|
57
|
+
# Monitoring requirements
|
|
58
|
+
monitoring_frequency: str
|
|
59
|
+
reassessment_timing: str
|
|
60
|
+
|
|
61
|
+
# Escalation criteria (when to call for help)
|
|
62
|
+
escalation_criteria: list[str] | None = None
|
|
63
|
+
|
|
64
|
+
# Documentation requirements
|
|
65
|
+
documentation_requirements: list[str] | None = None
|
|
66
|
+
|
|
67
|
+
# Raw protocol data
|
|
68
|
+
raw_protocol: dict[str, Any] | None = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ProtocolLoader:
|
|
72
|
+
"""Loads clinical protocols from JSON files.
|
|
73
|
+
|
|
74
|
+
Similar to loading .eslintrc or pyproject.toml - we're loading
|
|
75
|
+
the protocol configuration.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, protocol_directory: str | None = None):
|
|
79
|
+
if protocol_directory:
|
|
80
|
+
self.protocol_dir = Path(protocol_directory)
|
|
81
|
+
else:
|
|
82
|
+
# Default to protocols directory in plugin
|
|
83
|
+
plugin_dir = Path(__file__).parent.parent.parent
|
|
84
|
+
self.protocol_dir = plugin_dir / "protocols"
|
|
85
|
+
|
|
86
|
+
def load_protocol(self, protocol_name: str) -> ClinicalProtocol:
|
|
87
|
+
"""Load protocol by name.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
protocol_name: Name of protocol (e.g., "sepsis", "post_operative")
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
ClinicalProtocol object
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
>>> loader = ProtocolLoader()
|
|
97
|
+
>>> protocol = loader.load_protocol("sepsis")
|
|
98
|
+
>>> print(f"Loaded: {protocol.name} v{protocol.version}")
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
protocol_file = self.protocol_dir / f"{protocol_name}.json"
|
|
102
|
+
|
|
103
|
+
if not protocol_file.exists():
|
|
104
|
+
raise FileNotFoundError(
|
|
105
|
+
f"Protocol not found: {protocol_name}\nLooked in: {self.protocol_dir}",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
with open(protocol_file) as f:
|
|
109
|
+
data = json.load(f)
|
|
110
|
+
|
|
111
|
+
return self._parse_protocol(data)
|
|
112
|
+
|
|
113
|
+
def _parse_protocol(self, data: dict[str, Any]) -> ClinicalProtocol:
|
|
114
|
+
"""Parse protocol JSON into ClinicalProtocol object"""
|
|
115
|
+
# Parse screening criteria
|
|
116
|
+
screening_data = data.get("screening_criteria", {})
|
|
117
|
+
criteria = []
|
|
118
|
+
|
|
119
|
+
for crit in screening_data.get("criteria", []):
|
|
120
|
+
criteria.append(
|
|
121
|
+
ProtocolCriterion(
|
|
122
|
+
parameter=crit["parameter"],
|
|
123
|
+
condition=crit["condition"],
|
|
124
|
+
value=crit.get("value"),
|
|
125
|
+
points=crit.get("points", 0),
|
|
126
|
+
description=crit.get("description"),
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Parse interventions
|
|
131
|
+
interventions = []
|
|
132
|
+
for interv in data.get("interventions", []):
|
|
133
|
+
interventions.append(
|
|
134
|
+
ProtocolIntervention(
|
|
135
|
+
order=interv["order"],
|
|
136
|
+
action=interv["action"],
|
|
137
|
+
timing=interv["timing"],
|
|
138
|
+
required=interv.get("required", True),
|
|
139
|
+
parameters=interv.get("parameters"),
|
|
140
|
+
),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Parse monitoring requirements
|
|
144
|
+
monitoring = data.get("monitoring_requirements", {})
|
|
145
|
+
|
|
146
|
+
return ClinicalProtocol(
|
|
147
|
+
name=data["protocol_name"],
|
|
148
|
+
version=data["protocol_version"],
|
|
149
|
+
applies_to=data.get("applies_to", []),
|
|
150
|
+
screening_criteria=criteria,
|
|
151
|
+
screening_threshold=screening_data.get("threshold", 0),
|
|
152
|
+
interventions=interventions,
|
|
153
|
+
monitoring_frequency=monitoring.get("vitals_frequency", "hourly"),
|
|
154
|
+
reassessment_timing=monitoring.get("reassessment", "hourly"),
|
|
155
|
+
escalation_criteria=data.get("escalation_criteria", {}).get("if", []),
|
|
156
|
+
documentation_requirements=data.get("documentation_requirements", []),
|
|
157
|
+
raw_protocol=data,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def list_available_protocols(self) -> list[str]:
|
|
161
|
+
"""List all available protocols"""
|
|
162
|
+
if not self.protocol_dir.exists():
|
|
163
|
+
return []
|
|
164
|
+
|
|
165
|
+
protocols = []
|
|
166
|
+
for file in self.protocol_dir.glob("*.json"):
|
|
167
|
+
protocols.append(file.stem)
|
|
168
|
+
|
|
169
|
+
return sorted(protocols)
|
|
170
|
+
|
|
171
|
+
def validate_protocol(self, protocol: ClinicalProtocol) -> list[str]:
|
|
172
|
+
"""Validate protocol structure.
|
|
173
|
+
|
|
174
|
+
Returns list of validation errors (empty if valid)
|
|
175
|
+
"""
|
|
176
|
+
errors = []
|
|
177
|
+
|
|
178
|
+
if not protocol.name:
|
|
179
|
+
errors.append("Protocol must have a name")
|
|
180
|
+
|
|
181
|
+
if not protocol.version:
|
|
182
|
+
errors.append("Protocol must have a version")
|
|
183
|
+
|
|
184
|
+
if not protocol.screening_criteria:
|
|
185
|
+
errors.append("Protocol must have screening criteria")
|
|
186
|
+
|
|
187
|
+
if not protocol.interventions:
|
|
188
|
+
errors.append("Protocol must have interventions")
|
|
189
|
+
|
|
190
|
+
# Check intervention order
|
|
191
|
+
orders = [i.order for i in protocol.interventions]
|
|
192
|
+
if len(orders) != len(set(orders)):
|
|
193
|
+
errors.append("Intervention orders must be unique")
|
|
194
|
+
|
|
195
|
+
return errors
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def load_protocol(protocol_name: str, protocol_dir: str | None = None) -> ClinicalProtocol:
|
|
199
|
+
"""Convenience function to load a protocol.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
protocol_name: Name of protocol
|
|
203
|
+
protocol_dir: Optional custom protocol directory
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
ClinicalProtocol object
|
|
207
|
+
|
|
208
|
+
Example:
|
|
209
|
+
>>> protocol = load_protocol("sepsis")
|
|
210
|
+
>>> print(f"{protocol.name}: {len(protocol.interventions)} interventions")
|
|
211
|
+
|
|
212
|
+
"""
|
|
213
|
+
loader = ProtocolLoader(protocol_dir)
|
|
214
|
+
return loader.load_protocol(protocol_name)
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Sensor Data Parsers
|
|
2
|
+
|
|
3
|
+
Parses sensor data from various formats (HL7, FHIR, manual entry).
|
|
4
|
+
|
|
5
|
+
This is like parsing linter output - converting various formats to standard structure.
|
|
6
|
+
|
|
7
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
8
|
+
Licensed under Fair Source 0.9
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class VitalSignType(Enum):
|
|
19
|
+
"""Types of vital signs"""
|
|
20
|
+
|
|
21
|
+
HEART_RATE = "heart_rate"
|
|
22
|
+
BLOOD_PRESSURE = "blood_pressure"
|
|
23
|
+
RESPIRATORY_RATE = "respiratory_rate"
|
|
24
|
+
TEMPERATURE = "temperature"
|
|
25
|
+
OXYGEN_SATURATION = "oxygen_saturation"
|
|
26
|
+
MENTAL_STATUS = "mental_status"
|
|
27
|
+
PAIN_SCORE = "pain_score"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class VitalSignReading:
|
|
32
|
+
"""Standardized vital sign reading.
|
|
33
|
+
|
|
34
|
+
This is the universal format - all parsers convert to this.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
vital_type: VitalSignType
|
|
38
|
+
value: Any
|
|
39
|
+
unit: str
|
|
40
|
+
timestamp: datetime
|
|
41
|
+
source: str # "bedside_monitor", "manual_entry", "wearable"
|
|
42
|
+
patient_id: str
|
|
43
|
+
quality: str | None = None # "good", "poor", "artifact"
|
|
44
|
+
metadata: dict[str, Any] | None = None
|
|
45
|
+
|
|
46
|
+
def to_dict(self) -> dict[str, Any]:
|
|
47
|
+
"""Convert to dictionary"""
|
|
48
|
+
return {
|
|
49
|
+
"vital_type": self.vital_type.value,
|
|
50
|
+
"value": self.value,
|
|
51
|
+
"unit": self.unit,
|
|
52
|
+
"timestamp": self.timestamp.isoformat(),
|
|
53
|
+
"source": self.source,
|
|
54
|
+
"patient_id": self.patient_id,
|
|
55
|
+
"quality": self.quality,
|
|
56
|
+
"metadata": self.metadata or {},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BaseSensorParser:
|
|
61
|
+
"""Base class for sensor data parsers"""
|
|
62
|
+
|
|
63
|
+
def parse(self, data: str) -> list[VitalSignReading]:
|
|
64
|
+
"""Parse sensor data into standardized readings"""
|
|
65
|
+
raise NotImplementedError(
|
|
66
|
+
f"{self.__class__.__name__}.parse() must be implemented. "
|
|
67
|
+
"Create a subclass of BaseSensorParser and implement the parse() method. "
|
|
68
|
+
f"See FHIRObservationParser or SimpleJSONParser for examples."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class FHIRObservationParser(BaseSensorParser):
|
|
73
|
+
"""Parse FHIR Observation resources.
|
|
74
|
+
|
|
75
|
+
FHIR is standard for healthcare data exchange.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
# LOINC codes for common vitals
|
|
79
|
+
LOINC_MAPPINGS = {
|
|
80
|
+
"8867-4": VitalSignType.HEART_RATE,
|
|
81
|
+
"8480-6": VitalSignType.BLOOD_PRESSURE, # Systolic
|
|
82
|
+
"8462-4": VitalSignType.BLOOD_PRESSURE, # Diastolic
|
|
83
|
+
"9279-1": VitalSignType.RESPIRATORY_RATE,
|
|
84
|
+
"8310-5": VitalSignType.TEMPERATURE,
|
|
85
|
+
"2708-6": VitalSignType.OXYGEN_SATURATION,
|
|
86
|
+
"38208-5": VitalSignType.PAIN_SCORE,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
def parse(self, data: str) -> list[VitalSignReading]:
|
|
90
|
+
"""Parse FHIR Observation JSON"""
|
|
91
|
+
try:
|
|
92
|
+
observation = json.loads(data)
|
|
93
|
+
except json.JSONDecodeError:
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
if observation.get("resourceType") != "Observation":
|
|
97
|
+
return []
|
|
98
|
+
|
|
99
|
+
readings = []
|
|
100
|
+
|
|
101
|
+
# Extract LOINC code
|
|
102
|
+
code = observation.get("code", {})
|
|
103
|
+
loinc_code = None
|
|
104
|
+
|
|
105
|
+
for coding in code.get("coding", []):
|
|
106
|
+
if coding.get("system") == "http://loinc.org":
|
|
107
|
+
loinc_code = coding.get("code")
|
|
108
|
+
break
|
|
109
|
+
|
|
110
|
+
if not loinc_code or loinc_code not in self.LOINC_MAPPINGS:
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
vital_type = self.LOINC_MAPPINGS[loinc_code]
|
|
114
|
+
|
|
115
|
+
# Extract value
|
|
116
|
+
value_qty = observation.get("valueQuantity", {})
|
|
117
|
+
value = value_qty.get("value")
|
|
118
|
+
unit = value_qty.get("unit", "")
|
|
119
|
+
|
|
120
|
+
# Extract timestamp
|
|
121
|
+
timestamp_str = observation.get("effectiveDateTime")
|
|
122
|
+
timestamp = (
|
|
123
|
+
datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
|
|
124
|
+
if timestamp_str
|
|
125
|
+
else datetime.now()
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Extract patient ID
|
|
129
|
+
subject = observation.get("subject", {})
|
|
130
|
+
patient_id = subject.get("reference", "").split("/")[-1]
|
|
131
|
+
|
|
132
|
+
reading = VitalSignReading(
|
|
133
|
+
vital_type=vital_type,
|
|
134
|
+
value=value,
|
|
135
|
+
unit=unit,
|
|
136
|
+
timestamp=timestamp,
|
|
137
|
+
source="fhir_observation",
|
|
138
|
+
patient_id=patient_id,
|
|
139
|
+
metadata={"loinc_code": loinc_code},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
readings.append(reading)
|
|
143
|
+
|
|
144
|
+
return readings
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SimpleJSONParser(BaseSensorParser):
|
|
148
|
+
"""Parse simple JSON format for manual entry or simulation.
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
{
|
|
152
|
+
"patient_id": "12345",
|
|
153
|
+
"timestamp": "2024-01-20T14:30:00Z",
|
|
154
|
+
"vitals": {
|
|
155
|
+
"hr": 110,
|
|
156
|
+
"systolic_bp": 95,
|
|
157
|
+
"diastolic_bp": 60,
|
|
158
|
+
"respiratory_rate": 24,
|
|
159
|
+
"temp_f": 101.5,
|
|
160
|
+
"o2_sat": 94
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
VITAL_MAPPINGS = {
|
|
167
|
+
"hr": (VitalSignType.HEART_RATE, "bpm"),
|
|
168
|
+
"heart_rate": (VitalSignType.HEART_RATE, "bpm"),
|
|
169
|
+
"systolic_bp": (VitalSignType.BLOOD_PRESSURE, "mmHg"),
|
|
170
|
+
"diastolic_bp": (VitalSignType.BLOOD_PRESSURE, "mmHg"),
|
|
171
|
+
"bp": (VitalSignType.BLOOD_PRESSURE, "mmHg"),
|
|
172
|
+
"respiratory_rate": (VitalSignType.RESPIRATORY_RATE, "/min"),
|
|
173
|
+
"rr": (VitalSignType.RESPIRATORY_RATE, "/min"),
|
|
174
|
+
"temp_f": (VitalSignType.TEMPERATURE, "°F"),
|
|
175
|
+
"temp_c": (VitalSignType.TEMPERATURE, "°C"),
|
|
176
|
+
"temperature": (VitalSignType.TEMPERATURE, "°F"),
|
|
177
|
+
"o2_sat": (VitalSignType.OXYGEN_SATURATION, "%"),
|
|
178
|
+
"spo2": (VitalSignType.OXYGEN_SATURATION, "%"),
|
|
179
|
+
"mental_status": (VitalSignType.MENTAL_STATUS, "text"),
|
|
180
|
+
"pain": (VitalSignType.PAIN_SCORE, "0-10"),
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
def parse(self, data: str) -> list[VitalSignReading]:
|
|
184
|
+
"""Parse simple JSON format"""
|
|
185
|
+
try:
|
|
186
|
+
parsed = json.loads(data)
|
|
187
|
+
except json.JSONDecodeError:
|
|
188
|
+
return []
|
|
189
|
+
|
|
190
|
+
patient_id = parsed.get("patient_id", "unknown")
|
|
191
|
+
timestamp_str = parsed.get("timestamp")
|
|
192
|
+
timestamp = (
|
|
193
|
+
datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
|
|
194
|
+
if timestamp_str
|
|
195
|
+
else datetime.now()
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
vitals = parsed.get("vitals", {})
|
|
199
|
+
|
|
200
|
+
readings = []
|
|
201
|
+
|
|
202
|
+
for key, value in vitals.items():
|
|
203
|
+
if key in self.VITAL_MAPPINGS:
|
|
204
|
+
vital_type, unit = self.VITAL_MAPPINGS[key]
|
|
205
|
+
|
|
206
|
+
reading = VitalSignReading(
|
|
207
|
+
vital_type=vital_type,
|
|
208
|
+
value=value,
|
|
209
|
+
unit=unit,
|
|
210
|
+
timestamp=timestamp,
|
|
211
|
+
source="manual_entry",
|
|
212
|
+
patient_id=patient_id,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
readings.append(reading)
|
|
216
|
+
|
|
217
|
+
return readings
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class SensorParserFactory:
|
|
221
|
+
"""Factory for creating appropriate sensor parser"""
|
|
222
|
+
|
|
223
|
+
_parsers = {"fhir": FHIRObservationParser, "simple_json": SimpleJSONParser}
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def create(cls, format_type: str) -> BaseSensorParser:
|
|
227
|
+
"""Create parser for specified format"""
|
|
228
|
+
parser_class = cls._parsers.get(format_type)
|
|
229
|
+
|
|
230
|
+
if not parser_class:
|
|
231
|
+
raise ValueError(
|
|
232
|
+
f"Unsupported sensor format: {format_type}. "
|
|
233
|
+
f"Supported: {', '.join(cls._parsers.keys())}",
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return parser_class()
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def parse_sensor_data(data: str, format_type: str = "simple_json") -> list[VitalSignReading]:
|
|
240
|
+
"""Convenience function to parse sensor data.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
data: Raw sensor data (JSON string)
|
|
244
|
+
format_type: "fhir" or "simple_json"
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
List of VitalSignReading objects
|
|
248
|
+
|
|
249
|
+
Example:
|
|
250
|
+
>>> data = '{"patient_id": "12345", "vitals": {"hr": 110}}'
|
|
251
|
+
>>> readings = parse_sensor_data(data, "simple_json")
|
|
252
|
+
>>> print(f"HR: {readings[0].value} {readings[0].unit}")
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
parser = SensorParserFactory.create(format_type)
|
|
256
|
+
return parser.parse(data)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def normalize_vitals(readings: list[VitalSignReading]) -> dict[str, Any]:
|
|
260
|
+
"""Normalize vital sign readings into protocol-checkable format.
|
|
261
|
+
|
|
262
|
+
Takes list of VitalSignReading and converts to dict for protocol checker.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
readings: List of vital sign readings
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Dictionary with normalized values for protocol checking
|
|
269
|
+
|
|
270
|
+
Example:
|
|
271
|
+
>>> normalized = normalize_vitals(readings)
|
|
272
|
+
>>> # Returns: {"hr": 110, "systolic_bp": 95, "respiratory_rate": 24}
|
|
273
|
+
|
|
274
|
+
"""
|
|
275
|
+
normalized = {}
|
|
276
|
+
|
|
277
|
+
for reading in readings:
|
|
278
|
+
if reading.vital_type == VitalSignType.HEART_RATE:
|
|
279
|
+
normalized["hr"] = reading.value
|
|
280
|
+
|
|
281
|
+
elif reading.vital_type == VitalSignType.BLOOD_PRESSURE:
|
|
282
|
+
# Determine if systolic or diastolic based on value
|
|
283
|
+
if reading.value > 60: # Likely systolic
|
|
284
|
+
normalized["systolic_bp"] = reading.value
|
|
285
|
+
else: # Likely diastolic
|
|
286
|
+
normalized["diastolic_bp"] = reading.value
|
|
287
|
+
|
|
288
|
+
elif reading.vital_type == VitalSignType.RESPIRATORY_RATE:
|
|
289
|
+
normalized["respiratory_rate"] = reading.value
|
|
290
|
+
|
|
291
|
+
elif reading.vital_type == VitalSignType.TEMPERATURE:
|
|
292
|
+
if reading.unit == "°F":
|
|
293
|
+
normalized["temp_f"] = reading.value
|
|
294
|
+
elif reading.unit == "°C":
|
|
295
|
+
normalized["temp_c"] = reading.value
|
|
296
|
+
|
|
297
|
+
elif reading.vital_type == VitalSignType.OXYGEN_SATURATION:
|
|
298
|
+
normalized["o2_sat"] = reading.value
|
|
299
|
+
|
|
300
|
+
elif reading.vital_type == VitalSignType.MENTAL_STATUS:
|
|
301
|
+
normalized["mental_status"] = reading.value
|
|
302
|
+
|
|
303
|
+
elif reading.vital_type == VitalSignType.PAIN_SCORE:
|
|
304
|
+
normalized["pain_score"] = reading.value
|
|
305
|
+
|
|
306
|
+
return normalized
|