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,156 @@
|
|
|
1
|
+
"""The logistics tools as an MCP server, so the Go engine can use them.
|
|
2
|
+
|
|
3
|
+
aria-logistics-mcp [--data-dir DIR]
|
|
4
|
+
|
|
5
|
+
Speaks MCP over stdio (newline-delimited JSON-RPC 2.0). Three read-only
|
|
6
|
+
tools: analyze_logistics_data, score_carriers, plan_inventory_policy.
|
|
7
|
+
|
|
8
|
+
Shipper scope is not the model's choice. The server reads ARIA_OWNER_SCOPE
|
|
9
|
+
from its own environment (the engine passes it down from the bot that started
|
|
10
|
+
the turn) and every tool goes through logistics.tenancy, which fails closed.
|
|
11
|
+
|
|
12
|
+
A `file_path` argument is read only from inside the data folder
|
|
13
|
+
(`--data-dir`, else ARIA_LOGISTICS_DATA_DIR). With no data folder, files are
|
|
14
|
+
refused and the tools take inline records only: a chat must not be able to
|
|
15
|
+
name any file on the host.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, Callable, Dict, Optional, TextIO
|
|
25
|
+
|
|
26
|
+
from .carriers import SCHEMA as CARRIER_SCHEMA, tool_score_carriers
|
|
27
|
+
from .freight import tool_analyze_logistics_data
|
|
28
|
+
from .inventory import SCHEMA as INVENTORY_SCHEMA, tool_plan_inventory_policy
|
|
29
|
+
from .tenancy import OWNER_SCOPE_ENV, owner_params_schema
|
|
30
|
+
|
|
31
|
+
PROTOCOL_VERSION = "2024-11-05"
|
|
32
|
+
DATA_DIR_ENV = "ARIA_LOGISTICS_DATA_DIR"
|
|
33
|
+
|
|
34
|
+
FREIGHT_SCHEMA = {
|
|
35
|
+
"name": "analyze_logistics_data",
|
|
36
|
+
"description": "Analyze actual waybills from supplied records, a CSV/JSON file, or an existing local database",
|
|
37
|
+
"parameters": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"file_path": {"type": "string", "description": "CSV or JSON waybill file path"},
|
|
41
|
+
"waybills": {"type": "array", "description": "Waybill records with total_cost"},
|
|
42
|
+
**owner_params_schema(),
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
TOOLS: Dict[str, tuple[Callable[[Dict[str, Any]], Dict[str, Any]], Dict[str, Any]]] = {
|
|
48
|
+
"analyze_logistics_data": (tool_analyze_logistics_data, FREIGHT_SCHEMA),
|
|
49
|
+
"score_carriers": (tool_score_carriers, CARRIER_SCHEMA),
|
|
50
|
+
"plan_inventory_policy": (tool_plan_inventory_policy, INVENTORY_SCHEMA),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def tool_list() -> list[Dict[str, Any]]:
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
"name": name,
|
|
58
|
+
"description": schema["description"],
|
|
59
|
+
"inputSchema": schema.get("parameters") or {"type": "object", "properties": {}},
|
|
60
|
+
"annotations": {"readOnlyHint": True, "openWorldHint": False},
|
|
61
|
+
}
|
|
62
|
+
for name, (_, schema) in TOOLS.items()
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def confine(arguments: Dict[str, Any], data_dir: Optional[Path]) -> Dict[str, Any]:
|
|
67
|
+
"""Resolve file_path inside the data folder, or refuse it."""
|
|
68
|
+
file_path = arguments.get("file_path")
|
|
69
|
+
if not file_path:
|
|
70
|
+
return arguments
|
|
71
|
+
if data_dir is None:
|
|
72
|
+
raise ValueError(f"Files are not available here; set {DATA_DIR_ENV} or pass records inline")
|
|
73
|
+
root = data_dir.resolve()
|
|
74
|
+
candidate = Path(str(file_path)).expanduser()
|
|
75
|
+
path = (candidate if candidate.is_absolute() else root / candidate).resolve()
|
|
76
|
+
if path != root and root not in path.parents:
|
|
77
|
+
raise ValueError("file_path must be inside the logistics data folder")
|
|
78
|
+
return {**arguments, "file_path": str(path)}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def call_tool(name: str, arguments: Any, data_dir: Optional[Path]) -> Dict[str, Any]:
|
|
82
|
+
entry = TOOLS.get(name)
|
|
83
|
+
if entry is None:
|
|
84
|
+
return {"content": [{"type": "text", "text": f"Unknown tool: {name}"}], "isError": True}
|
|
85
|
+
if not isinstance(arguments, dict):
|
|
86
|
+
arguments = {}
|
|
87
|
+
try:
|
|
88
|
+
result = entry[0](confine(arguments, data_dir))
|
|
89
|
+
except ValueError as exc:
|
|
90
|
+
result = {"success": False, "error": str(exc)}
|
|
91
|
+
except Exception as exc: # a tool bug must not take the server down
|
|
92
|
+
result = {"success": False, "error": f"{type(exc).__name__}: {exc}"}
|
|
93
|
+
failed = isinstance(result, dict) and result.get("success") is False
|
|
94
|
+
text = json.dumps(result, ensure_ascii=False, default=str)
|
|
95
|
+
return {"content": [{"type": "text", "text": text}], "isError": failed}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def handle(message: Dict[str, Any], data_dir: Optional[Path]) -> Optional[Dict[str, Any]]:
|
|
99
|
+
method = message.get("method")
|
|
100
|
+
if "id" not in message:
|
|
101
|
+
return None # a notification
|
|
102
|
+
reply: Dict[str, Any] = {"jsonrpc": "2.0", "id": message["id"]}
|
|
103
|
+
params = message.get("params") or {}
|
|
104
|
+
if method == "initialize":
|
|
105
|
+
scope = os.environ.get(OWNER_SCOPE_ENV, "").strip()
|
|
106
|
+
reply["result"] = {
|
|
107
|
+
"protocolVersion": PROTOCOL_VERSION,
|
|
108
|
+
"capabilities": {"tools": {}},
|
|
109
|
+
"serverInfo": {"name": "aria-logistics", "version": "0.1.0"},
|
|
110
|
+
"instructions": "Logistics analyses scoped to one shipper." if scope else
|
|
111
|
+
"Logistics analyses; data spanning shippers needs owner_id or all_owners.",
|
|
112
|
+
}
|
|
113
|
+
elif method == "ping":
|
|
114
|
+
reply["result"] = {}
|
|
115
|
+
elif method == "tools/list":
|
|
116
|
+
reply["result"] = {"tools": tool_list()}
|
|
117
|
+
elif method == "tools/call":
|
|
118
|
+
reply["result"] = call_tool(str(params.get("name", "")), params.get("arguments"), data_dir)
|
|
119
|
+
else:
|
|
120
|
+
reply["error"] = {"code": -32601, "message": f"Method not found: {method}"}
|
|
121
|
+
return reply
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def serve(stdin: TextIO, stdout: TextIO, data_dir: Optional[Path]) -> None:
|
|
125
|
+
for line in stdin:
|
|
126
|
+
line = line.strip()
|
|
127
|
+
if not line:
|
|
128
|
+
continue
|
|
129
|
+
try:
|
|
130
|
+
message = json.loads(line)
|
|
131
|
+
except json.JSONDecodeError:
|
|
132
|
+
reply: Optional[Dict[str, Any]] = {"jsonrpc": "2.0", "id": None,
|
|
133
|
+
"error": {"code": -32700, "message": "Parse error"}}
|
|
134
|
+
else:
|
|
135
|
+
reply = handle(message, data_dir) if isinstance(message, dict) else None
|
|
136
|
+
if reply is not None:
|
|
137
|
+
stdout.write(json.dumps(reply, ensure_ascii=False) + "\n")
|
|
138
|
+
stdout.flush()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def main(argv: Optional[list[str]] = None) -> int:
|
|
142
|
+
parser = argparse.ArgumentParser(prog="aria-logistics-mcp", description=__doc__.splitlines()[0])
|
|
143
|
+
parser.add_argument("--data-dir", default=os.environ.get(DATA_DIR_ENV) or None,
|
|
144
|
+
help=f"folder file_path is read from (default ${DATA_DIR_ENV}; none: inline records only)")
|
|
145
|
+
args = parser.parse_args(argv)
|
|
146
|
+
data_dir = Path(args.data_dir).expanduser() if args.data_dir else None
|
|
147
|
+
# MCP is UTF-8 JSON lines. Windows' default (cp1252, CRLF) would crash on
|
|
148
|
+
# the first non-ASCII description and add \r to every line.
|
|
149
|
+
for stream in (sys.stdin, sys.stdout):
|
|
150
|
+
stream.reconfigure(encoding="utf-8", newline="\n") # type: ignore[union-attr]
|
|
151
|
+
serve(sys.stdin, sys.stdout, data_dir)
|
|
152
|
+
return 0
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Input loading and field validation shared by the logistics analyses.
|
|
2
|
+
|
|
3
|
+
Every analysis here reports figures a 3PL may bill or negotiate on, so the
|
|
4
|
+
rules are strict: a value that cannot be read is an error naming the row,
|
|
5
|
+
never a silent zero, and an empty input is an error, never a sample dataset.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import csv
|
|
11
|
+
import json
|
|
12
|
+
import math
|
|
13
|
+
import pathlib
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_records(params: dict[str, Any], key: str) -> tuple[list[dict[str, Any]], str]:
|
|
18
|
+
"""Records from params[key] or a CSV/JSON file, and a label for where they came from."""
|
|
19
|
+
inline = params.get(key)
|
|
20
|
+
file_path = params.get("file_path")
|
|
21
|
+
if inline is not None and file_path:
|
|
22
|
+
raise ValueError(f"Provide either file_path or {key}, not both")
|
|
23
|
+
if file_path:
|
|
24
|
+
path = pathlib.Path(str(file_path)).expanduser().resolve()
|
|
25
|
+
if not path.is_file():
|
|
26
|
+
# The OSError read "[Errno 2] No such file or directory:
|
|
27
|
+
# '/private/var/…/shipments.csv'". Say which files are there.
|
|
28
|
+
here = sorted(p.name for p in path.parent.glob("*") if p.suffix.lower() in (".csv", ".json"))[:6]
|
|
29
|
+
hint = f" CSV/JSON files in {path.parent.name or path.parent}: {', '.join(here)}" if here else ""
|
|
30
|
+
raise ValueError(f"No such file: {file_path}.{hint}")
|
|
31
|
+
suffix = path.suffix.lower()
|
|
32
|
+
if suffix == ".json":
|
|
33
|
+
content = json.loads(path.read_text(encoding="utf-8"))
|
|
34
|
+
records = content.get(key) if isinstance(content, dict) else content
|
|
35
|
+
elif suffix == ".csv":
|
|
36
|
+
with path.open(encoding="utf-8-sig", newline="") as handle:
|
|
37
|
+
records = list(csv.DictReader(handle))
|
|
38
|
+
else:
|
|
39
|
+
raise ValueError("Only CSV and JSON files are supported")
|
|
40
|
+
source = f"file:{path.name}"
|
|
41
|
+
else:
|
|
42
|
+
records = inline
|
|
43
|
+
source = "caller-supplied records"
|
|
44
|
+
if not isinstance(records, list) or not records:
|
|
45
|
+
raise ValueError(f"No {key} records supplied")
|
|
46
|
+
for row, record in enumerate(records, 1):
|
|
47
|
+
if not isinstance(record, dict):
|
|
48
|
+
raise ValueError(f"Row {row}: each record must be an object")
|
|
49
|
+
return records, source
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def number(record: dict[str, Any], field: str, row: int, *, required: bool = False,
|
|
53
|
+
positive: bool = False) -> float | None:
|
|
54
|
+
"""A finite, non-negative number, or None when absent and optional."""
|
|
55
|
+
value = record.get(field)
|
|
56
|
+
if value is None or (isinstance(value, str) and not value.strip()):
|
|
57
|
+
if required:
|
|
58
|
+
raise ValueError(f"Row {row}: {field} is required")
|
|
59
|
+
return None
|
|
60
|
+
if isinstance(value, bool):
|
|
61
|
+
raise ValueError(f"Row {row}: {field} must be a number")
|
|
62
|
+
try:
|
|
63
|
+
result = float(value)
|
|
64
|
+
except (TypeError, ValueError) as exc:
|
|
65
|
+
raise ValueError(f"Row {row}: {field} must be a number") from exc
|
|
66
|
+
if not math.isfinite(result) or result < 0:
|
|
67
|
+
raise ValueError(f"Row {row}: {field} must be finite and non-negative")
|
|
68
|
+
if positive and result == 0:
|
|
69
|
+
raise ValueError(f"Row {row}: {field} must be greater than zero")
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def flag(record: dict[str, Any], field: str, row: int) -> bool | None:
|
|
74
|
+
"""A yes/no field, accepting the spellings spreadsheets produce."""
|
|
75
|
+
value = record.get(field)
|
|
76
|
+
if value is None or (isinstance(value, str) and not value.strip()):
|
|
77
|
+
return None
|
|
78
|
+
if isinstance(value, bool):
|
|
79
|
+
return value
|
|
80
|
+
if value in (0, 1):
|
|
81
|
+
return bool(value)
|
|
82
|
+
if isinstance(value, str):
|
|
83
|
+
normalized = value.strip().lower()
|
|
84
|
+
if normalized in {"true", "yes", "y", "1", "是"}:
|
|
85
|
+
return True
|
|
86
|
+
if normalized in {"false", "no", "n", "0", "否"}:
|
|
87
|
+
return False
|
|
88
|
+
raise ValueError(f"Row {row}: {field} must be true/false")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def series(record: dict[str, Any], field: str, row: int) -> list[float] | None:
|
|
92
|
+
"""A list of non-negative numbers, from a JSON array or a CSV cell like "3;5;2"."""
|
|
93
|
+
value = record.get(field)
|
|
94
|
+
if value is None or (isinstance(value, str) and not value.strip()):
|
|
95
|
+
return None
|
|
96
|
+
if isinstance(value, str):
|
|
97
|
+
parts = [p for p in value.replace(",", ";").replace(" ", ";").split(";") if p]
|
|
98
|
+
elif isinstance(value, list):
|
|
99
|
+
parts = value
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError(f"Row {row}: {field} must be a list of numbers")
|
|
102
|
+
out: list[float] = []
|
|
103
|
+
for index, part in enumerate(parts, 1):
|
|
104
|
+
try:
|
|
105
|
+
item = float(part)
|
|
106
|
+
except (TypeError, ValueError) as exc:
|
|
107
|
+
raise ValueError(f"Row {row}: {field} item {index} must be a number") from exc
|
|
108
|
+
if not math.isfinite(item) or item < 0:
|
|
109
|
+
raise ValueError(f"Row {row}: {field} item {index} must be finite and non-negative")
|
|
110
|
+
out.append(item)
|
|
111
|
+
return out
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def text(record: dict[str, Any], field: str, row: int, *, required: bool = False) -> str:
|
|
115
|
+
value = record.get(field)
|
|
116
|
+
result = "" if value is None else str(value).strip()
|
|
117
|
+
if required and not result:
|
|
118
|
+
raise ValueError(f"Row {row}: {field} is required")
|
|
119
|
+
return result
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Shipper (货主) isolation for third-party-logistics analyses.
|
|
2
|
+
|
|
3
|
+
A 3PL holds many shippers' inventory and waybills side by side. An analysis
|
|
4
|
+
that sums them — an average cost per kg, a carrier ranking, a reorder list —
|
|
5
|
+
is computed from one client's commercial data and handed to another. That is
|
|
6
|
+
a data leak whether or not anyone notices, so the analyses do not decide it
|
|
7
|
+
case by case: every one of them goes through `scope_records` first, and it
|
|
8
|
+
fails closed.
|
|
9
|
+
|
|
10
|
+
- Records that carry no owner_id at all are single-tenant data, and pass.
|
|
11
|
+
- Records that all belong to one shipper pass.
|
|
12
|
+
- Records that span shippers are refused unless the caller names one
|
|
13
|
+
(`owner_id`, a client-facing analysis) or explicitly asks for the 3PL's
|
|
14
|
+
internal cross-shipper view (`all_owners=True`), which is marked as not
|
|
15
|
+
for client distribution.
|
|
16
|
+
- A mix of records with and without owner_id is refused, because there is
|
|
17
|
+
no safe way to attribute the unlabelled ones.
|
|
18
|
+
|
|
19
|
+
What an error message says is part of the guarantee: it may say how many
|
|
20
|
+
shippers the data spans, but never which, so a refusal does not itself
|
|
21
|
+
disclose another client's name.
|
|
22
|
+
|
|
23
|
+
Conversation scope
|
|
24
|
+
------------------
|
|
25
|
+
A chat bound to one shipper (a 3PL usually keeps one group per client) sets
|
|
26
|
+
ARIA_OWNER_SCOPE for every turn it runs. Then, whatever the model asks for:
|
|
27
|
+
- owner_id defaults to that shipper, and naming another is refused;
|
|
28
|
+
- all_owners is refused — the internal view has no place in a client chat;
|
|
29
|
+
- records without owner_id are refused, because nothing shows they are this
|
|
30
|
+
shipper's (a file on the host could be anyone's).
|
|
31
|
+
This is enforced here rather than in a prompt, so it holds for the CLI, the
|
|
32
|
+
MCP server and every chat channel alike, and does not depend on the model
|
|
33
|
+
choosing to comply.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import os
|
|
39
|
+
from typing import Any, Iterable
|
|
40
|
+
|
|
41
|
+
OWNER_FIELD = "owner_id"
|
|
42
|
+
OWNER_SCOPE_ENV = "ARIA_OWNER_SCOPE"
|
|
43
|
+
|
|
44
|
+
INTERNAL_MARKER = "INTERNAL — cross-shipper view; not for client distribution"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TenancyError(ValueError):
|
|
48
|
+
"""The records cannot be analysed without mixing shippers."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def active_owner_scope() -> str:
|
|
52
|
+
"""The shipper this process is confined to, or "" when unconfined."""
|
|
53
|
+
return os.environ.get(OWNER_SCOPE_ENV, "").strip()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _owner_of(record: dict[str, Any]) -> str:
|
|
57
|
+
value = record.get(OWNER_FIELD)
|
|
58
|
+
return "" if value is None else str(value).strip()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def scope_records(
|
|
62
|
+
records: Iterable[dict[str, Any]],
|
|
63
|
+
owner_id: str | None = None,
|
|
64
|
+
*,
|
|
65
|
+
all_owners: bool = False,
|
|
66
|
+
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
67
|
+
"""Return the records an analysis may use, and a description of the scope.
|
|
68
|
+
|
|
69
|
+
The scope dict is copied into every result, so a reader can always tell
|
|
70
|
+
whose data a number came from and whether it may be shown to a client.
|
|
71
|
+
"""
|
|
72
|
+
records = list(records)
|
|
73
|
+
wanted = (owner_id or "").strip()
|
|
74
|
+
if wanted and all_owners:
|
|
75
|
+
raise TenancyError("Pass owner_id or all_owners, not both")
|
|
76
|
+
|
|
77
|
+
scope = active_owner_scope()
|
|
78
|
+
if scope:
|
|
79
|
+
if all_owners:
|
|
80
|
+
raise TenancyError(
|
|
81
|
+
"This conversation is confined to one shipper; the cross-shipper view "
|
|
82
|
+
"is not available here"
|
|
83
|
+
)
|
|
84
|
+
if wanted and wanted != scope:
|
|
85
|
+
raise TenancyError("This conversation is confined to one shipper and cannot analyse another")
|
|
86
|
+
wanted = scope
|
|
87
|
+
|
|
88
|
+
labelled = [r for r in records if _owner_of(r)]
|
|
89
|
+
unlabelled = len(records) - len(labelled)
|
|
90
|
+
|
|
91
|
+
if not labelled:
|
|
92
|
+
if scope:
|
|
93
|
+
raise TenancyError(
|
|
94
|
+
f"This conversation is confined to one shipper, so every record must carry "
|
|
95
|
+
f"{OWNER_FIELD} to show it is theirs; these records have none"
|
|
96
|
+
)
|
|
97
|
+
if wanted:
|
|
98
|
+
raise TenancyError(
|
|
99
|
+
f"owner_id {wanted!r} was requested, but the records carry no {OWNER_FIELD}"
|
|
100
|
+
)
|
|
101
|
+
return records, {"mode": "single_tenant", "client_facing": True}
|
|
102
|
+
|
|
103
|
+
if unlabelled:
|
|
104
|
+
raise TenancyError(
|
|
105
|
+
f"{unlabelled} of {len(records)} records have no {OWNER_FIELD}; they cannot "
|
|
106
|
+
f"be attributed to a shipper, so they cannot be analysed alongside records "
|
|
107
|
+
f"that can"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
owners = {_owner_of(r) for r in labelled}
|
|
111
|
+
|
|
112
|
+
if wanted:
|
|
113
|
+
in_scope = [r for r in labelled if _owner_of(r) == wanted]
|
|
114
|
+
if not in_scope:
|
|
115
|
+
# Deliberately does not list the owners that *are* present.
|
|
116
|
+
raise TenancyError(f"No records for owner_id {wanted!r}")
|
|
117
|
+
result_scope = {"mode": "single_owner", "owner_id": wanted, "client_facing": True}
|
|
118
|
+
if scope:
|
|
119
|
+
result_scope["conversation_scoped"] = True
|
|
120
|
+
return in_scope, result_scope
|
|
121
|
+
|
|
122
|
+
if len(owners) == 1:
|
|
123
|
+
(only,) = owners
|
|
124
|
+
return labelled, {"mode": "single_owner", "owner_id": only, "client_facing": True}
|
|
125
|
+
|
|
126
|
+
if all_owners:
|
|
127
|
+
return labelled, {
|
|
128
|
+
"mode": "all_owners",
|
|
129
|
+
"shipper_count": len(owners),
|
|
130
|
+
"client_facing": False,
|
|
131
|
+
"marker": INTERNAL_MARKER,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
raise TenancyError(
|
|
135
|
+
f"These records belong to {len(owners)} different shippers. Pass owner_id to "
|
|
136
|
+
f"analyse one shipper (safe to share with that client), or all_owners=true for "
|
|
137
|
+
f"the internal cross-shipper view (not for client distribution)."
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def owner_params_schema() -> dict[str, Any]:
|
|
142
|
+
"""JSON-schema properties every tenant-aware tool accepts."""
|
|
143
|
+
return {
|
|
144
|
+
"owner_id": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"description": "Shipper (货主) to analyse. Required when records span several shippers.",
|
|
147
|
+
},
|
|
148
|
+
"all_owners": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"description": "Internal 3PL view across all shippers. Output is marked not for client distribution.",
|
|
151
|
+
},
|
|
152
|
+
}
|
aria_bots/market.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Market commands for the bots, answered by `aria-runtime daemon`.
|
|
2
|
+
|
|
3
|
+
The bots used to import the Python daemon's internals (its database path,
|
|
4
|
+
price fetcher and brief builders). Now they run the Go daemon's commands:
|
|
5
|
+
|
|
6
|
+
aria-runtime daemon quote SYMBOL --json
|
|
7
|
+
aria-runtime daemon once morning-brief --json
|
|
8
|
+
aria-runtime daemon alert add SYMBOL CONDITION VALUE
|
|
9
|
+
aria-runtime daemon alert list
|
|
10
|
+
aria-runtime daemon status
|
|
11
|
+
|
|
12
|
+
Quotes come from financial-service; alerts land in the same daemon.db the
|
|
13
|
+
running daemon watches (ARIA_DAEMON_DIR, else ~/.aria).
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import json
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
from . import engine
|
|
22
|
+
|
|
23
|
+
CONDITIONS = ("pct_change_above", "pct_change_below", "price_above", "price_below")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MarketError(RuntimeError):
|
|
27
|
+
"""The daemon command failed; the message is safe to show in a chat."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def daemon(*args: str, timeout: float = 60) -> str:
|
|
31
|
+
runtime = engine.find_engine()
|
|
32
|
+
if not runtime:
|
|
33
|
+
raise MarketError("未找到 aria-runtime:请安装,或用 ARIA_RUNTIME 指向它。")
|
|
34
|
+
proc = await asyncio.create_subprocess_exec(
|
|
35
|
+
runtime, "daemon", *args,
|
|
36
|
+
stdin=asyncio.subprocess.DEVNULL,
|
|
37
|
+
stdout=asyncio.subprocess.PIPE,
|
|
38
|
+
stderr=asyncio.subprocess.PIPE,
|
|
39
|
+
)
|
|
40
|
+
try:
|
|
41
|
+
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout)
|
|
42
|
+
except asyncio.TimeoutError:
|
|
43
|
+
proc.kill()
|
|
44
|
+
await proc.wait()
|
|
45
|
+
raise MarketError(f"aria-runtime daemon {args[0]} 超时(>{int(timeout)}s)") from None
|
|
46
|
+
if proc.returncode != 0:
|
|
47
|
+
detail = stderr.decode("utf-8", errors="replace").strip()[-300:] or f"exit {proc.returncode}"
|
|
48
|
+
raise MarketError(detail)
|
|
49
|
+
return stdout.decode("utf-8", errors="replace").strip()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def quote(symbol: str) -> dict:
|
|
53
|
+
"""{symbol, price, currency, source[, change_pct]}."""
|
|
54
|
+
return json.loads(await daemon("quote", symbol, "--json"))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def brief(command: str = "morning-brief") -> tuple[str, str]:
|
|
58
|
+
"""(title, body) of a brief: morning-brief, evening-brief or weekly-recap."""
|
|
59
|
+
data = json.loads(await daemon("once", command, "--json", timeout=120))
|
|
60
|
+
return data["title"], data["body"]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def add_alert(symbol: str, condition: str, value: str) -> str:
|
|
64
|
+
if condition not in CONDITIONS:
|
|
65
|
+
raise MarketError("条件必须是 " + " / ".join(CONDITIONS))
|
|
66
|
+
return await daemon("alert", "add", symbol, condition, value)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def list_alerts() -> str:
|
|
70
|
+
return await daemon("alert", "list")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def status() -> str:
|
|
74
|
+
return await daemon("status")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def price_card(data: dict) -> tuple[str, str, str]:
|
|
78
|
+
"""(title, body, color) for a quote card. Red is up, as in Chinese markets."""
|
|
79
|
+
symbol, price = data["symbol"], float(data["price"])
|
|
80
|
+
mark = {"CNY": "¥", "RMB": "¥", "USD": "$", "HKD": "HK$"}.get(str(data.get("currency") or "").upper(), "")
|
|
81
|
+
change: Optional[float] = data.get("change_pct")
|
|
82
|
+
lines = [f"**当前价** {mark}{price:,.3f}"]
|
|
83
|
+
if change is None:
|
|
84
|
+
color, arrow = "blue", "📈"
|
|
85
|
+
else:
|
|
86
|
+
lines.append(f"**涨跌幅** {change:+.2f}%")
|
|
87
|
+
color = "red" if change > 0 else "green" if change < 0 else "blue"
|
|
88
|
+
arrow = "📈" if change >= 0 else "📉"
|
|
89
|
+
if data.get("source"):
|
|
90
|
+
lines.append(f"**来源** {data['source']}")
|
|
91
|
+
return f"{arrow} {symbol}", "\n".join(lines), color
|