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,256 @@
1
+ """Dependency manager for cache optional dependencies.
2
+
3
+ Handles auto-detection, user prompts, and installation of sentence-transformers.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ import logging
10
+ import subprocess
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ import yaml
16
+
17
+ from attune.config import _validate_file_path
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class DependencyManager:
23
+ """Manage optional cache dependencies with user prompts.
24
+
25
+ Handles:
26
+ - Auto-detection of installed dependencies
27
+ - One-time user prompt to install cache deps
28
+ - Configuration persistence (user preferences)
29
+ - Pip-based installation
30
+
31
+ Example:
32
+ manager = DependencyManager()
33
+
34
+ if manager.should_prompt_cache_install():
35
+ manager.prompt_cache_install()
36
+
37
+ """
38
+
39
+ def __init__(self, config_path: Path | None = None):
40
+ """Initialize dependency manager.
41
+
42
+ Args:
43
+ config_path: Path to config file (default: ~/.attune/config.yml).
44
+
45
+ """
46
+ self.config_path = config_path or Path.home() / ".empathy" / "config.yml"
47
+ self.config = self._load_config()
48
+
49
+ def _load_config(self) -> dict:
50
+ """Load user configuration.
51
+
52
+ Returns:
53
+ Configuration dictionary.
54
+
55
+ """
56
+ if not self.config_path.exists():
57
+ return {}
58
+
59
+ try:
60
+ with open(self.config_path) as f:
61
+ return yaml.safe_load(f) or {}
62
+ except (yaml.YAMLError, OSError) as e:
63
+ logger.warning(f"Failed to load config: {e}")
64
+ return {}
65
+
66
+ def _save_config(self) -> None:
67
+ """Save configuration to disk."""
68
+ try:
69
+ self.config_path.parent.mkdir(parents=True, exist_ok=True)
70
+ validated_path = _validate_file_path(str(self.config_path))
71
+ with open(validated_path, "w") as f:
72
+ yaml.safe_dump(self.config, f, default_flow_style=False)
73
+ except (yaml.YAMLError, OSError, ValueError) as e:
74
+ logger.error(f"Failed to save config: {e}")
75
+
76
+ def is_cache_installed(self) -> bool:
77
+ """Check if cache dependencies are installed.
78
+
79
+ Returns:
80
+ True if sentence-transformers is available, False otherwise.
81
+
82
+ """
83
+ try:
84
+ import sentence_transformers # noqa: F401
85
+ import torch # noqa: F401
86
+
87
+ return True
88
+ except ImportError:
89
+ return False
90
+
91
+ def should_prompt_cache_install(self) -> bool:
92
+ """Check if we should prompt user to install cache.
93
+
94
+ Returns:
95
+ True if we should prompt, False otherwise.
96
+
97
+ """
98
+ # Never prompt if already installed
99
+ if self.is_cache_installed():
100
+ return False
101
+
102
+ # Never prompt if user already declined
103
+ cache_config = self.config.get("cache", {})
104
+ if cache_config.get("install_declined", False):
105
+ return False
106
+
107
+ # Never prompt if prompt already shown
108
+ if cache_config.get("prompt_shown", False):
109
+ return False
110
+
111
+ # Never prompt if user disabled prompts
112
+ if not cache_config.get("prompt_enabled", True):
113
+ return False
114
+
115
+ # Prompt on first run
116
+ return True
117
+
118
+ def prompt_cache_install(self) -> bool:
119
+ """Prompt user to install cache dependencies.
120
+
121
+ Returns:
122
+ True if user accepted and install succeeded, False otherwise.
123
+
124
+ """
125
+ print("\n" + "=" * 60)
126
+ print("⚡ Smart Caching Available")
127
+ print("=" * 60)
128
+ print()
129
+ print(" Empathy Framework can reduce your API costs by 70% with hybrid caching.")
130
+ print(" This requires installing sentence-transformers (~150MB).")
131
+ print()
132
+
133
+ try:
134
+ response = (
135
+ input(" Would you like to enable smart caching now? [Y/n]: ").strip().lower()
136
+ )
137
+ except (EOFError, KeyboardInterrupt):
138
+ print("\n Skipping cache installation.")
139
+ response = "n"
140
+
141
+ if response in ["y", "yes", ""]:
142
+ return self.install_cache_dependencies()
143
+ else:
144
+ print()
145
+ print(" ℹ Using hash-only cache (30% savings)")
146
+ print(" ℹ To enable later: empathy install cache")
147
+ print()
148
+ print("=" * 60)
149
+ print()
150
+
151
+ # Save that user declined
152
+ if "cache" not in self.config:
153
+ self.config["cache"] = {}
154
+ self.config["cache"]["install_declined"] = True
155
+ self.config["cache"]["prompt_shown"] = True
156
+ self._save_config()
157
+
158
+ return False
159
+
160
+ def install_cache_dependencies(self) -> bool:
161
+ """Install cache dependencies using pip.
162
+
163
+ Returns:
164
+ True if installation succeeded, False otherwise.
165
+
166
+ """
167
+ print()
168
+ print(" ↓ Installing cache dependencies...")
169
+ print()
170
+
171
+ packages = [
172
+ "sentence-transformers>=2.0.0",
173
+ "torch>=2.0.0",
174
+ "numpy>=1.24.0",
175
+ ]
176
+
177
+ try:
178
+ # Run pip install
179
+ subprocess.check_call(
180
+ [sys.executable, "-m", "pip", "install", "--quiet"] + packages,
181
+ stdout=subprocess.PIPE,
182
+ stderr=subprocess.PIPE,
183
+ )
184
+
185
+ print(" ✓ sentence-transformers installed")
186
+ print(" ✓ torch installed")
187
+ print(" ✓ numpy installed")
188
+ print()
189
+ print(" ✓ Smart caching enabled! Future runs will save 70% on costs.")
190
+ print()
191
+ print("=" * 60)
192
+ print()
193
+
194
+ # Mark as installed in config
195
+ if "cache" not in self.config:
196
+ self.config["cache"] = {}
197
+ self.config["cache"]["enabled"] = True
198
+ self.config["cache"]["install_declined"] = False
199
+ self.config["cache"]["prompt_shown"] = True
200
+ self._save_config()
201
+
202
+ return True
203
+
204
+ except subprocess.CalledProcessError as e:
205
+ print()
206
+ print(f" ✗ Installation failed: {e}")
207
+ print(" ℹ You can try manually: pip install empathy-framework[cache]")
208
+ print()
209
+ print("=" * 60)
210
+ print()
211
+
212
+ logger.error(f"Failed to install cache dependencies: {e}")
213
+ return False
214
+
215
+ def disable_prompts(self) -> None:
216
+ """Disable cache installation prompts."""
217
+ if "cache" not in self.config:
218
+ self.config["cache"] = {}
219
+ self.config["cache"]["prompt_enabled"] = False
220
+ self._save_config()
221
+ logger.info("Cache installation prompts disabled")
222
+
223
+ def enable_prompts(self) -> None:
224
+ """Re-enable cache installation prompts."""
225
+ if "cache" not in self.config:
226
+ self.config["cache"] = {}
227
+ self.config["cache"]["prompt_enabled"] = True
228
+ self.config["cache"]["prompt_shown"] = False
229
+ self.config["cache"]["install_declined"] = False
230
+ self._save_config()
231
+ logger.info("Cache installation prompts re-enabled")
232
+
233
+ def get_config(self) -> dict[str, Any]:
234
+ """Get cache configuration.
235
+
236
+ Returns:
237
+ Cache configuration dictionary.
238
+
239
+ """
240
+ result = self.config.get("cache", {})
241
+ if not isinstance(result, dict):
242
+ return {}
243
+ return result
244
+
245
+ def set_config(self, key: str, value: Any) -> None:
246
+ """Set cache configuration value.
247
+
248
+ Args:
249
+ key: Configuration key.
250
+ value: Configuration value.
251
+
252
+ """
253
+ if "cache" not in self.config:
254
+ self.config["cache"] = {}
255
+ self.config["cache"][key] = value
256
+ self._save_config()
@@ -0,0 +1,251 @@
1
+ """Hash-only cache implementation using SHA256 for exact matching.
2
+
3
+ Provides fast exact-match caching with ~30% hit rate. No dependencies required.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ import heapq
10
+ import logging
11
+ import time
12
+ from typing import Any
13
+
14
+ from .base import BaseCache, CacheEntry, CacheStats
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class HashOnlyCache(BaseCache):
20
+ """Fast hash-based cache for exact prompt matches.
21
+
22
+ Uses SHA256 hashing for O(1) lookup. Provides ~30% cache hit rate
23
+ for workflows with repeated exact prompts (e.g., re-reviewing same code).
24
+
25
+ No external dependencies required - always available as fallback.
26
+
27
+ Example:
28
+ cache = HashOnlyCache()
29
+
30
+ # First call (miss)
31
+ result = cache.get("code-review", "scan", prompt, "claude-3-5-sonnet")
32
+ # → None
33
+
34
+ # Store response
35
+ cache.put("code-review", "scan", prompt, "claude-3-5-sonnet", response)
36
+
37
+ # Second call with exact same prompt (hit)
38
+ result = cache.get("code-review", "scan", prompt, "claude-3-5-sonnet")
39
+ # → response (from cache, <5μs lookup)
40
+
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ max_size_mb: int = 500,
46
+ default_ttl: int = 86400,
47
+ max_memory_mb: int = 100,
48
+ ):
49
+ """Initialize hash-only cache.
50
+
51
+ Args:
52
+ max_size_mb: Maximum disk cache size in MB.
53
+ default_ttl: Default TTL in seconds (24 hours).
54
+ max_memory_mb: Maximum in-memory cache size in MB.
55
+
56
+ """
57
+ super().__init__(max_size_mb, default_ttl)
58
+ self.max_memory_mb = max_memory_mb
59
+ self._memory_cache: dict[str, CacheEntry] = {}
60
+ self._access_times: dict[str, float] = {} # For LRU eviction
61
+
62
+ logger.debug(
63
+ f"HashOnlyCache initialized (max_memory: {max_memory_mb}MB, "
64
+ f"max_disk: {max_size_mb}MB, ttl: {default_ttl}s)"
65
+ )
66
+
67
+ def get(
68
+ self,
69
+ workflow: str,
70
+ stage: str,
71
+ prompt: str,
72
+ model: str,
73
+ ) -> Any | None:
74
+ """Get cached response for exact prompt match.
75
+
76
+ Args:
77
+ workflow: Workflow name (e.g., "code-review").
78
+ stage: Stage name (e.g., "scan").
79
+ prompt: Exact prompt text.
80
+ model: Model identifier.
81
+
82
+ Returns:
83
+ Cached response if exact match found and not expired, None otherwise.
84
+
85
+ """
86
+ cache_key = self._create_cache_key(workflow, stage, prompt, model)
87
+
88
+ # Check in-memory cache
89
+ if cache_key in self._memory_cache:
90
+ entry = self._memory_cache[cache_key]
91
+
92
+ # Check if expired
93
+ current_time = time.time()
94
+ if entry.is_expired(current_time):
95
+ logger.debug(f"Cache entry expired: {cache_key[:16]}...")
96
+ self._evict_entry(cache_key)
97
+ self.stats.misses += 1
98
+ return None
99
+
100
+ # Cache hit!
101
+ self._access_times[cache_key] = current_time
102
+ self.stats.hits += 1
103
+ logger.debug(
104
+ f"Cache HIT (hash): {workflow}/{stage} "
105
+ f"(key: {cache_key[:16]}..., hit_rate: {self.stats.hit_rate:.1f}%)"
106
+ )
107
+ return entry.response
108
+
109
+ # Cache miss
110
+ self.stats.misses += 1
111
+ logger.debug(
112
+ f"Cache MISS (hash): {workflow}/{stage} "
113
+ f"(key: {cache_key[:16]}..., hit_rate: {self.stats.hit_rate:.1f}%)"
114
+ )
115
+ return None
116
+
117
+ def put(
118
+ self,
119
+ workflow: str,
120
+ stage: str,
121
+ prompt: str,
122
+ model: str,
123
+ response: Any,
124
+ ttl: int | None = None,
125
+ ) -> None:
126
+ """Store response in cache.
127
+
128
+ Args:
129
+ workflow: Workflow name.
130
+ stage: Stage name.
131
+ prompt: Prompt text.
132
+ model: Model identifier.
133
+ response: LLM response to cache.
134
+ ttl: Optional custom TTL (uses default if None).
135
+
136
+ """
137
+ cache_key = self._create_cache_key(workflow, stage, prompt, model)
138
+ prompt_hash = self._create_cache_key("", "", prompt, "") # Hash of prompt only
139
+
140
+ # Create cache entry
141
+ entry = CacheEntry(
142
+ key=cache_key,
143
+ response=response,
144
+ workflow=workflow,
145
+ stage=stage,
146
+ model=model,
147
+ prompt_hash=prompt_hash,
148
+ timestamp=time.time(),
149
+ ttl=ttl or self.default_ttl,
150
+ )
151
+
152
+ # Check if we need to evict entries
153
+ self._maybe_evict_lru()
154
+
155
+ # Store in memory
156
+ self._memory_cache[cache_key] = entry
157
+ self._access_times[cache_key] = entry.timestamp
158
+
159
+ logger.debug(
160
+ f"Cache PUT: {workflow}/{stage} (key: {cache_key[:16]}..., "
161
+ f"entries: {len(self._memory_cache)})"
162
+ )
163
+
164
+ def clear(self) -> None:
165
+ """Clear all cached entries."""
166
+ count = len(self._memory_cache)
167
+ self._memory_cache.clear()
168
+ self._access_times.clear()
169
+ logger.info(f"Cache cleared ({count} entries removed)")
170
+
171
+ def get_stats(self) -> CacheStats:
172
+ """Get cache statistics.
173
+
174
+ Returns:
175
+ CacheStats with hit/miss/eviction counts.
176
+
177
+ """
178
+ return self.stats
179
+
180
+ def _evict_entry(self, cache_key: str) -> None:
181
+ """Remove entry from cache.
182
+
183
+ Args:
184
+ cache_key: Key to evict.
185
+
186
+ """
187
+ if cache_key in self._memory_cache:
188
+ del self._memory_cache[cache_key]
189
+ if cache_key in self._access_times:
190
+ del self._access_times[cache_key]
191
+ self.stats.evictions += 1
192
+
193
+ def _maybe_evict_lru(self) -> None:
194
+ """Evict least recently used entries if cache is too large.
195
+
196
+ Uses LRU (Least Recently Used) eviction policy.
197
+
198
+ """
199
+ # Estimate memory usage (rough)
200
+ estimated_mb = len(self._memory_cache) * 0.01 # Rough estimate: 10KB per entry
201
+
202
+ if estimated_mb > self.max_memory_mb:
203
+ # Evict 10% of entries (LRU)
204
+ num_to_evict = max(1, len(self._memory_cache) // 10)
205
+
206
+ # Get oldest entries by access time (LRU eviction)
207
+ oldest_keys = heapq.nsmallest(
208
+ num_to_evict, self._access_times.items(), key=lambda x: x[1]
209
+ )
210
+
211
+ for cache_key, _ in oldest_keys:
212
+ self._evict_entry(cache_key)
213
+ logger.debug(f"LRU eviction: {cache_key[:16]}...")
214
+
215
+ logger.info(
216
+ f"LRU eviction: removed {num_to_evict} entries "
217
+ f"(cache size: {len(self._memory_cache)} entries)"
218
+ )
219
+
220
+ def evict_expired(self) -> int:
221
+ """Remove all expired entries.
222
+
223
+ Returns:
224
+ Number of entries evicted.
225
+
226
+ """
227
+ current_time = time.time()
228
+ expired_keys = [
229
+ key for key, entry in self._memory_cache.items() if entry.is_expired(current_time)
230
+ ]
231
+
232
+ for key in expired_keys:
233
+ self._evict_entry(key)
234
+
235
+ if expired_keys:
236
+ logger.info(f"Expired eviction: removed {len(expired_keys)} entries")
237
+
238
+ return len(expired_keys)
239
+
240
+ def size_info(self) -> dict[str, Any]:
241
+ """Get cache size information.
242
+
243
+ Returns:
244
+ Dictionary with cache size metrics.
245
+
246
+ """
247
+ return {
248
+ "entries": len(self._memory_cache),
249
+ "estimated_mb": len(self._memory_cache) * 0.01,
250
+ "max_memory_mb": self.max_memory_mb,
251
+ }