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