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,594 @@
1
+ """Telemetry analytics and reporting.
2
+
3
+ Analytics functions for telemetry data analysis.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ import heapq
10
+ from datetime import datetime
11
+ from typing import Any
12
+
13
+ from .storage import TelemetryStore
14
+
15
+
16
+ class TelemetryAnalytics:
17
+ """Analytics helpers for telemetry data.
18
+
19
+ Provides insights into cost optimization, provider usage, and performance.
20
+ """
21
+
22
+ def __init__(self, store: TelemetryStore | None = None):
23
+ """Initialize analytics.
24
+
25
+ Args:
26
+ store: TelemetryStore to analyze (creates default if None)
27
+
28
+ """
29
+ self.store = store or TelemetryStore()
30
+
31
+ def top_expensive_workflows(
32
+ self,
33
+ n: int = 10,
34
+ since: datetime | None = None,
35
+ ) -> list[dict[str, Any]]:
36
+ """Get the most expensive workflows.
37
+
38
+ Args:
39
+ n: Number of workflows to return
40
+ since: Only consider workflows after this time
41
+
42
+ Returns:
43
+ List of dicts with workflow_name, total_cost, run_count
44
+
45
+ """
46
+ workflows = self.store.get_workflows(since=since, limit=10000)
47
+
48
+ # Aggregate by workflow name
49
+ costs: dict[str, dict[str, Any]] = {}
50
+ for wf in workflows:
51
+ if wf.workflow_name not in costs:
52
+ costs[wf.workflow_name] = {
53
+ "workflow_name": wf.workflow_name,
54
+ "total_cost": 0.0,
55
+ "run_count": 0,
56
+ "total_savings": 0.0,
57
+ "avg_duration_ms": 0,
58
+ }
59
+ costs[wf.workflow_name]["total_cost"] += wf.total_cost
60
+ costs[wf.workflow_name]["run_count"] += 1
61
+ costs[wf.workflow_name]["total_savings"] += wf.savings
62
+
63
+ # Calculate averages and sort
64
+ result = list(costs.values())
65
+ for item in result:
66
+ if item["run_count"] > 0:
67
+ item["avg_cost"] = item["total_cost"] / item["run_count"]
68
+
69
+ result.sort(key=lambda x: x["total_cost"], reverse=True)
70
+ return result[:n]
71
+
72
+ def provider_usage_summary(
73
+ self,
74
+ since: datetime | None = None,
75
+ ) -> dict[str, dict[str, Any]]:
76
+ """Get usage summary by provider.
77
+
78
+ Args:
79
+ since: Only consider calls after this time
80
+
81
+ Returns:
82
+ Dict mapping provider to usage stats
83
+
84
+ """
85
+ calls = self.store.get_calls(since=since, limit=100000)
86
+
87
+ summary: dict[str, dict[str, Any]] = {}
88
+ for call in calls:
89
+ if call.provider not in summary:
90
+ summary[call.provider] = {
91
+ "call_count": 0,
92
+ "total_tokens": 0,
93
+ "total_cost": 0.0,
94
+ "error_count": 0,
95
+ "avg_latency_ms": 0,
96
+ "by_tier": {"cheap": 0, "capable": 0, "premium": 0},
97
+ }
98
+
99
+ s = summary[call.provider]
100
+ s["call_count"] += 1
101
+ s["total_tokens"] += call.input_tokens + call.output_tokens
102
+ s["total_cost"] += call.estimated_cost
103
+ if not call.success:
104
+ s["error_count"] += 1
105
+ if call.tier in s["by_tier"]:
106
+ s["by_tier"][call.tier] += 1
107
+
108
+ # Calculate averages
109
+ for _provider, stats in summary.items():
110
+ if stats["call_count"] > 0:
111
+ stats["avg_cost"] = stats["total_cost"] / stats["call_count"]
112
+
113
+ return summary
114
+
115
+ def tier_distribution(
116
+ self,
117
+ since: datetime | None = None,
118
+ ) -> dict[str, dict[str, Any]]:
119
+ """Get call distribution by tier.
120
+
121
+ Args:
122
+ since: Only consider calls after this time
123
+
124
+ Returns:
125
+ Dict mapping tier to stats
126
+
127
+ """
128
+ calls = self.store.get_calls(since=since, limit=100000)
129
+
130
+ dist: dict[str, dict[str, Any]] = {
131
+ "cheap": {"count": 0, "cost": 0.0, "tokens": 0},
132
+ "capable": {"count": 0, "cost": 0.0, "tokens": 0},
133
+ "premium": {"count": 0, "cost": 0.0, "tokens": 0},
134
+ }
135
+
136
+ for call in calls:
137
+ if call.tier in dist:
138
+ dist[call.tier]["count"] += 1
139
+ dist[call.tier]["cost"] += call.estimated_cost
140
+ dist[call.tier]["tokens"] += call.input_tokens + call.output_tokens
141
+
142
+ total_calls = sum(d["count"] for d in dist.values())
143
+ for _tier, stats in dist.items():
144
+ stats["percent"] = (stats["count"] / total_calls * 100) if total_calls > 0 else 0
145
+
146
+ return dist
147
+
148
+ def fallback_stats(
149
+ self,
150
+ since: datetime | None = None,
151
+ ) -> dict[str, Any]:
152
+ """Get fallback usage statistics.
153
+
154
+ Args:
155
+ since: Only consider calls after this time
156
+
157
+ Returns:
158
+ Dict with fallback stats
159
+
160
+ """
161
+ calls = self.store.get_calls(since=since, limit=100000)
162
+
163
+ total = len(calls)
164
+ fallback_count = sum(1 for c in calls if c.fallback_used)
165
+ error_count = sum(1 for c in calls if not c.success)
166
+
167
+ # Count by original provider
168
+ by_provider: dict[str, int] = {}
169
+ for call in calls:
170
+ if call.fallback_used and call.original_provider:
171
+ by_provider[call.original_provider] = by_provider.get(call.original_provider, 0) + 1
172
+
173
+ return {
174
+ "total_calls": total,
175
+ "fallback_count": fallback_count,
176
+ "fallback_percent": (fallback_count / total * 100) if total > 0 else 0,
177
+ "error_count": error_count,
178
+ "error_rate": (error_count / total * 100) if total > 0 else 0,
179
+ "by_original_provider": by_provider,
180
+ }
181
+
182
+ def sonnet_opus_fallback_analysis(
183
+ self,
184
+ since: datetime | None = None,
185
+ ) -> dict[str, Any]:
186
+ """Analyze Sonnet 4.5 → Opus 4.5 fallback performance and cost savings.
187
+
188
+ Tracks:
189
+ - How often Sonnet 4.5 succeeds vs needs Opus fallback
190
+ - Cost savings from using Sonnet instead of always using Opus
191
+ - Success rates by model
192
+
193
+ Args:
194
+ since: Only consider calls after this time
195
+
196
+ Returns:
197
+ Dict with fallback analysis and cost savings
198
+ """
199
+ calls = self.store.get_calls(since=since, limit=100000)
200
+
201
+ # Filter for Anthropic calls (Sonnet/Opus)
202
+ anthropic_calls = [
203
+ c
204
+ for c in calls
205
+ if c.provider == "anthropic"
206
+ and c.model_id in ["claude-sonnet-4-5", "claude-opus-4-5-20251101"]
207
+ ]
208
+
209
+ if not anthropic_calls:
210
+ return {
211
+ "total_calls": 0,
212
+ "sonnet_attempts": 0,
213
+ "sonnet_successes": 0,
214
+ "opus_fallbacks": 0,
215
+ "success_rate_sonnet": 0.0,
216
+ "fallback_rate": 0.0,
217
+ "actual_cost": 0.0,
218
+ "always_opus_cost": 0.0,
219
+ "savings": 0.0,
220
+ "savings_percent": 0.0,
221
+ }
222
+
223
+ total = len(anthropic_calls)
224
+
225
+ # Count Sonnet attempts and successes
226
+ sonnet_calls = [c for c in anthropic_calls if c.model_id == "claude-sonnet-4-5"]
227
+ sonnet_successes = sum(1 for c in sonnet_calls if c.success)
228
+
229
+ # Count Opus fallbacks (calls with fallback_used and ended up on Opus)
230
+ opus_fallbacks = sum(
231
+ 1
232
+ for c in anthropic_calls
233
+ if c.model_id == "claude-opus-4-5-20251101" and c.fallback_used
234
+ )
235
+
236
+ # Calculate costs
237
+ actual_cost = sum(c.estimated_cost for c in anthropic_calls)
238
+
239
+ # Calculate what it would cost if everything used Opus
240
+ opus_input_cost = 15.00 / 1_000_000 # per token
241
+ opus_output_cost = 75.00 / 1_000_000 # per token
242
+ always_opus_cost = sum(
243
+ (c.input_tokens * opus_input_cost) + (c.output_tokens * opus_output_cost)
244
+ for c in anthropic_calls
245
+ )
246
+
247
+ savings = always_opus_cost - actual_cost
248
+ savings_percent = (savings / always_opus_cost * 100) if always_opus_cost > 0 else 0
249
+
250
+ return {
251
+ "total_calls": total,
252
+ "sonnet_attempts": len(sonnet_calls),
253
+ "sonnet_successes": sonnet_successes,
254
+ "opus_fallbacks": opus_fallbacks,
255
+ "success_rate_sonnet": (
256
+ (sonnet_successes / len(sonnet_calls) * 100) if sonnet_calls else 0.0
257
+ ),
258
+ "fallback_rate": (opus_fallbacks / total * 100) if total > 0 else 0.0,
259
+ "actual_cost": actual_cost,
260
+ "always_opus_cost": always_opus_cost,
261
+ "savings": savings,
262
+ "savings_percent": savings_percent,
263
+ "avg_cost_per_call": actual_cost / total if total > 0 else 0.0,
264
+ "avg_opus_cost_per_call": always_opus_cost / total if total > 0 else 0.0,
265
+ }
266
+
267
+ def cost_savings_report(
268
+ self,
269
+ since: datetime | None = None,
270
+ ) -> dict[str, Any]:
271
+ """Generate cost savings report.
272
+
273
+ Args:
274
+ since: Only consider workflows after this time
275
+
276
+ Returns:
277
+ Dict with savings analysis
278
+
279
+ """
280
+ workflows = self.store.get_workflows(since=since, limit=10000)
281
+
282
+ total_cost = sum(wf.total_cost for wf in workflows)
283
+ total_baseline = sum(wf.baseline_cost for wf in workflows)
284
+ total_savings = sum(wf.savings for wf in workflows)
285
+
286
+ return {
287
+ "workflow_count": len(workflows),
288
+ "total_actual_cost": total_cost,
289
+ "total_baseline_cost": total_baseline,
290
+ "total_savings": total_savings,
291
+ "savings_percent": (
292
+ (total_savings / total_baseline * 100) if total_baseline > 0 else 0
293
+ ),
294
+ "avg_cost_per_workflow": total_cost / len(workflows) if workflows else 0,
295
+ }
296
+
297
+ # Tier 1 automation monitoring analytics
298
+
299
+ def task_routing_accuracy(
300
+ self,
301
+ since: datetime | None = None,
302
+ ) -> dict[str, Any]:
303
+ """Analyze task routing accuracy.
304
+
305
+ Args:
306
+ since: Only consider routings after this time
307
+
308
+ Returns:
309
+ Dict with routing accuracy metrics by task type and strategy
310
+
311
+ """
312
+ routings = self.store.get_task_routings(since=since, limit=10000)
313
+
314
+ if not routings:
315
+ return {
316
+ "total_tasks": 0,
317
+ "successful_routing": 0,
318
+ "accuracy_rate": 0.0,
319
+ "avg_confidence": 0.0,
320
+ "by_task_type": {},
321
+ "by_strategy": {},
322
+ }
323
+
324
+ total = len(routings)
325
+ successful = sum(1 for r in routings if r.success)
326
+ total_confidence = sum(r.confidence_score for r in routings)
327
+
328
+ # Aggregate by task type
329
+ by_type: dict[str, dict[str, int | float]] = {}
330
+ for r in routings:
331
+ if r.task_type not in by_type:
332
+ by_type[r.task_type] = {"total": 0, "success": 0}
333
+ by_type[r.task_type]["total"] += 1
334
+ if r.success:
335
+ by_type[r.task_type]["success"] += 1
336
+
337
+ # Calculate rates
338
+ for _task_type, stats in by_type.items():
339
+ stats["rate"] = stats["success"] / stats["total"] if stats["total"] > 0 else 0.0
340
+
341
+ # Aggregate by strategy
342
+ by_strategy: dict[str, dict[str, int]] = {}
343
+ for r in routings:
344
+ if r.routing_strategy not in by_strategy:
345
+ by_strategy[r.routing_strategy] = {"total": 0, "success": 0}
346
+ by_strategy[r.routing_strategy]["total"] += 1
347
+ if r.success:
348
+ by_strategy[r.routing_strategy]["success"] += 1
349
+
350
+ return {
351
+ "total_tasks": total,
352
+ "successful_routing": successful,
353
+ "accuracy_rate": successful / total if total > 0 else 0.0,
354
+ "avg_confidence": total_confidence / total if total > 0 else 0.0,
355
+ "by_task_type": by_type,
356
+ "by_strategy": by_strategy,
357
+ }
358
+
359
+ def test_execution_trends(
360
+ self,
361
+ since: datetime | None = None,
362
+ ) -> dict[str, Any]:
363
+ """Analyze test execution trends.
364
+
365
+ Args:
366
+ since: Only consider executions after this time
367
+
368
+ Returns:
369
+ Dict with test execution metrics and trends
370
+
371
+ """
372
+ executions = self.store.get_test_executions(since=since, limit=1000)
373
+
374
+ if not executions:
375
+ return {
376
+ "total_executions": 0,
377
+ "success_rate": 0.0,
378
+ "avg_duration_seconds": 0.0,
379
+ "total_tests_run": 0,
380
+ "total_failures": 0,
381
+ "coverage_trend": "stable",
382
+ "most_failing_tests": [],
383
+ }
384
+
385
+ total_execs = len(executions)
386
+ successful_execs = sum(1 for e in executions if e.success)
387
+ total_duration = sum(e.duration_seconds for e in executions)
388
+ total_tests = sum(e.total_tests for e in executions)
389
+ total_failures = sum(e.failed for e in executions)
390
+
391
+ # Find most failing tests
392
+ failure_counts: dict[str, int] = {}
393
+ for exec_rec in executions:
394
+ for test in exec_rec.failed_tests:
395
+ test_name = test.get("name", "unknown")
396
+ failure_counts[test_name] = failure_counts.get(test_name, 0) + 1
397
+
398
+ most_failing = [
399
+ {"name": name, "failures": count}
400
+ for name, count in heapq.nlargest(10, failure_counts.items(), key=lambda x: x[1])
401
+ ]
402
+
403
+ return {
404
+ "total_executions": total_execs,
405
+ "success_rate": successful_execs / total_execs if total_execs > 0 else 0.0,
406
+ "avg_duration_seconds": total_duration / total_execs if total_execs > 0 else 0.0,
407
+ "total_tests_run": total_tests,
408
+ "total_failures": total_failures,
409
+ "coverage_trend": "stable", # Will be computed from coverage_progress
410
+ "most_failing_tests": most_failing,
411
+ }
412
+
413
+ def coverage_progress(
414
+ self,
415
+ since: datetime | None = None,
416
+ ) -> dict[str, Any]:
417
+ """Track coverage progress over time.
418
+
419
+ Args:
420
+ since: Only consider coverage records after this time
421
+
422
+ Returns:
423
+ Dict with coverage metrics and trends
424
+
425
+ """
426
+ records = self.store.get_coverage_history(since=since, limit=1000)
427
+
428
+ if not records:
429
+ return {
430
+ "current_coverage": 0.0,
431
+ "previous_coverage": 0.0,
432
+ "change": 0.0,
433
+ "trend": "no_data",
434
+ "coverage_history": [],
435
+ "files_improved": 0,
436
+ "files_declined": 0,
437
+ "critical_gaps_count": 0,
438
+ }
439
+
440
+ # Latest and first records
441
+ latest = records[-1]
442
+ first = records[0]
443
+ current_coverage = latest.overall_percentage
444
+
445
+ # Calculate trend by comparing first to last
446
+ if len(records) == 1:
447
+ # Single record - no trend analysis possible
448
+ prev_coverage = 0.0
449
+ change = 0.0
450
+ trend = "stable"
451
+ else:
452
+ # Multiple records - compare first to last
453
+ prev_coverage = first.overall_percentage
454
+ change = current_coverage - prev_coverage
455
+
456
+ # Determine trend based on change
457
+ if change > 1.0:
458
+ trend = "improving"
459
+ elif change < -1.0:
460
+ trend = "declining"
461
+ else:
462
+ trend = "stable"
463
+
464
+ # Build coverage history from records
465
+ coverage_history = [
466
+ {
467
+ "timestamp": r.timestamp,
468
+ "coverage": r.overall_percentage,
469
+ "trend": r.trend,
470
+ }
471
+ for r in records
472
+ ]
473
+
474
+ return {
475
+ "current_coverage": current_coverage,
476
+ "previous_coverage": prev_coverage,
477
+ "change": change,
478
+ "trend": trend,
479
+ "coverage_history": coverage_history,
480
+ "files_improved": 0, # Would need file-level history
481
+ "files_declined": 0, # Would need file-level history
482
+ "critical_gaps_count": len(latest.critical_gaps),
483
+ }
484
+
485
+ def agent_performance(
486
+ self,
487
+ since: datetime | None = None,
488
+ ) -> dict[str, Any]:
489
+ """Analyze agent/workflow performance.
490
+
491
+ Args:
492
+ since: Only consider assignments after this time
493
+
494
+ Returns:
495
+ Dict with agent performance metrics
496
+
497
+ """
498
+ assignments = self.store.get_agent_assignments(
499
+ since=since, automated_only=False, limit=10000
500
+ )
501
+
502
+ if not assignments:
503
+ return {
504
+ "total_assignments": 0,
505
+ "by_agent": {},
506
+ "automation_rate": 0.0,
507
+ "human_review_rate": 0.0,
508
+ }
509
+
510
+ # Aggregate by agent
511
+ by_agent: dict[str, dict[str, Any]] = {}
512
+ total_assignments = len(assignments)
513
+ total_automated = 0
514
+ total_human_review = 0
515
+
516
+ for assignment in assignments:
517
+ agent = assignment.assigned_agent
518
+ if agent not in by_agent:
519
+ by_agent[agent] = {
520
+ "assignments": 0,
521
+ "completed": 0,
522
+ "successful": 0,
523
+ "success_rate": 0.0,
524
+ "avg_duration_hours": 0.0,
525
+ "quality_score_avg": 0.0,
526
+ "total_duration": 0.0,
527
+ "quality_scores": [],
528
+ }
529
+
530
+ stats = by_agent[agent]
531
+ stats["assignments"] += 1
532
+ if assignment.status == "completed":
533
+ stats["completed"] += 1
534
+ if assignment.actual_duration_hours is not None:
535
+ stats["total_duration"] += assignment.actual_duration_hours
536
+
537
+ # Track successful assignments (not just completed)
538
+ if assignment.success:
539
+ stats["successful"] += 1
540
+
541
+ if assignment.automated_eligible:
542
+ total_automated += 1
543
+ if assignment.human_review_required:
544
+ total_human_review += 1
545
+
546
+ # Calculate averages
547
+ for _agent, stats in by_agent.items():
548
+ if stats["assignments"] > 0:
549
+ stats["success_rate"] = stats["successful"] / stats["assignments"]
550
+ if stats["completed"] > 0:
551
+ stats["avg_duration_hours"] = stats["total_duration"] / stats["completed"]
552
+
553
+ # Remove helper fields
554
+ del stats["total_duration"]
555
+ del stats["quality_scores"]
556
+ del stats["successful"] # Remove helper field, keep success_rate
557
+
558
+ return {
559
+ "total_assignments": total_assignments,
560
+ "by_agent": by_agent,
561
+ "automation_rate": (
562
+ total_automated / total_assignments if total_assignments > 0 else 0.0
563
+ ),
564
+ "human_review_rate": (
565
+ total_human_review / total_assignments if total_assignments > 0 else 0.0
566
+ ),
567
+ }
568
+
569
+ def tier1_summary(
570
+ self,
571
+ since: datetime | None = None,
572
+ ) -> dict[str, Any]:
573
+ """Comprehensive Tier 1 automation summary.
574
+
575
+ Args:
576
+ since: Only consider records after this time
577
+
578
+ Returns:
579
+ Dict combining all Tier 1 metrics
580
+
581
+ """
582
+ return {
583
+ "task_routing": self.task_routing_accuracy(since),
584
+ "test_execution": self.test_execution_trends(since),
585
+ "coverage": self.coverage_progress(since),
586
+ "agent_performance": self.agent_performance(since),
587
+ "cost_savings": self.cost_savings_report(since),
588
+ }
589
+
590
+
591
+ # Singleton for global telemetry
592
+ _telemetry_store: TelemetryStore | None = None
593
+
594
+