aria-code 0.167.0__py3-none-win_amd64.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 (601) hide show
  1. aria_bots/__init__.py +7 -0
  2. aria_bots/channels/README.md +97 -0
  3. aria_bots/channels/__init__.py +1 -0
  4. aria_bots/channels/approvals.py +263 -0
  5. aria_bots/channels/conversation.py +280 -0
  6. aria_bots/channels/digest.py +186 -0
  7. aria_bots/channels/registry.py +95 -0
  8. aria_bots/engine.py +144 -0
  9. aria_bots/feishu_bot.py +1385 -0
  10. aria_bots/logistics/__init__.py +1 -0
  11. aria_bots/logistics/carriers.py +253 -0
  12. aria_bots/logistics/freight.py +154 -0
  13. aria_bots/logistics/inventory.py +242 -0
  14. aria_bots/logistics/mcp_server.py +156 -0
  15. aria_bots/logistics/records.py +119 -0
  16. aria_bots/logistics/tenancy.py +152 -0
  17. aria_bots/market.py +91 -0
  18. aria_bots/relay_client.py +326 -0
  19. aria_bots/telegram_bot.py +299 -0
  20. aria_code/__init__.py +111 -0
  21. aria_code/_bin/aria-native.exe +0 -0
  22. aria_code/_bin/aria-runtime.exe +0 -0
  23. aria_code/_version.py +20 -0
  24. aria_code/account_memory.py +296 -0
  25. aria_code/account_session.py +166 -0
  26. aria_code/adk_apps/__init__.py +1 -0
  27. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  28. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  29. aria_code/adk_apps/aria_research/__init__.py +1 -0
  30. aria_code/adk_apps/aria_research/agent.py +31 -0
  31. aria_code/agents/__init__.py +49 -0
  32. aria_code/agents/base.py +329 -0
  33. aria_code/agents/code_review.py +200 -0
  34. aria_code/agents/deep/__init__.py +37 -0
  35. aria_code/agents/deep/calibration_loop.py +144 -0
  36. aria_code/agents/deep/critic.py +125 -0
  37. aria_code/agents/deep/deepen.py +193 -0
  38. aria_code/agents/deep/models.py +149 -0
  39. aria_code/agents/deep/pipeline.py +164 -0
  40. aria_code/agents/deep/quant_fusion.py +192 -0
  41. aria_code/agents/deep/themes.py +95 -0
  42. aria_code/agents/deep/tiers.py +106 -0
  43. aria_code/agents/engineering/__init__.py +14 -0
  44. aria_code/agents/engineering/coder.py +222 -0
  45. aria_code/agents/engineering/tester.py +145 -0
  46. aria_code/agents/financial/__init__.py +21 -0
  47. aria_code/agents/financial/cashflow_burn.py +119 -0
  48. aria_code/agents/financial/catalyst.py +279 -0
  49. aria_code/agents/financial/corporate_finance.py +175 -0
  50. aria_code/agents/financial/debate.py +224 -0
  51. aria_code/agents/financial/earnings.py +315 -0
  52. aria_code/agents/financial/fundamental.py +178 -0
  53. aria_code/agents/financial/macro.py +111 -0
  54. aria_code/agents/financial/news.py +231 -0
  55. aria_code/agents/financial/risk.py +146 -0
  56. aria_code/agents/financial/sector.py +279 -0
  57. aria_code/agents/financial/strategist.py +121 -0
  58. aria_code/agents/financial/stripe_revenue.py +127 -0
  59. aria_code/agents/financial/synthesis.py +274 -0
  60. aria_code/agents/financial/technical.py +277 -0
  61. aria_code/agents/orchestrator.py +44 -0
  62. aria_code/agents/portfolio_agent.py +392 -0
  63. aria_code/agents/realty/__init__.py +62 -0
  64. aria_code/agents/realty/asset_diagnosis.py +152 -0
  65. aria_code/agents/realty/business_match.py +166 -0
  66. aria_code/agents/realty/cashflow_verify.py +209 -0
  67. aria_code/agents/realty/contract_rules.py +210 -0
  68. aria_code/agents/realty/energy_anomaly.py +189 -0
  69. aria_code/agents/realty/exit_settlement.py +208 -0
  70. aria_code/agents/realty/fulfillment_risk.py +206 -0
  71. aria_code/agents/realty/ops_optimize.py +160 -0
  72. aria_code/agents/realty/revenue_share.py +215 -0
  73. aria_code/agents/registry.py +168 -0
  74. aria_code/agents/signal_scheme.py +111 -0
  75. aria_code/agents/sports/__init__.py +0 -0
  76. aria_code/agents/sports/football_agent.py +197 -0
  77. aria_code/agents/supervisor.py +113 -0
  78. aria_code/agents/team.py +500 -0
  79. aria_code/agents/video/__init__.py +16 -0
  80. aria_code/agents/video/director.py +376 -0
  81. aria_code/agents/video/qa_agent.py +143 -0
  82. aria_code/agents/video/script_doctor.py +112 -0
  83. aria_code/agents/warehouse/__init__.py +25 -0
  84. aria_code/agents/warehouse/contracts.py +26 -0
  85. aria_code/agents/warehouse/dashboard.py +155 -0
  86. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  87. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  88. aria_code/agents/warehouse/inventory_health.py +24 -0
  89. aria_code/agents/warehouse/local_run.py +71 -0
  90. aria_code/agents/warehouse/local_snapshot.py +132 -0
  91. aria_code/agents/warehouse/logistics_cost.py +84 -0
  92. aria_code/agents/warehouse/logistics_sync.py +21 -0
  93. aria_code/agents/warehouse/navigation.schema.json +110 -0
  94. aria_code/agents/warehouse/task_agent.py +172 -0
  95. aria_code/agents/warehouse/workflow.py +31 -0
  96. aria_code/aliyun_data_client.py +4 -0
  97. aria_code/apps/README.md +12 -0
  98. aria_code/apps/__init__.py +2 -0
  99. aria_code/apps/channels/README.md +9 -0
  100. aria_code/apps/channels/__init__.py +17 -0
  101. aria_code/apps/channels/approvals.py +11 -0
  102. aria_code/apps/channels/conversation.py +11 -0
  103. aria_code/apps/channels/digest.py +11 -0
  104. aria_code/apps/channels/intake.py +92 -0
  105. aria_code/apps/channels/registry.py +11 -0
  106. aria_code/apps/channels/tradingview.py +94 -0
  107. aria_code/apps/cli/README.md +13 -0
  108. aria_code/apps/cli/__init__.py +2 -0
  109. aria_code/apps/cli/_optional.py +120 -0
  110. aria_code/apps/cli/app_server.py +656 -0
  111. aria_code/apps/cli/auto_update.py +125 -0
  112. aria_code/apps/cli/bootstrap.py +206 -0
  113. aria_code/apps/cli/broker_render.py +206 -0
  114. aria_code/apps/cli/chat_turn.py +1327 -0
  115. aria_code/apps/cli/cloud_models.py +231 -0
  116. aria_code/apps/cli/codegen_paths.py +29 -0
  117. aria_code/apps/cli/commands/__init__.py +34 -0
  118. aria_code/apps/cli/commands/_business_render.py +78 -0
  119. aria_code/apps/cli/commands/_ui.py +71 -0
  120. aria_code/apps/cli/commands/analysis_cmds.py +378 -0
  121. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  122. aria_code/apps/cli/commands/auth_cmds.py +215 -0
  123. aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
  124. aria_code/apps/cli/commands/broker_cmds.py +1236 -0
  125. aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
  126. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  127. aria_code/apps/cli/commands/catalog.py +189 -0
  128. aria_code/apps/cli/commands/core_cmds.py +2748 -0
  129. aria_code/apps/cli/commands/data_cmds.py +521 -0
  130. aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
  131. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  132. aria_code/apps/cli/commands/engineering_cmds.py +399 -0
  133. aria_code/apps/cli/commands/file_cmds.py +312 -0
  134. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  135. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  136. aria_code/apps/cli/commands/market.py +544 -0
  137. aria_code/apps/cli/commands/market_cmds.py +1419 -0
  138. aria_code/apps/cli/commands/market_context.py +432 -0
  139. aria_code/apps/cli/commands/market_render.py +7 -0
  140. aria_code/apps/cli/commands/model_cmds.py +2374 -0
  141. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  142. aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
  143. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  144. aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
  145. aria_code/apps/cli/commands/report.py +476 -0
  146. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  147. aria_code/apps/cli/commands/session_cmds.py +236 -0
  148. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  149. aria_code/apps/cli/commands/team.py +723 -0
  150. aria_code/apps/cli/commands/team_render.py +8 -0
  151. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  152. aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
  153. aria_code/apps/cli/commands/workflow_cmds.py +629 -0
  154. aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
  155. aria_code/apps/cli/config_paths.py +73 -0
  156. aria_code/apps/cli/config_store.py +158 -0
  157. aria_code/apps/cli/constants.py +88 -0
  158. aria_code/apps/cli/context.py +34 -0
  159. aria_code/apps/cli/conversation_policy.py +53 -0
  160. aria_code/apps/cli/deterministic.py +200 -0
  161. aria_code/apps/cli/direct.py +48 -0
  162. aria_code/apps/cli/earnings_view.py +172 -0
  163. aria_code/apps/cli/endpoint_migration.py +64 -0
  164. aria_code/apps/cli/exec_events.py +255 -0
  165. aria_code/apps/cli/file_references.py +155 -0
  166. aria_code/apps/cli/finance_service_catalog.py +111 -0
  167. aria_code/apps/cli/football_reports.py +231 -0
  168. aria_code/apps/cli/github_app_auth.py +135 -0
  169. aria_code/apps/cli/google_login.py +239 -0
  170. aria_code/apps/cli/handlers/__init__.py +11 -0
  171. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  172. aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
  173. aria_code/apps/cli/handlers/market_handlers.py +2570 -0
  174. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  175. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  176. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  177. aria_code/apps/cli/headless.py +352 -0
  178. aria_code/apps/cli/helpers.py +326 -0
  179. aria_code/apps/cli/hooks.py +180 -0
  180. aria_code/apps/cli/i18n.py +310 -0
  181. aria_code/apps/cli/intent.py +136 -0
  182. aria_code/apps/cli/intent_router.py +305 -0
  183. aria_code/apps/cli/intent_shadow.py +72 -0
  184. aria_code/apps/cli/intent_signals.py +347 -0
  185. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  186. aria_code/apps/cli/local_tool_schemas.py +550 -0
  187. aria_code/apps/cli/main.py +39 -0
  188. aria_code/apps/cli/market_hours.py +100 -0
  189. aria_code/apps/cli/market_metadata.py +135 -0
  190. aria_code/apps/cli/market_universe.py +487 -0
  191. aria_code/apps/cli/message_processing.py +310 -0
  192. aria_code/apps/cli/mixin_binding.py +32 -0
  193. aria_code/apps/cli/model_catalog.py +652 -0
  194. aria_code/apps/cli/model_probe.py +169 -0
  195. aria_code/apps/cli/model_retirement.py +65 -0
  196. aria_code/apps/cli/native_branding.py +12 -0
  197. aria_code/apps/cli/native_bridge.py +118 -0
  198. aria_code/apps/cli/native_entry.py +58 -0
  199. aria_code/apps/cli/pdf_report.py +977 -0
  200. aria_code/apps/cli/plan_mode.py +139 -0
  201. aria_code/apps/cli/plotly_html.py +15 -0
  202. aria_code/apps/cli/prediction_feedback.py +202 -0
  203. aria_code/apps/cli/preflight.py +502 -0
  204. aria_code/apps/cli/project_aria.py +60 -0
  205. aria_code/apps/cli/prompt_assembly.py +78 -0
  206. aria_code/apps/cli/prompts/__init__.py +0 -0
  207. aria_code/apps/cli/prompts/coding.py +71 -0
  208. aria_code/apps/cli/prompts/select.py +193 -0
  209. aria_code/apps/cli/prompts/system_prompts.py +562 -0
  210. aria_code/apps/cli/prompts/ui.py +436 -0
  211. aria_code/apps/cli/provider_endpoints.py +237 -0
  212. aria_code/apps/cli/provider_keys.py +73 -0
  213. aria_code/apps/cli/providers/__init__.py +1 -0
  214. aria_code/apps/cli/providers/base.py +775 -0
  215. aria_code/apps/cli/providers/chat_routing.py +139 -0
  216. aria_code/apps/cli/providers/collaboration.py +98 -0
  217. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  218. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  219. aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
  220. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  221. aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
  222. aria_code/apps/cli/providers/runtime_bridge.py +586 -0
  223. aria_code/apps/cli/providers/vertexai_stream.py +592 -0
  224. aria_code/apps/cli/release_download.py +159 -0
  225. aria_code/apps/cli/review_runner.py +111 -0
  226. aria_code/apps/cli/runtime_consumer.py +769 -0
  227. aria_code/apps/cli/sandbox.py +32 -0
  228. aria_code/apps/cli/session_export.py +120 -0
  229. aria_code/apps/cli/session_jsonl.py +208 -0
  230. aria_code/apps/cli/session_refresh.py +123 -0
  231. aria_code/apps/cli/session_store.py +125 -0
  232. aria_code/apps/cli/skills_catalog.py +273 -0
  233. aria_code/apps/cli/task_isolation.py +211 -0
  234. aria_code/apps/cli/terminal_chrome.py +412 -0
  235. aria_code/apps/cli/todo_tracker.py +190 -0
  236. aria_code/apps/cli/tool_executor.py +932 -0
  237. aria_code/apps/cli/tool_registry.py +34 -0
  238. aria_code/apps/cli/tool_scope.py +113 -0
  239. aria_code/apps/cli/tools/__init__.py +40 -0
  240. aria_code/apps/cli/tools/context.py +46 -0
  241. aria_code/apps/cli/tools/file_tools.py +181 -0
  242. aria_code/apps/cli/tools/market_tools.py +626 -0
  243. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  244. aria_code/apps/cli/tools/system_tools.py +656 -0
  245. aria_code/apps/cli/tools/write_tools.py +961 -0
  246. aria_code/apps/cli/tradingview_bridge.py +529 -0
  247. aria_code/apps/cli/transactions.py +269 -0
  248. aria_code/apps/cli/turn_planning.py +137 -0
  249. aria_code/apps/cli/update_check.py +257 -0
  250. aria_code/apps/cli/updater.py +373 -0
  251. aria_code/apps/cli/utils/__init__.py +0 -0
  252. aria_code/apps/cli/utils/market_detect.py +1663 -0
  253. aria_code/apps/cli/web_login.py +136 -0
  254. aria_code/apps/cli/workflow_runner.py +127 -0
  255. aria_code/apps/cli/workspace_route.py +121 -0
  256. aria_code/apps/daemon/README.md +14 -0
  257. aria_code/apps/vscode/README.md +115 -0
  258. aria_code/apps/vscode/package.json +70 -0
  259. aria_code/aria_cli.py +5244 -0
  260. aria_code/aria_daemon.py +1372 -0
  261. aria_code/aria_feishu_bot.py +16 -0
  262. aria_code/aria_mcp_server.py +61 -0
  263. aria_code/aria_relay_client.py +4 -0
  264. aria_code/aria_telegram_bot.py +11 -0
  265. aria_code/ariarc.py +385 -0
  266. aria_code/artifacts.py +581 -0
  267. aria_code/backtest_engine.py +4 -0
  268. aria_code/backtest_report.py +4 -0
  269. aria_code/brokers/__init__.py +78 -0
  270. aria_code/brokers/automation.py +193 -0
  271. aria_code/brokers/base.py +231 -0
  272. aria_code/brokers/capabilities.py +264 -0
  273. aria_code/brokers/cn/__init__.py +10 -0
  274. aria_code/brokers/cn/easytrader_broker.py +193 -0
  275. aria_code/brokers/cn/futu_broker.py +194 -0
  276. aria_code/brokers/cn/longbridge_broker.py +190 -0
  277. aria_code/brokers/cn/tiger_broker.py +196 -0
  278. aria_code/brokers/cn/xtquant_broker.py +225 -0
  279. aria_code/brokers/config.py +414 -0
  280. aria_code/brokers/intl/__init__.py +5 -0
  281. aria_code/brokers/intl/alpaca_broker.py +183 -0
  282. aria_code/brokers/intl/ibkr_broker.py +215 -0
  283. aria_code/brokers/intl/trading212_broker.py +183 -0
  284. aria_code/brokers/intl/webull_broker.py +156 -0
  285. aria_code/brokers/paper_broker.py +266 -0
  286. aria_code/brokers/planning.py +296 -0
  287. aria_code/brokers/registry.py +213 -0
  288. aria_code/brokers/trading.py +267 -0
  289. aria_code/canva_client.py +4 -0
  290. aria_code/change_store.py +127 -0
  291. aria_code/clients/__init__.py +0 -0
  292. aria_code/clients/aliyun_data_client.py +660 -0
  293. aria_code/clients/aria_relay_client.py +16 -0
  294. aria_code/clients/canva_client.py +284 -0
  295. aria_code/clients/figma_client.py +100 -0
  296. aria_code/clients/football_data_client.py +1671 -0
  297. aria_code/clients/kling_video_client.py +182 -0
  298. aria_code/clients/market_data_client.py +1985 -0
  299. aria_code/clients/openai_image_client.py +242 -0
  300. aria_code/clients/runway_video_client.py +157 -0
  301. aria_code/clients/warehouse_erp_client.py +105 -0
  302. aria_code/cloud_memory.py +190 -0
  303. aria_code/command_safety.py +19 -0
  304. aria_code/computer_use_server.py +276 -0
  305. aria_code/computer_use_tools.py +4 -0
  306. aria_code/dashboard_generator.py +579 -0
  307. aria_code/data_analysis_tools.py +4 -0
  308. aria_code/data_cleaner.py +483 -0
  309. aria_code/data_service.py +513 -0
  310. aria_code/datasources/__init__.py +23 -0
  311. aria_code/datasources/base.py +166 -0
  312. aria_code/datasources/router.py +253 -0
  313. aria_code/datasources/sec_statements.py +217 -0
  314. aria_code/datasources/sources/__init__.py +15 -0
  315. aria_code/datasources/sources/akshare_source.py +269 -0
  316. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  317. aria_code/datasources/sources/edgar_source.py +219 -0
  318. aria_code/datasources/sources/finnhub_source.py +198 -0
  319. aria_code/datasources/sources/fred_source.py +223 -0
  320. aria_code/datasources/sources/tushare_source.py +143 -0
  321. aria_code/datasources/sources/web_scraper_source.py +278 -0
  322. aria_code/datasources/sources/world_bank_source.py +206 -0
  323. aria_code/datasources/sources/yfinance_source.py +152 -0
  324. aria_code/doctor.py +860 -0
  325. aria_code/domain/__init__.py +0 -0
  326. aria_code/domain/_finance_core/__init__.py +5 -0
  327. aria_code/domain/_finance_core/backtest_engine.py +325 -0
  328. aria_code/domain/_finance_core/performance_stats.py +101 -0
  329. aria_code/domain/backtest_engine.py +75 -0
  330. aria_code/domain/backtest_report.py +488 -0
  331. aria_code/domain/finance_formulas.py +389 -0
  332. aria_code/domain/performance_stats.py +6 -0
  333. aria_code/domain/portfolio_ledger.py +307 -0
  334. aria_code/domain/strategy_vault.py +421 -0
  335. aria_code/evals/__init__.py +37 -0
  336. aria_code/evals/behavior.py +113 -0
  337. aria_code/evals/gap.py +93 -0
  338. aria_code/evals/harness.py +949 -0
  339. aria_code/evals/review_probe.py +132 -0
  340. aria_code/evals/runner.py +425 -0
  341. aria_code/evals/scorecard.py +322 -0
  342. aria_code/evals/summary.py +125 -0
  343. aria_code/external_agent_runner.py +115 -0
  344. aria_code/figma_client.py +4 -0
  345. aria_code/file_analysis_tools.py +4 -0
  346. aria_code/finance_formulas.py +4 -0
  347. aria_code/football_data_client.py +4 -0
  348. aria_code/image_gen_tools.py +4 -0
  349. aria_code/intent_classifier.py +459 -0
  350. aria_code/kling_video_client.py +4 -0
  351. aria_code/licensing.py +151 -0
  352. aria_code/local_finance_tools.py +4 -0
  353. aria_code/local_image_provider.py +4 -0
  354. aria_code/local_llm_provider.py +4 -0
  355. aria_code/macro_tools.py +4 -0
  356. aria_code/markdown_pdf.py +289 -0
  357. aria_code/market_data_client.py +4 -0
  358. aria_code/mcp_client.py +744 -0
  359. aria_code/memory_manager.py +273 -0
  360. aria_code/model_capability.py +512 -0
  361. aria_code/notification_tools.py +4 -0
  362. aria_code/openai_image_client.py +4 -0
  363. aria_code/packages/__init__.py +26 -0
  364. aria_code/packages/adk_bridge/__init__.py +11 -0
  365. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  366. aria_code/packages/adk_bridge/market_tools.py +125 -0
  367. aria_code/packages/aria_agents/__init__.py +5 -0
  368. aria_code/packages/aria_agents/manifest.py +78 -0
  369. aria_code/packages/aria_core/__init__.py +34 -0
  370. aria_code/packages/aria_core/architecture.py +269 -0
  371. aria_code/packages/aria_core/export.py +147 -0
  372. aria_code/packages/aria_core/manifest.py +65 -0
  373. aria_code/packages/aria_core/paths.py +57 -0
  374. aria_code/packages/aria_core/secure_file.py +68 -0
  375. aria_code/packages/aria_infra/__init__.py +15 -0
  376. aria_code/packages/aria_infra/arthera.py +58 -0
  377. aria_code/packages/aria_infra/doctor.py +246 -0
  378. aria_code/packages/aria_infra/product.py +40 -0
  379. aria_code/packages/aria_mcp/__init__.py +25 -0
  380. aria_code/packages/aria_mcp/bridge.py +232 -0
  381. aria_code/packages/aria_mcp/circuit.py +82 -0
  382. aria_code/packages/aria_mcp/config.py +162 -0
  383. aria_code/packages/aria_mcp/installer.py +95 -0
  384. aria_code/packages/aria_mcp/server.py +1705 -0
  385. aria_code/packages/aria_mcp/tools.py +81 -0
  386. aria_code/packages/aria_sdk/__init__.py +19 -0
  387. aria_code/packages/aria_sdk/client.py +393 -0
  388. aria_code/packages/aria_sdk/providers.py +70 -0
  389. aria_code/packages/aria_sdk/streaming.py +75 -0
  390. aria_code/packages/aria_sdk/types.py +86 -0
  391. aria_code/packages/aria_services/__init__.py +92 -0
  392. aria_code/packages/aria_services/cache.py +60 -0
  393. aria_code/packages/aria_services/context.py +356 -0
  394. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  395. aria_code/packages/aria_services/data.py +11 -0
  396. aria_code/packages/aria_services/provider_health.py +304 -0
  397. aria_code/packages/aria_services/references.py +247 -0
  398. aria_code/packages/aria_services/registry.py +249 -0
  399. aria_code/packages/aria_services/research_protocol.py +137 -0
  400. aria_code/packages/aria_services/research_quality.py +186 -0
  401. aria_code/packages/aria_services/settings.py +160 -0
  402. aria_code/packages/aria_services/usage.py +151 -0
  403. aria_code/packages/aria_skills/__init__.py +46 -0
  404. aria_code/packages/aria_skills/catalog.py +149 -0
  405. aria_code/packages/aria_skills/loader.py +455 -0
  406. aria_code/packages/aria_skills/registry.py +84 -0
  407. aria_code/packages/aria_tools/__init__.py +5 -0
  408. aria_code/packages/aria_tools/financial/__init__.py +5 -0
  409. aria_code/packages/aria_tools/financial/_paths.py +92 -0
  410. aria_code/packages/aria_tools/registry.py +129 -0
  411. aria_code/packages/quant_engine/__init__.py +33 -0
  412. aria_code/packages/quant_engine/_namespace.py +24 -0
  413. aria_code/packages/quant_engine/backtest/__init__.py +10 -0
  414. aria_code/packages/quant_engine/backtest/core.py +53 -0
  415. aria_code/packages/quant_engine/backtest/engine.py +102 -0
  416. aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
  417. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  418. aria_code/packages/quant_engine/sports/__init__.py +76 -0
  419. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  420. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  421. aria_code/packages/quant_engine/sports/elo.py +300 -0
  422. aria_code/packages/quant_engine/sports/form.py +188 -0
  423. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  424. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  425. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  426. aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
  427. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  428. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  429. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  430. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  431. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  432. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  433. aria_code/packs/__init__.py +85 -0
  434. aria_code/packs/base.py +168 -0
  435. aria_code/packs/finance/__init__.py +250 -0
  436. aria_code/packs/healthcare/__init__.py +40 -0
  437. aria_code/packs/logistics/__init__.py +171 -0
  438. aria_code/packs/payments/__init__.py +139 -0
  439. aria_code/packs/realty/__init__.py +190 -0
  440. aria_code/packs/registry.py +202 -0
  441. aria_code/packs/rules.py +90 -0
  442. aria_code/plan_utils.py +194 -0
  443. aria_code/plugin_loader.py +328 -0
  444. aria_code/portfolio_ledger.py +4 -0
  445. aria_code/preview_server.py +449 -0
  446. aria_code/privacy/__init__.py +5 -0
  447. aria_code/privacy/feedback.py +123 -0
  448. aria_code/project_review.py +278 -0
  449. aria_code/project_review_api.py +235 -0
  450. aria_code/project_review_server.py +18 -0
  451. aria_code/project_tools.py +4 -0
  452. aria_code/providers/__init__.py +30 -0
  453. aria_code/providers/google_video_provider.py +256 -0
  454. aria_code/providers/llm/__init__.py +19 -0
  455. aria_code/providers/llm/anthropic.py +184 -0
  456. aria_code/providers/llm/arthera_gateway.py +90 -0
  457. aria_code/providers/llm/autoconfig.py +213 -0
  458. aria_code/providers/llm/base.py +139 -0
  459. aria_code/providers/llm/custom.py +158 -0
  460. aria_code/providers/llm/ollama.py +137 -0
  461. aria_code/providers/llm/openai_compat.py +350 -0
  462. aria_code/providers/llm/registry.py +457 -0
  463. aria_code/providers/local_image_provider.py +202 -0
  464. aria_code/providers/local_llm_provider.py +549 -0
  465. aria_code/providers/ollama_models.py +79 -0
  466. aria_code/providers/openai_params.py +32 -0
  467. aria_code/providers/tool_messages.py +100 -0
  468. aria_code/realty_data_tools.py +4 -0
  469. aria_code/report_exporters.py +150 -0
  470. aria_code/report_generator.py +1764 -0
  471. aria_code/review_service.py +497 -0
  472. aria_code/runtime/__init__.py +176 -0
  473. aria_code/runtime/acceptance.py +632 -0
  474. aria_code/runtime/agent_loop.py +1851 -0
  475. aria_code/runtime/agent_profiles.py +75 -0
  476. aria_code/runtime/approval.py +71 -0
  477. aria_code/runtime/baseline.py +219 -0
  478. aria_code/runtime/budget.py +232 -0
  479. aria_code/runtime/checkpoints.py +481 -0
  480. aria_code/runtime/consult.py +65 -0
  481. aria_code/runtime/contract.py +282 -0
  482. aria_code/runtime/delivery.py +432 -0
  483. aria_code/runtime/events.py +113 -0
  484. aria_code/runtime/gateway.py +167 -0
  485. aria_code/runtime/lsp.py +484 -0
  486. aria_code/runtime/native_index.py +153 -0
  487. aria_code/runtime/orchestrator.py +55 -0
  488. aria_code/runtime/processes.py +343 -0
  489. aria_code/runtime/project_graph.py +614 -0
  490. aria_code/runtime/repo_map.py +712 -0
  491. aria_code/runtime/research_bundle.py +105 -0
  492. aria_code/runtime/review.py +269 -0
  493. aria_code/runtime/run_state.py +127 -0
  494. aria_code/runtime/run_store.py +522 -0
  495. aria_code/runtime/scoped_runs.py +113 -0
  496. aria_code/runtime/self_healing.py +422 -0
  497. aria_code/runtime/semantic_diff.py +171 -0
  498. aria_code/runtime/subagent.py +721 -0
  499. aria_code/runtime/symbol_edit.py +117 -0
  500. aria_code/runtime/task_brief.py +46 -0
  501. aria_code/runtime/task_context.py +145 -0
  502. aria_code/runtime/task_ledger.py +108 -0
  503. aria_code/runtime/task_orchestrator.py +90 -0
  504. aria_code/runtime/task_outcome.py +33 -0
  505. aria_code/runtime/task_verification.py +266 -0
  506. aria_code/runtime/task_worktree.py +448 -0
  507. aria_code/runtime/tool_executor.py +329 -0
  508. aria_code/runtime/tool_policy.py +107 -0
  509. aria_code/runtime/transactions.py +216 -0
  510. aria_code/runtime/windows_job.py +115 -0
  511. aria_code/runtime/workflows.py +222 -0
  512. aria_code/runtime/worktrees.py +215 -0
  513. aria_code/runway_video_client.py +4 -0
  514. aria_code/safety/__init__.py +27 -0
  515. aria_code/safety/client_isolation.py +178 -0
  516. aria_code/safety/permissions.py +339 -0
  517. aria_code/safety/risk.py +699 -0
  518. aria_code/safety/sandbox.py +148 -0
  519. aria_code/safety/service.py +102 -0
  520. aria_code/service_endpoints.py +14 -0
  521. aria_code/setup_wizard.py +684 -0
  522. aria_code/spreadsheet_tools.py +4 -0
  523. aria_code/strategy_vault.py +4 -0
  524. aria_code/tools/__init__.py +0 -0
  525. aria_code/tools/artifact_tools.py +252 -0
  526. aria_code/tools/broker_tools.py +92 -0
  527. aria_code/tools/build_quant_engine.py +97 -0
  528. aria_code/tools/code_audit_tools.py +192 -0
  529. aria_code/tools/computer_use_tools.py +509 -0
  530. aria_code/tools/data_analysis_tools.py +831 -0
  531. aria_code/tools/enterprise_finance_tools.py +202 -0
  532. aria_code/tools/extended_tools.py +240 -0
  533. aria_code/tools/file_analysis_tools.py +880 -0
  534. aria_code/tools/image_gen_tools.py +114 -0
  535. aria_code/tools/local_finance_tools.py +3488 -0
  536. aria_code/tools/logistics_carriers.py +11 -0
  537. aria_code/tools/logistics_inventory.py +11 -0
  538. aria_code/tools/logistics_io.py +11 -0
  539. aria_code/tools/logistics_tenancy.py +11 -0
  540. aria_code/tools/logistics_tools.py +11 -0
  541. aria_code/tools/macro_tools.py +412 -0
  542. aria_code/tools/notification_tools.py +249 -0
  543. aria_code/tools/project_tools.py +525 -0
  544. aria_code/tools/realty_data_tools.py +662 -0
  545. aria_code/tools/spreadsheet_tools.py +399 -0
  546. aria_code/tools/stripe_tools.py +115 -0
  547. aria_code/tools/video_tools.py +500 -0
  548. aria_code/training/__init__.py +1 -0
  549. aria_code/training/compare.py +117 -0
  550. aria_code/training/prelude_commands.py +111 -0
  551. aria_code/training/prelude_eval.py +390 -0
  552. aria_code/training/prelude_real.py +183 -0
  553. aria_code/training/prelude_train.py +248 -0
  554. aria_code/training/sft_dataset.py +230 -0
  555. aria_code/training/trajectory_dataset.py +263 -0
  556. aria_code/ui/__init__.py +105 -0
  557. aria_code/ui/banner.py +404 -0
  558. aria_code/ui/completer.py +571 -0
  559. aria_code/ui/console.py +285 -0
  560. aria_code/ui/input_box.py +505 -0
  561. aria_code/ui/mentions.py +206 -0
  562. aria_code/ui/picker.py +248 -0
  563. aria_code/ui/render/__init__.py +11 -0
  564. aria_code/ui/render/actions.py +297 -0
  565. aria_code/ui/render/finance.py +1732 -0
  566. aria_code/ui/render/market.py +253 -0
  567. aria_code/ui/render/output.py +954 -0
  568. aria_code/ui/render/responsive.py +75 -0
  569. aria_code/ui/render/team.py +367 -0
  570. aria_code/ui/robot.py +239 -0
  571. aria_code/ui/startup_dashboard.py +110 -0
  572. aria_code/video_analysis.py +147 -0
  573. aria_code/video_editor.py +518 -0
  574. aria_code/video_workflow/__init__.py +8 -0
  575. aria_code/video_workflow/__main__.py +61 -0
  576. aria_code/video_workflow/engines/__init__.py +18 -0
  577. aria_code/video_workflow/engines/base.py +69 -0
  578. aria_code/video_workflow/engines/custom_model.py +204 -0
  579. aria_code/video_workflow/engines/google_veo.py +94 -0
  580. aria_code/video_workflow/engines/local_engine.py +71 -0
  581. aria_code/video_workflow/engines/registry.py +98 -0
  582. aria_code/video_workflow/google_models.py +278 -0
  583. aria_code/video_workflow/music/__init__.py +5 -0
  584. aria_code/video_workflow/music/foley_service.py +132 -0
  585. aria_code/video_workflow/music/service.py +212 -0
  586. aria_code/video_workflow/pipeline.py +620 -0
  587. aria_code/video_workflow/plan.py +202 -0
  588. aria_code/video_workflow/session.py +297 -0
  589. aria_code/workspace/__init__.py +6 -0
  590. aria_code/workspace/files.py +219 -0
  591. aria_code/workspace/verify.py +167 -0
  592. aria_code/workspace_changes.py +199 -0
  593. aria_code/workspace_service.py +196 -0
  594. aria_code/workspace_tools.py +150 -0
  595. aria_code-0.167.0.dist-info/METADATA +330 -0
  596. aria_code-0.167.0.dist-info/RECORD +601 -0
  597. aria_code-0.167.0.dist-info/WHEEL +5 -0
  598. aria_code-0.167.0.dist-info/entry_points.txt +5 -0
  599. aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
  600. aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
  601. aria_code-0.167.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,457 @@
1
+ """
2
+ providers/llm/registry.py — LLM Provider 注册中心
3
+ ==================================================
4
+ • 从 ~/.aria/providers.yaml 等用户级文件加载配置(不读项目里的 .aria.json)
5
+ • 按优先级自动路由:本地 Ollama → DeepSeek → OpenAI → Anthropic → Groq
6
+ • 提供 stream_cloud_fallback() 供 aria_cli.py 调用
7
+
8
+ 用户配置示例 (~/.aria/providers.yaml):
9
+ llm:
10
+ default: ollama/qwen2.5:7b
11
+ fallback:
12
+ - deepseek/deepseek-chat
13
+ - openai/gpt-4o-mini
14
+ - anthropic/claude-3-5-haiku-latest
15
+ code_tasks: ollama/qwen2.5-coder:7b
16
+ heavy_analysis: anthropic/claude-3-5-sonnet-20241022
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import asyncio
22
+ import logging
23
+ import os
24
+ from pathlib import Path
25
+ from typing import Any, AsyncIterator, Callable, Dict, List, Optional, Tuple, Type
26
+
27
+ import yaml
28
+
29
+ from .base import BaseLLMProvider, Message, ProviderConfig
30
+ from .ollama import OllamaProvider
31
+ from .openai_compat import (
32
+ DeepSeekProvider, OpenAIProvider, GroqProvider, GoogleProvider,
33
+
34
+ TogetherProvider, DashScopeProvider, LMStudioProvider,
35
+ SiliconFlowProvider, MoonshotProvider, ZhiPuProvider,
36
+ )
37
+ from .anthropic import AnthropicProvider
38
+ from .custom import CustomProviderSpec, build_custom_provider, parse_custom_providers
39
+ from aria_code.packages.aria_services.provider_health import (
40
+ GLOBAL_PROVIDER_HEALTH,
41
+ ProviderHealthRegistry,
42
+ classify_provider_error,
43
+ )
44
+ from aria_code.packages.aria_core.paths import aria_home
45
+
46
+ from .arthera_gateway import ArtheraGatewayProvider
47
+
48
+ logger = logging.getLogger(__name__)
49
+
50
+ # ── Provider 目录:name → class ──────────────────────────────────────────────
51
+ _PROVIDER_CLASSES: Dict[str, Type[BaseLLMProvider]] = {
52
+ "ollama": OllamaProvider,
53
+ "deepseek": DeepSeekProvider,
54
+ "openai": OpenAIProvider,
55
+ "anthropic": AnthropicProvider,
56
+ # Gemini is the default model in the shipped config but had no registry
57
+ # entry, so it could not be reached through the provider chain at all.
58
+ "google": GoogleProvider,
59
+ "gemini": GoogleProvider,
60
+ # Google models through the Arthera gateway, authorised by the session
61
+ # /login stored. Needs no key from the user, which is the point.
62
+ "arthera": ArtheraGatewayProvider,
63
+ "groq": GroqProvider,
64
+ "together": TogetherProvider,
65
+ "dashscope": DashScopeProvider,
66
+ "lmstudio": LMStudioProvider,
67
+ # 国内可访问
68
+ "siliconflow": SiliconFlowProvider,
69
+ "moonshot": MoonshotProvider,
70
+ "zhipu": ZhiPuProvider,
71
+ }
72
+
73
+ # ── 默认 fallback 优先级(无用户配置时)────────────────────────────────────
74
+ # 国内环境优先走 DeepSeek / SiliconFlow / DashScope,再尝试 OpenAI / Groq
75
+ _DEFAULT_FALLBACK_CHAIN = [
76
+ ("ollama", None, None),
77
+ # Before the key-based entries: a signed-in user already has Google
78
+ # models and should not be asked for a key to reach them. Skipped when
79
+ # signed out, because the provider reports itself unavailable.
80
+ ("arthera", None, "google/gemini-3.8-flash"),
81
+ ("deepseek", "DEEPSEEK_API_KEY", "deepseek-chat"),
82
+ ("siliconflow", "SILICONFLOW_API_KEY", "deepseek-ai/DeepSeek-V3"),
83
+ ("dashscope", "DASHSCOPE_API_KEY", "qwen-plus"),
84
+ ("moonshot", "MOONSHOT_API_KEY", "moonshot-v1-8k"),
85
+ ("zhipu", "ZHIPUAI_API_KEY", "glm-4-flash"),
86
+ ("google", "GEMINI_API_KEY", "gemini-3.8-flash"),
87
+ ("openai", "OPENAI_API_KEY", "gpt-4o-mini"),
88
+ ("anthropic", "ANTHROPIC_API_KEY", "claude-3-5-haiku-latest"),
89
+ ("groq", "GROQ_API_KEY", "llama-3.3-70b-versatile"),
90
+ ]
91
+
92
+ # ── 用户可注册自定义 provider ─────────────────────────────────────────────────
93
+ def register_provider(name: str, cls: Type[BaseLLMProvider]) -> None:
94
+ """注册自定义 provider 类(供插件/用户扩展使用)"""
95
+ _PROVIDER_CLASSES[name.lower()] = cls
96
+ logger.info(f"✓ 注册自定义 provider: {name}")
97
+
98
+
99
+ # ── 配置加载 ──────────────────────────────────────────────────────────────────
100
+ _CONFIG_PATHS = [
101
+ # ~/.arthera/providers.json is the primary path used by the aria-code CLI (/apikey command)
102
+ aria_home() / "providers.json",
103
+ # Legacy / alternative paths
104
+ Path.home() / ".aria" / "providers.yaml",
105
+ Path.home() / ".aria" / "providers.json",
106
+ # No project-relative ./.aria.json or ./.aria.yaml: every reader of this
107
+ # list chooses where conversations go or which credentials accompany them
108
+ # (model_providers base_url + env_key, llm.<name>.base_url, the fallback
109
+ # chain), and a cloned repository must not be able to decide that. The Go
110
+ # runtime (go/runtime/provider_declared.go) applies the same rule.
111
+ ]
112
+
113
+
114
+ _CUSTOM_SPECS_CACHE: Optional[Dict[str, "CustomProviderSpec"]] = None
115
+
116
+
117
+ def _custom_provider_specs() -> Dict[str, "CustomProviderSpec"]:
118
+ """解析一次并缓存。配置在进程生命周期内不变,重复解析只会重复刷日志。"""
119
+ global _CUSTOM_SPECS_CACHE
120
+ if _CUSTOM_SPECS_CACHE is None:
121
+ try:
122
+ _CUSTOM_SPECS_CACHE = parse_custom_providers(_load_raw_user_config())
123
+ except Exception as exc:
124
+ logger.warning("解析 model_providers 失败,忽略自定义 provider: %s", exc)
125
+ _CUSTOM_SPECS_CACHE = {}
126
+ return _CUSTOM_SPECS_CACHE
127
+
128
+
129
+ def reload_custom_providers() -> Dict[str, "CustomProviderSpec"]:
130
+ """清空缓存并重新解析(供 /config reload 之类的命令调用)。"""
131
+ global _CUSTOM_SPECS_CACHE
132
+ _CUSTOM_SPECS_CACHE = None
133
+ return _custom_provider_specs()
134
+
135
+
136
+ def _load_raw_user_config() -> Dict:
137
+ """读取用户配置的**顶层**结构。
138
+
139
+ 注意跟 _load_user_config() 的区别:那个函数返回的是 `llm` 子节(历史行为,
140
+ 调用方依赖它直接拿到 default/fallback),而 model_providers 与 llm 平级,
141
+ 必须从顶层取,否则永远读不到。
142
+ """
143
+ for path in _CONFIG_PATHS:
144
+ if not path.exists():
145
+ continue
146
+ try:
147
+ with open(path, encoding="utf-8") as fh:
148
+ data = yaml.safe_load(fh) if path.suffix in (".yaml", ".yml") \
149
+ else __import__("json").load(fh)
150
+ if isinstance(data, dict):
151
+ return data
152
+ except Exception as exc:
153
+ logger.debug("加载配置 %s 失败: %s", path, exc)
154
+ return {}
155
+
156
+ def _load_user_config() -> Dict:
157
+ for p in _CONFIG_PATHS:
158
+ if p.exists():
159
+ try:
160
+ with open(p, encoding="utf-8") as f:
161
+ data = yaml.safe_load(f) if p.suffix in (".yaml",".yml") \
162
+ else __import__("json").load(f)
163
+ return data.get("llm", data) if isinstance(data, dict) else {}
164
+ except Exception as e:
165
+ logger.debug(f"加载配置 {p} 失败: {e}")
166
+ return {}
167
+
168
+
169
+ def _load_provider_cfg_from_file(name: str) -> Dict[str, str]:
170
+ """
171
+ 从 ~/.arthera/providers.json 的 llm 节读取指定 provider 的 api_key / base_url。
172
+ 这是 /apikey set 命令写入的位置;ProviderConfig.from_env() 只读环境变量,
173
+ 此函数补足文件侧的配置,让两者合并后才能正确工作。
174
+ """
175
+ import json as _json
176
+ primary = aria_home() / "providers.json"
177
+ for p in [primary] + _CONFIG_PATHS:
178
+ if not p.exists():
179
+ continue
180
+ try:
181
+ raw = _json.loads(p.read_text(encoding="utf-8")) if p.suffix == ".json" \
182
+ else yaml.safe_load(p.read_text(encoding="utf-8"))
183
+ llm = raw.get("llm", raw) if isinstance(raw, dict) else {}
184
+ entry = llm.get(name.lower(), {})
185
+ if entry:
186
+ return {k: v for k, v in entry.items() if v}
187
+ except Exception:
188
+ pass
189
+ return {}
190
+
191
+
192
+ def _parse_provider_spec(spec: str) -> Tuple[str, Optional[str]]:
193
+ """
194
+ 解析 'deepseek/deepseek-chat' → ('deepseek', 'deepseek-chat')
195
+ 解析 'ollama' → ('ollama', None)
196
+ """
197
+ if "/" in spec:
198
+ name, model = spec.split("/", 1)
199
+ return name.strip().lower(), model.strip()
200
+ return spec.strip().lower(), None
201
+
202
+
203
+ def get_provider(
204
+ spec: str,
205
+ api_key: Optional[str] = None,
206
+ base_url: Optional[str] = None,
207
+ ) -> BaseLLMProvider:
208
+ """
209
+ 按 spec 字符串实例化 provider。
210
+
211
+ Examples:
212
+ get_provider("ollama/qwen2.5:7b")
213
+ get_provider("deepseek/deepseek-chat")
214
+ get_provider("anthropic/claude-3-5-haiku-latest")
215
+ """
216
+ name, model = _parse_provider_spec(spec)
217
+
218
+ # 用户在 model_providers 里声明的 provider 优先于内置同名项——用户显式写下
219
+ # 的配置应当能覆盖内置默认(比如把 openai 指向自建的兼容网关)。
220
+ custom = _custom_provider_specs().get(name)
221
+ if custom is not None:
222
+ if custom.env_key and not custom.api_key():
223
+ raise ValueError(custom.missing_key_hint())
224
+ provider = build_custom_provider(custom, model)
225
+ if api_key:
226
+ provider.config.api_key = api_key
227
+ if base_url:
228
+ provider.config.base_url = base_url
229
+ return provider
230
+
231
+ cls = _PROVIDER_CLASSES.get(name)
232
+ if not cls:
233
+ known = list(_PROVIDER_CLASSES) + list(_custom_provider_specs())
234
+ raise ValueError(
235
+ f"未知 provider: '{name}'。可用: {', '.join(sorted(known))}\n"
236
+ f"如需接入未列出的服务,在 ~/.aria/providers.yaml 的 model_providers "
237
+ f"段声明即可(只填 base_url 与 env_key,不要写密钥值)。"
238
+ )
239
+ cfg = _build_cfg(name, model)
240
+ # 调用方显式传入的参数优先级最高
241
+ if api_key:
242
+ cfg.api_key = api_key
243
+ if base_url:
244
+ cfg.base_url = base_url
245
+ return cls(cfg)
246
+
247
+
248
+ def list_available_providers() -> List[Dict[str, Any]]:
249
+ """返回所有 provider 及其可用状态(同步,用于 /config 命令显示)"""
250
+ result = []
251
+ for name, cls in _PROVIDER_CLASSES.items():
252
+ cfg = _build_cfg(name) # 合并环境变量 + providers.json
253
+ available = cfg.is_configured()
254
+ result.append({
255
+ "name": name,
256
+ "available": available,
257
+ "local": cls.local,
258
+ "tools": cls.supports_tools,
259
+ "thinking": cls.supports_thinking,
260
+ "source": "builtin",
261
+ })
262
+ # 用户在 model_providers 里声明的 provider 也要出现在 /config 里——否则
263
+ # 用户配好了却在列表中看不到,会以为没生效。
264
+ for pid, spec in sorted(_custom_provider_specs().items()):
265
+ result.append({
266
+ "name": pid,
267
+ "available": bool(spec.api_key()) if spec.env_key else True,
268
+ "local": False,
269
+ "tools": True,
270
+ "thinking": False,
271
+ "source": "custom",
272
+ "base_url": spec.base_url,
273
+ "hint": "" if (not spec.env_key or spec.api_key()) else spec.missing_key_hint(),
274
+ })
275
+ return result
276
+
277
+
278
+ def _build_cfg(name: str, model: Optional[str] = None) -> ProviderConfig:
279
+ """
280
+ 构建 ProviderConfig:环境变量优先,再回落到 providers.json,
281
+ 确保 /apikey set 保存的 key 能被实际使用。
282
+ """
283
+ cfg = ProviderConfig.from_env(name)
284
+ file_cfg = _load_provider_cfg_from_file(name)
285
+
286
+ # 补充 api_key(文件里的)— 环境变量已在 from_env() 中优先读取;
287
+ # 文件是后备:提示用户改用环境变量以避免明文存储 key。
288
+ if not cfg.api_key and file_cfg.get("api_key"):
289
+ cfg.api_key = file_cfg["api_key"]
290
+ _env_names = {
291
+ "deepseek": "DEEPSEEK_API_KEY", "openai": "OPENAI_API_KEY",
292
+ "anthropic": "ANTHROPIC_API_KEY", "groq": "GROQ_API_KEY",
293
+ "siliconflow": "SILICONFLOW_API_KEY", "moonshot": "MOONSHOT_API_KEY",
294
+ "zhipu": "ZHIPUAI_API_KEY", "dashscope": "DASHSCOPE_API_KEY",
295
+ }
296
+ if name.lower() in _env_names:
297
+ logger.debug(
298
+ "⚠ API key for '%s' loaded from ~/.arthera/providers.json (plaintext). "
299
+ "Migrate to env var: export %s=<key> then remove api_key from aria_code.providers.json.",
300
+ name, _env_names[name.lower()],
301
+ )
302
+ # 补充 base_url(支持用户自定义端点 / 代理)
303
+ if not cfg.base_url and file_cfg.get("base_url"):
304
+ cfg.base_url = file_cfg["base_url"]
305
+
306
+ if model:
307
+ cfg.model = model
308
+ return cfg
309
+
310
+
311
+ async def _try_provider(
312
+ spec: str,
313
+ messages: List[Message],
314
+ on_token: Optional[Callable] = None,
315
+ cancel_event=None,
316
+ *,
317
+ health: ProviderHealthRegistry | None = None,
318
+ ) -> Optional[Dict[str, Any]]:
319
+ """尝试用指定 provider 完成对话,失败返回 None。"""
320
+ health = health or GLOBAL_PROVIDER_HEALTH
321
+ try:
322
+ name, model = _parse_provider_spec(spec)
323
+ cls = _PROVIDER_CLASSES.get(name)
324
+ if not cls:
325
+ return None
326
+
327
+ cfg = _build_cfg(name, model)
328
+ provider = cls(cfg)
329
+
330
+ if health.provider_in_cooldown(name):
331
+ logger.debug(f"[{name}] cooling down, skipped")
332
+ return None
333
+
334
+ if not await provider.is_available():
335
+ logger.debug(f"[{name}] 不可用,跳过")
336
+ health.mark_issue(classify_provider_error(name, "provider unavailable"))
337
+ return None
338
+
339
+ logger.info(f"[{name}] 尝试生成响应 (model={cfg.model})")
340
+ full_text = ""
341
+ async for event in provider.stream(
342
+ messages, cancel_event=cancel_event
343
+ ):
344
+ t = event.get("type")
345
+ if t == "token":
346
+ tok = event.get("text", "")
347
+ full_text += tok
348
+ if on_token:
349
+ on_token(tok)
350
+ elif t == "error":
351
+ err = event.get("message")
352
+ logger.debug(f"[{name}] 流式错误: {err}")
353
+ health.mark_issue(classify_provider_error(name, err))
354
+ return None
355
+ elif t == "done":
356
+ break
357
+
358
+ if not full_text.strip():
359
+ health.mark_issue(classify_provider_error(name, "provider returned no usable data"))
360
+ return None
361
+
362
+ health.mark_success(name)
363
+ return {
364
+ "success": True,
365
+ "response": full_text,
366
+ "provider": name,
367
+ "model": cfg.model or "unknown",
368
+ }
369
+ except Exception as e:
370
+ logger.debug(f"[{spec}] 异常: {e}")
371
+ health.mark_issue(classify_provider_error(_parse_provider_spec(spec)[0], e))
372
+ return None
373
+
374
+
375
+ async def stream_cloud_fallback(
376
+ message: str,
377
+ history: List[Dict],
378
+ on_token: Optional[Callable] = None,
379
+ cancel_event=None,
380
+ *,
381
+ health: ProviderHealthRegistry | None = None,
382
+ include_defaults: bool = True,
383
+ ) -> Dict[str, Any]:
384
+ """
385
+ CLI fallback 入口:当 Ollama 不可用时调用。
386
+ 按优先级依次尝试云端 provider,首个成功的直接返回。
387
+
388
+ 优先级:
389
+ 1. 用户 ~/.aria/providers.yaml 里的 fallback 列表
390
+ 2. 内置默认链: DeepSeek → OpenAI → Anthropic → Groq → DashScope
391
+ """
392
+ # 构建消息列表
393
+ msgs: List[Message] = [
394
+ Message(role="system", content=(
395
+ "You are Aria, an AI-native product and software-engineering assistant. "
396
+ "Help users review products, understand repositories, design architecture, "
397
+ "write and verify code, and improve quality. Finance is an optional domain "
398
+ "capability used only when the request is explicitly financial. Answer concisely "
399
+ "and never claim to have inspected files or run tests unless tool evidence proves it."
400
+ ))
401
+ ]
402
+ for h in (history or [])[-12:]:
403
+ role = h.get("role", "user")
404
+ if role in ("user", "assistant"):
405
+ msgs.append(Message(role=role, content=h.get("content", "")))
406
+ msgs.append(Message(role="user", content=message))
407
+
408
+ # 加载用户配置的 fallback 链
409
+ user_cfg = _load_user_config()
410
+ user_fallback: List[str] = user_cfg.get("fallback", [])
411
+
412
+ health = health or GLOBAL_PROVIDER_HEALTH
413
+
414
+ # 云端 provider 列表(跳过本地)
415
+ cloud_specs: List[str] = []
416
+ for spec in user_fallback:
417
+ name, _ = _parse_provider_spec(spec)
418
+ cls = _PROVIDER_CLASSES.get(name)
419
+ if cls and not cls.local and not health.provider_in_cooldown(name):
420
+ cloud_specs.append(spec)
421
+
422
+ # Built-in discovery is opt-in. The CLI default only follows the user's
423
+ # explicit fallback list, preventing a selected Ollama model from silently
424
+ # consuming an unrelated provider account.
425
+ if include_defaults:
426
+ for name, env_var, model in _DEFAULT_FALLBACK_CHAIN:
427
+ cls = _PROVIDER_CLASSES.get(name)
428
+ if not cls or cls.local:
429
+ continue
430
+ spec = f"{name}/{model}" if model else name
431
+ if not any(s.startswith(name) for s in cloud_specs):
432
+ # 环境变量 OR providers.json 任一有 key 即可
433
+ has_key = (env_var and os.getenv(env_var)) or \
434
+ bool(_load_provider_cfg_from_file(name).get("api_key"))
435
+ if has_key and not health.provider_in_cooldown(name):
436
+ cloud_specs.append(spec)
437
+
438
+ if not cloud_specs:
439
+ return {
440
+ "success": False,
441
+ "error": "no_cloud_provider",
442
+ "response": "",
443
+ "provider": "none",
444
+ }
445
+
446
+ for spec in cloud_specs:
447
+ result = await _try_provider(spec, msgs, on_token=on_token,
448
+ cancel_event=cancel_event, health=health)
449
+ if result:
450
+ return result
451
+
452
+ return {
453
+ "success": False,
454
+ "error": "all_providers_failed",
455
+ "response": "",
456
+ "provider": "none",
457
+ }
@@ -0,0 +1,202 @@
1
+ """Local, self-hosted image generation — the non-OpenAI alternative to
2
+ openai_image_client.py, same relationship local_llm_provider.py (Ollama) has
3
+ to a cloud chat API: no per-call cost, no API key, runs entirely on this
4
+ machine via the open-weight `diffusers` pipeline.
5
+
6
+ Heavy dependencies (torch, diffusers, transformers, accelerate) are lazy-
7
+ imported and entirely optional — aria-code's core install stays lean, same
8
+ pattern as akshare/ccxt/playwright elsewhere in this codebase. Nothing here
9
+ downloads model weights or installs packages on import; that only happens
10
+ the first time generate_image_local()/edit_image_local() actually runs, and
11
+ raises a clear, actionable error if the extras aren't installed rather than
12
+ failing on a confusing internal import error.
13
+
14
+ Default model is SDXL-Turbo (~7GB, 1-4 step generation) rather than a full
15
+ SDXL/FLUX checkpoint (15-24GB) — chosen for feasibility on 16GB-class
16
+ unified-memory Macs and to leave room on disk, not because it's the best
17
+ possible quality. Override via ARIA_LOCAL_IMAGE_MODEL if you have the RAM
18
+ and disk for something larger.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import os
24
+ from pathlib import Path
25
+ from typing import Any, Dict, Optional
26
+
27
+ DEFAULT_MODEL = os.getenv("ARIA_LOCAL_IMAGE_MODEL", "stabilityai/sdxl-turbo")
28
+
29
+ # SDXL-Turbo is trained around ~1024px; a raw phone photo (commonly 12MP,
30
+ # e.g. 4032x3024) fed straight into img2img allocates latents sized to the
31
+ # INPUT resolution, not the model's native one — confirmed empirically on a
32
+ # 16GB-class Mac: five real 12MP photos through edit_image_local() all threw
33
+ # "MPS backend out of memory" before this cap existed. Downscaling to this
34
+ # budget keeps the long edge near SDXL-Turbo's trained regime regardless of
35
+ # source resolution.
36
+ _MAX_IMG2IMG_DIMENSION = 1024
37
+
38
+ _pipeline_cache: Dict[str, Any] = {}
39
+ _img2img_cache: Dict[str, Any] = {}
40
+
41
+
42
+ def _resize_for_img2img(image, max_dim: int = _MAX_IMG2IMG_DIMENSION):
43
+ """Downscale `image` so its longer edge is at most `max_dim`, rounding
44
+ both dimensions down to a multiple of 8 (required by SDXL's VAE).
45
+ A no-op (aside from the multiple-of-8 rounding) when already small."""
46
+ width, height = image.size
47
+ scale = min(1.0, max_dim / max(width, height))
48
+ new_width = max(8, int(width * scale) // 8 * 8)
49
+ new_height = max(8, int(height * scale) // 8 * 8)
50
+ if (new_width, new_height) == (width, height):
51
+ return image
52
+ from PIL import Image as _PILImage
53
+ return image.resize((new_width, new_height), _PILImage.LANCZOS)
54
+
55
+
56
+ def _select_device() -> str:
57
+ import torch
58
+
59
+ if torch.backends.mps.is_available():
60
+ return "mps"
61
+ if torch.cuda.is_available():
62
+ return "cuda"
63
+ return "cpu"
64
+
65
+
66
+ def _require_diffusers():
67
+ try:
68
+ import torch # noqa: F401
69
+ import diffusers # noqa: F401
70
+ except ImportError as exc:
71
+ raise RuntimeError(
72
+ "Local image generation needs the optional 'image_gen' extra: "
73
+ "pip install -e '.[image_gen]' (adds torch + diffusers, ~a few GB). "
74
+ f"Missing: {exc}"
75
+ ) from exc
76
+
77
+
78
+ def _get_pipeline(model: str):
79
+ if model in _pipeline_cache:
80
+ return _pipeline_cache[model]
81
+ _require_diffusers()
82
+ import torch
83
+ from diffusers import AutoPipelineForText2Image
84
+
85
+ device = _select_device()
86
+ dtype = torch.float16 if device in ("mps", "cuda") else torch.float32
87
+ # variant="fp16" fetches the smaller fp16-suffixed weight files directly
88
+ # (roughly half the download of full fp32 checkpoints) instead of pulling
89
+ # fp32 weights and casting after — matters a lot on a flaky connection.
90
+ variant = "fp16" if dtype is torch.float16 else None
91
+ pipe = AutoPipelineForText2Image.from_pretrained(model, torch_dtype=dtype, variant=variant)
92
+ pipe = pipe.to(device)
93
+ _pipeline_cache[model] = pipe
94
+ return pipe
95
+
96
+
97
+ def _get_img2img_pipeline(model: str):
98
+ if model in _img2img_cache:
99
+ return _img2img_cache[model]
100
+ _require_diffusers()
101
+ import torch
102
+ from diffusers import AutoPipelineForImage2Image
103
+
104
+ device = _select_device()
105
+ dtype = torch.float16 if device in ("mps", "cuda") else torch.float32
106
+ variant = "fp16" if dtype is torch.float16 else None
107
+ pipe = AutoPipelineForImage2Image.from_pretrained(model, torch_dtype=dtype, variant=variant)
108
+ pipe = pipe.to(device)
109
+ _img2img_cache[model] = pipe
110
+ return pipe
111
+
112
+
113
+ def generate_image_local(
114
+ prompt: str,
115
+ *,
116
+ model: str = DEFAULT_MODEL,
117
+ width: int = 1024,
118
+ height: int = 1024,
119
+ steps: int = 4,
120
+ guidance_scale: float = 0.0,
121
+ ) -> Dict[str, Any]:
122
+ """Generate a new image from a text prompt entirely locally.
123
+
124
+ guidance_scale=0.0 matches SDXL-Turbo's distilled few-step training —
125
+ raise it (e.g. 7.5) if you switch `model` to a non-turbo checkpoint that
126
+ expects classifier-free guidance.
127
+
128
+ Blocking, CPU/GPU-bound — callers on an event loop must run this in an
129
+ executor, same as every other client in this codebase. First call for a
130
+ given `model` loads weights from disk (or downloads them once) into
131
+ memory and is much slower than subsequent calls.
132
+ """
133
+ from artifacts import create_user_artifact
134
+
135
+ try:
136
+ pipe = _get_pipeline(model)
137
+ result = pipe(
138
+ prompt=prompt,
139
+ width=width,
140
+ height=height,
141
+ num_inference_steps=steps,
142
+ guidance_scale=guidance_scale,
143
+ )
144
+ image = result.images[0]
145
+ except Exception as exc:
146
+ return {"success": False, "error": str(exc)}
147
+
148
+ artifact = create_user_artifact("image", "generated", "poster_local", ".png")
149
+ image.save(artifact.path)
150
+ return {"success": True, "path": str(artifact.path), "model": model}
151
+
152
+
153
+ def edit_image_local(
154
+ image_path: str,
155
+ prompt: str,
156
+ *,
157
+ model: str = DEFAULT_MODEL,
158
+ strength: float = 0.6,
159
+ steps: int = 4,
160
+ guidance_scale: float = 0.0,
161
+ ) -> Dict[str, Any]:
162
+ """Transform an existing local photo per `prompt`, entirely locally
163
+ (image-to-image, not the mask-based inpainting openai_image_client uses).
164
+
165
+ `strength` (0-1) controls how much the output is allowed to diverge from
166
+ the input — low keeps it close to the original photo, high lets the
167
+ prompt dominate. 0.6 is a reasonable "restyle but keep composition"
168
+ default for the minimal-editorial-poster use case.
169
+ """
170
+ from artifacts import create_user_artifact
171
+
172
+ src = Path(image_path)
173
+ if not src.exists():
174
+ return {"success": False, "error": f"File not found: {image_path}"}
175
+
176
+ try:
177
+ from PIL import Image, ImageOps
178
+
179
+ # exif_transpose is required, not optional: a phone photo shot in
180
+ # portrait commonly stores its pixels landscape plus an EXIF
181
+ # orientation tag telling viewers to rotate it on display — PIL
182
+ # does NOT apply that automatically on open(). Skipping this fed
183
+ # the diffusion pipeline a sideways image; confirmed by reproducing
184
+ # it directly (raw PIL open of a real orientation=6 photo came out
185
+ # sideways, matching the sideways/garbled output this masked).
186
+ init_image = ImageOps.exif_transpose(Image.open(src)).convert("RGB")
187
+ init_image = _resize_for_img2img(init_image)
188
+ pipe = _get_img2img_pipeline(model)
189
+ result = pipe(
190
+ prompt=prompt,
191
+ image=init_image,
192
+ strength=strength,
193
+ num_inference_steps=steps,
194
+ guidance_scale=guidance_scale,
195
+ )
196
+ image = result.images[0]
197
+ except Exception as exc:
198
+ return {"success": False, "error": str(exc)}
199
+
200
+ artifact = create_user_artifact("image", "edited", f"{src.stem}_edited_local", ".png")
201
+ image.save(artifact.path)
202
+ return {"success": True, "path": str(artifact.path), "model": model}