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.
Files changed (273) hide show
  1. empathy_framework-4.7.1.dist-info/METADATA +690 -0
  2. empathy_framework-4.7.1.dist-info/RECORD +379 -0
  3. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/top_level.txt +1 -2
  4. empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
  5. empathy_llm_toolkit/agent_factory/__init__.py +6 -6
  6. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
  7. empathy_llm_toolkit/agents_md/__init__.py +22 -0
  8. empathy_llm_toolkit/agents_md/loader.py +218 -0
  9. empathy_llm_toolkit/agents_md/parser.py +271 -0
  10. empathy_llm_toolkit/agents_md/registry.py +307 -0
  11. empathy_llm_toolkit/commands/__init__.py +51 -0
  12. empathy_llm_toolkit/commands/context.py +375 -0
  13. empathy_llm_toolkit/commands/loader.py +301 -0
  14. empathy_llm_toolkit/commands/models.py +231 -0
  15. empathy_llm_toolkit/commands/parser.py +371 -0
  16. empathy_llm_toolkit/commands/registry.py +429 -0
  17. empathy_llm_toolkit/config/__init__.py +8 -8
  18. empathy_llm_toolkit/config/unified.py +3 -7
  19. empathy_llm_toolkit/context/__init__.py +22 -0
  20. empathy_llm_toolkit/context/compaction.py +455 -0
  21. empathy_llm_toolkit/context/manager.py +434 -0
  22. empathy_llm_toolkit/hooks/__init__.py +24 -0
  23. empathy_llm_toolkit/hooks/config.py +306 -0
  24. empathy_llm_toolkit/hooks/executor.py +289 -0
  25. empathy_llm_toolkit/hooks/registry.py +302 -0
  26. empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
  27. empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
  28. empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
  29. empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
  30. empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
  31. empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
  32. empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
  33. empathy_llm_toolkit/learning/__init__.py +30 -0
  34. empathy_llm_toolkit/learning/evaluator.py +438 -0
  35. empathy_llm_toolkit/learning/extractor.py +514 -0
  36. empathy_llm_toolkit/learning/storage.py +560 -0
  37. empathy_llm_toolkit/providers.py +4 -11
  38. empathy_llm_toolkit/security/__init__.py +17 -17
  39. empathy_llm_toolkit/utils/tokens.py +2 -5
  40. empathy_os/__init__.py +202 -70
  41. empathy_os/cache_monitor.py +5 -3
  42. empathy_os/cli/__init__.py +11 -55
  43. empathy_os/cli/__main__.py +29 -15
  44. empathy_os/cli/commands/inspection.py +21 -12
  45. empathy_os/cli/commands/memory.py +4 -12
  46. empathy_os/cli/commands/profiling.py +198 -0
  47. empathy_os/cli/commands/utilities.py +27 -7
  48. empathy_os/cli.py +28 -57
  49. empathy_os/cli_unified.py +525 -1164
  50. empathy_os/cost_tracker.py +9 -3
  51. empathy_os/dashboard/server.py +200 -2
  52. empathy_os/hot_reload/__init__.py +7 -7
  53. empathy_os/hot_reload/config.py +6 -7
  54. empathy_os/hot_reload/integration.py +35 -35
  55. empathy_os/hot_reload/reloader.py +57 -57
  56. empathy_os/hot_reload/watcher.py +28 -28
  57. empathy_os/hot_reload/websocket.py +2 -2
  58. empathy_os/memory/__init__.py +11 -4
  59. empathy_os/memory/claude_memory.py +1 -1
  60. empathy_os/memory/cross_session.py +8 -12
  61. empathy_os/memory/edges.py +6 -6
  62. empathy_os/memory/file_session.py +770 -0
  63. empathy_os/memory/graph.py +30 -30
  64. empathy_os/memory/nodes.py +6 -6
  65. empathy_os/memory/short_term.py +15 -9
  66. empathy_os/memory/unified.py +606 -140
  67. empathy_os/meta_workflows/agent_creator.py +3 -9
  68. empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
  69. empathy_os/meta_workflows/form_engine.py +6 -18
  70. empathy_os/meta_workflows/intent_detector.py +64 -24
  71. empathy_os/meta_workflows/models.py +3 -1
  72. empathy_os/meta_workflows/pattern_learner.py +13 -31
  73. empathy_os/meta_workflows/plan_generator.py +55 -47
  74. empathy_os/meta_workflows/session_context.py +2 -3
  75. empathy_os/meta_workflows/workflow.py +20 -51
  76. empathy_os/models/cli.py +2 -2
  77. empathy_os/models/tasks.py +1 -2
  78. empathy_os/models/telemetry.py +4 -1
  79. empathy_os/models/token_estimator.py +3 -1
  80. empathy_os/monitoring/alerts.py +938 -9
  81. empathy_os/monitoring/alerts_cli.py +346 -183
  82. empathy_os/orchestration/execution_strategies.py +12 -29
  83. empathy_os/orchestration/pattern_learner.py +20 -26
  84. empathy_os/orchestration/real_tools.py +6 -15
  85. empathy_os/platform_utils.py +2 -1
  86. empathy_os/plugins/__init__.py +2 -2
  87. empathy_os/plugins/base.py +64 -64
  88. empathy_os/plugins/registry.py +32 -32
  89. empathy_os/project_index/index.py +49 -15
  90. empathy_os/project_index/models.py +1 -2
  91. empathy_os/project_index/reports.py +1 -1
  92. empathy_os/project_index/scanner.py +1 -0
  93. empathy_os/redis_memory.py +10 -7
  94. empathy_os/resilience/__init__.py +1 -1
  95. empathy_os/resilience/health.py +10 -10
  96. empathy_os/routing/__init__.py +7 -7
  97. empathy_os/routing/chain_executor.py +37 -37
  98. empathy_os/routing/classifier.py +36 -36
  99. empathy_os/routing/smart_router.py +40 -40
  100. empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
  101. empathy_os/scaffolding/__init__.py +8 -8
  102. empathy_os/scaffolding/__main__.py +1 -1
  103. empathy_os/scaffolding/cli.py +28 -28
  104. empathy_os/socratic/__init__.py +3 -19
  105. empathy_os/socratic/ab_testing.py +25 -36
  106. empathy_os/socratic/blueprint.py +38 -38
  107. empathy_os/socratic/cli.py +34 -20
  108. empathy_os/socratic/collaboration.py +30 -28
  109. empathy_os/socratic/domain_templates.py +9 -1
  110. empathy_os/socratic/embeddings.py +17 -13
  111. empathy_os/socratic/engine.py +135 -70
  112. empathy_os/socratic/explainer.py +70 -60
  113. empathy_os/socratic/feedback.py +24 -19
  114. empathy_os/socratic/forms.py +15 -10
  115. empathy_os/socratic/generator.py +51 -35
  116. empathy_os/socratic/llm_analyzer.py +25 -23
  117. empathy_os/socratic/mcp_server.py +99 -159
  118. empathy_os/socratic/session.py +19 -13
  119. empathy_os/socratic/storage.py +98 -67
  120. empathy_os/socratic/success.py +38 -27
  121. empathy_os/socratic/visual_editor.py +51 -39
  122. empathy_os/socratic/web_ui.py +99 -66
  123. empathy_os/telemetry/cli.py +3 -1
  124. empathy_os/telemetry/usage_tracker.py +1 -3
  125. empathy_os/test_generator/__init__.py +3 -3
  126. empathy_os/test_generator/cli.py +28 -28
  127. empathy_os/test_generator/generator.py +64 -66
  128. empathy_os/test_generator/risk_analyzer.py +11 -11
  129. empathy_os/vscode_bridge 2.py +173 -0
  130. empathy_os/vscode_bridge.py +173 -0
  131. empathy_os/workflows/__init__.py +212 -120
  132. empathy_os/workflows/batch_processing.py +8 -24
  133. empathy_os/workflows/bug_predict.py +1 -1
  134. empathy_os/workflows/code_review.py +20 -5
  135. empathy_os/workflows/code_review_pipeline.py +13 -8
  136. empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
  137. empathy_os/workflows/manage_documentation.py +1 -0
  138. empathy_os/workflows/orchestrated_health_check.py +6 -11
  139. empathy_os/workflows/orchestrated_release_prep.py +3 -3
  140. empathy_os/workflows/pr_review.py +18 -10
  141. empathy_os/workflows/progressive/README 2.md +454 -0
  142. empathy_os/workflows/progressive/__init__ 2.py +92 -0
  143. empathy_os/workflows/progressive/__init__.py +2 -12
  144. empathy_os/workflows/progressive/cli 2.py +242 -0
  145. empathy_os/workflows/progressive/cli.py +14 -37
  146. empathy_os/workflows/progressive/core 2.py +488 -0
  147. empathy_os/workflows/progressive/core.py +12 -12
  148. empathy_os/workflows/progressive/orchestrator 2.py +701 -0
  149. empathy_os/workflows/progressive/orchestrator.py +166 -144
  150. empathy_os/workflows/progressive/reports 2.py +528 -0
  151. empathy_os/workflows/progressive/reports.py +22 -31
  152. empathy_os/workflows/progressive/telemetry 2.py +280 -0
  153. empathy_os/workflows/progressive/telemetry.py +8 -14
  154. empathy_os/workflows/progressive/test_gen 2.py +514 -0
  155. empathy_os/workflows/progressive/test_gen.py +29 -48
  156. empathy_os/workflows/progressive/workflow 2.py +628 -0
  157. empathy_os/workflows/progressive/workflow.py +31 -70
  158. empathy_os/workflows/release_prep.py +21 -6
  159. empathy_os/workflows/release_prep_crew.py +1 -0
  160. empathy_os/workflows/secure_release.py +13 -6
  161. empathy_os/workflows/security_audit.py +8 -3
  162. empathy_os/workflows/test_coverage_boost_crew.py +3 -2
  163. empathy_os/workflows/test_maintenance_crew.py +1 -0
  164. empathy_os/workflows/test_runner.py +16 -12
  165. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
  166. empathy_software_plugin/cli.py +0 -122
  167. patterns/README.md +119 -0
  168. patterns/__init__.py +95 -0
  169. patterns/behavior.py +298 -0
  170. patterns/code_review_memory.json +441 -0
  171. patterns/core.py +97 -0
  172. patterns/debugging.json +3763 -0
  173. patterns/empathy.py +268 -0
  174. patterns/health_check_memory.json +505 -0
  175. patterns/input.py +161 -0
  176. patterns/memory_graph.json +8 -0
  177. patterns/refactoring_memory.json +1113 -0
  178. patterns/registry.py +663 -0
  179. patterns/security_memory.json +8 -0
  180. patterns/structural.py +415 -0
  181. patterns/validation.py +194 -0
  182. coach_wizards/__init__.py +0 -45
  183. coach_wizards/accessibility_wizard.py +0 -91
  184. coach_wizards/api_wizard.py +0 -91
  185. coach_wizards/base_wizard.py +0 -209
  186. coach_wizards/cicd_wizard.py +0 -91
  187. coach_wizards/code_reviewer_README.md +0 -60
  188. coach_wizards/code_reviewer_wizard.py +0 -180
  189. coach_wizards/compliance_wizard.py +0 -91
  190. coach_wizards/database_wizard.py +0 -91
  191. coach_wizards/debugging_wizard.py +0 -91
  192. coach_wizards/documentation_wizard.py +0 -91
  193. coach_wizards/generate_wizards.py +0 -347
  194. coach_wizards/localization_wizard.py +0 -173
  195. coach_wizards/migration_wizard.py +0 -91
  196. coach_wizards/monitoring_wizard.py +0 -91
  197. coach_wizards/observability_wizard.py +0 -91
  198. coach_wizards/performance_wizard.py +0 -91
  199. coach_wizards/prompt_engineering_wizard.py +0 -661
  200. coach_wizards/refactoring_wizard.py +0 -91
  201. coach_wizards/scaling_wizard.py +0 -90
  202. coach_wizards/security_wizard.py +0 -92
  203. coach_wizards/testing_wizard.py +0 -91
  204. empathy_framework-4.6.6.dist-info/METADATA +0 -1597
  205. empathy_framework-4.6.6.dist-info/RECORD +0 -410
  206. empathy_llm_toolkit/wizards/__init__.py +0 -43
  207. empathy_llm_toolkit/wizards/base_wizard.py +0 -364
  208. empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
  209. empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
  210. empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
  211. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
  212. empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
  213. empathy_os/wizard_factory_cli.py +0 -170
  214. empathy_software_plugin/wizards/__init__.py +0 -42
  215. empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
  216. empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
  217. empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
  218. empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
  219. empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
  220. empathy_software_plugin/wizards/base_wizard.py +0 -288
  221. empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
  222. empathy_software_plugin/wizards/code_review_wizard.py +0 -604
  223. empathy_software_plugin/wizards/debugging/__init__.py +0 -50
  224. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
  225. empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
  226. empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
  227. empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
  228. empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
  229. empathy_software_plugin/wizards/debugging/verification.py +0 -369
  230. empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
  231. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
  232. empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
  233. empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
  234. empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
  235. empathy_software_plugin/wizards/performance/__init__.py +0 -9
  236. empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
  237. empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
  238. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
  239. empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
  240. empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
  241. empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
  242. empathy_software_plugin/wizards/security/__init__.py +0 -32
  243. empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
  244. empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
  245. empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
  246. empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
  247. empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
  248. empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
  249. empathy_software_plugin/wizards/testing/__init__.py +0 -27
  250. empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
  251. empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
  252. empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
  253. empathy_software_plugin/wizards/testing_wizard.py +0 -274
  254. wizards/__init__.py +0 -82
  255. wizards/admission_assessment_wizard.py +0 -644
  256. wizards/care_plan.py +0 -321
  257. wizards/clinical_assessment.py +0 -769
  258. wizards/discharge_planning.py +0 -77
  259. wizards/discharge_summary_wizard.py +0 -468
  260. wizards/dosage_calculation.py +0 -497
  261. wizards/incident_report_wizard.py +0 -454
  262. wizards/medication_reconciliation.py +0 -85
  263. wizards/nursing_assessment.py +0 -171
  264. wizards/patient_education.py +0 -654
  265. wizards/quality_improvement.py +0 -705
  266. wizards/sbar_report.py +0 -324
  267. wizards/sbar_wizard.py +0 -608
  268. wizards/shift_handoff_wizard.py +0 -535
  269. wizards/soap_note_wizard.py +0 -679
  270. wizards/treatment_plan.py +0 -15
  271. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/WHEEL +0 -0
  272. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/entry_points.txt +0 -0
  273. {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(f"Cost Savings: ${result.cost_savings:.2f} ({result.cost_savings_percent:.0f}% vs all-Premium)")
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": result.tier_results[0].timestamp.isoformat() if result.tier_results else None,
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(task_id: str, storage_path: str = ".empathy/progressive_runs") -> dict[str, Any]:
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