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,441 @@
1
+ """Leverage Point Analysis for System Interventions
2
+
3
+ Identifies high-leverage intervention points based on Donella Meadows's
4
+ seminal work "Leverage Points: Places to Intervene in a System".
5
+
6
+ Leverage points are places within a complex system where a small shift
7
+ can produce big changes in system behavior.
8
+
9
+ Copyright 2025 Smart AI Memory, LLC
10
+ Licensed under Fair Source 0.9
11
+ """
12
+
13
+ import heapq
14
+ from dataclasses import dataclass, field
15
+ from enum import IntEnum
16
+ from typing import Any
17
+
18
+
19
+ class LeverageLevel(IntEnum):
20
+ """Donella Meadows's 12 Leverage Points (ordered by effectiveness)
21
+
22
+ Higher numbers = More effective leverage points
23
+ Lower numbers = Less effective (but often easier to implement)
24
+ """
25
+
26
+ # Low leverage (easy to change, small impact)
27
+ PARAMETERS = 1 # Constants, numbers (least effective)
28
+ BUFFERS = 2 # Stabilizing stocks relative to flows
29
+ STOCK_FLOW = 3 # Physical structure of system
30
+ DELAYS = 4 # Length of delays relative to change rate
31
+ BALANCING_LOOPS = 5 # Strength of negative feedback loops
32
+ REINFORCING_LOOPS = 6 # Strength of positive feedback loops
33
+
34
+ # Medium leverage
35
+ INFORMATION_FLOWS = 7 # Structure of information flows
36
+ RULES = 8 # Rules of the system (incentives, constraints)
37
+ SELF_ORGANIZATION = 9 # Power to add/change system structure
38
+
39
+ # High leverage (hard to change, huge impact)
40
+ GOALS = 10 # Goals of the system
41
+ PARADIGM = 11 # Mindset or paradigm out of which system arises
42
+ TRANSCEND_PARADIGM = 12 # Power to transcend paradigms (most effective)
43
+
44
+
45
+ @dataclass
46
+ class LeveragePoint:
47
+ """A specific leverage point identified in the system
48
+
49
+ Represents a place where intervention can create significant change
50
+ in system behavior.
51
+ """
52
+
53
+ level: LeverageLevel
54
+ description: str
55
+ problem_domain: str
56
+ impact_potential: float = 0.5 # 0.0-1.0
57
+ implementation_difficulty: float = 0.5 # 0.0-1.0
58
+ current_state: str | None = None
59
+ proposed_intervention: str | None = None
60
+ expected_outcomes: list[str] = field(default_factory=list)
61
+ risks: list[str] = field(default_factory=list)
62
+
63
+
64
+ class LeveragePointAnalyzer:
65
+ """Identifies high-leverage intervention points in AI-human systems
66
+
67
+ Based on Donella Meadows's 12 leverage points. Helps identify where
68
+ to intervene in a system for maximum effectiveness.
69
+
70
+ **The 12 Leverage Points (in increasing order of effectiveness):**
71
+
72
+ 1. **Parameters**: Constants, numbers (e.g., change tax rate)
73
+ - Least effective but easiest to change
74
+
75
+ 2. **Buffers**: Size of stabilizing stocks
76
+ - Example: inventory sizes, account balances
77
+
78
+ 3. **Stock-Flow Structure**: Physical system structure
79
+ - Example: road networks, factories
80
+
81
+ 4. **Delays**: Length of delays relative to rate of change
82
+ - Example: feedback delay in learning systems
83
+
84
+ 5. **Balancing Feedback Loops**: Strength of stabilizing loops
85
+ - Example: quality control mechanisms
86
+
87
+ 6. **Reinforcing Feedback Loops**: Strength of amplifying loops
88
+ - Example: compound interest, viral growth
89
+
90
+ 7. **Information Flows**: Structure of information flows
91
+ - Example: transparency, data access
92
+
93
+ 8. **Rules**: Incentives, punishments, constraints
94
+ - Example: laws, policies, protocols
95
+
96
+ 9. **Self-Organization**: Power to evolve system structure
97
+ - Example: ability to create new rules
98
+
99
+ 10. **Goals**: Purpose or function of the system
100
+ - Example: shift from growth to sustainability
101
+
102
+ 11. **Paradigm**: Mindset underlying the system
103
+ - Example: worldview, shared assumptions
104
+
105
+ 12. **Transcending Paradigms**: Keep perspective on all paradigms
106
+ - Most effective but hardest to achieve
107
+
108
+ Example:
109
+ >>> analyzer = LeveragePointAnalyzer()
110
+ >>> problem = {
111
+ ... "class": "documentation_burden",
112
+ ... "description": "Developers spend 40% time on repetitive docs",
113
+ ... "instances": 18
114
+ ... }
115
+ >>> points = analyzer.find_leverage_points(problem)
116
+ >>> for point in points:
117
+ ... print(f"{point.level.name}: {point.description}")
118
+
119
+ """
120
+
121
+ def __init__(self):
122
+ """Initialize LeveragePointAnalyzer"""
123
+ self.identified_points: list[LeveragePoint] = []
124
+
125
+ def find_leverage_points(self, problem_class: dict[str, Any]) -> list[LeveragePoint]:
126
+ """Find high-leverage intervention points for a problem class
127
+
128
+ Analyzes the problem and identifies leverage points at different
129
+ levels of the Meadows hierarchy.
130
+
131
+ Args:
132
+ problem_class: Dict with keys:
133
+ - class: Problem category (e.g., "documentation_burden")
134
+ - description: Problem description
135
+ - instances: Number of occurrences (optional)
136
+ - context: Additional context (optional)
137
+
138
+ Returns:
139
+ List of leverage points, ranked by effectiveness
140
+
141
+ Example:
142
+ >>> problem = {
143
+ ... "class": "trust_deficit",
144
+ ... "description": "Users don't trust AI recommendations",
145
+ ... "instances": 50
146
+ ... }
147
+ >>> points = analyzer.find_leverage_points(problem)
148
+
149
+ """
150
+ points: list[LeveragePoint] = []
151
+ problem_type = problem_class.get("class", "unknown")
152
+ description = problem_class.get("description", "")
153
+
154
+ # Analyze based on problem type
155
+ if "documentation" in problem_type.lower() or "documentation" in description.lower():
156
+ points.extend(self._analyze_documentation_problem(problem_class))
157
+
158
+ elif "trust" in problem_type.lower() or "trust" in description.lower():
159
+ points.extend(self._analyze_trust_problem(problem_class))
160
+
161
+ elif "efficiency" in problem_type.lower() or "speed" in description.lower():
162
+ points.extend(self._analyze_efficiency_problem(problem_class))
163
+
164
+ else:
165
+ # Generic analysis for unknown problem types
166
+ points.extend(self._generic_leverage_analysis(problem_class))
167
+
168
+ # Rank by effectiveness (leverage level)
169
+ points_ranked = self.rank_by_effectiveness(points)
170
+
171
+ self.identified_points.extend(points_ranked)
172
+ return points_ranked
173
+
174
+ def rank_by_effectiveness(self, points: list[LeveragePoint]) -> list[LeveragePoint]:
175
+ """Rank leverage points by Meadows's hierarchy
176
+
177
+ Higher leverage levels (paradigms, goals) ranked before lower
178
+ levels (parameters, buffers).
179
+
180
+ Args:
181
+ points: List of leverage points to rank
182
+
183
+ Returns:
184
+ Sorted list with most effective points first
185
+
186
+ """
187
+ return sorted(points, key=lambda p: p.level, reverse=True)
188
+
189
+ def _analyze_documentation_problem(self, problem: dict[str, Any]) -> list[LeveragePoint]:
190
+ """Analyze leverage points for documentation problems"""
191
+ points = []
192
+
193
+ # High leverage: Change paradigm (how we think about docs)
194
+ points.append(
195
+ LeveragePoint(
196
+ level=LeverageLevel.PARADIGM,
197
+ description="Paradigm shift: Docs as learning artifact, not compliance burden",
198
+ problem_domain="documentation",
199
+ impact_potential=0.9,
200
+ implementation_difficulty=0.8,
201
+ proposed_intervention="Reframe docs as 'capturing team learning' not 'creating artifacts'",
202
+ expected_outcomes=[
203
+ "Developers see value in documentation",
204
+ "Documentation becomes natural part of workflow",
205
+ "Quality improves as purpose clarifies",
206
+ ],
207
+ ),
208
+ )
209
+
210
+ # High leverage: Change goal
211
+ points.append(
212
+ LeveragePoint(
213
+ level=LeverageLevel.GOALS,
214
+ description="Change goal: From 'comprehensive docs' to 'shared understanding'",
215
+ problem_domain="documentation",
216
+ impact_potential=0.85,
217
+ implementation_difficulty=0.6,
218
+ proposed_intervention="Optimize for team understanding not document completeness",
219
+ expected_outcomes=[
220
+ "Focus on what matters",
221
+ "Less redundant documentation",
222
+ "More collaboration",
223
+ ],
224
+ ),
225
+ )
226
+
227
+ # Medium leverage: Self-organization (Level 5 systems thinking)
228
+ points.append(
229
+ LeveragePoint(
230
+ level=LeverageLevel.SELF_ORGANIZATION,
231
+ description="Enable self-organization: AI agents auto-generate docs from patterns",
232
+ problem_domain="documentation",
233
+ impact_potential=0.8,
234
+ implementation_difficulty=0.5,
235
+ proposed_intervention="Deploy Level 5 system that detects patterns and auto-documents",
236
+ expected_outcomes=[
237
+ "Reduce manual documentation by 70%",
238
+ "Free developers for creative work",
239
+ "Maintain quality through pattern detection",
240
+ ],
241
+ ),
242
+ )
243
+
244
+ # Low leverage: Parameters (quickest but least impactful)
245
+ points.append(
246
+ LeveragePoint(
247
+ level=LeverageLevel.PARAMETERS,
248
+ description="Adjust parameters: Reduce required documentation fields",
249
+ problem_domain="documentation",
250
+ impact_potential=0.3,
251
+ implementation_difficulty=0.1,
252
+ proposed_intervention="Cut required fields from 20 to 8",
253
+ expected_outcomes=["Faster documentation", "May not address root cause"],
254
+ ),
255
+ )
256
+
257
+ return points
258
+
259
+ def _analyze_trust_problem(self, problem: dict[str, Any]) -> list[LeveragePoint]:
260
+ """Analyze leverage points for trust problems"""
261
+ points = []
262
+
263
+ # High leverage: Paradigm shift
264
+ points.append(
265
+ LeveragePoint(
266
+ level=LeverageLevel.PARADIGM,
267
+ description="Shift paradigm: AI as collaborator, not tool",
268
+ problem_domain="trust",
269
+ impact_potential=0.9,
270
+ implementation_difficulty=0.8,
271
+ proposed_intervention="Reframe AI relationship from automation to collaboration",
272
+ expected_outcomes=[
273
+ "Users engage differently with AI",
274
+ "Set appropriate expectations",
275
+ "Build genuine partnership",
276
+ ],
277
+ ),
278
+ )
279
+
280
+ # High leverage: Information flows
281
+ points.append(
282
+ LeveragePoint(
283
+ level=LeverageLevel.INFORMATION_FLOWS,
284
+ description="Increase transparency: Show AI reasoning process",
285
+ problem_domain="trust",
286
+ impact_potential=0.75,
287
+ implementation_difficulty=0.4,
288
+ proposed_intervention="Implement explainable AI with visible reasoning chains",
289
+ expected_outcomes=[
290
+ "Users understand AI decisions",
291
+ "Can verify AI logic",
292
+ "Trust through transparency",
293
+ ],
294
+ ),
295
+ )
296
+
297
+ # Medium leverage: Reinforcing feedback loops
298
+ points.append(
299
+ LeveragePoint(
300
+ level=LeverageLevel.REINFORCING_LOOPS,
301
+ description="Activate virtuous cycle: Success → Trust → Delegation → More Success",
302
+ problem_domain="trust",
303
+ impact_potential=0.7,
304
+ implementation_difficulty=0.5,
305
+ proposed_intervention="Start with high-confidence, low-risk tasks for momentum",
306
+ expected_outcomes=[
307
+ "Quick wins build trust",
308
+ "Positive feedback loop activated",
309
+ "Natural progression to harder tasks",
310
+ ],
311
+ ),
312
+ )
313
+
314
+ return points
315
+
316
+ def _analyze_efficiency_problem(self, problem: dict[str, Any]) -> list[LeveragePoint]:
317
+ """Analyze leverage points for efficiency problems"""
318
+ points = []
319
+
320
+ # High leverage: Goals
321
+ points.append(
322
+ LeveragePoint(
323
+ level=LeverageLevel.GOALS,
324
+ description="Redefine goal: From 'fast completion' to 'sustainable pace'",
325
+ problem_domain="efficiency",
326
+ impact_potential=0.8,
327
+ implementation_difficulty=0.6,
328
+ proposed_intervention="Optimize for long-term throughput not short-term speed",
329
+ expected_outcomes=["Prevent burnout", "Sustainable productivity", "Higher quality"],
330
+ ),
331
+ )
332
+
333
+ # Medium leverage: Delays
334
+ points.append(
335
+ LeveragePoint(
336
+ level=LeverageLevel.DELAYS,
337
+ description="Reduce feedback delays: Real-time testing and validation",
338
+ problem_domain="efficiency",
339
+ impact_potential=0.65,
340
+ implementation_difficulty=0.4,
341
+ proposed_intervention="Implement continuous testing with instant feedback",
342
+ expected_outcomes=["Faster iteration", "Catch errors early", "Better learning"],
343
+ ),
344
+ )
345
+
346
+ return points
347
+
348
+ def _generic_leverage_analysis(self, problem: dict[str, Any]) -> list[LeveragePoint]:
349
+ """Generic leverage point analysis for unknown problem types"""
350
+ points = []
351
+
352
+ # Always consider paradigm shift (highest leverage)
353
+ points.append(
354
+ LeveragePoint(
355
+ level=LeverageLevel.PARADIGM,
356
+ description="Question underlying assumptions about this problem",
357
+ problem_domain=problem.get("class", "unknown"),
358
+ impact_potential=0.8,
359
+ implementation_difficulty=0.8,
360
+ proposed_intervention="Examine and challenge fundamental beliefs about problem",
361
+ ),
362
+ )
363
+
364
+ # Consider information flows
365
+ points.append(
366
+ LeveragePoint(
367
+ level=LeverageLevel.INFORMATION_FLOWS,
368
+ description="Improve information flow and transparency",
369
+ problem_domain=problem.get("class", "unknown"),
370
+ impact_potential=0.6,
371
+ implementation_difficulty=0.4,
372
+ proposed_intervention="Make relevant information more accessible to stakeholders",
373
+ ),
374
+ )
375
+
376
+ return points
377
+
378
+ def get_top_leverage_points(
379
+ self,
380
+ n: int = 3,
381
+ min_level: LeverageLevel | None = None,
382
+ ) -> list[LeveragePoint]:
383
+ """Get top N leverage points, optionally filtered by minimum level
384
+
385
+ Args:
386
+ n: Number of top points to return
387
+ min_level: Optional minimum leverage level to consider
388
+
389
+ Returns:
390
+ Top N leverage points
391
+
392
+ """
393
+ points = self.identified_points
394
+
395
+ if min_level:
396
+ points = [p for p in points if p.level >= min_level]
397
+
398
+ return heapq.nlargest(n, points, key=lambda p: p.level)
399
+
400
+ def analyze_intervention_feasibility(self, point: LeveragePoint) -> dict[str, Any]:
401
+ """Analyze feasibility of intervening at a leverage point
402
+
403
+ Considers:
404
+ - Impact potential
405
+ - Implementation difficulty
406
+ - Risk level
407
+ - Time to results
408
+
409
+ Args:
410
+ point: Leverage point to analyze
411
+
412
+ Returns:
413
+ Feasibility analysis with recommendation
414
+
415
+ """
416
+ # Calculate feasibility score (impact vs difficulty)
417
+ feasibility_score = point.impact_potential / max(point.implementation_difficulty, 0.1)
418
+
419
+ # Determine recommendation
420
+ if feasibility_score > 1.5:
421
+ recommendation = "HIGHLY RECOMMENDED: High impact, manageable difficulty"
422
+ elif feasibility_score > 1.0:
423
+ recommendation = "RECOMMENDED: Good balance of impact and feasibility"
424
+ elif feasibility_score > 0.7:
425
+ recommendation = "CONSIDER: Significant effort but worthwhile impact"
426
+ else:
427
+ recommendation = "CAUTION: High difficulty relative to impact"
428
+
429
+ return {
430
+ "leverage_level": point.level.name,
431
+ "impact_potential": point.impact_potential,
432
+ "implementation_difficulty": point.implementation_difficulty,
433
+ "feasibility_score": feasibility_score,
434
+ "recommendation": recommendation,
435
+ "risks": point.risks,
436
+ "expected_outcomes": point.expected_outcomes,
437
+ }
438
+
439
+ def reset(self):
440
+ """Reset analyzer state"""
441
+ self.identified_points = []
@@ -0,0 +1,261 @@
1
+ """Centralized Logging Configuration for Empathy Framework
2
+
3
+ Provides professional logging setup with:
4
+ - Console and file logging
5
+ - Structured logging with context
6
+ - Log rotation for production use
7
+ - Configurable log levels
8
+
9
+ Copyright 2025 Smart AI Memory, LLC
10
+ Licensed under Fair Source 0.9
11
+ """
12
+
13
+ import logging
14
+ import logging.handlers
15
+ import os
16
+ import sys
17
+ from pathlib import Path
18
+
19
+
20
+ class StructuredFormatter(logging.Formatter):
21
+ """Custom formatter for structured logging with context."""
22
+
23
+ COLORS = {
24
+ "DEBUG": "\033[36m", # Cyan
25
+ "INFO": "\033[32m", # Green
26
+ "WARNING": "\033[33m", # Yellow
27
+ "ERROR": "\033[31m", # Red
28
+ "CRITICAL": "\033[35m", # Magenta
29
+ }
30
+ RESET = "\033[0m"
31
+ BOLD = "\033[1m"
32
+
33
+ def __init__(self, use_color: bool = True, include_context: bool = False):
34
+ """Initialize formatter.
35
+
36
+ Args:
37
+ use_color: Whether to use colored output
38
+ include_context: Whether to include contextual information
39
+
40
+ """
41
+ self.use_color = use_color and sys.stderr.isatty()
42
+ self.include_context = include_context
43
+ super().__init__()
44
+
45
+ def format(self, record: logging.LogRecord) -> str:
46
+ """Format log record with optional colors and context."""
47
+ # Get color for this level
48
+ color = self.COLORS.get(record.levelname, self.RESET) if self.use_color else ""
49
+ reset = self.RESET if self.use_color else ""
50
+
51
+ # Build the main log message
52
+ timestamp = self.formatTime(record, "%Y-%m-%d %H:%M:%S")
53
+ level_name = f"{color}{record.levelname}{reset}" if self.use_color else record.levelname
54
+
55
+ # Format: [TIMESTAMP] [LEVEL] module.function: message
56
+ log_msg = (
57
+ f"[{timestamp}] [{level_name}] {record.name}:{record.funcName}: {record.getMessage()}"
58
+ )
59
+
60
+ # Add context if available
61
+ if self.include_context and hasattr(record, "context"):
62
+ context_str = " ".join(f"{k}={v}" for k, v in record.context.items())
63
+ log_msg += f" [{context_str}]"
64
+
65
+ # Add exception info if present
66
+ if record.exc_info:
67
+ log_msg += "\n" + self.formatException(record.exc_info)
68
+
69
+ return log_msg
70
+
71
+
72
+ def create_logger(
73
+ name: str,
74
+ level: int = logging.INFO,
75
+ log_file: str | None = None,
76
+ log_dir: str | None = None,
77
+ use_color: bool = True,
78
+ include_context: bool = False,
79
+ max_bytes: int = 10 * 1024 * 1024, # 10MB
80
+ backup_count: int = 5,
81
+ ) -> logging.Logger:
82
+ """Create a configured logger instance.
83
+
84
+ Args:
85
+ name: Logger name (typically __name__)
86
+ level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
87
+ log_file: Optional log file path
88
+ log_dir: Optional log directory for file-based logging
89
+ use_color: Whether to use colored console output
90
+ include_context: Whether to include contextual information
91
+ max_bytes: Max size of log file before rotation
92
+ backup_count: Number of backup files to keep
93
+
94
+ Returns:
95
+ Configured logger instance
96
+
97
+ Example:
98
+ >>> logger = create_logger(__name__, level=logging.DEBUG)
99
+ >>> logger.info("Application started")
100
+ >>> logger.debug("Detailed debug information")
101
+
102
+ """
103
+ logger = logging.getLogger(name)
104
+
105
+ # Avoid adding handlers multiple times
106
+ if logger.handlers:
107
+ return logger
108
+
109
+ logger.setLevel(level)
110
+
111
+ # Console handler
112
+ console_handler = logging.StreamHandler(sys.stderr)
113
+ console_handler.setLevel(level)
114
+ console_formatter = StructuredFormatter(use_color=use_color, include_context=include_context)
115
+ console_handler.setFormatter(console_formatter)
116
+ logger.addHandler(console_handler)
117
+
118
+ # File handler with rotation (if log_file or log_dir specified)
119
+ if log_file or log_dir:
120
+ if log_dir:
121
+ log_dir_path = Path(log_dir)
122
+ log_dir_path.mkdir(parents=True, exist_ok=True)
123
+ log_file_path = log_dir_path / f"{name.replace('.', '_')}.log"
124
+ else:
125
+ log_file_path = Path(log_file) # type: ignore[arg-type]
126
+ log_file_path.parent.mkdir(parents=True, exist_ok=True)
127
+
128
+ file_handler = logging.handlers.RotatingFileHandler(
129
+ str(log_file_path),
130
+ maxBytes=max_bytes,
131
+ backupCount=backup_count,
132
+ )
133
+ file_handler.setLevel(level)
134
+ file_formatter = StructuredFormatter(use_color=False, include_context=include_context)
135
+ file_handler.setFormatter(file_formatter)
136
+ logger.addHandler(file_handler)
137
+
138
+ return logger
139
+
140
+
141
+ class LoggingConfig:
142
+ """Centralized logging configuration manager."""
143
+
144
+ _configured = False
145
+ _loggers: dict[str, logging.Logger] = {}
146
+ _level: int = logging.INFO
147
+ _log_dir: str | None = None
148
+ _use_color: bool = True
149
+ _include_context: bool = False
150
+
151
+ @classmethod
152
+ def configure(
153
+ cls,
154
+ level: int = logging.INFO,
155
+ log_dir: str | None = None,
156
+ use_color: bool = True,
157
+ include_context: bool = False,
158
+ ) -> None:
159
+ """Configure global logging settings.
160
+
161
+ Args:
162
+ level: Default logging level
163
+ log_dir: Directory for log files
164
+ use_color: Whether to use colored output
165
+ include_context: Whether to include contextual information
166
+
167
+ Example:
168
+ >>> LoggingConfig.configure(
169
+ ... level=logging.DEBUG,
170
+ ... log_dir="./logs",
171
+ ... use_color=True
172
+ ... )
173
+
174
+ """
175
+ cls._level = level
176
+ cls._log_dir = log_dir
177
+ cls._use_color = use_color
178
+ cls._include_context = include_context
179
+ cls._configured = True
180
+
181
+ @classmethod
182
+ def get_logger(
183
+ cls,
184
+ name: str,
185
+ level: int | None = None,
186
+ ) -> logging.Logger:
187
+ """Get or create a logger instance.
188
+
189
+ Args:
190
+ name: Logger name (typically __name__)
191
+ level: Optional override for logging level
192
+
193
+ Returns:
194
+ Configured logger instance
195
+
196
+ Example:
197
+ >>> logger = LoggingConfig.get_logger(__name__)
198
+ >>> logger.info("Processing data")
199
+
200
+ """
201
+ if name not in cls._loggers:
202
+ if not cls._configured:
203
+ cls.configure()
204
+
205
+ logger = create_logger(
206
+ name,
207
+ level=level or cls._level,
208
+ log_dir=cls._log_dir,
209
+ use_color=cls._use_color,
210
+ include_context=cls._include_context,
211
+ )
212
+ cls._loggers[name] = logger
213
+
214
+ return cls._loggers[name]
215
+
216
+ @classmethod
217
+ def set_level(cls, level: int) -> None:
218
+ """Set logging level for all loggers."""
219
+ for logger in cls._loggers.values():
220
+ logger.setLevel(level)
221
+ for handler in logger.handlers:
222
+ handler.setLevel(level)
223
+
224
+
225
+ def get_logger(name: str) -> logging.Logger:
226
+ """Convenience function to get a logger.
227
+
228
+ This is the primary function to use throughout the codebase.
229
+
230
+ Args:
231
+ name: Logger name (typically __name__)
232
+
233
+ Returns:
234
+ Configured logger instance
235
+
236
+ Example:
237
+ >>> from attune.logging_config import get_logger
238
+ >>> logger = get_logger(__name__)
239
+ >>> logger.info("Starting process")
240
+ >>> logger.debug("Processing item", extra={"context": {"item_id": 123}})
241
+
242
+ """
243
+ return LoggingConfig.get_logger(name)
244
+
245
+
246
+ # Initialize with environment variable support
247
+ def init_logging_from_env() -> None:
248
+ """Initialize logging configuration from environment variables."""
249
+ log_level_str = os.getenv("EMPATHY_LOG_LEVEL", "INFO").upper()
250
+ log_level = getattr(logging, log_level_str, logging.INFO)
251
+
252
+ log_dir = os.getenv("EMPATHY_LOG_DIR")
253
+ use_color = os.getenv("EMPATHY_LOG_COLOR", "true").lower() == "true"
254
+ include_context = os.getenv("EMPATHY_LOG_CONTEXT", "false").lower() == "true"
255
+
256
+ LoggingConfig.configure(
257
+ level=log_level,
258
+ log_dir=log_dir,
259
+ use_color=use_color,
260
+ include_context=include_context,
261
+ )