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,320 @@
1
+ """Resilient Agent Wrapper
2
+
3
+ Applies production-ready resilience patterns (circuit breaker, retry, timeout,
4
+ fallback) to any agent created by the Agent Factory.
5
+
6
+ Usage:
7
+ from attune_llm.agent_factory import AgentFactory
8
+ from attune_llm.agent_factory.resilient import ResilientAgent, ResilienceConfig
9
+
10
+ factory = AgentFactory()
11
+ agent = factory.create_agent(name="researcher", role="researcher")
12
+
13
+ # Wrap with resilience
14
+ resilient_agent = ResilientAgent(agent, ResilienceConfig(
15
+ enable_circuit_breaker=True,
16
+ failure_threshold=3,
17
+ enable_retry=True,
18
+ max_attempts=3
19
+ ))
20
+
21
+ result = await resilient_agent.invoke("Research AI trends")
22
+
23
+ Copyright 2025 Smart-AI-Memory
24
+ Licensed under Fair Source License 0.9
25
+ """
26
+
27
+ import asyncio
28
+ import functools
29
+ import logging
30
+ from dataclasses import dataclass, field
31
+ from typing import Any
32
+
33
+ from attune_llm.agent_factory.base import AgentConfig, BaseAgent
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ @dataclass
39
+ class ResilienceConfig:
40
+ """Configuration for resilience patterns."""
41
+
42
+ # Circuit Breaker
43
+ enable_circuit_breaker: bool = True
44
+ failure_threshold: int = 3
45
+ reset_timeout: float = 60.0
46
+ half_open_max_calls: int = 3
47
+
48
+ # Retry
49
+ enable_retry: bool = True
50
+ max_attempts: int = 2
51
+ initial_delay: float = 1.0
52
+ backoff_factor: float = 2.0
53
+ max_delay: float = 30.0
54
+ jitter: bool = True
55
+
56
+ # Timeout
57
+ enable_timeout: bool = True
58
+ timeout_seconds: float = 30.0
59
+
60
+ # Fallback
61
+ enable_fallback: bool = False
62
+ fallback_value: Any = field(
63
+ default_factory=lambda: {
64
+ "output": "Service temporarily unavailable",
65
+ "metadata": {"fallback": True},
66
+ },
67
+ )
68
+
69
+ @classmethod
70
+ def from_agent_config(cls, config: AgentConfig) -> "ResilienceConfig":
71
+ """Create ResilienceConfig from AgentConfig resilience fields."""
72
+ return cls(
73
+ enable_circuit_breaker=getattr(config, "resilience_enabled", False),
74
+ failure_threshold=getattr(config, "circuit_breaker_threshold", 3),
75
+ enable_retry=getattr(config, "resilience_enabled", False),
76
+ max_attempts=getattr(config, "retry_max_attempts", 2),
77
+ enable_timeout=getattr(config, "resilience_enabled", False),
78
+ timeout_seconds=getattr(config, "timeout_seconds", 30.0),
79
+ )
80
+
81
+
82
+ class ResilientAgent(BaseAgent):
83
+ """Agent wrapper that applies resilience patterns.
84
+
85
+ Wraps any BaseAgent implementation with:
86
+ - Circuit breaker: Prevents cascading failures
87
+ - Retry with backoff: Handles transient errors
88
+ - Timeout: Prevents hanging operations
89
+ - Fallback: Graceful degradation
90
+
91
+ The wrapper preserves the underlying agent's interface while adding
92
+ fault tolerance capabilities.
93
+ """
94
+
95
+ def __init__(self, agent: BaseAgent, config: ResilienceConfig | None = None):
96
+ """Initialize resilient agent wrapper.
97
+
98
+ Args:
99
+ agent: The underlying agent to wrap
100
+ config: Resilience configuration (uses defaults if not provided)
101
+
102
+ """
103
+ # Initialize with wrapped agent's config
104
+ super().__init__(agent.config)
105
+ self._wrapped = agent
106
+ self._resilience_config = config or ResilienceConfig()
107
+ self._circuit_breaker: Any = None
108
+ self._setup_resilience()
109
+
110
+ def _setup_resilience(self) -> None:
111
+ """Set up resilience decorators based on config."""
112
+ config = self._resilience_config
113
+
114
+ # Set up circuit breaker if enabled
115
+ if config.enable_circuit_breaker:
116
+ try:
117
+ from attune.resilience import CircuitBreaker
118
+
119
+ self._circuit_breaker = CircuitBreaker(
120
+ name=f"agent_{self.name}",
121
+ failure_threshold=config.failure_threshold,
122
+ reset_timeout=config.reset_timeout,
123
+ half_open_max_calls=config.half_open_max_calls,
124
+ )
125
+ except ImportError:
126
+ logger.warning("attune.resilience not available, circuit breaker disabled")
127
+
128
+ async def invoke(self, input_data: str | dict, context: dict | None = None) -> dict:
129
+ """Invoke the agent with resilience patterns applied.
130
+
131
+ Args:
132
+ input_data: User input or structured data
133
+ context: Optional context (previous results, shared state)
134
+
135
+ Returns:
136
+ Dict with at least {"output": str, "metadata": dict}
137
+
138
+ Raises:
139
+ CircuitOpenError: If circuit breaker is open
140
+ asyncio.TimeoutError: If operation times out and no fallback
141
+ Exception: If all retries exhausted and no fallback
142
+
143
+ """
144
+ config = self._resilience_config
145
+
146
+ # Build the resilient call chain
147
+ async def _call():
148
+ return await self._wrapped.invoke(input_data, context)
149
+
150
+ # Apply timeout
151
+ if config.enable_timeout:
152
+ _call = self._with_timeout(_call, config.timeout_seconds)
153
+
154
+ # Apply retry
155
+ if config.enable_retry:
156
+ _call = self._with_retry(
157
+ _call,
158
+ config.max_attempts,
159
+ config.initial_delay,
160
+ config.backoff_factor,
161
+ config.max_delay,
162
+ config.jitter,
163
+ )
164
+
165
+ # Apply circuit breaker
166
+ if config.enable_circuit_breaker and self._circuit_breaker:
167
+ _call = self._with_circuit_breaker(_call)
168
+
169
+ # Execute with optional fallback
170
+ try:
171
+ result = await _call()
172
+ # Add resilience metadata
173
+ if "metadata" in result:
174
+ result["metadata"]["resilience"] = {
175
+ "circuit_breaker_enabled": config.enable_circuit_breaker,
176
+ "retry_enabled": config.enable_retry,
177
+ "timeout_enabled": config.enable_timeout,
178
+ }
179
+ return dict(result)
180
+ except Exception as e:
181
+ if config.enable_fallback:
182
+ logger.warning(f"Agent {self.name} failed, using fallback: {e}")
183
+ fallback = config.fallback_value
184
+ if callable(fallback):
185
+ return dict(fallback(input_data, context, e))
186
+ return dict(fallback) if isinstance(fallback, dict) else {"output": fallback}
187
+ raise
188
+
189
+ async def stream(self, input_data: str | dict, context: dict | None = None):
190
+ """Stream agent response with resilience patterns.
191
+
192
+ Note: Streaming has limited resilience support (timeout only).
193
+ Circuit breaker and retry work at the full response level.
194
+ """
195
+ config = self._resilience_config
196
+
197
+ async def _stream():
198
+ async for chunk in self._wrapped.stream(input_data, context):
199
+ yield chunk
200
+
201
+ # Apply timeout to entire stream
202
+ if config.enable_timeout:
203
+ try:
204
+ async with asyncio.timeout(config.timeout_seconds): # type: ignore[attr-defined]
205
+ async for chunk in _stream():
206
+ yield chunk
207
+ except asyncio.TimeoutError:
208
+ if config.enable_fallback:
209
+ yield {"output": "Stream timed out", "metadata": {"fallback": True}}
210
+ else:
211
+ raise
212
+ else:
213
+ async for chunk in _stream():
214
+ yield chunk
215
+
216
+ def _with_timeout(self, func, timeout_seconds: float):
217
+ """Wrap function with timeout."""
218
+
219
+ @functools.wraps(func)
220
+ async def wrapper():
221
+ return await asyncio.wait_for(func(), timeout=timeout_seconds)
222
+
223
+ return wrapper
224
+
225
+ def _with_retry(
226
+ self,
227
+ func,
228
+ max_attempts: int,
229
+ initial_delay: float,
230
+ backoff_factor: float,
231
+ max_delay: float,
232
+ jitter: bool,
233
+ ):
234
+ """Wrap function with retry logic."""
235
+ import random
236
+
237
+ @functools.wraps(func)
238
+ async def wrapper():
239
+ last_exception = None
240
+ delay = initial_delay
241
+
242
+ for attempt in range(max_attempts):
243
+ try:
244
+ return await func()
245
+ except asyncio.TimeoutError:
246
+ # Don't retry timeouts by default
247
+ raise
248
+ except Exception as e:
249
+ last_exception = e
250
+ if attempt < max_attempts - 1:
251
+ actual_delay = delay
252
+ if jitter:
253
+ actual_delay = delay * (0.5 + random.random())
254
+ actual_delay = min(actual_delay, max_delay)
255
+ logger.debug(
256
+ f"Agent {self.name} attempt {attempt + 1} failed, "
257
+ f"retrying in {actual_delay:.2f}s: {e}",
258
+ )
259
+ await asyncio.sleep(actual_delay)
260
+ delay = min(delay * backoff_factor, max_delay)
261
+
262
+ raise last_exception
263
+
264
+ return wrapper
265
+
266
+ def _with_circuit_breaker(self, func):
267
+ """Wrap function with circuit breaker."""
268
+
269
+ @functools.wraps(func)
270
+ async def wrapper():
271
+ from attune.resilience import CircuitOpenError
272
+
273
+ # Check if circuit is open (failing fast)
274
+ if self._circuit_breaker.is_open:
275
+ reset_time = self._circuit_breaker.get_time_until_reset()
276
+ raise CircuitOpenError(
277
+ name=f"agent_{self.name}",
278
+ reset_time=reset_time,
279
+ )
280
+
281
+ try:
282
+ result = await func()
283
+ self._circuit_breaker.record_success()
284
+ return result
285
+ except Exception as e:
286
+ self._circuit_breaker.record_failure(e)
287
+ raise
288
+
289
+ return wrapper
290
+
291
+ # Delegate other methods to wrapped agent
292
+ def add_tool(self, tool: Any) -> None:
293
+ """Add a tool to the wrapped agent."""
294
+ self._wrapped.add_tool(tool)
295
+
296
+ def get_conversation_history(self) -> list[dict]:
297
+ """Get conversation history from wrapped agent."""
298
+ return self._wrapped.get_conversation_history()
299
+
300
+ def clear_history(self) -> None:
301
+ """Clear conversation history in wrapped agent."""
302
+ self._wrapped.clear_history()
303
+
304
+ @property
305
+ def model(self) -> str:
306
+ """Get the model being used by wrapped agent."""
307
+ return self._wrapped.model
308
+
309
+ @property
310
+ def circuit_state(self) -> str | None:
311
+ """Get current circuit breaker state."""
312
+ if self._circuit_breaker:
313
+ return str(self._circuit_breaker.state.value)
314
+ return None
315
+
316
+ def reset_circuit_breaker(self) -> None:
317
+ """Manually reset the circuit breaker."""
318
+ if self._circuit_breaker:
319
+ self._circuit_breaker.reset()
320
+ logger.info(f"Circuit breaker reset for agent {self.name}")
@@ -0,0 +1,22 @@
1
+ """Markdown Agent System
2
+
3
+ Define agents in Markdown files with YAML frontmatter for portability.
4
+ Integrates with Empathy Framework's UnifiedAgentConfig and model tier system.
5
+
6
+ Markdown agent format inspired by everything-claude-code by Affaan Mustafa.
7
+ See: https://github.com/affaan-m/everything-claude-code (MIT License)
8
+ See: ACKNOWLEDGMENTS.md for full attribution.
9
+
10
+ Copyright 2025 Smart-AI-Memory
11
+ Licensed under Fair Source License 0.9
12
+ """
13
+
14
+ from attune_llm.agents_md.loader import AgentLoader
15
+ from attune_llm.agents_md.parser import MarkdownAgentParser
16
+ from attune_llm.agents_md.registry import AgentRegistry
17
+
18
+ __all__ = [
19
+ "MarkdownAgentParser",
20
+ "AgentLoader",
21
+ "AgentRegistry",
22
+ ]
@@ -0,0 +1,218 @@
1
+ """Agent Loader
2
+
3
+ Loads agents from directory structures.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ import logging
10
+ from collections.abc import Iterator
11
+ from pathlib import Path
12
+
13
+ from attune_llm.agents_md.parser import MarkdownAgentParser
14
+ from attune_llm.config.unified import UnifiedAgentConfig
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class AgentLoader:
20
+ """Loader for discovering and loading markdown agent files.
21
+
22
+ Scans directories for .md files with agent definitions and loads them
23
+ into UnifiedAgentConfig instances.
24
+
25
+ Example:
26
+ loader = AgentLoader()
27
+
28
+ # Load a single agent
29
+ config = loader.load("agents/architect.md")
30
+
31
+ # Load all agents from a directory
32
+ agents = loader.load_directory("agents/")
33
+
34
+ # Discover and iterate agents lazily
35
+ for config in loader.discover("agents/"):
36
+ print(config.name)
37
+ """
38
+
39
+ def __init__(self, parser: MarkdownAgentParser | None = None):
40
+ """Initialize the loader.
41
+
42
+ Args:
43
+ parser: Optional custom parser instance
44
+
45
+ """
46
+ self.parser = parser or MarkdownAgentParser()
47
+
48
+ def load(self, file_path: str | Path) -> UnifiedAgentConfig:
49
+ """Load a single agent file.
50
+
51
+ Args:
52
+ file_path: Path to the agent markdown file
53
+
54
+ Returns:
55
+ UnifiedAgentConfig instance
56
+
57
+ """
58
+ return self.parser.parse_file(file_path)
59
+
60
+ def load_directory(
61
+ self,
62
+ directory: str | Path,
63
+ recursive: bool = False,
64
+ ) -> dict[str, UnifiedAgentConfig]:
65
+ """Load all agents from a directory.
66
+
67
+ Args:
68
+ directory: Directory to scan for .md files
69
+ recursive: If True, scan subdirectories
70
+
71
+ Returns:
72
+ Dictionary mapping agent names to configs
73
+
74
+ """
75
+ agents = {}
76
+
77
+ for config in self.discover(directory, recursive=recursive):
78
+ if config.name in agents:
79
+ logger.warning(
80
+ "Duplicate agent name '%s' - keeping first occurrence",
81
+ config.name,
82
+ )
83
+ continue
84
+ agents[config.name] = config
85
+
86
+ logger.info("Loaded %d agent(s) from %s", len(agents), directory)
87
+ return agents
88
+
89
+ def discover(
90
+ self,
91
+ directory: str | Path,
92
+ recursive: bool = False,
93
+ ) -> Iterator[UnifiedAgentConfig]:
94
+ """Discover and yield agents from a directory.
95
+
96
+ Args:
97
+ directory: Directory to scan
98
+ recursive: If True, scan subdirectories
99
+
100
+ Yields:
101
+ UnifiedAgentConfig instances
102
+
103
+ """
104
+ directory = Path(directory)
105
+
106
+ if not directory.exists():
107
+ logger.warning("Agent directory not found: %s", directory)
108
+ return
109
+
110
+ if not directory.is_dir():
111
+ raise ValueError(f"Not a directory: {directory}")
112
+
113
+ # Get pattern for globbing
114
+ pattern = "**/*.md" if recursive else "*.md"
115
+
116
+ for file_path in sorted(directory.glob(pattern)):
117
+ if not file_path.is_file():
118
+ continue
119
+
120
+ # Skip files that don't look like agent definitions
121
+ if file_path.name.startswith("_"):
122
+ continue
123
+ if file_path.name.upper() in ("README.MD", "CHANGELOG.MD"):
124
+ continue
125
+
126
+ try:
127
+ config = self.parser.parse_file(file_path)
128
+ yield config
129
+ except ValueError as e:
130
+ logger.warning("Skipping invalid agent file %s: %s", file_path, e)
131
+ except Exception as e:
132
+ logger.error("Error loading agent file %s: %s", file_path, e)
133
+
134
+ def validate_directory(
135
+ self,
136
+ directory: str | Path,
137
+ recursive: bool = False,
138
+ ) -> dict[str, list[str]]:
139
+ """Validate all agent files in a directory.
140
+
141
+ Args:
142
+ directory: Directory to validate
143
+ recursive: If True, scan subdirectories
144
+
145
+ Returns:
146
+ Dictionary mapping file paths to lists of errors
147
+
148
+ """
149
+ directory = Path(directory)
150
+ results = {}
151
+
152
+ pattern = "**/*.md" if recursive else "*.md"
153
+
154
+ for file_path in sorted(directory.glob(pattern)):
155
+ if not file_path.is_file():
156
+ continue
157
+ if file_path.name.startswith("_"):
158
+ continue
159
+ if file_path.name.upper() in ("README.MD", "CHANGELOG.MD"):
160
+ continue
161
+
162
+ errors = self.parser.validate_file(file_path)
163
+ if errors:
164
+ results[str(file_path)] = errors
165
+
166
+ return results
167
+
168
+ def get_agent_names(
169
+ self,
170
+ directory: str | Path,
171
+ recursive: bool = False,
172
+ ) -> list[str]:
173
+ """Get list of agent names in a directory without fully loading.
174
+
175
+ Args:
176
+ directory: Directory to scan
177
+ recursive: If True, scan subdirectories
178
+
179
+ Returns:
180
+ List of agent names
181
+
182
+ """
183
+ names = []
184
+ for config in self.discover(directory, recursive=recursive):
185
+ names.append(config.name)
186
+ return names
187
+
188
+
189
+ def load_agents_from_paths(
190
+ paths: list[str | Path],
191
+ parser: MarkdownAgentParser | None = None,
192
+ ) -> dict[str, UnifiedAgentConfig]:
193
+ """Load agents from multiple paths (files or directories).
194
+
195
+ Args:
196
+ paths: List of file or directory paths
197
+ parser: Optional custom parser
198
+
199
+ Returns:
200
+ Dictionary mapping agent names to configs
201
+
202
+ """
203
+ loader = AgentLoader(parser=parser)
204
+ agents = {}
205
+
206
+ for path in paths:
207
+ path = Path(path)
208
+
209
+ if path.is_file():
210
+ config = loader.load(path)
211
+ agents[config.name] = config
212
+ elif path.is_dir():
213
+ dir_agents = loader.load_directory(path)
214
+ agents.update(dir_agents)
215
+ else:
216
+ logger.warning("Path not found: %s", path)
217
+
218
+ return agents