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,172 @@
|
|
|
1
|
+
"""/earnings SYMBOL: the next report, the last four against estimates, and the quarterly numbers.
|
|
2
|
+
|
|
3
|
+
/earnings handed off to /report, which wrote a general technical-analysis
|
|
4
|
+
report: price, moving averages, RSI. Nothing about earnings. This shows
|
|
5
|
+
what the command names, from yfinance: the next report date with the
|
|
6
|
+
consensus range, reported EPS against the estimate for recent quarters, and
|
|
7
|
+
revenue, net income and diluted EPS by quarter. `/earnings SYMBOL --report`
|
|
8
|
+
still writes the Markdown report.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import math
|
|
14
|
+
from typing import Any, Optional
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _num(value: Any) -> Optional[float]:
|
|
18
|
+
try:
|
|
19
|
+
number = float(value)
|
|
20
|
+
except (TypeError, ValueError):
|
|
21
|
+
return None
|
|
22
|
+
return None if math.isnan(number) or math.isinf(number) else number
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def fetch_earnings(symbol: str, ticker: Any = None) -> dict:
|
|
26
|
+
"""Earnings data for ``symbol`` from yfinance (or the ``ticker`` object given)."""
|
|
27
|
+
import logging
|
|
28
|
+
|
|
29
|
+
# yfinance logs "HTTP Error 404 …" straight to the terminal for a symbol
|
|
30
|
+
# without earnings; the answer below says so in a sentence instead.
|
|
31
|
+
yf_log = logging.getLogger("yfinance")
|
|
32
|
+
previous = yf_log.level
|
|
33
|
+
yf_log.setLevel(logging.CRITICAL)
|
|
34
|
+
try:
|
|
35
|
+
return _fetch_earnings(symbol, ticker)
|
|
36
|
+
finally:
|
|
37
|
+
yf_log.setLevel(previous)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _fetch_earnings(symbol: str, ticker: Any = None) -> dict:
|
|
41
|
+
if ticker is None:
|
|
42
|
+
try:
|
|
43
|
+
import yfinance as yf
|
|
44
|
+
except ImportError:
|
|
45
|
+
return {"success": False, "error": "yfinance is not installed"}
|
|
46
|
+
ticker = yf.Ticker(symbol)
|
|
47
|
+
out: dict = {"success": True, "symbol": symbol.upper(), "history": [], "quarters": [], "next": {}}
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
dates = ticker.earnings_dates
|
|
51
|
+
except Exception:
|
|
52
|
+
dates = None
|
|
53
|
+
if dates is not None and not getattr(dates, "empty", True):
|
|
54
|
+
for when, row in dates.sort_index(ascending=False).iterrows():
|
|
55
|
+
reported = _num(row.get("Reported EPS"))
|
|
56
|
+
estimate = _num(row.get("EPS Estimate"))
|
|
57
|
+
if reported is None:
|
|
58
|
+
continue
|
|
59
|
+
surprise = _num(row.get("Surprise(%)"))
|
|
60
|
+
if surprise is None and estimate:
|
|
61
|
+
surprise = (reported - estimate) / abs(estimate) * 100
|
|
62
|
+
out["history"].append({"date": str(when)[:10], "estimate": estimate, "reported": reported,
|
|
63
|
+
"surprise_pct": surprise})
|
|
64
|
+
if len(out["history"]) == 4:
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
calendar = ticker.calendar or {}
|
|
69
|
+
except Exception:
|
|
70
|
+
calendar = {}
|
|
71
|
+
if isinstance(calendar, dict) and calendar:
|
|
72
|
+
upcoming = calendar.get("Earnings Date") or []
|
|
73
|
+
out["next"] = {
|
|
74
|
+
"date": str(upcoming[0]) if upcoming else None,
|
|
75
|
+
"eps_avg": _num(calendar.get("Earnings Average")),
|
|
76
|
+
"eps_low": _num(calendar.get("Earnings Low")),
|
|
77
|
+
"eps_high": _num(calendar.get("Earnings High")),
|
|
78
|
+
"revenue_avg": _num(calendar.get("Revenue Average")),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
income = ticker.quarterly_income_stmt
|
|
83
|
+
except Exception:
|
|
84
|
+
income = None
|
|
85
|
+
if income is not None and not getattr(income, "empty", True):
|
|
86
|
+
for column in list(income.columns)[:5]:
|
|
87
|
+
def field(name: str) -> Optional[float]:
|
|
88
|
+
return _num(income.at[name, column]) if name in income.index else None
|
|
89
|
+
revenue, net = field("Total Revenue"), field("Net Income")
|
|
90
|
+
out["quarters"].append({
|
|
91
|
+
"period": str(column)[:10], "revenue": revenue, "net_income": net,
|
|
92
|
+
"eps": field("Diluted EPS"),
|
|
93
|
+
"net_margin": net / revenue * 100 if revenue and net is not None else None,
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
if not (out["history"] or out["quarters"] or out["next"]):
|
|
97
|
+
return {"success": False, "symbol": symbol.upper(),
|
|
98
|
+
"error": f"No earnings data for {symbol.upper()} (ETFs, indices and some non-US listings have none)"}
|
|
99
|
+
return out
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _money(value: Optional[float]) -> str:
|
|
103
|
+
if value is None:
|
|
104
|
+
return "—"
|
|
105
|
+
for size, unit in ((1e12, "T"), (1e9, "B"), (1e6, "M")):
|
|
106
|
+
if abs(value) >= size:
|
|
107
|
+
return f"{value / size:.2f}{unit}"
|
|
108
|
+
return f"{value:,.0f}"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def render_earnings(data: dict, console, lang: str = "en") -> None:
|
|
112
|
+
from rich.padding import Padding
|
|
113
|
+
from rich.table import Table
|
|
114
|
+
|
|
115
|
+
zh = str(lang).lower().startswith("zh")
|
|
116
|
+
T = (lambda a, b: a) if zh else (lambda a, b: b)
|
|
117
|
+
symbol = data.get("symbol", "")
|
|
118
|
+
console.print()
|
|
119
|
+
console.print(f" [bold]{symbol} {T('财报', 'earnings')}[/bold] [dim]yfinance[/dim]")
|
|
120
|
+
|
|
121
|
+
nxt = data.get("next") or {}
|
|
122
|
+
if nxt.get("date"):
|
|
123
|
+
parts = [f"{T('下次财报', 'Next report')} {nxt['date']}"]
|
|
124
|
+
if nxt.get("eps_avg") is not None:
|
|
125
|
+
band = (f" ({nxt['eps_low']:.2f}–{nxt['eps_high']:.2f})"
|
|
126
|
+
if nxt.get("eps_low") is not None and nxt.get("eps_high") is not None else "")
|
|
127
|
+
parts.append(f"{T('EPS 一致预期', 'EPS consensus')} {nxt['eps_avg']:.2f}{band}")
|
|
128
|
+
if nxt.get("revenue_avg"):
|
|
129
|
+
parts.append(f"{T('营收预期', 'revenue consensus')} {_money(nxt['revenue_avg'])}")
|
|
130
|
+
console.print(" [dim]" + " · ".join(parts) + "[/dim]")
|
|
131
|
+
|
|
132
|
+
history = data.get("history") or []
|
|
133
|
+
if history:
|
|
134
|
+
console.print()
|
|
135
|
+
table = Table(box=None, show_header=True, header_style="bold", padding=(0, 2))
|
|
136
|
+
table.add_column(T("公布日", "Reported"))
|
|
137
|
+
table.add_column(T("EPS 预期", "EPS est."), justify="right")
|
|
138
|
+
table.add_column(T("EPS 实际", "EPS actual"), justify="right")
|
|
139
|
+
table.add_column(T("超预期", "Surprise"), justify="right")
|
|
140
|
+
for row in history:
|
|
141
|
+
surprise = row.get("surprise_pct")
|
|
142
|
+
colour = "green" if (surprise or 0) > 0 else "red" if (surprise or 0) < 0 else "dim"
|
|
143
|
+
table.add_row(row["date"], f"{row['estimate']:.2f}" if row.get("estimate") is not None else "—",
|
|
144
|
+
f"{row['reported']:.2f}",
|
|
145
|
+
f"[{colour}]{surprise:+.1f}%[/{colour}]" if surprise is not None else "—")
|
|
146
|
+
console.print(Padding(table, (0, 0, 0, 2)))
|
|
147
|
+
beats = sum(1 for r in history if (r.get("surprise_pct") or 0) > 0)
|
|
148
|
+
console.print(f" [dim]{T(f'近 {len(history)} 次中 {beats} 次超预期', f'Beat the estimate in {beats} of the last {len(history)}')}[/dim]")
|
|
149
|
+
|
|
150
|
+
quarters = data.get("quarters") or []
|
|
151
|
+
if quarters:
|
|
152
|
+
console.print()
|
|
153
|
+
table = Table(box=None, show_header=True, header_style="bold", padding=(0, 2))
|
|
154
|
+
table.add_column(T("季度末", "Quarter"))
|
|
155
|
+
table.add_column(T("营收", "Revenue"), justify="right")
|
|
156
|
+
table.add_column(T("净利润", "Net income"), justify="right")
|
|
157
|
+
table.add_column(T("净利率", "Net margin"), justify="right")
|
|
158
|
+
table.add_column(T("稀释 EPS (GAAP)", "Diluted EPS (GAAP)"), justify="right")
|
|
159
|
+
for q in quarters:
|
|
160
|
+
table.add_row(q["period"], _money(q.get("revenue")), _money(q.get("net_income")),
|
|
161
|
+
f"{q['net_margin']:.1f}%" if q.get("net_margin") is not None else "—",
|
|
162
|
+
f"{q['eps']:.2f}" if q.get("eps") is not None else "—")
|
|
163
|
+
console.print(Padding(table, (0, 0, 0, 2)))
|
|
164
|
+
console.print(f" [dim]{T('上表 EPS 为公司公布的调整后口径,此处为 GAAP 稀释口径,两者可能不同', 'EPS above is as reported against estimates (adjusted); here it is GAAP diluted, which can differ')}[/dim]")
|
|
165
|
+
if len(quarters) == 5 and quarters[0].get("revenue") and quarters[4].get("revenue"):
|
|
166
|
+
yoy = quarters[0]["revenue"] / quarters[4]["revenue"] * 100 - 100
|
|
167
|
+
console.print(f" [dim]{T('最近一季营收同比', 'Latest quarter revenue year on year')} {yoy:+.1f}%[/dim]")
|
|
168
|
+
console.print(f" [dim]{T('完整报告:', 'Full report: ')}/earnings {symbol} --report[/dim]")
|
|
169
|
+
console.print()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
__all__ = ["fetch_earnings", "render_earnings"]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""The server an install talks to, and the old default that stranded it on localhost.
|
|
2
|
+
|
|
3
|
+
``default_config()`` used to save ``api_url = http://localhost:8000``. That
|
|
4
|
+
looked like a default but was written into every user's config.json on first
|
|
5
|
+
run, where it became indistinguishable from a choice. Anyone who installed
|
|
6
|
+
from GitHub then had every backend call, and Google sign-in, aimed at a
|
|
7
|
+
developer backend on their own machine: a refused connection, or on a machine
|
|
8
|
+
that happened to run one, a page reading ``{"detail":"Not Found"}``. Fixing the
|
|
9
|
+
default in code cannot reach a config that already holds the old value, so the
|
|
10
|
+
old default is recognised here and replaced once, written back and announced,
|
|
11
|
+
the same way a retired model is (see model_retirement).
|
|
12
|
+
|
|
13
|
+
What counts as a choice and is left alone:
|
|
14
|
+
|
|
15
|
+
* ``ARTHERA_API_URL`` in the environment, which now also overrides a saved value
|
|
16
|
+
(in memory only), so a developer with a real local backend sets it once;
|
|
17
|
+
* any ``api_url`` other than the exact old default, such as a staging server;
|
|
18
|
+
* ``api_url_pinned: true``, an escape hatch for someone who wants localhost.
|
|
19
|
+
|
|
20
|
+
``local_mode`` is not touched: choosing a local model is a different decision
|
|
21
|
+
from where the backend lives.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
from aria_code.service_endpoints import API_URL, current_url
|
|
28
|
+
|
|
29
|
+
PRODUCTION_API_URL = API_URL
|
|
30
|
+
LEGACY_DEFAULT_API_URL = "http://localhost:8000"
|
|
31
|
+
# The product moved from arthera.finance to arthera.xyz and the old domain no
|
|
32
|
+
# longer answers, so a config that was migrated to (or installed with) the old
|
|
33
|
+
# production address points at nothing. It is replaced the same way the
|
|
34
|
+
# localhost default is.
|
|
35
|
+
RETIRED_API_URLS = ("http://localhost:8000", "https://api.arthera.finance")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _norm(url: object) -> str:
|
|
39
|
+
return str(url or "").strip().rstrip("/").lower()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def migrate_config(config: dict) -> list[tuple[str, str, str]]:
|
|
43
|
+
"""Move the old localhost default to production in place; return (key, old, new)."""
|
|
44
|
+
old = config.get("api_url")
|
|
45
|
+
retired = bool(old) and current_url(str(old)) != str(old).strip().rstrip('/')
|
|
46
|
+
env = os.getenv("ARTHERA_API_URL", "").strip()
|
|
47
|
+
if env:
|
|
48
|
+
# An explicit environment choice wins over the file for this run only;
|
|
49
|
+
# nothing is rewritten, so unsetting the variable restores the file.
|
|
50
|
+
config["api_url"] = current_url(env)
|
|
51
|
+
return []
|
|
52
|
+
if retired:
|
|
53
|
+
config["api_url"] = PRODUCTION_API_URL
|
|
54
|
+
return [("api_url", str(old), PRODUCTION_API_URL)]
|
|
55
|
+
if config.get("api_url_pinned"):
|
|
56
|
+
return []
|
|
57
|
+
old = config.get("api_url")
|
|
58
|
+
if _norm(old) not in {LEGACY_DEFAULT_API_URL, "https://api.arthera.finance", "https://cn.arthera.finance"}:
|
|
59
|
+
return []
|
|
60
|
+
config["api_url"] = PRODUCTION_API_URL
|
|
61
|
+
return [("api_url", str(old), PRODUCTION_API_URL)]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
__all__ = ["LEGACY_DEFAULT_API_URL", "PRODUCTION_API_URL", "RETIRED_API_URLS", "migrate_config"]
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""JSON events for `aria-code -p … --format jsonl`, one per line, as the turn runs.
|
|
2
|
+
|
|
3
|
+
`-p --json` printed one document at the end, so a CI job saw nothing until the
|
|
4
|
+
turn was over and could not tell which tool ran, how long it took or where
|
|
5
|
+
it failed. With jsonl each step is a line on stdout as it happens:
|
|
6
|
+
|
|
7
|
+
{"type": "turn.started", "prompt": "...", "model": "...", "ts": ...}
|
|
8
|
+
{"type": "tool.started", "tool": "run_command", "params": {...}, "ts": ...}
|
|
9
|
+
{"type": "tool.completed", "tool": "run_command", "success": true, "elapsed_s": 1.2,
|
|
10
|
+
"error": "", "ts": ...}
|
|
11
|
+
{"type": "turn.completed", "success": true, "response": "...", "tools_used": [...], ...}
|
|
12
|
+
|
|
13
|
+
Tool parameters are shortened and secrets masked: a written file's contents
|
|
14
|
+
or a command's token have no place in a CI log.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import re
|
|
21
|
+
import time
|
|
22
|
+
from typing import Any, TextIO
|
|
23
|
+
|
|
24
|
+
from aria_code.apps.cli.runtime_consumer import MEASURED_ELAPSED_KEY, _redact_activity_text
|
|
25
|
+
|
|
26
|
+
PARAM_CHARS = 300
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def json_safe(value: Any) -> Any:
|
|
30
|
+
"""``value`` with anything json cannot write turned into text."""
|
|
31
|
+
return json.loads(json.dumps(value, ensure_ascii=False, default=str))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
RESULT_CHARS = 20_000
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _full() -> bool:
|
|
38
|
+
"""ARIA_EVENTS_FULL=1: keep whole parameters and tool results (eval trajectories).
|
|
39
|
+
|
|
40
|
+
The default view is for logs and CI: short, file bodies by size. A
|
|
41
|
+
trajectory to learn from needs what was written and what came back.
|
|
42
|
+
"""
|
|
43
|
+
import os
|
|
44
|
+
|
|
45
|
+
return os.environ.get("ARIA_EVENTS_FULL", "").strip() in ("1", "true", "yes")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_SECRET = re.compile(
|
|
49
|
+
r"(?i)\b(api[_-]?key|token|password|passwd|secret)(\s*[=:]\s*)[^\s'\"]+"
|
|
50
|
+
r"|\b(bearer|basic)(\s+)[A-Za-z0-9._~+/=-]+"
|
|
51
|
+
r"|\b(?:sk|pk|rk)-[A-Za-z0-9_-]{8,}|\bgh[pousr]_[A-Za-z0-9]{12,}|\bAIza[0-9A-Za-z_-]{20,}"
|
|
52
|
+
r"|\bya29\.[0-9A-Za-z_-]+"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _mask_text(text: str) -> str:
|
|
57
|
+
"""Secret-shaped values masked; whitespace, indentation and newlines kept."""
|
|
58
|
+
def repl(match: "re.Match") -> str:
|
|
59
|
+
if match.group(1):
|
|
60
|
+
return f"{match.group(1)}{match.group(2)}***"
|
|
61
|
+
if match.group(3):
|
|
62
|
+
return f"{match.group(3)}{match.group(4)}***"
|
|
63
|
+
return "***"
|
|
64
|
+
return _SECRET.sub(repl, str(text))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _mask_value(value: Any) -> Any:
|
|
68
|
+
if isinstance(value, str):
|
|
69
|
+
return _mask_text(value)
|
|
70
|
+
if isinstance(value, dict):
|
|
71
|
+
return {k: _mask_value(v) for k, v in value.items() if not str(k).startswith("_")}
|
|
72
|
+
if isinstance(value, list):
|
|
73
|
+
return [_mask_value(v) for v in value]
|
|
74
|
+
return value
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _param_view(params: dict) -> dict:
|
|
78
|
+
if _full():
|
|
79
|
+
return _mask_value(json_safe(dict(params or {})))
|
|
80
|
+
view = {}
|
|
81
|
+
for key, value in (params or {}).items():
|
|
82
|
+
if str(key).startswith("_"):
|
|
83
|
+
continue # execution context the host added
|
|
84
|
+
if isinstance(value, str):
|
|
85
|
+
# A file's contents is not a parameter worth a log line; its size is.
|
|
86
|
+
view[key] = (f"<{len(value)} chars>" if len(value) > 2000
|
|
87
|
+
else _redact_activity_text(value, limit=PARAM_CHARS))
|
|
88
|
+
elif isinstance(value, (int, float, bool)) or value is None:
|
|
89
|
+
view[key] = value
|
|
90
|
+
else:
|
|
91
|
+
view[key] = _redact_activity_text(json.dumps(value, ensure_ascii=False, default=str),
|
|
92
|
+
limit=PARAM_CHARS)
|
|
93
|
+
return view
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ExecEvents:
|
|
97
|
+
"""Writes events to ``out``; does nothing when ``out`` is None."""
|
|
98
|
+
|
|
99
|
+
def __init__(self, out: TextIO | None):
|
|
100
|
+
self.out = out
|
|
101
|
+
# Preserve the established JSONL contract unless a streaming consumer
|
|
102
|
+
# opts in. Never emit provider reasoning; only visible answer tokens.
|
|
103
|
+
import os
|
|
104
|
+
self.streaming = out is not None and os.environ.get("ARIA_EVENTS_STREAM", "").strip().lower() in (
|
|
105
|
+
"1", "true", "yes",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def text_delta(self, text: str) -> None:
|
|
109
|
+
if self.streaming and text:
|
|
110
|
+
self.emit("answer.delta", text=text)
|
|
111
|
+
|
|
112
|
+
def status(self, state: str, message: str) -> None:
|
|
113
|
+
if self.streaming:
|
|
114
|
+
self.emit("turn.status", state=_redact_activity_text(state, limit=80),
|
|
115
|
+
message=_redact_activity_text(message, limit=PARAM_CHARS))
|
|
116
|
+
|
|
117
|
+
def emit(self, event_type: str, **fields: Any) -> None:
|
|
118
|
+
if self.out is None:
|
|
119
|
+
return
|
|
120
|
+
record = {"type": event_type, **json_safe(fields), "ts": round(time.time(), 3)}
|
|
121
|
+
try:
|
|
122
|
+
self.out.write(json.dumps(record, ensure_ascii=False) + "\n")
|
|
123
|
+
self.out.flush()
|
|
124
|
+
except (OSError, ValueError):
|
|
125
|
+
self.out = None # the reader went away; stop writing
|
|
126
|
+
|
|
127
|
+
def tool_started(self, tool: str, params: dict) -> None:
|
|
128
|
+
self.emit("tool.started", tool=tool, params=_param_view(params))
|
|
129
|
+
|
|
130
|
+
def tool_completed(self, tool: str, result: dict) -> None:
|
|
131
|
+
result = result if isinstance(result, dict) else {"success": bool(result)}
|
|
132
|
+
fields: dict[str, Any] = {"tool": tool, "success": bool(result.get("success", True))}
|
|
133
|
+
if isinstance(result.get(MEASURED_ELAPSED_KEY), (int, float)):
|
|
134
|
+
fields["elapsed_s"] = round(float(result[MEASURED_ELAPSED_KEY]), 3)
|
|
135
|
+
if result.get("error"):
|
|
136
|
+
fields["error"] = _redact_activity_text(result["error"], limit=PARAM_CHARS)
|
|
137
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
138
|
+
if result.get("reused") is True:
|
|
139
|
+
fields["reused"] = True
|
|
140
|
+
# Counts describe the returned data, never inferred model prose.
|
|
141
|
+
if tool == "list_files" and isinstance(data.get("items"), list):
|
|
142
|
+
fields["item_count"] = len(data["items"])
|
|
143
|
+
for key in ("exit_code", "path", "process_id", "change_id"):
|
|
144
|
+
if key in data:
|
|
145
|
+
fields[key] = data[key]
|
|
146
|
+
if _full():
|
|
147
|
+
masked = _mask_value(json_safe(result))
|
|
148
|
+
body = json.dumps(masked, ensure_ascii=False)
|
|
149
|
+
fields["result"] = masked if len(body) <= RESULT_CHARS else body[:RESULT_CHARS] + "…"
|
|
150
|
+
brief = _brief(tool, data)
|
|
151
|
+
if brief:
|
|
152
|
+
fields["brief"] = brief
|
|
153
|
+
self.emit("tool.completed", **fields)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _tool_name(tool: str) -> str:
|
|
157
|
+
return str(tool or "").rsplit("__", 1)[-1]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _brief_bodies(data: dict) -> list[dict]:
|
|
161
|
+
bodies = [data]
|
|
162
|
+
nested = data.get("data")
|
|
163
|
+
if isinstance(nested, dict):
|
|
164
|
+
bodies.append(nested)
|
|
165
|
+
metrics = data.get("metrics")
|
|
166
|
+
if isinstance(metrics, dict):
|
|
167
|
+
bodies.append(metrics)
|
|
168
|
+
content = data.get("content")
|
|
169
|
+
if isinstance(content, str) and content.lstrip().startswith("{"):
|
|
170
|
+
try:
|
|
171
|
+
inner = json.loads(content)
|
|
172
|
+
except json.JSONDecodeError:
|
|
173
|
+
inner = None
|
|
174
|
+
if isinstance(inner, dict):
|
|
175
|
+
bodies.append(inner)
|
|
176
|
+
if isinstance(inner.get("data"), dict):
|
|
177
|
+
bodies.append(inner["data"])
|
|
178
|
+
if isinstance(inner.get("metrics"), dict):
|
|
179
|
+
bodies.append(inner["metrics"])
|
|
180
|
+
return bodies
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _brief_num(body: dict, *keys: str) -> float | None:
|
|
184
|
+
for key in keys:
|
|
185
|
+
value = body.get(key)
|
|
186
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
187
|
+
continue
|
|
188
|
+
if value != value or value in (float("inf"), float("-inf")):
|
|
189
|
+
continue
|
|
190
|
+
return float(value)
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _brief_text(body: dict, *keys: str) -> str:
|
|
195
|
+
for key in keys:
|
|
196
|
+
value = body.get(key)
|
|
197
|
+
if not isinstance(value, str):
|
|
198
|
+
continue
|
|
199
|
+
text = " ".join(value.split())
|
|
200
|
+
if text:
|
|
201
|
+
return text[:24]
|
|
202
|
+
return ""
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _brief_percent(body: dict, fraction_key: str, pct_key: str) -> float | None:
|
|
206
|
+
direct = _brief_num(body, pct_key)
|
|
207
|
+
if direct is not None:
|
|
208
|
+
return round(direct, 2)
|
|
209
|
+
fraction = _brief_num(body, fraction_key)
|
|
210
|
+
if fraction is None:
|
|
211
|
+
return None
|
|
212
|
+
return round(fraction * 100, 2)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _brief(tool: str, data: dict) -> dict | None:
|
|
216
|
+
"""One terminal line of numbers the tool returned. Missing fields stay out."""
|
|
217
|
+
if not isinstance(data, dict) or not data:
|
|
218
|
+
return None
|
|
219
|
+
bodies = _brief_bodies(data)
|
|
220
|
+
for body in bodies:
|
|
221
|
+
diff = body.get("diff")
|
|
222
|
+
if isinstance(diff, str) and diff:
|
|
223
|
+
added = sum(1 for line in diff.splitlines() if line.startswith("+") and not line.startswith("+++"))
|
|
224
|
+
removed = sum(1 for line in diff.splitlines() if line.startswith("-") and not line.startswith("---"))
|
|
225
|
+
return {"kind": "edit", "added": added, "removed": removed}
|
|
226
|
+
name = _tool_name(tool)
|
|
227
|
+
if name in {"get_market_data", "get_crypto_data", "get_forex_data", "financial_quote"}:
|
|
228
|
+
for body in bodies:
|
|
229
|
+
price = _brief_num(body, "price", "latest_close", "current_price")
|
|
230
|
+
if price is None:
|
|
231
|
+
continue
|
|
232
|
+
brief: dict[str, Any] = {"kind": "quote", "price": price}
|
|
233
|
+
label = _brief_text(body, "name", "symbol")
|
|
234
|
+
if label:
|
|
235
|
+
brief["name"] = label
|
|
236
|
+
change = _brief_num(body, "change_pct", "change_pct_24h", "changePercent")
|
|
237
|
+
if change is not None:
|
|
238
|
+
brief["change_pct"] = change
|
|
239
|
+
if isinstance(body.get("stale"), bool):
|
|
240
|
+
brief["stale"] = body["stale"]
|
|
241
|
+
return brief
|
|
242
|
+
if name in {"backtest_strategy", "cloud_backtest", "run_portfolio_backtest", "financial_backtest"}:
|
|
243
|
+
for body in bodies:
|
|
244
|
+
returned = _brief_percent(body, "total_return", "total_return_pct")
|
|
245
|
+
if returned is None:
|
|
246
|
+
continue
|
|
247
|
+
brief = {"kind": "backtest", "return_pct": returned}
|
|
248
|
+
drawdown = _brief_percent(body, "max_drawdown", "max_drawdown_pct")
|
|
249
|
+
if drawdown is not None:
|
|
250
|
+
brief["drawdown_pct"] = round(abs(drawdown), 2)
|
|
251
|
+
return brief
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
__all__ = ["ExecEvents", "json_safe"]
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Candidates for an `@` file reference, the way Claude Code and Codex offer them.
|
|
2
|
+
|
|
3
|
+
Typing `@` in a prompt names a file or folder. The old completion listed one
|
|
4
|
+
directory at a time, like a shell, which makes a file three levels down take
|
|
5
|
+
three Tabs. This searches the whole workspace instead: `@auth` finds
|
|
6
|
+
`src/api/auth.py`, `@rdme` finds `README.md`, and `@src/` lists what is in
|
|
7
|
+
`src/`. Files the project ignores (`.gitignore`) and build clutter are left out.
|
|
8
|
+
|
|
9
|
+
The result is a list of whole replacement texts: the prompt as typed, with the
|
|
10
|
+
`@token` replaced by one candidate. That is the shape the Go runtime has always
|
|
11
|
+
answered with, so the frontend has one way to read both.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
import time
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Dict, List, Optional, Tuple
|
|
21
|
+
|
|
22
|
+
#: How many candidates a request returns.
|
|
23
|
+
LIMIT = 8
|
|
24
|
+
|
|
25
|
+
_SKIP_DIRS = {
|
|
26
|
+
".git", "node_modules", "__pycache__", ".venv", "venv", "dist", "build", "target",
|
|
27
|
+
".mypy_cache", ".pytest_cache", ".ruff_cache", ".next", ".cache", "site-packages",
|
|
28
|
+
}
|
|
29
|
+
_MAX_FILES = 20000
|
|
30
|
+
_TTL_SECONDS = 10.0
|
|
31
|
+
_cache: Dict[str, Tuple[float, List[str]]] = {}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _git_files(root: Path) -> Optional[List[str]]:
|
|
35
|
+
if not (root / ".git").exists():
|
|
36
|
+
return None
|
|
37
|
+
try:
|
|
38
|
+
out = subprocess.run(
|
|
39
|
+
["git", "-C", str(root), "ls-files", "-co", "--exclude-standard", "-z"],
|
|
40
|
+
capture_output=True, timeout=3, check=True,
|
|
41
|
+
).stdout
|
|
42
|
+
except (OSError, subprocess.SubprocessError):
|
|
43
|
+
return None
|
|
44
|
+
return [p for p in out.decode("utf-8", "replace").split("\0") if p][:_MAX_FILES]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _walk_files(root: Path) -> List[str]:
|
|
48
|
+
found: List[str] = []
|
|
49
|
+
for current, dirs, names in os.walk(root):
|
|
50
|
+
dirs[:] = sorted(d for d in dirs if d not in _SKIP_DIRS and not d.startswith("."))
|
|
51
|
+
base = Path(current).relative_to(root)
|
|
52
|
+
for name in sorted(names):
|
|
53
|
+
if name.startswith(".") and name not in (".gitignore", ".env.example"):
|
|
54
|
+
continue
|
|
55
|
+
found.append((base / name).as_posix())
|
|
56
|
+
if len(found) >= _MAX_FILES:
|
|
57
|
+
return found
|
|
58
|
+
return found
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def workspace_files(root: Path, now: Optional[float] = None) -> List[str]:
|
|
62
|
+
"""Every file in the workspace (relative, `/`-separated), cached for a few seconds."""
|
|
63
|
+
key = str(root)
|
|
64
|
+
stamp = time.monotonic() if now is None else now
|
|
65
|
+
hit = _cache.get(key)
|
|
66
|
+
if hit and stamp - hit[0] < _TTL_SECONDS:
|
|
67
|
+
return hit[1]
|
|
68
|
+
files = _git_files(root)
|
|
69
|
+
if files is None:
|
|
70
|
+
files = _walk_files(root)
|
|
71
|
+
_cache[key] = (stamp, files)
|
|
72
|
+
return files
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _token(text: str) -> Optional[Tuple[int, str]]:
|
|
76
|
+
"""Where the `@token` starts and what has been typed of it, or None."""
|
|
77
|
+
start = text.rfind("@")
|
|
78
|
+
if start < 0 or (start > 0 and text[start - 1] not in " \t\n"):
|
|
79
|
+
return None
|
|
80
|
+
token = text[start + 1:]
|
|
81
|
+
if token.startswith("file:"):
|
|
82
|
+
token = token[5:]
|
|
83
|
+
token = token.lstrip('"')
|
|
84
|
+
if any(c in token for c in "\n\r\t"):
|
|
85
|
+
return None
|
|
86
|
+
return start, token
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _is_subsequence(needle: str, hay: str) -> bool:
|
|
90
|
+
it = iter(hay)
|
|
91
|
+
return all(c in it for c in needle)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _children(files: List[str], prefix: str) -> List[str]:
|
|
95
|
+
"""What sits directly inside `prefix` ('' for the top level): folders end in `/`."""
|
|
96
|
+
seen: Dict[str, None] = {}
|
|
97
|
+
for path in files:
|
|
98
|
+
if not path.startswith(prefix):
|
|
99
|
+
continue
|
|
100
|
+
rest = path[len(prefix):]
|
|
101
|
+
if not rest:
|
|
102
|
+
continue
|
|
103
|
+
head, sep, _ = rest.partition("/")
|
|
104
|
+
seen[prefix + head + ("/" if sep else "")] = None
|
|
105
|
+
entries = list(seen)
|
|
106
|
+
return sorted(entries, key=lambda e: (not e.endswith("/"), e.lower()))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _search(files: List[str], query: str) -> List[str]:
|
|
110
|
+
q = query.lower()
|
|
111
|
+
dirs: Dict[str, None] = {}
|
|
112
|
+
for path in files:
|
|
113
|
+
parts = path.split("/")
|
|
114
|
+
for i in range(1, len(parts)):
|
|
115
|
+
dirs["/".join(parts[:i]) + "/"] = None
|
|
116
|
+
scored: List[Tuple[int, int, str]] = []
|
|
117
|
+
for path in [*files, *dirs]:
|
|
118
|
+
low = path.lower()
|
|
119
|
+
bare = low.rstrip("/")
|
|
120
|
+
base = bare.rsplit("/", 1)[-1]
|
|
121
|
+
if base.startswith(q):
|
|
122
|
+
tier = 0
|
|
123
|
+
elif low.startswith(q):
|
|
124
|
+
tier = 1
|
|
125
|
+
elif q in base:
|
|
126
|
+
tier = 2
|
|
127
|
+
elif q in low:
|
|
128
|
+
tier = 3
|
|
129
|
+
elif _is_subsequence(q, base):
|
|
130
|
+
tier = 4
|
|
131
|
+
elif _is_subsequence(q, low):
|
|
132
|
+
tier = 5
|
|
133
|
+
else:
|
|
134
|
+
continue
|
|
135
|
+
scored.append((tier, len(path), path))
|
|
136
|
+
scored.sort()
|
|
137
|
+
return [path for _, _, path in scored]
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def reference_candidates(text: str, root: Path, limit: int = LIMIT) -> List[str]:
|
|
141
|
+
"""Replacement prompts for the `@token` at the end of `text`, best first."""
|
|
142
|
+
found = _token(text)
|
|
143
|
+
if found is None:
|
|
144
|
+
return []
|
|
145
|
+
start, token = found
|
|
146
|
+
files = workspace_files(root)
|
|
147
|
+
if token == "" or token.endswith("/"):
|
|
148
|
+
paths = _children(files, token)
|
|
149
|
+
else:
|
|
150
|
+
paths = _search(files, token)
|
|
151
|
+
out: List[str] = []
|
|
152
|
+
for path in paths[:limit]:
|
|
153
|
+
shown = f'"{path}"' if " " in path else path
|
|
154
|
+
out.append(f"{text[:start]}@{shown}")
|
|
155
|
+
return out
|