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/config.py ADDED
@@ -0,0 +1,545 @@
1
+ """Configuration Management for Empathy Framework
2
+
3
+ Supports:
4
+ - YAML configuration files
5
+ - JSON configuration files
6
+ - Environment variables
7
+ - Default configuration
8
+
9
+ Copyright 2025 Smart AI Memory, LLC
10
+ Licensed under Fair Source 0.9
11
+ """
12
+
13
+ import json
14
+ import os
15
+ from dataclasses import asdict, dataclass, field
16
+ from pathlib import Path
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ if TYPE_CHECKING:
20
+ from attune.workflows.config import ModelConfig
21
+
22
+ try:
23
+ import yaml
24
+
25
+ YAML_AVAILABLE = True
26
+ except ImportError:
27
+ YAML_AVAILABLE = False
28
+
29
+
30
+ def _validate_file_path(path: str, allowed_dir: str | None = None) -> Path:
31
+ """Validate file path to prevent path traversal and arbitrary writes.
32
+
33
+ Args:
34
+ path: File path to validate
35
+ allowed_dir: Optional directory to restrict writes to
36
+
37
+ Returns:
38
+ Validated Path object
39
+
40
+ Raises:
41
+ ValueError: If path is invalid or unsafe
42
+ """
43
+ if not path or not isinstance(path, str):
44
+ raise ValueError("path must be a non-empty string")
45
+
46
+ # Check for null bytes
47
+ if "\x00" in path:
48
+ raise ValueError("path contains null bytes")
49
+
50
+ try:
51
+ resolved = Path(path).resolve()
52
+ except (OSError, RuntimeError) as e:
53
+ raise ValueError(f"Invalid path: {e}")
54
+
55
+ # Check if within allowed directory
56
+ if allowed_dir:
57
+ try:
58
+ allowed = Path(allowed_dir).resolve()
59
+ resolved.relative_to(allowed)
60
+ except ValueError:
61
+ raise ValueError(f"path must be within {allowed_dir}")
62
+
63
+ # Check for dangerous system paths
64
+ # Note: On macOS, /etc is a symlink to /private/etc, so we check both
65
+ dangerous_paths = [
66
+ "/etc",
67
+ "/sys",
68
+ "/proc",
69
+ "/dev",
70
+ "/private/etc", # macOS: /etc -> /private/etc
71
+ "/private/var/root", # macOS: root's home directory
72
+ "/usr/bin", # System binaries
73
+ "/usr/sbin", # System admin binaries
74
+ "/bin", # Essential binaries
75
+ "/sbin", # System binaries
76
+ ]
77
+ resolved_str = str(resolved)
78
+ for dangerous in dangerous_paths:
79
+ if resolved_str.startswith(dangerous + "/") or resolved_str == dangerous:
80
+ raise ValueError(f"Cannot write to system directory: {dangerous}")
81
+
82
+ return resolved
83
+
84
+
85
+ @dataclass
86
+ class EmpathyConfig:
87
+ """Configuration for EmpathyOS instance
88
+
89
+ Can be loaded from:
90
+ - YAML file (.empathy.yml, attune.config.yml)
91
+ - JSON file (.empathy.json, attune.config.json)
92
+ - Environment variables (EMPATHY_*)
93
+ - Direct instantiation
94
+ """
95
+
96
+ # Core settings
97
+ user_id: str = "default_user"
98
+ target_level: int = 3
99
+ confidence_threshold: float = 0.75
100
+
101
+ # Trust settings
102
+ trust_building_rate: float = 0.05
103
+ trust_erosion_rate: float = 0.10
104
+
105
+ # Persistence settings
106
+ persistence_enabled: bool = True
107
+ persistence_backend: str = "sqlite" # "sqlite", "json", "none"
108
+ persistence_path: str = "./empathy_data"
109
+
110
+ # State management
111
+ state_persistence: bool = True
112
+ state_path: str = "./empathy_state"
113
+
114
+ # Metrics settings
115
+ metrics_enabled: bool = True
116
+ metrics_path: str = "./metrics.db"
117
+
118
+ # Logging settings
119
+ log_level: str = "INFO"
120
+ log_file: str | None = None
121
+ structured_logging: bool = True
122
+
123
+ # Pattern library settings
124
+ pattern_library_enabled: bool = True
125
+ pattern_sharing: bool = True
126
+ pattern_confidence_threshold: float = 0.3
127
+
128
+ # Advanced settings
129
+ async_enabled: bool = True
130
+ feedback_loop_monitoring: bool = True
131
+ leverage_point_analysis: bool = True
132
+
133
+ # Custom metadata
134
+ metadata: dict[str, Any] = field(default_factory=dict)
135
+
136
+ # Model settings
137
+ models: list["ModelConfig"] = field(default_factory=list)
138
+ default_model: str | None = None
139
+ log_path: str | None = None
140
+ max_threads: int = 4
141
+ model_router: dict[str, Any] | None = None
142
+
143
+ def __post_init__(self):
144
+ """Post-initialization validation."""
145
+ if self.default_model and not any(m.name == self.default_model for m in self.models):
146
+ raise ValueError(f"Default model '{self.default_model}' not in models.")
147
+
148
+ @classmethod
149
+ def from_yaml(cls, filepath: str) -> "EmpathyConfig":
150
+ """Load configuration from YAML file
151
+
152
+ Args:
153
+ filepath: Path to YAML configuration file
154
+
155
+ Returns:
156
+ EmpathyConfig instance
157
+
158
+ Raises:
159
+ ImportError: If PyYAML is not installed
160
+ FileNotFoundError: If file doesn't exist
161
+
162
+ Example:
163
+ >>> config = EmpathyConfig.from_yaml("attune.config.yml")
164
+ >>> empathy = EmpathyOS(config=config)
165
+
166
+ Note:
167
+ Unknown fields in the YAML file are silently ignored.
168
+ This allows config files to contain settings for other
169
+ components (e.g., model_preferences, workflows) without
170
+ breaking EmpathyConfig loading.
171
+
172
+ """
173
+ if not YAML_AVAILABLE:
174
+ raise ImportError(
175
+ "PyYAML is required for YAML configuration. Install with: pip install pyyaml",
176
+ )
177
+
178
+ with open(filepath) as f:
179
+ data = yaml.safe_load(f)
180
+
181
+ # Filter to only known fields (gracefully ignore unknown fields like
182
+ # 'provider', 'model_preferences', 'workflows', etc.)
183
+ from dataclasses import fields as dataclass_fields
184
+
185
+ valid_fields = {f.name for f in dataclass_fields(cls)}
186
+ filtered_data = {k: v for k, v in data.items() if k in valid_fields}
187
+
188
+ return cls.from_dict(filtered_data)
189
+
190
+ @classmethod
191
+ def from_dict(cls, data: dict[str, Any]) -> "EmpathyConfig":
192
+ """Create an EmpathyConfig from a dictionary, ignoring unknown fields."""
193
+ known_fields = {f.name for f in cls.__dataclass_fields__.values()}
194
+ filtered_data = {k: v for k, v in data.items() if k in known_fields}
195
+
196
+ # Handle nested ModelConfig objects
197
+ if filtered_data.get("models"):
198
+ from attune.workflows.config import ModelConfig
199
+
200
+ filtered_data["models"] = [ModelConfig(**m) for m in filtered_data["models"]]
201
+
202
+ return cls(**filtered_data)
203
+
204
+ @classmethod
205
+ def from_json(cls, filepath: str) -> "EmpathyConfig":
206
+ """Load configuration from JSON file
207
+
208
+ Args:
209
+ filepath: Path to JSON configuration file
210
+
211
+ Returns:
212
+ EmpathyConfig instance
213
+
214
+ Example:
215
+ >>> config = EmpathyConfig.from_json("attune.config.json")
216
+ >>> empathy = EmpathyOS(config=config)
217
+
218
+ Note:
219
+ Unknown fields in the JSON file are silently ignored.
220
+
221
+ """
222
+ with open(filepath) as f:
223
+ data = json.load(f)
224
+
225
+ # Filter to only known fields (gracefully ignore unknown fields)
226
+ from dataclasses import fields as dataclass_fields
227
+
228
+ valid_fields = {f.name for f in dataclass_fields(cls)}
229
+ filtered_data = {k: v for k, v in data.items() if k in valid_fields}
230
+
231
+ return cls(**filtered_data)
232
+
233
+ @classmethod
234
+ def from_env(cls, prefix: str = "EMPATHY_") -> "EmpathyConfig":
235
+ """Load configuration from environment variables
236
+
237
+ Environment variables should be prefixed with EMPATHY_
238
+ and match config field names in uppercase.
239
+
240
+ Example:
241
+ EMPATHY_USER_ID=alice
242
+ EMPATHY_TARGET_LEVEL=4
243
+ EMPATHY_CONFIDENCE_THRESHOLD=0.8
244
+
245
+ Args:
246
+ prefix: Environment variable prefix (default: "EMPATHY_")
247
+
248
+ Returns:
249
+ EmpathyConfig instance
250
+
251
+ Example:
252
+ >>> os.environ["EMPATHY_USER_ID"] = "alice"
253
+ >>> config = EmpathyConfig.from_env()
254
+ >>> print(config.user_id) # "alice"
255
+
256
+ """
257
+ from dataclasses import fields as dataclass_fields
258
+
259
+ # Get valid field names from the dataclass
260
+ valid_fields = {f.name for f in dataclass_fields(cls)}
261
+
262
+ data: dict[str, Any] = {}
263
+
264
+ # Get all environment variables with prefix
265
+ for key, value in os.environ.items():
266
+ if key.startswith(prefix):
267
+ # Convert EMPATHY_USER_ID -> user_id
268
+ field_name = key[len(prefix) :].lower()
269
+
270
+ # Skip unknown fields (e.g., EMPATHY_MASTER_KEY for encryption)
271
+ if field_name not in valid_fields:
272
+ continue
273
+
274
+ # Type conversion based on field name
275
+ if field_name in ("target_level",):
276
+ data[field_name] = int(value)
277
+ elif field_name in (
278
+ "confidence_threshold",
279
+ "trust_building_rate",
280
+ "trust_erosion_rate",
281
+ "pattern_confidence_threshold",
282
+ ):
283
+ data[field_name] = float(value)
284
+ elif field_name in (
285
+ "persistence_enabled",
286
+ "state_persistence",
287
+ "metrics_enabled",
288
+ "structured_logging",
289
+ "pattern_library_enabled",
290
+ "pattern_sharing",
291
+ "async_enabled",
292
+ "feedback_loop_monitoring",
293
+ "leverage_point_analysis",
294
+ ):
295
+ data[field_name] = value.lower() in ("true", "1", "yes")
296
+ else:
297
+ data[field_name] = value
298
+
299
+ return cls(**data)
300
+
301
+ @classmethod
302
+ def from_file(cls, filepath: str | None = None) -> "EmpathyConfig":
303
+ """Automatically detect and load configuration from file
304
+
305
+ Looks for configuration files in this order:
306
+ 1. Provided filepath
307
+ 2. .empathy.yml
308
+ 3. .empathy.yaml
309
+ 4. attune.config.yml
310
+ 5. attune.config.yaml
311
+ 6. .empathy.json
312
+ 7. attune.config.json
313
+
314
+ Args:
315
+ filepath: Optional explicit path to config file
316
+
317
+ Returns:
318
+ EmpathyConfig instance, or default if no file found
319
+
320
+ Example:
321
+ >>> config = EmpathyConfig.from_file() # Auto-detect
322
+ >>> config = EmpathyConfig.from_file("my-config.yml")
323
+
324
+ """
325
+ search_paths = [
326
+ filepath,
327
+ ".empathy.yml",
328
+ ".empathy.yaml",
329
+ "attune.config.yml",
330
+ "attune.config.yaml",
331
+ ".empathy.json",
332
+ "attune.config.json",
333
+ ]
334
+
335
+ for path in search_paths:
336
+ if path and Path(path).exists():
337
+ if path.endswith((".yml", ".yaml")):
338
+ return cls.from_yaml(path)
339
+ if path.endswith(".json"):
340
+ return cls.from_json(path)
341
+
342
+ # No config file found - return default
343
+ return cls()
344
+
345
+ def to_yaml(self, filepath: str):
346
+ """Save configuration to YAML file
347
+
348
+ Args:
349
+ filepath: Path to save YAML file
350
+
351
+ Example:
352
+ >>> config = EmpathyConfig(user_id="alice", target_level=4)
353
+ >>> config.to_yaml("my-config.yml")
354
+
355
+ """
356
+ if not YAML_AVAILABLE:
357
+ raise ImportError(
358
+ "PyYAML is required for YAML export. Install with: pip install pyyaml",
359
+ )
360
+
361
+ validated_path = _validate_file_path(filepath)
362
+ data = asdict(self)
363
+
364
+ with open(validated_path, "w") as f:
365
+ yaml.dump(data, f, default_flow_style=False, sort_keys=False)
366
+
367
+ def to_json(self, filepath: str, indent: int = 2):
368
+ """Save configuration to JSON file
369
+
370
+ Args:
371
+ filepath: Path to save JSON file
372
+ indent: JSON indentation (default: 2)
373
+
374
+ Example:
375
+ >>> config = EmpathyConfig(user_id="alice", target_level=4)
376
+ >>> config.to_json("my-config.json")
377
+
378
+ """
379
+ validated_path = _validate_file_path(filepath)
380
+ data = asdict(self)
381
+
382
+ with open(validated_path, "w") as f:
383
+ json.dump(data, f, indent=indent)
384
+
385
+ def to_dict(self) -> dict[str, Any]:
386
+ """Convert configuration to dictionary"""
387
+ return asdict(self)
388
+
389
+ def update(self, **kwargs):
390
+ """Update configuration fields
391
+
392
+ Args:
393
+ **kwargs: Fields to update
394
+
395
+ Example:
396
+ >>> config = EmpathyConfig()
397
+ >>> config.update(user_id="bob", target_level=5)
398
+
399
+ """
400
+ for key, value in kwargs.items():
401
+ if hasattr(self, key):
402
+ setattr(self, key, value)
403
+
404
+ def merge(self, other: "EmpathyConfig") -> "EmpathyConfig":
405
+ """Merge with another configuration (other takes precedence)
406
+
407
+ Args:
408
+ other: Configuration to merge
409
+
410
+ Returns:
411
+ New merged configuration
412
+
413
+ Example:
414
+ >>> base = EmpathyConfig(user_id="alice")
415
+ >>> override = EmpathyConfig(target_level=5)
416
+ >>> merged = base.merge(override)
417
+
418
+ """
419
+ # Start with base values
420
+ base_dict = self.to_dict()
421
+ other_dict = other.to_dict()
422
+
423
+ # Get default values for comparison
424
+ defaults = EmpathyConfig().to_dict()
425
+
426
+ # Only update fields from 'other' that differ from defaults
427
+ for key, value in other_dict.items():
428
+ if value != defaults.get(key):
429
+ base_dict[key] = value
430
+
431
+ return EmpathyConfig(**base_dict)
432
+
433
+ def validate(self) -> bool:
434
+ """Validate configuration values
435
+
436
+ Returns:
437
+ True if valid, raises ValueError if invalid
438
+
439
+ Raises:
440
+ ValueError: If configuration is invalid
441
+
442
+ """
443
+ if self.target_level not in range(1, 6):
444
+ raise ValueError(f"target_level must be 1-5, got {self.target_level}")
445
+
446
+ if not 0.0 <= self.confidence_threshold <= 1.0:
447
+ raise ValueError(
448
+ f"confidence_threshold must be 0.0-1.0, got {self.confidence_threshold}",
449
+ )
450
+
451
+ if not 0.0 <= self.pattern_confidence_threshold <= 1.0:
452
+ threshold_val = self.pattern_confidence_threshold
453
+ raise ValueError(f"pattern_confidence_threshold must be 0.0-1.0, got {threshold_val}")
454
+
455
+ if self.persistence_backend not in ("sqlite", "json", "none"):
456
+ backend_val = self.persistence_backend
457
+ raise ValueError(
458
+ f"persistence_backend must be 'sqlite', 'json', or 'none', got {backend_val}",
459
+ )
460
+
461
+ return True
462
+
463
+ def __repr__(self) -> str:
464
+ """String representation"""
465
+ return (
466
+ f"EmpathyConfig(user_id={self.user_id!r}, target_level={self.target_level}, "
467
+ f"confidence_threshold={self.confidence_threshold})"
468
+ )
469
+
470
+
471
+ def load_config(
472
+ filepath: str | None = None,
473
+ use_env: bool = True,
474
+ defaults: dict[str, Any] | None = None,
475
+ ) -> EmpathyConfig:
476
+ """Load configuration with flexible precedence
477
+
478
+ Precedence (highest to lowest):
479
+ 1. Environment variables (if use_env=True)
480
+ 2. Configuration file (if provided/found)
481
+ 3. Defaults (if provided)
482
+ 4. Built-in defaults
483
+
484
+ Args:
485
+ filepath: Optional path to config file
486
+ use_env: Whether to check environment variables (default: True)
487
+ defaults: Optional default values
488
+
489
+ Returns:
490
+ EmpathyConfig instance
491
+
492
+ Example:
493
+ >>> # Load from file, override with env vars
494
+ >>> config = load_config("empathy.yml", use_env=True)
495
+
496
+ >>> # Load with custom defaults
497
+ >>> config = load_config(defaults={"target_level": 4})
498
+
499
+ """
500
+ # Start with built-in defaults
501
+ config = EmpathyConfig()
502
+
503
+ # Apply custom defaults
504
+ if defaults:
505
+ config.update(**defaults)
506
+
507
+ # Load from file if provided/found
508
+ # First check if a file actually exists
509
+ file_found = False
510
+ if filepath and Path(filepath).exists():
511
+ file_found = True
512
+ else:
513
+ # Check default config file locations
514
+ for default_path in [
515
+ ".empathy.yml",
516
+ ".empathy.yaml",
517
+ "attune.config.yml",
518
+ "attune.config.yaml",
519
+ ".empathy.json",
520
+ "attune.config.json",
521
+ ]:
522
+ if Path(default_path).exists():
523
+ file_found = True
524
+ break
525
+
526
+ if file_found:
527
+ try:
528
+ file_config = EmpathyConfig.from_file(filepath)
529
+ config = config.merge(file_config)
530
+ except (FileNotFoundError, json.JSONDecodeError):
531
+ pass # Use defaults
532
+
533
+ # Override with environment variables
534
+ if use_env:
535
+ try:
536
+ env_config = EmpathyConfig.from_env()
537
+ config = config.merge(env_config)
538
+ except (ValueError, TypeError):
539
+ # Graceful fallback: invalid env var type conversion
540
+ pass # Use current config if environment parsing fails
541
+
542
+ # Validate final configuration
543
+ config.validate()
544
+
545
+ return config