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
attune/cache/hybrid.py ADDED
@@ -0,0 +1,457 @@
1
+ """Hybrid cache with hash + semantic similarity matching.
2
+
3
+ Combines fast hash-based exact matching with intelligent semantic similarity
4
+ for maximum cache hit rate (~70%).
5
+
6
+ Requires optional dependencies:
7
+ - sentence-transformers
8
+ - torch
9
+ - numpy
10
+
11
+ Copyright 2025 Smart-AI-Memory
12
+ Licensed under Fair Source License 0.9
13
+ """
14
+
15
+ import hashlib
16
+ import heapq
17
+ import logging
18
+ import time
19
+ from pathlib import Path
20
+ from typing import TYPE_CHECKING, Any
21
+
22
+ import numpy as np
23
+
24
+ from .base import BaseCache, CacheEntry, CacheStats
25
+ from .storage import CacheStorage
26
+
27
+ if TYPE_CHECKING:
28
+ from sentence_transformers import SentenceTransformer
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
34
+ """Calculate cosine similarity between two vectors.
35
+
36
+ Args:
37
+ a: First vector.
38
+ b: Second vector.
39
+
40
+ Returns:
41
+ Similarity score (0.0 to 1.0).
42
+
43
+ """
44
+ return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
45
+
46
+
47
+ class HybridCache(BaseCache):
48
+ """Hybrid hash + semantic similarity cache for maximum hit rate.
49
+
50
+ Provides two-tier caching:
51
+ 1. Fast path: Hash-based exact matching (~1-5μs lookup)
52
+ 2. Smart path: Semantic similarity matching (~50ms lookup)
53
+
54
+ Achieves ~70% cache hit rate vs ~30% for hash-only.
55
+
56
+ Example:
57
+ cache = HybridCache(similarity_threshold=0.95)
58
+
59
+ # First call (miss)
60
+ result = cache.get("code-review", "scan", "Add auth middleware", "sonnet")
61
+ # → None (cache miss)
62
+
63
+ cache.put("code-review", "scan", "Add auth middleware", "sonnet", response1)
64
+
65
+ # Exact match (hash hit, <5μs)
66
+ result = cache.get("code-review", "scan", "Add auth middleware", "sonnet")
67
+ # → response1 (hash cache hit)
68
+
69
+ # Similar prompt (semantic hit, ~50ms)
70
+ result = cache.get("code-review", "scan", "Add logging middleware", "sonnet")
71
+ # → response1 (92% similar, semantic cache hit)
72
+
73
+ """
74
+
75
+ def __init__(
76
+ self,
77
+ max_size_mb: int = 500,
78
+ default_ttl: int = 86400,
79
+ max_memory_mb: int = 100,
80
+ similarity_threshold: float = 0.95,
81
+ model_name: str = "all-MiniLM-L6-v2",
82
+ device: str = "cpu",
83
+ cache_dir: Path | None = None,
84
+ ):
85
+ """Initialize hybrid cache.
86
+
87
+ Args:
88
+ max_size_mb: Maximum disk cache size in MB.
89
+ default_ttl: Default TTL in seconds (24 hours).
90
+ max_memory_mb: Maximum in-memory cache size in MB.
91
+ similarity_threshold: Semantic similarity threshold (0.0-1.0, default: 0.95).
92
+ model_name: Sentence transformer model (default: all-MiniLM-L6-v2).
93
+ device: Device for embeddings ("cpu" or "cuda").
94
+ cache_dir: Directory for persistent cache storage (default: ~/.attune/cache/).
95
+
96
+ """
97
+ super().__init__(max_size_mb, default_ttl)
98
+ self.max_memory_mb = max_memory_mb
99
+ self.similarity_threshold = similarity_threshold
100
+ self.model_name = model_name
101
+ self.device = device
102
+
103
+ # Hash cache (fast path)
104
+ self._hash_cache: dict[str, CacheEntry] = {}
105
+ self._access_times: dict[str, float] = {}
106
+
107
+ # Semantic cache (smart path)
108
+ self._semantic_cache: list[tuple[np.ndarray, CacheEntry]] = []
109
+
110
+ # Load sentence transformer model
111
+ self._model: SentenceTransformer | None = None
112
+ self._load_model()
113
+
114
+ # Initialize persistent storage
115
+ self._storage = CacheStorage(cache_dir=cache_dir, max_disk_mb=max_size_mb)
116
+
117
+ # Load existing entries from storage into memory caches
118
+ self._load_from_storage()
119
+
120
+ logger.info(
121
+ f"HybridCache initialized (model: {model_name}, threshold: {similarity_threshold}, "
122
+ f"device: {device}, max_memory: {max_memory_mb}MB, "
123
+ f"loaded: {len(self._hash_cache)} entries from disk)"
124
+ )
125
+
126
+ def _load_model(self) -> None:
127
+ """Load sentence transformer model for embeddings."""
128
+ try:
129
+ from sentence_transformers import SentenceTransformer
130
+
131
+ logger.debug(f"Loading sentence transformer model: {self.model_name}")
132
+ self._model = SentenceTransformer(self.model_name, device=self.device)
133
+ logger.info(f"Sentence transformer loaded successfully on {self.device}")
134
+
135
+ except ImportError as e:
136
+ logger.error(
137
+ f"Failed to load sentence-transformers: {e}. "
138
+ "Install with: pip install empathy-framework[cache]"
139
+ )
140
+ raise
141
+ except Exception as e:
142
+ logger.warning(f"Failed to load model {self.model_name}: {e}")
143
+ logger.warning("Falling back to hash-only mode")
144
+ self._model = None
145
+
146
+ def _load_from_storage(self) -> None:
147
+ """Load cached entries from persistent storage into memory caches."""
148
+ try:
149
+ # Get all non-expired entries from storage
150
+ entries = self._storage.get_all()
151
+
152
+ if not entries:
153
+ logger.debug("No cached entries found in storage")
154
+ return
155
+
156
+ # Populate hash cache
157
+ for entry in entries:
158
+ self._hash_cache[entry.key] = entry
159
+ self._access_times[entry.key] = entry.timestamp
160
+
161
+ logger.info(f"Loaded {len(entries)} entries from persistent storage into hash cache")
162
+
163
+ # Populate semantic cache if model available
164
+ if self._model is not None:
165
+ logger.debug("Generating embeddings for cached prompts...")
166
+ # Note: We don't have the original prompts, so semantic cache
167
+ # will be populated on-demand as cache hits occur
168
+ # This is acceptable since semantic matching is secondary to hash matching
169
+ logger.debug("Semantic cache will be populated on-demand from hash hits")
170
+
171
+ except Exception as e:
172
+ logger.warning(f"Failed to load cache from storage: {e}, starting with empty cache")
173
+
174
+ def get(
175
+ self,
176
+ workflow: str,
177
+ stage: str,
178
+ prompt: str,
179
+ model: str,
180
+ ) -> Any | None:
181
+ """Get cached response using hybrid hash + semantic matching.
182
+
183
+ Args:
184
+ workflow: Workflow name.
185
+ stage: Stage name.
186
+ prompt: Prompt text.
187
+ model: Model identifier.
188
+
189
+ Returns:
190
+ Cached response if found (hash or semantic match), None otherwise.
191
+
192
+ """
193
+ cache_key = self._create_cache_key(workflow, stage, prompt, model)
194
+ current_time = time.time()
195
+
196
+ # Step 1: Try hash cache (fast path, <5μs)
197
+ if cache_key in self._hash_cache:
198
+ entry = self._hash_cache[cache_key]
199
+
200
+ if entry.is_expired(current_time):
201
+ self._evict_entry(cache_key)
202
+ self.stats.misses += 1
203
+ return None
204
+
205
+ # Hash hit!
206
+ self._access_times[cache_key] = current_time
207
+ self.stats.hits += 1
208
+ logger.debug(
209
+ f"Cache HIT (hash): {workflow}/{stage} (hit_rate: {self.stats.hit_rate:.1f}%)"
210
+ )
211
+ return entry.response
212
+
213
+ # Step 2: Try semantic cache (smart path, ~50ms)
214
+ if self._model is not None:
215
+ semantic_result = self._semantic_lookup(prompt, workflow, stage, model)
216
+ if semantic_result is not None:
217
+ # Semantic hit! Add to hash cache for future fast lookups
218
+ entry, similarity = semantic_result
219
+ self._hash_cache[cache_key] = entry
220
+ self._access_times[cache_key] = current_time
221
+
222
+ self.stats.hits += 1
223
+ logger.debug(
224
+ f"Cache HIT (semantic): {workflow}/{stage} "
225
+ f"(similarity: {similarity:.3f}, hit_rate: {self.stats.hit_rate:.1f}%)"
226
+ )
227
+ return entry.response
228
+
229
+ # Step 3: Cache miss
230
+ self.stats.misses += 1
231
+ logger.debug(
232
+ f"Cache MISS (hybrid): {workflow}/{stage} (hit_rate: {self.stats.hit_rate:.1f}%)"
233
+ )
234
+ return None
235
+
236
+ def _semantic_lookup(
237
+ self,
238
+ prompt: str,
239
+ workflow: str,
240
+ stage: str,
241
+ model: str,
242
+ ) -> tuple[CacheEntry, float] | None:
243
+ """Perform semantic similarity lookup.
244
+
245
+ Args:
246
+ prompt: Prompt text.
247
+ workflow: Workflow name.
248
+ stage: Stage name.
249
+ model: Model identifier.
250
+
251
+ Returns:
252
+ Tuple of (CacheEntry, similarity_score) if match found, None otherwise.
253
+
254
+ """
255
+ if not self._semantic_cache:
256
+ return None
257
+
258
+ if self._model is None:
259
+ raise RuntimeError(
260
+ f"Sentence transformer model '{self.model_name}' not loaded. "
261
+ "Install required dependencies with: pip install empathy-framework[cache] "
262
+ "or pip install sentence-transformers torch"
263
+ )
264
+
265
+ # Encode prompt
266
+ prompt_embedding = self._model.encode(prompt, convert_to_numpy=True)
267
+
268
+ # Find best match
269
+ best_similarity = 0.0
270
+ best_entry = None
271
+ current_time = time.time()
272
+
273
+ for cached_embedding, entry in self._semantic_cache:
274
+ # Only match same workflow, stage, and model
275
+ if entry.workflow != workflow or entry.stage != stage or entry.model != model:
276
+ continue
277
+
278
+ # Skip expired
279
+ if entry.is_expired(current_time):
280
+ continue
281
+
282
+ # Calculate similarity
283
+ similarity = cosine_similarity(prompt_embedding, cached_embedding)
284
+
285
+ if similarity > best_similarity:
286
+ best_similarity = similarity
287
+ best_entry = entry
288
+
289
+ # Check if best match exceeds threshold
290
+ if best_similarity >= self.similarity_threshold and best_entry is not None:
291
+ return (best_entry, best_similarity)
292
+
293
+ return None
294
+
295
+ def put(
296
+ self,
297
+ workflow: str,
298
+ stage: str,
299
+ prompt: str,
300
+ model: str,
301
+ response: Any,
302
+ ttl: int | None = None,
303
+ ) -> None:
304
+ """Store response in both hash and semantic caches, and persist to disk.
305
+
306
+ Args:
307
+ workflow: Workflow name.
308
+ stage: Stage name.
309
+ prompt: Prompt text.
310
+ model: Model identifier.
311
+ response: LLM response to cache.
312
+ ttl: Optional custom TTL.
313
+
314
+ """
315
+ cache_key = self._create_cache_key(workflow, stage, prompt, model)
316
+ prompt_hash = hashlib.sha256(prompt.encode()).hexdigest()
317
+
318
+ # Create cache entry
319
+ entry = CacheEntry(
320
+ key=cache_key,
321
+ response=response,
322
+ workflow=workflow,
323
+ stage=stage,
324
+ model=model,
325
+ prompt_hash=prompt_hash,
326
+ timestamp=time.time(),
327
+ ttl=ttl or self.default_ttl,
328
+ )
329
+
330
+ # Maybe evict before adding
331
+ self._maybe_evict_lru()
332
+
333
+ # Store in hash cache
334
+ self._hash_cache[cache_key] = entry
335
+ self._access_times[cache_key] = entry.timestamp
336
+
337
+ # Store in semantic cache (if model available)
338
+ if self._model is not None:
339
+ prompt_embedding = self._model.encode(prompt, convert_to_numpy=True)
340
+ self._semantic_cache.append((prompt_embedding, entry))
341
+
342
+ # Persist to disk storage
343
+ try:
344
+ self._storage.put(entry)
345
+ logger.debug(
346
+ f"Cache PUT (hybrid): {workflow}/{stage} "
347
+ f"(hash_entries: {len(self._hash_cache)}, "
348
+ f"semantic_entries: {len(self._semantic_cache)}, "
349
+ f"persisted: True)"
350
+ )
351
+ except Exception as e:
352
+ logger.warning(f"Failed to persist cache entry to disk: {e}")
353
+ logger.debug(
354
+ f"Cache PUT (hybrid): {workflow}/{stage} "
355
+ f"(hash_entries: {len(self._hash_cache)}, "
356
+ f"semantic_entries: {len(self._semantic_cache)}, "
357
+ f"persisted: False)"
358
+ )
359
+
360
+ def clear(self) -> None:
361
+ """Clear all cached entries from memory and disk."""
362
+ hash_count = len(self._hash_cache)
363
+ semantic_count = len(self._semantic_cache)
364
+
365
+ self._hash_cache.clear()
366
+ self._access_times.clear()
367
+ self._semantic_cache.clear()
368
+
369
+ # Clear persistent storage
370
+ try:
371
+ storage_count = self._storage.clear()
372
+ logger.info(
373
+ f"Cache cleared (hash: {hash_count}, semantic: {semantic_count}, "
374
+ f"storage: {storage_count} entries)"
375
+ )
376
+ except Exception as e:
377
+ logger.warning(f"Failed to clear persistent storage: {e}")
378
+ logger.info(f"Cache cleared (hash: {hash_count}, semantic: {semantic_count} entries)")
379
+
380
+ def get_stats(self) -> CacheStats:
381
+ """Get cache statistics."""
382
+ return self.stats
383
+
384
+ def _evict_entry(self, cache_key: str) -> None:
385
+ """Remove entry from both caches.
386
+
387
+ Args:
388
+ cache_key: Key to evict.
389
+
390
+ """
391
+ # Remove from hash cache
392
+ if cache_key in self._hash_cache:
393
+ entry = self._hash_cache[cache_key]
394
+ del self._hash_cache[cache_key]
395
+
396
+ # Remove from semantic cache
397
+ self._semantic_cache = [
398
+ (emb, e) for emb, e in self._semantic_cache if e.key != entry.key
399
+ ]
400
+
401
+ if cache_key in self._access_times:
402
+ del self._access_times[cache_key]
403
+
404
+ self.stats.evictions += 1
405
+
406
+ def _maybe_evict_lru(self) -> None:
407
+ """Evict least recently used entries if cache too large."""
408
+ # Estimate memory (rough)
409
+ estimated_mb = (len(self._hash_cache) * 0.01) + (len(self._semantic_cache) * 0.1)
410
+
411
+ if estimated_mb > self.max_memory_mb:
412
+ # Evict 10% of entries
413
+ num_to_evict = max(1, len(self._hash_cache) // 10)
414
+
415
+ # Get oldest entries by access time (LRU eviction)
416
+ oldest_keys = heapq.nsmallest(
417
+ num_to_evict, self._access_times.items(), key=lambda x: x[1]
418
+ )
419
+
420
+ for cache_key, _ in oldest_keys:
421
+ self._evict_entry(cache_key)
422
+
423
+ logger.info(
424
+ f"LRU eviction: removed {num_to_evict} entries "
425
+ f"(hash: {len(self._hash_cache)}, semantic: {len(self._semantic_cache)})"
426
+ )
427
+
428
+ def evict_expired(self) -> int:
429
+ """Remove all expired entries."""
430
+ current_time = time.time()
431
+ expired_keys = [
432
+ key for key, entry in self._hash_cache.items() if entry.is_expired(current_time)
433
+ ]
434
+
435
+ for key in expired_keys:
436
+ self._evict_entry(key)
437
+
438
+ if expired_keys:
439
+ logger.info(f"Expired eviction: removed {len(expired_keys)} entries")
440
+
441
+ return len(expired_keys)
442
+
443
+ def size_info(self) -> dict[str, Any]:
444
+ """Get cache size information."""
445
+ hash_mb = len(self._hash_cache) * 0.01
446
+ semantic_mb = len(self._semantic_cache) * 0.1
447
+
448
+ return {
449
+ "hash_entries": len(self._hash_cache),
450
+ "semantic_entries": len(self._semantic_cache),
451
+ "hash_size_mb": round(hash_mb, 2),
452
+ "semantic_size_mb": round(semantic_mb, 2),
453
+ "total_size_mb": round(hash_mb + semantic_mb, 2),
454
+ "max_memory_mb": self.max_memory_mb,
455
+ "model": self.model_name,
456
+ "threshold": self.similarity_threshold,
457
+ }