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/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"
|