attune-ai 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (457) hide show
  1. attune/__init__.py +358 -0
  2. attune/adaptive/__init__.py +13 -0
  3. attune/adaptive/task_complexity.py +127 -0
  4. attune/agent_monitoring.py +414 -0
  5. attune/cache/__init__.py +117 -0
  6. attune/cache/base.py +166 -0
  7. attune/cache/dependency_manager.py +256 -0
  8. attune/cache/hash_only.py +251 -0
  9. attune/cache/hybrid.py +457 -0
  10. attune/cache/storage.py +285 -0
  11. attune/cache_monitor.py +356 -0
  12. attune/cache_stats.py +298 -0
  13. attune/cli/__init__.py +152 -0
  14. attune/cli/__main__.py +12 -0
  15. attune/cli/commands/__init__.py +1 -0
  16. attune/cli/commands/batch.py +264 -0
  17. attune/cli/commands/cache.py +248 -0
  18. attune/cli/commands/help.py +331 -0
  19. attune/cli/commands/info.py +140 -0
  20. attune/cli/commands/inspect.py +436 -0
  21. attune/cli/commands/inspection.py +57 -0
  22. attune/cli/commands/memory.py +48 -0
  23. attune/cli/commands/metrics.py +92 -0
  24. attune/cli/commands/orchestrate.py +184 -0
  25. attune/cli/commands/patterns.py +207 -0
  26. attune/cli/commands/profiling.py +202 -0
  27. attune/cli/commands/provider.py +98 -0
  28. attune/cli/commands/routing.py +285 -0
  29. attune/cli/commands/setup.py +96 -0
  30. attune/cli/commands/status.py +235 -0
  31. attune/cli/commands/sync.py +166 -0
  32. attune/cli/commands/tier.py +121 -0
  33. attune/cli/commands/utilities.py +114 -0
  34. attune/cli/commands/workflow.py +579 -0
  35. attune/cli/core.py +32 -0
  36. attune/cli/parsers/__init__.py +68 -0
  37. attune/cli/parsers/batch.py +118 -0
  38. attune/cli/parsers/cache.py +65 -0
  39. attune/cli/parsers/help.py +41 -0
  40. attune/cli/parsers/info.py +26 -0
  41. attune/cli/parsers/inspect.py +66 -0
  42. attune/cli/parsers/metrics.py +42 -0
  43. attune/cli/parsers/orchestrate.py +61 -0
  44. attune/cli/parsers/patterns.py +54 -0
  45. attune/cli/parsers/provider.py +40 -0
  46. attune/cli/parsers/routing.py +110 -0
  47. attune/cli/parsers/setup.py +42 -0
  48. attune/cli/parsers/status.py +47 -0
  49. attune/cli/parsers/sync.py +31 -0
  50. attune/cli/parsers/tier.py +33 -0
  51. attune/cli/parsers/workflow.py +77 -0
  52. attune/cli/utils/__init__.py +1 -0
  53. attune/cli/utils/data.py +242 -0
  54. attune/cli/utils/helpers.py +68 -0
  55. attune/cli_legacy.py +3957 -0
  56. attune/cli_minimal.py +1159 -0
  57. attune/cli_router.py +437 -0
  58. attune/cli_unified.py +814 -0
  59. attune/config/__init__.py +66 -0
  60. attune/config/xml_config.py +286 -0
  61. attune/config.py +545 -0
  62. attune/coordination.py +870 -0
  63. attune/core.py +1511 -0
  64. attune/core_modules/__init__.py +15 -0
  65. attune/cost_tracker.py +626 -0
  66. attune/dashboard/__init__.py +41 -0
  67. attune/dashboard/app.py +512 -0
  68. attune/dashboard/simple_server.py +435 -0
  69. attune/dashboard/standalone_server.py +547 -0
  70. attune/discovery.py +306 -0
  71. attune/emergence.py +306 -0
  72. attune/exceptions.py +123 -0
  73. attune/feedback_loops.py +373 -0
  74. attune/hot_reload/README.md +473 -0
  75. attune/hot_reload/__init__.py +62 -0
  76. attune/hot_reload/config.py +83 -0
  77. attune/hot_reload/integration.py +229 -0
  78. attune/hot_reload/reloader.py +298 -0
  79. attune/hot_reload/watcher.py +183 -0
  80. attune/hot_reload/websocket.py +177 -0
  81. attune/levels.py +577 -0
  82. attune/leverage_points.py +441 -0
  83. attune/logging_config.py +261 -0
  84. attune/mcp/__init__.py +10 -0
  85. attune/mcp/server.py +506 -0
  86. attune/memory/__init__.py +237 -0
  87. attune/memory/claude_memory.py +469 -0
  88. attune/memory/config.py +224 -0
  89. attune/memory/control_panel.py +1290 -0
  90. attune/memory/control_panel_support.py +145 -0
  91. attune/memory/cross_session.py +845 -0
  92. attune/memory/edges.py +179 -0
  93. attune/memory/encryption.py +159 -0
  94. attune/memory/file_session.py +770 -0
  95. attune/memory/graph.py +570 -0
  96. attune/memory/long_term.py +913 -0
  97. attune/memory/long_term_types.py +99 -0
  98. attune/memory/mixins/__init__.py +25 -0
  99. attune/memory/mixins/backend_init_mixin.py +249 -0
  100. attune/memory/mixins/capabilities_mixin.py +208 -0
  101. attune/memory/mixins/handoff_mixin.py +208 -0
  102. attune/memory/mixins/lifecycle_mixin.py +49 -0
  103. attune/memory/mixins/long_term_mixin.py +352 -0
  104. attune/memory/mixins/promotion_mixin.py +109 -0
  105. attune/memory/mixins/short_term_mixin.py +182 -0
  106. attune/memory/nodes.py +179 -0
  107. attune/memory/redis_bootstrap.py +540 -0
  108. attune/memory/security/__init__.py +31 -0
  109. attune/memory/security/audit_logger.py +932 -0
  110. attune/memory/security/pii_scrubber.py +640 -0
  111. attune/memory/security/secrets_detector.py +678 -0
  112. attune/memory/short_term.py +2192 -0
  113. attune/memory/simple_storage.py +302 -0
  114. attune/memory/storage/__init__.py +15 -0
  115. attune/memory/storage_backend.py +167 -0
  116. attune/memory/summary_index.py +583 -0
  117. attune/memory/types.py +446 -0
  118. attune/memory/unified.py +182 -0
  119. attune/meta_workflows/__init__.py +74 -0
  120. attune/meta_workflows/agent_creator.py +248 -0
  121. attune/meta_workflows/builtin_templates.py +567 -0
  122. attune/meta_workflows/cli_commands/__init__.py +56 -0
  123. attune/meta_workflows/cli_commands/agent_commands.py +321 -0
  124. attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
  125. attune/meta_workflows/cli_commands/config_commands.py +232 -0
  126. attune/meta_workflows/cli_commands/memory_commands.py +182 -0
  127. attune/meta_workflows/cli_commands/template_commands.py +354 -0
  128. attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
  129. attune/meta_workflows/cli_meta_workflows.py +59 -0
  130. attune/meta_workflows/form_engine.py +292 -0
  131. attune/meta_workflows/intent_detector.py +409 -0
  132. attune/meta_workflows/models.py +569 -0
  133. attune/meta_workflows/pattern_learner.py +738 -0
  134. attune/meta_workflows/plan_generator.py +384 -0
  135. attune/meta_workflows/session_context.py +397 -0
  136. attune/meta_workflows/template_registry.py +229 -0
  137. attune/meta_workflows/workflow.py +984 -0
  138. attune/metrics/__init__.py +12 -0
  139. attune/metrics/collector.py +31 -0
  140. attune/metrics/prompt_metrics.py +194 -0
  141. attune/models/__init__.py +172 -0
  142. attune/models/__main__.py +13 -0
  143. attune/models/adaptive_routing.py +437 -0
  144. attune/models/auth_cli.py +444 -0
  145. attune/models/auth_strategy.py +450 -0
  146. attune/models/cli.py +655 -0
  147. attune/models/empathy_executor.py +354 -0
  148. attune/models/executor.py +257 -0
  149. attune/models/fallback.py +762 -0
  150. attune/models/provider_config.py +282 -0
  151. attune/models/registry.py +472 -0
  152. attune/models/tasks.py +359 -0
  153. attune/models/telemetry/__init__.py +71 -0
  154. attune/models/telemetry/analytics.py +594 -0
  155. attune/models/telemetry/backend.py +196 -0
  156. attune/models/telemetry/data_models.py +431 -0
  157. attune/models/telemetry/storage.py +489 -0
  158. attune/models/token_estimator.py +420 -0
  159. attune/models/validation.py +280 -0
  160. attune/monitoring/__init__.py +52 -0
  161. attune/monitoring/alerts.py +946 -0
  162. attune/monitoring/alerts_cli.py +448 -0
  163. attune/monitoring/multi_backend.py +271 -0
  164. attune/monitoring/otel_backend.py +362 -0
  165. attune/optimization/__init__.py +19 -0
  166. attune/optimization/context_optimizer.py +272 -0
  167. attune/orchestration/__init__.py +67 -0
  168. attune/orchestration/agent_templates.py +707 -0
  169. attune/orchestration/config_store.py +499 -0
  170. attune/orchestration/execution_strategies.py +2111 -0
  171. attune/orchestration/meta_orchestrator.py +1168 -0
  172. attune/orchestration/pattern_learner.py +696 -0
  173. attune/orchestration/real_tools.py +931 -0
  174. attune/pattern_cache.py +187 -0
  175. attune/pattern_library.py +542 -0
  176. attune/patterns/debugging/all_patterns.json +81 -0
  177. attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
  178. attune/patterns/refactoring_memory.json +89 -0
  179. attune/persistence.py +564 -0
  180. attune/platform_utils.py +265 -0
  181. attune/plugins/__init__.py +28 -0
  182. attune/plugins/base.py +361 -0
  183. attune/plugins/registry.py +268 -0
  184. attune/project_index/__init__.py +32 -0
  185. attune/project_index/cli.py +335 -0
  186. attune/project_index/index.py +667 -0
  187. attune/project_index/models.py +504 -0
  188. attune/project_index/reports.py +474 -0
  189. attune/project_index/scanner.py +777 -0
  190. attune/project_index/scanner_parallel.py +291 -0
  191. attune/prompts/__init__.py +61 -0
  192. attune/prompts/config.py +77 -0
  193. attune/prompts/context.py +177 -0
  194. attune/prompts/parser.py +285 -0
  195. attune/prompts/registry.py +313 -0
  196. attune/prompts/templates.py +208 -0
  197. attune/redis_config.py +302 -0
  198. attune/redis_memory.py +799 -0
  199. attune/resilience/__init__.py +56 -0
  200. attune/resilience/circuit_breaker.py +256 -0
  201. attune/resilience/fallback.py +179 -0
  202. attune/resilience/health.py +300 -0
  203. attune/resilience/retry.py +209 -0
  204. attune/resilience/timeout.py +135 -0
  205. attune/routing/__init__.py +43 -0
  206. attune/routing/chain_executor.py +433 -0
  207. attune/routing/classifier.py +217 -0
  208. attune/routing/smart_router.py +234 -0
  209. attune/routing/workflow_registry.py +343 -0
  210. attune/scaffolding/README.md +589 -0
  211. attune/scaffolding/__init__.py +35 -0
  212. attune/scaffolding/__main__.py +14 -0
  213. attune/scaffolding/cli.py +240 -0
  214. attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
  215. attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
  216. attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
  217. attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
  218. attune/socratic/__init__.py +256 -0
  219. attune/socratic/ab_testing.py +958 -0
  220. attune/socratic/blueprint.py +533 -0
  221. attune/socratic/cli.py +703 -0
  222. attune/socratic/collaboration.py +1114 -0
  223. attune/socratic/domain_templates.py +924 -0
  224. attune/socratic/embeddings.py +738 -0
  225. attune/socratic/engine.py +794 -0
  226. attune/socratic/explainer.py +682 -0
  227. attune/socratic/feedback.py +772 -0
  228. attune/socratic/forms.py +629 -0
  229. attune/socratic/generator.py +732 -0
  230. attune/socratic/llm_analyzer.py +637 -0
  231. attune/socratic/mcp_server.py +702 -0
  232. attune/socratic/session.py +312 -0
  233. attune/socratic/storage.py +667 -0
  234. attune/socratic/success.py +730 -0
  235. attune/socratic/visual_editor.py +860 -0
  236. attune/socratic/web_ui.py +958 -0
  237. attune/telemetry/__init__.py +39 -0
  238. attune/telemetry/agent_coordination.py +475 -0
  239. attune/telemetry/agent_tracking.py +367 -0
  240. attune/telemetry/approval_gates.py +545 -0
  241. attune/telemetry/cli.py +1231 -0
  242. attune/telemetry/commands/__init__.py +14 -0
  243. attune/telemetry/commands/dashboard_commands.py +696 -0
  244. attune/telemetry/event_streaming.py +409 -0
  245. attune/telemetry/feedback_loop.py +567 -0
  246. attune/telemetry/usage_tracker.py +591 -0
  247. attune/templates.py +754 -0
  248. attune/test_generator/__init__.py +38 -0
  249. attune/test_generator/__main__.py +14 -0
  250. attune/test_generator/cli.py +234 -0
  251. attune/test_generator/generator.py +355 -0
  252. attune/test_generator/risk_analyzer.py +216 -0
  253. attune/test_generator/templates/unit_test.py.jinja2 +272 -0
  254. attune/tier_recommender.py +384 -0
  255. attune/tools.py +183 -0
  256. attune/trust/__init__.py +28 -0
  257. attune/trust/circuit_breaker.py +579 -0
  258. attune/trust_building.py +527 -0
  259. attune/validation/__init__.py +19 -0
  260. attune/validation/xml_validator.py +281 -0
  261. attune/vscode_bridge.py +173 -0
  262. attune/workflow_commands.py +780 -0
  263. attune/workflow_patterns/__init__.py +33 -0
  264. attune/workflow_patterns/behavior.py +249 -0
  265. attune/workflow_patterns/core.py +76 -0
  266. attune/workflow_patterns/output.py +99 -0
  267. attune/workflow_patterns/registry.py +255 -0
  268. attune/workflow_patterns/structural.py +288 -0
  269. attune/workflows/__init__.py +539 -0
  270. attune/workflows/autonomous_test_gen.py +1268 -0
  271. attune/workflows/base.py +2667 -0
  272. attune/workflows/batch_processing.py +342 -0
  273. attune/workflows/bug_predict.py +1084 -0
  274. attune/workflows/builder.py +273 -0
  275. attune/workflows/caching.py +253 -0
  276. attune/workflows/code_review.py +1048 -0
  277. attune/workflows/code_review_adapters.py +312 -0
  278. attune/workflows/code_review_pipeline.py +722 -0
  279. attune/workflows/config.py +645 -0
  280. attune/workflows/dependency_check.py +644 -0
  281. attune/workflows/document_gen/__init__.py +25 -0
  282. attune/workflows/document_gen/config.py +30 -0
  283. attune/workflows/document_gen/report_formatter.py +162 -0
  284. attune/workflows/document_gen/workflow.py +1426 -0
  285. attune/workflows/document_manager.py +216 -0
  286. attune/workflows/document_manager_README.md +134 -0
  287. attune/workflows/documentation_orchestrator.py +1205 -0
  288. attune/workflows/history.py +510 -0
  289. attune/workflows/keyboard_shortcuts/__init__.py +39 -0
  290. attune/workflows/keyboard_shortcuts/generators.py +391 -0
  291. attune/workflows/keyboard_shortcuts/parsers.py +416 -0
  292. attune/workflows/keyboard_shortcuts/prompts.py +295 -0
  293. attune/workflows/keyboard_shortcuts/schema.py +193 -0
  294. attune/workflows/keyboard_shortcuts/workflow.py +509 -0
  295. attune/workflows/llm_base.py +363 -0
  296. attune/workflows/manage_docs.py +87 -0
  297. attune/workflows/manage_docs_README.md +134 -0
  298. attune/workflows/manage_documentation.py +821 -0
  299. attune/workflows/new_sample_workflow1.py +149 -0
  300. attune/workflows/new_sample_workflow1_README.md +150 -0
  301. attune/workflows/orchestrated_health_check.py +849 -0
  302. attune/workflows/orchestrated_release_prep.py +600 -0
  303. attune/workflows/output.py +413 -0
  304. attune/workflows/perf_audit.py +863 -0
  305. attune/workflows/pr_review.py +762 -0
  306. attune/workflows/progress.py +785 -0
  307. attune/workflows/progress_server.py +322 -0
  308. attune/workflows/progressive/README 2.md +454 -0
  309. attune/workflows/progressive/README.md +454 -0
  310. attune/workflows/progressive/__init__.py +82 -0
  311. attune/workflows/progressive/cli.py +219 -0
  312. attune/workflows/progressive/core.py +488 -0
  313. attune/workflows/progressive/orchestrator.py +723 -0
  314. attune/workflows/progressive/reports.py +520 -0
  315. attune/workflows/progressive/telemetry.py +274 -0
  316. attune/workflows/progressive/test_gen.py +495 -0
  317. attune/workflows/progressive/workflow.py +589 -0
  318. attune/workflows/refactor_plan.py +694 -0
  319. attune/workflows/release_prep.py +895 -0
  320. attune/workflows/release_prep_crew.py +969 -0
  321. attune/workflows/research_synthesis.py +404 -0
  322. attune/workflows/routing.py +168 -0
  323. attune/workflows/secure_release.py +593 -0
  324. attune/workflows/security_adapters.py +297 -0
  325. attune/workflows/security_audit.py +1329 -0
  326. attune/workflows/security_audit_phase3.py +355 -0
  327. attune/workflows/seo_optimization.py +633 -0
  328. attune/workflows/step_config.py +234 -0
  329. attune/workflows/telemetry_mixin.py +269 -0
  330. attune/workflows/test5.py +125 -0
  331. attune/workflows/test5_README.md +158 -0
  332. attune/workflows/test_coverage_boost_crew.py +849 -0
  333. attune/workflows/test_gen/__init__.py +52 -0
  334. attune/workflows/test_gen/ast_analyzer.py +249 -0
  335. attune/workflows/test_gen/config.py +88 -0
  336. attune/workflows/test_gen/data_models.py +38 -0
  337. attune/workflows/test_gen/report_formatter.py +289 -0
  338. attune/workflows/test_gen/test_templates.py +381 -0
  339. attune/workflows/test_gen/workflow.py +655 -0
  340. attune/workflows/test_gen.py +54 -0
  341. attune/workflows/test_gen_behavioral.py +477 -0
  342. attune/workflows/test_gen_parallel.py +341 -0
  343. attune/workflows/test_lifecycle.py +526 -0
  344. attune/workflows/test_maintenance.py +627 -0
  345. attune/workflows/test_maintenance_cli.py +590 -0
  346. attune/workflows/test_maintenance_crew.py +840 -0
  347. attune/workflows/test_runner.py +622 -0
  348. attune/workflows/tier_tracking.py +531 -0
  349. attune/workflows/xml_enhanced_crew.py +285 -0
  350. attune_ai-2.0.0.dist-info/METADATA +1026 -0
  351. attune_ai-2.0.0.dist-info/RECORD +457 -0
  352. attune_ai-2.0.0.dist-info/WHEEL +5 -0
  353. attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
  354. attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
  355. attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
  356. attune_ai-2.0.0.dist-info/top_level.txt +5 -0
  357. attune_healthcare/__init__.py +13 -0
  358. attune_healthcare/monitors/__init__.py +9 -0
  359. attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
  360. attune_healthcare/monitors/monitoring/__init__.py +44 -0
  361. attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
  362. attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
  363. attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
  364. attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
  365. attune_llm/README.md +553 -0
  366. attune_llm/__init__.py +28 -0
  367. attune_llm/agent_factory/__init__.py +53 -0
  368. attune_llm/agent_factory/adapters/__init__.py +85 -0
  369. attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
  370. attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
  371. attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
  372. attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
  373. attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
  374. attune_llm/agent_factory/adapters/native.py +228 -0
  375. attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
  376. attune_llm/agent_factory/base.py +305 -0
  377. attune_llm/agent_factory/crews/__init__.py +67 -0
  378. attune_llm/agent_factory/crews/code_review.py +1113 -0
  379. attune_llm/agent_factory/crews/health_check.py +1262 -0
  380. attune_llm/agent_factory/crews/refactoring.py +1128 -0
  381. attune_llm/agent_factory/crews/security_audit.py +1018 -0
  382. attune_llm/agent_factory/decorators.py +287 -0
  383. attune_llm/agent_factory/factory.py +558 -0
  384. attune_llm/agent_factory/framework.py +193 -0
  385. attune_llm/agent_factory/memory_integration.py +328 -0
  386. attune_llm/agent_factory/resilient.py +320 -0
  387. attune_llm/agents_md/__init__.py +22 -0
  388. attune_llm/agents_md/loader.py +218 -0
  389. attune_llm/agents_md/parser.py +271 -0
  390. attune_llm/agents_md/registry.py +307 -0
  391. attune_llm/claude_memory.py +466 -0
  392. attune_llm/cli/__init__.py +8 -0
  393. attune_llm/cli/sync_claude.py +487 -0
  394. attune_llm/code_health.py +1313 -0
  395. attune_llm/commands/__init__.py +51 -0
  396. attune_llm/commands/context.py +375 -0
  397. attune_llm/commands/loader.py +301 -0
  398. attune_llm/commands/models.py +231 -0
  399. attune_llm/commands/parser.py +371 -0
  400. attune_llm/commands/registry.py +429 -0
  401. attune_llm/config/__init__.py +29 -0
  402. attune_llm/config/unified.py +291 -0
  403. attune_llm/context/__init__.py +22 -0
  404. attune_llm/context/compaction.py +455 -0
  405. attune_llm/context/manager.py +434 -0
  406. attune_llm/contextual_patterns.py +361 -0
  407. attune_llm/core.py +907 -0
  408. attune_llm/git_pattern_extractor.py +435 -0
  409. attune_llm/hooks/__init__.py +24 -0
  410. attune_llm/hooks/config.py +306 -0
  411. attune_llm/hooks/executor.py +289 -0
  412. attune_llm/hooks/registry.py +302 -0
  413. attune_llm/hooks/scripts/__init__.py +39 -0
  414. attune_llm/hooks/scripts/evaluate_session.py +201 -0
  415. attune_llm/hooks/scripts/first_time_init.py +285 -0
  416. attune_llm/hooks/scripts/pre_compact.py +207 -0
  417. attune_llm/hooks/scripts/session_end.py +183 -0
  418. attune_llm/hooks/scripts/session_start.py +163 -0
  419. attune_llm/hooks/scripts/suggest_compact.py +225 -0
  420. attune_llm/learning/__init__.py +30 -0
  421. attune_llm/learning/evaluator.py +438 -0
  422. attune_llm/learning/extractor.py +514 -0
  423. attune_llm/learning/storage.py +560 -0
  424. attune_llm/levels.py +227 -0
  425. attune_llm/pattern_confidence.py +414 -0
  426. attune_llm/pattern_resolver.py +272 -0
  427. attune_llm/pattern_summary.py +350 -0
  428. attune_llm/providers.py +967 -0
  429. attune_llm/routing/__init__.py +32 -0
  430. attune_llm/routing/model_router.py +362 -0
  431. attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
  432. attune_llm/security/PHASE2_COMPLETE.md +384 -0
  433. attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
  434. attune_llm/security/QUICK_REFERENCE.md +316 -0
  435. attune_llm/security/README.md +262 -0
  436. attune_llm/security/__init__.py +62 -0
  437. attune_llm/security/audit_logger.py +929 -0
  438. attune_llm/security/audit_logger_example.py +152 -0
  439. attune_llm/security/pii_scrubber.py +640 -0
  440. attune_llm/security/secrets_detector.py +678 -0
  441. attune_llm/security/secrets_detector_example.py +304 -0
  442. attune_llm/security/secure_memdocs.py +1192 -0
  443. attune_llm/security/secure_memdocs_example.py +278 -0
  444. attune_llm/session_status.py +745 -0
  445. attune_llm/state.py +246 -0
  446. attune_llm/utils/__init__.py +5 -0
  447. attune_llm/utils/tokens.py +349 -0
  448. attune_software/SOFTWARE_PLUGIN_README.md +57 -0
  449. attune_software/__init__.py +13 -0
  450. attune_software/cli/__init__.py +120 -0
  451. attune_software/cli/inspect.py +362 -0
  452. attune_software/cli.py +574 -0
  453. attune_software/plugin.py +188 -0
  454. workflow_scaffolding/__init__.py +11 -0
  455. workflow_scaffolding/__main__.py +12 -0
  456. workflow_scaffolding/cli.py +206 -0
  457. workflow_scaffolding/generator.py +265 -0
@@ -0,0 +1,219 @@
1
+ """CLI commands for progressive workflow management.
2
+
3
+ Provides commands for:
4
+ - Listing saved results
5
+ - Viewing detailed reports
6
+ - Generating analytics
7
+ - Cleaning up old results
8
+ """
9
+
10
+ import argparse
11
+ import sys
12
+
13
+ from attune.workflows.progressive.reports import (
14
+ cleanup_old_results,
15
+ format_cost_analytics_report,
16
+ generate_cost_analytics,
17
+ list_saved_results,
18
+ load_result_from_disk,
19
+ )
20
+
21
+
22
+ def cmd_list_results(args: argparse.Namespace) -> int:
23
+ """List all saved progressive workflow results.
24
+
25
+ Args:
26
+ args: Parsed command line arguments
27
+
28
+ Returns:
29
+ Exit code (0 for success)
30
+ """
31
+ storage_path = args.storage_path or ".attune/progressive_runs"
32
+ results = list_saved_results(storage_path)
33
+
34
+ if not results:
35
+ print(f"No results found in {storage_path}")
36
+ return 0
37
+
38
+ print(f"\n📋 Found {len(results)} progressive workflow results:\n")
39
+ print(f"{'Task ID':<40} {'Workflow':<15} {'Cost':<10} {'Savings':<12} {'Success'}")
40
+ print("─" * 90)
41
+
42
+ for result in results:
43
+ task_id = result.get("task_id", "unknown")
44
+ workflow = result.get("workflow", "unknown")[:14]
45
+ cost = result.get("total_cost", 0.0)
46
+ savings = result.get("cost_savings_percent", 0.0)
47
+ success = "✅" if result.get("success", False) else "❌"
48
+
49
+ print(f"{task_id:<40} {workflow:<15} ${cost:<9.2f} {savings:>6.1f}% {success}")
50
+
51
+ print()
52
+ return 0
53
+
54
+
55
+ def cmd_show_report(args: argparse.Namespace) -> int:
56
+ """Show detailed report for a specific task.
57
+
58
+ Args:
59
+ args: Parsed command line arguments
60
+
61
+ Returns:
62
+ Exit code (0 for success, 1 for error)
63
+ """
64
+ task_id = args.task_id
65
+ storage_path = args.storage_path or ".attune/progressive_runs"
66
+
67
+ try:
68
+ result_data = load_result_from_disk(task_id, storage_path)
69
+
70
+ if args.json:
71
+ import json
72
+
73
+ print(json.dumps(result_data, indent=2))
74
+ else:
75
+ # Show human-readable report
76
+ report = result_data.get("report", "")
77
+ if report:
78
+ print(report)
79
+ else:
80
+ print("No report found for this task")
81
+
82
+ return 0
83
+
84
+ except FileNotFoundError as e:
85
+ print(f"Error: {e}", file=sys.stderr)
86
+ return 1
87
+
88
+
89
+ def cmd_analytics(args: argparse.Namespace) -> int:
90
+ """Show cost optimization analytics.
91
+
92
+ Args:
93
+ args: Parsed command line arguments
94
+
95
+ Returns:
96
+ Exit code (0 for success)
97
+ """
98
+ storage_path = args.storage_path or ".attune/progressive_runs"
99
+ analytics = generate_cost_analytics(storage_path)
100
+
101
+ if analytics["total_runs"] == 0:
102
+ print(f"No results found in {storage_path}")
103
+ return 0
104
+
105
+ if args.json:
106
+ import json
107
+
108
+ print(json.dumps(analytics, indent=2))
109
+ else:
110
+ report = format_cost_analytics_report(analytics)
111
+ print(report)
112
+
113
+ return 0
114
+
115
+
116
+ def cmd_cleanup(args: argparse.Namespace) -> int:
117
+ """Clean up old progressive workflow results.
118
+
119
+ Args:
120
+ args: Parsed command line arguments
121
+
122
+ Returns:
123
+ Exit code (0 for success)
124
+ """
125
+ storage_path = args.storage_path or ".attune/progressive_runs"
126
+ retention_days = args.retention_days
127
+ dry_run = args.dry_run
128
+
129
+ deleted, retained = cleanup_old_results(
130
+ storage_path=storage_path, retention_days=retention_days, dry_run=dry_run
131
+ )
132
+
133
+ if dry_run:
134
+ print("\n🔍 Dry run mode - no files deleted\n")
135
+ print(f"Would delete: {deleted} results older than {retention_days} days")
136
+ print(f"Would retain: {retained} recent results")
137
+ else:
138
+ print("\n🗑️ Cleanup complete\n")
139
+ print(f"Deleted: {deleted} results older than {retention_days} days")
140
+ print(f"Retained: {retained} recent results")
141
+
142
+ return 0
143
+
144
+
145
+ def create_parser() -> argparse.ArgumentParser:
146
+ """Create argument parser for progressive CLI.
147
+
148
+ Returns:
149
+ Configured argument parser
150
+ """
151
+ parser = argparse.ArgumentParser(
152
+ prog="empathy progressive", description="Manage progressive tier escalation workflows"
153
+ )
154
+
155
+ parser.add_argument(
156
+ "--storage-path",
157
+ type=str,
158
+ default=None,
159
+ help="Custom storage path (default: .attune/progressive_runs)",
160
+ )
161
+
162
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
163
+
164
+ # List command
165
+ list_parser = subparsers.add_parser("list", help="List all saved progressive workflow results")
166
+ list_parser.set_defaults(func=cmd_list_results)
167
+
168
+ # Show command
169
+ show_parser = subparsers.add_parser("show", help="Show detailed report for a specific task")
170
+ show_parser.add_argument("task_id", type=str, help="Task ID to display")
171
+ show_parser.add_argument("--json", action="store_true", help="Output in JSON format")
172
+ show_parser.set_defaults(func=cmd_show_report)
173
+
174
+ # Analytics command
175
+ analytics_parser = subparsers.add_parser("analytics", help="Show cost optimization analytics")
176
+ analytics_parser.add_argument("--json", action="store_true", help="Output in JSON format")
177
+ analytics_parser.set_defaults(func=cmd_analytics)
178
+
179
+ # Cleanup command
180
+ cleanup_parser = subparsers.add_parser(
181
+ "cleanup", help="Clean up old progressive workflow results"
182
+ )
183
+ cleanup_parser.add_argument(
184
+ "--retention-days",
185
+ type=int,
186
+ default=30,
187
+ help="Number of days to retain results (default: 30)",
188
+ )
189
+ cleanup_parser.add_argument(
190
+ "--dry-run",
191
+ action="store_true",
192
+ help="Show what would be deleted without actually deleting",
193
+ )
194
+ cleanup_parser.set_defaults(func=cmd_cleanup)
195
+
196
+ return parser
197
+
198
+
199
+ def main(argv: list[str] | None = None) -> int:
200
+ """Main entry point for progressive CLI.
201
+
202
+ Args:
203
+ argv: Command line arguments (defaults to sys.argv[1:])
204
+
205
+ Returns:
206
+ Exit code
207
+ """
208
+ parser = create_parser()
209
+ args = parser.parse_args(argv)
210
+
211
+ if not hasattr(args, "func"):
212
+ parser.print_help()
213
+ return 1
214
+
215
+ return args.func(args)
216
+
217
+
218
+ if __name__ == "__main__":
219
+ sys.exit(main())
@@ -0,0 +1,488 @@
1
+ """Core data structures for progressive tier escalation.
2
+
3
+ This module defines the fundamental data structures used throughout the
4
+ progressive escalation system, including failure analysis, quality metrics,
5
+ tier results, and configuration.
6
+ """
7
+
8
+ from dataclasses import dataclass, field
9
+ from datetime import datetime
10
+ from enum import Enum
11
+ from typing import Any
12
+
13
+
14
+ class Tier(Enum):
15
+ """Model tier levels for progressive escalation.
16
+
17
+ Attributes:
18
+ CHEAP: Low-cost models (e.g., gpt-4o-mini, claude-3-haiku)
19
+ CAPABLE: Mid-tier models (e.g., claude-3-5-sonnet, gpt-4o)
20
+ PREMIUM: High-end models (e.g., claude-opus-4, o1)
21
+ """
22
+
23
+ CHEAP = "cheap"
24
+ CAPABLE = "capable"
25
+ PREMIUM = "premium"
26
+
27
+ def __lt__(self, other: "Tier") -> bool:
28
+ """Compare tiers for ordering (CHEAP < CAPABLE < PREMIUM)."""
29
+ order = {Tier.CHEAP: 0, Tier.CAPABLE: 1, Tier.PREMIUM: 2}
30
+ return order[self] < order[other]
31
+
32
+
33
+ @dataclass
34
+ class FailureAnalysis:
35
+ """Multi-signal failure detection and quality analysis.
36
+
37
+ Combines multiple signals to provide robust failure detection:
38
+ 1. Syntax errors in generated code
39
+ 2. Execution failures (test pass rate)
40
+ 3. Quality metrics (coverage, assertion depth)
41
+ 4. LLM confidence signals
42
+
43
+ The composite quality score (CQS) provides an objective measure
44
+ that combines all signals with appropriate weighting.
45
+
46
+ Attributes:
47
+ syntax_errors: List of syntax errors found in generated code
48
+ test_failures: List of test execution failures
49
+ test_pass_rate: Percentage of tests that passed (0.0-1.0)
50
+ coverage_percent: Code coverage percentage (0.0-100.0)
51
+ assertion_depth: Average number of assertions per test
52
+ confidence_score: LLM confidence level (0.0-1.0)
53
+ llm_uncertainty_signals: Uncertainty phrases detected in LLM response
54
+
55
+ Example:
56
+ >>> analysis = FailureAnalysis(
57
+ ... test_pass_rate=0.85,
58
+ ... coverage_percent=78.0,
59
+ ... assertion_depth=5.2,
60
+ ... confidence_score=0.92
61
+ ... )
62
+ >>> analysis.calculate_quality_score()
63
+ 87.7
64
+ >>> analysis.should_escalate
65
+ False
66
+ """
67
+
68
+ syntax_errors: list[SyntaxError] = field(default_factory=list)
69
+ test_failures: list[dict[str, Any]] = field(default_factory=list)
70
+ test_pass_rate: float = 0.0
71
+ coverage_percent: float = 0.0
72
+ assertion_depth: float = 0.0
73
+ confidence_score: float = 0.0
74
+ llm_uncertainty_signals: list[str] = field(default_factory=list)
75
+
76
+ def calculate_quality_score(self) -> float:
77
+ """Calculate composite quality score (CQS) from 0-100.
78
+
79
+ Formula:
80
+ CQS = (
81
+ 0.40 × test_pass_rate +
82
+ 0.25 × code_coverage +
83
+ 0.20 × assertion_quality +
84
+ 0.15 × llm_confidence
85
+ ) × syntax_error_penalty
86
+
87
+ Weights:
88
+ - Test pass rate: 40% (most important - functionality must work)
89
+ - Code coverage: 25% (thoroughness matters)
90
+ - Assertion quality: 20% (test depth is important)
91
+ - LLM confidence: 15% (signals potential brittleness)
92
+
93
+ Penalties:
94
+ - Syntax errors: 50% penalty (halves the score)
95
+
96
+ Returns:
97
+ Quality score from 0.0 (worst) to 100.0 (perfect)
98
+
99
+ Example:
100
+ >>> analysis = FailureAnalysis(
101
+ ... test_pass_rate=0.90,
102
+ ... coverage_percent=85.0,
103
+ ... assertion_depth=6.0,
104
+ ... confidence_score=0.95
105
+ ... )
106
+ >>> analysis.calculate_quality_score()
107
+ 91.25
108
+ """
109
+ # Component scores (convert to 0-100 scale)
110
+ pass_rate_score = self.test_pass_rate * 100
111
+ coverage_score = self.coverage_percent
112
+
113
+ # Assertion quality: cap at 100% (10 assertions = 100%)
114
+ assertion_quality_score = min(self.assertion_depth * 10, 100)
115
+
116
+ confidence_score_scaled = self.confidence_score * 100
117
+
118
+ # Weighted composite
119
+ cqs = (
120
+ 0.40 * pass_rate_score
121
+ + 0.25 * coverage_score
122
+ + 0.20 * assertion_quality_score
123
+ + 0.15 * confidence_score_scaled
124
+ )
125
+
126
+ # Apply syntax error penalty
127
+ if len(self.syntax_errors) > 0:
128
+ cqs *= 0.5 # Halve score for any syntax errors
129
+
130
+ return min(cqs, 100.0)
131
+
132
+ @property
133
+ def should_escalate(self) -> bool:
134
+ """Determine if this result should trigger escalation.
135
+
136
+ Multi-criteria decision based on:
137
+ - Low CQS (<70)
138
+ - Multiple syntax errors (>3)
139
+ - Low test pass rate (<70%)
140
+ - Low coverage (<60%)
141
+
142
+ Returns:
143
+ True if escalation is recommended, False otherwise
144
+
145
+ Example:
146
+ >>> analysis = FailureAnalysis(test_pass_rate=0.50)
147
+ >>> analysis.should_escalate
148
+ True
149
+ """
150
+ cqs = self.calculate_quality_score()
151
+ return (
152
+ cqs < 70
153
+ or len(self.syntax_errors) > 3
154
+ or self.test_pass_rate < 0.7
155
+ or self.coverage_percent < 60
156
+ )
157
+
158
+ @property
159
+ def failure_severity(self) -> str:
160
+ """Determine severity level of failures.
161
+
162
+ Returns:
163
+ "CRITICAL": Severe failures, consider skipping to Premium
164
+ "HIGH": Significant failures, escalate to next tier
165
+ "MODERATE": Minor failures, retry at current tier
166
+ "LOW": Acceptable quality, no escalation needed
167
+
168
+ Example:
169
+ >>> analysis = FailureAnalysis(test_pass_rate=0.25)
170
+ >>> analysis.failure_severity
171
+ 'CRITICAL'
172
+ """
173
+ cqs = self.calculate_quality_score()
174
+
175
+ if len(self.syntax_errors) > 5 or self.test_pass_rate < 0.3:
176
+ return "CRITICAL"
177
+ elif cqs < 70 or self.test_pass_rate < 0.5:
178
+ return "HIGH"
179
+ elif cqs < 80 or self.test_pass_rate < 0.7:
180
+ return "MODERATE"
181
+ else:
182
+ return "LOW"
183
+
184
+
185
+ @dataclass
186
+ class TierResult:
187
+ """Results from a single tier execution attempt.
188
+
189
+ Captures all information about a tier's execution including
190
+ generated artifacts, quality analysis, cost, and escalation decision.
191
+
192
+ Attributes:
193
+ tier: Which tier executed (CHEAP, CAPABLE, or PREMIUM)
194
+ model: Specific model used (e.g., "gpt-4o-mini")
195
+ attempt: Attempt number at this tier (1-based)
196
+ timestamp: When this execution occurred
197
+ generated_items: Generated artifacts (tests, code, etc.)
198
+ failure_analysis: Quality and failure analysis
199
+ cost: Cost in USD for this execution
200
+ duration: Execution time in seconds
201
+ escalated: Whether this result triggered escalation
202
+ escalation_reason: Human-readable reason for escalation
203
+
204
+ Example:
205
+ >>> result = TierResult(
206
+ ... tier=Tier.CHEAP,
207
+ ... model="gpt-4o-mini",
208
+ ... attempt=1,
209
+ ... timestamp=datetime.now(),
210
+ ... generated_items=[{"code": "test_foo()"}],
211
+ ... failure_analysis=FailureAnalysis(test_pass_rate=0.65),
212
+ ... cost=0.15,
213
+ ... duration=12.5
214
+ ... )
215
+ >>> result.quality_score
216
+ 65.0
217
+ """
218
+
219
+ tier: Tier
220
+ model: str
221
+ attempt: int
222
+ timestamp: datetime
223
+
224
+ # Generated artifacts
225
+ generated_items: list[dict[str, Any]] = field(default_factory=list)
226
+
227
+ # Analysis
228
+ failure_analysis: FailureAnalysis = field(default_factory=FailureAnalysis)
229
+ cost: float = 0.0
230
+ duration: float = 0.0
231
+ tokens_used: dict[str, int] = field(default_factory=dict)
232
+
233
+ # Decision
234
+ escalated: bool = False
235
+ escalation_reason: str = ""
236
+
237
+ @property
238
+ def quality_score(self) -> float:
239
+ """Get composite quality score for this tier result.
240
+
241
+ Returns:
242
+ CQS from 0.0 to 100.0
243
+ """
244
+ return self.failure_analysis.calculate_quality_score()
245
+
246
+ @property
247
+ def success_count(self) -> int:
248
+ """Count of successfully generated items (CQS >= 80).
249
+
250
+ Returns:
251
+ Number of items meeting quality threshold
252
+ """
253
+ return sum(1 for item in self.generated_items if item.get("quality_score", 0) >= 80)
254
+
255
+ @property
256
+ def success_rate(self) -> float:
257
+ """Percentage of items successfully generated.
258
+
259
+ Returns:
260
+ Success rate from 0.0 to 1.0
261
+ """
262
+ if not self.generated_items:
263
+ return 0.0
264
+ return self.success_count / len(self.generated_items)
265
+
266
+
267
+ @dataclass
268
+ class ProgressiveWorkflowResult:
269
+ """Complete results from a progressive workflow execution.
270
+
271
+ Captures the full progression history across all tiers, including
272
+ costs, quality metrics, and escalation decisions.
273
+
274
+ Attributes:
275
+ workflow_name: Name of the workflow (e.g., "test-gen")
276
+ task_id: Unique identifier for this execution
277
+ tier_results: Chronological list of tier execution results
278
+ final_result: The last tier result (may be successful or failed)
279
+ total_cost: Total cost in USD across all tiers
280
+ total_duration: Total execution time in seconds
281
+ success: Whether the workflow completed successfully
282
+
283
+ Example:
284
+ >>> result = ProgressiveWorkflowResult(
285
+ ... workflow_name="test-gen",
286
+ ... task_id="test-gen-20260117-143022",
287
+ ... tier_results=[cheap_result, capable_result],
288
+ ... final_result=capable_result,
289
+ ... total_cost=0.75,
290
+ ... total_duration=45.2,
291
+ ... success=True
292
+ ... )
293
+ >>> print(result.generate_report())
294
+ 🎯 PROGRESSIVE ESCALATION REPORT
295
+ ...
296
+ """
297
+
298
+ workflow_name: str
299
+ task_id: str
300
+ tier_results: list[TierResult]
301
+
302
+ final_result: TierResult
303
+ total_cost: float
304
+ total_duration: float
305
+ success: bool
306
+
307
+ def generate_report(self) -> str:
308
+ """Generate human-readable progression report.
309
+
310
+ Creates a detailed report showing:
311
+ - Tier-by-tier breakdown
312
+ - Quality scores and success rates
313
+ - Cost analysis and savings
314
+ - Escalation decisions
315
+
316
+ Returns:
317
+ Formatted report string
318
+ """
319
+ # Implementation will be in reports.py module
320
+ from attune.workflows.progressive.reports import generate_progression_report
321
+
322
+ return generate_progression_report(self)
323
+
324
+ def save_to_disk(self, storage_path: str) -> None:
325
+ """Save detailed results to disk.
326
+
327
+ Creates a directory with:
328
+ - summary.json: High-level metrics
329
+ - tier_N_<tier_name>.json: Detailed tier results
330
+ - report.txt: Human-readable report
331
+
332
+ Args:
333
+ storage_path: Base path for saving results
334
+ """
335
+ from attune.workflows.progressive.reports import save_results_to_disk
336
+
337
+ save_results_to_disk(self, storage_path)
338
+
339
+ @property
340
+ def cost_savings(self) -> float:
341
+ """Calculate cost savings vs running all items at Premium tier.
342
+
343
+ Returns:
344
+ Dollar amount saved by using progressive escalation
345
+ """
346
+ # Estimate what it would cost if all items were Premium
347
+ total_items = sum(len(r.generated_items) for r in self.tier_results)
348
+
349
+ # Assume Premium costs ~$0.05 per item (conservative estimate)
350
+ all_premium_cost = total_items * 0.05
351
+
352
+ savings = all_premium_cost - self.total_cost
353
+ return max(savings, 0.0)
354
+
355
+ @property
356
+ def cost_savings_percent(self) -> float:
357
+ """Calculate percentage of cost saved.
358
+
359
+ Returns:
360
+ Savings percentage (0-100)
361
+ """
362
+ total_items = sum(len(r.generated_items) for r in self.tier_results)
363
+ all_premium_cost = total_items * 0.05
364
+
365
+ if all_premium_cost == 0:
366
+ return 0.0
367
+
368
+ return (self.cost_savings / all_premium_cost) * 100
369
+
370
+
371
+ @dataclass
372
+ class EscalationConfig:
373
+ """Configuration for progressive tier escalation.
374
+
375
+ Controls all aspects of the escalation system including retry logic,
376
+ thresholds, cost management, and storage.
377
+
378
+ Attributes:
379
+ enabled: Whether progressive escalation is active
380
+ tiers: Ordered list of tiers to use (default: all three)
381
+
382
+ Retry configuration:
383
+ cheap_min_attempts: Minimum attempts at cheap tier
384
+ cheap_max_attempts: Maximum attempts at cheap tier
385
+ capable_min_attempts: Minimum attempts at capable tier
386
+ capable_max_attempts: Maximum attempts at capable tier
387
+ premium_max_attempts: Maximum attempts at premium tier
388
+
389
+ Thresholds (Cheap → Capable):
390
+ cheap_to_capable_failure_rate: Max failure rate before escalation
391
+ cheap_to_capable_min_cqs: Min quality score to avoid escalation
392
+ cheap_to_capable_max_syntax_errors: Max syntax errors allowed
393
+
394
+ Thresholds (Capable → Premium):
395
+ capable_to_premium_failure_rate: Max failure rate before escalation
396
+ capable_to_premium_min_cqs: Min quality score to avoid escalation
397
+ capable_to_premium_max_syntax_errors: Max syntax errors allowed
398
+
399
+ Stagnation detection:
400
+ improvement_threshold: Min CQS improvement to avoid stagnation (%)
401
+ consecutive_stagnation_limit: Consecutive stagnations before escalation
402
+
403
+ Cost management:
404
+ max_cost: Maximum total cost in USD
405
+ auto_approve_under: Auto-approve escalations under this cost
406
+ warn_on_budget_exceeded: Print warning if budget exceeded
407
+ abort_on_budget_exceeded: Abort execution if budget exceeded
408
+
409
+ Storage:
410
+ save_tier_results: Whether to save tier results to disk
411
+ storage_path: Directory for saving results
412
+
413
+ Example:
414
+ >>> config = EscalationConfig(
415
+ ... enabled=True,
416
+ ... max_cost=10.00,
417
+ ... auto_approve_under=5.00,
418
+ ... cheap_min_attempts=2,
419
+ ... capable_max_attempts=6
420
+ ... )
421
+ """
422
+
423
+ # Global settings
424
+ enabled: bool = False
425
+ tiers: list[Tier] = field(default_factory=lambda: [Tier.CHEAP, Tier.CAPABLE, Tier.PREMIUM])
426
+
427
+ # Retry configuration
428
+ cheap_min_attempts: int = 2
429
+ cheap_max_attempts: int = 3
430
+ capable_min_attempts: int = 2
431
+ capable_max_attempts: int = 6
432
+ premium_max_attempts: int = 1
433
+
434
+ # Thresholds: Cheap → Capable
435
+ cheap_to_capable_failure_rate: float = 0.30
436
+ cheap_to_capable_min_cqs: float = 70.0
437
+ cheap_to_capable_max_syntax_errors: int = 3
438
+
439
+ # Thresholds: Capable → Premium
440
+ capable_to_premium_failure_rate: float = 0.20
441
+ capable_to_premium_min_cqs: float = 80.0
442
+ capable_to_premium_max_syntax_errors: int = 1
443
+
444
+ # Stagnation detection
445
+ improvement_threshold: float = 5.0 # 5% CQS improvement required
446
+ consecutive_stagnation_limit: int = 2
447
+
448
+ # Cost management
449
+ max_cost: float = 5.00
450
+ auto_approve_under: float | None = None
451
+ warn_on_budget_exceeded: bool = True
452
+ abort_on_budget_exceeded: bool = False
453
+
454
+ # Storage
455
+ save_tier_results: bool = True
456
+ storage_path: str = ".attune/progressive_runs"
457
+
458
+ def get_max_attempts(self, tier: Tier) -> int:
459
+ """Get maximum attempts for a specific tier.
460
+
461
+ Args:
462
+ tier: The tier to query
463
+
464
+ Returns:
465
+ Maximum number of attempts allowed
466
+ """
467
+ if tier == Tier.CHEAP:
468
+ return self.cheap_max_attempts
469
+ elif tier == Tier.CAPABLE:
470
+ return self.capable_max_attempts
471
+ else: # PREMIUM
472
+ return self.premium_max_attempts
473
+
474
+ def get_min_attempts(self, tier: Tier) -> int:
475
+ """Get minimum attempts for a specific tier.
476
+
477
+ Args:
478
+ tier: The tier to query
479
+
480
+ Returns:
481
+ Minimum number of attempts required
482
+ """
483
+ if tier == Tier.CHEAP:
484
+ return self.cheap_min_attempts
485
+ elif tier == Tier.CAPABLE:
486
+ return self.capable_min_attempts
487
+ else: # PREMIUM
488
+ return 1 # Premium always gets exactly 1 attempt