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.
- aria_bots/__init__.py +7 -0
- aria_bots/channels/README.md +97 -0
- aria_bots/channels/__init__.py +1 -0
- aria_bots/channels/approvals.py +263 -0
- aria_bots/channels/conversation.py +280 -0
- aria_bots/channels/digest.py +186 -0
- aria_bots/channels/registry.py +95 -0
- aria_bots/engine.py +144 -0
- aria_bots/feishu_bot.py +1385 -0
- aria_bots/logistics/__init__.py +1 -0
- aria_bots/logistics/carriers.py +253 -0
- aria_bots/logistics/freight.py +154 -0
- aria_bots/logistics/inventory.py +242 -0
- aria_bots/logistics/mcp_server.py +156 -0
- aria_bots/logistics/records.py +119 -0
- aria_bots/logistics/tenancy.py +152 -0
- aria_bots/market.py +91 -0
- aria_bots/relay_client.py +326 -0
- aria_bots/telegram_bot.py +299 -0
- aria_code/__init__.py +111 -0
- aria_code/_bin/aria-native.exe +0 -0
- aria_code/_bin/aria-runtime.exe +0 -0
- aria_code/_version.py +20 -0
- aria_code/account_memory.py +296 -0
- aria_code/account_session.py +166 -0
- aria_code/adk_apps/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/agent.py +32 -0
- aria_code/adk_apps/aria_research/__init__.py +1 -0
- aria_code/adk_apps/aria_research/agent.py +31 -0
- aria_code/agents/__init__.py +49 -0
- aria_code/agents/base.py +329 -0
- aria_code/agents/code_review.py +200 -0
- aria_code/agents/deep/__init__.py +37 -0
- aria_code/agents/deep/calibration_loop.py +144 -0
- aria_code/agents/deep/critic.py +125 -0
- aria_code/agents/deep/deepen.py +193 -0
- aria_code/agents/deep/models.py +149 -0
- aria_code/agents/deep/pipeline.py +164 -0
- aria_code/agents/deep/quant_fusion.py +192 -0
- aria_code/agents/deep/themes.py +95 -0
- aria_code/agents/deep/tiers.py +106 -0
- aria_code/agents/engineering/__init__.py +14 -0
- aria_code/agents/engineering/coder.py +222 -0
- aria_code/agents/engineering/tester.py +145 -0
- aria_code/agents/financial/__init__.py +21 -0
- aria_code/agents/financial/cashflow_burn.py +119 -0
- aria_code/agents/financial/catalyst.py +279 -0
- aria_code/agents/financial/corporate_finance.py +175 -0
- aria_code/agents/financial/debate.py +224 -0
- aria_code/agents/financial/earnings.py +315 -0
- aria_code/agents/financial/fundamental.py +178 -0
- aria_code/agents/financial/macro.py +111 -0
- aria_code/agents/financial/news.py +231 -0
- aria_code/agents/financial/risk.py +146 -0
- aria_code/agents/financial/sector.py +279 -0
- aria_code/agents/financial/strategist.py +121 -0
- aria_code/agents/financial/stripe_revenue.py +127 -0
- aria_code/agents/financial/synthesis.py +274 -0
- aria_code/agents/financial/technical.py +277 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +392 -0
- aria_code/agents/realty/__init__.py +62 -0
- aria_code/agents/realty/asset_diagnosis.py +152 -0
- aria_code/agents/realty/business_match.py +166 -0
- aria_code/agents/realty/cashflow_verify.py +209 -0
- aria_code/agents/realty/contract_rules.py +210 -0
- aria_code/agents/realty/energy_anomaly.py +189 -0
- aria_code/agents/realty/exit_settlement.py +208 -0
- aria_code/agents/realty/fulfillment_risk.py +206 -0
- aria_code/agents/realty/ops_optimize.py +160 -0
- aria_code/agents/realty/revenue_share.py +215 -0
- aria_code/agents/registry.py +168 -0
- aria_code/agents/signal_scheme.py +111 -0
- aria_code/agents/sports/__init__.py +0 -0
- aria_code/agents/sports/football_agent.py +197 -0
- aria_code/agents/supervisor.py +113 -0
- aria_code/agents/team.py +500 -0
- aria_code/agents/video/__init__.py +16 -0
- aria_code/agents/video/director.py +376 -0
- aria_code/agents/video/qa_agent.py +143 -0
- aria_code/agents/video/script_doctor.py +112 -0
- aria_code/agents/warehouse/__init__.py +25 -0
- aria_code/agents/warehouse/contracts.py +26 -0
- aria_code/agents/warehouse/dashboard.py +155 -0
- aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
- aria_code/agents/warehouse/inbound_exceptions.py +25 -0
- aria_code/agents/warehouse/inventory_health.py +24 -0
- aria_code/agents/warehouse/local_run.py +71 -0
- aria_code/agents/warehouse/local_snapshot.py +132 -0
- aria_code/agents/warehouse/logistics_cost.py +84 -0
- aria_code/agents/warehouse/logistics_sync.py +21 -0
- aria_code/agents/warehouse/navigation.schema.json +110 -0
- aria_code/agents/warehouse/task_agent.py +172 -0
- aria_code/agents/warehouse/workflow.py +31 -0
- aria_code/aliyun_data_client.py +4 -0
- aria_code/apps/README.md +12 -0
- aria_code/apps/__init__.py +2 -0
- aria_code/apps/channels/README.md +9 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/approvals.py +11 -0
- aria_code/apps/channels/conversation.py +11 -0
- aria_code/apps/channels/digest.py +11 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +11 -0
- aria_code/apps/channels/tradingview.py +94 -0
- aria_code/apps/cli/README.md +13 -0
- aria_code/apps/cli/__init__.py +2 -0
- aria_code/apps/cli/_optional.py +120 -0
- aria_code/apps/cli/app_server.py +656 -0
- aria_code/apps/cli/auto_update.py +125 -0
- aria_code/apps/cli/bootstrap.py +206 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/chat_turn.py +1327 -0
- aria_code/apps/cli/cloud_models.py +231 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +34 -0
- aria_code/apps/cli/commands/_business_render.py +78 -0
- aria_code/apps/cli/commands/_ui.py +71 -0
- aria_code/apps/cli/commands/analysis_cmds.py +378 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +215 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
- aria_code/apps/cli/commands/broker_cmds.py +1236 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +189 -0
- aria_code/apps/cli/commands/core_cmds.py +2748 -0
- aria_code/apps/cli/commands/data_cmds.py +521 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/engineering_cmds.py +399 -0
- aria_code/apps/cli/commands/file_cmds.py +312 -0
- aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
- aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
- aria_code/apps/cli/commands/market.py +544 -0
- aria_code/apps/cli/commands/market_cmds.py +1419 -0
- aria_code/apps/cli/commands/market_context.py +432 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2374 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
- aria_code/apps/cli/commands/report.py +476 -0
- aria_code/apps/cli/commands/scaffold_templates.py +617 -0
- aria_code/apps/cli/commands/session_cmds.py +236 -0
- aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
- aria_code/apps/cli/commands/team.py +723 -0
- aria_code/apps/cli/commands/team_render.py +8 -0
- aria_code/apps/cli/commands/ui_cmds.py +401 -0
- aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
- aria_code/apps/cli/commands/workflow_cmds.py +629 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +158 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/conversation_policy.py +53 -0
- aria_code/apps/cli/deterministic.py +200 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/earnings_view.py +172 -0
- aria_code/apps/cli/endpoint_migration.py +64 -0
- aria_code/apps/cli/exec_events.py +255 -0
- aria_code/apps/cli/file_references.py +155 -0
- aria_code/apps/cli/finance_service_catalog.py +111 -0
- aria_code/apps/cli/football_reports.py +231 -0
- aria_code/apps/cli/github_app_auth.py +135 -0
- aria_code/apps/cli/google_login.py +239 -0
- aria_code/apps/cli/handlers/__init__.py +11 -0
- aria_code/apps/cli/handlers/broker_handlers.py +122 -0
- aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
- aria_code/apps/cli/handlers/market_handlers.py +2570 -0
- aria_code/apps/cli/handlers/realty_handlers.py +114 -0
- aria_code/apps/cli/handlers/sports_handlers.py +22 -0
- aria_code/apps/cli/handlers/strategy_advice.py +82 -0
- aria_code/apps/cli/headless.py +352 -0
- aria_code/apps/cli/helpers.py +326 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +310 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +305 -0
- aria_code/apps/cli/intent_shadow.py +72 -0
- aria_code/apps/cli/intent_signals.py +347 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +550 -0
- aria_code/apps/cli/main.py +39 -0
- aria_code/apps/cli/market_hours.py +100 -0
- aria_code/apps/cli/market_metadata.py +135 -0
- aria_code/apps/cli/market_universe.py +487 -0
- aria_code/apps/cli/message_processing.py +310 -0
- aria_code/apps/cli/mixin_binding.py +32 -0
- aria_code/apps/cli/model_catalog.py +652 -0
- aria_code/apps/cli/model_probe.py +169 -0
- aria_code/apps/cli/model_retirement.py +65 -0
- aria_code/apps/cli/native_branding.py +12 -0
- aria_code/apps/cli/native_bridge.py +118 -0
- aria_code/apps/cli/native_entry.py +58 -0
- aria_code/apps/cli/pdf_report.py +977 -0
- aria_code/apps/cli/plan_mode.py +139 -0
- aria_code/apps/cli/plotly_html.py +15 -0
- aria_code/apps/cli/prediction_feedback.py +202 -0
- aria_code/apps/cli/preflight.py +502 -0
- aria_code/apps/cli/project_aria.py +60 -0
- aria_code/apps/cli/prompt_assembly.py +78 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +71 -0
- aria_code/apps/cli/prompts/select.py +193 -0
- aria_code/apps/cli/prompts/system_prompts.py +562 -0
- aria_code/apps/cli/prompts/ui.py +436 -0
- aria_code/apps/cli/provider_endpoints.py +237 -0
- aria_code/apps/cli/provider_keys.py +73 -0
- aria_code/apps/cli/providers/__init__.py +1 -0
- aria_code/apps/cli/providers/base.py +775 -0
- aria_code/apps/cli/providers/chat_routing.py +139 -0
- aria_code/apps/cli/providers/collaboration.py +98 -0
- aria_code/apps/cli/providers/llm/__init__.py +1 -0
- aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
- aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
- aria_code/apps/cli/providers/runtime_bridge.py +586 -0
- aria_code/apps/cli/providers/vertexai_stream.py +592 -0
- aria_code/apps/cli/release_download.py +159 -0
- aria_code/apps/cli/review_runner.py +111 -0
- aria_code/apps/cli/runtime_consumer.py +769 -0
- aria_code/apps/cli/sandbox.py +32 -0
- aria_code/apps/cli/session_export.py +120 -0
- aria_code/apps/cli/session_jsonl.py +208 -0
- aria_code/apps/cli/session_refresh.py +123 -0
- aria_code/apps/cli/session_store.py +125 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/task_isolation.py +211 -0
- aria_code/apps/cli/terminal_chrome.py +412 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +932 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +113 -0
- aria_code/apps/cli/tools/__init__.py +40 -0
- aria_code/apps/cli/tools/context.py +46 -0
- aria_code/apps/cli/tools/file_tools.py +181 -0
- aria_code/apps/cli/tools/market_tools.py +626 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +656 -0
- aria_code/apps/cli/tools/write_tools.py +961 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/transactions.py +269 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +257 -0
- aria_code/apps/cli/updater.py +373 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1663 -0
- aria_code/apps/cli/web_login.py +136 -0
- aria_code/apps/cli/workflow_runner.py +127 -0
- aria_code/apps/cli/workspace_route.py +121 -0
- aria_code/apps/daemon/README.md +14 -0
- aria_code/apps/vscode/README.md +115 -0
- aria_code/apps/vscode/package.json +70 -0
- aria_code/aria_cli.py +5244 -0
- aria_code/aria_daemon.py +1372 -0
- aria_code/aria_feishu_bot.py +16 -0
- aria_code/aria_mcp_server.py +61 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_telegram_bot.py +11 -0
- aria_code/ariarc.py +385 -0
- aria_code/artifacts.py +581 -0
- aria_code/backtest_engine.py +4 -0
- aria_code/backtest_report.py +4 -0
- aria_code/brokers/__init__.py +78 -0
- aria_code/brokers/automation.py +193 -0
- aria_code/brokers/base.py +231 -0
- aria_code/brokers/capabilities.py +264 -0
- aria_code/brokers/cn/__init__.py +10 -0
- aria_code/brokers/cn/easytrader_broker.py +193 -0
- aria_code/brokers/cn/futu_broker.py +194 -0
- aria_code/brokers/cn/longbridge_broker.py +190 -0
- aria_code/brokers/cn/tiger_broker.py +196 -0
- aria_code/brokers/cn/xtquant_broker.py +225 -0
- aria_code/brokers/config.py +414 -0
- aria_code/brokers/intl/__init__.py +5 -0
- aria_code/brokers/intl/alpaca_broker.py +183 -0
- aria_code/brokers/intl/ibkr_broker.py +215 -0
- aria_code/brokers/intl/trading212_broker.py +183 -0
- aria_code/brokers/intl/webull_broker.py +156 -0
- aria_code/brokers/paper_broker.py +266 -0
- aria_code/brokers/planning.py +296 -0
- aria_code/brokers/registry.py +213 -0
- aria_code/brokers/trading.py +267 -0
- aria_code/canva_client.py +4 -0
- aria_code/change_store.py +127 -0
- aria_code/clients/__init__.py +0 -0
- aria_code/clients/aliyun_data_client.py +660 -0
- aria_code/clients/aria_relay_client.py +16 -0
- aria_code/clients/canva_client.py +284 -0
- aria_code/clients/figma_client.py +100 -0
- aria_code/clients/football_data_client.py +1671 -0
- aria_code/clients/kling_video_client.py +182 -0
- aria_code/clients/market_data_client.py +1985 -0
- aria_code/clients/openai_image_client.py +242 -0
- aria_code/clients/runway_video_client.py +157 -0
- aria_code/clients/warehouse_erp_client.py +105 -0
- aria_code/cloud_memory.py +190 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_server.py +276 -0
- aria_code/computer_use_tools.py +4 -0
- aria_code/dashboard_generator.py +579 -0
- aria_code/data_analysis_tools.py +4 -0
- aria_code/data_cleaner.py +483 -0
- aria_code/data_service.py +513 -0
- aria_code/datasources/__init__.py +23 -0
- aria_code/datasources/base.py +166 -0
- aria_code/datasources/router.py +253 -0
- aria_code/datasources/sec_statements.py +217 -0
- aria_code/datasources/sources/__init__.py +15 -0
- aria_code/datasources/sources/akshare_source.py +269 -0
- aria_code/datasources/sources/alpha_vantage_source.py +203 -0
- aria_code/datasources/sources/edgar_source.py +219 -0
- aria_code/datasources/sources/finnhub_source.py +198 -0
- aria_code/datasources/sources/fred_source.py +223 -0
- aria_code/datasources/sources/tushare_source.py +143 -0
- aria_code/datasources/sources/web_scraper_source.py +278 -0
- aria_code/datasources/sources/world_bank_source.py +206 -0
- aria_code/datasources/sources/yfinance_source.py +152 -0
- aria_code/doctor.py +860 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/_finance_core/__init__.py +5 -0
- aria_code/domain/_finance_core/backtest_engine.py +325 -0
- aria_code/domain/_finance_core/performance_stats.py +101 -0
- aria_code/domain/backtest_engine.py +75 -0
- aria_code/domain/backtest_report.py +488 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/performance_stats.py +6 -0
- aria_code/domain/portfolio_ledger.py +307 -0
- aria_code/domain/strategy_vault.py +421 -0
- aria_code/evals/__init__.py +37 -0
- aria_code/evals/behavior.py +113 -0
- aria_code/evals/gap.py +93 -0
- aria_code/evals/harness.py +949 -0
- aria_code/evals/review_probe.py +132 -0
- aria_code/evals/runner.py +425 -0
- aria_code/evals/scorecard.py +322 -0
- aria_code/evals/summary.py +125 -0
- aria_code/external_agent_runner.py +115 -0
- aria_code/figma_client.py +4 -0
- aria_code/file_analysis_tools.py +4 -0
- aria_code/finance_formulas.py +4 -0
- aria_code/football_data_client.py +4 -0
- aria_code/image_gen_tools.py +4 -0
- aria_code/intent_classifier.py +459 -0
- aria_code/kling_video_client.py +4 -0
- aria_code/licensing.py +151 -0
- aria_code/local_finance_tools.py +4 -0
- aria_code/local_image_provider.py +4 -0
- aria_code/local_llm_provider.py +4 -0
- aria_code/macro_tools.py +4 -0
- aria_code/markdown_pdf.py +289 -0
- aria_code/market_data_client.py +4 -0
- aria_code/mcp_client.py +744 -0
- aria_code/memory_manager.py +273 -0
- aria_code/model_capability.py +512 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +26 -0
- aria_code/packages/adk_bridge/__init__.py +11 -0
- aria_code/packages/adk_bridge/code_review_tools.py +49 -0
- aria_code/packages/adk_bridge/market_tools.py +125 -0
- aria_code/packages/aria_agents/__init__.py +5 -0
- aria_code/packages/aria_agents/manifest.py +78 -0
- aria_code/packages/aria_core/__init__.py +34 -0
- aria_code/packages/aria_core/architecture.py +269 -0
- aria_code/packages/aria_core/export.py +147 -0
- aria_code/packages/aria_core/manifest.py +65 -0
- aria_code/packages/aria_core/paths.py +57 -0
- aria_code/packages/aria_core/secure_file.py +68 -0
- aria_code/packages/aria_infra/__init__.py +15 -0
- aria_code/packages/aria_infra/arthera.py +58 -0
- aria_code/packages/aria_infra/doctor.py +246 -0
- aria_code/packages/aria_infra/product.py +40 -0
- aria_code/packages/aria_mcp/__init__.py +25 -0
- aria_code/packages/aria_mcp/bridge.py +232 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +162 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1705 -0
- aria_code/packages/aria_mcp/tools.py +81 -0
- aria_code/packages/aria_sdk/__init__.py +19 -0
- aria_code/packages/aria_sdk/client.py +393 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +75 -0
- aria_code/packages/aria_sdk/types.py +86 -0
- aria_code/packages/aria_services/__init__.py +92 -0
- aria_code/packages/aria_services/cache.py +60 -0
- aria_code/packages/aria_services/context.py +356 -0
- aria_code/packages/aria_services/context_checkpoints.py +157 -0
- aria_code/packages/aria_services/data.py +11 -0
- aria_code/packages/aria_services/provider_health.py +304 -0
- aria_code/packages/aria_services/references.py +247 -0
- aria_code/packages/aria_services/registry.py +249 -0
- aria_code/packages/aria_services/research_protocol.py +137 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +160 -0
- aria_code/packages/aria_services/usage.py +151 -0
- aria_code/packages/aria_skills/__init__.py +46 -0
- aria_code/packages/aria_skills/catalog.py +149 -0
- aria_code/packages/aria_skills/loader.py +455 -0
- aria_code/packages/aria_skills/registry.py +84 -0
- aria_code/packages/aria_tools/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/_paths.py +92 -0
- aria_code/packages/aria_tools/registry.py +129 -0
- aria_code/packages/quant_engine/__init__.py +33 -0
- aria_code/packages/quant_engine/_namespace.py +24 -0
- aria_code/packages/quant_engine/backtest/__init__.py +10 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +102 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +76 -0
- aria_code/packages/quant_engine/sports/calibrator.py +354 -0
- aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
- aria_code/packages/quant_engine/sports/elo.py +300 -0
- aria_code/packages/quant_engine/sports/form.py +188 -0
- aria_code/packages/quant_engine/sports/h2h.py +195 -0
- aria_code/packages/quant_engine/sports/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
- aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
- aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
- aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- aria_code/packs/__init__.py +85 -0
- aria_code/packs/base.py +168 -0
- aria_code/packs/finance/__init__.py +250 -0
- aria_code/packs/healthcare/__init__.py +40 -0
- aria_code/packs/logistics/__init__.py +171 -0
- aria_code/packs/payments/__init__.py +139 -0
- aria_code/packs/realty/__init__.py +190 -0
- aria_code/packs/registry.py +202 -0
- aria_code/packs/rules.py +90 -0
- aria_code/plan_utils.py +194 -0
- aria_code/plugin_loader.py +328 -0
- aria_code/portfolio_ledger.py +4 -0
- aria_code/preview_server.py +449 -0
- aria_code/privacy/__init__.py +5 -0
- aria_code/privacy/feedback.py +123 -0
- aria_code/project_review.py +278 -0
- aria_code/project_review_api.py +235 -0
- aria_code/project_review_server.py +18 -0
- aria_code/project_tools.py +4 -0
- aria_code/providers/__init__.py +30 -0
- aria_code/providers/google_video_provider.py +256 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/arthera_gateway.py +90 -0
- aria_code/providers/llm/autoconfig.py +213 -0
- aria_code/providers/llm/base.py +139 -0
- aria_code/providers/llm/custom.py +158 -0
- aria_code/providers/llm/ollama.py +137 -0
- aria_code/providers/llm/openai_compat.py +350 -0
- aria_code/providers/llm/registry.py +457 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +549 -0
- aria_code/providers/ollama_models.py +79 -0
- aria_code/providers/openai_params.py +32 -0
- aria_code/providers/tool_messages.py +100 -0
- aria_code/realty_data_tools.py +4 -0
- aria_code/report_exporters.py +150 -0
- aria_code/report_generator.py +1764 -0
- aria_code/review_service.py +497 -0
- aria_code/runtime/__init__.py +176 -0
- aria_code/runtime/acceptance.py +632 -0
- aria_code/runtime/agent_loop.py +1851 -0
- aria_code/runtime/agent_profiles.py +75 -0
- aria_code/runtime/approval.py +71 -0
- aria_code/runtime/baseline.py +219 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +481 -0
- aria_code/runtime/consult.py +65 -0
- aria_code/runtime/contract.py +282 -0
- aria_code/runtime/delivery.py +432 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +167 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/native_index.py +153 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/processes.py +343 -0
- aria_code/runtime/project_graph.py +614 -0
- aria_code/runtime/repo_map.py +712 -0
- aria_code/runtime/research_bundle.py +105 -0
- aria_code/runtime/review.py +269 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +522 -0
- aria_code/runtime/scoped_runs.py +113 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/semantic_diff.py +171 -0
- aria_code/runtime/subagent.py +721 -0
- aria_code/runtime/symbol_edit.py +117 -0
- aria_code/runtime/task_brief.py +46 -0
- aria_code/runtime/task_context.py +145 -0
- aria_code/runtime/task_ledger.py +108 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/task_outcome.py +33 -0
- aria_code/runtime/task_verification.py +266 -0
- aria_code/runtime/task_worktree.py +448 -0
- aria_code/runtime/tool_executor.py +329 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/transactions.py +216 -0
- aria_code/runtime/windows_job.py +115 -0
- aria_code/runtime/workflows.py +222 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +27 -0
- aria_code/safety/client_isolation.py +178 -0
- aria_code/safety/permissions.py +339 -0
- aria_code/safety/risk.py +699 -0
- aria_code/safety/sandbox.py +148 -0
- aria_code/safety/service.py +102 -0
- aria_code/service_endpoints.py +14 -0
- aria_code/setup_wizard.py +684 -0
- aria_code/spreadsheet_tools.py +4 -0
- aria_code/strategy_vault.py +4 -0
- aria_code/tools/__init__.py +0 -0
- aria_code/tools/artifact_tools.py +252 -0
- aria_code/tools/broker_tools.py +92 -0
- aria_code/tools/build_quant_engine.py +97 -0
- aria_code/tools/code_audit_tools.py +192 -0
- aria_code/tools/computer_use_tools.py +509 -0
- aria_code/tools/data_analysis_tools.py +831 -0
- aria_code/tools/enterprise_finance_tools.py +202 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +880 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3488 -0
- aria_code/tools/logistics_carriers.py +11 -0
- aria_code/tools/logistics_inventory.py +11 -0
- aria_code/tools/logistics_io.py +11 -0
- aria_code/tools/logistics_tenancy.py +11 -0
- aria_code/tools/logistics_tools.py +11 -0
- aria_code/tools/macro_tools.py +412 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +662 -0
- aria_code/tools/spreadsheet_tools.py +399 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/tools/video_tools.py +500 -0
- aria_code/training/__init__.py +1 -0
- aria_code/training/compare.py +117 -0
- aria_code/training/prelude_commands.py +111 -0
- aria_code/training/prelude_eval.py +390 -0
- aria_code/training/prelude_real.py +183 -0
- aria_code/training/prelude_train.py +248 -0
- aria_code/training/sft_dataset.py +230 -0
- aria_code/training/trajectory_dataset.py +263 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +404 -0
- aria_code/ui/completer.py +571 -0
- aria_code/ui/console.py +285 -0
- aria_code/ui/input_box.py +505 -0
- aria_code/ui/mentions.py +206 -0
- aria_code/ui/picker.py +248 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/actions.py +297 -0
- aria_code/ui/render/finance.py +1732 -0
- aria_code/ui/render/market.py +253 -0
- aria_code/ui/render/output.py +954 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +367 -0
- aria_code/ui/robot.py +239 -0
- aria_code/ui/startup_dashboard.py +110 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +518 -0
- aria_code/video_workflow/__init__.py +8 -0
- aria_code/video_workflow/__main__.py +61 -0
- aria_code/video_workflow/engines/__init__.py +18 -0
- aria_code/video_workflow/engines/base.py +69 -0
- aria_code/video_workflow/engines/custom_model.py +204 -0
- aria_code/video_workflow/engines/google_veo.py +94 -0
- aria_code/video_workflow/engines/local_engine.py +71 -0
- aria_code/video_workflow/engines/registry.py +98 -0
- aria_code/video_workflow/google_models.py +278 -0
- aria_code/video_workflow/music/__init__.py +5 -0
- aria_code/video_workflow/music/foley_service.py +132 -0
- aria_code/video_workflow/music/service.py +212 -0
- aria_code/video_workflow/pipeline.py +620 -0
- aria_code/video_workflow/plan.py +202 -0
- aria_code/video_workflow/session.py +297 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +219 -0
- aria_code/workspace/verify.py +167 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.167.0.dist-info/METADATA +330 -0
- aria_code-0.167.0.dist-info/RECORD +601 -0
- aria_code-0.167.0.dist-info/WHEEL +5 -0
- aria_code-0.167.0.dist-info/entry_points.txt +5 -0
- aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
- 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
|
+
|