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/models/cli.py ADDED
@@ -0,0 +1,655 @@
1
+ """CLI for Multi-Model Configuration Inspection
2
+
3
+ Provides commands to:
4
+ - Print effective model registry
5
+ - Show task-to-tier mappings
6
+ - Validate configuration files
7
+ - Display cost estimates
8
+ - View telemetry and analytics
9
+
10
+ Usage:
11
+ python -m attune.models.cli registry
12
+ python -m attune.models.cli tasks
13
+ python -m attune.models.cli validate path/to/config.yaml
14
+ python -m attune.models.cli costs --input-tokens 10000 --output-tokens 2000
15
+ python -m attune.models.cli telemetry --summary
16
+ python -m attune.models.cli telemetry --costs
17
+ python -m attune.models.cli telemetry --providers
18
+
19
+ Copyright 2025 Smart-AI-Memory
20
+ Licensed under Fair Source License 0.9
21
+ """
22
+
23
+ import argparse
24
+ import json
25
+ import sys
26
+ from datetime import datetime, timedelta
27
+
28
+ from .provider_config import (
29
+ configure_provider_cli,
30
+ configure_provider_interactive,
31
+ get_provider_config,
32
+ )
33
+ from .registry import get_all_models
34
+ from .tasks import get_all_tasks, get_tier_for_task
35
+ from .telemetry import TelemetryAnalytics, TelemetryStore
36
+ from .validation import validate_yaml_file
37
+
38
+
39
+ def print_registry(provider: str | None = None, format: str = "table") -> None:
40
+ """Print the model registry.
41
+
42
+ Args:
43
+ provider: Optional provider to filter by
44
+ format: Output format ("table" or "json")
45
+
46
+ """
47
+ registry = get_all_models()
48
+
49
+ if provider:
50
+ if provider not in registry:
51
+ print(f"Error: Unknown provider '{provider}'")
52
+ print(f"Available providers: {', '.join(registry.keys())}")
53
+ sys.exit(1)
54
+ registry = {provider: registry[provider]}
55
+
56
+ if format == "json":
57
+ # Convert to JSON-serializable format
58
+ output: dict[str, dict[str, dict[str, object]]] = {}
59
+ for prov, tiers in registry.items():
60
+ output[prov] = {}
61
+ for tier, info in tiers.items():
62
+ output[prov][tier] = {
63
+ "id": info.id,
64
+ "input_cost_per_million": info.input_cost_per_million,
65
+ "output_cost_per_million": info.output_cost_per_million,
66
+ "max_tokens": info.max_tokens,
67
+ "supports_vision": info.supports_vision,
68
+ "supports_tools": info.supports_tools,
69
+ }
70
+ print(json.dumps(output, indent=2))
71
+ else:
72
+ # Table format
73
+ print("\n" + "=" * 80)
74
+ print("MODEL REGISTRY")
75
+ print("=" * 80)
76
+
77
+ for prov, tiers in sorted(registry.items()):
78
+ print(f"\n[{prov.upper()}]")
79
+ print("-" * 60)
80
+ print(f"{'Tier':<10} {'Model ID':<35} {'Input/M':<10} {'Output/M':<10}")
81
+ print("-" * 60)
82
+
83
+ for tier in ["cheap", "capable", "premium"]:
84
+ if tier in tiers:
85
+ info = tiers[tier]
86
+ print(
87
+ f"{tier:<10} {info.id:<35} "
88
+ f"${info.input_cost_per_million:<9.2f} "
89
+ f"${info.output_cost_per_million:<9.2f}",
90
+ )
91
+
92
+ print("\n" + "=" * 80)
93
+
94
+
95
+ def print_tasks(tier: str | None = None, format: str = "table") -> None:
96
+ """Print task-to-tier mappings.
97
+
98
+ Args:
99
+ tier: Optional tier to filter by
100
+ format: Output format ("table" or "json")
101
+
102
+ """
103
+ all_tasks = get_all_tasks()
104
+
105
+ if tier:
106
+ if tier not in all_tasks:
107
+ print(f"Error: Unknown tier '{tier}'")
108
+ print(f"Available tiers: {', '.join(all_tasks.keys())}")
109
+ sys.exit(1)
110
+ all_tasks = {tier: all_tasks[tier]}
111
+
112
+ if format == "json":
113
+ print(json.dumps(all_tasks, indent=2))
114
+ else:
115
+ print("\n" + "=" * 60)
116
+ print("TASK-TO-TIER MAPPINGS")
117
+ print("=" * 60)
118
+
119
+ for tier_name, tasks in sorted(all_tasks.items()):
120
+ print(f"\n[{tier_name.upper()}] - {len(tasks)} tasks")
121
+ print("-" * 40)
122
+ for task in sorted(tasks):
123
+ print(f" • {task}")
124
+
125
+ print("\n" + "=" * 60)
126
+
127
+
128
+ def print_costs(
129
+ input_tokens: int = 10000,
130
+ output_tokens: int = 2000,
131
+ provider: str | None = None,
132
+ format: str = "table",
133
+ ) -> None:
134
+ """Print cost estimates for all tiers.
135
+
136
+ Args:
137
+ input_tokens: Number of input tokens
138
+ output_tokens: Number of output tokens
139
+ provider: Optional provider to filter by
140
+ format: Output format
141
+
142
+ """
143
+ registry = get_all_models()
144
+
145
+ if provider:
146
+ if provider not in registry:
147
+ print(f"Error: Unknown provider '{provider}'")
148
+ sys.exit(1)
149
+ providers = [provider]
150
+ else:
151
+ providers = list(registry.keys())
152
+
153
+ if format == "json":
154
+ output: dict[str, dict[str, dict[str, object]]] = {}
155
+ for prov in providers:
156
+ output[prov] = {}
157
+ for tier in ["cheap", "capable", "premium"]:
158
+ if tier in registry[prov]:
159
+ info = registry[prov][tier]
160
+ input_cost = (input_tokens / 1_000_000) * info.input_cost_per_million
161
+ output_cost = (output_tokens / 1_000_000) * info.output_cost_per_million
162
+ output[prov][tier] = {
163
+ "input_cost": round(input_cost, 6),
164
+ "output_cost": round(output_cost, 6),
165
+ "total_cost": round(input_cost + output_cost, 6),
166
+ }
167
+ print(json.dumps(output, indent=2))
168
+ else:
169
+ print("\n" + "=" * 70)
170
+ print(f"COST ESTIMATES ({input_tokens:,} input / {output_tokens:,} output tokens)")
171
+ print("=" * 70)
172
+
173
+ for prov in sorted(providers):
174
+ print(f"\n[{prov.upper()}]")
175
+ print("-" * 50)
176
+ print(f"{'Tier':<10} {'Input':<12} {'Output':<12} {'Total':<12}")
177
+ print("-" * 50)
178
+
179
+ for tier in ["cheap", "capable", "premium"]:
180
+ if tier in registry[prov]:
181
+ info = registry[prov][tier]
182
+ input_cost = (input_tokens / 1_000_000) * info.input_cost_per_million
183
+ output_cost = (output_tokens / 1_000_000) * info.output_cost_per_million
184
+ total = input_cost + output_cost
185
+ print(f"{tier:<10} ${input_cost:<11.4f} ${output_cost:<11.4f} ${total:<11.4f}")
186
+
187
+ print("\n" + "=" * 70)
188
+
189
+
190
+ def validate_file(file_path: str, format: str = "table") -> int:
191
+ """Validate a configuration file.
192
+
193
+ Args:
194
+ file_path: Path to YAML config file
195
+ format: Output format
196
+
197
+ Returns:
198
+ Exit code (0 = valid, 1 = errors)
199
+
200
+ """
201
+ result = validate_yaml_file(file_path)
202
+
203
+ if format == "json":
204
+ output = {
205
+ "valid": result.valid,
206
+ "errors": [{"path": e.path, "message": e.message} for e in result.errors],
207
+ "warnings": [{"path": w.path, "message": w.message} for w in result.warnings],
208
+ }
209
+ print(json.dumps(output, indent=2))
210
+ else:
211
+ print(f"\nValidating: {file_path}")
212
+ print("-" * 60)
213
+ print(result)
214
+
215
+ return 0 if result.valid else 1
216
+
217
+
218
+ def print_effective_config(provider: str = "anthropic") -> None:
219
+ """Print the effective configuration for a provider.
220
+
221
+ Args:
222
+ provider: Provider to show config for
223
+
224
+ """
225
+ registry = get_all_models()
226
+
227
+ if provider not in registry:
228
+ print(f"Error: Unknown provider '{provider}'")
229
+ sys.exit(1)
230
+
231
+ print("\n" + "=" * 70)
232
+ print(f"EFFECTIVE CONFIGURATION: {provider.upper()}")
233
+ print("=" * 70)
234
+
235
+ # Provider models
236
+ print("\n[Models]")
237
+ for tier in ["cheap", "capable", "premium"]:
238
+ if tier in registry[provider]:
239
+ info = registry[provider][tier]
240
+ print(f" {tier}: {info.id}")
241
+
242
+ # Task routing
243
+ print("\n[Task Routing Examples]")
244
+ example_tasks = ["summarize", "fix_bug", "coordinate"]
245
+ for task in example_tasks:
246
+ task_tier = get_tier_for_task(task)
247
+ model = registry[provider].get(task_tier.value)
248
+ model_id = model.id if model else "N/A"
249
+ print(f" {task} → {task_tier.value} → {model_id}")
250
+
251
+ # Default timeouts
252
+ print("\n[Default Timeouts]")
253
+ print(" cheap: 30,000 ms")
254
+ print(" capable: 60,000 ms")
255
+ print(" premium: 120,000 ms")
256
+
257
+ print("\n" + "=" * 70)
258
+
259
+
260
+ def print_telemetry_summary(
261
+ days: int = 7,
262
+ format: str = "table",
263
+ storage_dir: str = ".empathy",
264
+ ) -> None:
265
+ """Print telemetry summary.
266
+
267
+ Args:
268
+ days: Number of days to look back
269
+ format: Output format
270
+ storage_dir: Directory containing telemetry files
271
+
272
+ """
273
+ store = TelemetryStore(storage_dir)
274
+ analytics = TelemetryAnalytics(store)
275
+
276
+ since = datetime.now() - timedelta(days=days)
277
+ calls = store.get_calls(since=since, limit=10000)
278
+ workflows = store.get_workflows(since=since, limit=1000)
279
+
280
+ if format == "json":
281
+ output = {
282
+ "period_days": days,
283
+ "total_calls": len(calls),
284
+ "total_workflows": len(workflows),
285
+ "total_cost": sum(c.estimated_cost for c in calls),
286
+ "success_rate": sum(1 for c in calls if c.success) / len(calls) * 100 if calls else 0,
287
+ }
288
+ print(json.dumps(output, indent=2))
289
+ else:
290
+ print("\n" + "=" * 60)
291
+ print(f"TELEMETRY SUMMARY (Last {days} days)")
292
+ print("=" * 60)
293
+
294
+ print("\n[Overview]")
295
+ print(f" Total LLM calls: {len(calls):,}")
296
+ print(f" Total workflows: {len(workflows):,}")
297
+ total_cost = sum(c.estimated_cost for c in calls)
298
+ print(f" Total cost: ${total_cost:.4f}")
299
+ if calls:
300
+ success_rate = sum(1 for c in calls if c.success) / len(calls) * 100
301
+ print(f" Success rate: {success_rate:.1f}%")
302
+
303
+ # Tier breakdown
304
+ tier_dist = analytics.tier_distribution(since)
305
+ print("\n[Tier Distribution]")
306
+ for tier, stats in tier_dist.items():
307
+ print(f" {tier}: {stats['count']} calls ({stats['percent']:.1f}%)")
308
+
309
+ print("\n" + "=" * 60)
310
+
311
+
312
+ def print_telemetry_costs(
313
+ days: int = 30,
314
+ format: str = "table",
315
+ storage_dir: str = ".empathy",
316
+ ) -> None:
317
+ """Print cost savings report.
318
+
319
+ Args:
320
+ days: Number of days to look back
321
+ format: Output format
322
+ storage_dir: Directory containing telemetry files
323
+
324
+ """
325
+ store = TelemetryStore(storage_dir)
326
+ analytics = TelemetryAnalytics(store)
327
+
328
+ since = datetime.now() - timedelta(days=days)
329
+ report = analytics.cost_savings_report(since)
330
+
331
+ if format == "json":
332
+ print(json.dumps(report, indent=2))
333
+ else:
334
+ print("\n" + "=" * 60)
335
+ print(f"COST SAVINGS REPORT (Last {days} days)")
336
+ print("=" * 60)
337
+
338
+ print("\n[Summary]")
339
+ print(f" Workflow runs: {report['workflow_count']}")
340
+ print(f" Actual cost: ${report['total_actual_cost']:.4f}")
341
+ print(f" Baseline cost (all premium): ${report['total_baseline_cost']:.4f}")
342
+ print(f" Total savings: ${report['total_savings']:.4f}")
343
+ print(f" Savings percent: {report['savings_percent']:.1f}%")
344
+ print(f" Avg cost per workflow: ${report['avg_cost_per_workflow']:.4f}")
345
+
346
+ # Top expensive workflows
347
+ top_wfs = analytics.top_expensive_workflows(n=5, since=since)
348
+ if top_wfs:
349
+ print("\n[Top Expensive Workflows]")
350
+ for wf in top_wfs:
351
+ print(f" {wf['workflow_name']}: ${wf['total_cost']:.4f} ({wf['run_count']} runs)")
352
+
353
+ print("\n" + "=" * 60)
354
+
355
+
356
+ def print_telemetry_providers(
357
+ days: int = 30,
358
+ format: str = "table",
359
+ storage_dir: str = ".empathy",
360
+ ) -> None:
361
+ """Print provider usage summary.
362
+
363
+ Args:
364
+ days: Number of days to look back
365
+ format: Output format
366
+ storage_dir: Directory containing telemetry files
367
+
368
+ """
369
+ store = TelemetryStore(storage_dir)
370
+ analytics = TelemetryAnalytics(store)
371
+
372
+ since = datetime.now() - timedelta(days=days)
373
+ summary = analytics.provider_usage_summary(since)
374
+
375
+ if format == "json":
376
+ print(json.dumps(summary, indent=2))
377
+ else:
378
+ print("\n" + "=" * 60)
379
+ print(f"PROVIDER USAGE (Last {days} days)")
380
+ print("=" * 60)
381
+
382
+ for provider, stats in sorted(summary.items()):
383
+ print(f"\n[{provider.upper()}]")
384
+ print(f" Calls: {stats['call_count']:,}")
385
+ print(f" Tokens: {stats['total_tokens']:,}")
386
+ print(f" Cost: ${stats['total_cost']:.4f}")
387
+ print(f" Errors: {stats['error_count']}")
388
+ print(f" By tier: {stats['by_tier']}")
389
+
390
+ print("\n" + "=" * 60)
391
+
392
+
393
+ def print_telemetry_fallbacks(
394
+ days: int = 30,
395
+ format: str = "table",
396
+ storage_dir: str = ".empathy",
397
+ ) -> None:
398
+ """Print fallback statistics.
399
+
400
+ Args:
401
+ days: Number of days to look back
402
+ format: Output format
403
+ storage_dir: Directory containing telemetry files
404
+
405
+ """
406
+ store = TelemetryStore(storage_dir)
407
+ analytics = TelemetryAnalytics(store)
408
+
409
+ since = datetime.now() - timedelta(days=days)
410
+ stats = analytics.fallback_stats(since)
411
+
412
+ if format == "json":
413
+ print(json.dumps(stats, indent=2))
414
+ else:
415
+ print("\n" + "=" * 60)
416
+ print(f"FALLBACK STATISTICS (Last {days} days)")
417
+ print("=" * 60)
418
+
419
+ print("\n[Summary]")
420
+ print(f" Total calls: {stats['total_calls']:,}")
421
+ print(f" Fallback count: {stats['fallback_count']:,}")
422
+ print(f" Fallback rate: {stats['fallback_percent']:.2f}%")
423
+ print(f" Error count: {stats['error_count']:,}")
424
+ print(f" Error rate: {stats['error_rate']:.2f}%")
425
+
426
+ if stats["by_original_provider"]:
427
+ print("\n[Fallbacks by Original Provider]")
428
+ for provider, count in stats["by_original_provider"].items():
429
+ print(f" {provider}: {count}")
430
+
431
+ print("\n" + "=" * 60)
432
+
433
+
434
+ def print_provider_config(format: str = "table") -> None:
435
+ """Print current provider configuration.
436
+
437
+ Args:
438
+ format: Output format ("table" or "json")
439
+
440
+ """
441
+ config = get_provider_config()
442
+
443
+ if format == "json":
444
+ output = config.to_dict()
445
+ output["available_providers"] = config.available_providers
446
+ output["effective_models"] = {
447
+ tier: {"id": model.id, "provider": model.provider}
448
+ for tier, model in config.get_effective_registry().items()
449
+ if model
450
+ }
451
+ print(json.dumps(output, indent=2))
452
+ else:
453
+ print("\n" + "=" * 60)
454
+ print("PROVIDER CONFIGURATION")
455
+ print("=" * 60)
456
+
457
+ print("\n[Current Settings]")
458
+ print(f" Mode: {config.mode.value}")
459
+ print(f" Primary provider: {config.primary_provider}")
460
+ print(f" Cost optimization: {config.cost_optimization}")
461
+ print(f" Prefer local (Ollama): {config.prefer_local}")
462
+
463
+ print("\n[Available Providers]")
464
+ if config.available_providers:
465
+ for provider in config.available_providers:
466
+ print(f" - {provider}")
467
+ else:
468
+ print(" (No API keys detected)")
469
+
470
+ print("\n[Effective Model Mapping]")
471
+ effective = config.get_effective_registry()
472
+ for tier in ["cheap", "capable", "premium"]:
473
+ model = effective.get(tier)
474
+ if model:
475
+ print(f" {tier:<10} -> {model.id} ({model.provider})")
476
+ else:
477
+ print(f" {tier:<10} -> (not configured)")
478
+
479
+ if config.tier_providers:
480
+ print("\n[Custom Tier Providers]")
481
+ for tier, provider in config.tier_providers.items():
482
+ print(f" {tier}: {provider}")
483
+
484
+ print("\n" + "=" * 60)
485
+
486
+
487
+ def configure_provider(
488
+ provider: str | None = None,
489
+ mode: str | None = None,
490
+ interactive: bool = False,
491
+ ) -> int:
492
+ """Configure provider settings.
493
+
494
+ Args:
495
+ provider: Provider to set (anthropic, openai, google, ollama, hybrid)
496
+ mode: Mode to set (single, hybrid)
497
+ interactive: Whether to run interactive configuration
498
+
499
+ Returns:
500
+ Exit code (0 for success)
501
+
502
+ """
503
+ if interactive:
504
+ configure_provider_interactive()
505
+ return 0
506
+
507
+ if provider or mode:
508
+ config = configure_provider_cli(provider=provider, mode=mode)
509
+ config.save()
510
+ print("Provider configuration updated:")
511
+ print(f" Mode: {config.mode.value}")
512
+ print(f" Provider: {config.primary_provider}")
513
+ print("\nEffective models:")
514
+ for tier, model in config.get_effective_registry().items():
515
+ if model:
516
+ print(f" {tier}: {model.id}")
517
+ return 0
518
+
519
+ # Show current config
520
+ print_provider_config()
521
+ return 0
522
+
523
+
524
+ def main() -> int:
525
+ """Main CLI entry point."""
526
+ parser = argparse.ArgumentParser(
527
+ description="Multi-Model Configuration CLI",
528
+ formatter_class=argparse.RawDescriptionHelpFormatter,
529
+ epilog="""
530
+ Examples:
531
+ %(prog)s registry Show all models
532
+ %(prog)s registry --provider anthropic Show Anthropic models only
533
+ %(prog)s tasks Show task-to-tier mappings
534
+ %(prog)s costs --input 50000 Estimate costs for 50k input tokens
535
+ %(prog)s validate config.yaml Validate a config file
536
+ %(prog)s effective --provider openai Show effective config for OpenAI
537
+ %(prog)s telemetry Show telemetry summary
538
+ %(prog)s telemetry --costs Show cost savings report
539
+ %(prog)s telemetry --providers Show provider usage
540
+ %(prog)s telemetry --fallbacks Show fallback stats
541
+ %(prog)s provider Show current provider config
542
+ %(prog)s provider --set anthropic Set Anthropic as primary
543
+ %(prog)s provider --set hybrid Enable hybrid mode
544
+ %(prog)s provider --interactive Interactive setup workflow
545
+ """,
546
+ )
547
+
548
+ subparsers = parser.add_subparsers(dest="command", help="Command to run")
549
+
550
+ # Registry command
551
+ reg_parser = subparsers.add_parser("registry", help="Show model registry")
552
+ reg_parser.add_argument("--provider", "-p", help="Filter by provider")
553
+ reg_parser.add_argument("--format", "-f", choices=["table", "json"], default="table")
554
+
555
+ # Tasks command
556
+ tasks_parser = subparsers.add_parser("tasks", help="Show task-to-tier mappings")
557
+ tasks_parser.add_argument("--tier", "-t", help="Filter by tier")
558
+ tasks_parser.add_argument("--format", "-f", choices=["table", "json"], default="table")
559
+
560
+ # Costs command
561
+ costs_parser = subparsers.add_parser("costs", help="Estimate costs")
562
+ costs_parser.add_argument("--input-tokens", "-i", type=int, default=10000, help="Input tokens")
563
+ costs_parser.add_argument("--output-tokens", "-o", type=int, default=2000, help="Output tokens")
564
+ costs_parser.add_argument("--provider", "-p", help="Filter by provider")
565
+ costs_parser.add_argument("--format", "-f", choices=["table", "json"], default="table")
566
+
567
+ # Validate command
568
+ val_parser = subparsers.add_parser("validate", help="Validate config file")
569
+ val_parser.add_argument("file", help="Path to YAML config file")
570
+ val_parser.add_argument("--format", "-f", choices=["table", "json"], default="table")
571
+
572
+ # Effective command
573
+ eff_parser = subparsers.add_parser("effective", help="Show effective config")
574
+ eff_parser.add_argument("--provider", "-p", default="anthropic", help="Provider to show")
575
+
576
+ # Telemetry command
577
+ tel_parser = subparsers.add_parser("telemetry", help="Show telemetry data")
578
+ tel_parser.add_argument("--days", "-d", type=int, default=7, help="Number of days to look back")
579
+ tel_parser.add_argument("--costs", action="store_true", help="Show cost savings report")
580
+ tel_parser.add_argument("--providers", action="store_true", help="Show provider usage")
581
+ tel_parser.add_argument("--fallbacks", action="store_true", help="Show fallback statistics")
582
+ tel_parser.add_argument("--storage-dir", default=".empathy", help="Telemetry storage directory")
583
+ tel_parser.add_argument("--format", "-f", choices=["table", "json"], default="table")
584
+
585
+ # Provider command
586
+ prov_parser = subparsers.add_parser("provider", help="Configure provider settings")
587
+ prov_parser.add_argument(
588
+ "--set",
589
+ "-s",
590
+ dest="provider_set",
591
+ help="Set provider (anthropic, openai, google, ollama, hybrid)",
592
+ )
593
+ prov_parser.add_argument(
594
+ "--mode",
595
+ "-m",
596
+ choices=["single", "hybrid"],
597
+ help="Set mode (single or hybrid)",
598
+ )
599
+ prov_parser.add_argument(
600
+ "--interactive",
601
+ "-i",
602
+ action="store_true",
603
+ help="Interactive configuration workflow",
604
+ )
605
+ prov_parser.add_argument("--format", "-f", choices=["table", "json"], default="table")
606
+
607
+ args = parser.parse_args()
608
+
609
+ if not args.command:
610
+ parser.print_help()
611
+ return 1
612
+
613
+ if args.command == "registry":
614
+ print_registry(args.provider, args.format)
615
+ return 0
616
+
617
+ if args.command == "tasks":
618
+ print_tasks(args.tier, args.format)
619
+ return 0
620
+
621
+ if args.command == "costs":
622
+ print_costs(args.input_tokens, args.output_tokens, args.provider, args.format)
623
+ return 0
624
+
625
+ if args.command == "validate":
626
+ return validate_file(args.file, args.format)
627
+
628
+ if args.command == "effective":
629
+ print_effective_config(args.provider)
630
+ return 0
631
+
632
+ if args.command == "telemetry":
633
+ if args.costs:
634
+ print_telemetry_costs(args.days, args.format, args.storage_dir)
635
+ elif args.providers:
636
+ print_telemetry_providers(args.days, args.format, args.storage_dir)
637
+ elif args.fallbacks:
638
+ print_telemetry_fallbacks(args.days, args.format, args.storage_dir)
639
+ else:
640
+ print_telemetry_summary(args.days, args.format, args.storage_dir)
641
+ return 0
642
+
643
+ if args.command == "provider":
644
+ if args.interactive:
645
+ return configure_provider(interactive=True)
646
+ if args.provider_set or args.mode:
647
+ return configure_provider(provider=args.provider_set, mode=args.mode)
648
+ print_provider_config(args.format)
649
+ return 0
650
+
651
+ return 1
652
+
653
+
654
+ if __name__ == "__main__":
655
+ sys.exit(main())