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,592 @@
1
+ """Native Google Cloud Vertex AI LLM Provider using google-genai.
2
+
3
+ google-genai is an optional dependency: most users run Ollama or an
4
+ OpenAI-compatible endpoint and should not have to install a Google SDK. That
5
+ makes the "it is not installed" path a normal one to land on, so it has to say
6
+ what to do rather than leaking a ModuleNotFoundError.
7
+ """
8
+
9
+ import asyncio
10
+ import base64
11
+ import json
12
+ import os
13
+ import re
14
+ from typing import AsyncGenerator, Optional
15
+
16
+ from aria_code.apps.cli.providers.base import (
17
+ LLMDone,
18
+ LLMEvent,
19
+ LLMProvider,
20
+ LLMThinking,
21
+ LLMToken,
22
+ LLMToolCall,
23
+ )
24
+
25
+ _MISSING_SDK_MESSAGE = (
26
+ "Gemini/Vertex AI 需要 google-genai,当前未安装。\n"
27
+ " 安装:pip install google-genai\n"
28
+ " 或改用其他模型:/model (Ollama 本地模型无需额外依赖)"
29
+ )
30
+
31
+ # How runtime.agent_loop.build_tool_followup ends its results text; what
32
+ # follows is guidance, not a copy of the results.
33
+ _TOOL_RESULTS_TRAILERS = (
34
+ "\n\n⚠ Tool(s) returned errors:",
35
+ "\n\nAll tools completed successfully.",
36
+ )
37
+
38
+
39
+ import logging
40
+
41
+ logger = logging.getLogger(__name__)
42
+
43
+ _EMPTY_ROUND_RETRIES = 2
44
+
45
+ # Waits before re-sending a request Vertex turned away as busy: 429
46
+ # RESOURCE_EXHAUSTED (quota or shared capacity) and 500/503/504. The 40-task
47
+ # eval run lost inventory-reorder this way, mid-task, to a single 429 that
48
+ # ended the turn. Retried only while the round has produced nothing, so no
49
+ # text or tool call is ever delivered twice.
50
+ _BUSY_BACKOFF = (2, 5, 10, 20, 40)
51
+ _BUSY_CODES = frozenset({429, 500, 503, 504})
52
+ _BUSY_STATUSES = frozenset({"RESOURCE_EXHAUSTED", "UNAVAILABLE", "INTERNAL", "DEADLINE_EXCEEDED"})
53
+ _sleep = asyncio.sleep
54
+
55
+
56
+ def _busy(error) -> bool:
57
+ """True when Vertex refused the request for load, not for anything in it."""
58
+ return (getattr(error, "code", None) in _BUSY_CODES
59
+ or str(getattr(error, "status", "") or "").upper() in _BUSY_STATUSES)
60
+
61
+
62
+ async def _wait(seconds: float, cancel_event: Optional[asyncio.Event]) -> bool:
63
+ """Sleep up to *seconds*; True if the user cancelled meanwhile."""
64
+ if cancel_event is None:
65
+ await _sleep(seconds)
66
+ return False
67
+ waited = 0.0
68
+ while waited < seconds:
69
+ if cancel_event.is_set():
70
+ return True
71
+ step = min(0.5, seconds - waited)
72
+ await _sleep(step)
73
+ waited += step
74
+ return cancel_event.is_set()
75
+
76
+
77
+ def _chunk_text(chunk) -> str:
78
+ """The text parts of a chunk, read without chunk.text.
79
+
80
+ chunk.text logs "there are non-text parts in the response: ['function_call']"
81
+ for every chunk that carries a call, which filled the eval logs.
82
+ """
83
+ out = []
84
+ for candidate in (getattr(chunk, "candidates", None) or [])[:1]:
85
+ content = getattr(candidate, "content", None)
86
+ for part in (getattr(content, "parts", None) or []):
87
+ text = getattr(part, "text", None)
88
+ if text and not getattr(part, "thought", False):
89
+ out.append(text)
90
+ if out:
91
+ return "".join(out)
92
+ if not getattr(chunk, "candidates", None):
93
+ try:
94
+ return chunk.text or ""
95
+ except Exception:
96
+ return ""
97
+ return ""
98
+
99
+
100
+ def _chunk_calls(chunk) -> list:
101
+ """The function calls in a chunk, with their ids and thought signatures.
102
+
103
+ Signatures are base64 text: the call is recorded in history, and history
104
+ is JSON.
105
+ """
106
+ calls = []
107
+ for candidate in (getattr(chunk, "candidates", None) or [])[:1]:
108
+ content = getattr(candidate, "content", None)
109
+ for part in (getattr(content, "parts", None) or []):
110
+ fc = getattr(part, "function_call", None)
111
+ if not fc:
112
+ continue
113
+ call = {
114
+ "tool": fc.name,
115
+ "params": {k: v for k, v in fc.args.items()} if fc.args else {},
116
+ }
117
+ if getattr(fc, "id", None):
118
+ call["call_id"] = fc.id
119
+ signature = getattr(part, "thought_signature", None)
120
+ if signature:
121
+ call["thought_signature"] = base64.b64encode(signature).decode("ascii")
122
+ calls.append(call)
123
+ if not calls:
124
+ # No call parts to read (a chunk without candidates): take the SDK's
125
+ # own list, which has no signatures.
126
+ for fc in (getattr(chunk, "function_calls", None) or []):
127
+ calls.append({
128
+ "tool": fc.name,
129
+ "params": {k: v for k, v in fc.args.items()} if fc.args else {},
130
+ })
131
+ return calls
132
+
133
+
134
+ class VertexAIProvider(LLMProvider):
135
+ """Native Vertex AI provider using google-genai."""
136
+
137
+ def __init__(
138
+ self,
139
+ model: str,
140
+ config: Optional[dict] = None,
141
+ system_override: Optional[str] = None,
142
+ ):
143
+ self.model = model
144
+ self.config = config or {}
145
+ self.system_override = system_override
146
+ self._client = None
147
+
148
+ def _api_key(self) -> str:
149
+ """Gemini API key from config, falling back to the standard env vars."""
150
+ for value in (
151
+ self.config.get("api_key"),
152
+ self.config.get("gemini_key"),
153
+ os.getenv("GEMINI_API_KEY"),
154
+ os.getenv("GOOGLE_API_KEY"),
155
+ ):
156
+ key = str(value or "").strip()
157
+ if key:
158
+ return key
159
+ return ""
160
+
161
+ def _use_vertex(self) -> bool:
162
+ """Decide between Vertex AI (ADC) and the Gemini API-key endpoint.
163
+
164
+ ``use_vertexai`` used to default to True unconditionally, so a developer
165
+ holding only a GEMINI_API_KEY got ``genai.Client(vertexai=True)`` and a
166
+ credentials error — Vertex needs application-default credentials and a
167
+ project. An explicit config value still wins; otherwise pick whichever
168
+ set of credentials is actually present.
169
+ """
170
+ configured = self.config.get("use_vertexai")
171
+ if configured is not None:
172
+ return bool(configured)
173
+ env_flag = os.getenv("GOOGLE_GENAI_USE_VERTEXAI", "").strip().lower()
174
+ if env_flag in {"1", "true", "yes", "on"}:
175
+ return True
176
+ if env_flag in {"0", "false", "no", "off"}:
177
+ return False
178
+ has_vertex_creds = bool(
179
+ os.getenv("GOOGLE_CLOUD_PROJECT")
180
+ or os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
181
+ )
182
+ if has_vertex_creds:
183
+ return True
184
+ # No project/ADC configured: an API key is the only usable path.
185
+ return not self._api_key()
186
+
187
+ def _get_client(self):
188
+ if self._client is None:
189
+ from google import genai
190
+
191
+ if self._use_vertex():
192
+ project = os.getenv("GOOGLE_CLOUD_PROJECT") or self.config.get("gcp_project")
193
+ # global, not us-central1: the Gemini 3 previews
194
+ # (gemini-3-flash-preview, gemini-3.1-pro-preview) are served
195
+ # only from the global endpoint and 404 in a region, and the
196
+ # global endpoint serves 2.5 as well.
197
+ location = (
198
+ os.getenv("GOOGLE_CLOUD_LOCATION")
199
+ or self.config.get("gcp_location")
200
+ or "global"
201
+ )
202
+ kwargs = {"vertexai": True, "location": location}
203
+ if project:
204
+ kwargs["project"] = str(project)
205
+ self._client = genai.Client(**kwargs)
206
+ else:
207
+ api_key = self._api_key()
208
+ if not api_key:
209
+ raise RuntimeError(
210
+ "Gemini 需要凭据:设置 GEMINI_API_KEY,或配置 Vertex AI "
211
+ "(GOOGLE_CLOUD_PROJECT + gcloud auth application-default login)。"
212
+ )
213
+ self._client = genai.Client(api_key=api_key)
214
+ return self._client
215
+
216
+ def _requires_thought_signatures(self) -> bool:
217
+ """Gemini 3 and later reject a replayed function call without its signature."""
218
+ match = re.search(r"gemini-(\d+)", str(self.model).lower())
219
+ return bool(match) and int(match.group(1)) >= 3
220
+
221
+ @staticmethod
222
+ def _append(contents: list, role: str, parts: list, types) -> None:
223
+ # Gemini requires alternating roles: user, model, user, model.
224
+ if contents and contents[-1].role == role:
225
+ contents[-1].parts.extend(parts)
226
+ else:
227
+ contents.append(types.Content(role=role, parts=parts))
228
+
229
+ @staticmethod
230
+ def _strip_tool_results_text(text: str) -> str:
231
+ """Drop the text copy of tool results from the loop's follow-up message.
232
+
233
+ The follow-up repeats every result as ``## Tool Results ...`` text for
234
+ providers that have no native tool turn. Once the results have gone to
235
+ Gemini as function responses, that copy is not just redundant: it is a
236
+ transcript of tool calls written as prose, and Gemini learns from it to
237
+ write its next calls — and their results — as prose too, which run
238
+ nothing. Only what the loop appends after the results is kept: the
239
+ error / completion guidance and any loop-guard directives.
240
+ """
241
+ cut = max(text.rfind(marker) for marker in _TOOL_RESULTS_TRAILERS)
242
+ return text[cut:].strip() if cut >= 0 else ""
243
+
244
+ def _messages_to_contents(self, messages: list):
245
+ # Convert aria chat messages to genai Content objects
246
+ from google.genai import types
247
+
248
+ system_instruction = self.system_override or ""
249
+ contents = []
250
+ # Calls from the latest native model turn that still need a
251
+ # function_response, as [name, id] pairs.
252
+ unanswered: list = []
253
+ native_results = False
254
+
255
+ def answer_leftovers() -> None:
256
+ # Gemini rejects a turn that leaves any call of the previous model
257
+ # turn unanswered — e.g. when the batch was cut short.
258
+ parts = [
259
+ types.Part(function_response=types.FunctionResponse(
260
+ name=name, id=call_id, response={"error": "not executed"}))
261
+ for name, call_id in unanswered
262
+ ]
263
+ unanswered.clear()
264
+ if parts:
265
+ self._append(contents, "user", parts, types)
266
+
267
+ for msg in messages:
268
+ role = msg.get("role", "user")
269
+ content_str = msg.get("content", "")
270
+
271
+ if role == "system":
272
+ if system_instruction:
273
+ system_instruction += "\n\n" + content_str
274
+ else:
275
+ system_instruction = content_str
276
+ continue
277
+
278
+ genai_role = "user" if role == "user" else "model"
279
+
280
+ if role == "assistant" and msg.get("tool_calls"):
281
+ answer_leftovers()
282
+ parts = self._function_call_parts(msg["tool_calls"], types)
283
+ if parts is not None:
284
+ if str(content_str or "").strip():
285
+ parts.insert(0, types.Part.from_text(text=content_str))
286
+ self._append(contents, "model", parts, types)
287
+ unanswered = [
288
+ [part.function_call.name, part.function_call.id]
289
+ for part in parts
290
+ if part.function_call
291
+ ]
292
+ native_results = False
293
+ continue
294
+ # No usable signatures (history from before they were kept, or
295
+ # from another provider): fall through to the text rendering.
296
+
297
+ if role == "tool" and unanswered:
298
+ tool_name = msg.get("name") or ""
299
+ index = next(
300
+ (i for i, (name, _) in enumerate(unanswered) if name == tool_name),
301
+ 0,
302
+ )
303
+ name, call_id = unanswered.pop(index)
304
+ self._append(contents, "user", [types.Part(
305
+ function_response=types.FunctionResponse(
306
+ name=name, id=call_id, response={"result": str(content_str)}),
307
+ )], types)
308
+ native_results = True
309
+ continue
310
+
311
+ if role == "tool":
312
+ # No native call to answer: rendered as text instead.
313
+ #
314
+ # Gemini only accepts a function_response that answers a
315
+ # function_call it can see in the preceding model turn.
316
+ # Sending an unanswered function_response made the
317
+ # conversation malformed, and Gemini replied with a single
318
+ # whitespace character and no tool call.
319
+ tool_name = msg.get("name") or "tool"
320
+ text = f"[{tool_name}] {content_str}".strip()
321
+ if not text:
322
+ continue
323
+ if contents and contents[-1].role == "user":
324
+ contents[-1].parts.append(types.Part.from_text(text=text))
325
+ else:
326
+ contents.append(types.Content(
327
+ role="user", parts=[types.Part.from_text(text=text)]))
328
+ continue
329
+
330
+ answer_leftovers()
331
+ if (
332
+ native_results
333
+ and role == "user"
334
+ and isinstance(content_str, str)
335
+ and content_str.lstrip().startswith("## Tool Results")
336
+ ):
337
+ content_str = self._strip_tool_results_text(content_str)
338
+ native_results = False
339
+
340
+ # An empty part is worse than no part. When a model answers a turn
341
+ # with nothing but a function call — which Gemini does routinely,
342
+ # and which the agent loop records as an assistant message whose
343
+ # text is "" — this used to send Content(role="model", parts=[""]).
344
+ # Gemini responds to that with a single whitespace character and no
345
+ # tool call, so the second round of every tool-using turn came back
346
+ # as "empty_response" and the task died after one step.
347
+ if not str(content_str or "").strip():
348
+ continue
349
+
350
+ # Check if previous message has same role
351
+ # (Gemini requires alternating roles: user, model, user, model)
352
+ if contents and contents[-1].role == genai_role:
353
+ contents[-1].parts.append(types.Part.from_text(text=content_str))
354
+ else:
355
+ contents.append(types.Content(role=genai_role, parts=[types.Part.from_text(text=content_str)]))
356
+
357
+ # Gemini refuses a request whose last turn is the model's ("Requests
358
+ # ending with a model turn are not supported"). That happens when the
359
+ # history ends on a tool call that was never answered, or when the
360
+ # message that would have followed it was empty and skipped above.
361
+ answer_leftovers()
362
+ if contents and contents[-1].role == "model":
363
+ contents.append(types.Content(role="user", parts=[types.Part.from_text(text="Continue.")]))
364
+ return contents, system_instruction
365
+
366
+ def _function_call_parts(self, tool_calls: list, types) -> Optional[list]:
367
+ """The assistant's recorded calls as function_call parts.
368
+
369
+ None when they cannot be replayed natively: Gemini 3 rejects a call
370
+ replayed without the thought signature it was issued with, and calls
371
+ recorded before signatures were kept (or by another provider) have none.
372
+ """
373
+ parts = []
374
+ signed = False
375
+ for tool_call in tool_calls:
376
+ fn = tool_call.get("function", tool_call)
377
+ name = str(fn.get("name") or "")
378
+ if not name:
379
+ continue
380
+ args = fn.get("arguments", {})
381
+ if isinstance(args, str):
382
+ try:
383
+ args = json.loads(args)
384
+ except json.JSONDecodeError:
385
+ args = {}
386
+ part = types.Part(function_call=types.FunctionCall(
387
+ name=name, args=dict(args or {}), id=tool_call.get("id") or None))
388
+ signature = tool_call.get("thought_signature")
389
+ if signature:
390
+ part.thought_signature = base64.b64decode(signature)
391
+ signed = True
392
+ parts.append(part)
393
+ if not parts or (self._requires_thought_signatures() and not signed):
394
+ return None
395
+ return parts
396
+
397
+ def _schema_from_dict(self, d: dict, types):
398
+ if not d:
399
+ return None
400
+ t = d.get("type", "string").upper()
401
+ if t == "ARRAY":
402
+ items = d.get("items", {})
403
+ return types.Schema(
404
+ type="ARRAY",
405
+ description=d.get("description", ""),
406
+ items=self._schema_from_dict(items, types) if items else types.Schema(type="STRING")
407
+ )
408
+ elif t == "OBJECT":
409
+ props = d.get("properties", {})
410
+ req = d.get("required", [])
411
+ schema_props = {k: self._schema_from_dict(v, types) for k, v in props.items()}
412
+ return types.Schema(
413
+ type="OBJECT",
414
+ description=d.get("description", ""),
415
+ properties=schema_props if schema_props else None,
416
+ required=req if req else None
417
+ )
418
+ else:
419
+ return types.Schema(
420
+ type=t,
421
+ description=d.get("description", "")
422
+ )
423
+
424
+ def _tools_to_genai(self, tools: list):
425
+ if not tools:
426
+ return None
427
+ from google.genai import types
428
+ genai_tools = []
429
+ # Vertex rejects the entire request when two declarations share a name
430
+ # ("Duplicate function declaration found: web_fetch"), where
431
+ # OpenAI-compatible backends just take the last one. The registries
432
+ # upstream should not produce duplicates, but this is the boundary
433
+ # where a duplicate becomes a hard 400 for the whole turn, so it is
434
+ # also the boundary that has to be certain.
435
+ seen: set = set()
436
+ for tool in tools:
437
+ func = tool.get("function", tool)
438
+ name = func.get("name")
439
+ if not name or name in seen:
440
+ continue
441
+ seen.add(name)
442
+ desc = func.get("description", "")
443
+
444
+ # Map parameters recursively
445
+ params = func.get("parameters", {})
446
+ schema = self._schema_from_dict(params, types) if params else None
447
+
448
+ tool_declaration = types.FunctionDeclaration(
449
+ name=name,
450
+ description=desc,
451
+ parameters=schema
452
+ )
453
+ genai_tools.append(types.Tool(function_declarations=[tool_declaration]))
454
+
455
+ return genai_tools
456
+
457
+ async def stream(
458
+ self,
459
+ messages: list,
460
+ tools: list,
461
+ *,
462
+ cancel_event: Optional[asyncio.Event] = None,
463
+ ) -> AsyncGenerator[LLMEvent, None]:
464
+ # These imports must sit INSIDE the try. They were above it, so when
465
+ # google-genai was not installed they raised first and the handler
466
+ # below — the one that explains how to fix it — was unreachable. The
467
+ # user saw a bare "No module named 'google.genai'" and no way forward.
468
+ try:
469
+ from google.genai import types
470
+ from google.genai.errors import APIError
471
+
472
+ client = self._get_client()
473
+ except ImportError:
474
+ yield LLMDone(
475
+ response="", provider="vertexai", success=False,
476
+ error=_MISSING_SDK_MESSAGE,
477
+ )
478
+ return
479
+ except Exception as e:
480
+ yield LLMDone(response="", provider="vertexai", success=False, error=str(e))
481
+ return
482
+
483
+ contents, system_instruction = self._messages_to_contents(messages)
484
+ genai_tools = self._tools_to_genai(tools)
485
+
486
+ config = types.GenerateContentConfig(
487
+ system_instruction=system_instruction if system_instruction else None,
488
+ tools=genai_tools if genai_tools else None,
489
+ temperature=self.config.get("temperature", 0.7),
490
+ )
491
+
492
+ try:
493
+ # Gemini sometimes ends a round with no text and no function call:
494
+ # finish_reason MALFORMED_FUNCTION_CALL (it wrote a call it could
495
+ # not emit), or an empty STOP after reading tool results. The round
496
+ # then came back empty, the turn ended as "empty_response", and
497
+ # `aria-code -p` exited 1 a few seconds in: three of nine eval tasks
498
+ # on the first Vertex runs, different ones each time. The same
499
+ # request usually succeeds when sent again, so it is, up to twice,
500
+ # and if it is still empty the finish reason goes into the error.
501
+ full_response = ""
502
+ usage = {}
503
+ tool_calls = []
504
+ finish_reasons: list = []
505
+ busy_tries = 0
506
+ for attempt in range(1 + _EMPTY_ROUND_RETRIES):
507
+ # The SDK sends the request when the stream is first read, so
508
+ # a 429 surfaces inside the loop below, not at the call. A busy
509
+ # refusal is not an empty round: it has its own budget and does
510
+ # not use up one of those retries.
511
+ while True:
512
+ try:
513
+ response_stream = await client.aio.models.generate_content_stream(
514
+ model=self.model,
515
+ contents=contents,
516
+ config=config,
517
+ )
518
+ async for chunk in response_stream:
519
+ if cancel_event and cancel_event.is_set():
520
+ yield LLMDone(response=full_response, provider="vertexai", success=True, cancelled=True)
521
+ return
522
+
523
+ text = _chunk_text(chunk)
524
+ if text:
525
+ full_response += text
526
+ yield LLMToken(text=text)
527
+
528
+ # Read calls from the parts, not chunk.function_calls:
529
+ # the thought signature lives on the part, and Gemini 3
530
+ # refuses the next request if the call is replayed
531
+ # without it.
532
+ for call in _chunk_calls(chunk):
533
+ yield LLMToolCall(
534
+ tool=call["tool"], params=call["params"],
535
+ call_id=call.get("call_id"),
536
+ thought_signature=call.get("thought_signature"),
537
+ )
538
+ tool_calls.append(call)
539
+
540
+ for candidate in (getattr(chunk, "candidates", None) or []):
541
+ reason = getattr(candidate, "finish_reason", None)
542
+ if reason:
543
+ finish_reasons.append(str(getattr(reason, "name", reason)))
544
+
545
+ if chunk.usage_metadata:
546
+ usage = {
547
+ "prompt_tokens": chunk.usage_metadata.prompt_token_count,
548
+ "completion_tokens": chunk.usage_metadata.candidates_token_count,
549
+ }
550
+ break
551
+ except APIError as e:
552
+ if full_response or tool_calls or not _busy(e) or busy_tries >= len(_BUSY_BACKOFF):
553
+ raise
554
+ delay = _BUSY_BACKOFF[busy_tries]
555
+ busy_tries += 1
556
+ finish_reasons = []
557
+ logger.info("Vertex is busy (%s %s); retrying in %ss (%d/%d)",
558
+ getattr(e, "code", ""), getattr(e, "status", ""),
559
+ delay, busy_tries, len(_BUSY_BACKOFF))
560
+ if await _wait(delay, cancel_event):
561
+ yield LLMDone(response="", provider="vertexai", success=True, cancelled=True)
562
+ return
563
+ if full_response.strip() or tool_calls:
564
+ break
565
+ logger.info("Vertex returned an empty round (finish_reason %s), attempt %d",
566
+ finish_reasons[-1:] or "none", attempt + 1)
567
+ if attempt < _EMPTY_ROUND_RETRIES:
568
+ finish_reasons = []
569
+
570
+ if not full_response.strip() and not tool_calls:
571
+ reason = finish_reasons[-1] if finish_reasons else "none"
572
+ yield LLMDone(
573
+ response="", provider="vertexai", success=False,
574
+ error=f"empty_response (Vertex finish_reason {reason} after "
575
+ f"{1 + _EMPTY_ROUND_RETRIES} attempts)",
576
+ )
577
+ return
578
+
579
+ yield LLMDone(
580
+ response=full_response,
581
+ tool_calls_pending=tool_calls,
582
+ usage=usage,
583
+ provider="vertexai",
584
+ success=True,
585
+ cancelled=False,
586
+ )
587
+
588
+ except APIError as e:
589
+ yield LLMDone(response="", provider="vertexai", success=False, error=f"Vertex AI API Error: {e.message}")
590
+ except Exception as e:
591
+ yield LLMDone(response="", provider="vertexai", success=False, error=f"Vertex AI Error: {str(e)}")
592
+