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.
Files changed (284) hide show
  1. agents/__init__.py +32 -0
  2. agents/base.py +190 -0
  3. agents/deep/__init__.py +37 -0
  4. agents/deep/calibration_loop.py +144 -0
  5. agents/deep/critic.py +125 -0
  6. agents/deep/deepen.py +193 -0
  7. agents/deep/models.py +149 -0
  8. agents/deep/pipeline.py +164 -0
  9. agents/deep/quant_fusion.py +192 -0
  10. agents/deep/themes.py +95 -0
  11. agents/deep/tiers.py +106 -0
  12. agents/financial/__init__.py +10 -0
  13. agents/financial/catalyst.py +279 -0
  14. agents/financial/debate.py +145 -0
  15. agents/financial/earnings.py +303 -0
  16. agents/financial/fundamental.py +159 -0
  17. agents/financial/macro.py +99 -0
  18. agents/financial/news.py +207 -0
  19. agents/financial/risk.py +132 -0
  20. agents/financial/sector.py +279 -0
  21. agents/financial/synthesis.py +274 -0
  22. agents/financial/technical.py +258 -0
  23. agents/portfolio_agent.py +333 -0
  24. agents/realty/__init__.py +62 -0
  25. agents/realty/asset_diagnosis.py +150 -0
  26. agents/realty/business_match.py +165 -0
  27. agents/realty/cashflow_verify.py +208 -0
  28. agents/realty/contract_rules.py +209 -0
  29. agents/realty/energy_anomaly.py +188 -0
  30. agents/realty/exit_settlement.py +207 -0
  31. agents/realty/fulfillment_risk.py +205 -0
  32. agents/realty/ops_optimize.py +159 -0
  33. agents/realty/revenue_share.py +214 -0
  34. agents/registry.py +144 -0
  35. agents/sports/__init__.py +0 -0
  36. agents/sports/football_agent.py +169 -0
  37. agents/team.py +289 -0
  38. aliyun_data_client.py +660 -0
  39. apps/README.md +12 -0
  40. apps/__init__.py +2 -0
  41. apps/channels/README.md +15 -0
  42. apps/cli/README.md +13 -0
  43. apps/cli/__init__.py +2 -0
  44. apps/cli/bootstrap.py +99 -0
  45. apps/cli/codegen_paths.py +29 -0
  46. apps/cli/commands/__init__.py +16 -0
  47. apps/cli/commands/analysis_cmds.py +288 -0
  48. apps/cli/commands/backtest_cmds.py +1887 -0
  49. apps/cli/commands/broker_cmds.py +1154 -0
  50. apps/cli/commands/business_workflow_cmds.py +289 -0
  51. apps/cli/commands/catalog.py +84 -0
  52. apps/cli/commands/data_cmds.py +405 -0
  53. apps/cli/commands/diagnostic_cmds.py +179 -0
  54. apps/cli/commands/diagnostic_ops_cmds.py +696 -0
  55. apps/cli/commands/finance_render.py +12 -0
  56. apps/cli/commands/market.py +399 -0
  57. apps/cli/commands/market_cmds.py +1276 -0
  58. apps/cli/commands/market_context.py +425 -0
  59. apps/cli/commands/market_render.py +7 -0
  60. apps/cli/commands/model_cmds.py +1579 -0
  61. apps/cli/commands/ops_cmds.py +668 -0
  62. apps/cli/commands/portfolio_cmds.py +962 -0
  63. apps/cli/commands/report.py +377 -0
  64. apps/cli/commands/scaffold_templates.py +617 -0
  65. apps/cli/commands/session_cmds.py +179 -0
  66. apps/cli/commands/session_ux_cmds.py +280 -0
  67. apps/cli/commands/team.py +588 -0
  68. apps/cli/commands/team_render.py +8 -0
  69. apps/cli/commands/ui_cmds.py +358 -0
  70. apps/cli/commands/workflow_cmds.py +279 -0
  71. apps/cli/commands/workspace_cmds.py +1414 -0
  72. apps/cli/config_paths.py +70 -0
  73. apps/cli/config_store.py +61 -0
  74. apps/cli/deterministic.py +122 -0
  75. apps/cli/direct.py +48 -0
  76. apps/cli/github_app_auth.py +135 -0
  77. apps/cli/handlers/__init__.py +11 -0
  78. apps/cli/handlers/broker_handlers.py +122 -0
  79. apps/cli/handlers/chart_handlers.py +1309 -0
  80. apps/cli/handlers/market_handlers.py +2509 -0
  81. apps/cli/handlers/realty_handlers.py +114 -0
  82. apps/cli/handlers/strategy_advice.py +82 -0
  83. apps/cli/hooks.py +180 -0
  84. apps/cli/i18n.py +284 -0
  85. apps/cli/intent.py +136 -0
  86. apps/cli/intent_router.py +217 -0
  87. apps/cli/lifecycle_hooks.py +48 -0
  88. apps/cli/main.py +29 -0
  89. apps/cli/market_metadata.py +135 -0
  90. apps/cli/market_universe.py +265 -0
  91. apps/cli/message_processing.py +257 -0
  92. apps/cli/plan_mode.py +139 -0
  93. apps/cli/plotly_html.py +15 -0
  94. apps/cli/prediction_feedback.py +202 -0
  95. apps/cli/preflight.py +497 -0
  96. apps/cli/project_aria.py +60 -0
  97. apps/cli/prompts/__init__.py +0 -0
  98. apps/cli/prompts/coding.py +658 -0
  99. apps/cli/prompts/system_prompts.py +531 -0
  100. apps/cli/prompts/ui.py +434 -0
  101. apps/cli/providers/__init__.py +1 -0
  102. apps/cli/providers/base.py +271 -0
  103. apps/cli/providers/chat_routing.py +80 -0
  104. apps/cli/providers/llm/__init__.py +1 -0
  105. apps/cli/providers/llm/ollama_stream.py +1170 -0
  106. apps/cli/providers/llm/sse_stream.py +216 -0
  107. apps/cli/providers/runtime_bridge.py +185 -0
  108. apps/cli/runtime_consumer.py +489 -0
  109. apps/cli/session_export.py +87 -0
  110. apps/cli/session_jsonl.py +207 -0
  111. apps/cli/session_store.py +112 -0
  112. apps/cli/todo_tracker.py +190 -0
  113. apps/cli/tools/__init__.py +40 -0
  114. apps/cli/tools/context.py +46 -0
  115. apps/cli/tools/file_tools.py +112 -0
  116. apps/cli/tools/market_tools.py +549 -0
  117. apps/cli/tools/notebook_tools.py +111 -0
  118. apps/cli/tools/system_tools.py +669 -0
  119. apps/cli/tools/write_tools.py +715 -0
  120. apps/cli/tradingview_bridge.py +434 -0
  121. apps/cli/update_check.py +152 -0
  122. apps/cli/utils/__init__.py +0 -0
  123. apps/cli/utils/market_detect.py +1578 -0
  124. apps/daemon/README.md +14 -0
  125. apps/vscode/README.md +115 -0
  126. apps/vscode/package.json +70 -0
  127. aria_cli.py +11636 -0
  128. aria_code-4.1.3.dist-info/METADATA +952 -0
  129. aria_code-4.1.3.dist-info/RECORD +284 -0
  130. aria_code-4.1.3.dist-info/WHEEL +5 -0
  131. aria_code-4.1.3.dist-info/entry_points.txt +2 -0
  132. aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
  133. aria_code-4.1.3.dist-info/top_level.txt +50 -0
  134. aria_daemon.py +1295 -0
  135. aria_feishu_bot.py +1359 -0
  136. aria_relay_client.py +182 -0
  137. aria_relay_server.py +405 -0
  138. aria_telegram_bot.py +202 -0
  139. ariarc.py +328 -0
  140. artifacts.py +491 -0
  141. backtest_report.py +472 -0
  142. brokers/__init__.py +72 -0
  143. brokers/base.py +207 -0
  144. brokers/capabilities.py +264 -0
  145. brokers/cn/__init__.py +10 -0
  146. brokers/cn/easytrader_broker.py +193 -0
  147. brokers/cn/futu_broker.py +194 -0
  148. brokers/cn/longbridge_broker.py +190 -0
  149. brokers/cn/tiger_broker.py +196 -0
  150. brokers/cn/xtquant_broker.py +175 -0
  151. brokers/config.py +364 -0
  152. brokers/intl/__init__.py +5 -0
  153. brokers/intl/alpaca_broker.py +183 -0
  154. brokers/intl/ibkr_broker.py +215 -0
  155. brokers/intl/webull_broker.py +156 -0
  156. brokers/paper_broker.py +259 -0
  157. brokers/planning.py +296 -0
  158. brokers/registry.py +181 -0
  159. brokers/trading.py +237 -0
  160. change_store.py +127 -0
  161. command_safety.py +19 -0
  162. computer_use_tools.py +504 -0
  163. dashboard_generator.py +578 -0
  164. data_analysis_tools.py +808 -0
  165. data_cleaner.py +483 -0
  166. data_service.py +481 -0
  167. datasources/__init__.py +23 -0
  168. datasources/base.py +166 -0
  169. datasources/router.py +221 -0
  170. datasources/sources/__init__.py +15 -0
  171. datasources/sources/akshare_source.py +269 -0
  172. datasources/sources/alpha_vantage_source.py +202 -0
  173. datasources/sources/edgar_source.py +218 -0
  174. datasources/sources/finnhub_source.py +197 -0
  175. datasources/sources/fred_source.py +219 -0
  176. datasources/sources/tushare_source.py +141 -0
  177. datasources/sources/web_scraper_source.py +278 -0
  178. datasources/sources/world_bank_source.py +205 -0
  179. datasources/sources/yfinance_source.py +152 -0
  180. demo_player.py +204 -0
  181. doctor.py +508 -0
  182. file_analysis_tools.py +734 -0
  183. finance_formulas.py +389 -0
  184. football_data_client.py +1670 -0
  185. intent_classifier.py +358 -0
  186. local_finance_tools.py +3221 -0
  187. local_llm_provider.py +552 -0
  188. macro_tools.py +368 -0
  189. market_data_client.py +1899 -0
  190. mcp_client.py +506 -0
  191. memory_manager.py +245 -0
  192. model_capability.py +416 -0
  193. notification_tools.py +248 -0
  194. packages/__init__.py +23 -0
  195. packages/aria_agents/__init__.py +5 -0
  196. packages/aria_agents/manifest.py +69 -0
  197. packages/aria_core/__init__.py +34 -0
  198. packages/aria_core/architecture.py +192 -0
  199. packages/aria_core/export.py +124 -0
  200. packages/aria_core/manifest.py +65 -0
  201. packages/aria_infra/__init__.py +15 -0
  202. packages/aria_infra/arthera.py +52 -0
  203. packages/aria_infra/doctor.py +246 -0
  204. packages/aria_infra/product.py +37 -0
  205. packages/aria_mcp/__init__.py +25 -0
  206. packages/aria_mcp/bridge.py +38 -0
  207. packages/aria_mcp/config.py +97 -0
  208. packages/aria_mcp/tools.py +61 -0
  209. packages/aria_sdk/__init__.py +19 -0
  210. packages/aria_sdk/client.py +396 -0
  211. packages/aria_sdk/providers.py +70 -0
  212. packages/aria_sdk/streaming.py +73 -0
  213. packages/aria_sdk/types.py +86 -0
  214. packages/aria_services/__init__.py +55 -0
  215. packages/aria_services/context.py +258 -0
  216. packages/aria_services/data.py +11 -0
  217. packages/aria_services/provider_health.py +189 -0
  218. packages/aria_services/registry.py +213 -0
  219. packages/aria_services/usage.py +138 -0
  220. packages/aria_skills/__init__.py +5 -0
  221. packages/aria_skills/registry.py +59 -0
  222. packages/aria_tools/__init__.py +5 -0
  223. packages/aria_tools/registry.py +128 -0
  224. packages/quant_engine/__init__.py +6 -0
  225. packages/quant_engine/sports/__init__.py +72 -0
  226. packages/quant_engine/sports/calibrator.py +353 -0
  227. packages/quant_engine/sports/dixon_coles.py +234 -0
  228. packages/quant_engine/sports/elo.py +299 -0
  229. packages/quant_engine/sports/form.py +188 -0
  230. packages/quant_engine/sports/h2h.py +195 -0
  231. packages/quant_engine/sports/ml_model.py +354 -0
  232. packages/quant_engine/sports/predictor.py +311 -0
  233. packages/quant_engine/sports/tracker.py +664 -0
  234. packages/quant_engine/stochastic/__init__.py +27 -0
  235. packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  236. packages/quant_engine/stochastic/ito_calculus.py +477 -0
  237. packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  238. packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  239. packages/quant_engine/stochastic/options_pricing.py +573 -0
  240. packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  241. plan_utils.py +194 -0
  242. plugin_loader.py +328 -0
  243. portfolio_ledger.py +262 -0
  244. privacy/__init__.py +5 -0
  245. privacy/feedback.py +123 -0
  246. project_tools.py +525 -0
  247. providers/__init__.py +30 -0
  248. providers/llm/__init__.py +19 -0
  249. providers/llm/anthropic.py +184 -0
  250. providers/llm/base.py +139 -0
  251. providers/llm/ollama.py +128 -0
  252. providers/llm/openai_compat.py +282 -0
  253. providers/llm/registry.py +358 -0
  254. realty_data_tools.py +659 -0
  255. report_generator.py +1314 -0
  256. runtime/__init__.py +103 -0
  257. runtime/agent_loop.py +1183 -0
  258. runtime/approval.py +51 -0
  259. runtime/events.py +102 -0
  260. runtime/gateway.py +128 -0
  261. runtime/lsp.py +346 -0
  262. runtime/subagent.py +258 -0
  263. runtime/tool_executor.py +104 -0
  264. runtime/tool_policy.py +106 -0
  265. safety/__init__.py +21 -0
  266. safety/permissions.py +275 -0
  267. setup_wizard.py +653 -0
  268. strategy_vault.py +420 -0
  269. ui/__init__.py +100 -0
  270. ui/banner.py +310 -0
  271. ui/completer.py +391 -0
  272. ui/console.py +271 -0
  273. ui/image_render.py +243 -0
  274. ui/input_box.py +376 -0
  275. ui/picker.py +195 -0
  276. ui/render/__init__.py +11 -0
  277. ui/render/finance.py +1480 -0
  278. ui/render/market.py +225 -0
  279. ui/render/output.py +681 -0
  280. ui/render/team.py +346 -0
  281. ui/robot.py +235 -0
  282. workspace/__init__.py +6 -0
  283. workspace/files.py +170 -0
  284. workspace/verify.py +113 -0
apps/cli/intent.py ADDED
@@ -0,0 +1,136 @@
1
+ """Intent classification helpers extracted from aria_cli.py.
2
+
3
+ All functions are pure — no I/O, no globals, fully unit-testable.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ # ── Keyword lists ─────────────────────────────────────────────────────────────
8
+
9
+ CODING_KEYWORDS = (
10
+ "write", "generate", "create", "script", "code", "plot", "backtest",
11
+ "策略", "代码", "回测", "编写", "生成", "k线", "k-line", "kline",
12
+ "analyze and save", "analysis script", "python", "dashboard",
13
+ "写一个", "生成代码", "写代码", "编写代码",
14
+ )
15
+
16
+ VISUAL_ARTIFACT_KEYWORDS = (
17
+ "图表", "走势图", "k线图", "k线", "k-line", "kline", "candlestick",
18
+ "chart", "plot", "dashboard", "看板", "晨报", "日报", "周报", "月报",
19
+ "report", "热力图", "heatmap",
20
+ )
21
+
22
+ ANALYSIS_KEYWORDS = (
23
+ "analyze", "analysis", "分析", "研究", "评估", "研判",
24
+ "技术面", "基本面", "走势", "趋势", "行情",
25
+ "技术分析", "技术指标", "支撑", "阻力", "支撑位", "阻力位",
26
+ "rsi", "macd", "bollinger", "布林", "均线", "kdj", "kdj指标",
27
+ "stock analysis", "technical analysis", "fundamental",
28
+ "valuation", "estimate", "outlook", "投资建议", "买入", "卖出",
29
+ "看多", "看空", "多头", "空头", "金叉", "死叉",
30
+ )
31
+
32
+ ANALYSIS_NON_STOCK_TOPICS = (
33
+ "房价", "楼市", "房产", "房地产", "租金", "二手房", "新房", "商铺", "折旧",
34
+ "宏观", "宏观经济", "宏观政策", "宏观角度", "经济政策", "货币政策",
35
+ "财政政策", "gdp", "通胀", "通货膨胀", "cpi", "ppi", "利率政策",
36
+ "黄金", "原油", "大宗商品", "汇率", "外汇", "美元指数",
37
+ )
38
+
39
+ GENERAL_KNOWLEDGE_KEYWORDS = (
40
+ "什么是", "what is", "what are", "how does", "explain", "define",
41
+ "解释", "定义", "概念", "原理", "介绍", "步骤", "流程", "怎么",
42
+ "如何理解", "是什么", "为什么", "区别", "difference between",
43
+ "tell me about", "describe", "how to", "注册", "成立", "公司",
44
+ "基本概念", "简介", "举例", "example", "例子",
45
+ "足球", "篮球", "网球", "棒球", "橄榄球", "排球", "乒乓球", "羽毛球",
46
+ "世界杯", "欧冠", "英超", "德甲", "西甲", "意甲", "法甲", "bundesliga",
47
+ "比赛", "赛事", "比分", "进球", "射门", "门将", "球队", "球员", "教练",
48
+ "联赛", "积分榜", "赛程", "晋级", "淘汰赛", "决赛", "半决赛",
49
+ "football", "soccer", "match", "goal", "league", "champion",
50
+ "nba", "nfl", "mlb", "f1", "赛车", "奥运", "olympic",
51
+ )
52
+
53
+ FINANCE_CONCEPT_TERMS = (
54
+ "dcf", "pe", "pb", "ps", "ev", "ebitda", "ebit", "wacc", "capm",
55
+ "beta", "alpha", "sharpe", "sortino", "var", "cvar", "drawdown",
56
+ "black-scholes", "bs模型", "期权", "期货", "衍生品", "套利",
57
+ "量化", "quant", "回测", "factor", "因子", "ic值", "ir值",
58
+ "市盈率", "市净率", "净利润", "营业收入", "自由现金流", "贴现",
59
+ "折现", "估值", "valuation", "ipo", "etf", "reits", "债券",
60
+ "利率", "收益率", "久期", "凸性", "信用利差", "风险溢价",
61
+ "动量", "均值回归", "布林带", "macd", "rsi", "kdj", "技术指标",
62
+ "北向资金", "融资融券", "股指期货", "沪深300", "中证500",
63
+ )
64
+
65
+ SPORTS_KEYWORDS = (
66
+ "足球", "世界杯", "欧冠", "英超", "德甲", "西甲", "意甲", "法甲",
67
+ "篮球", "nba", "网球", "f1", "赛车", "奥运", "olympic",
68
+ "比赛", "赛事", "比分", "进球", "球队", "球员", "联赛",
69
+ "football", "soccer", "world cup", "champions league",
70
+ "match", "score", "league", "premier league", "bundesliga",
71
+ )
72
+
73
+
74
+ # ── Classifier functions ──────────────────────────────────────────────────────
75
+
76
+ def is_coding_request(message: str) -> bool:
77
+ """Return True if message looks like a coding/file-generation task."""
78
+ low = message.lower()
79
+ if any(k in low for k in VISUAL_ARTIFACT_KEYWORDS):
80
+ return True
81
+ if any(k in low for k in CODING_KEYWORDS):
82
+ return True
83
+ if low.startswith("/code") or low.startswith("/gen-"):
84
+ return True
85
+ return False
86
+
87
+
88
+ def is_sports_query(message: str) -> bool:
89
+ """Return True if the message is about sports/football."""
90
+ low = message.lower()
91
+ return any(k in low for k in SPORTS_KEYWORDS)
92
+
93
+
94
+ def is_analysis_request(message: str) -> bool:
95
+ """Return True if message is a stock/crypto technical analysis request (not coding).
96
+
97
+ Excludes real-estate, pure macro, and sports questions: those match keywords
98
+ like '分析'/'走势' but should NOT use the stock technical-analysis template
99
+ (which requires injected market data to be useful).
100
+ """
101
+ if is_coding_request(message):
102
+ return False
103
+ low = message.lower()
104
+ if any(k in low for k in ANALYSIS_NON_STOCK_TOPICS):
105
+ return False
106
+ if is_sports_query(message):
107
+ return False
108
+ return any(k in low for k in ANALYSIS_KEYWORDS)
109
+
110
+
111
+ def is_general_knowledge(message: str) -> bool:
112
+ """Return True for pure knowledge/explanation questions that don't need tools.
113
+
114
+ Finance/quant terms are excluded so they keep the full FINANCE_CHAT_PROMPT
115
+ even when phrased as "X是什么" explanatory questions.
116
+
117
+ Pure macro/conceptual analysis questions are treated as general knowledge:
118
+ routing them to the finance prompt with tools causes the model to fetch live
119
+ prices and output the stock-analysis template instead of thoughtful commentary.
120
+ """
121
+ if is_coding_request(message) or is_analysis_request(message):
122
+ return False
123
+ low = message.lower().strip()
124
+ if any(term in low for term in FINANCE_CONCEPT_TERMS):
125
+ return False
126
+ _macro_conceptual = (
127
+ "宏观", "宏观经济", "宏观政策", "宏观角度", "宏观分析",
128
+ "货币政策", "财政政策", "值得投资吗", "应该投资吗", "是否值得",
129
+ "投资逻辑", "长期展望", "未来前景",
130
+ )
131
+ if (any(k in low for k in _macro_conceptual)
132
+ and not any(c.isdigit() for c in low)):
133
+ return True
134
+ if len(low) < 30 and not any(c.isdigit() for c in low):
135
+ return True
136
+ return any(k in low for k in GENERAL_KNOWLEDGE_KEYWORDS)
@@ -0,0 +1,217 @@
1
+ """Canonical lightweight intent routing for CLI services.
2
+
3
+ This module keeps routing side-effect free. It does not call tools, install
4
+ packages, or fetch data; it only classifies a user message into stable service
5
+ intents that other layers can reuse.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from typing import Iterable
12
+
13
+
14
+ def _contains_any(text: str, keywords: Iterable[str]) -> bool:
15
+ return any(keyword in text for keyword in keywords)
16
+
17
+
18
+ def _first_token(text: str) -> str:
19
+ stripped = text.strip()
20
+ if not stripped:
21
+ return ""
22
+ return stripped.split(maxsplit=1)[0].lower()
23
+
24
+
25
+ def _add_unique(items: list[str], name: str) -> None:
26
+ if name not in items:
27
+ items.append(name)
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class IntentRoute:
32
+ message: str
33
+ primary: str
34
+ intents: tuple[str, ...]
35
+ services: tuple[str, ...]
36
+ explicit_code: bool = False
37
+ visual_artifact: bool = False
38
+ market_related: bool = False
39
+
40
+ @property
41
+ def wants_market_prefetch(self) -> bool:
42
+ return self.market_related and not self.visual_artifact and self.primary != "general"
43
+
44
+ @property
45
+ def allows_code_autorun(self) -> bool:
46
+ return self.primary in {"code", "strategy", "backtest"} and self.explicit_code
47
+
48
+
49
+ COMMAND_INTENTS = {
50
+ "/chart": "chart",
51
+ "/dashboard": "dashboard",
52
+ "/report": "report",
53
+ "/team": "market_research",
54
+ "/analyze": "market_analysis",
55
+ "/ta": "market_analysis",
56
+ "/quote": "market_snapshot",
57
+ "/backtest": "backtest",
58
+ "/auto-strategy": "strategy",
59
+ "/strategy": "strategy",
60
+ "/ui": "ui_artifact",
61
+ "/vision": "vision",
62
+ "/upload-image": "vision",
63
+ "/screenshot": "screenshot",
64
+ "/browser": "browser",
65
+ "/file": "file_analysis",
66
+ "/github": "github",
67
+ "/mcp": "mcp",
68
+ "/cloud": "cloud",
69
+ }
70
+
71
+
72
+ def detect_intents(message: str) -> tuple[str, ...]:
73
+ low = message.lower().strip()
74
+ cmd = _first_token(low)
75
+ intents: list[str] = []
76
+
77
+ mapped = COMMAND_INTENTS.get(cmd)
78
+ if mapped:
79
+ _add_unique(intents, mapped)
80
+
81
+ if _contains_any(low, ("k线图", "k线", "k-line", "kline", "candlestick", "走势图", "图表", "chart", "plot")):
82
+ _add_unique(intents, "chart")
83
+ if _contains_any(low, ("看板", "晨报", "日报", "dashboard", "market board", "持仓看板")):
84
+ _add_unique(intents, "dashboard")
85
+ if _contains_any(low, ("研究报告", "财报", "报告", "report", "研报")):
86
+ _add_unique(intents, "report")
87
+ if _contains_any(low, ("回测", "backtest", "收益曲线", "最大回撤")):
88
+ _add_unique(intents, "backtest")
89
+ if _contains_any(low, ("策略代码", "写策略", "量化策略", "交易策略", "strategy code", "trading bot")):
90
+ _add_unique(intents, "strategy")
91
+ if _contains_any(low, ("技术分析", "技术指标", "rsi", "macd", "均线", "支撑", "阻力")):
92
+ _add_unique(intents, "market_analysis")
93
+ if _contains_any(low, ("股票", "行情", "持仓", "portfolio", "quote", "market data")):
94
+ _add_unique(intents, "market_snapshot")
95
+ if _contains_any(low, ("图片", "图像", "截图", "上传图片", "分析图片", "识别图片", "image", "screenshot")):
96
+ _add_unique(intents, "vision")
97
+ if _contains_any(low, ("浏览器", "网页截图", "打开网页", "browser", "playwright")):
98
+ _add_unique(intents, "browser")
99
+ if _contains_any(low, ("pdf", "docx", "word", "excel", "xlsx", "csv", "文件分析", "上传文件")):
100
+ _add_unique(intents, "file_analysis")
101
+ if _contains_any(low, ("github", "pull request", "pr ", "issue", "ci")):
102
+ _add_unique(intents, "github")
103
+ if _contains_any(low, ("mcp", "server", "tools", "skills")):
104
+ _add_unique(intents, "mcp")
105
+ if _contains_any(low, ("ollama", "本地模型", "local model")):
106
+ _add_unique(intents, "local_model")
107
+ if _contains_any(low, ("阿里云", "aliyun", "cloud service", "云端服务")):
108
+ _add_unique(intents, "cloud")
109
+ if _contains_any(low, ("搜索", "联网", "上网查", "web search", "google", "查一下", "搜一下", "最新消息")):
110
+ _add_unique(intents, "web_search")
111
+ if _contains_any(low, ("加密货币", "比特币", "以太坊", "bitcoin", "btc", "eth", "crypto", "币安", "binance", "okx", "资金费率", "funding rate")):
112
+ _add_unique(intents, "crypto")
113
+ if _contains_any(low, ("足球", "球赛", "比分预测", "世界杯", "欧洲杯", "英超", "西甲", "football", "soccer", "world cup", "premier league")):
114
+ _add_unique(intents, "sports")
115
+
116
+ return tuple(intents)
117
+
118
+
119
+ def _service_names(intents: tuple[str, ...]) -> tuple[str, ...]:
120
+ services: list[str] = []
121
+
122
+ def service(name: str) -> None:
123
+ _add_unique(services, name)
124
+
125
+ if any(i in intents for i in ("market_snapshot", "market_analysis", "chart", "dashboard", "report", "backtest", "strategy", "market_research")):
126
+ service("market_data")
127
+ if "chart" in intents:
128
+ service("chart_renderer")
129
+ if "dashboard" in intents:
130
+ service("dashboard_generator")
131
+ if "report" in intents:
132
+ service("report_generator")
133
+ if "backtest" in intents or "strategy" in intents:
134
+ service("backtest_engine")
135
+ if "vision" in intents:
136
+ service("vision_input")
137
+ if "screenshot" in intents:
138
+ service("screenshot")
139
+ if "browser" in intents:
140
+ service("browser")
141
+ if "file_analysis" in intents:
142
+ service("file_parser")
143
+ if "github" in intents:
144
+ service("github_cli")
145
+ if "mcp" in intents:
146
+ service("mcp")
147
+ if "local_model" in intents:
148
+ service("local_llm")
149
+ if "cloud" in intents:
150
+ service("cloud_runtime")
151
+ if "web_search" in intents:
152
+ service("web_search")
153
+ if "crypto" in intents:
154
+ service("crypto_data")
155
+ if "sports" in intents:
156
+ service("sports_data")
157
+ return tuple(services)
158
+
159
+
160
+ def build_intent_route(message: str) -> IntentRoute:
161
+ low = message.lower().strip()
162
+ intents = detect_intents(message)
163
+ explicit_code = _contains_any(low, (
164
+ "代码", "脚本", "python", "程序", "实现", "开发", "修改文件",
165
+ "写代码", "编写代码", "策略代码", "保存为.py", ".py",
166
+ "script", "code", "program", "implement", "edit file", "write file",
167
+ ))
168
+ try:
169
+ from intent_classifier import (
170
+ INTENT_ANALYSIS,
171
+ INTENT_CODING,
172
+ INTENT_FINANCE,
173
+ INTENT_GENERAL,
174
+ INTENT_REALTIME,
175
+ classify_intent_sync,
176
+ is_visual_market_artifact_request,
177
+ )
178
+ classifier_intent = classify_intent_sync(message)
179
+ visual_artifact = bool(is_visual_market_artifact_request(message))
180
+ except Exception:
181
+ INTENT_ANALYSIS = "analysis"
182
+ INTENT_CODING = "coding"
183
+ INTENT_FINANCE = "finance"
184
+ INTENT_GENERAL = "general"
185
+ INTENT_REALTIME = "realtime"
186
+ classifier_intent = INTENT_FINANCE
187
+ visual_artifact = any(i in intents for i in ("chart", "dashboard", "report", "ui_artifact"))
188
+
189
+ if intents:
190
+ primary = intents[0]
191
+ elif classifier_intent == INTENT_REALTIME:
192
+ primary = "market_snapshot"
193
+ elif classifier_intent == INTENT_ANALYSIS:
194
+ primary = "market_analysis"
195
+ elif classifier_intent == INTENT_CODING:
196
+ primary = "code"
197
+ elif classifier_intent == INTENT_GENERAL:
198
+ primary = "general"
199
+ elif classifier_intent == INTENT_FINANCE:
200
+ primary = "finance"
201
+ else:
202
+ primary = "finance"
203
+
204
+ market_related = any(i in intents for i in (
205
+ "market_snapshot", "market_analysis", "chart", "dashboard", "report",
206
+ "backtest", "strategy", "market_research",
207
+ )) or classifier_intent in {INTENT_ANALYSIS, INTENT_REALTIME, INTENT_FINANCE}
208
+
209
+ return IntentRoute(
210
+ message=message,
211
+ primary=primary,
212
+ intents=intents,
213
+ services=_service_names(intents),
214
+ explicit_code=explicit_code,
215
+ visual_artifact=visual_artifact,
216
+ market_related=market_related,
217
+ )
@@ -0,0 +1,48 @@
1
+ """Simple lifecycle shell hooks for the CLI adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import subprocess
7
+ from pathlib import Path
8
+ from typing import Mapping
9
+
10
+
11
+ def run_event_hook(
12
+ event: str,
13
+ *,
14
+ config_dir: Path,
15
+ cwd: Path | None = None,
16
+ env_extra: Mapping[str, str] | None = None,
17
+ timeout: int = 10,
18
+ ) -> None:
19
+ """Run executable hook scripts for a lifecycle event.
20
+
21
+ Script locations are intentionally simple for compatibility:
22
+ `<config_dir>/hooks/<event>.sh` and `<cwd>/.aria/hooks/<event>.sh`.
23
+ Hook failures are non-fatal.
24
+ """
25
+ root = cwd or Path.cwd()
26
+ dirs = [
27
+ config_dir / "hooks",
28
+ root / ".aria" / "hooks",
29
+ ]
30
+ env = dict(os.environ)
31
+ env["ARIA_EVENT"] = event
32
+ if env_extra:
33
+ env.update({str(k): str(v) for k, v in env_extra.items()})
34
+ for hook_dir in dirs:
35
+ script = hook_dir / f"{event}.sh"
36
+ if not script.exists() or script.stat().st_size <= 0:
37
+ continue
38
+ try:
39
+ subprocess.run(
40
+ [str(script)],
41
+ env=env,
42
+ timeout=timeout,
43
+ capture_output=True,
44
+ text=True,
45
+ check=False,
46
+ )
47
+ except Exception:
48
+ continue
apps/cli/main.py ADDED
@@ -0,0 +1,29 @@
1
+ """Installed console entrypoint for Aria Code.
2
+
3
+ The terminal implementation lives in ``aria_cli`` as an async ``main()``. A
4
+ console script (declared in pyproject ``[project.scripts]`` as
5
+ ``aria-code = "apps.cli.main:main"``) must be a *synchronous* callable, so this
6
+ shim wraps the coroutine in ``asyncio.run`` — mirroring the
7
+ ``if __name__ == "__main__"`` block in ``aria_cli.py``. Calling the async
8
+ ``aria_cli.main`` directly from a console script would only create a coroutine
9
+ that is never awaited.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ import sys
16
+
17
+
18
+ def main() -> None:
19
+ """Synchronous entry point for the ``aria-code`` command."""
20
+ from aria_cli import main as _async_main
21
+
22
+ try:
23
+ asyncio.run(_async_main())
24
+ except KeyboardInterrupt:
25
+ print()
26
+ sys.exit(0)
27
+
28
+
29
+ __all__ = ["main"]
@@ -0,0 +1,135 @@
1
+ """Local market symbol metadata used to normalize provider output.
2
+
3
+ Providers occasionally omit display names or return a generic/default currency.
4
+ This module keeps deterministic exchange metadata close to the CLI so prompt
5
+ injection and tool responses do not ask the model to guess symbol identity.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from typing import Any, Mapping
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class MarketAssetMeta:
15
+ symbol: str
16
+ short_label: str
17
+ display_name: str
18
+ currency: str
19
+ exchange: str = ""
20
+
21
+
22
+ _SYMBOL_META: dict[str, MarketAssetMeta] = {
23
+ "MC.PA": MarketAssetMeta(
24
+ symbol="MC.PA",
25
+ short_label="LVMH/路易威登",
26
+ display_name="LVMH Moet Hennessy Louis Vuitton SE",
27
+ currency="EUR",
28
+ exchange="Euronext Paris",
29
+ ),
30
+ "RMS.PA": MarketAssetMeta(
31
+ symbol="RMS.PA",
32
+ short_label="Hermes/爱马仕",
33
+ display_name="Hermes International SCA",
34
+ currency="EUR",
35
+ exchange="Euronext Paris",
36
+ ),
37
+ "KER.PA": MarketAssetMeta(
38
+ symbol="KER.PA",
39
+ short_label="Kering/开云集团",
40
+ display_name="Kering SA",
41
+ currency="EUR",
42
+ exchange="Euronext Paris",
43
+ ),
44
+ }
45
+
46
+ _SUFFIX_CURRENCY: tuple[tuple[str, str], ...] = (
47
+ (".SS", "CNY"),
48
+ (".SH", "CNY"),
49
+ (".SZ", "CNY"),
50
+ (".HK", "HKD"),
51
+ (".PA", "EUR"),
52
+ (".DE", "EUR"),
53
+ (".MI", "EUR"),
54
+ (".AS", "EUR"),
55
+ (".BR", "EUR"),
56
+ (".MC", "EUR"),
57
+ (".LS", "EUR"),
58
+ (".SW", "CHF"),
59
+ (".L", "GBp"),
60
+ (".TO", "CAD"),
61
+ (".AX", "AUD"),
62
+ (".T", "JPY"),
63
+ )
64
+
65
+
66
+ def normalize_market_symbol(symbol: str) -> str:
67
+ return str(symbol or "").strip().upper()
68
+
69
+
70
+ def market_asset_meta(symbol: str) -> MarketAssetMeta | None:
71
+ return _SYMBOL_META.get(normalize_market_symbol(symbol))
72
+
73
+
74
+ def default_currency_for_symbol(symbol: str) -> str:
75
+ normalized = normalize_market_symbol(symbol)
76
+ meta = market_asset_meta(normalized)
77
+ if meta:
78
+ return meta.currency
79
+ for suffix, currency in _SUFFIX_CURRENCY:
80
+ if normalized.endswith(suffix):
81
+ return currency
82
+ return ""
83
+
84
+
85
+ def _provider_currency_looks_wrong(symbol: str, provider_currency: Any, inferred_currency: str) -> bool:
86
+ if not inferred_currency:
87
+ return False
88
+ currency = str(provider_currency or "").strip().upper()
89
+ inferred = inferred_currency.upper()
90
+ if not currency:
91
+ return True
92
+ if currency == inferred:
93
+ return False
94
+ normalized = normalize_market_symbol(symbol)
95
+ # Most wrong cases here come from global fallbacks defaulting non-US symbols
96
+ # to USD. Do not override real US tickers that have no exchange suffix.
97
+ return currency == "USD" and "." in normalized
98
+
99
+
100
+ def enrich_market_quote(symbol: str, quote: Mapping[str, Any] | None) -> dict[str, Any]:
101
+ """Return a quote copy with deterministic symbol name/currency metadata."""
102
+ data = dict(quote or {})
103
+ normalized = normalize_market_symbol(symbol or data.get("symbol") or "")
104
+ if normalized and not data.get("symbol"):
105
+ data["symbol"] = normalized
106
+
107
+ meta = market_asset_meta(normalized)
108
+ inferred_currency = default_currency_for_symbol(normalized)
109
+ if _provider_currency_looks_wrong(normalized, data.get("currency"), inferred_currency):
110
+ data["currency"] = inferred_currency
111
+ data["currency_source"] = "symbol_metadata"
112
+ elif not data.get("currency") and inferred_currency:
113
+ data["currency"] = inferred_currency
114
+ data["currency_source"] = "symbol_metadata"
115
+
116
+ if meta:
117
+ current_name = str(data.get("name") or "").strip()
118
+ if not current_name or current_name.upper() == normalized:
119
+ data["name"] = meta.display_name
120
+ data.setdefault("display_name", meta.display_name)
121
+ data.setdefault("short_label", meta.short_label)
122
+ data.setdefault("exchange", meta.exchange)
123
+ return data
124
+
125
+
126
+ def market_display_label(symbol: str, quote: Mapping[str, Any] | None = None) -> str:
127
+ data = dict(quote or {})
128
+ normalized = normalize_market_symbol(symbol or data.get("symbol") or "")
129
+ meta = market_asset_meta(normalized)
130
+ if meta:
131
+ return f"{meta.short_label}({normalized})"
132
+ name = str(data.get("name") or data.get("display_name") or "").strip()
133
+ if name and name.upper() != normalized:
134
+ return f"{name}({normalized})"
135
+ return normalized or name or "market_data"