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,367 @@
1
+ """Agent Heartbeat Tracking System.
2
+
3
+ Pattern 1 from Agent Coordination Architecture - TTL-based heartbeat monitoring
4
+ for tracking agent execution status and detecting stale/failed agents.
5
+
6
+ Usage:
7
+ # Start tracking an agent
8
+ coordinator = HeartbeatCoordinator()
9
+ coordinator.start_heartbeat(
10
+ agent_id="code-review-abc123",
11
+ metadata={"workflow": "code-review", "run_id": "xyz"}
12
+ )
13
+
14
+ # Update progress
15
+ coordinator.beat(
16
+ status="running",
17
+ progress=0.5,
18
+ current_task="Analyzing functions"
19
+ )
20
+
21
+ # Stop tracking
22
+ coordinator.stop_heartbeat(final_status="completed")
23
+
24
+ # Monitor all active agents
25
+ active_agents = coordinator.get_active_agents()
26
+ for agent in active_agents:
27
+ print(f"{agent.agent_id}: {agent.status} - {agent.current_task}")
28
+
29
+ Copyright 2025 Smart-AI-Memory
30
+ Licensed under Fair Source License 0.9
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import logging
36
+ from dataclasses import dataclass, field
37
+ from datetime import datetime
38
+ from typing import Any
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+
43
+ @dataclass
44
+ class AgentHeartbeat:
45
+ """Agent heartbeat data structure.
46
+
47
+ Represents the current state of a running agent, stored in Redis with TTL.
48
+ """
49
+
50
+ agent_id: str
51
+ status: str # "starting", "running", "completed", "failed", "cancelled"
52
+ progress: float # 0.0 to 1.0
53
+ current_task: str
54
+ last_beat: datetime
55
+ metadata: dict[str, Any] = field(default_factory=dict)
56
+ display_name: str | None = None # Optional human-readable name for dashboard
57
+
58
+ def to_dict(self) -> dict[str, Any]:
59
+ """Convert to dictionary for serialization."""
60
+ return {
61
+ "agent_id": self.agent_id,
62
+ "status": self.status,
63
+ "progress": self.progress,
64
+ "current_task": self.current_task,
65
+ "last_beat": self.last_beat.isoformat() if isinstance(self.last_beat, datetime) else self.last_beat,
66
+ "metadata": self.metadata,
67
+ "display_name": self.display_name,
68
+ }
69
+
70
+ @classmethod
71
+ def from_dict(cls, data: dict[str, Any]) -> AgentHeartbeat:
72
+ """Create from dictionary."""
73
+ # Convert ISO string back to datetime
74
+ last_beat = data.get("last_beat")
75
+ if isinstance(last_beat, str):
76
+ last_beat = datetime.fromisoformat(last_beat)
77
+ elif not isinstance(last_beat, datetime):
78
+ last_beat = datetime.utcnow()
79
+
80
+ return cls(
81
+ agent_id=data["agent_id"],
82
+ status=data["status"],
83
+ progress=data["progress"],
84
+ current_task=data["current_task"],
85
+ last_beat=last_beat,
86
+ metadata=data.get("metadata", {}),
87
+ display_name=data.get("display_name"),
88
+ )
89
+
90
+
91
+ class HeartbeatCoordinator:
92
+ """Coordinates agent heartbeats using Redis TTL keys.
93
+
94
+ Agents publish heartbeats with a TTL. If an agent stops responding,
95
+ its heartbeat key expires automatically, indicating failure/crash.
96
+
97
+ Attributes:
98
+ HEARTBEAT_TTL: Default heartbeat TTL in seconds (30s)
99
+ HEARTBEAT_INTERVAL: Recommended update interval (10s)
100
+ """
101
+
102
+ HEARTBEAT_TTL = 30 # Heartbeat expires after 30s of no updates
103
+ HEARTBEAT_INTERVAL = 10 # Agents should update every 10s
104
+
105
+ def __init__(self, memory=None, enable_streaming: bool = False):
106
+ """Initialize heartbeat coordinator.
107
+
108
+ Args:
109
+ memory: Memory instance (UnifiedMemory or ShortTermMemory).
110
+ If None, attempts to get from UsageTracker.
111
+ enable_streaming: If True, publish heartbeat events to Redis Streams
112
+ for real-time monitoring (Pattern 4).
113
+ """
114
+ self.memory = memory
115
+ self.agent_id: str | None = None
116
+ self.display_name: str | None = None
117
+ self._enable_streaming = enable_streaming
118
+ self._event_streamer = None
119
+
120
+ if self.memory is None:
121
+ try:
122
+ from attune.telemetry import UsageTracker
123
+
124
+ tracker = UsageTracker.get_instance()
125
+ if hasattr(tracker, "_memory"):
126
+ self.memory = tracker._memory
127
+ except (ImportError, AttributeError):
128
+ pass
129
+
130
+ if self.memory is None:
131
+ logger.warning("No memory backend available for heartbeat tracking")
132
+
133
+ def _get_event_streamer(self):
134
+ """Get or create EventStreamer instance (lazy initialization)."""
135
+ if not self._enable_streaming:
136
+ return None
137
+
138
+ if self._event_streamer is None:
139
+ try:
140
+ from attune.telemetry.event_streaming import EventStreamer
141
+
142
+ self._event_streamer = EventStreamer(memory=self.memory)
143
+ except Exception as e:
144
+ logger.warning(f"Failed to initialize EventStreamer: {e}")
145
+ self._enable_streaming = False
146
+
147
+ return self._event_streamer
148
+
149
+ def start_heartbeat(
150
+ self, agent_id: str, metadata: dict[str, Any] | None = None, display_name: str | None = None
151
+ ) -> None:
152
+ """Start heartbeat for an agent.
153
+
154
+ Args:
155
+ agent_id: Unique agent identifier
156
+ metadata: Initial metadata (workflow, run_id, etc.)
157
+ display_name: Optional human-readable name for dashboard display
158
+ """
159
+ if not self.memory:
160
+ logger.debug("Heartbeat tracking disabled (no memory backend)")
161
+ return
162
+
163
+ self.agent_id = agent_id
164
+ self.display_name = display_name
165
+ self._publish_heartbeat(
166
+ status="starting",
167
+ progress=0.0,
168
+ current_task="initializing",
169
+ metadata=metadata or {},
170
+ display_name=display_name,
171
+ )
172
+
173
+ def beat(self, status: str = "running", progress: float = 0.0, current_task: str = "") -> None:
174
+ """Publish heartbeat update.
175
+
176
+ Args:
177
+ status: Current agent status
178
+ progress: Progress percentage (0.0 - 1.0)
179
+ current_task: Human-readable current task description
180
+ """
181
+ if not self.agent_id or not self.memory:
182
+ return
183
+
184
+ self._publish_heartbeat(
185
+ status=status,
186
+ progress=progress,
187
+ current_task=current_task,
188
+ metadata={},
189
+ display_name=self.display_name,
190
+ )
191
+
192
+ def stop_heartbeat(self, final_status: str = "completed") -> None:
193
+ """Stop heartbeat (agent finished).
194
+
195
+ Args:
196
+ final_status: Final status ("completed", "failed", "cancelled")
197
+ """
198
+ if not self.agent_id or not self.memory:
199
+ return
200
+
201
+ # Publish final heartbeat with short TTL
202
+ self._publish_heartbeat(
203
+ status=final_status,
204
+ progress=1.0,
205
+ current_task="finished",
206
+ metadata={"final": True},
207
+ )
208
+
209
+ # Clear agent ID
210
+ self.agent_id = None
211
+
212
+ def _publish_heartbeat(
213
+ self, status: str, progress: float, current_task: str, metadata: dict[str, Any], display_name: str | None = None
214
+ ) -> None:
215
+ """Publish heartbeat to Redis with TTL and optionally to event stream."""
216
+ if not self.memory or not self.agent_id:
217
+ return
218
+
219
+ heartbeat = AgentHeartbeat(
220
+ agent_id=self.agent_id,
221
+ status=status,
222
+ progress=progress,
223
+ current_task=current_task,
224
+ last_beat=datetime.utcnow(),
225
+ metadata=metadata,
226
+ display_name=display_name,
227
+ )
228
+
229
+ # Store in Redis with TTL (Pattern 1)
230
+ key = f"empathy:heartbeat:{self.agent_id}"
231
+ try:
232
+ # Use direct Redis access for heartbeats (need custom 30s TTL)
233
+ if hasattr(self.memory, "_client") and self.memory._client:
234
+ # Direct Redis access with setex for custom TTL
235
+ import json
236
+
237
+ self.memory._client.setex(key, self.HEARTBEAT_TTL, json.dumps(heartbeat.to_dict()))
238
+ else:
239
+ logger.warning("Cannot publish heartbeat: no Redis backend available")
240
+ except Exception as e:
241
+ logger.warning(f"Failed to publish heartbeat for {self.agent_id}: {e}")
242
+
243
+ # Publish to event stream (Pattern 4 - optional)
244
+ streamer = self._get_event_streamer()
245
+ if streamer:
246
+ try:
247
+ streamer.publish_event(
248
+ event_type="agent_heartbeat",
249
+ data=heartbeat.to_dict(),
250
+ source="attune",
251
+ )
252
+ except Exception as e:
253
+ logger.debug(f"Failed to publish heartbeat event to stream: {e}")
254
+
255
+ def get_active_agents(self) -> list[AgentHeartbeat]:
256
+ """Get all currently active agents.
257
+
258
+ Returns:
259
+ List of active agent heartbeats
260
+ """
261
+ if not self.memory:
262
+ return []
263
+
264
+ try:
265
+ # Scan for empathy:heartbeat:* keys
266
+ if hasattr(self.memory, "_client") and self.memory._client:
267
+ keys = self.memory._client.keys("empathy:heartbeat:*")
268
+ else:
269
+ logger.warning("Cannot scan for heartbeats: no Redis access")
270
+ return []
271
+
272
+ heartbeats = []
273
+ for key in keys:
274
+ if isinstance(key, bytes):
275
+ key = key.decode("utf-8")
276
+
277
+ data = self._retrieve_heartbeat(key)
278
+ if data:
279
+ heartbeats.append(AgentHeartbeat.from_dict(data))
280
+
281
+ return heartbeats
282
+ except Exception as e:
283
+ logger.error(f"Failed to get active agents: {e}")
284
+ return []
285
+
286
+ def is_agent_alive(self, agent_id: str) -> bool:
287
+ """Check if agent is still alive.
288
+
289
+ Args:
290
+ agent_id: Agent to check
291
+
292
+ Returns:
293
+ True if heartbeat key exists (agent is alive)
294
+ """
295
+ if not self.memory:
296
+ return False
297
+
298
+ key = f"heartbeat:{agent_id}"
299
+ data = self._retrieve_heartbeat(key)
300
+ return data is not None
301
+
302
+ def get_agent_status(self, agent_id: str) -> AgentHeartbeat | None:
303
+ """Get current status of an agent.
304
+
305
+ Args:
306
+ agent_id: Agent to query
307
+
308
+ Returns:
309
+ AgentHeartbeat or None if agent not active
310
+ """
311
+ if not self.memory:
312
+ return None
313
+
314
+ key = f"heartbeat:{agent_id}"
315
+ data = self._retrieve_heartbeat(key)
316
+
317
+ if data:
318
+ return AgentHeartbeat.from_dict(data)
319
+ return None
320
+
321
+ def _retrieve_heartbeat(self, key: str) -> dict[str, Any] | None:
322
+ """Retrieve heartbeat data from memory.
323
+
324
+ Heartbeat keys are stored directly as 'heartbeat:{agent_id}' and must be
325
+ retrieved via direct Redis access, not through the standard retrieve() method
326
+ which expects keys with 'working:{agent_id}:{key}' format.
327
+ """
328
+ if not self.memory:
329
+ return None
330
+
331
+ try:
332
+ # Use direct Redis access for heartbeat keys
333
+ if hasattr(self.memory, "_client") and self.memory._client:
334
+ import json
335
+
336
+ data = self.memory._client.get(key)
337
+ if data:
338
+ if isinstance(data, bytes):
339
+ data = data.decode("utf-8")
340
+ result = json.loads(data)
341
+ return result if isinstance(result, dict) else None
342
+ return None
343
+ except Exception as e:
344
+ logger.debug(f"Failed to retrieve heartbeat {key}: {e}")
345
+ return None
346
+
347
+ def get_stale_agents(self, threshold_seconds: float = 60.0) -> list[AgentHeartbeat]:
348
+ """Get agents that haven't updated in a while (but key still exists).
349
+
350
+ This detects agents that are stuck or slow, not crashed (TTL would expire).
351
+
352
+ Args:
353
+ threshold_seconds: Time without update to consider stale
354
+
355
+ Returns:
356
+ List of stale agent heartbeats
357
+ """
358
+ active = self.get_active_agents()
359
+ now = datetime.utcnow()
360
+ stale = []
361
+
362
+ for agent in active:
363
+ time_since_beat = (now - agent.last_beat).total_seconds()
364
+ if time_since_beat > threshold_seconds and agent.status not in ("completed", "failed", "cancelled"):
365
+ stale.append(agent)
366
+
367
+ return stale