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,448 @@
1
+ """Alert CLI Workflow
2
+
3
+ Interactive workflow for setting up LLM telemetry alerts.
4
+
5
+ **Usage:**
6
+ empathy alerts init # Interactive setup workflow
7
+ empathy alerts list # List configured alerts
8
+ empathy alerts delete # Delete an alert
9
+ empathy alerts watch # Start monitoring
10
+ empathy alerts history # View alert history
11
+ empathy alerts metrics # View current metrics
12
+
13
+ Copyright 2025-2026 Smart-AI-Memory
14
+ Licensed under Fair Source License 0.9
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import signal
20
+ import sys
21
+ import time
22
+
23
+ import click
24
+
25
+ from .alerts import (
26
+ get_alert_engine,
27
+ )
28
+
29
+
30
+ @click.group()
31
+ def alerts():
32
+ """Alert management commands for LLM telemetry monitoring."""
33
+ pass
34
+
35
+
36
+ @alerts.command()
37
+ @click.option("--non-interactive", is_flag=True, help="Skip interactive prompts")
38
+ @click.option(
39
+ "--metric", type=click.Choice(["daily_cost", "error_rate", "avg_latency", "token_usage"])
40
+ )
41
+ @click.option("--threshold", type=float)
42
+ @click.option("--channel", type=click.Choice(["webhook", "email", "stdout"]))
43
+ @click.option("--webhook-url", help="Webhook URL (for webhook channel)")
44
+ @click.option("--email", help="Email address (for email channel)")
45
+ def init(
46
+ non_interactive: bool,
47
+ metric: str | None,
48
+ threshold: float | None,
49
+ channel: str | None,
50
+ webhook_url: str | None,
51
+ email: str | None,
52
+ ):
53
+ """Initialize an alert with interactive workflow or CLI flags."""
54
+ if non_interactive:
55
+ # Non-interactive mode - require all parameters
56
+ if not all([metric, threshold, channel]):
57
+ click.echo(
58
+ "Error: --metric, --threshold, and --channel required in non-interactive mode"
59
+ )
60
+ sys.exit(1)
61
+
62
+ if channel == "webhook" and not webhook_url:
63
+ click.echo("Error: --webhook-url required for webhook channel")
64
+ sys.exit(1)
65
+
66
+ if channel == "email" and not email:
67
+ click.echo("Error: --email required for email channel")
68
+ sys.exit(1)
69
+
70
+ _create_alert(metric, threshold, channel, webhook_url, email)
71
+ return
72
+
73
+ # Interactive workflow
74
+ click.echo("šŸ”” Alert Setup Workflow\n")
75
+
76
+ # Question 1: What metric?
77
+ click.echo("1. What metric do you want to monitor?")
78
+ click.echo(" a) Daily cost (total USD spent)")
79
+ click.echo(" b) Error rate (% of failed calls)")
80
+ click.echo(" c) Latency (avg response time)")
81
+ click.echo(" d) Token usage (total tokens)")
82
+
83
+ metric_choice = click.prompt("Choose (a/b/c/d)", type=click.Choice(["a", "b", "c", "d"]))
84
+
85
+ metric_map = {
86
+ "a": ("daily_cost", "Daily Cost", "USD"),
87
+ "b": ("error_rate", "Error Rate", "%"),
88
+ "c": ("avg_latency", "Average Latency", "ms"),
89
+ "d": ("token_usage", "Token Usage", "tokens"),
90
+ }
91
+
92
+ metric, metric_name, unit = metric_map[metric_choice]
93
+
94
+ # Question 2: What threshold?
95
+ click.echo(f"\n2. What threshold for {metric_name}?")
96
+ defaults = {
97
+ "daily_cost": 10.0,
98
+ "error_rate": 10.0,
99
+ "avg_latency": 3000,
100
+ "token_usage": 100000,
101
+ }
102
+ threshold = click.prompt(f"Threshold ({unit})", type=float, default=defaults[metric])
103
+
104
+ # Question 3: Where to send?
105
+ click.echo("\n3. Where should alerts be sent?")
106
+ click.echo(" a) Webhook (Slack, Discord, Teams)")
107
+ click.echo(" b) Email")
108
+ click.echo(" c) Console output")
109
+
110
+ channel_choice = click.prompt("Choose (a/b/c)", type=click.Choice(["a", "b", "c"]))
111
+
112
+ channel_map = {
113
+ "a": "webhook",
114
+ "b": "email",
115
+ "c": "stdout",
116
+ }
117
+
118
+ channel = channel_map[channel_choice]
119
+ webhook_url = None
120
+ email_addr = None
121
+
122
+ if channel == "webhook":
123
+ webhook_url = click.prompt("Webhook URL")
124
+ elif channel == "email":
125
+ email_addr = click.prompt("Email address")
126
+
127
+ _create_alert(metric, threshold, channel, webhook_url, email_addr)
128
+
129
+
130
+ def _create_alert(
131
+ metric: str,
132
+ threshold: float,
133
+ channel: str,
134
+ webhook_url: str | None,
135
+ email: str | None,
136
+ ) -> None:
137
+ """Create an alert with the given configuration."""
138
+ engine = get_alert_engine()
139
+ alert_id = f"alert_{metric}_{int(time.time())}"
140
+
141
+ metric_names = {
142
+ "daily_cost": "Daily Cost",
143
+ "error_rate": "Error Rate",
144
+ "avg_latency": "Average Latency",
145
+ "token_usage": "Token Usage",
146
+ }
147
+
148
+ try:
149
+ engine.add_alert(
150
+ alert_id=alert_id,
151
+ name=f"{metric_names.get(metric, metric)} Alert",
152
+ metric=metric,
153
+ threshold=threshold,
154
+ channel=channel,
155
+ webhook_url=webhook_url,
156
+ email=email,
157
+ )
158
+
159
+ click.echo("\nāœ… Alert created successfully!")
160
+ click.echo(f" ID: {alert_id}")
161
+ click.echo(f" Metric: {metric_names.get(metric, metric)}")
162
+ click.echo(f" Threshold: {threshold}")
163
+ click.echo(f" Channel: {channel}")
164
+
165
+ click.echo("\nšŸ’” Tip: Run 'empathy alerts watch' to start monitoring")
166
+
167
+ except ValueError as e:
168
+ click.echo(f"\nāŒ Error: {e}")
169
+ sys.exit(1)
170
+
171
+
172
+ @alerts.command(name="list")
173
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
174
+ def list_cmd(as_json: bool):
175
+ """List all configured alerts."""
176
+ engine = get_alert_engine()
177
+ alerts_list = engine.list_alerts()
178
+
179
+ if not alerts_list:
180
+ if as_json:
181
+ click.echo("[]")
182
+ else:
183
+ click.echo("No alerts configured. Run 'empathy alerts init' to create one.")
184
+ return
185
+
186
+ if as_json:
187
+ import json
188
+
189
+ click.echo(json.dumps([a.to_dict() for a in alerts_list], indent=2))
190
+ return
191
+
192
+ click.echo("šŸ“‹ Configured Alerts:\n")
193
+
194
+ for alert in alerts_list:
195
+ status = "āœ“ Enabled" if alert.enabled else "āœ— Disabled"
196
+ click.echo(f" [{status}] {alert.name}")
197
+ click.echo(f" ID: {alert.alert_id}")
198
+ click.echo(f" Metric: {alert.metric.value} >= {alert.threshold}")
199
+ click.echo(f" Channel: {alert.channel.value}")
200
+ click.echo(f" Severity: {alert.severity.value}")
201
+ click.echo(f" Cooldown: {alert.cooldown_seconds}s")
202
+ click.echo()
203
+
204
+
205
+ @alerts.command()
206
+ @click.argument("alert_id")
207
+ def delete(alert_id: str):
208
+ """Delete an alert by ID."""
209
+ engine = get_alert_engine()
210
+ deleted = engine.delete_alert(alert_id)
211
+
212
+ if deleted:
213
+ click.echo(f"āœ… Alert '{alert_id}' deleted successfully")
214
+ else:
215
+ click.echo(f"āŒ Alert '{alert_id}' not found")
216
+ sys.exit(1)
217
+
218
+
219
+ @alerts.command()
220
+ @click.argument("alert_id")
221
+ def enable(alert_id: str):
222
+ """Enable an alert by ID."""
223
+ engine = get_alert_engine()
224
+ if engine.enable_alert(alert_id):
225
+ click.echo(f"āœ… Alert '{alert_id}' enabled")
226
+ else:
227
+ click.echo(f"āŒ Alert '{alert_id}' not found")
228
+ sys.exit(1)
229
+
230
+
231
+ @alerts.command()
232
+ @click.argument("alert_id")
233
+ def disable(alert_id: str):
234
+ """Disable an alert by ID."""
235
+ engine = get_alert_engine()
236
+ if engine.disable_alert(alert_id):
237
+ click.echo(f"āœ… Alert '{alert_id}' disabled")
238
+ else:
239
+ click.echo(f"āŒ Alert '{alert_id}' not found")
240
+ sys.exit(1)
241
+
242
+
243
+ @alerts.command()
244
+ @click.option("--interval", default=60, help="Check interval in seconds (default: 60)")
245
+ @click.option("--daemon", is_flag=True, help="Run as background daemon")
246
+ @click.option("--once", is_flag=True, help="Check once and exit")
247
+ def watch(interval: int, daemon: bool, once: bool):
248
+ """Watch telemetry and trigger alerts when thresholds are exceeded."""
249
+ engine = get_alert_engine()
250
+
251
+ alerts_list = engine.list_alerts()
252
+ if not alerts_list:
253
+ click.echo("No alerts configured. Run 'empathy alerts init' first.")
254
+ sys.exit(1)
255
+
256
+ enabled_count = sum(1 for a in alerts_list if a.enabled)
257
+ click.echo(f"šŸ”” Monitoring {enabled_count} enabled alert(s)")
258
+
259
+ if once:
260
+ # Single check mode
261
+ events = engine.check_and_trigger()
262
+ if events:
263
+ click.echo(f"\nāš ļø {len(events)} alert(s) triggered!")
264
+ for event in events:
265
+ click.echo(
266
+ f" - {event.alert_name}: {event.current_value:.2f} >= {event.threshold:.2f}"
267
+ )
268
+ else:
269
+ click.echo("āœ… All metrics within thresholds")
270
+ return
271
+
272
+ if daemon:
273
+ click.echo("šŸ”„ Starting alert watcher as daemon...")
274
+ click.echo(
275
+ "āš ļø Daemon mode runs in background. Use 'ps aux | grep empathy' to check status."
276
+ )
277
+ # Daemonize
278
+ _daemonize()
279
+
280
+ click.echo(f"šŸ”„ Starting alert watcher (checking every {interval}s)...")
281
+ click.echo(" Press Ctrl+C to stop\n")
282
+
283
+ # Set up signal handler for graceful shutdown
284
+ running = True
285
+
286
+ def signal_handler(sig, frame):
287
+ nonlocal running
288
+ running = False
289
+ click.echo("\nāœ“ Alert watcher stopped")
290
+
291
+ signal.signal(signal.SIGINT, signal_handler)
292
+ signal.signal(signal.SIGTERM, signal_handler)
293
+
294
+ check_count = 0
295
+ triggered_count = 0
296
+
297
+ try:
298
+ while running:
299
+ check_count += 1
300
+ events = engine.check_and_trigger()
301
+
302
+ if events:
303
+ triggered_count += len(events)
304
+ for event in events:
305
+ click.echo(f"āš ļø ALERT: {event.alert_name}")
306
+ click.echo(
307
+ f" {event.metric.value}: {event.current_value:.2f} >= {event.threshold:.2f}"
308
+ )
309
+
310
+ # Status update every 5 checks
311
+ if check_count % 5 == 0:
312
+ click.echo(
313
+ f" [Check #{check_count}] Monitoring... ({triggered_count} alerts triggered)"
314
+ )
315
+
316
+ time.sleep(interval)
317
+ except KeyboardInterrupt:
318
+ pass
319
+
320
+ click.echo(f"\nšŸ“Š Summary: {check_count} checks, {triggered_count} alerts triggered")
321
+
322
+
323
+ def _daemonize():
324
+ """Daemonize the current process (Unix only)."""
325
+ import os
326
+
327
+ # Double fork to detach from terminal
328
+ try:
329
+ pid = os.fork()
330
+ if pid > 0:
331
+ # Parent exits
332
+ sys.exit(0)
333
+ except OSError as e:
334
+ click.echo(f"Fork #1 failed: {e}")
335
+ sys.exit(1)
336
+
337
+ # Decouple from parent environment
338
+ os.chdir("/")
339
+ os.setsid()
340
+ os.umask(0)
341
+
342
+ # Second fork
343
+ try:
344
+ pid = os.fork()
345
+ if pid > 0:
346
+ click.echo(f"Daemon started with PID: {pid}")
347
+ sys.exit(0)
348
+ except OSError as e:
349
+ click.echo(f"Fork #2 failed: {e}")
350
+ sys.exit(1)
351
+
352
+ # Redirect standard file descriptors
353
+ sys.stdout.flush()
354
+ sys.stderr.flush()
355
+
356
+ # Close file descriptors
357
+ with open("/dev/null", "rb", 0) as f:
358
+ os.dup2(f.fileno(), sys.stdin.fileno())
359
+ with open("/dev/null", "ab", 0) as f:
360
+ os.dup2(f.fileno(), sys.stdout.fileno())
361
+ with open("/dev/null", "ab", 0) as f:
362
+ os.dup2(f.fileno(), sys.stderr.fileno())
363
+
364
+
365
+ @alerts.command()
366
+ @click.option("--alert-id", help="Filter by alert ID")
367
+ @click.option("--limit", default=20, help="Maximum records to show")
368
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
369
+ def history(alert_id: str | None, limit: int, as_json: bool):
370
+ """View alert trigger history."""
371
+ engine = get_alert_engine()
372
+ records = engine.get_alert_history(alert_id=alert_id, limit=limit)
373
+
374
+ if not records:
375
+ if as_json:
376
+ click.echo("[]")
377
+ else:
378
+ click.echo("No alert history found.")
379
+ return
380
+
381
+ if as_json:
382
+ import json
383
+
384
+ click.echo(json.dumps(records, indent=2))
385
+ return
386
+
387
+ click.echo("šŸ“œ Alert History:\n")
388
+
389
+ for record in records:
390
+ delivered = "āœ“" if record["delivered"] else "āœ—"
391
+ click.echo(f" [{delivered}] {record['alert_id']}")
392
+ click.echo(
393
+ f" Metric: {record['metric']} = {record['current_value']:.2f} (threshold: {record['threshold']:.2f})"
394
+ )
395
+ click.echo(f" Severity: {record['severity']}")
396
+ click.echo(f" Triggered: {record['triggered_at']}")
397
+ if record.get("delivery_error"):
398
+ click.echo(f" Error: {record['delivery_error']}")
399
+ click.echo()
400
+
401
+
402
+ @alerts.command()
403
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
404
+ def metrics(as_json: bool):
405
+ """View current telemetry metrics."""
406
+ engine = get_alert_engine()
407
+ current_metrics = engine.get_metrics()
408
+
409
+ if as_json:
410
+ import json
411
+
412
+ click.echo(json.dumps(current_metrics, indent=2))
413
+ return
414
+
415
+ click.echo("šŸ“Š Current Metrics (last 24 hours):\n")
416
+
417
+ metric_info = {
418
+ "daily_cost": ("Daily Cost", "USD"),
419
+ "error_rate": ("Error Rate", "%"),
420
+ "avg_latency": ("Avg Latency", "ms"),
421
+ "token_usage": ("Token Usage", "tokens"),
422
+ }
423
+
424
+ for key, value in current_metrics.items():
425
+ name, unit = metric_info.get(key, (key, ""))
426
+ click.echo(f" {name}: {value:.2f} {unit}")
427
+
428
+ click.echo()
429
+
430
+ # Show alerts that would trigger
431
+ alerts_list = engine.list_alerts()
432
+ triggered = []
433
+ for alert in alerts_list:
434
+ if alert.enabled:
435
+ current = current_metrics.get(alert.metric.value, 0)
436
+ if current >= alert.threshold:
437
+ triggered.append((alert.name, current, alert.threshold))
438
+
439
+ if triggered:
440
+ click.echo("āš ļø Alerts that would trigger:")
441
+ for name, current, threshold in triggered:
442
+ click.echo(f" {name}: {current:.2f} >= {threshold:.2f}")
443
+ else:
444
+ click.echo("āœ… All metrics within thresholds")
445
+
446
+
447
+ if __name__ == "__main__":
448
+ alerts()