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,775 @@
|
|
|
1
|
+
"""LLM provider abstraction layer.
|
|
2
|
+
|
|
3
|
+
Defines the minimal Protocol that every provider must satisfy so the
|
|
4
|
+
agent loop can call any backend (Ollama, AriaSSE, DeepSeek, etc.)
|
|
5
|
+
without importing provider-specific code.
|
|
6
|
+
|
|
7
|
+
Usage
|
|
8
|
+
-----
|
|
9
|
+
Implement the protocol on any class or pass a coroutine that matches
|
|
10
|
+
``stream()``'s signature as a bare ``provider_fn`` callable.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
from contextlib import suppress
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import AsyncGenerator, Optional, Protocol, runtime_checkable
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ── Event types emitted by LLMProvider.stream() ──────────────────────────────
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class LLMToken:
|
|
24
|
+
"""A single text token from the model."""
|
|
25
|
+
text: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class LLMThinking:
|
|
30
|
+
"""One thinking/reasoning token (extended-thinking models)."""
|
|
31
|
+
content: str
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class LLMToolCall:
|
|
36
|
+
"""Model requested a tool call.
|
|
37
|
+
|
|
38
|
+
``call_id`` and ``thought_signature`` are opaque values some backends
|
|
39
|
+
(Gemini) hand out with a call and require back, unchanged, when the call
|
|
40
|
+
is replayed in history. Providers that have no such thing leave them None.
|
|
41
|
+
The signature is base64 text, not bytes, because history is JSON.
|
|
42
|
+
"""
|
|
43
|
+
tool: str
|
|
44
|
+
params: dict
|
|
45
|
+
call_id: Optional[str] = None
|
|
46
|
+
thought_signature: Optional[str] = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class LLMToolResult:
|
|
51
|
+
"""Provider reported a tool execution result summary."""
|
|
52
|
+
tool: str
|
|
53
|
+
summary: str
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class LLMStatus:
|
|
58
|
+
"""Provider emitted a streaming status update."""
|
|
59
|
+
state: str
|
|
60
|
+
message: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True)
|
|
64
|
+
class LLMDone:
|
|
65
|
+
"""Stream finished. Carries the aggregated result."""
|
|
66
|
+
response: str
|
|
67
|
+
tool_calls_pending: list = field(default_factory=list)
|
|
68
|
+
usage: dict = field(default_factory=dict)
|
|
69
|
+
provider: str = "unknown"
|
|
70
|
+
success: bool = True
|
|
71
|
+
cancelled: bool = False
|
|
72
|
+
error: str = ""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Union type for type-checkers
|
|
76
|
+
LLMEvent = LLMToken | LLMThinking | LLMToolCall | LLMToolResult | LLMStatus | LLMDone
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def event_kind(event: object) -> str:
|
|
80
|
+
"""The event's type by name — use this, not isinstance, to dispatch events.
|
|
81
|
+
|
|
82
|
+
These modules are importable under two roots: ``aria_code.apps.cli…`` and
|
|
83
|
+
the bare ``apps.cli…`` that tests patch. Each root defines its own copy of
|
|
84
|
+
every event class, and runtime_bridge builds providers from the bare root
|
|
85
|
+
while the consumers checked against the packaged one. isinstance then
|
|
86
|
+
failed for every event: a provider streamed "ready", the consumer saw
|
|
87
|
+
nothing, and the turn ended as empty_response. That hit every chat turn
|
|
88
|
+
through ConfiguredProvider's own events — OpenAI-compatible endpoints,
|
|
89
|
+
LM Studio, the registry providers, and Gemini without ADC.
|
|
90
|
+
"""
|
|
91
|
+
return type(event).__name__
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _resolve_ollama_stream():
|
|
95
|
+
"""Prefer the aria_cli rebound stream_ollama when available."""
|
|
96
|
+
import sys
|
|
97
|
+
|
|
98
|
+
# Console entry points import ``aria_cli``; direct execution
|
|
99
|
+
# (``python aria_cli.py`` and the npm launcher) registers it as
|
|
100
|
+
# ``__main__``. Both own the rebound function whose globals include the
|
|
101
|
+
# CLI tool registry and cache helpers.
|
|
102
|
+
#
|
|
103
|
+
# ``aria_code.aria_cli`` is the same file reached through the package root
|
|
104
|
+
# rather than as a bare top-level module — the import the SDK and daemon
|
|
105
|
+
# use. Without it here they fell through to the raw extracted function,
|
|
106
|
+
# whose globals lack the borrowed CLI helpers, and the first cache-eligible
|
|
107
|
+
# turn died with ``NameError: name '_cache_key' is not defined``.
|
|
108
|
+
for module_name in ("aria_cli", "aria_code.aria_cli", "__main__"):
|
|
109
|
+
module = sys.modules.get(module_name)
|
|
110
|
+
rebound = getattr(module, "stream_ollama", None) if module else None
|
|
111
|
+
if callable(rebound):
|
|
112
|
+
return rebound
|
|
113
|
+
from aria_code.apps.cli.providers.llm.ollama_stream import stream_ollama
|
|
114
|
+
|
|
115
|
+
return stream_ollama
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async def _stream_callback_provider(invoke, *, done_provider: str) -> AsyncGenerator[LLMEvent, None]:
|
|
119
|
+
"""Convert a callback-based provider coroutine into a real async event stream."""
|
|
120
|
+
|
|
121
|
+
queue: asyncio.Queue[LLMEvent] = asyncio.Queue()
|
|
122
|
+
|
|
123
|
+
def _on_token(tok: str) -> None:
|
|
124
|
+
queue.put_nowait(LLMToken(text=tok))
|
|
125
|
+
|
|
126
|
+
def _on_thinking(content: str) -> None:
|
|
127
|
+
queue.put_nowait(LLMThinking(content=content))
|
|
128
|
+
|
|
129
|
+
def _on_tool_call(tool: str, params: dict) -> None:
|
|
130
|
+
queue.put_nowait(LLMToolCall(tool=tool, params=params))
|
|
131
|
+
|
|
132
|
+
def _on_tool_result(tool: str, summary: str) -> None:
|
|
133
|
+
queue.put_nowait(LLMToolResult(tool=tool, summary=summary))
|
|
134
|
+
|
|
135
|
+
def _on_status(state: str, message: str) -> None:
|
|
136
|
+
queue.put_nowait(LLMStatus(state=state, message=message))
|
|
137
|
+
|
|
138
|
+
task = asyncio.create_task(
|
|
139
|
+
invoke(_on_token, _on_thinking, _on_tool_call, _on_tool_result, _on_status)
|
|
140
|
+
)
|
|
141
|
+
try:
|
|
142
|
+
while not task.done() or not queue.empty():
|
|
143
|
+
try:
|
|
144
|
+
yield await asyncio.wait_for(queue.get(), timeout=0.05)
|
|
145
|
+
except asyncio.TimeoutError:
|
|
146
|
+
continue
|
|
147
|
+
try:
|
|
148
|
+
result = await task
|
|
149
|
+
except Exception as exc:
|
|
150
|
+
yield LLMDone(response="", provider=done_provider, success=False, error=str(exc))
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
yield LLMDone(
|
|
154
|
+
response=result.get("response", ""),
|
|
155
|
+
tool_calls_pending=result.get("tool_calls_pending", []),
|
|
156
|
+
usage=result.get("usage", {}),
|
|
157
|
+
provider=result.get("provider", done_provider),
|
|
158
|
+
success=result.get("success", False),
|
|
159
|
+
cancelled=result.get("cancelled", False),
|
|
160
|
+
error=result.get("error", ""),
|
|
161
|
+
)
|
|
162
|
+
finally:
|
|
163
|
+
# A deadline or an early-closing consumer must also stop the request
|
|
164
|
+
# behind this callback adapter, rather than leave it running invisibly.
|
|
165
|
+
if not task.done():
|
|
166
|
+
task.cancel()
|
|
167
|
+
with suppress(asyncio.CancelledError, Exception):
|
|
168
|
+
await task
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# ── Protocol ─────────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
@runtime_checkable
|
|
174
|
+
class LLMProvider(Protocol):
|
|
175
|
+
"""Minimal interface every LLM backend must implement.
|
|
176
|
+
|
|
177
|
+
``stream()`` is an async generator that yields ``LLMEvent`` objects.
|
|
178
|
+
The final event is always ``LLMDone``; callers may break early on
|
|
179
|
+
``LLMDone`` or consume the full stream.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
messages:
|
|
184
|
+
Full conversation history (list of {"role": …, "content": …} dicts).
|
|
185
|
+
tools:
|
|
186
|
+
OpenAI-format function schema list; empty list disables tool calls.
|
|
187
|
+
cancel_event:
|
|
188
|
+
asyncio.Event that, when set, signals the provider to stop.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
async def stream(
|
|
192
|
+
self,
|
|
193
|
+
messages: list,
|
|
194
|
+
tools: list,
|
|
195
|
+
*,
|
|
196
|
+
cancel_event: Optional[asyncio.Event] = None,
|
|
197
|
+
) -> AsyncGenerator[LLMEvent, None]:
|
|
198
|
+
...
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# ── Thin adapters (wrap existing callables as LLMProvider) ───────────────────
|
|
202
|
+
|
|
203
|
+
class OllamaProvider:
|
|
204
|
+
"""Wraps ``stream_ollama`` as an ``LLMProvider``.
|
|
205
|
+
|
|
206
|
+
Import lazily to avoid circular dependencies — ``stream_ollama`` lives in
|
|
207
|
+
the same providers package and rebinds globals from aria_cli at startup.
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def __init__(
|
|
211
|
+
self,
|
|
212
|
+
ollama_url: str,
|
|
213
|
+
model: str,
|
|
214
|
+
*,
|
|
215
|
+
system_override: Optional[str] = None,
|
|
216
|
+
show_market_prefetch_status: bool = True,
|
|
217
|
+
include_ambient_context: bool = True,
|
|
218
|
+
) -> None:
|
|
219
|
+
self.ollama_url = ollama_url
|
|
220
|
+
self.model = model
|
|
221
|
+
self.system_override = system_override
|
|
222
|
+
self.show_market_prefetch_status = show_market_prefetch_status
|
|
223
|
+
self.include_ambient_context = include_ambient_context
|
|
224
|
+
|
|
225
|
+
async def stream(
|
|
226
|
+
self,
|
|
227
|
+
messages: list,
|
|
228
|
+
tools: list,
|
|
229
|
+
*,
|
|
230
|
+
cancel_event: Optional[asyncio.Event] = None,
|
|
231
|
+
) -> AsyncGenerator[LLMEvent, None]:
|
|
232
|
+
stream_ollama = _resolve_ollama_stream()
|
|
233
|
+
|
|
234
|
+
# Extract last user message as the prompt; the rest is history
|
|
235
|
+
history = [m for m in messages if not (m.get("role") == "user" and m is messages[-1])]
|
|
236
|
+
prompt = messages[-1].get("content", "") if messages else ""
|
|
237
|
+
|
|
238
|
+
async def _invoke(on_token, on_thinking, on_tool_call, on_tool_result, _on_status):
|
|
239
|
+
return await stream_ollama(
|
|
240
|
+
self.ollama_url,
|
|
241
|
+
prompt,
|
|
242
|
+
history,
|
|
243
|
+
model=self.model,
|
|
244
|
+
on_token=on_token,
|
|
245
|
+
on_thinking=on_thinking,
|
|
246
|
+
on_tool_call=on_tool_call,
|
|
247
|
+
on_tool_result=on_tool_result,
|
|
248
|
+
cancel_event=cancel_event,
|
|
249
|
+
enable_tools=bool(tools),
|
|
250
|
+
tool_schemas=list(tools or []),
|
|
251
|
+
defer_tool_execution=True,
|
|
252
|
+
system_override=self.system_override,
|
|
253
|
+
show_market_prefetch_status=self.show_market_prefetch_status,
|
|
254
|
+
**({"include_ambient_context": False} if not self.include_ambient_context else {}),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
async for event in _stream_callback_provider(_invoke, done_provider="ollama"):
|
|
258
|
+
yield event
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class AriaSSEProvider:
|
|
262
|
+
"""Wraps ``stream_chat`` (Aria cloud SSE) as an ``LLMProvider``."""
|
|
263
|
+
|
|
264
|
+
def __init__(
|
|
265
|
+
self,
|
|
266
|
+
api_url: str,
|
|
267
|
+
model: str,
|
|
268
|
+
*,
|
|
269
|
+
auth_token: Optional[str] = None,
|
|
270
|
+
thinking_mode: str = "auto",
|
|
271
|
+
user_context: Optional[dict] = None,
|
|
272
|
+
system_override: Optional[str] = None,
|
|
273
|
+
project_context: str = "",
|
|
274
|
+
use_react_gateway: bool = False,
|
|
275
|
+
local_tools: bool = False,
|
|
276
|
+
) -> None:
|
|
277
|
+
self.api_url = api_url
|
|
278
|
+
self.model = model
|
|
279
|
+
self.auth_token = auth_token
|
|
280
|
+
self.thinking_mode = thinking_mode
|
|
281
|
+
self.user_context = user_context or {}
|
|
282
|
+
self.system_override = system_override
|
|
283
|
+
self.project_context = project_context
|
|
284
|
+
self.use_react_gateway = use_react_gateway
|
|
285
|
+
self.local_tools = local_tools
|
|
286
|
+
|
|
287
|
+
async def stream(
|
|
288
|
+
self,
|
|
289
|
+
messages: list,
|
|
290
|
+
tools: list,
|
|
291
|
+
*,
|
|
292
|
+
cancel_event: Optional[asyncio.Event] = None,
|
|
293
|
+
) -> AsyncGenerator[LLMEvent, None]:
|
|
294
|
+
from aria_code.apps.cli.providers.llm.sse_stream import stream_chat
|
|
295
|
+
|
|
296
|
+
history = [m for m in messages if not (m.get("role") == "user" and m is messages[-1])]
|
|
297
|
+
prompt = messages[-1].get("content", "") if messages else ""
|
|
298
|
+
|
|
299
|
+
uctx = dict(self.user_context)
|
|
300
|
+
if self.system_override:
|
|
301
|
+
uctx["system_role_override"] = self.system_override
|
|
302
|
+
|
|
303
|
+
async def _invoke(on_token, on_thinking, on_tool_call, on_tool_result, on_status):
|
|
304
|
+
return await stream_chat(
|
|
305
|
+
self.api_url,
|
|
306
|
+
prompt,
|
|
307
|
+
history,
|
|
308
|
+
model=self.model,
|
|
309
|
+
thinking_mode=self.thinking_mode,
|
|
310
|
+
user_context=uctx or None,
|
|
311
|
+
auth_token=self.auth_token,
|
|
312
|
+
on_token=on_token,
|
|
313
|
+
on_thinking=on_thinking,
|
|
314
|
+
on_tool_call=on_tool_call,
|
|
315
|
+
on_tool_result=on_tool_result,
|
|
316
|
+
on_status=on_status,
|
|
317
|
+
cancel_event=cancel_event,
|
|
318
|
+
project_context=self.project_context,
|
|
319
|
+
use_react_gateway=self.use_react_gateway,
|
|
320
|
+
tool_schemas=list(tools or []) if self.local_tools else None,
|
|
321
|
+
local_tool_execution=self.local_tools,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
async for event in _stream_callback_provider(_invoke, done_provider="aria_sse"):
|
|
325
|
+
yield event
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _opt_in(config: dict, key: str, default: bool = True) -> bool:
|
|
329
|
+
"""Read a tri-state boolean setting where "unset" means *default*.
|
|
330
|
+
|
|
331
|
+
``config.get(key, default)`` is the wrong tool here and produced a real
|
|
332
|
+
failure: a config holding ``"use_vertexai": null`` returns None — the
|
|
333
|
+
stored value, not the default — so a Google model silently opted out of
|
|
334
|
+
Vertex and went down the OpenAI-compatible path instead. Settings that are
|
|
335
|
+
written as null by a config round-trip have to mean "not configured".
|
|
336
|
+
"""
|
|
337
|
+
value = config.get(key)
|
|
338
|
+
return default if value is None else bool(value)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _gcloud(*args: str, timeout: float = 20) -> str:
|
|
342
|
+
import subprocess
|
|
343
|
+
|
|
344
|
+
result = subprocess.run(["gcloud", *args], capture_output=True, text=True, timeout=timeout,
|
|
345
|
+
stdin=subprocess.DEVNULL)
|
|
346
|
+
return result.stdout.strip() if result.returncode == 0 else ""
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _adc_available() -> bool:
|
|
350
|
+
"""Application-default credentials exist (file, env, or running on Google Cloud)."""
|
|
351
|
+
import os
|
|
352
|
+
|
|
353
|
+
if os.getenv("GOOGLE_APPLICATION_CREDENTIALS") or os.getenv("K_SERVICE") or os.getenv("GCE_METADATA_HOST"):
|
|
354
|
+
return True
|
|
355
|
+
if os.name == "nt":
|
|
356
|
+
folder = os.getenv("CLOUDSDK_CONFIG") or os.path.join(os.getenv("APPDATA", ""), "gcloud")
|
|
357
|
+
else:
|
|
358
|
+
folder = os.getenv("CLOUDSDK_CONFIG") or os.path.join(os.path.expanduser("~"), ".config", "gcloud")
|
|
359
|
+
return os.path.isfile(os.path.join(folder, "application_default_credentials.json"))
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def google_cloud_available() -> bool:
|
|
363
|
+
"""Credentials Vertex AI can use: application-default credentials or a gcloud CLI."""
|
|
364
|
+
import shutil
|
|
365
|
+
|
|
366
|
+
return _adc_available() or bool(shutil.which("gcloud"))
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _google_api_key(config: dict) -> str:
|
|
370
|
+
import os
|
|
371
|
+
|
|
372
|
+
from aria_code.providers.llm.registry import _load_provider_cfg_from_file
|
|
373
|
+
|
|
374
|
+
for value in (config.get("api_key"), config.get("gemini_key"), os.getenv("GEMINI_API_KEY"),
|
|
375
|
+
os.getenv("GOOGLE_API_KEY"), _load_provider_cfg_from_file("google").get("api_key")):
|
|
376
|
+
if str(value or "").strip():
|
|
377
|
+
return str(value).strip()
|
|
378
|
+
return ""
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _gcloud_login_only(config: dict) -> bool:
|
|
382
|
+
"""No ADC and no Gemini key, but a gcloud CLI to borrow a token from."""
|
|
383
|
+
import shutil
|
|
384
|
+
|
|
385
|
+
return not _adc_available() and not _google_api_key(config) and bool(shutil.which("gcloud"))
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _gcloud_folder() -> str:
|
|
389
|
+
import os
|
|
390
|
+
|
|
391
|
+
if os.name == "nt":
|
|
392
|
+
return os.getenv("CLOUDSDK_CONFIG") or os.path.join(os.getenv("APPDATA", ""), "gcloud")
|
|
393
|
+
return os.getenv("CLOUDSDK_CONFIG") or os.path.join(os.path.expanduser("~"), ".config", "gcloud")
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _gcloud_default_project(folder: str) -> str:
|
|
397
|
+
"""The active gcloud configuration's core/project, read from its file."""
|
|
398
|
+
import configparser
|
|
399
|
+
import os
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
with open(os.path.join(folder, "active_config"), encoding="utf-8") as handle:
|
|
403
|
+
active = handle.read().strip() or "default"
|
|
404
|
+
except OSError:
|
|
405
|
+
active = "default"
|
|
406
|
+
parser = configparser.ConfigParser()
|
|
407
|
+
try:
|
|
408
|
+
parser.read(os.path.join(folder, "configurations", f"config_{active}"), encoding="utf-8")
|
|
409
|
+
return parser.get("core", "project", fallback="").strip()
|
|
410
|
+
except configparser.Error:
|
|
411
|
+
return ""
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def google_readiness(config: dict) -> str:
|
|
415
|
+
""""" when a Gemini request can be attempted, else what is missing.
|
|
416
|
+
|
|
417
|
+
The banner said "Cloud model configured" for the default model on a
|
|
418
|
+
machine with no Google credentials at all, and the first question then
|
|
419
|
+
failed. This reads only the environment and gcloud's files — no
|
|
420
|
+
subprocess — so it is cheap enough for startup.
|
|
421
|
+
"""
|
|
422
|
+
import os
|
|
423
|
+
import shutil
|
|
424
|
+
|
|
425
|
+
if _google_api_key(config) or _adc_available():
|
|
426
|
+
return ""
|
|
427
|
+
if not shutil.which("gcloud"):
|
|
428
|
+
return "no Google credentials"
|
|
429
|
+
# Same order as vertex_openai_endpoint, so the banner names the problem
|
|
430
|
+
# the first request will report.
|
|
431
|
+
folder = _gcloud_folder()
|
|
432
|
+
project = (os.getenv("GOOGLE_CLOUD_PROJECT") or str(config.get("gcp_project") or "")
|
|
433
|
+
or _gcloud_default_project(folder))
|
|
434
|
+
if not project or project == "(unset)":
|
|
435
|
+
return "no Google Cloud project"
|
|
436
|
+
if not any(os.path.isfile(os.path.join(folder, name)) for name in ("credentials.db", "access_tokens.db")):
|
|
437
|
+
return "gcloud not signed in"
|
|
438
|
+
return ""
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def is_vertex_open_model(model: str) -> bool:
|
|
442
|
+
"""A model Vertex serves through its OpenAI-compatible endpoint, not generateContent.
|
|
443
|
+
|
|
444
|
+
Gemma ("gemma-4-26b-a4b-it-maas", "gemma-3-27b-it") and other Model Garden
|
|
445
|
+
managed models ("…-maas") are open models: the Gemini API does not serve
|
|
446
|
+
them, and the native SDK path 404'd.
|
|
447
|
+
"""
|
|
448
|
+
name = str(model or "").lower().split("/")[-1]
|
|
449
|
+
return name.startswith("gemma") or name.endswith("-maas")
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def _adc_access_token() -> str:
|
|
453
|
+
"""An access token from application-default credentials, or "" if there are none."""
|
|
454
|
+
try:
|
|
455
|
+
import google.auth
|
|
456
|
+
import google.auth.transport.requests
|
|
457
|
+
|
|
458
|
+
credentials, _ = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
|
|
459
|
+
credentials.refresh(google.auth.transport.requests.Request())
|
|
460
|
+
return str(credentials.token or "")
|
|
461
|
+
except Exception:
|
|
462
|
+
return ""
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def vertex_openai_endpoint(config: dict) -> dict:
|
|
466
|
+
"""Vertex AI's OpenAI-compatible endpoint and a token from the gcloud login.
|
|
467
|
+
|
|
468
|
+
Returns {} when gcloud is not installed (the caller reports the missing
|
|
469
|
+
API key), {"error": …} when gcloud is there but the request cannot be
|
|
470
|
+
made, otherwise {"token", "base_url", "project", "location"}.
|
|
471
|
+
|
|
472
|
+
This path used to read the project from `gcloud config get-value project`
|
|
473
|
+
alone; with no default project set that is empty, and every request went
|
|
474
|
+
to ".../projects//locations/...". The region was fixed to us-central1,
|
|
475
|
+
and both gcloud calls ran without a timeout on the event loop.
|
|
476
|
+
"""
|
|
477
|
+
import os
|
|
478
|
+
import shutil
|
|
479
|
+
|
|
480
|
+
# Application-default credentials first (a CI runner signed in with
|
|
481
|
+
# Workload Identity Federation, a service account, `gcloud auth
|
|
482
|
+
# application-default login`), then the gcloud CLI's own login.
|
|
483
|
+
adc_token = _adc_access_token() if _adc_available() else ""
|
|
484
|
+
if not adc_token and not shutil.which("gcloud"):
|
|
485
|
+
return {}
|
|
486
|
+
try:
|
|
487
|
+
project = (os.getenv("GOOGLE_CLOUD_PROJECT") or str(config.get("gcp_project") or "")).strip()
|
|
488
|
+
if not project and shutil.which("gcloud"):
|
|
489
|
+
project = _gcloud("config", "get-value", "project", timeout=10).strip()
|
|
490
|
+
if not project or project == "(unset)":
|
|
491
|
+
return {"error": "vertex_needs_project: set GOOGLE_CLOUD_PROJECT, /config set gcp_project=<id>, "
|
|
492
|
+
"or `gcloud config set project <id>`"}
|
|
493
|
+
location = (os.getenv("GOOGLE_CLOUD_LOCATION") or str(config.get("gcp_location") or "") or "global").strip()
|
|
494
|
+
token = adc_token or _gcloud("auth", "print-access-token")
|
|
495
|
+
except Exception as exc: # timeout, permissions: report, never hang the chat
|
|
496
|
+
return {"error": f"vertex_gcloud_failed: {type(exc).__name__}"}
|
|
497
|
+
if not token:
|
|
498
|
+
return {"error": "vertex_not_logged_in: run `gcloud auth login`"}
|
|
499
|
+
host = "aiplatform.googleapis.com" if location == "global" else f"{location}-aiplatform.googleapis.com"
|
|
500
|
+
return {"token": token, "project": project, "location": location,
|
|
501
|
+
"base_url": f"https://{host}/v1/projects/{project}/locations/{location}/endpoints/openapi"}
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
class ConfiguredProvider:
|
|
505
|
+
"""Adapter for the provider selected by ``/model provider/model``.
|
|
506
|
+
|
|
507
|
+
Ollama keeps its dedicated CLI adapter because that path owns Aria's local
|
|
508
|
+
intent and tool orchestration. Other local runtimes use the shared
|
|
509
|
+
``LocalLLMProvider`` OpenAI-compatible transport, while cloud APIs use the
|
|
510
|
+
provider registry (including Anthropic's native protocol).
|
|
511
|
+
"""
|
|
512
|
+
|
|
513
|
+
LOCAL_OPENAI_BACKENDS = {"lmstudio", "vllm", "llamacpp", "jan", "custom"}
|
|
514
|
+
GENERIC_OPENAI_BACKENDS = {
|
|
515
|
+
"google", "gemini", "xai", "grok", "mistral", "cohere",
|
|
516
|
+
"perplexity", "baidu", "ernie", "qianfan", "bytedance",
|
|
517
|
+
"doubao", "ark", "minimax", "stepfun", "01ai", "yi",
|
|
518
|
+
}
|
|
519
|
+
GENERIC_ENV_KEYS = {
|
|
520
|
+
"google": "GOOGLE_API_KEY", "gemini": "GOOGLE_API_KEY",
|
|
521
|
+
"xai": "XAI_API_KEY", "grok": "XAI_API_KEY",
|
|
522
|
+
"mistral": "MISTRAL_API_KEY", "cohere": "COHERE_API_KEY",
|
|
523
|
+
"perplexity": "PERPLEXITY_API_KEY", "baidu": "QIANFAN_ACCESS_KEY",
|
|
524
|
+
"ernie": "QIANFAN_ACCESS_KEY", "qianfan": "QIANFAN_ACCESS_KEY",
|
|
525
|
+
"bytedance": "ARK_API_KEY", "doubao": "ARK_API_KEY",
|
|
526
|
+
"ark": "ARK_API_KEY", "minimax": "MINIMAX_API_KEY",
|
|
527
|
+
"stepfun": "STEPFUN_API_KEY", "01ai": "ONEAI_API_KEY",
|
|
528
|
+
"yi": "ONEAI_API_KEY",
|
|
529
|
+
}
|
|
530
|
+
GENERIC_BASE_URLS = {
|
|
531
|
+
"google": "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
532
|
+
"gemini": "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
533
|
+
"xai": "https://api.x.ai/v1", "grok": "https://api.x.ai/v1",
|
|
534
|
+
"mistral": "https://api.mistral.ai/v1",
|
|
535
|
+
"cohere": "https://api.cohere.ai/compatibility/v1",
|
|
536
|
+
"perplexity": "https://api.perplexity.ai",
|
|
537
|
+
"baidu": "https://qianfan.baidubce.com/v2",
|
|
538
|
+
"ernie": "https://qianfan.baidubce.com/v2",
|
|
539
|
+
"qianfan": "https://qianfan.baidubce.com/v2",
|
|
540
|
+
"bytedance": "https://ark.cn-beijing.volces.com/api/v3",
|
|
541
|
+
"doubao": "https://ark.cn-beijing.volces.com/api/v3",
|
|
542
|
+
"ark": "https://ark.cn-beijing.volces.com/api/v3",
|
|
543
|
+
"minimax": "https://api.minimax.chat/v1",
|
|
544
|
+
"stepfun": "https://api.stepfun.com/v1",
|
|
545
|
+
"01ai": "https://api.lingyiwanwu.com/v1",
|
|
546
|
+
"yi": "https://api.lingyiwanwu.com/v1",
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
def __init__(
|
|
550
|
+
self,
|
|
551
|
+
config: dict,
|
|
552
|
+
model: str,
|
|
553
|
+
*,
|
|
554
|
+
system_override: Optional[str] = None,
|
|
555
|
+
) -> None:
|
|
556
|
+
self.config = dict(config or {})
|
|
557
|
+
from aria_code.apps.cli.providers.chat_routing import (
|
|
558
|
+
model_provider,
|
|
559
|
+
normalize_provider_name,
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
# A provider-qualified model id names its own backend and wins over
|
|
563
|
+
# local_provider — the default config pairs model="gemini-pro" with
|
|
564
|
+
# local_provider="ollama", so deriving the backend from local_provider
|
|
565
|
+
# alone sent every Gemini request to Ollama.
|
|
566
|
+
declared = model_provider(model)
|
|
567
|
+
self.backend = declared or normalize_provider_name(
|
|
568
|
+
self.config.get("local_provider") or "ollama"
|
|
569
|
+
)
|
|
570
|
+
# Downstream APIs expect the bare model name; "google/gemini-2.5-pro"
|
|
571
|
+
# would 404 against Google's endpoint and would be re-prefixed into
|
|
572
|
+
# "google/google/gemini-2.5-pro" by the registry path below.
|
|
573
|
+
self.model = model.split("/", 1)[1] if declared else model
|
|
574
|
+
self.config["local_provider"] = self.backend
|
|
575
|
+
self.system_override = system_override
|
|
576
|
+
|
|
577
|
+
async def _stream_vertex_open_model(self, prepared: list, tools: list, cancel_event):
|
|
578
|
+
"""Gemma and other managed open models, through Vertex's OpenAI-compatible endpoint."""
|
|
579
|
+
from aria_code.local_llm_provider import LocalLLMProvider
|
|
580
|
+
|
|
581
|
+
vertex = await asyncio.to_thread(vertex_openai_endpoint, self.config)
|
|
582
|
+
if not vertex or vertex.get("error"):
|
|
583
|
+
yield LLMDone(response="", provider="vertexai", success=False,
|
|
584
|
+
error=(vertex or {}).get("error") or
|
|
585
|
+
"vertex_needs_credentials: run `gcloud auth application-default login` "
|
|
586
|
+
"or set GOOGLE_APPLICATION_CREDENTIALS")
|
|
587
|
+
return
|
|
588
|
+
cfg = dict(self.config)
|
|
589
|
+
cfg.update({
|
|
590
|
+
"model": f"google/{self.model}",
|
|
591
|
+
"local_provider": "custom",
|
|
592
|
+
"custom_endpoint": vertex["base_url"],
|
|
593
|
+
"custom_model": f"google/{self.model}",
|
|
594
|
+
"local_api_key": vertex["token"],
|
|
595
|
+
})
|
|
596
|
+
provider = LocalLLMProvider.from_config(cfg)
|
|
597
|
+
async for event in provider.stream(prepared, tools=tools, cancel_event=cancel_event):
|
|
598
|
+
kind = event.get("type")
|
|
599
|
+
if kind == "token":
|
|
600
|
+
yield LLMToken(text=str(event.get("text", "")))
|
|
601
|
+
elif kind == "thinking":
|
|
602
|
+
yield LLMThinking(content=str(event.get("text", "")))
|
|
603
|
+
elif kind == "tool_call":
|
|
604
|
+
yield LLMToolCall(tool=str(event.get("name", "")), params=dict(event.get("arguments") or {}))
|
|
605
|
+
elif kind == "error":
|
|
606
|
+
yield LLMDone(response="", provider="vertexai", success=False,
|
|
607
|
+
error=str(event.get("message") or "provider_error"))
|
|
608
|
+
return
|
|
609
|
+
elif kind == "done":
|
|
610
|
+
yield LLMDone(response=str(event.get("text") or ""), usage=dict(event.get("usage") or {}),
|
|
611
|
+
provider="vertexai", success=True,
|
|
612
|
+
cancelled=str(event.get("stop_reason") or "") == "cancelled")
|
|
613
|
+
return
|
|
614
|
+
yield LLMDone(response="", provider="vertexai", success=False, error="empty_response")
|
|
615
|
+
|
|
616
|
+
def _messages(self, messages: list) -> list:
|
|
617
|
+
prepared = [dict(message) for message in messages]
|
|
618
|
+
if not self.system_override:
|
|
619
|
+
return prepared
|
|
620
|
+
if prepared and prepared[0].get("role") == "system":
|
|
621
|
+
prepared[0]["content"] = (
|
|
622
|
+
self.system_override + "\n\n" + str(prepared[0].get("content", ""))
|
|
623
|
+
)
|
|
624
|
+
else:
|
|
625
|
+
prepared.insert(0, {"role": "system", "content": self.system_override})
|
|
626
|
+
return prepared
|
|
627
|
+
|
|
628
|
+
async def stream(
|
|
629
|
+
self,
|
|
630
|
+
messages: list,
|
|
631
|
+
tools: list,
|
|
632
|
+
*,
|
|
633
|
+
cancel_event: Optional[asyncio.Event] = None,
|
|
634
|
+
) -> AsyncGenerator[LLMEvent, None]:
|
|
635
|
+
prepared = self._messages(messages)
|
|
636
|
+
|
|
637
|
+
native_vertex = self.backend in ("vertexai", "vertex-ai", "google-genai") or (
|
|
638
|
+
self.backend in ("google", "gemini") and _opt_in(self.config, "use_vertexai")
|
|
639
|
+
)
|
|
640
|
+
if self.backend in ("google", "gemini", "vertexai", "vertex-ai", "google-genai") \
|
|
641
|
+
and is_vertex_open_model(self.model):
|
|
642
|
+
async for evt in self._stream_vertex_open_model(prepared, tools, cancel_event):
|
|
643
|
+
yield evt
|
|
644
|
+
return
|
|
645
|
+
if native_vertex and self.backend in ("google", "gemini") and _gcloud_login_only(self.config):
|
|
646
|
+
# The SDK route accepts only application-default credentials. With
|
|
647
|
+
# just a `gcloud auth login` it failed outright, so a logged-in
|
|
648
|
+
# user could not reach Gemini; the OpenAI-compatible Vertex
|
|
649
|
+
# endpoint below takes the gcloud login's token instead.
|
|
650
|
+
native_vertex = False
|
|
651
|
+
if native_vertex:
|
|
652
|
+
from aria_code.apps.cli.providers.vertexai_stream import VertexAIProvider
|
|
653
|
+
provider = VertexAIProvider(
|
|
654
|
+
model=self.model,
|
|
655
|
+
config=self.config,
|
|
656
|
+
system_override=self.system_override,
|
|
657
|
+
)
|
|
658
|
+
async for evt in provider.stream(prepared, tools=tools, cancel_event=cancel_event):
|
|
659
|
+
yield evt
|
|
660
|
+
return
|
|
661
|
+
|
|
662
|
+
if self.backend in self.LOCAL_OPENAI_BACKENDS | self.GENERIC_OPENAI_BACKENDS:
|
|
663
|
+
from aria_code.local_llm_provider import LocalLLMProvider
|
|
664
|
+
|
|
665
|
+
cfg = dict(self.config)
|
|
666
|
+
cfg["model"] = self.model
|
|
667
|
+
if self.backend in self.GENERIC_OPENAI_BACKENDS:
|
|
668
|
+
import os
|
|
669
|
+
from aria_code.providers.llm.registry import _load_provider_cfg_from_file
|
|
670
|
+
|
|
671
|
+
file_cfg = _load_provider_cfg_from_file(self.backend)
|
|
672
|
+
api_key = (
|
|
673
|
+
os.getenv(self.GENERIC_ENV_KEYS[self.backend], "")
|
|
674
|
+
or str(file_cfg.get("api_key") or "")
|
|
675
|
+
)
|
|
676
|
+
base_url = file_cfg.get("base_url") or self.GENERIC_BASE_URLS[self.backend]
|
|
677
|
+
model_name = self.model
|
|
678
|
+
|
|
679
|
+
if not api_key and self.backend in ("google", "gemini"):
|
|
680
|
+
# No Gemini API key: use Vertex AI with the gcloud login.
|
|
681
|
+
vertex = await asyncio.to_thread(vertex_openai_endpoint, self.config)
|
|
682
|
+
if vertex.get("error"):
|
|
683
|
+
yield LLMDone(response="", provider="vertexai", success=False, error=vertex["error"])
|
|
684
|
+
return
|
|
685
|
+
if vertex:
|
|
686
|
+
api_key, base_url = vertex["token"], vertex["base_url"]
|
|
687
|
+
if not model_name.startswith("google/"):
|
|
688
|
+
model_name = f"google/{model_name}"
|
|
689
|
+
|
|
690
|
+
if not api_key:
|
|
691
|
+
yield LLMDone(
|
|
692
|
+
response="", provider=self.backend, success=False,
|
|
693
|
+
error=f"missing_api_key:{self.backend}",
|
|
694
|
+
)
|
|
695
|
+
return
|
|
696
|
+
cfg.update({
|
|
697
|
+
"local_provider": "custom",
|
|
698
|
+
"custom_endpoint": base_url,
|
|
699
|
+
"custom_model": model_name,
|
|
700
|
+
"local_api_key": api_key,
|
|
701
|
+
})
|
|
702
|
+
provider = LocalLLMProvider.from_config(cfg)
|
|
703
|
+
source = self.backend
|
|
704
|
+
event_stream = provider.stream(prepared, tools=tools, cancel_event=cancel_event)
|
|
705
|
+
else:
|
|
706
|
+
from aria_code.providers.llm.base import Message
|
|
707
|
+
from aria_code.providers.llm.registry import get_provider
|
|
708
|
+
|
|
709
|
+
try:
|
|
710
|
+
provider = get_provider(f"{self.backend}/{self.model}")
|
|
711
|
+
except Exception as exc:
|
|
712
|
+
yield LLMDone(
|
|
713
|
+
response="", provider=self.backend, success=False,
|
|
714
|
+
error=f"{self.backend}: {exc}",
|
|
715
|
+
)
|
|
716
|
+
return
|
|
717
|
+
source = self.backend
|
|
718
|
+
# The registry providers send role and content only; native tool
|
|
719
|
+
# history would reach them as "tool" messages with no call id.
|
|
720
|
+
from aria_code.providers.tool_messages import flatten_tool_turns
|
|
721
|
+
|
|
722
|
+
registry_messages = [
|
|
723
|
+
Message(
|
|
724
|
+
role=str(message.get("role", "user")),
|
|
725
|
+
content=str(message.get("content", "")),
|
|
726
|
+
name=message.get("name"),
|
|
727
|
+
tool_call_id=message.get("tool_call_id"),
|
|
728
|
+
)
|
|
729
|
+
for message in flatten_tool_turns(prepared)
|
|
730
|
+
]
|
|
731
|
+
# providers.llm accepts bare function schemas, while the CLI owns
|
|
732
|
+
# OpenAI envelopes. Normalize once at this adapter boundary.
|
|
733
|
+
registry_tools = [
|
|
734
|
+
item.get("function", item)
|
|
735
|
+
if isinstance(item, dict) and item.get("type") == "function"
|
|
736
|
+
else item
|
|
737
|
+
for item in tools
|
|
738
|
+
]
|
|
739
|
+
event_stream = provider.stream(
|
|
740
|
+
registry_messages, tools=registry_tools, cancel_event=cancel_event
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
async for event in event_stream:
|
|
744
|
+
kind = event.get("type")
|
|
745
|
+
if kind == "token":
|
|
746
|
+
yield LLMToken(text=str(event.get("text", "")))
|
|
747
|
+
elif kind == "thinking":
|
|
748
|
+
yield LLMThinking(content=str(event.get("text", "")))
|
|
749
|
+
elif kind == "tool_call":
|
|
750
|
+
yield LLMToolCall(
|
|
751
|
+
tool=str(event.get("name", "")),
|
|
752
|
+
params=dict(event.get("arguments") or {}),
|
|
753
|
+
)
|
|
754
|
+
elif kind == "error":
|
|
755
|
+
yield LLMDone(
|
|
756
|
+
response="",
|
|
757
|
+
provider=source,
|
|
758
|
+
success=False,
|
|
759
|
+
error=str(event.get("message") or "provider_error"),
|
|
760
|
+
)
|
|
761
|
+
return
|
|
762
|
+
elif kind == "done":
|
|
763
|
+
stop_reason = str(event.get("stop_reason") or "")
|
|
764
|
+
yield LLMDone(
|
|
765
|
+
response=str(event.get("text") or ""),
|
|
766
|
+
usage=dict(event.get("usage") or {}),
|
|
767
|
+
provider=source,
|
|
768
|
+
success=True,
|
|
769
|
+
cancelled=stop_reason == "cancelled",
|
|
770
|
+
)
|
|
771
|
+
return
|
|
772
|
+
|
|
773
|
+
yield LLMDone(
|
|
774
|
+
response="", provider=source, success=False, error="empty_response"
|
|
775
|
+
)
|