aria-code 4.1.3__py3-none-any.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.
- agents/__init__.py +32 -0
- agents/base.py +190 -0
- agents/deep/__init__.py +37 -0
- agents/deep/calibration_loop.py +144 -0
- agents/deep/critic.py +125 -0
- agents/deep/deepen.py +193 -0
- agents/deep/models.py +149 -0
- agents/deep/pipeline.py +164 -0
- agents/deep/quant_fusion.py +192 -0
- agents/deep/themes.py +95 -0
- agents/deep/tiers.py +106 -0
- agents/financial/__init__.py +10 -0
- agents/financial/catalyst.py +279 -0
- agents/financial/debate.py +145 -0
- agents/financial/earnings.py +303 -0
- agents/financial/fundamental.py +159 -0
- agents/financial/macro.py +99 -0
- agents/financial/news.py +207 -0
- agents/financial/risk.py +132 -0
- agents/financial/sector.py +279 -0
- agents/financial/synthesis.py +274 -0
- agents/financial/technical.py +258 -0
- agents/portfolio_agent.py +333 -0
- agents/realty/__init__.py +62 -0
- agents/realty/asset_diagnosis.py +150 -0
- agents/realty/business_match.py +165 -0
- agents/realty/cashflow_verify.py +208 -0
- agents/realty/contract_rules.py +209 -0
- agents/realty/energy_anomaly.py +188 -0
- agents/realty/exit_settlement.py +207 -0
- agents/realty/fulfillment_risk.py +205 -0
- agents/realty/ops_optimize.py +159 -0
- agents/realty/revenue_share.py +214 -0
- agents/registry.py +144 -0
- agents/sports/__init__.py +0 -0
- agents/sports/football_agent.py +169 -0
- agents/team.py +289 -0
- aliyun_data_client.py +660 -0
- apps/README.md +12 -0
- apps/__init__.py +2 -0
- apps/channels/README.md +15 -0
- apps/cli/README.md +13 -0
- apps/cli/__init__.py +2 -0
- apps/cli/bootstrap.py +99 -0
- apps/cli/codegen_paths.py +29 -0
- apps/cli/commands/__init__.py +16 -0
- apps/cli/commands/analysis_cmds.py +288 -0
- apps/cli/commands/backtest_cmds.py +1887 -0
- apps/cli/commands/broker_cmds.py +1154 -0
- apps/cli/commands/business_workflow_cmds.py +289 -0
- apps/cli/commands/catalog.py +84 -0
- apps/cli/commands/data_cmds.py +405 -0
- apps/cli/commands/diagnostic_cmds.py +179 -0
- apps/cli/commands/diagnostic_ops_cmds.py +696 -0
- apps/cli/commands/finance_render.py +12 -0
- apps/cli/commands/market.py +399 -0
- apps/cli/commands/market_cmds.py +1276 -0
- apps/cli/commands/market_context.py +425 -0
- apps/cli/commands/market_render.py +7 -0
- apps/cli/commands/model_cmds.py +1579 -0
- apps/cli/commands/ops_cmds.py +668 -0
- apps/cli/commands/portfolio_cmds.py +962 -0
- apps/cli/commands/report.py +377 -0
- apps/cli/commands/scaffold_templates.py +617 -0
- apps/cli/commands/session_cmds.py +179 -0
- apps/cli/commands/session_ux_cmds.py +280 -0
- apps/cli/commands/team.py +588 -0
- apps/cli/commands/team_render.py +8 -0
- apps/cli/commands/ui_cmds.py +358 -0
- apps/cli/commands/workflow_cmds.py +279 -0
- apps/cli/commands/workspace_cmds.py +1414 -0
- apps/cli/config_paths.py +70 -0
- apps/cli/config_store.py +61 -0
- apps/cli/deterministic.py +122 -0
- apps/cli/direct.py +48 -0
- apps/cli/github_app_auth.py +135 -0
- apps/cli/handlers/__init__.py +11 -0
- apps/cli/handlers/broker_handlers.py +122 -0
- apps/cli/handlers/chart_handlers.py +1309 -0
- apps/cli/handlers/market_handlers.py +2509 -0
- apps/cli/handlers/realty_handlers.py +114 -0
- apps/cli/handlers/strategy_advice.py +82 -0
- apps/cli/hooks.py +180 -0
- apps/cli/i18n.py +284 -0
- apps/cli/intent.py +136 -0
- apps/cli/intent_router.py +217 -0
- apps/cli/lifecycle_hooks.py +48 -0
- apps/cli/main.py +29 -0
- apps/cli/market_metadata.py +135 -0
- apps/cli/market_universe.py +265 -0
- apps/cli/message_processing.py +257 -0
- apps/cli/plan_mode.py +139 -0
- apps/cli/plotly_html.py +15 -0
- apps/cli/prediction_feedback.py +202 -0
- apps/cli/preflight.py +497 -0
- apps/cli/project_aria.py +60 -0
- apps/cli/prompts/__init__.py +0 -0
- apps/cli/prompts/coding.py +658 -0
- apps/cli/prompts/system_prompts.py +531 -0
- apps/cli/prompts/ui.py +434 -0
- apps/cli/providers/__init__.py +1 -0
- apps/cli/providers/base.py +271 -0
- apps/cli/providers/chat_routing.py +80 -0
- apps/cli/providers/llm/__init__.py +1 -0
- apps/cli/providers/llm/ollama_stream.py +1170 -0
- apps/cli/providers/llm/sse_stream.py +216 -0
- apps/cli/providers/runtime_bridge.py +185 -0
- apps/cli/runtime_consumer.py +489 -0
- apps/cli/session_export.py +87 -0
- apps/cli/session_jsonl.py +207 -0
- apps/cli/session_store.py +112 -0
- apps/cli/todo_tracker.py +190 -0
- apps/cli/tools/__init__.py +40 -0
- apps/cli/tools/context.py +46 -0
- apps/cli/tools/file_tools.py +112 -0
- apps/cli/tools/market_tools.py +549 -0
- apps/cli/tools/notebook_tools.py +111 -0
- apps/cli/tools/system_tools.py +669 -0
- apps/cli/tools/write_tools.py +715 -0
- apps/cli/tradingview_bridge.py +434 -0
- apps/cli/update_check.py +152 -0
- apps/cli/utils/__init__.py +0 -0
- apps/cli/utils/market_detect.py +1578 -0
- apps/daemon/README.md +14 -0
- apps/vscode/README.md +115 -0
- apps/vscode/package.json +70 -0
- aria_cli.py +11636 -0
- aria_code-4.1.3.dist-info/METADATA +952 -0
- aria_code-4.1.3.dist-info/RECORD +284 -0
- aria_code-4.1.3.dist-info/WHEEL +5 -0
- aria_code-4.1.3.dist-info/entry_points.txt +2 -0
- aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
- aria_code-4.1.3.dist-info/top_level.txt +50 -0
- aria_daemon.py +1295 -0
- aria_feishu_bot.py +1359 -0
- aria_relay_client.py +182 -0
- aria_relay_server.py +405 -0
- aria_telegram_bot.py +202 -0
- ariarc.py +328 -0
- artifacts.py +491 -0
- backtest_report.py +472 -0
- brokers/__init__.py +72 -0
- brokers/base.py +207 -0
- brokers/capabilities.py +264 -0
- brokers/cn/__init__.py +10 -0
- brokers/cn/easytrader_broker.py +193 -0
- brokers/cn/futu_broker.py +194 -0
- brokers/cn/longbridge_broker.py +190 -0
- brokers/cn/tiger_broker.py +196 -0
- brokers/cn/xtquant_broker.py +175 -0
- brokers/config.py +364 -0
- brokers/intl/__init__.py +5 -0
- brokers/intl/alpaca_broker.py +183 -0
- brokers/intl/ibkr_broker.py +215 -0
- brokers/intl/webull_broker.py +156 -0
- brokers/paper_broker.py +259 -0
- brokers/planning.py +296 -0
- brokers/registry.py +181 -0
- brokers/trading.py +237 -0
- change_store.py +127 -0
- command_safety.py +19 -0
- computer_use_tools.py +504 -0
- dashboard_generator.py +578 -0
- data_analysis_tools.py +808 -0
- data_cleaner.py +483 -0
- data_service.py +481 -0
- datasources/__init__.py +23 -0
- datasources/base.py +166 -0
- datasources/router.py +221 -0
- datasources/sources/__init__.py +15 -0
- datasources/sources/akshare_source.py +269 -0
- datasources/sources/alpha_vantage_source.py +202 -0
- datasources/sources/edgar_source.py +218 -0
- datasources/sources/finnhub_source.py +197 -0
- datasources/sources/fred_source.py +219 -0
- datasources/sources/tushare_source.py +141 -0
- datasources/sources/web_scraper_source.py +278 -0
- datasources/sources/world_bank_source.py +205 -0
- datasources/sources/yfinance_source.py +152 -0
- demo_player.py +204 -0
- doctor.py +508 -0
- file_analysis_tools.py +734 -0
- finance_formulas.py +389 -0
- football_data_client.py +1670 -0
- intent_classifier.py +358 -0
- local_finance_tools.py +3221 -0
- local_llm_provider.py +552 -0
- macro_tools.py +368 -0
- market_data_client.py +1899 -0
- mcp_client.py +506 -0
- memory_manager.py +245 -0
- model_capability.py +416 -0
- notification_tools.py +248 -0
- packages/__init__.py +23 -0
- packages/aria_agents/__init__.py +5 -0
- packages/aria_agents/manifest.py +69 -0
- packages/aria_core/__init__.py +34 -0
- packages/aria_core/architecture.py +192 -0
- packages/aria_core/export.py +124 -0
- packages/aria_core/manifest.py +65 -0
- packages/aria_infra/__init__.py +15 -0
- packages/aria_infra/arthera.py +52 -0
- packages/aria_infra/doctor.py +246 -0
- packages/aria_infra/product.py +37 -0
- packages/aria_mcp/__init__.py +25 -0
- packages/aria_mcp/bridge.py +38 -0
- packages/aria_mcp/config.py +97 -0
- packages/aria_mcp/tools.py +61 -0
- packages/aria_sdk/__init__.py +19 -0
- packages/aria_sdk/client.py +396 -0
- packages/aria_sdk/providers.py +70 -0
- packages/aria_sdk/streaming.py +73 -0
- packages/aria_sdk/types.py +86 -0
- packages/aria_services/__init__.py +55 -0
- packages/aria_services/context.py +258 -0
- packages/aria_services/data.py +11 -0
- packages/aria_services/provider_health.py +189 -0
- packages/aria_services/registry.py +213 -0
- packages/aria_services/usage.py +138 -0
- packages/aria_skills/__init__.py +5 -0
- packages/aria_skills/registry.py +59 -0
- packages/aria_tools/__init__.py +5 -0
- packages/aria_tools/registry.py +128 -0
- packages/quant_engine/__init__.py +6 -0
- packages/quant_engine/sports/__init__.py +72 -0
- packages/quant_engine/sports/calibrator.py +353 -0
- packages/quant_engine/sports/dixon_coles.py +234 -0
- packages/quant_engine/sports/elo.py +299 -0
- packages/quant_engine/sports/form.py +188 -0
- packages/quant_engine/sports/h2h.py +195 -0
- packages/quant_engine/sports/ml_model.py +354 -0
- packages/quant_engine/sports/predictor.py +311 -0
- packages/quant_engine/sports/tracker.py +664 -0
- packages/quant_engine/stochastic/__init__.py +27 -0
- packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- packages/quant_engine/stochastic/ito_calculus.py +477 -0
- packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- packages/quant_engine/stochastic/options_pricing.py +573 -0
- packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- plan_utils.py +194 -0
- plugin_loader.py +328 -0
- portfolio_ledger.py +262 -0
- privacy/__init__.py +5 -0
- privacy/feedback.py +123 -0
- project_tools.py +525 -0
- providers/__init__.py +30 -0
- providers/llm/__init__.py +19 -0
- providers/llm/anthropic.py +184 -0
- providers/llm/base.py +139 -0
- providers/llm/ollama.py +128 -0
- providers/llm/openai_compat.py +282 -0
- providers/llm/registry.py +358 -0
- realty_data_tools.py +659 -0
- report_generator.py +1314 -0
- runtime/__init__.py +103 -0
- runtime/agent_loop.py +1183 -0
- runtime/approval.py +51 -0
- runtime/events.py +102 -0
- runtime/gateway.py +128 -0
- runtime/lsp.py +346 -0
- runtime/subagent.py +258 -0
- runtime/tool_executor.py +104 -0
- runtime/tool_policy.py +106 -0
- safety/__init__.py +21 -0
- safety/permissions.py +275 -0
- setup_wizard.py +653 -0
- strategy_vault.py +420 -0
- ui/__init__.py +100 -0
- ui/banner.py +310 -0
- ui/completer.py +391 -0
- ui/console.py +271 -0
- ui/image_render.py +243 -0
- ui/input_box.py +376 -0
- ui/picker.py +195 -0
- ui/render/__init__.py +11 -0
- ui/render/finance.py +1480 -0
- ui/render/market.py +225 -0
- ui/render/output.py +681 -0
- ui/render/team.py +346 -0
- ui/robot.py +235 -0
- workspace/__init__.py +6 -0
- workspace/files.py +170 -0
- workspace/verify.py +113 -0
apps/cli/preflight.py
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
"""Intent-aware dependency preflight for Aria CLI.
|
|
2
|
+
|
|
3
|
+
This module is intentionally pure and side-effect free: it detects likely user
|
|
4
|
+
intent, checks local availability, and returns install guidance. It never
|
|
5
|
+
installs packages or mutates config.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
import importlib.util
|
|
11
|
+
import os
|
|
12
|
+
import shlex
|
|
13
|
+
import shutil
|
|
14
|
+
from typing import Callable, Iterable, Mapping
|
|
15
|
+
|
|
16
|
+
from apps.cli.intent_router import build_intent_route, detect_intents
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
ModuleChecker = Callable[[str], bool]
|
|
20
|
+
CommandChecker = Callable[[str], bool]
|
|
21
|
+
EnvGetter = Callable[[str], str | None]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class PythonRequirement:
|
|
26
|
+
module: str
|
|
27
|
+
package: str
|
|
28
|
+
purpose: str
|
|
29
|
+
required: bool = True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class CommandRequirement:
|
|
34
|
+
command: str
|
|
35
|
+
install_hint: str
|
|
36
|
+
purpose: str
|
|
37
|
+
required: bool = True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class EnvRequirement:
|
|
42
|
+
name: str
|
|
43
|
+
purpose: str
|
|
44
|
+
required: bool = False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class IntentPreflight:
|
|
49
|
+
intents: tuple[str, ...]
|
|
50
|
+
services: tuple[str, ...]
|
|
51
|
+
python: tuple[PythonRequirement, ...]
|
|
52
|
+
commands: tuple[CommandRequirement, ...]
|
|
53
|
+
env: tuple[EnvRequirement, ...]
|
|
54
|
+
missing_python: tuple[PythonRequirement, ...]
|
|
55
|
+
missing_commands: tuple[CommandRequirement, ...]
|
|
56
|
+
missing_env: tuple[EnvRequirement, ...]
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def has_findings(self) -> bool:
|
|
60
|
+
return bool(self.missing_python or self.missing_commands or self.missing_env)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def has_required_findings(self) -> bool:
|
|
64
|
+
return any(req.required for req in self.missing_python + self.missing_commands + self.missing_env)
|
|
65
|
+
|
|
66
|
+
def pip_install_command(self) -> str:
|
|
67
|
+
packages: list[str] = []
|
|
68
|
+
seen: set[str] = set()
|
|
69
|
+
for req in self.missing_python:
|
|
70
|
+
if req.package not in seen:
|
|
71
|
+
packages.append(req.package)
|
|
72
|
+
seen.add(req.package)
|
|
73
|
+
if not packages:
|
|
74
|
+
return ""
|
|
75
|
+
return "python3 -m pip install " + " ".join(shlex.quote(pkg) for pkg in packages)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True)
|
|
79
|
+
class InstallPlan:
|
|
80
|
+
"""User-confirmed install guidance derived from an intent preflight."""
|
|
81
|
+
|
|
82
|
+
services: tuple[str, ...]
|
|
83
|
+
pip_packages: tuple[str, ...]
|
|
84
|
+
pip_command: str
|
|
85
|
+
command_hints: tuple[str, ...]
|
|
86
|
+
env_hints: tuple[str, ...]
|
|
87
|
+
has_required_items: bool
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def has_actions(self) -> bool:
|
|
91
|
+
return bool(self.pip_packages or self.command_hints or self.env_hints)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass(frozen=True)
|
|
95
|
+
class InstallSelection:
|
|
96
|
+
"""Resolved user selection for Python package installation."""
|
|
97
|
+
|
|
98
|
+
pip_packages: tuple[str, ...]
|
|
99
|
+
skipped_packages: tuple[str, ...] = ()
|
|
100
|
+
mode: str = "all"
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def has_packages(self) -> bool:
|
|
104
|
+
return bool(self.pip_packages)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
_PY_REQS: Mapping[str, PythonRequirement] = {
|
|
108
|
+
"aiohttp": PythonRequirement("aiohttp", "aiohttp", "异步 HTTP 请求"),
|
|
109
|
+
"requests": PythonRequirement("requests", "requests", "HTTP 请求"),
|
|
110
|
+
"pandas": PythonRequirement("pandas", "pandas", "表格与行情数据处理"),
|
|
111
|
+
"numpy": PythonRequirement("numpy", "numpy", "数值计算"),
|
|
112
|
+
"yfinance": PythonRequirement("yfinance", "yfinance", "美股/港股/ETF/加密行情"),
|
|
113
|
+
"akshare": PythonRequirement("akshare", "akshare", "A 股与中文市场数据"),
|
|
114
|
+
"matplotlib": PythonRequirement("matplotlib", "matplotlib", "PNG 静态图表渲染", required=False),
|
|
115
|
+
"mplfinance": PythonRequirement("mplfinance", "mplfinance", "K 线 PNG 渲染", required=False),
|
|
116
|
+
"PIL": PythonRequirement("PIL", "Pillow", "图片读取与剪贴板图片"),
|
|
117
|
+
"mss": PythonRequirement("mss", "mss", "屏幕截图"),
|
|
118
|
+
"pyautogui": PythonRequirement("pyautogui", "pyautogui", "桌面自动化"),
|
|
119
|
+
"playwright": PythonRequirement("playwright", "playwright", "浏览器自动化"),
|
|
120
|
+
"openpyxl": PythonRequirement("openpyxl", "openpyxl", "Excel 文件解析"),
|
|
121
|
+
"pdfplumber": PythonRequirement("pdfplumber", "pdfplumber", "PDF 文本与表格解析", required=False),
|
|
122
|
+
"pypdf": PythonRequirement("pypdf", "pypdf", "PDF 解析备用引擎", required=False),
|
|
123
|
+
"docx": PythonRequirement("docx", "python-docx", "Word/DOCX 文件解析"),
|
|
124
|
+
"bs4": PythonRequirement("bs4", "beautifulsoup4", "HTML 文件解析"),
|
|
125
|
+
"duckdb": PythonRequirement("duckdb", "duckdb", "本地分析型 SQL"),
|
|
126
|
+
"vectorbt": PythonRequirement("vectorbt", "vectorbt", "增强向量化回测", required=False),
|
|
127
|
+
"alpaca": PythonRequirement("alpaca", "alpaca-py", "Alpaca 券商连接"),
|
|
128
|
+
"tigeropen": PythonRequirement("tigeropen", "tigeropen", "Tiger 券商连接"),
|
|
129
|
+
"longbridge": PythonRequirement("longbridge", "longbridge", "Longbridge 券商连接"),
|
|
130
|
+
"ib_insync": PythonRequirement("ib_insync", "ib_insync", "IBKR 券商连接"),
|
|
131
|
+
"futu": PythonRequirement("futu", "futu-api", "富途券商连接"),
|
|
132
|
+
"webull": PythonRequirement("webull", "webull", "Webull 券商连接"),
|
|
133
|
+
"easytrader": PythonRequirement("easytrader", "easytrader", "A 股交易客户端连接"),
|
|
134
|
+
"ddgs": PythonRequirement("ddgs", "ddgs", "零配置联网搜索 (web_search 工具)", required=False),
|
|
135
|
+
"ccxt": PythonRequirement("ccxt", "ccxt", "加密货币交易所行情/资金费率", required=False),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_CMD_REQS: Mapping[str, CommandRequirement] = {
|
|
139
|
+
"ollama": CommandRequirement("ollama", "https://ollama.com/download", "本地模型运行"),
|
|
140
|
+
"node": CommandRequirement("node", "brew install node", "部分 MCP server 运行时", required=False),
|
|
141
|
+
"gh": CommandRequirement("gh", "brew install gh && gh auth login", "GitHub PR/Issue/CI 操作"),
|
|
142
|
+
"playwright": CommandRequirement("playwright", "python3 -m playwright install chromium", "安装 Chromium 浏览器内核", required=False),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
_BROKER_MODULES = {
|
|
146
|
+
"alpaca": ("alpaca", ("alpaca", "alpaca-py")),
|
|
147
|
+
"tiger": ("tigeropen", ("tiger", "老虎", "tigeropen")),
|
|
148
|
+
"longbridge": ("longbridge", ("longbridge", "长桥")),
|
|
149
|
+
"ibkr": ("ib_insync", ("ibkr", "interactive brokers", "盈透")),
|
|
150
|
+
"futu": ("futu", ("futu", "富途")),
|
|
151
|
+
"webull": ("webull", ("webull",)),
|
|
152
|
+
"easytrader": ("easytrader", ("easytrader", "同花顺", "雪球")),
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def package_to_module(package: str) -> str:
|
|
157
|
+
"""Map a pip package name to its import module name (e.g. Pillow → PIL)."""
|
|
158
|
+
for req in _PY_REQS.values():
|
|
159
|
+
if req.package == package:
|
|
160
|
+
return req.module
|
|
161
|
+
# Common fallbacks for packages not in _PY_REQS
|
|
162
|
+
_COMMON = {
|
|
163
|
+
"duckduckgo-search": "duckduckgo_search",
|
|
164
|
+
"scikit-learn": "sklearn",
|
|
165
|
+
"beautifulsoup4": "bs4",
|
|
166
|
+
"python-docx": "docx",
|
|
167
|
+
"Pillow": "PIL",
|
|
168
|
+
"futu-api": "futu",
|
|
169
|
+
"alpaca-py": "alpaca",
|
|
170
|
+
}
|
|
171
|
+
if package in _COMMON:
|
|
172
|
+
return _COMMON[package]
|
|
173
|
+
return package.replace("-", "_")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _default_module_available(module: str) -> bool:
|
|
177
|
+
return importlib.util.find_spec(module) is not None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _default_command_available(command: str) -> bool:
|
|
181
|
+
return shutil.which(command) is not None
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _add_req(target: list[PythonRequirement], key: str) -> None:
|
|
185
|
+
req = _PY_REQS[key]
|
|
186
|
+
if not any(item.module == req.module for item in target):
|
|
187
|
+
target.append(req)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _add_cmd(target: list[CommandRequirement], key: str) -> None:
|
|
191
|
+
req = _CMD_REQS[key]
|
|
192
|
+
if not any(item.command == req.command for item in target):
|
|
193
|
+
target.append(req)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _contains_any(text: str, keywords: Iterable[str]) -> bool:
|
|
197
|
+
return any(keyword in text for keyword in keywords)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _detect_intents(message: str) -> tuple[str, ...]:
|
|
201
|
+
return detect_intents(message)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def build_intent_preflight(
|
|
205
|
+
message: str,
|
|
206
|
+
*,
|
|
207
|
+
module_available: ModuleChecker | None = None,
|
|
208
|
+
command_available: CommandChecker | None = None,
|
|
209
|
+
env_get: EnvGetter | None = None,
|
|
210
|
+
) -> IntentPreflight:
|
|
211
|
+
"""Build an intent-aware dependency report for a user request."""
|
|
212
|
+
module_available = module_available or _default_module_available
|
|
213
|
+
command_available = command_available or _default_command_available
|
|
214
|
+
env_get = env_get or os.environ.get
|
|
215
|
+
|
|
216
|
+
low = message.lower().strip()
|
|
217
|
+
route = build_intent_route(message)
|
|
218
|
+
intents = route.intents
|
|
219
|
+
python_reqs: list[PythonRequirement] = []
|
|
220
|
+
command_reqs: list[CommandRequirement] = []
|
|
221
|
+
env_reqs: list[EnvRequirement] = []
|
|
222
|
+
services: list[str] = list(route.services)
|
|
223
|
+
|
|
224
|
+
def service(name: str) -> None:
|
|
225
|
+
if name not in services:
|
|
226
|
+
services.append(name)
|
|
227
|
+
|
|
228
|
+
if any(i in intents for i in ("market_snapshot", "market_analysis", "chart", "dashboard", "report", "backtest", "strategy", "market_research")):
|
|
229
|
+
service("market_data")
|
|
230
|
+
_add_req(python_reqs, "pandas")
|
|
231
|
+
_add_req(python_reqs, "numpy")
|
|
232
|
+
_add_req(python_reqs, "yfinance")
|
|
233
|
+
if any(ch in low for ch in ("a股", "沪", "深", ".sz", ".ss", "港股", "宁德时代", "贵州茅台")):
|
|
234
|
+
_add_req(python_reqs, "akshare")
|
|
235
|
+
|
|
236
|
+
if "chart" in intents:
|
|
237
|
+
service("chart_renderer")
|
|
238
|
+
if _contains_any(low, ("png", "图片", "静态", "k线", "candlestick")):
|
|
239
|
+
_add_req(python_reqs, "matplotlib")
|
|
240
|
+
_add_req(python_reqs, "mplfinance")
|
|
241
|
+
|
|
242
|
+
if "dashboard" in intents:
|
|
243
|
+
service("dashboard_generator")
|
|
244
|
+
_add_req(python_reqs, "akshare")
|
|
245
|
+
|
|
246
|
+
if "report" in intents:
|
|
247
|
+
service("report_generator")
|
|
248
|
+
_add_req(python_reqs, "matplotlib")
|
|
249
|
+
|
|
250
|
+
if "backtest" in intents or "strategy" in intents:
|
|
251
|
+
service("backtest_engine")
|
|
252
|
+
_add_req(python_reqs, "vectorbt")
|
|
253
|
+
|
|
254
|
+
if "vision" in intents:
|
|
255
|
+
service("vision_input")
|
|
256
|
+
_add_req(python_reqs, "PIL")
|
|
257
|
+
|
|
258
|
+
if "screenshot" in intents:
|
|
259
|
+
service("screenshot")
|
|
260
|
+
_add_req(python_reqs, "PIL")
|
|
261
|
+
_add_req(python_reqs, "mss")
|
|
262
|
+
|
|
263
|
+
if "browser" in intents:
|
|
264
|
+
service("browser")
|
|
265
|
+
_add_req(python_reqs, "playwright")
|
|
266
|
+
_add_req(python_reqs, "PIL")
|
|
267
|
+
_add_cmd(command_reqs, "playwright")
|
|
268
|
+
|
|
269
|
+
if "file_analysis" in intents:
|
|
270
|
+
service("file_parser")
|
|
271
|
+
_add_req(python_reqs, "pandas")
|
|
272
|
+
_add_req(python_reqs, "openpyxl")
|
|
273
|
+
_add_req(python_reqs, "pdfplumber")
|
|
274
|
+
_add_req(python_reqs, "pypdf")
|
|
275
|
+
_add_req(python_reqs, "docx")
|
|
276
|
+
_add_req(python_reqs, "bs4")
|
|
277
|
+
_add_req(python_reqs, "PIL")
|
|
278
|
+
|
|
279
|
+
for _, (module, aliases) in _BROKER_MODULES.items():
|
|
280
|
+
if _contains_any(low, aliases):
|
|
281
|
+
service("broker_connector")
|
|
282
|
+
_add_req(python_reqs, module)
|
|
283
|
+
|
|
284
|
+
if "github" in intents:
|
|
285
|
+
service("github_cli")
|
|
286
|
+
_add_cmd(command_reqs, "gh")
|
|
287
|
+
|
|
288
|
+
if "mcp" in intents:
|
|
289
|
+
service("mcp")
|
|
290
|
+
_add_cmd(command_reqs, "node")
|
|
291
|
+
|
|
292
|
+
if "local_model" in intents:
|
|
293
|
+
service("local_llm")
|
|
294
|
+
_add_cmd(command_reqs, "ollama")
|
|
295
|
+
|
|
296
|
+
if "cloud" in intents:
|
|
297
|
+
service("cloud_runtime")
|
|
298
|
+
env_reqs.append(EnvRequirement("ALIYUN_ACCESS_KEY_ID", "阿里云访问密钥", required=False))
|
|
299
|
+
|
|
300
|
+
if "web_search" in intents:
|
|
301
|
+
service("web_search")
|
|
302
|
+
_add_req(python_reqs, "ddgs")
|
|
303
|
+
env_reqs.append(EnvRequirement("BRAVE_SEARCH_API_KEY", "Brave 搜索 (免费 2000 次/月,比 DuckDuckGo 更稳定)", required=False))
|
|
304
|
+
|
|
305
|
+
if "crypto" in intents:
|
|
306
|
+
service("crypto_data")
|
|
307
|
+
_add_req(python_reqs, "ccxt")
|
|
308
|
+
|
|
309
|
+
if "sports" in intents:
|
|
310
|
+
service("sports_data")
|
|
311
|
+
env_reqs.append(EnvRequirement("FOOTBALL_DATA_API_KEY", "football-data.org 实时赛程/积分榜 (免费注册)", required=False))
|
|
312
|
+
|
|
313
|
+
missing_python = tuple(req for req in python_reqs if not module_available(req.module))
|
|
314
|
+
missing_commands = tuple(req for req in command_reqs if not command_available(req.command))
|
|
315
|
+
missing_env = tuple(req for req in env_reqs if not env_get(req.name))
|
|
316
|
+
|
|
317
|
+
return IntentPreflight(
|
|
318
|
+
intents=intents,
|
|
319
|
+
services=tuple(services),
|
|
320
|
+
python=tuple(python_reqs),
|
|
321
|
+
commands=tuple(command_reqs),
|
|
322
|
+
env=tuple(env_reqs),
|
|
323
|
+
missing_python=missing_python,
|
|
324
|
+
missing_commands=missing_commands,
|
|
325
|
+
missing_env=missing_env,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def build_full_dependency_report(
|
|
330
|
+
*,
|
|
331
|
+
module_available: ModuleChecker | None = None,
|
|
332
|
+
command_available: CommandChecker | None = None,
|
|
333
|
+
env_get: EnvGetter | None = None,
|
|
334
|
+
include_optional: bool = True,
|
|
335
|
+
) -> IntentPreflight:
|
|
336
|
+
"""Scan every known requirement (intent-independent) for a full health check.
|
|
337
|
+
|
|
338
|
+
Used by `/install` with no arguments — reports all missing packages/tools
|
|
339
|
+
across the whole project, not just those implied by a single message.
|
|
340
|
+
"""
|
|
341
|
+
module_available = module_available or _default_module_available
|
|
342
|
+
command_available = command_available or _default_command_available
|
|
343
|
+
env_get = env_get or os.environ.get
|
|
344
|
+
|
|
345
|
+
# Broker SDKs are intent-specific — a user only needs the one for their
|
|
346
|
+
# broker, so a generic full scan should not propose installing all of them.
|
|
347
|
+
_broker_modules = {module for module, _ in _BROKER_MODULES.values()}
|
|
348
|
+
python_reqs = tuple(
|
|
349
|
+
r for r in _PY_REQS.values() if r.module not in _broker_modules
|
|
350
|
+
)
|
|
351
|
+
command_reqs = tuple(_CMD_REQS.values())
|
|
352
|
+
env_reqs: tuple[EnvRequirement, ...] = (
|
|
353
|
+
EnvRequirement("BRAVE_SEARCH_API_KEY", "Brave 联网搜索 (免费 2000 次/月)", required=False),
|
|
354
|
+
EnvRequirement("FOOTBALL_DATA_API_KEY", "football-data.org 实时足球数据", required=False),
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
def _keep(req) -> bool:
|
|
358
|
+
return include_optional or getattr(req, "required", True)
|
|
359
|
+
|
|
360
|
+
missing_python = tuple(
|
|
361
|
+
r for r in python_reqs if _keep(r) and not module_available(r.module)
|
|
362
|
+
)
|
|
363
|
+
missing_commands = tuple(
|
|
364
|
+
r for r in command_reqs if _keep(r) and not command_available(r.command)
|
|
365
|
+
)
|
|
366
|
+
missing_env = tuple(r for r in env_reqs if not env_get(r.name))
|
|
367
|
+
|
|
368
|
+
return IntentPreflight(
|
|
369
|
+
intents=("full_scan",),
|
|
370
|
+
services=("all",),
|
|
371
|
+
python=python_reqs,
|
|
372
|
+
commands=command_reqs,
|
|
373
|
+
env=env_reqs,
|
|
374
|
+
missing_python=missing_python,
|
|
375
|
+
missing_commands=missing_commands,
|
|
376
|
+
missing_env=missing_env,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def build_install_plan(report: IntentPreflight) -> InstallPlan:
|
|
381
|
+
"""Convert preflight findings into explicit user-approved install steps."""
|
|
382
|
+
packages: list[str] = []
|
|
383
|
+
seen: set[str] = set()
|
|
384
|
+
for req in report.missing_python:
|
|
385
|
+
if req.package not in seen:
|
|
386
|
+
packages.append(req.package)
|
|
387
|
+
seen.add(req.package)
|
|
388
|
+
|
|
389
|
+
command_hints = tuple(
|
|
390
|
+
f"{req.command}: {req.install_hint}"
|
|
391
|
+
for req in report.missing_commands
|
|
392
|
+
)
|
|
393
|
+
env_hints = tuple(
|
|
394
|
+
f"{req.name}: {req.purpose}"
|
|
395
|
+
for req in report.missing_env
|
|
396
|
+
)
|
|
397
|
+
pip_command = ""
|
|
398
|
+
if packages:
|
|
399
|
+
pip_command = "python3 -m pip install " + " ".join(shlex.quote(pkg) for pkg in packages)
|
|
400
|
+
|
|
401
|
+
return InstallPlan(
|
|
402
|
+
services=report.services,
|
|
403
|
+
pip_packages=tuple(packages),
|
|
404
|
+
pip_command=pip_command,
|
|
405
|
+
command_hints=command_hints,
|
|
406
|
+
env_hints=env_hints,
|
|
407
|
+
has_required_items=report.has_required_findings,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def select_install_packages(
|
|
412
|
+
plan: InstallPlan,
|
|
413
|
+
report: IntentPreflight | None = None,
|
|
414
|
+
*,
|
|
415
|
+
mode: str = "all",
|
|
416
|
+
custom: Iterable[str] | None = None,
|
|
417
|
+
) -> InstallSelection:
|
|
418
|
+
"""Resolve selector choices into concrete pip packages.
|
|
419
|
+
|
|
420
|
+
Modes:
|
|
421
|
+
- all: install every missing Python package in the plan
|
|
422
|
+
- required: install only required Python packages
|
|
423
|
+
- optional: install only optional Python packages
|
|
424
|
+
- custom: install only packages named in ``custom`` if present in the plan
|
|
425
|
+
- skip/plan: install nothing
|
|
426
|
+
"""
|
|
427
|
+
available = list(plan.pip_packages)
|
|
428
|
+
normalized_mode = (mode or "all").strip().lower()
|
|
429
|
+
if normalized_mode in {"skip", "none", "cancel", "plan", "dry-run", "dry_run"}:
|
|
430
|
+
return InstallSelection((), tuple(available), normalized_mode)
|
|
431
|
+
|
|
432
|
+
if normalized_mode == "required" and report is not None:
|
|
433
|
+
selected = [
|
|
434
|
+
req.package for req in report.missing_python
|
|
435
|
+
if req.required and req.package in available
|
|
436
|
+
]
|
|
437
|
+
elif normalized_mode == "optional" and report is not None:
|
|
438
|
+
selected = [
|
|
439
|
+
req.package for req in report.missing_python
|
|
440
|
+
if not req.required and req.package in available
|
|
441
|
+
]
|
|
442
|
+
elif normalized_mode == "custom":
|
|
443
|
+
wanted = {str(item).strip() for item in (custom or []) if str(item).strip()}
|
|
444
|
+
selected = [pkg for pkg in available if pkg in wanted]
|
|
445
|
+
else:
|
|
446
|
+
selected = available
|
|
447
|
+
|
|
448
|
+
selected = list(dict.fromkeys(selected))
|
|
449
|
+
skipped = [pkg for pkg in available if pkg not in selected]
|
|
450
|
+
return InstallSelection(tuple(selected), tuple(skipped), normalized_mode)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def format_preflight_plain(report: IntentPreflight) -> str:
|
|
454
|
+
"""Return a concise plain-text preflight message."""
|
|
455
|
+
if not report.has_findings:
|
|
456
|
+
return ""
|
|
457
|
+
plan = build_install_plan(report)
|
|
458
|
+
|
|
459
|
+
if not report.has_required_findings:
|
|
460
|
+
lines = ["依赖提示:可选增强能力缺失,当前会使用内置/降级实现。"]
|
|
461
|
+
if report.missing_python:
|
|
462
|
+
reqs = ", ".join(f"{r.package}({r.purpose})" for r in report.missing_python)
|
|
463
|
+
lines.append("可选 Python 包: " + reqs)
|
|
464
|
+
if report.missing_commands:
|
|
465
|
+
tools = ", ".join(plan.command_hints)
|
|
466
|
+
lines.append("可选工具: " + tools)
|
|
467
|
+
if report.missing_env:
|
|
468
|
+
envs = ", ".join(plan.env_hints)
|
|
469
|
+
lines.append("可选环境变量: " + envs)
|
|
470
|
+
if plan.pip_command:
|
|
471
|
+
lines.append("选择安装: /install --auto(all/required/optional/custom/plan/skip)或 " + plan.pip_command)
|
|
472
|
+
elif plan.has_actions:
|
|
473
|
+
lines.append("配置: 按上方提示手动配置,或用 /setup 查看配置向导。")
|
|
474
|
+
return "\n".join(lines)
|
|
475
|
+
|
|
476
|
+
lines = ["依赖预检:当前请求可能需要补充本机能力"]
|
|
477
|
+
if report.intents:
|
|
478
|
+
lines.append("意图: " + ", ".join(report.intents))
|
|
479
|
+
if report.services:
|
|
480
|
+
lines.append("服务: " + ", ".join(report.services))
|
|
481
|
+
if report.missing_python:
|
|
482
|
+
reqs = ", ".join(f"{r.package}({r.purpose})" for r in report.missing_python)
|
|
483
|
+
lines.append("缺少 Python 包: " + reqs)
|
|
484
|
+
if plan.pip_command:
|
|
485
|
+
lines.append("安装命令: " + plan.pip_command)
|
|
486
|
+
if report.missing_commands:
|
|
487
|
+
for hint in plan.command_hints:
|
|
488
|
+
lines.append("缺少工具: " + hint)
|
|
489
|
+
if report.missing_env:
|
|
490
|
+
envs = ", ".join(plan.env_hints)
|
|
491
|
+
lines.append("可选环境变量未配置: " + envs)
|
|
492
|
+
if plan.has_actions:
|
|
493
|
+
if plan.pip_packages:
|
|
494
|
+
lines.append("选择安装: 运行 /install --auto 进入选择器,或 /install 扫描全部缺失。")
|
|
495
|
+
else:
|
|
496
|
+
lines.append("Aria 不会自动安装;按上方提示手动配置,或用 /setup 查看配置向导。")
|
|
497
|
+
return "\n".join(lines)
|
apps/cli/project_aria.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project-level ARIA.md helpers.
|
|
3
|
+
|
|
4
|
+
Used by /init scaffolds and repository bootstrap to keep project memory files
|
|
5
|
+
structured and consistent.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build_project_aria_md(
|
|
11
|
+
*,
|
|
12
|
+
project_name: str,
|
|
13
|
+
stack: str,
|
|
14
|
+
entry: str = "main.py",
|
|
15
|
+
purpose: str = "",
|
|
16
|
+
notes: list[str] | None = None,
|
|
17
|
+
conventions: list[str] | None = None,
|
|
18
|
+
) -> str:
|
|
19
|
+
"""Build a durable project-level ARIA.md template."""
|
|
20
|
+
notes = notes or []
|
|
21
|
+
conventions = conventions or [
|
|
22
|
+
"Keep CLI output stable and avoid leaking local paths in normal UI.",
|
|
23
|
+
"Prefer thin CLI entry points plus command mixins for implementation.",
|
|
24
|
+
"Treat turn envelopes, traces, exports, and provider health as shared contracts.",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
lines = [
|
|
28
|
+
"# Memory",
|
|
29
|
+
"",
|
|
30
|
+
f"- **Project**: {project_name}",
|
|
31
|
+
f"- **Stack**: {stack}",
|
|
32
|
+
f"- **Entry**: {entry}",
|
|
33
|
+
]
|
|
34
|
+
if purpose:
|
|
35
|
+
lines.append(f"- **Purpose**: {purpose}")
|
|
36
|
+
lines.append("- **Conventions**:")
|
|
37
|
+
lines.extend(f" - {item}" for item in conventions)
|
|
38
|
+
if notes:
|
|
39
|
+
lines.append("- **Notes**:")
|
|
40
|
+
lines.extend(f" - {item}" for item in notes)
|
|
41
|
+
|
|
42
|
+
lines.extend([
|
|
43
|
+
"",
|
|
44
|
+
"## Memory Layers",
|
|
45
|
+
"- Project ARIA.md describes this repository and overrides the global profile when both apply.",
|
|
46
|
+
"- `~/.arthera/ARIA.md` is the user profile and carries cross-project preferences.",
|
|
47
|
+
"- Session history and traces stay ephemeral unless exported explicitly.",
|
|
48
|
+
"",
|
|
49
|
+
"## Operational Rules",
|
|
50
|
+
"- Keep generated code and user artifacts in the user's workspace, not in the source tree, unless the user asks otherwise.",
|
|
51
|
+
"- Prefer deterministic service output envelopes for CLI, trace, and export surfaces.",
|
|
52
|
+
"- Use `/doctor` or the health views before trusting external providers.",
|
|
53
|
+
"",
|
|
54
|
+
"## Workflow Notes",
|
|
55
|
+
"- Use `/init` to refresh project context after substantial structural changes.",
|
|
56
|
+
"- Use `/memory add` for durable project facts and `/memory profile` for user preferences.",
|
|
57
|
+
"- Keep long sessions compacted and avoid relying on chat history as the only source of truth.",
|
|
58
|
+
"",
|
|
59
|
+
])
|
|
60
|
+
return "\n".join(lines)
|
|
File without changes
|