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,514 @@
1
+ """Progressive test generation workflow with tier escalation.
2
+
3
+ This module implements test generation with automatic escalation from cheap
4
+ to capable to premium tiers based on test quality metrics.
5
+ """
6
+
7
+ import ast
8
+ import logging
9
+ import subprocess
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from empathy_os.workflows.progressive.core import (
15
+ EscalationConfig,
16
+ FailureAnalysis,
17
+ ProgressiveWorkflowResult,
18
+ Tier,
19
+ TierResult,
20
+ )
21
+ from empathy_os.workflows.progressive.workflow import ProgressiveWorkflow
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
27
+ """Test generation workflow with progressive tier escalation.
28
+
29
+ Generates tests for Python functions using a cost-efficient progressive
30
+ approach:
31
+ 1. Start with cheap tier (gpt-4o-mini) for volume
32
+ 2. Escalate failed tests to capable tier (claude-3-5-sonnet)
33
+ 3. Escalate persistent failures to premium tier (claude-opus-4)
34
+
35
+ Quality metrics tracked:
36
+ - Syntax errors (AST parsing)
37
+ - Test execution (pass/fail)
38
+ - Code coverage
39
+ - Assertion depth
40
+
41
+ Example:
42
+ >>> config = EscalationConfig(enabled=True, max_cost=10.00)
43
+ >>> workflow = ProgressiveTestGenWorkflow(config)
44
+ >>> result = workflow.execute(target_file="app.py")
45
+ >>> print(result.generate_report())
46
+ """
47
+
48
+ def __init__(self, config: EscalationConfig | None = None):
49
+ """Initialize progressive test generation workflow.
50
+
51
+ Args:
52
+ config: Escalation configuration (uses defaults if None)
53
+ """
54
+ super().__init__(config)
55
+ self.target_file: Path | None = None
56
+
57
+ def execute(self, target_file: str, **kwargs) -> ProgressiveWorkflowResult:
58
+ """Generate tests for target file with progressive escalation.
59
+
60
+ Args:
61
+ target_file: Path to Python file to generate tests for
62
+ **kwargs: Additional parameters
63
+
64
+ Returns:
65
+ Complete workflow results with progression history
66
+
67
+ Raises:
68
+ FileNotFoundError: If target_file doesn't exist
69
+ BudgetExceededError: If cost exceeds budget
70
+ UserCancelledError: If user declines approval
71
+
72
+ Example:
73
+ >>> result = workflow.execute(target_file="src/app.py")
74
+ >>> print(f"Generated {len(result.final_result.generated_items)} tests")
75
+ """
76
+ self.target_file = Path(target_file)
77
+
78
+ if not self.target_file.exists():
79
+ raise FileNotFoundError(f"Target file not found: {target_file}")
80
+
81
+ logger.info(f"Generating tests for {target_file}")
82
+
83
+ # Parse target file to extract functions
84
+ functions = self._parse_functions(self.target_file)
85
+
86
+ if not functions:
87
+ logger.warning(f"No functions found in {target_file}")
88
+ return self._create_empty_result("test-gen")
89
+
90
+ logger.info(f"Found {len(functions)} functions to test")
91
+
92
+ # Execute with progressive escalation
93
+ return self._execute_progressive(
94
+ items=functions,
95
+ workflow_name="test-gen",
96
+ **kwargs
97
+ )
98
+
99
+ def _parse_functions(self, file_path: Path) -> list[dict[str, Any]]:
100
+ """Parse Python file to extract function definitions.
101
+
102
+ Args:
103
+ file_path: Path to Python file
104
+
105
+ Returns:
106
+ List of function metadata dicts with keys:
107
+ - name: Function name
108
+ - lineno: Line number
109
+ - args: List of argument names
110
+ - docstring: Function docstring (if present)
111
+ - code: Full function source code
112
+
113
+ Example:
114
+ >>> functions = workflow._parse_functions(Path("app.py"))
115
+ >>> print(functions[0]["name"])
116
+ 'calculate_total'
117
+ """
118
+ try:
119
+ source = file_path.read_text()
120
+ tree = ast.parse(source)
121
+ except SyntaxError as e:
122
+ logger.error(f"Syntax error in {file_path}: {e}")
123
+ return []
124
+
125
+ functions = []
126
+
127
+ for node in ast.walk(tree):
128
+ if isinstance(node, ast.FunctionDef):
129
+ # Extract function info
130
+ func_info = {
131
+ "name": node.name,
132
+ "lineno": node.lineno,
133
+ "args": [arg.arg for arg in node.args.args],
134
+ "docstring": ast.get_docstring(node) or "",
135
+ "code": ast.unparse(node), # Python 3.9+
136
+ "file": str(file_path)
137
+ }
138
+ functions.append(func_info)
139
+
140
+ return functions
141
+
142
+ def _execute_tier_impl(
143
+ self,
144
+ tier: Tier,
145
+ items: list[Any],
146
+ context: dict[str, Any] | None,
147
+ **kwargs
148
+ ) -> list[dict[str, Any]]:
149
+ """Execute test generation at specific tier.
150
+
151
+ Args:
152
+ tier: Which tier to execute at
153
+ items: Functions to generate tests for
154
+ context: Context from previous tier (if escalating)
155
+ **kwargs: Additional parameters
156
+
157
+ Returns:
158
+ List of generated test items with quality scores
159
+
160
+ Note:
161
+ This is a placeholder implementation. In production, this would
162
+ call the actual LLM API to generate tests.
163
+ """
164
+ logger.info(f"Generating {len(items)} tests at {tier.value} tier")
165
+
166
+ # Build prompt for this tier
167
+ base_task = self._build_test_gen_task(items)
168
+ prompt = self.meta_orchestrator.build_tier_prompt(
169
+ tier,
170
+ base_task,
171
+ context
172
+ )
173
+
174
+ # TODO: Call LLM API with prompt
175
+ # For now, simulate test generation
176
+ generated_tests = self._simulate_test_generation(tier, items)
177
+
178
+ return generated_tests
179
+
180
+ def _build_test_gen_task(self, functions: list[dict[str, Any]]) -> str:
181
+ """Build task description for test generation.
182
+
183
+ Args:
184
+ functions: List of function metadata
185
+
186
+ Returns:
187
+ Task description string
188
+
189
+ Example:
190
+ >>> task = workflow._build_test_gen_task([{"name": "foo", ...}])
191
+ >>> print(task)
192
+ 'Generate pytest tests for 1 functions from app.py'
193
+ """
194
+ file_name = self.target_file.name if self.target_file else "module"
195
+ func_names = [f["name"] for f in functions]
196
+
197
+ task = f"Generate pytest tests for {len(functions)} function(s) from {file_name}"
198
+
199
+ if len(func_names) <= 3:
200
+ task += f": {', '.join(func_names)}"
201
+
202
+ return task
203
+
204
+ def _simulate_test_generation(
205
+ self,
206
+ tier: Tier,
207
+ functions: list[dict[str, Any]]
208
+ ) -> list[dict[str, Any]]:
209
+ """Simulate test generation (placeholder for LLM integration).
210
+
211
+ In production, this would call the LLM API. For now, it generates
212
+ mock test data with varying quality based on tier.
213
+
214
+ Args:
215
+ tier: Which tier is generating
216
+ functions: Functions to generate tests for
217
+
218
+ Returns:
219
+ List of generated test items with quality metrics
220
+
221
+ Note:
222
+ This is temporary scaffolding. Real implementation will:
223
+ 1. Call LLM API with tier-appropriate model
224
+ 2. Parse generated test code
225
+ 3. Validate syntax
226
+ 4. Execute tests
227
+ 5. Calculate coverage
228
+ """
229
+ generated_tests = []
230
+
231
+ # Simulate different quality levels per tier
232
+ base_quality = {
233
+ Tier.CHEAP: 70,
234
+ Tier.CAPABLE: 85,
235
+ Tier.PREMIUM: 95
236
+ }[tier]
237
+
238
+ for func in functions:
239
+ # Generate mock test code
240
+ test_code = self._generate_mock_test(func)
241
+
242
+ # Analyze test quality
243
+ analysis = self._analyze_generated_test(test_code, func)
244
+
245
+ # Calculate quality score
246
+ quality_score = analysis.calculate_quality_score()
247
+
248
+ generated_tests.append({
249
+ "function_name": func["name"],
250
+ "test_code": test_code,
251
+ "quality_score": quality_score,
252
+ "passed": analysis.test_pass_rate > 0.5,
253
+ "coverage": analysis.coverage_percent,
254
+ "assertions": analysis.assertion_depth,
255
+ "confidence": analysis.confidence_score,
256
+ "syntax_errors": [str(e) for e in analysis.syntax_errors],
257
+ "error": "" if not analysis.syntax_errors else str(analysis.syntax_errors[0])
258
+ })
259
+
260
+ return generated_tests
261
+
262
+ def _generate_mock_test(self, func: dict[str, Any]) -> str:
263
+ """Generate mock test code (placeholder).
264
+
265
+ Args:
266
+ func: Function metadata
267
+
268
+ Returns:
269
+ Generated test code as string
270
+ """
271
+ func_name = func["name"]
272
+ args = func["args"]
273
+
274
+ # Generate simple test template
275
+ test_code = f'''def test_{func_name}():
276
+ """Test {func_name} function."""
277
+ # Arrange
278
+ {self._generate_test_setup(args)}
279
+
280
+ # Act
281
+ result = {func_name}({", ".join(args)})
282
+
283
+ # Assert
284
+ assert result is not None
285
+ '''
286
+
287
+ return test_code
288
+
289
+ def _generate_test_setup(self, args: list[str]) -> str:
290
+ """Generate test setup code for arguments.
291
+
292
+ Args:
293
+ args: List of argument names
294
+
295
+ Returns:
296
+ Setup code as string
297
+ """
298
+ if not args:
299
+ return "pass"
300
+
301
+ setup_lines = []
302
+ for arg in args:
303
+ # Simple type inference based on name
304
+ if "count" in arg or "num" in arg or "index" in arg:
305
+ setup_lines.append(f"{arg} = 1")
306
+ elif "name" in arg or "text" in arg or "message" in arg:
307
+ setup_lines.append(f'{arg} = "test"')
308
+ elif "items" in arg or "list" in arg:
309
+ setup_lines.append(f"{arg} = []")
310
+ else:
311
+ setup_lines.append(f'{arg} = "value"')
312
+
313
+ return "\n ".join(setup_lines)
314
+
315
+ def _analyze_generated_test(
316
+ self,
317
+ test_code: str,
318
+ func: dict[str, Any]
319
+ ) -> FailureAnalysis:
320
+ """Analyze quality of generated test.
321
+
322
+ Args:
323
+ test_code: Generated test code
324
+ func: Original function metadata
325
+
326
+ Returns:
327
+ Failure analysis with quality metrics
328
+ """
329
+ analysis = FailureAnalysis()
330
+
331
+ # 1. Check syntax
332
+ try:
333
+ ast.parse(test_code)
334
+ except SyntaxError as e:
335
+ analysis.syntax_errors.append(e)
336
+ return analysis # Can't proceed with invalid syntax
337
+
338
+ # 2. Count assertions
339
+ try:
340
+ tree = ast.parse(test_code)
341
+ assertion_count = sum(
342
+ 1 for node in ast.walk(tree)
343
+ if isinstance(node, ast.Assert)
344
+ )
345
+ analysis.assertion_depth = assertion_count
346
+ except Exception as e:
347
+ logger.warning(f"Failed to count assertions: {e}")
348
+ analysis.assertion_depth = 0
349
+
350
+ # 3. Simulate test execution (placeholder)
351
+ # In production, would actually run the test
352
+ analysis.test_pass_rate = 0.8 # Mock: 80% pass rate
353
+
354
+ # 4. Simulate coverage (placeholder)
355
+ # In production, would use coverage.py
356
+ analysis.coverage_percent = 75.0 # Mock: 75% coverage
357
+
358
+ # 5. Estimate confidence (placeholder)
359
+ # In production, would parse from LLM response
360
+ analysis.confidence_score = 0.85 # Mock: 85% confidence
361
+
362
+ return analysis
363
+
364
+ def _create_empty_result(self, workflow_name: str) -> ProgressiveWorkflowResult:
365
+ """Create empty result when no functions found.
366
+
367
+ Args:
368
+ workflow_name: Name of workflow
369
+
370
+ Returns:
371
+ Empty workflow result
372
+ """
373
+ empty_result = TierResult(
374
+ tier=Tier.CHEAP,
375
+ model=self._get_model_for_tier(Tier.CHEAP),
376
+ attempt=1,
377
+ timestamp=datetime.now(),
378
+ generated_items=[],
379
+ failure_analysis=FailureAnalysis(),
380
+ cost=0.0,
381
+ duration=0.0
382
+ )
383
+
384
+ task_id = f"{workflow_name}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
385
+
386
+ return ProgressiveWorkflowResult(
387
+ workflow_name=workflow_name,
388
+ task_id=task_id,
389
+ tier_results=[empty_result],
390
+ final_result=empty_result,
391
+ total_cost=0.0,
392
+ total_duration=0.0,
393
+ success=False
394
+ )
395
+
396
+
397
+ def execute_test_file(test_file: Path) -> dict[str, Any]:
398
+ """Execute a test file using pytest.
399
+
400
+ Args:
401
+ test_file: Path to test file
402
+
403
+ Returns:
404
+ Dict with execution results:
405
+ - passed: Number of tests passed
406
+ - failed: Number of tests failed
407
+ - pass_rate: Percentage passed (0.0-1.0)
408
+ - output: pytest output
409
+
410
+ Example:
411
+ >>> result = execute_test_file(Path("test_app.py"))
412
+ >>> print(f"Pass rate: {result['pass_rate']:.1%}")
413
+ """
414
+ try:
415
+ result = subprocess.run(
416
+ ["pytest", str(test_file), "-v", "--tb=short"],
417
+ capture_output=True,
418
+ text=True,
419
+ timeout=60
420
+ )
421
+
422
+ # Parse pytest output to get pass/fail counts
423
+ # This is a simple parser - production would be more robust
424
+ output = result.stdout + result.stderr
425
+
426
+ passed = output.count(" PASSED")
427
+ failed = output.count(" FAILED")
428
+ total = passed + failed
429
+
430
+ pass_rate = passed / total if total > 0 else 0.0
431
+
432
+ return {
433
+ "passed": passed,
434
+ "failed": failed,
435
+ "total": total,
436
+ "pass_rate": pass_rate,
437
+ "output": output,
438
+ "returncode": result.returncode
439
+ }
440
+
441
+ except subprocess.TimeoutExpired:
442
+ return {
443
+ "passed": 0,
444
+ "failed": 0,
445
+ "total": 0,
446
+ "pass_rate": 0.0,
447
+ "output": "Test execution timed out",
448
+ "returncode": -1
449
+ }
450
+ except Exception as e:
451
+ logger.error(f"Failed to execute tests: {e}")
452
+ return {
453
+ "passed": 0,
454
+ "failed": 0,
455
+ "total": 0,
456
+ "pass_rate": 0.0,
457
+ "output": str(e),
458
+ "returncode": -1
459
+ }
460
+
461
+
462
+ def calculate_coverage(test_file: Path, source_file: Path) -> float:
463
+ """Calculate code coverage for a test file.
464
+
465
+ Args:
466
+ test_file: Path to test file
467
+ source_file: Path to source file being tested
468
+
469
+ Returns:
470
+ Coverage percentage (0.0-100.0)
471
+
472
+ Example:
473
+ >>> coverage = calculate_coverage(
474
+ ... Path("test_app.py"),
475
+ ... Path("app.py")
476
+ ... )
477
+ >>> print(f"Coverage: {coverage:.1f}%")
478
+ """
479
+ try:
480
+ # Run pytest with coverage
481
+ result = subprocess.run(
482
+ [
483
+ "pytest",
484
+ str(test_file),
485
+ f"--cov={source_file.stem}",
486
+ "--cov-report=term-missing",
487
+ "--no-cov-on-fail"
488
+ ],
489
+ capture_output=True,
490
+ text=True,
491
+ timeout=60,
492
+ cwd=source_file.parent
493
+ )
494
+
495
+ output = result.stdout + result.stderr
496
+
497
+ # Parse coverage percentage from output
498
+ # Look for line like: "app.py 85%"
499
+ for line in output.split("\n"):
500
+ if source_file.name in line and "%" in line:
501
+ # Extract percentage
502
+ parts = line.split()
503
+ for part in parts:
504
+ if "%" in part:
505
+ try:
506
+ return float(part.rstrip("%"))
507
+ except ValueError:
508
+ pass
509
+
510
+ return 0.0
511
+
512
+ except Exception as e:
513
+ logger.error(f"Failed to calculate coverage: {e}")
514
+ return 0.0
@@ -90,11 +90,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
90
90
  logger.info(f"Found {len(functions)} functions to test")
91
91
 
92
92
  # Execute with progressive escalation
93
- return self._execute_progressive(
94
- items=functions,
95
- workflow_name="test-gen",
96
- **kwargs
97
- )
93
+ return self._execute_progressive(items=functions, workflow_name="test-gen", **kwargs)
98
94
 
99
95
  def _parse_functions(self, file_path: Path) -> list[dict[str, Any]]:
100
96
  """Parse Python file to extract function definitions.
@@ -133,18 +129,14 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
133
129
  "args": [arg.arg for arg in node.args.args],
134
130
  "docstring": ast.get_docstring(node) or "",
135
131
  "code": ast.unparse(node), # Python 3.9+
136
- "file": str(file_path)
132
+ "file": str(file_path),
137
133
  }
138
134
  functions.append(func_info)
139
135
 
140
136
  return functions
141
137
 
142
138
  def _execute_tier_impl(
143
- self,
144
- tier: Tier,
145
- items: list[Any],
146
- context: dict[str, Any] | None,
147
- **kwargs
139
+ self, tier: Tier, items: list[Any], context: dict[str, Any] | None, **kwargs
148
140
  ) -> list[dict[str, Any]]:
149
141
  """Execute test generation at specific tier.
150
142
 
@@ -165,11 +157,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
165
157
 
166
158
  # Build prompt for this tier (prepared for future LLM integration)
167
159
  base_task = self._build_test_gen_task(items)
168
- _prompt = self.meta_orchestrator.build_tier_prompt( # noqa: F841
169
- tier,
170
- base_task,
171
- context
172
- )
160
+ _prompt = self.meta_orchestrator.build_tier_prompt(tier, base_task, context) # noqa: F841
173
161
 
174
162
  # TODO: Call LLM API with _prompt
175
163
  # For now, simulate test generation
@@ -202,9 +190,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
202
190
  return task
203
191
 
204
192
  def _simulate_test_generation(
205
- self,
206
- tier: Tier,
207
- functions: list[dict[str, Any]]
193
+ self, tier: Tier, functions: list[dict[str, Any]]
208
194
  ) -> list[dict[str, Any]]:
209
195
  """Simulate test generation (placeholder for LLM integration).
210
196
 
@@ -232,7 +218,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
232
218
  _base_quality = { # noqa: F841
233
219
  Tier.CHEAP: 70,
234
220
  Tier.CAPABLE: 85,
235
- Tier.PREMIUM: 95
221
+ Tier.PREMIUM: 95,
236
222
  }[tier]
237
223
 
238
224
  for func in functions:
@@ -245,17 +231,19 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
245
231
  # Calculate quality score
246
232
  quality_score = analysis.calculate_quality_score()
247
233
 
248
- generated_tests.append({
249
- "function_name": func["name"],
250
- "test_code": test_code,
251
- "quality_score": quality_score,
252
- "passed": analysis.test_pass_rate > 0.5,
253
- "coverage": analysis.coverage_percent,
254
- "assertions": analysis.assertion_depth,
255
- "confidence": analysis.confidence_score,
256
- "syntax_errors": [str(e) for e in analysis.syntax_errors],
257
- "error": "" if not analysis.syntax_errors else str(analysis.syntax_errors[0])
258
- })
234
+ generated_tests.append(
235
+ {
236
+ "function_name": func["name"],
237
+ "test_code": test_code,
238
+ "quality_score": quality_score,
239
+ "passed": analysis.test_pass_rate > 0.5,
240
+ "coverage": analysis.coverage_percent,
241
+ "assertions": analysis.assertion_depth,
242
+ "confidence": analysis.confidence_score,
243
+ "syntax_errors": [str(e) for e in analysis.syntax_errors],
244
+ "error": "" if not analysis.syntax_errors else str(analysis.syntax_errors[0]),
245
+ }
246
+ )
259
247
 
260
248
  return generated_tests
261
249
 
@@ -312,11 +300,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
312
300
 
313
301
  return "\n ".join(setup_lines)
314
302
 
315
- def _analyze_generated_test(
316
- self,
317
- test_code: str,
318
- func: dict[str, Any]
319
- ) -> FailureAnalysis:
303
+ def _analyze_generated_test(self, test_code: str, func: dict[str, Any]) -> FailureAnalysis:
320
304
  """Analyze quality of generated test.
321
305
 
322
306
  Args:
@@ -338,10 +322,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
338
322
  # 2. Count assertions
339
323
  try:
340
324
  tree = ast.parse(test_code)
341
- assertion_count = sum(
342
- 1 for node in ast.walk(tree)
343
- if isinstance(node, ast.Assert)
344
- )
325
+ assertion_count = sum(1 for node in ast.walk(tree) if isinstance(node, ast.Assert))
345
326
  analysis.assertion_depth = assertion_count
346
327
  except Exception as e:
347
328
  logger.warning(f"Failed to count assertions: {e}")
@@ -378,7 +359,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
378
359
  generated_items=[],
379
360
  failure_analysis=FailureAnalysis(),
380
361
  cost=0.0,
381
- duration=0.0
362
+ duration=0.0,
382
363
  )
383
364
 
384
365
  task_id = f"{workflow_name}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
@@ -390,7 +371,7 @@ class ProgressiveTestGenWorkflow(ProgressiveWorkflow):
390
371
  final_result=empty_result,
391
372
  total_cost=0.0,
392
373
  total_duration=0.0,
393
- success=False
374
+ success=False,
394
375
  )
395
376
 
396
377
 
@@ -416,7 +397,7 @@ def execute_test_file(test_file: Path) -> dict[str, Any]:
416
397
  ["pytest", str(test_file), "-v", "--tb=short"],
417
398
  capture_output=True,
418
399
  text=True,
419
- timeout=60
400
+ timeout=60,
420
401
  )
421
402
 
422
403
  # Parse pytest output to get pass/fail counts
@@ -435,7 +416,7 @@ def execute_test_file(test_file: Path) -> dict[str, Any]:
435
416
  "total": total,
436
417
  "pass_rate": pass_rate,
437
418
  "output": output,
438
- "returncode": result.returncode
419
+ "returncode": result.returncode,
439
420
  }
440
421
 
441
422
  except subprocess.TimeoutExpired:
@@ -445,7 +426,7 @@ def execute_test_file(test_file: Path) -> dict[str, Any]:
445
426
  "total": 0,
446
427
  "pass_rate": 0.0,
447
428
  "output": "Test execution timed out",
448
- "returncode": -1
429
+ "returncode": -1,
449
430
  }
450
431
  except Exception as e:
451
432
  logger.error(f"Failed to execute tests: {e}")
@@ -455,7 +436,7 @@ def execute_test_file(test_file: Path) -> dict[str, Any]:
455
436
  "total": 0,
456
437
  "pass_rate": 0.0,
457
438
  "output": str(e),
458
- "returncode": -1
439
+ "returncode": -1,
459
440
  }
460
441
 
461
442
 
@@ -484,12 +465,12 @@ def calculate_coverage(test_file: Path, source_file: Path) -> float:
484
465
  str(test_file),
485
466
  f"--cov={source_file.stem}",
486
467
  "--cov-report=term-missing",
487
- "--no-cov-on-fail"
468
+ "--no-cov-on-fail",
488
469
  ],
489
470
  capture_output=True,
490
471
  text=True,
491
472
  timeout=60,
492
- cwd=source_file.parent
473
+ cwd=source_file.parent,
493
474
  )
494
475
 
495
476
  output = result.stdout + result.stderr