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,645 @@
1
+ """Workflow Configuration
2
+
3
+ Provides flexible configuration for workflow model selection:
4
+ - YAML/JSON config file support
5
+ - Environment variable overrides
6
+ - Per-workflow provider and model customization
7
+ - Easy extension for new models/providers
8
+
9
+ Configuration priority (highest to lowest):
10
+ 1. Constructor arguments
11
+ 2. Environment variables (EMPATHY_WORKFLOW_PROVIDER, etc.)
12
+ 3. Config file (.attune/workflows.yaml)
13
+ 4. Built-in defaults
14
+
15
+ Model configurations are sourced from the unified registry at
16
+ attune.models.MODEL_REGISTRY.
17
+
18
+ Copyright 2025 Smart-AI-Memory
19
+ Licensed under Fair Source License 0.9
20
+ """
21
+
22
+ import json
23
+ import os
24
+ from dataclasses import dataclass, field
25
+ from pathlib import Path
26
+ from typing import Any
27
+
28
+ # Import from unified registry
29
+ from attune.models import MODEL_REGISTRY, ModelInfo
30
+ from attune.models.registry import ModelProvider, ModelTier
31
+
32
+ # Try to import yaml, fall back gracefully
33
+ try:
34
+ import yaml
35
+
36
+ YAML_AVAILABLE = True
37
+ except ImportError:
38
+ YAML_AVAILABLE = False
39
+
40
+
41
+ def _validate_file_path(path: str, allowed_dir: str | None = None) -> Path:
42
+ """Validate file path to prevent path traversal and arbitrary writes.
43
+
44
+ Args:
45
+ path: File path to validate
46
+ allowed_dir: Optional directory to restrict writes to
47
+
48
+ Returns:
49
+ Validated Path object
50
+
51
+ Raises:
52
+ ValueError: If path is invalid or unsafe
53
+ """
54
+ if not path or not isinstance(path, str):
55
+ raise ValueError("path must be a non-empty string")
56
+
57
+ # Check for null bytes
58
+ if "\x00" in path:
59
+ raise ValueError("path contains null bytes")
60
+
61
+ try:
62
+ resolved = Path(path).resolve()
63
+ except (OSError, RuntimeError) as e:
64
+ raise ValueError(f"Invalid path: {e}")
65
+
66
+ # Check if within allowed directory
67
+ if allowed_dir:
68
+ try:
69
+ allowed = Path(allowed_dir).resolve()
70
+ resolved.relative_to(allowed)
71
+ except ValueError:
72
+ raise ValueError(f"path must be within {allowed_dir}")
73
+
74
+ # Check for dangerous system paths
75
+ dangerous_paths = ["/etc", "/sys", "/proc", "/dev"]
76
+ for dangerous in dangerous_paths:
77
+ if str(resolved).startswith(dangerous):
78
+ raise ValueError(f"Cannot write to system directory: {dangerous}")
79
+
80
+ return resolved
81
+
82
+
83
+ # Re-export for backward compatibility
84
+ __all__ = [
85
+ "DEFAULT_MODELS",
86
+ "ModelConfig",
87
+ "ModelProvider",
88
+ "ModelTier",
89
+ "WorkflowConfig",
90
+ "get_model",
91
+ ]
92
+
93
+
94
+ @dataclass
95
+ class ModelConfig:
96
+ """Configuration for a specific model.
97
+
98
+ Note: This class is kept for backward compatibility. New code should
99
+ use attune.models.ModelInfo from the unified registry.
100
+ """
101
+
102
+ name: str
103
+ provider: str
104
+ tier: str
105
+ input_cost_per_million: float = 0.0
106
+ output_cost_per_million: float = 0.0
107
+ max_tokens: int = 4096
108
+ supports_vision: bool = False
109
+ supports_tools: bool = True
110
+
111
+ @classmethod
112
+ def from_model_info(cls, info: ModelInfo) -> "ModelConfig":
113
+ """Create ModelConfig from unified ModelInfo."""
114
+ return cls(
115
+ name=info.id,
116
+ provider=info.provider,
117
+ tier=info.tier,
118
+ input_cost_per_million=info.input_cost_per_million,
119
+ output_cost_per_million=info.output_cost_per_million,
120
+ max_tokens=info.max_tokens,
121
+ supports_vision=info.supports_vision,
122
+ supports_tools=info.supports_tools,
123
+ )
124
+
125
+
126
+ @dataclass
127
+ class WorkflowConfig:
128
+ """Configuration for workflow model selection and XML prompts."""
129
+
130
+ # Default provider for all workflows
131
+ default_provider: str = "anthropic"
132
+
133
+ # Per-workflow provider overrides
134
+ workflow_providers: dict[str, str] = field(default_factory=dict)
135
+
136
+ # Custom model mappings (provider -> tier -> model)
137
+ custom_models: dict[str, dict[str, str]] = field(default_factory=dict)
138
+
139
+ # Model pricing overrides
140
+ pricing_overrides: dict[str, dict[str, float]] = field(default_factory=dict)
141
+
142
+ # XML prompt configuration - global defaults
143
+ xml_prompt_defaults: dict[str, Any] = field(default_factory=dict)
144
+
145
+ # Per-workflow XML prompt configuration overrides
146
+ workflow_xml_configs: dict[str, dict[str, Any]] = field(default_factory=dict)
147
+
148
+ # ==========================================================================
149
+ # Compliance and Feature Flags
150
+ # ==========================================================================
151
+
152
+ # Compliance mode: "standard" (default) or "hipaa" (healthcare)
153
+ # - standard: PII scrubbing disabled, test-gen disabled
154
+ # - hipaa: PII scrubbing enabled, test-gen enabled, stricter auditing
155
+ compliance_mode: str = "standard"
156
+
157
+ # Explicitly enabled workflows (added to defaults)
158
+ # Use this to opt-in to workflows like "test-gen"
159
+ enabled_workflows: list[str] = field(default_factory=list)
160
+
161
+ # Explicitly disabled workflows (removed from defaults)
162
+ disabled_workflows: list[str] = field(default_factory=list)
163
+
164
+ # PII scrubbing - auto-enabled in hipaa mode, opt-in otherwise
165
+ pii_scrubbing_enabled: bool | None = None # None = use compliance_mode default
166
+
167
+ # Audit logging level - "standard", "enhanced", or "hipaa"
168
+ audit_level: str = "standard"
169
+
170
+ @classmethod
171
+ def load(cls, config_path: str | Path | None = None) -> "WorkflowConfig":
172
+ """Load workflow configuration from file and environment.
173
+
174
+ Args:
175
+ config_path: Optional path to config file. If None, searches:
176
+ 1. .attune/workflows.yaml
177
+ 2. .attune/workflows.json
178
+ 3. attune.config.yaml (workflows section)
179
+
180
+ Returns:
181
+ WorkflowConfig instance
182
+
183
+ """
184
+ config_data: dict[str, Any] = {}
185
+
186
+ # Search for config file
187
+ if config_path is None:
188
+ search_paths = [
189
+ Path(".attune/workflows.yaml"),
190
+ Path(".attune/workflows.yml"),
191
+ Path(".attune/workflows.json"),
192
+ Path("attune.config.yml"), # Main config file
193
+ Path("attune.config.yaml"),
194
+ ]
195
+ for path in search_paths:
196
+ if path.exists():
197
+ config_path = path
198
+ break
199
+
200
+ # Load from file if found
201
+ if config_path is not None:
202
+ config_path = Path(config_path)
203
+ if config_path.exists():
204
+ config_data = cls._load_file(config_path)
205
+
206
+ # Apply environment variable overrides
207
+ config_data = cls._apply_env_overrides(config_data)
208
+
209
+ return cls(
210
+ default_provider=config_data.get("default_provider", "anthropic"),
211
+ workflow_providers=config_data.get("workflow_providers", {}),
212
+ custom_models=config_data.get("custom_models", {}),
213
+ pricing_overrides=config_data.get("pricing_overrides", {}),
214
+ xml_prompt_defaults=config_data.get("xml_prompt_defaults", {}),
215
+ workflow_xml_configs=config_data.get("workflow_xml_configs", {}),
216
+ # Compliance and feature flags
217
+ compliance_mode=config_data.get("compliance_mode", "standard"),
218
+ enabled_workflows=config_data.get("enabled_workflows", []),
219
+ disabled_workflows=config_data.get("disabled_workflows", []),
220
+ pii_scrubbing_enabled=config_data.get("pii_scrubbing_enabled"),
221
+ audit_level=config_data.get("audit_level", "standard"),
222
+ )
223
+
224
+ @staticmethod
225
+ def _load_file(path: Path) -> dict[str, Any]:
226
+ """Load config from YAML or JSON file."""
227
+ content = path.read_text()
228
+
229
+ if path.suffix in (".yaml", ".yml"):
230
+ if not YAML_AVAILABLE:
231
+ raise ImportError("PyYAML required for YAML config. Install: pip install pyyaml")
232
+ data = yaml.safe_load(content)
233
+ else:
234
+ data = json.loads(content)
235
+
236
+ result: dict[str, Any] = {}
237
+
238
+ # Handle root-level provider from attune.config.yml
239
+ if "provider" in data:
240
+ result["default_provider"] = data["provider"]
241
+
242
+ # Handle model_preferences as custom_models
243
+ if "model_preferences" in data:
244
+ provider = data.get("provider", "anthropic")
245
+ result["custom_models"] = {provider: data["model_preferences"]}
246
+
247
+ # Handle nested 'workflows' key from attune.config.yaml
248
+ if "workflows" in data and isinstance(data["workflows"], dict):
249
+ result.update(data["workflows"])
250
+ elif "workflows" not in data and "provider" not in data:
251
+ # Legacy format: entire file is workflow config
252
+ result = dict(data)
253
+
254
+ return result
255
+
256
+ @staticmethod
257
+ def _apply_env_overrides(config: dict[str, Any]) -> dict[str, Any]:
258
+ """Apply environment variable overrides."""
259
+ # Ensure nested dicts exist (YAML may load them as None)
260
+ if config.get("workflow_providers") is None:
261
+ config["workflow_providers"] = {}
262
+ if config.get("custom_models") is None:
263
+ config["custom_models"] = {}
264
+ if config.get("pricing_overrides") is None:
265
+ config["pricing_overrides"] = {}
266
+
267
+ # EMPATHY_WORKFLOW_PROVIDER - default provider
268
+ env_provider = os.environ.get("EMPATHY_WORKFLOW_PROVIDER")
269
+ if env_provider:
270
+ config["default_provider"] = env_provider.lower()
271
+
272
+ # EMPATHY_WORKFLOW_<NAME>_PROVIDER - per-workflow provider
273
+ for key, value in os.environ.items():
274
+ if key.startswith("EMPATHY_WORKFLOW_") and key.endswith("_PROVIDER"):
275
+ workflow_name = key[17:-9].lower().replace("_", "-")
276
+ config["workflow_providers"][workflow_name] = value.lower()
277
+
278
+ # EMPATHY_MODEL_<TIER> - tier model overrides
279
+ for tier in ["CHEAP", "CAPABLE", "PREMIUM"]:
280
+ env_model = os.environ.get(f"EMPATHY_MODEL_{tier}")
281
+ if env_model:
282
+ if "env" not in config["custom_models"]:
283
+ config["custom_models"]["env"] = {}
284
+ config["custom_models"]["env"][tier.lower()] = env_model
285
+
286
+ return config
287
+
288
+ def get_provider_for_workflow(self, workflow_name: str) -> str:
289
+ """Get the provider for a specific workflow."""
290
+ return self.workflow_providers.get(workflow_name, self.default_provider)
291
+
292
+ def get_model_for_tier(self, provider: str, tier: str) -> str | None:
293
+ """Get custom model for a provider/tier, or None for default."""
294
+ # Check for env overrides first
295
+ if "env" in self.custom_models:
296
+ if tier in self.custom_models["env"]:
297
+ return self.custom_models["env"][tier]
298
+
299
+ # Check provider-specific overrides
300
+ if provider in self.custom_models:
301
+ if tier in self.custom_models[provider]:
302
+ return self.custom_models[provider][tier]
303
+
304
+ return None
305
+
306
+ def get_pricing(self, model: str) -> dict[str, float] | None:
307
+ """Get custom pricing for a model, or None for default."""
308
+ return self.pricing_overrides.get(model)
309
+
310
+ def get_xml_config_for_workflow(self, workflow_name: str) -> dict[str, Any]:
311
+ """Get XML prompt configuration for a specific workflow.
312
+
313
+ Merges global defaults with workflow-specific overrides.
314
+
315
+ Args:
316
+ workflow_name: The workflow name (e.g., "security-audit").
317
+
318
+ Returns:
319
+ Dictionary with XML prompt configuration.
320
+
321
+ """
322
+ # Start with defaults
323
+ config = dict(self.xml_prompt_defaults)
324
+
325
+ # Apply workflow-specific overrides
326
+ if workflow_name in self.workflow_xml_configs:
327
+ config.update(self.workflow_xml_configs[workflow_name])
328
+
329
+ return config
330
+
331
+ def is_xml_enabled_for_workflow(self, workflow_name: str) -> bool:
332
+ """Check if XML prompts are enabled for a workflow.
333
+
334
+ Args:
335
+ workflow_name: The workflow name.
336
+
337
+ Returns:
338
+ True if XML prompts are enabled.
339
+
340
+ """
341
+ config = self.get_xml_config_for_workflow(workflow_name)
342
+ return bool(config.get("enabled", False))
343
+
344
+ # ==========================================================================
345
+ # Compliance and Feature Flag Methods
346
+ # ==========================================================================
347
+
348
+ def is_hipaa_mode(self) -> bool:
349
+ """Check if HIPAA compliance mode is enabled."""
350
+ return self.compliance_mode.lower() == "hipaa"
351
+
352
+ def is_pii_scrubbing_enabled(self) -> bool:
353
+ """Check if PII scrubbing is enabled.
354
+
355
+ Returns True if:
356
+ - Explicitly enabled via pii_scrubbing_enabled=True
357
+ - OR compliance_mode is "hipaa" (and not explicitly disabled)
358
+
359
+ Returns:
360
+ True if PII scrubbing should be active
361
+
362
+ """
363
+ # Explicit setting takes precedence
364
+ if self.pii_scrubbing_enabled is not None:
365
+ return self.pii_scrubbing_enabled
366
+
367
+ # Default based on compliance mode
368
+ return self.is_hipaa_mode()
369
+
370
+ def is_workflow_enabled(self, workflow_name: str) -> bool | None:
371
+ """Check if a specific workflow is enabled.
372
+
373
+ Args:
374
+ workflow_name: Name of the workflow (e.g., "test-gen")
375
+
376
+ Returns:
377
+ True if workflow is enabled, False if disabled, None for default behavior
378
+
379
+ """
380
+ # Explicitly disabled takes precedence
381
+ if workflow_name in self.disabled_workflows:
382
+ return False
383
+
384
+ # Explicitly enabled
385
+ if workflow_name in self.enabled_workflows:
386
+ return True
387
+
388
+ # HIPAA mode enables healthcare-specific workflows
389
+ if self.is_hipaa_mode():
390
+ hipaa_workflows = {"test-gen"} # Workflows auto-enabled in HIPAA mode
391
+ if workflow_name in hipaa_workflows:
392
+ return True
393
+
394
+ # Default: workflow must be in standard registry (handled by __init__.py)
395
+ return None # None means "use default registry behavior"
396
+
397
+ def get_effective_audit_level(self) -> str:
398
+ """Get the effective audit level based on compliance mode.
399
+
400
+ Returns:
401
+ Audit level string: "standard", "enhanced", or "hipaa"
402
+
403
+ """
404
+ # Explicit setting takes precedence
405
+ if self.audit_level != "standard":
406
+ return self.audit_level
407
+
408
+ # HIPAA mode defaults to hipaa audit level
409
+ if self.is_hipaa_mode():
410
+ return "hipaa"
411
+
412
+ return "standard"
413
+
414
+ def save(self, path: str | Path) -> None:
415
+ """Save configuration to file."""
416
+ # Validate path first (convert Path to string for validation)
417
+ path_str = str(path)
418
+ validated_path = _validate_file_path(path_str)
419
+
420
+ data = {
421
+ "default_provider": self.default_provider,
422
+ "workflow_providers": self.workflow_providers,
423
+ "custom_models": self.custom_models,
424
+ "pricing_overrides": self.pricing_overrides,
425
+ "xml_prompt_defaults": self.xml_prompt_defaults,
426
+ "workflow_xml_configs": self.workflow_xml_configs,
427
+ # Compliance and feature flags
428
+ "compliance_mode": self.compliance_mode,
429
+ "enabled_workflows": self.enabled_workflows,
430
+ "disabled_workflows": self.disabled_workflows,
431
+ "pii_scrubbing_enabled": self.pii_scrubbing_enabled,
432
+ "audit_level": self.audit_level,
433
+ }
434
+
435
+ validated_path.parent.mkdir(parents=True, exist_ok=True)
436
+
437
+ if validated_path.suffix in (".yaml", ".yml"):
438
+ if not YAML_AVAILABLE:
439
+ raise ImportError("PyYAML required for YAML config")
440
+ with open(validated_path, "w") as f:
441
+ yaml.dump(data, f, default_flow_style=False)
442
+ else:
443
+ with open(validated_path, "w") as f:
444
+ json.dump(data, f, indent=2)
445
+
446
+
447
+ # =============================================================================
448
+ # DEFAULT_MODELS - Built from unified registry
449
+ # =============================================================================
450
+ # This is now populated from attune.models.MODEL_REGISTRY for consistency
451
+ # across the framework.
452
+
453
+
454
+ def _build_default_models() -> dict[str, dict[str, ModelConfig]]:
455
+ """Build DEFAULT_MODELS from the unified registry."""
456
+ result: dict[str, dict[str, ModelConfig]] = {}
457
+ for provider, tiers in MODEL_REGISTRY.items():
458
+ result[provider] = {}
459
+ for tier, info in tiers.items():
460
+ result[provider][tier] = ModelConfig.from_model_info(info)
461
+ return result
462
+
463
+
464
+ # Lazy initialization - built on first access
465
+ _default_models_cache: dict[str, dict[str, ModelConfig]] | None = None
466
+
467
+
468
+ def _get_default_models() -> dict[str, dict[str, ModelConfig]]:
469
+ """Get DEFAULT_MODELS, building from registry if needed."""
470
+ global _default_models_cache
471
+ if _default_models_cache is None:
472
+ _default_models_cache = _build_default_models()
473
+ return _default_models_cache
474
+
475
+
476
+ # For backward compatibility, DEFAULT_MODELS is now a property-like access
477
+ # Users should access via get_default_models() or directly use MODEL_REGISTRY
478
+ DEFAULT_MODELS: dict[str, dict[str, ModelConfig]] = {} # Populated below
479
+
480
+
481
+ def _ensure_default_models() -> None:
482
+ """Ensure DEFAULT_MODELS is populated."""
483
+ global DEFAULT_MODELS
484
+ if not DEFAULT_MODELS:
485
+ DEFAULT_MODELS.update(_get_default_models())
486
+
487
+
488
+ def get_model(provider: str, tier: str, config: WorkflowConfig | None = None) -> str:
489
+ """Get the model name for a provider/tier combination.
490
+
491
+ Args:
492
+ provider: Model provider (anthropic, openai, ollama, hybrid)
493
+ tier: Model tier (cheap, capable, premium)
494
+ config: Optional WorkflowConfig for custom overrides
495
+
496
+ Returns:
497
+ Model name string
498
+
499
+ """
500
+ # Ensure DEFAULT_MODELS is populated from registry
501
+ _ensure_default_models()
502
+
503
+ # Check config overrides first
504
+ if config:
505
+ custom = config.get_model_for_tier(provider, tier)
506
+ if custom:
507
+ return custom
508
+
509
+ # Fall back to defaults
510
+ if provider in DEFAULT_MODELS and tier in DEFAULT_MODELS[provider]:
511
+ return DEFAULT_MODELS[provider][tier].name
512
+
513
+ # Ultimate fallback
514
+ return DEFAULT_MODELS["anthropic"]["capable"].name
515
+
516
+
517
+ def create_example_config() -> str:
518
+ """Generate an example configuration file content."""
519
+ return """# Empathy Framework - Workflow Configuration
520
+ # Place this file at: .attune/workflows.yaml
521
+
522
+ # =============================================================================
523
+ # PROVIDER SELECTION
524
+ # =============================================================================
525
+ # Choose from: anthropic, openai, ollama, hybrid
526
+ #
527
+ # - anthropic: All Claude models (Haiku → Sonnet → Opus 4.5)
528
+ # - openai: All OpenAI models (GPT-4o-mini → GPT-4o → GPT-5.2)
529
+ # - ollama: Local models (llama3.2:3b → llama3.1:8b → llama3.1:70b)
530
+ # - hybrid: Mix of best models from different providers:
531
+ # cheap: gpt-4o-mini (cheapest)
532
+ # capable: claude-sonnet-4 (best reasoning)
533
+ # premium: claude-opus-4.5 (best overall)
534
+
535
+ default_provider: anthropic
536
+
537
+ # =============================================================================
538
+ # PER-WORKFLOW PROVIDER OVERRIDES
539
+ # =============================================================================
540
+ # Use different providers for specific workflows
541
+ workflow_providers:
542
+ # research: hybrid # Use hybrid for research
543
+ # code-review: anthropic
544
+ # doc-gen: openai
545
+
546
+ # =============================================================================
547
+ # CUSTOM MODEL MAPPINGS
548
+ # =============================================================================
549
+ # Override default models for specific provider/tier combinations
550
+ custom_models:
551
+ anthropic:
552
+ cheap: claude-3-5-haiku-20241022
553
+ capable: claude-sonnet-4-20250514
554
+ premium: claude-opus-4-5-20251101
555
+ openai:
556
+ cheap: gpt-4o-mini
557
+ capable: gpt-4o
558
+ premium: gpt-5.2
559
+ ollama:
560
+ cheap: llama3.2:3b
561
+ capable: llama3.1:8b
562
+ premium: llama3.1:70b
563
+ # Create your own hybrid mix:
564
+ hybrid:
565
+ cheap: gpt-4o-mini # OpenAI - cheapest per token
566
+ capable: claude-sonnet-4-20250514 # Anthropic - best code/reasoning
567
+ premium: claude-opus-4-5-20251101 # Anthropic - best overall
568
+
569
+ # =============================================================================
570
+ # CUSTOM PRICING (per million tokens)
571
+ # =============================================================================
572
+ # Add pricing for models not in the default list
573
+ pricing_overrides:
574
+ mixtral:latest:
575
+ input: 0.0
576
+ output: 0.0
577
+ my-custom-model:
578
+ input: 1.00
579
+ output: 5.00
580
+
581
+ # =============================================================================
582
+ # XML PROMPT CONFIGURATION
583
+ # =============================================================================
584
+ # Enable structured XML prompts for consistent LLM interactions.
585
+ # XML prompts improve parsing reliability for dashboards and automation.
586
+
587
+ # Global defaults for all workflows
588
+ xml_prompt_defaults:
589
+ enabled: false # Set to true to enable XML prompts globally
590
+ schema_version: "1.0" # XML schema version
591
+ enforce_response_xml: false # Require XML in responses
592
+ fallback_on_parse_error: true # Fall back to raw text if XML fails
593
+
594
+ # Per-workflow XML configuration (overrides defaults)
595
+ workflow_xml_configs:
596
+ security-audit:
597
+ enabled: true
598
+ enforce_response_xml: true
599
+ template_name: "security-audit"
600
+ code-review:
601
+ enabled: true
602
+ enforce_response_xml: true
603
+ template_name: "code-review"
604
+ research:
605
+ enabled: true
606
+ enforce_response_xml: false # More flexible for research
607
+ template_name: "research"
608
+ bug-predict:
609
+ enabled: true
610
+ enforce_response_xml: true
611
+ template_name: "bug-analysis"
612
+ perf-audit:
613
+ enabled: true
614
+ enforce_response_xml: true
615
+ template_name: "perf-audit"
616
+ test-gen:
617
+ enabled: true
618
+ enforce_response_xml: true
619
+ template_name: "test-gen"
620
+ doc-gen:
621
+ enabled: true
622
+ enforce_response_xml: true
623
+ template_name: "doc-gen"
624
+ release-prep:
625
+ enabled: true
626
+ enforce_response_xml: true
627
+ template_name: "release-prep"
628
+ dependency-check:
629
+ enabled: true
630
+ enforce_response_xml: true
631
+ template_name: "dependency-check"
632
+ refactor-plan:
633
+ enabled: true
634
+ enforce_response_xml: true
635
+ template_name: "refactor-plan"
636
+
637
+ # =============================================================================
638
+ # ENVIRONMENT VARIABLE OVERRIDES
639
+ # =============================================================================
640
+ # EMPATHY_WORKFLOW_PROVIDER=hybrid # Default provider
641
+ # EMPATHY_WORKFLOW_RESEARCH_PROVIDER=anthropic # Per-workflow
642
+ # EMPATHY_MODEL_CHEAP=gpt-4o-mini # Tier model override
643
+ # EMPATHY_MODEL_CAPABLE=claude-sonnet-4-20250514
644
+ # EMPATHY_MODEL_PREMIUM=claude-opus-4-5-20251101
645
+ """