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
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"""Report generation and result storage for progressive workflows.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for:
|
|
4
|
+
1. Generating human-readable progression reports
|
|
5
|
+
2. Saving detailed results to disk
|
|
6
|
+
3. Formatting cost analysis
|
|
7
|
+
4. Creating progression visualizations
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from empathy_os.workflows.progressive.core import (
|
|
16
|
+
ProgressiveWorkflowResult,
|
|
17
|
+
Tier,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def generate_progression_report(result: ProgressiveWorkflowResult) -> str:
|
|
24
|
+
"""Generate human-readable progression report.
|
|
25
|
+
|
|
26
|
+
Creates a detailed ASCII report showing:
|
|
27
|
+
- Tier-by-tier breakdown
|
|
28
|
+
- Quality scores and success rates
|
|
29
|
+
- Cost analysis and savings
|
|
30
|
+
- Escalation decisions
|
|
31
|
+
- Final results summary
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
result: Progressive workflow result
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Formatted report string
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
>>> print(generate_progression_report(result))
|
|
41
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
42
|
+
🎯 PROGRESSIVE ESCALATION REPORT
|
|
43
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
44
|
+
...
|
|
45
|
+
"""
|
|
46
|
+
report = []
|
|
47
|
+
|
|
48
|
+
# Header
|
|
49
|
+
report.append("━" * 60)
|
|
50
|
+
report.append("🎯 PROGRESSIVE ESCALATION REPORT")
|
|
51
|
+
report.append("━" * 60)
|
|
52
|
+
report.append("")
|
|
53
|
+
|
|
54
|
+
# Summary
|
|
55
|
+
report.append(f"Workflow: {result.workflow_name}")
|
|
56
|
+
report.append(f"Task ID: {result.task_id}")
|
|
57
|
+
report.append(f"Duration: {_format_duration(result.total_duration)}")
|
|
58
|
+
report.append(f"Total Cost: ${result.total_cost:.2f}")
|
|
59
|
+
report.append("")
|
|
60
|
+
|
|
61
|
+
# Cost savings
|
|
62
|
+
if result.cost_savings > 0:
|
|
63
|
+
report.append(f"Cost Savings: ${result.cost_savings:.2f} ({result.cost_savings_percent:.0f}% vs all-Premium)")
|
|
64
|
+
report.append("")
|
|
65
|
+
|
|
66
|
+
report.append("TIER BREAKDOWN:")
|
|
67
|
+
report.append("")
|
|
68
|
+
|
|
69
|
+
# Tier-by-tier breakdown
|
|
70
|
+
for tier_result in result.tier_results:
|
|
71
|
+
tier_emoji = {
|
|
72
|
+
Tier.CHEAP: "💰",
|
|
73
|
+
Tier.CAPABLE: "📊",
|
|
74
|
+
Tier.PREMIUM: "💎"
|
|
75
|
+
}[tier_result.tier]
|
|
76
|
+
|
|
77
|
+
report.append(f"{tier_emoji} {tier_result.tier.value.upper()} Tier ({tier_result.model})")
|
|
78
|
+
report.append(f" • Items: {len(tier_result.generated_items)}")
|
|
79
|
+
report.append(f" • Attempts: {tier_result.attempt}")
|
|
80
|
+
|
|
81
|
+
success_count = tier_result.success_count
|
|
82
|
+
total_items = len(tier_result.generated_items)
|
|
83
|
+
success_rate = tier_result.success_rate * 100
|
|
84
|
+
|
|
85
|
+
report.append(f" • Success: {success_count}/{total_items} ({success_rate:.0f}%)")
|
|
86
|
+
report.append(f" • Quality: CQS={tier_result.quality_score:.1f}")
|
|
87
|
+
report.append(f" • Cost: ${tier_result.cost:.2f}")
|
|
88
|
+
report.append(f" • Duration: {_format_duration(tier_result.duration)}")
|
|
89
|
+
|
|
90
|
+
if tier_result.escalated:
|
|
91
|
+
report.append(f" • Escalated: {tier_result.escalation_reason}")
|
|
92
|
+
|
|
93
|
+
report.append("")
|
|
94
|
+
|
|
95
|
+
report.append("━" * 60)
|
|
96
|
+
report.append("")
|
|
97
|
+
report.append("FINAL RESULTS:")
|
|
98
|
+
|
|
99
|
+
total_items = sum(len(r.generated_items) for r in result.tier_results)
|
|
100
|
+
total_successful = sum(r.success_count for r in result.tier_results)
|
|
101
|
+
|
|
102
|
+
status_icon = "✅" if result.success else "❌"
|
|
103
|
+
status_text = "Success" if result.success else "Incomplete"
|
|
104
|
+
|
|
105
|
+
report.append(f"{status_icon} {total_successful}/{total_items} items completed")
|
|
106
|
+
report.append(f"{status_icon} Overall CQS: {result.final_result.quality_score:.0f}")
|
|
107
|
+
report.append(f"{status_icon} Status: {status_text}")
|
|
108
|
+
report.append("")
|
|
109
|
+
|
|
110
|
+
report.append("━" * 60)
|
|
111
|
+
report.append("")
|
|
112
|
+
report.append("Detailed results saved to:")
|
|
113
|
+
report.append(f".empathy/progressive_runs/{result.task_id}/")
|
|
114
|
+
report.append("")
|
|
115
|
+
|
|
116
|
+
return "\n".join(report)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def save_results_to_disk(result: ProgressiveWorkflowResult, storage_path: str) -> None:
|
|
120
|
+
"""Save detailed results to disk.
|
|
121
|
+
|
|
122
|
+
Creates a directory structure:
|
|
123
|
+
<storage_path>/<task_id>/
|
|
124
|
+
├── summary.json
|
|
125
|
+
├── tier_0_cheap.json
|
|
126
|
+
├── tier_1_capable.json
|
|
127
|
+
├── tier_2_premium.json (if used)
|
|
128
|
+
└── report.txt
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
result: Progressive workflow result
|
|
132
|
+
storage_path: Base directory for storage
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
>>> save_results_to_disk(result, ".empathy/progressive_runs")
|
|
136
|
+
# Creates .empathy/progressive_runs/test-gen-20260117-143022/...
|
|
137
|
+
"""
|
|
138
|
+
task_dir = Path(storage_path) / result.task_id
|
|
139
|
+
task_dir.mkdir(parents=True, exist_ok=True)
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
# Use the created task directory
|
|
143
|
+
validated_dir = task_dir
|
|
144
|
+
|
|
145
|
+
# Save summary
|
|
146
|
+
summary = {
|
|
147
|
+
"workflow": result.workflow_name,
|
|
148
|
+
"task_id": result.task_id,
|
|
149
|
+
"timestamp": result.tier_results[0].timestamp.isoformat() if result.tier_results else None,
|
|
150
|
+
"total_cost": result.total_cost,
|
|
151
|
+
"total_duration": result.total_duration,
|
|
152
|
+
"cost_savings": result.cost_savings,
|
|
153
|
+
"cost_savings_percent": result.cost_savings_percent,
|
|
154
|
+
"success": result.success,
|
|
155
|
+
"tier_count": len(result.tier_results),
|
|
156
|
+
"final_cqs": result.final_result.quality_score if result.final_result else 0
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
summary_file = validated_dir / "summary.json"
|
|
160
|
+
summary_file.write_text(json.dumps(summary, indent=2))
|
|
161
|
+
|
|
162
|
+
# Save each tier result
|
|
163
|
+
for i, tier_result in enumerate(result.tier_results):
|
|
164
|
+
tier_data = {
|
|
165
|
+
"tier": tier_result.tier.value,
|
|
166
|
+
"model": tier_result.model,
|
|
167
|
+
"attempt": tier_result.attempt,
|
|
168
|
+
"timestamp": tier_result.timestamp.isoformat(),
|
|
169
|
+
"quality_score": tier_result.quality_score,
|
|
170
|
+
"success_count": tier_result.success_count,
|
|
171
|
+
"success_rate": tier_result.success_rate,
|
|
172
|
+
"cost": tier_result.cost,
|
|
173
|
+
"duration": tier_result.duration,
|
|
174
|
+
"escalated": tier_result.escalated,
|
|
175
|
+
"escalation_reason": tier_result.escalation_reason,
|
|
176
|
+
"failure_analysis": {
|
|
177
|
+
"syntax_errors": len(tier_result.failure_analysis.syntax_errors),
|
|
178
|
+
"test_pass_rate": tier_result.failure_analysis.test_pass_rate,
|
|
179
|
+
"coverage": tier_result.failure_analysis.coverage_percent,
|
|
180
|
+
"assertion_depth": tier_result.failure_analysis.assertion_depth,
|
|
181
|
+
"confidence": tier_result.failure_analysis.confidence_score
|
|
182
|
+
},
|
|
183
|
+
"item_count": len(tier_result.generated_items)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
tier_file = validated_dir / f"tier_{i}_{tier_result.tier.value}.json"
|
|
187
|
+
tier_file.write_text(json.dumps(tier_data, indent=2))
|
|
188
|
+
|
|
189
|
+
# Save human-readable report
|
|
190
|
+
report_file = validated_dir / "report.txt"
|
|
191
|
+
report_file.write_text(generate_progression_report(result))
|
|
192
|
+
|
|
193
|
+
logger.info(f"Saved progressive results to {validated_dir}")
|
|
194
|
+
|
|
195
|
+
except ValueError as e:
|
|
196
|
+
logger.error(f"Failed to save results: {e}")
|
|
197
|
+
raise
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _format_duration(seconds: float) -> str:
|
|
201
|
+
"""Format duration in human-readable form.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
seconds: Duration in seconds
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Formatted string (e.g., "1m 23s", "45s")
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
>>> _format_duration(83.5)
|
|
211
|
+
'1m 24s'
|
|
212
|
+
>>> _format_duration(12.3)
|
|
213
|
+
'12s'
|
|
214
|
+
"""
|
|
215
|
+
if seconds < 60:
|
|
216
|
+
return f"{int(seconds)}s"
|
|
217
|
+
|
|
218
|
+
minutes = int(seconds // 60)
|
|
219
|
+
remaining_seconds = int(seconds % 60)
|
|
220
|
+
|
|
221
|
+
return f"{minutes}m {remaining_seconds}s"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def load_result_from_disk(task_id: str, storage_path: str = ".empathy/progressive_runs") -> dict[str, Any]:
|
|
225
|
+
"""Load saved result from disk.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
task_id: Task ID to load
|
|
229
|
+
storage_path: Base storage directory
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Dictionary with summary and tier results
|
|
233
|
+
|
|
234
|
+
Raises:
|
|
235
|
+
FileNotFoundError: If task_id not found
|
|
236
|
+
|
|
237
|
+
Example:
|
|
238
|
+
>>> result = load_result_from_disk("test-gen-20260117-143022")
|
|
239
|
+
>>> print(result["summary"]["total_cost"])
|
|
240
|
+
0.95
|
|
241
|
+
"""
|
|
242
|
+
task_dir = Path(storage_path) / task_id
|
|
243
|
+
|
|
244
|
+
if not task_dir.exists():
|
|
245
|
+
raise FileNotFoundError(f"Task {task_id} not found in {storage_path}")
|
|
246
|
+
|
|
247
|
+
# Load summary
|
|
248
|
+
summary_file = task_dir / "summary.json"
|
|
249
|
+
if not summary_file.exists():
|
|
250
|
+
raise FileNotFoundError(f"Summary file not found for task {task_id}")
|
|
251
|
+
|
|
252
|
+
summary = json.loads(summary_file.read_text())
|
|
253
|
+
|
|
254
|
+
# Load tier results
|
|
255
|
+
tier_results = []
|
|
256
|
+
for tier_file in sorted(task_dir.glob("tier_*.json")):
|
|
257
|
+
tier_data = json.loads(tier_file.read_text())
|
|
258
|
+
tier_results.append(tier_data)
|
|
259
|
+
|
|
260
|
+
# Load report
|
|
261
|
+
report_file = task_dir / "report.txt"
|
|
262
|
+
report = report_file.read_text() if report_file.exists() else ""
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
"summary": summary,
|
|
266
|
+
"tier_results": tier_results,
|
|
267
|
+
"report": report
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def list_saved_results(storage_path: str = ".empathy/progressive_runs") -> list[dict[str, Any]]:
|
|
272
|
+
"""List all saved progressive results.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
storage_path: Base storage directory
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
List of result summaries sorted by timestamp (newest first)
|
|
279
|
+
|
|
280
|
+
Example:
|
|
281
|
+
>>> results = list_saved_results()
|
|
282
|
+
>>> for r in results:
|
|
283
|
+
... print(f"{r['task_id']}: ${r['total_cost']:.2f}")
|
|
284
|
+
"""
|
|
285
|
+
storage_dir = Path(storage_path)
|
|
286
|
+
|
|
287
|
+
if not storage_dir.exists():
|
|
288
|
+
return []
|
|
289
|
+
|
|
290
|
+
summaries = []
|
|
291
|
+
|
|
292
|
+
for task_dir in storage_dir.iterdir():
|
|
293
|
+
if not task_dir.is_dir():
|
|
294
|
+
continue
|
|
295
|
+
|
|
296
|
+
summary_file = task_dir / "summary.json"
|
|
297
|
+
if not summary_file.exists():
|
|
298
|
+
continue
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
summary = json.loads(summary_file.read_text())
|
|
302
|
+
summaries.append(summary)
|
|
303
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
304
|
+
logger.warning(f"Failed to load summary from {task_dir}: {e}")
|
|
305
|
+
|
|
306
|
+
# Sort by timestamp (newest first)
|
|
307
|
+
summaries.sort(key=lambda s: s.get("timestamp", ""), reverse=True)
|
|
308
|
+
|
|
309
|
+
return summaries
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def cleanup_old_results(
|
|
313
|
+
storage_path: str = ".empathy/progressive_runs",
|
|
314
|
+
retention_days: int = 30,
|
|
315
|
+
dry_run: bool = False
|
|
316
|
+
) -> tuple[int, int]:
|
|
317
|
+
"""Clean up old progressive workflow results.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
storage_path: Base storage directory
|
|
321
|
+
retention_days: Number of days to retain results (default: 30)
|
|
322
|
+
dry_run: If True, only report what would be deleted without deleting
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Tuple of (deleted_count, retained_count)
|
|
326
|
+
|
|
327
|
+
Example:
|
|
328
|
+
>>> deleted, retained = cleanup_old_results(retention_days=7)
|
|
329
|
+
>>> print(f"Deleted {deleted} old results, kept {retained}")
|
|
330
|
+
"""
|
|
331
|
+
from datetime import datetime, timedelta
|
|
332
|
+
|
|
333
|
+
storage_dir = Path(storage_path)
|
|
334
|
+
|
|
335
|
+
if not storage_dir.exists():
|
|
336
|
+
return (0, 0)
|
|
337
|
+
|
|
338
|
+
cutoff_date = datetime.now() - timedelta(days=retention_days)
|
|
339
|
+
deleted_count = 0
|
|
340
|
+
retained_count = 0
|
|
341
|
+
|
|
342
|
+
for task_dir in storage_dir.iterdir():
|
|
343
|
+
if not task_dir.is_dir():
|
|
344
|
+
continue
|
|
345
|
+
|
|
346
|
+
summary_file = task_dir / "summary.json"
|
|
347
|
+
if not summary_file.exists():
|
|
348
|
+
continue
|
|
349
|
+
|
|
350
|
+
try:
|
|
351
|
+
summary = json.loads(summary_file.read_text())
|
|
352
|
+
timestamp_str = summary.get("timestamp")
|
|
353
|
+
|
|
354
|
+
if not timestamp_str:
|
|
355
|
+
logger.warning(f"No timestamp in {task_dir}, skipping")
|
|
356
|
+
retained_count += 1
|
|
357
|
+
continue
|
|
358
|
+
|
|
359
|
+
timestamp = datetime.fromisoformat(timestamp_str)
|
|
360
|
+
|
|
361
|
+
if timestamp < cutoff_date:
|
|
362
|
+
# Old result, delete it
|
|
363
|
+
if not dry_run:
|
|
364
|
+
import shutil
|
|
365
|
+
shutil.rmtree(task_dir)
|
|
366
|
+
logger.info(f"Deleted old result: {task_dir.name}")
|
|
367
|
+
else:
|
|
368
|
+
logger.info(f"Would delete: {task_dir.name}")
|
|
369
|
+
deleted_count += 1
|
|
370
|
+
else:
|
|
371
|
+
retained_count += 1
|
|
372
|
+
|
|
373
|
+
except (json.JSONDecodeError, ValueError, OSError) as e:
|
|
374
|
+
logger.warning(f"Error processing {task_dir}: {e}")
|
|
375
|
+
retained_count += 1
|
|
376
|
+
|
|
377
|
+
return (deleted_count, retained_count)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def generate_cost_analytics(
|
|
381
|
+
storage_path: str = ".empathy/progressive_runs"
|
|
382
|
+
) -> dict[str, Any]:
|
|
383
|
+
"""Generate cost optimization analytics from saved results.
|
|
384
|
+
|
|
385
|
+
Analyzes historical progressive workflow runs to provide insights:
|
|
386
|
+
- Total cost savings
|
|
387
|
+
- Average escalation rate
|
|
388
|
+
- Most cost-effective workflow types
|
|
389
|
+
- Tier usage distribution
|
|
390
|
+
- Success rates by tier
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
storage_path: Base storage directory
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Dictionary with analytics data
|
|
397
|
+
|
|
398
|
+
Example:
|
|
399
|
+
>>> analytics = generate_cost_analytics()
|
|
400
|
+
>>> print(f"Total savings: ${analytics['total_savings']:.2f}")
|
|
401
|
+
>>> print(f"Avg escalation rate: {analytics['avg_escalation_rate']:.1%}")
|
|
402
|
+
"""
|
|
403
|
+
results = list_saved_results(storage_path)
|
|
404
|
+
|
|
405
|
+
if not results:
|
|
406
|
+
return {
|
|
407
|
+
"total_runs": 0,
|
|
408
|
+
"total_cost": 0.0,
|
|
409
|
+
"total_savings": 0.0,
|
|
410
|
+
"avg_savings_percent": 0.0
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
total_runs = len(results)
|
|
414
|
+
total_cost = sum(r.get("total_cost", 0) for r in results)
|
|
415
|
+
total_savings = sum(r.get("cost_savings", 0) for r in results)
|
|
416
|
+
|
|
417
|
+
# Calculate average savings percent (weighted by cost)
|
|
418
|
+
weighted_savings = sum(
|
|
419
|
+
r.get("cost_savings_percent", 0) * r.get("total_cost", 0)
|
|
420
|
+
for r in results
|
|
421
|
+
)
|
|
422
|
+
avg_savings_percent = weighted_savings / total_cost if total_cost > 0 else 0
|
|
423
|
+
|
|
424
|
+
# Tier usage statistics
|
|
425
|
+
tier_usage = {"cheap": 0, "capable": 0, "premium": 0}
|
|
426
|
+
tier_costs = {"cheap": 0.0, "capable": 0.0, "premium": 0.0}
|
|
427
|
+
escalation_count = 0
|
|
428
|
+
|
|
429
|
+
for result in results:
|
|
430
|
+
tier_count = result.get("tier_count", 0)
|
|
431
|
+
if tier_count > 1:
|
|
432
|
+
escalation_count += 1
|
|
433
|
+
|
|
434
|
+
escalation_rate = escalation_count / total_runs if total_runs > 0 else 0
|
|
435
|
+
|
|
436
|
+
# Success rate
|
|
437
|
+
successful_runs = sum(1 for r in results if r.get("success", False))
|
|
438
|
+
success_rate = successful_runs / total_runs if total_runs > 0 else 0
|
|
439
|
+
|
|
440
|
+
# Average final CQS
|
|
441
|
+
avg_cqs = sum(r.get("final_cqs", 0) for r in results) / total_runs if total_runs > 0 else 0
|
|
442
|
+
|
|
443
|
+
# Per-workflow analytics
|
|
444
|
+
workflow_stats: dict[str, dict[str, Any]] = {}
|
|
445
|
+
for result in results:
|
|
446
|
+
workflow = result.get("workflow", "unknown")
|
|
447
|
+
if workflow not in workflow_stats:
|
|
448
|
+
workflow_stats[workflow] = {
|
|
449
|
+
"runs": 0,
|
|
450
|
+
"total_cost": 0.0,
|
|
451
|
+
"total_savings": 0.0,
|
|
452
|
+
"successes": 0
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
stats = workflow_stats[workflow]
|
|
456
|
+
stats["runs"] += 1
|
|
457
|
+
stats["total_cost"] += result.get("total_cost", 0)
|
|
458
|
+
stats["total_savings"] += result.get("cost_savings", 0)
|
|
459
|
+
if result.get("success", False):
|
|
460
|
+
stats["successes"] += 1
|
|
461
|
+
|
|
462
|
+
# Calculate per-workflow averages
|
|
463
|
+
for stats in workflow_stats.values():
|
|
464
|
+
stats["avg_cost"] = stats["total_cost"] / stats["runs"]
|
|
465
|
+
stats["avg_savings"] = stats["total_savings"] / stats["runs"]
|
|
466
|
+
stats["success_rate"] = stats["successes"] / stats["runs"]
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
"total_runs": total_runs,
|
|
470
|
+
"total_cost": round(total_cost, 2),
|
|
471
|
+
"total_savings": round(total_savings, 2),
|
|
472
|
+
"avg_savings_percent": round(avg_savings_percent, 1),
|
|
473
|
+
"escalation_rate": round(escalation_rate, 2),
|
|
474
|
+
"success_rate": round(success_rate, 2),
|
|
475
|
+
"avg_final_cqs": round(avg_cqs, 1),
|
|
476
|
+
"tier_usage": tier_usage,
|
|
477
|
+
"tier_costs": tier_costs,
|
|
478
|
+
"workflow_stats": workflow_stats
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def format_cost_analytics_report(analytics: dict[str, Any]) -> str:
|
|
483
|
+
"""Format cost analytics as human-readable report.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
analytics: Analytics data from generate_cost_analytics()
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
Formatted report string
|
|
490
|
+
|
|
491
|
+
Example:
|
|
492
|
+
>>> analytics = generate_cost_analytics()
|
|
493
|
+
>>> print(format_cost_analytics_report(analytics))
|
|
494
|
+
"""
|
|
495
|
+
report = []
|
|
496
|
+
|
|
497
|
+
report.append("━" * 60)
|
|
498
|
+
report.append("📊 PROGRESSIVE ESCALATION ANALYTICS")
|
|
499
|
+
report.append("━" * 60)
|
|
500
|
+
report.append("")
|
|
501
|
+
|
|
502
|
+
# Overall statistics
|
|
503
|
+
report.append("OVERALL STATISTICS:")
|
|
504
|
+
report.append(f" Total Runs: {analytics['total_runs']}")
|
|
505
|
+
report.append(f" Total Cost: ${analytics['total_cost']:.2f}")
|
|
506
|
+
report.append(f" Total Savings: ${analytics['total_savings']:.2f}")
|
|
507
|
+
report.append(f" Avg Savings: {analytics['avg_savings_percent']:.1f}%")
|
|
508
|
+
report.append(f" Escalation Rate: {analytics['escalation_rate']:.1%}")
|
|
509
|
+
report.append(f" Success Rate: {analytics['success_rate']:.1%}")
|
|
510
|
+
report.append(f" Avg Final CQS: {analytics['avg_final_cqs']:.1f}")
|
|
511
|
+
report.append("")
|
|
512
|
+
|
|
513
|
+
# Per-workflow breakdown
|
|
514
|
+
if analytics.get("workflow_stats"):
|
|
515
|
+
report.append("PER-WORKFLOW BREAKDOWN:")
|
|
516
|
+
report.append("")
|
|
517
|
+
|
|
518
|
+
for workflow, stats in sorted(analytics["workflow_stats"].items()):
|
|
519
|
+
report.append(f" {workflow}:")
|
|
520
|
+
report.append(f" Runs: {stats['runs']}")
|
|
521
|
+
report.append(f" Avg Cost: ${stats['avg_cost']:.2f}")
|
|
522
|
+
report.append(f" Avg Savings: ${stats['avg_savings']:.2f}")
|
|
523
|
+
report.append(f" Success Rate: {stats['success_rate']:.1%}")
|
|
524
|
+
report.append("")
|
|
525
|
+
|
|
526
|
+
report.append("━" * 60)
|
|
527
|
+
|
|
528
|
+
return "\n".join(report)
|
|
@@ -13,10 +13,7 @@ from pathlib import Path
|
|
|
13
13
|
from typing import Any
|
|
14
14
|
|
|
15
15
|
from empathy_os.config import _validate_file_path
|
|
16
|
-
from empathy_os.workflows.progressive.core import
|
|
17
|
-
ProgressiveWorkflowResult,
|
|
18
|
-
Tier,
|
|
19
|
-
)
|
|
16
|
+
from empathy_os.workflows.progressive.core import ProgressiveWorkflowResult, Tier
|
|
20
17
|
|
|
21
18
|
logger = logging.getLogger(__name__)
|
|
22
19
|
|
|
@@ -61,7 +58,9 @@ def generate_progression_report(result: ProgressiveWorkflowResult) -> str:
|
|
|
61
58
|
|
|
62
59
|
# Cost savings
|
|
63
60
|
if result.cost_savings > 0:
|
|
64
|
-
report.append(
|
|
61
|
+
report.append(
|
|
62
|
+
f"Cost Savings: ${result.cost_savings:.2f} ({result.cost_savings_percent:.0f}% vs all-Premium)"
|
|
63
|
+
)
|
|
65
64
|
report.append("")
|
|
66
65
|
|
|
67
66
|
report.append("TIER BREAKDOWN:")
|
|
@@ -69,11 +68,7 @@ def generate_progression_report(result: ProgressiveWorkflowResult) -> str:
|
|
|
69
68
|
|
|
70
69
|
# Tier-by-tier breakdown
|
|
71
70
|
for tier_result in result.tier_results:
|
|
72
|
-
tier_emoji = {
|
|
73
|
-
Tier.CHEAP: "💰",
|
|
74
|
-
Tier.CAPABLE: "📊",
|
|
75
|
-
Tier.PREMIUM: "💎"
|
|
76
|
-
}[tier_result.tier]
|
|
71
|
+
tier_emoji = {Tier.CHEAP: "💰", Tier.CAPABLE: "📊", Tier.PREMIUM: "💎"}[tier_result.tier]
|
|
77
72
|
|
|
78
73
|
report.append(f"{tier_emoji} {tier_result.tier.value.upper()} Tier ({tier_result.model})")
|
|
79
74
|
report.append(f" • Items: {len(tier_result.generated_items)}")
|
|
@@ -147,14 +142,16 @@ def save_results_to_disk(result: ProgressiveWorkflowResult, storage_path: str) -
|
|
|
147
142
|
summary = {
|
|
148
143
|
"workflow": result.workflow_name,
|
|
149
144
|
"task_id": result.task_id,
|
|
150
|
-
"timestamp":
|
|
145
|
+
"timestamp": (
|
|
146
|
+
result.tier_results[0].timestamp.isoformat() if result.tier_results else None
|
|
147
|
+
),
|
|
151
148
|
"total_cost": result.total_cost,
|
|
152
149
|
"total_duration": result.total_duration,
|
|
153
150
|
"cost_savings": result.cost_savings,
|
|
154
151
|
"cost_savings_percent": result.cost_savings_percent,
|
|
155
152
|
"success": result.success,
|
|
156
153
|
"tier_count": len(result.tier_results),
|
|
157
|
-
"final_cqs": result.final_result.quality_score if result.final_result else 0
|
|
154
|
+
"final_cqs": result.final_result.quality_score if result.final_result else 0,
|
|
158
155
|
}
|
|
159
156
|
|
|
160
157
|
summary_file = validated_dir / "summary.json"
|
|
@@ -179,9 +176,9 @@ def save_results_to_disk(result: ProgressiveWorkflowResult, storage_path: str) -
|
|
|
179
176
|
"test_pass_rate": tier_result.failure_analysis.test_pass_rate,
|
|
180
177
|
"coverage": tier_result.failure_analysis.coverage_percent,
|
|
181
178
|
"assertion_depth": tier_result.failure_analysis.assertion_depth,
|
|
182
|
-
"confidence": tier_result.failure_analysis.confidence_score
|
|
179
|
+
"confidence": tier_result.failure_analysis.confidence_score,
|
|
183
180
|
},
|
|
184
|
-
"item_count": len(tier_result.generated_items)
|
|
181
|
+
"item_count": len(tier_result.generated_items),
|
|
185
182
|
}
|
|
186
183
|
|
|
187
184
|
tier_file = validated_dir / f"tier_{i}_{tier_result.tier.value}.json"
|
|
@@ -222,7 +219,9 @@ def _format_duration(seconds: float) -> str:
|
|
|
222
219
|
return f"{minutes}m {remaining_seconds}s"
|
|
223
220
|
|
|
224
221
|
|
|
225
|
-
def load_result_from_disk(
|
|
222
|
+
def load_result_from_disk(
|
|
223
|
+
task_id: str, storage_path: str = ".empathy/progressive_runs"
|
|
224
|
+
) -> dict[str, Any]:
|
|
226
225
|
"""Load saved result from disk.
|
|
227
226
|
|
|
228
227
|
Args:
|
|
@@ -262,11 +261,7 @@ def load_result_from_disk(task_id: str, storage_path: str = ".empathy/progressiv
|
|
|
262
261
|
report_file = task_dir / "report.txt"
|
|
263
262
|
report = report_file.read_text() if report_file.exists() else ""
|
|
264
263
|
|
|
265
|
-
return {
|
|
266
|
-
"summary": summary,
|
|
267
|
-
"tier_results": tier_results,
|
|
268
|
-
"report": report
|
|
269
|
-
}
|
|
264
|
+
return {"summary": summary, "tier_results": tier_results, "report": report}
|
|
270
265
|
|
|
271
266
|
|
|
272
267
|
def list_saved_results(storage_path: str = ".empathy/progressive_runs") -> list[dict[str, Any]]:
|
|
@@ -311,9 +306,7 @@ def list_saved_results(storage_path: str = ".empathy/progressive_runs") -> list[
|
|
|
311
306
|
|
|
312
307
|
|
|
313
308
|
def cleanup_old_results(
|
|
314
|
-
storage_path: str = ".empathy/progressive_runs",
|
|
315
|
-
retention_days: int = 30,
|
|
316
|
-
dry_run: bool = False
|
|
309
|
+
storage_path: str = ".empathy/progressive_runs", retention_days: int = 30, dry_run: bool = False
|
|
317
310
|
) -> tuple[int, int]:
|
|
318
311
|
"""Clean up old progressive workflow results.
|
|
319
312
|
|
|
@@ -363,6 +356,7 @@ def cleanup_old_results(
|
|
|
363
356
|
# Old result, delete it
|
|
364
357
|
if not dry_run:
|
|
365
358
|
import shutil
|
|
359
|
+
|
|
366
360
|
shutil.rmtree(task_dir)
|
|
367
361
|
logger.info(f"Deleted old result: {task_dir.name}")
|
|
368
362
|
else:
|
|
@@ -378,9 +372,7 @@ def cleanup_old_results(
|
|
|
378
372
|
return (deleted_count, retained_count)
|
|
379
373
|
|
|
380
374
|
|
|
381
|
-
def generate_cost_analytics(
|
|
382
|
-
storage_path: str = ".empathy/progressive_runs"
|
|
383
|
-
) -> dict[str, Any]:
|
|
375
|
+
def generate_cost_analytics(storage_path: str = ".empathy/progressive_runs") -> dict[str, Any]:
|
|
384
376
|
"""Generate cost optimization analytics from saved results.
|
|
385
377
|
|
|
386
378
|
Analyzes historical progressive workflow runs to provide insights:
|
|
@@ -408,7 +400,7 @@ def generate_cost_analytics(
|
|
|
408
400
|
"total_runs": 0,
|
|
409
401
|
"total_cost": 0.0,
|
|
410
402
|
"total_savings": 0.0,
|
|
411
|
-
"avg_savings_percent": 0.0
|
|
403
|
+
"avg_savings_percent": 0.0,
|
|
412
404
|
}
|
|
413
405
|
|
|
414
406
|
total_runs = len(results)
|
|
@@ -417,8 +409,7 @@ def generate_cost_analytics(
|
|
|
417
409
|
|
|
418
410
|
# Calculate average savings percent (weighted by cost)
|
|
419
411
|
weighted_savings = sum(
|
|
420
|
-
r.get("cost_savings_percent", 0) * r.get("total_cost", 0)
|
|
421
|
-
for r in results
|
|
412
|
+
r.get("cost_savings_percent", 0) * r.get("total_cost", 0) for r in results
|
|
422
413
|
)
|
|
423
414
|
avg_savings_percent = weighted_savings / total_cost if total_cost > 0 else 0
|
|
424
415
|
|
|
@@ -450,7 +441,7 @@ def generate_cost_analytics(
|
|
|
450
441
|
"runs": 0,
|
|
451
442
|
"total_cost": 0.0,
|
|
452
443
|
"total_savings": 0.0,
|
|
453
|
-
"successes": 0
|
|
444
|
+
"successes": 0,
|
|
454
445
|
}
|
|
455
446
|
|
|
456
447
|
stats = workflow_stats[workflow]
|
|
@@ -476,7 +467,7 @@ def generate_cost_analytics(
|
|
|
476
467
|
"avg_final_cqs": round(avg_cqs, 1),
|
|
477
468
|
"tier_usage": tier_usage,
|
|
478
469
|
"tier_costs": tier_costs,
|
|
479
|
-
"workflow_stats": workflow_stats
|
|
470
|
+
"workflow_stats": workflow_stats,
|
|
480
471
|
}
|
|
481
472
|
|
|
482
473
|
|