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
|
@@ -257,9 +257,7 @@ class WorkflowReference:
|
|
|
257
257
|
def __post_init__(self):
|
|
258
258
|
"""Validate that exactly one reference type is provided."""
|
|
259
259
|
if bool(self.workflow_id) == bool(self.inline):
|
|
260
|
-
raise ValueError(
|
|
261
|
-
"WorkflowReference must have exactly one of: workflow_id or inline"
|
|
262
|
-
)
|
|
260
|
+
raise ValueError("WorkflowReference must have exactly one of: workflow_id or inline")
|
|
263
261
|
|
|
264
262
|
|
|
265
263
|
@dataclass
|
|
@@ -415,8 +413,7 @@ def get_workflow(workflow_id: str) -> WorkflowDefinition:
|
|
|
415
413
|
"""
|
|
416
414
|
if workflow_id not in WORKFLOW_REGISTRY:
|
|
417
415
|
raise ValueError(
|
|
418
|
-
f"Unknown workflow: {workflow_id}. "
|
|
419
|
-
f"Available: {list(WORKFLOW_REGISTRY.keys())}"
|
|
416
|
+
f"Unknown workflow: {workflow_id}. Available: {list(WORKFLOW_REGISTRY.keys())}"
|
|
420
417
|
)
|
|
421
418
|
return WORKFLOW_REGISTRY[workflow_id]
|
|
422
419
|
|
|
@@ -530,9 +527,7 @@ class ConditionEvaluator:
|
|
|
530
527
|
|
|
531
528
|
return current
|
|
532
529
|
|
|
533
|
-
def _evaluate_natural_language(
|
|
534
|
-
self, condition_text: str, context: dict[str, Any]
|
|
535
|
-
) -> bool:
|
|
530
|
+
def _evaluate_natural_language(self, condition_text: str, context: dict[str, Any]) -> bool:
|
|
536
531
|
"""Evaluate natural language condition using LLM.
|
|
537
532
|
|
|
538
533
|
Args:
|
|
@@ -617,9 +612,7 @@ Respond with ONLY "TRUE" or "FALSE" (no explanation)."""
|
|
|
617
612
|
result = match_ratio > 0.5
|
|
618
613
|
return not result if is_negated else result
|
|
619
614
|
|
|
620
|
-
def _evaluate_composite(
|
|
621
|
-
self, predicate: dict[str, Any], context: dict[str, Any]
|
|
622
|
-
) -> bool:
|
|
615
|
+
def _evaluate_composite(self, predicate: dict[str, Any], context: dict[str, Any]) -> bool:
|
|
623
616
|
"""Evaluate composite condition (AND/OR of other conditions).
|
|
624
617
|
|
|
625
618
|
Args:
|
|
@@ -1159,7 +1152,7 @@ class RefinementStrategy(ExecutionStrategy):
|
|
|
1159
1152
|
total_duration = 0.0
|
|
1160
1153
|
|
|
1161
1154
|
for i, agent in enumerate(agents):
|
|
1162
|
-
stage_name = f"stage_{i+1}"
|
|
1155
|
+
stage_name = f"stage_{i + 1}"
|
|
1163
1156
|
logger.info(f"Refinement {stage_name}: {agent.id}")
|
|
1164
1157
|
|
|
1165
1158
|
result = await self._execute_agent(agent, current_context)
|
|
@@ -1171,7 +1164,7 @@ class RefinementStrategy(ExecutionStrategy):
|
|
|
1171
1164
|
current_context[f"{stage_name}_output"] = result.output
|
|
1172
1165
|
current_context["previous_output"] = result.output
|
|
1173
1166
|
else:
|
|
1174
|
-
logger.error(f"Refinement stage {i+1} failed: {result.error}")
|
|
1167
|
+
logger.error(f"Refinement stage {i + 1} failed: {result.error}")
|
|
1175
1168
|
break # Stop refinement on failure
|
|
1176
1169
|
|
|
1177
1170
|
# Final output is from last successful stage
|
|
@@ -1303,9 +1296,7 @@ class ConditionalStrategy(ExecutionStrategy):
|
|
|
1303
1296
|
self.else_branch = else_branch
|
|
1304
1297
|
self.evaluator = ConditionEvaluator()
|
|
1305
1298
|
|
|
1306
|
-
async def execute(
|
|
1307
|
-
self, agents: list[AgentTemplate], context: dict[str, Any]
|
|
1308
|
-
) -> StrategyResult:
|
|
1299
|
+
async def execute(self, agents: list[AgentTemplate], context: dict[str, Any]) -> StrategyResult:
|
|
1309
1300
|
"""Execute conditional branching."""
|
|
1310
1301
|
logger.info(f"Conditional: Evaluating '{self.condition.description or 'condition'}'")
|
|
1311
1302
|
|
|
@@ -1353,9 +1344,7 @@ class MultiConditionalStrategy(ExecutionStrategy):
|
|
|
1353
1344
|
self.default_branch = default_branch
|
|
1354
1345
|
self.evaluator = ConditionEvaluator()
|
|
1355
1346
|
|
|
1356
|
-
async def execute(
|
|
1357
|
-
self, agents: list[AgentTemplate], context: dict[str, Any]
|
|
1358
|
-
) -> StrategyResult:
|
|
1347
|
+
async def execute(self, agents: list[AgentTemplate], context: dict[str, Any]) -> StrategyResult:
|
|
1359
1348
|
"""Execute multi-conditional branching."""
|
|
1360
1349
|
for i, (condition, branch) in enumerate(self.conditions):
|
|
1361
1350
|
if self.evaluator.evaluate(condition, context):
|
|
@@ -1427,9 +1416,7 @@ class NestedStrategy(ExecutionStrategy):
|
|
|
1427
1416
|
self.workflow_ref = workflow_ref
|
|
1428
1417
|
self.max_depth = max_depth
|
|
1429
1418
|
|
|
1430
|
-
async def execute(
|
|
1431
|
-
self, agents: list[AgentTemplate], context: dict[str, Any]
|
|
1432
|
-
) -> StrategyResult:
|
|
1419
|
+
async def execute(self, agents: list[AgentTemplate], context: dict[str, Any]) -> StrategyResult:
|
|
1433
1420
|
"""Execute nested workflow.
|
|
1434
1421
|
|
|
1435
1422
|
Args:
|
|
@@ -1471,9 +1458,7 @@ class NestedStrategy(ExecutionStrategy):
|
|
|
1471
1458
|
logger.error(error_msg)
|
|
1472
1459
|
raise RecursionError(error_msg)
|
|
1473
1460
|
|
|
1474
|
-
logger.info(
|
|
1475
|
-
f"Nested: Entering '{workflow_id}' at depth {nesting.current_depth + 1}"
|
|
1476
|
-
)
|
|
1461
|
+
logger.info(f"Nested: Entering '{workflow_id}' at depth {nesting.current_depth + 1}")
|
|
1477
1462
|
|
|
1478
1463
|
# Create child context with updated nesting
|
|
1479
1464
|
child_nesting = nesting.enter(workflow_id)
|
|
@@ -1529,9 +1514,7 @@ class NestedSequentialStrategy(ExecutionStrategy):
|
|
|
1529
1514
|
self.steps = steps
|
|
1530
1515
|
self.max_depth = max_depth
|
|
1531
1516
|
|
|
1532
|
-
async def execute(
|
|
1533
|
-
self, agents: list[AgentTemplate], context: dict[str, Any]
|
|
1534
|
-
) -> StrategyResult:
|
|
1517
|
+
async def execute(self, agents: list[AgentTemplate], context: dict[str, Any]) -> StrategyResult:
|
|
1535
1518
|
"""Execute steps sequentially, handling both agents and nested workflows."""
|
|
1536
1519
|
if not self.steps:
|
|
1537
1520
|
raise ValueError("steps list cannot be empty")
|
|
@@ -1640,7 +1623,7 @@ def get_strategy(strategy_name: str) -> ExecutionStrategy:
|
|
|
1640
1623
|
"""
|
|
1641
1624
|
if strategy_name not in STRATEGY_REGISTRY:
|
|
1642
1625
|
raise ValueError(
|
|
1643
|
-
f"Unknown strategy: {strategy_name}.
|
|
1626
|
+
f"Unknown strategy: {strategy_name}. Available: {list(STRATEGY_REGISTRY.keys())}"
|
|
1644
1627
|
)
|
|
1645
1628
|
|
|
1646
1629
|
strategy_class = STRATEGY_REGISTRY[strategy_name]
|
|
@@ -17,6 +17,7 @@ Security:
|
|
|
17
17
|
import json
|
|
18
18
|
import logging
|
|
19
19
|
from collections import defaultdict
|
|
20
|
+
from collections.abc import Iterator
|
|
20
21
|
from dataclasses import asdict, dataclass, field
|
|
21
22
|
from datetime import datetime
|
|
22
23
|
from pathlib import Path
|
|
@@ -121,9 +122,7 @@ class PatternStats:
|
|
|
121
122
|
|
|
122
123
|
# Running average for confidence
|
|
123
124
|
n = self.total_executions
|
|
124
|
-
self.avg_confidence = (
|
|
125
|
-
(self.avg_confidence * (n - 1) + record.confidence) / n
|
|
126
|
-
)
|
|
125
|
+
self.avg_confidence = (self.avg_confidence * (n - 1) + record.confidence) / n
|
|
127
126
|
|
|
128
127
|
def to_dict(self) -> dict[str, Any]:
|
|
129
128
|
"""Convert to dictionary for serialization."""
|
|
@@ -289,21 +288,14 @@ class LearningStore:
|
|
|
289
288
|
data = json.load(f)
|
|
290
289
|
|
|
291
290
|
# Load records
|
|
292
|
-
self._records = [
|
|
293
|
-
ExecutionRecord.from_dict(r) for r in data.get("records", [])
|
|
294
|
-
]
|
|
291
|
+
self._records = [ExecutionRecord.from_dict(r) for r in data.get("records", [])]
|
|
295
292
|
|
|
296
293
|
# Load stats
|
|
297
|
-
self._stats = {
|
|
298
|
-
s["pattern"]: PatternStats.from_dict(s)
|
|
299
|
-
for s in data.get("stats", [])
|
|
300
|
-
}
|
|
294
|
+
self._stats = {s["pattern"]: PatternStats.from_dict(s) for s in data.get("stats", [])}
|
|
301
295
|
|
|
302
296
|
# Rebuild context index
|
|
303
297
|
for i, record in enumerate(self._records):
|
|
304
|
-
sig = ContextSignature(
|
|
305
|
-
task_type=record.context_features.get("task_type", "")
|
|
306
|
-
)
|
|
298
|
+
sig = ContextSignature(task_type=record.context_features.get("task_type", ""))
|
|
307
299
|
self._context_index[sig.task_type].append(i)
|
|
308
300
|
|
|
309
301
|
logger.info(
|
|
@@ -376,14 +368,22 @@ class LearningStore:
|
|
|
376
368
|
"""
|
|
377
369
|
return self._stats.get(pattern)
|
|
378
370
|
|
|
371
|
+
def iter_all_stats(self) -> Iterator[PatternStats]:
|
|
372
|
+
"""Iterate over all pattern statistics (memory-efficient).
|
|
373
|
+
|
|
374
|
+
Yields patterns in arbitrary order. For sorted results,
|
|
375
|
+
use get_all_stats().
|
|
376
|
+
"""
|
|
377
|
+
yield from self._stats.values()
|
|
378
|
+
|
|
379
379
|
def get_all_stats(self) -> list[PatternStats]:
|
|
380
|
-
"""Get all pattern statistics.
|
|
380
|
+
"""Get all pattern statistics sorted by success rate.
|
|
381
381
|
|
|
382
|
-
|
|
383
|
-
|
|
382
|
+
Note: For large pattern sets, prefer iter_all_stats() when
|
|
383
|
+
you don't need sorted results.
|
|
384
384
|
"""
|
|
385
385
|
return sorted(
|
|
386
|
-
self.
|
|
386
|
+
self.iter_all_stats(),
|
|
387
387
|
key=lambda s: s.success_rate,
|
|
388
388
|
reverse=True,
|
|
389
389
|
)
|
|
@@ -457,9 +457,7 @@ class PatternRecommender:
|
|
|
457
457
|
"""
|
|
458
458
|
self.store = store
|
|
459
459
|
|
|
460
|
-
def recommend(
|
|
461
|
-
self, context: dict[str, Any], top_k: int = 3
|
|
462
|
-
) -> list[PatternRecommendation]:
|
|
460
|
+
def recommend(self, context: dict[str, Any], top_k: int = 3) -> list[PatternRecommendation]:
|
|
463
461
|
"""Recommend patterns for a context.
|
|
464
462
|
|
|
465
463
|
Uses hybrid approach:
|
|
@@ -516,9 +514,7 @@ class PatternRecommender:
|
|
|
516
514
|
recommendations = []
|
|
517
515
|
for pattern, scores in pattern_scores.items():
|
|
518
516
|
if scores["total_similarity"] > 0:
|
|
519
|
-
weighted_success =
|
|
520
|
-
scores["success_similarity"] / scores["total_similarity"]
|
|
521
|
-
)
|
|
517
|
+
weighted_success = scores["success_similarity"] / scores["total_similarity"]
|
|
522
518
|
stats = self.store.get_stats(pattern)
|
|
523
519
|
|
|
524
520
|
recommendations.append(
|
|
@@ -626,9 +622,7 @@ class PatternLearner:
|
|
|
626
622
|
self.store.add_record(record)
|
|
627
623
|
logger.debug(f"Recorded {pattern} execution: success={success}")
|
|
628
624
|
|
|
629
|
-
def recommend(
|
|
630
|
-
self, context: dict[str, Any], top_k: int = 3
|
|
631
|
-
) -> list[PatternRecommendation]:
|
|
625
|
+
def recommend(self, context: dict[str, Any], top_k: int = 3) -> list[PatternRecommendation]:
|
|
632
626
|
"""Get pattern recommendations for a context.
|
|
633
627
|
|
|
634
628
|
Args:
|
|
@@ -98,9 +98,7 @@ class RealCoverageAnalyzer:
|
|
|
98
98
|
file_age = time.time() - coverage_file.stat().st_mtime
|
|
99
99
|
# Use existing file if less than 1 hour old
|
|
100
100
|
if file_age < 3600:
|
|
101
|
-
logger.info(
|
|
102
|
-
f"Using existing coverage data (age: {file_age/60:.1f} minutes)"
|
|
103
|
-
)
|
|
101
|
+
logger.info(f"Using existing coverage data (age: {file_age / 60:.1f} minutes)")
|
|
104
102
|
else:
|
|
105
103
|
logger.info("Existing coverage data is stale, regenerating")
|
|
106
104
|
use_existing = False
|
|
@@ -230,6 +228,7 @@ class RealTestGenerator:
|
|
|
230
228
|
# Try to load .env file
|
|
231
229
|
try:
|
|
232
230
|
from dotenv import load_dotenv
|
|
231
|
+
|
|
233
232
|
load_dotenv()
|
|
234
233
|
except ImportError:
|
|
235
234
|
pass # python-dotenv not required
|
|
@@ -254,9 +253,7 @@ class RealTestGenerator:
|
|
|
254
253
|
logger.warning(f"Failed to initialize LLM: {e}. Falling back to templates")
|
|
255
254
|
self.use_llm = False
|
|
256
255
|
|
|
257
|
-
def generate_tests_for_file(
|
|
258
|
-
self, source_file: str, missing_lines: list[int]
|
|
259
|
-
) -> Path:
|
|
256
|
+
def generate_tests_for_file(self, source_file: str, missing_lines: list[int]) -> Path:
|
|
260
257
|
"""Generate tests for uncovered code in a file.
|
|
261
258
|
|
|
262
259
|
Args:
|
|
@@ -294,9 +291,7 @@ class RealTestGenerator:
|
|
|
294
291
|
if self.use_llm and self._llm:
|
|
295
292
|
test_code = self._generate_llm_tests(source_file, source_code, missing_lines)
|
|
296
293
|
else:
|
|
297
|
-
test_code = self._generate_basic_test_template(
|
|
298
|
-
source_file, source_code, missing_lines
|
|
299
|
-
)
|
|
294
|
+
test_code = self._generate_basic_test_template(source_file, source_code, missing_lines)
|
|
300
295
|
|
|
301
296
|
# Write test file
|
|
302
297
|
validated_path = _validate_file_path(str(test_path))
|
|
@@ -423,9 +418,7 @@ Return ONLY the Python test code, starting with imports. No markdown, no explana
|
|
|
423
418
|
|
|
424
419
|
except Exception as e:
|
|
425
420
|
logger.error(f"LLM test generation failed: {e}, falling back to template")
|
|
426
|
-
return self._generate_basic_test_template(
|
|
427
|
-
source_file, source_code, missing_lines
|
|
428
|
-
)
|
|
421
|
+
return self._generate_basic_test_template(source_file, source_code, missing_lines)
|
|
429
422
|
|
|
430
423
|
def _extract_api_docs(self, source_code: str) -> str:
|
|
431
424
|
"""Extract API signatures from source code using AST.
|
|
@@ -808,9 +801,7 @@ class RealCodeQualityAnalyzer:
|
|
|
808
801
|
)
|
|
809
802
|
|
|
810
803
|
# Count error lines
|
|
811
|
-
error_count = sum(
|
|
812
|
-
1 for line in result.stdout.split("\n") if ": error:" in line
|
|
813
|
-
)
|
|
804
|
+
error_count = sum(1 for line in result.stdout.split("\n") if ": error:" in line)
|
|
814
805
|
return error_count
|
|
815
806
|
|
|
816
807
|
except FileNotFoundError:
|
empathy_os/platform_utils.py
CHANGED
|
@@ -200,7 +200,8 @@ def write_text_file(path: str | Path, content: str, encoding: str = "utf-8") ->
|
|
|
200
200
|
|
|
201
201
|
"""
|
|
202
202
|
validated_path = _validate_file_path(str(path))
|
|
203
|
-
|
|
203
|
+
result: int = validated_path.write_text(content, encoding=encoding)
|
|
204
|
+
return result
|
|
204
205
|
|
|
205
206
|
|
|
206
207
|
def normalize_path(path: str | Path) -> Path:
|
empathy_os/plugins/__init__.py
CHANGED
|
@@ -8,7 +8,7 @@ Licensed under Fair Source 0.9
|
|
|
8
8
|
|
|
9
9
|
from .base import (
|
|
10
10
|
BasePlugin,
|
|
11
|
-
|
|
11
|
+
BaseWorkflow,
|
|
12
12
|
PluginError,
|
|
13
13
|
PluginLoadError,
|
|
14
14
|
PluginMetadata,
|
|
@@ -18,7 +18,7 @@ from .registry import PluginRegistry, get_global_registry
|
|
|
18
18
|
|
|
19
19
|
__all__ = [
|
|
20
20
|
"BasePlugin",
|
|
21
|
-
"
|
|
21
|
+
"BaseWorkflow",
|
|
22
22
|
"PluginError",
|
|
23
23
|
"PluginLoadError",
|
|
24
24
|
"PluginMetadata",
|
empathy_os/plugins/base.py
CHANGED
|
@@ -30,25 +30,25 @@ class PluginMetadata:
|
|
|
30
30
|
dependencies: list[str] | None = None # Additional package dependencies
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
class
|
|
34
|
-
"""Universal base class for all
|
|
33
|
+
class BaseWorkflow(ABC):
|
|
34
|
+
"""Universal base class for all workflows across all domains.
|
|
35
35
|
|
|
36
|
-
This replaces domain-specific base classes (
|
|
36
|
+
This replaces domain-specific base classes (BaseCoachWorkflow, etc.)
|
|
37
37
|
to provide a unified interface.
|
|
38
38
|
|
|
39
39
|
Design Philosophy:
|
|
40
40
|
- Domain-agnostic: Works for software, healthcare, finance, etc.
|
|
41
|
-
- Level-aware: Each
|
|
42
|
-
- Pattern-contributing:
|
|
41
|
+
- Level-aware: Each workflow declares its empathy level
|
|
42
|
+
- Pattern-contributing: Workflows share learnings via pattern library
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
45
|
def __init__(self, name: str, domain: str, empathy_level: int, category: str | None = None):
|
|
46
|
-
"""Initialize a
|
|
46
|
+
"""Initialize a workflow
|
|
47
47
|
|
|
48
48
|
Args:
|
|
49
|
-
name: Human-readable
|
|
50
|
-
domain: Domain this
|
|
51
|
-
empathy_level: Which empathy level this
|
|
49
|
+
name: Human-readable workflow name
|
|
50
|
+
domain: Domain this workflow belongs to (e.g., 'software', 'healthcare')
|
|
51
|
+
empathy_level: Which empathy level this workflow operates at (1-5)
|
|
52
52
|
category: Optional category within domain
|
|
53
53
|
|
|
54
54
|
"""
|
|
@@ -56,16 +56,16 @@ class BaseWizard(ABC):
|
|
|
56
56
|
self.domain = domain
|
|
57
57
|
self.empathy_level = empathy_level
|
|
58
58
|
self.category = category
|
|
59
|
-
self.logger = logging.getLogger(f"
|
|
59
|
+
self.logger = logging.getLogger(f"workflow.{domain}.{name}")
|
|
60
60
|
|
|
61
61
|
@abstractmethod
|
|
62
62
|
async def analyze(self, context: dict[str, Any]) -> dict[str, Any]:
|
|
63
63
|
"""Analyze the given context and return results.
|
|
64
64
|
|
|
65
|
-
This is the main entry point for all
|
|
65
|
+
This is the main entry point for all workflows. The context structure
|
|
66
66
|
is domain-specific but the return format should follow a standard pattern.
|
|
67
67
|
Subclasses must implement domain-specific analysis logic that aligns with
|
|
68
|
-
the
|
|
68
|
+
the workflow's empathy level.
|
|
69
69
|
|
|
70
70
|
Args:
|
|
71
71
|
context: dict[str, Any]
|
|
@@ -83,7 +83,7 @@ class BaseWizard(ABC):
|
|
|
83
83
|
- 'recommendations': list[dict] - Actionable next steps
|
|
84
84
|
- 'patterns': list[str] - Patterns detected for the pattern library
|
|
85
85
|
- 'confidence': float - Confidence score between 0.0 and 1.0
|
|
86
|
-
- '
|
|
86
|
+
- 'workflow': str - Name of the workflow that performed analysis
|
|
87
87
|
- 'empathy_level': int - Empathy level of this analysis (1-5)
|
|
88
88
|
- 'timestamp': str - ISO format timestamp of analysis
|
|
89
89
|
|
|
@@ -103,9 +103,9 @@ class BaseWizard(ABC):
|
|
|
103
103
|
|
|
104
104
|
@abstractmethod
|
|
105
105
|
def get_required_context(self) -> list[str]:
|
|
106
|
-
"""Declare what context fields this
|
|
106
|
+
"""Declare what context fields this workflow needs.
|
|
107
107
|
|
|
108
|
-
This method defines the contract between the caller and the
|
|
108
|
+
This method defines the contract between the caller and the workflow.
|
|
109
109
|
The caller must provide all declared fields before calling analyze().
|
|
110
110
|
This enables validation via validate_context() and helps with introspection.
|
|
111
111
|
|
|
@@ -116,9 +116,9 @@ class BaseWizard(ABC):
|
|
|
116
116
|
passed to analyze().
|
|
117
117
|
|
|
118
118
|
Examples:
|
|
119
|
-
Software
|
|
120
|
-
Healthcare
|
|
121
|
-
Finance
|
|
119
|
+
Software workflow returns: ['code', 'file_path', 'language']
|
|
120
|
+
Healthcare workflow returns: ['patient_id', 'vitals', 'medications']
|
|
121
|
+
Finance workflow returns: ['transactions', 'account_id', 'period']
|
|
122
122
|
|
|
123
123
|
Note:
|
|
124
124
|
- Must return at least one field (even if minimal)
|
|
@@ -143,12 +143,12 @@ class BaseWizard(ABC):
|
|
|
143
143
|
missing = [key for key in required if key not in context]
|
|
144
144
|
|
|
145
145
|
if missing:
|
|
146
|
-
raise ValueError(f"
|
|
146
|
+
raise ValueError(f"Workflow '{self.name}' missing required context: {missing}")
|
|
147
147
|
|
|
148
148
|
return True
|
|
149
149
|
|
|
150
150
|
def get_empathy_level(self) -> int:
|
|
151
|
-
"""Get the empathy level this
|
|
151
|
+
"""Get the empathy level this workflow operates at"""
|
|
152
152
|
return self.empathy_level
|
|
153
153
|
|
|
154
154
|
def contribute_patterns(self, analysis_result: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -165,7 +165,7 @@ class BaseWizard(ABC):
|
|
|
165
165
|
"""
|
|
166
166
|
# Default implementation - override for custom pattern extraction
|
|
167
167
|
return {
|
|
168
|
-
"
|
|
168
|
+
"workflow": self.name,
|
|
169
169
|
"domain": self.domain,
|
|
170
170
|
"timestamp": datetime.now().isoformat(),
|
|
171
171
|
"patterns": analysis_result.get("patterns", []),
|
|
@@ -175,18 +175,18 @@ class BaseWizard(ABC):
|
|
|
175
175
|
class BasePlugin(ABC):
|
|
176
176
|
"""Base class for domain plugins.
|
|
177
177
|
|
|
178
|
-
A plugin is a collection of
|
|
178
|
+
A plugin is a collection of workflows and patterns for a specific domain.
|
|
179
179
|
|
|
180
180
|
Example:
|
|
181
|
-
- SoftwarePlugin: 16+ coach
|
|
182
|
-
- HealthcarePlugin: Clinical and compliance
|
|
183
|
-
- FinancePlugin: Fraud detection, compliance
|
|
181
|
+
- SoftwarePlugin: 16+ coach workflows for code analysis
|
|
182
|
+
- HealthcarePlugin: Clinical and compliance workflows
|
|
183
|
+
- FinancePlugin: Fraud detection, compliance workflows
|
|
184
184
|
|
|
185
185
|
"""
|
|
186
186
|
|
|
187
187
|
def __init__(self):
|
|
188
188
|
self.logger = logging.getLogger(f"plugin.{self.get_metadata().domain}")
|
|
189
|
-
self.
|
|
189
|
+
self._workflows: dict[str, type[BaseWorkflow]] = {}
|
|
190
190
|
self._initialized = False
|
|
191
191
|
|
|
192
192
|
@abstractmethod
|
|
@@ -220,44 +220,44 @@ class BasePlugin(ABC):
|
|
|
220
220
|
"""
|
|
221
221
|
|
|
222
222
|
@abstractmethod
|
|
223
|
-
def
|
|
224
|
-
"""Register all
|
|
223
|
+
def register_workflows(self) -> dict[str, type[BaseWorkflow]]:
|
|
224
|
+
"""Register all workflows provided by this plugin.
|
|
225
225
|
|
|
226
|
-
This method defines all analysis
|
|
227
|
-
|
|
226
|
+
This method defines all analysis workflows available in this plugin.
|
|
227
|
+
Workflows are lazy-instantiated by get_workflow() when first requested.
|
|
228
228
|
This method is called during plugin initialization.
|
|
229
229
|
|
|
230
230
|
Returns:
|
|
231
|
-
dict[str, type[
|
|
232
|
-
Dictionary mapping
|
|
231
|
+
dict[str, type[BaseWorkflow]]
|
|
232
|
+
Dictionary mapping workflow identifiers to Workflow classes (not instances).
|
|
233
233
|
Keys should be lowercase, snake_case identifiers. Values should be
|
|
234
234
|
uninstantiated class references.
|
|
235
235
|
|
|
236
236
|
Returns:
|
|
237
|
-
dict[str, type[
|
|
237
|
+
dict[str, type[BaseWorkflow]]
|
|
238
238
|
Mapping structure:
|
|
239
239
|
{
|
|
240
|
-
'
|
|
241
|
-
'
|
|
240
|
+
'workflow_id': WorkflowClass,
|
|
241
|
+
'another_workflow': AnotherWorkflowClass,
|
|
242
242
|
...
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
Example:
|
|
246
246
|
Software plugin might return:
|
|
247
247
|
{
|
|
248
|
-
'security':
|
|
249
|
-
'performance':
|
|
250
|
-
'maintainability':
|
|
251
|
-
'accessibility':
|
|
248
|
+
'security': SecurityWorkflow,
|
|
249
|
+
'performance': PerformanceWorkflow,
|
|
250
|
+
'maintainability': MaintainabilityWorkflow,
|
|
251
|
+
'accessibility': AccessibilityWorkflow,
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
Note:
|
|
255
255
|
- Return only the class, not instances (instantiation is lazy)
|
|
256
|
-
- Use consistent, descriptive
|
|
257
|
-
- All returned classes must be subclasses of
|
|
258
|
-
- Can return empty dict {} if plugin provides no
|
|
256
|
+
- Use consistent, descriptive workflow IDs
|
|
257
|
+
- All returned classes must be subclasses of BaseWorkflow
|
|
258
|
+
- Can return empty dict {} if plugin provides no workflows initially
|
|
259
259
|
- Called once during initialization via initialize()
|
|
260
|
-
- Framework caches results in self.
|
|
260
|
+
- Framework caches results in self._workflows
|
|
261
261
|
|
|
262
262
|
"""
|
|
263
263
|
|
|
@@ -284,63 +284,63 @@ class BasePlugin(ABC):
|
|
|
284
284
|
|
|
285
285
|
self.logger.info(f"Initializing plugin: {self.get_metadata().name}")
|
|
286
286
|
|
|
287
|
-
# Register
|
|
288
|
-
self.
|
|
287
|
+
# Register workflows
|
|
288
|
+
self._workflows = self.register_workflows()
|
|
289
289
|
|
|
290
290
|
self.logger.info(
|
|
291
|
-
f"Plugin '{self.get_metadata().name}' initialized with {len(self.
|
|
291
|
+
f"Plugin '{self.get_metadata().name}' initialized with {len(self._workflows)} workflows",
|
|
292
292
|
)
|
|
293
293
|
|
|
294
294
|
self._initialized = True
|
|
295
295
|
|
|
296
|
-
def
|
|
297
|
-
"""Get a
|
|
296
|
+
def get_workflow(self, workflow_id: str) -> type[BaseWorkflow] | None:
|
|
297
|
+
"""Get a workflow by ID.
|
|
298
298
|
|
|
299
299
|
Args:
|
|
300
|
-
|
|
300
|
+
workflow_id: Workflow identifier
|
|
301
301
|
|
|
302
302
|
Returns:
|
|
303
|
-
|
|
303
|
+
Workflow class or None if not found
|
|
304
304
|
|
|
305
305
|
"""
|
|
306
306
|
if not self._initialized:
|
|
307
307
|
self.initialize()
|
|
308
308
|
|
|
309
|
-
return self.
|
|
309
|
+
return self._workflows.get(workflow_id)
|
|
310
310
|
|
|
311
|
-
def
|
|
312
|
-
"""List all
|
|
311
|
+
def list_workflows(self) -> list[str]:
|
|
312
|
+
"""List all workflow IDs provided by this plugin.
|
|
313
313
|
|
|
314
314
|
Returns:
|
|
315
|
-
List of
|
|
315
|
+
List of workflow identifiers
|
|
316
316
|
|
|
317
317
|
"""
|
|
318
318
|
if not self._initialized:
|
|
319
319
|
self.initialize()
|
|
320
320
|
|
|
321
|
-
return list(self.
|
|
321
|
+
return list(self._workflows.keys())
|
|
322
322
|
|
|
323
|
-
def
|
|
324
|
-
"""Get information about a
|
|
323
|
+
def get_workflow_info(self, workflow_id: str) -> dict[str, Any] | None:
|
|
324
|
+
"""Get information about a workflow without instantiating it.
|
|
325
325
|
|
|
326
326
|
Args:
|
|
327
|
-
|
|
327
|
+
workflow_id: Workflow identifier
|
|
328
328
|
|
|
329
329
|
Returns:
|
|
330
|
-
Dictionary with
|
|
330
|
+
Dictionary with workflow metadata
|
|
331
331
|
|
|
332
332
|
"""
|
|
333
|
-
|
|
334
|
-
if not
|
|
333
|
+
workflow_class = self.get_workflow(workflow_id)
|
|
334
|
+
if not workflow_class:
|
|
335
335
|
return None
|
|
336
336
|
|
|
337
337
|
# Create temporary instance to get metadata
|
|
338
|
-
# (
|
|
338
|
+
# (workflows should be lightweight to construct)
|
|
339
339
|
# Subclasses provide their own defaults for name, domain, empathy_level
|
|
340
|
-
temp_instance =
|
|
340
|
+
temp_instance = workflow_class() # type: ignore[call-arg]
|
|
341
341
|
|
|
342
342
|
return {
|
|
343
|
-
"id":
|
|
343
|
+
"id": workflow_id,
|
|
344
344
|
"name": temp_instance.name,
|
|
345
345
|
"domain": temp_instance.domain,
|
|
346
346
|
"empathy_level": temp_instance.empathy_level,
|