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
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""BusinessWorkflowCommandsMixin — research, earnings, realty, ops workflow commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BusinessWorkflowCommandsMixin:
|
|
7
|
+
"""Mixin: financial research and realty/operations workflow commands."""
|
|
8
|
+
|
|
9
|
+
async def cmd_research(self, args: str):
|
|
10
|
+
sym = args.strip().upper() or "AAPL"
|
|
11
|
+
# Route research through the deterministic team workflow so data
|
|
12
|
+
# fetching, rendering, and artifact saving happen in one service path.
|
|
13
|
+
await self.cmd_team(f"{sym} --full")
|
|
14
|
+
|
|
15
|
+
async def cmd_earnings_workflow(self, args: str):
|
|
16
|
+
parts = args.strip().split()
|
|
17
|
+
sym = parts[0].upper() if parts else "AAPL"
|
|
18
|
+
period = " ".join(parts[1:]) if len(parts) > 1 else "最近一个季度"
|
|
19
|
+
report_type = "deep" if any(k in period.lower() for k in ("deep", "深度", "全年", "年报", "10-k")) else "standard"
|
|
20
|
+
# Earnings review is a report workflow, not a code-generation prompt.
|
|
21
|
+
# The report command already fetches market data, records provenance,
|
|
22
|
+
# asks the model for the narrative, and saves the Markdown artifact.
|
|
23
|
+
await self.cmd_report(f"{sym} --format md --type {report_type}")
|
|
24
|
+
|
|
25
|
+
async def cmd_asset_diag(self, args: str):
|
|
26
|
+
asset_id = args.strip()
|
|
27
|
+
if not asset_id:
|
|
28
|
+
_p("用法: /asset-diag <资产ID或名称> 例: /asset-diag asset_000001", "dim")
|
|
29
|
+
return
|
|
30
|
+
asset_info = {}
|
|
31
|
+
api_url = self.terminal.config.get("api_url", "http://localhost:8000")
|
|
32
|
+
try:
|
|
33
|
+
import aiohttp
|
|
34
|
+
async with aiohttp.ClientSession() as sess:
|
|
35
|
+
async with sess.get(
|
|
36
|
+
f"{api_url}/api/realty/assets/{asset_id}",
|
|
37
|
+
timeout=aiohttp.ClientTimeout(total=5)
|
|
38
|
+
) as resp:
|
|
39
|
+
if resp.status == 200:
|
|
40
|
+
body = await resp.json()
|
|
41
|
+
raw = body.get("data", {})
|
|
42
|
+
asset_info = {
|
|
43
|
+
"area": raw.get("area_sqm", 0),
|
|
44
|
+
"location": raw.get("address", asset_id),
|
|
45
|
+
"vacancy_days": raw.get("vacancy_days", 0),
|
|
46
|
+
"expected_rent": raw.get("monthly_rent_market", 0),
|
|
47
|
+
"allowed_business": raw.get("allowed_business_types", []),
|
|
48
|
+
"property_state": raw.get("property_state", "正常"),
|
|
49
|
+
"floor_height": raw.get("floor_height", 0),
|
|
50
|
+
}
|
|
51
|
+
_p(f"已从 API 加载资产: {raw.get('name', asset_id)}", "ok")
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
54
|
+
if not asset_info:
|
|
55
|
+
_p("[dim]提示: 未找到资产数据,以 ID 作为位置标识演示(结果仅供参考)[/dim]")
|
|
56
|
+
asset_info = {
|
|
57
|
+
"location": asset_id,
|
|
58
|
+
"area": 0, "vacancy_days": 0,
|
|
59
|
+
"expected_rent": 0, "allowed_business": [],
|
|
60
|
+
"property_state": "正常",
|
|
61
|
+
}
|
|
62
|
+
await self._run_realty_agent("asset_diagnosis", asset_id, {"asset_info": asset_info})
|
|
63
|
+
|
|
64
|
+
async def cmd_contract_draft(self, args: str):
|
|
65
|
+
parts = args.split() if args else []
|
|
66
|
+
project_id = parts[0] if parts else "demo_project"
|
|
67
|
+
nego = {"guaranteed_amount": 0, "revenue_share_pct": 0}
|
|
68
|
+
for i, p in enumerate(parts):
|
|
69
|
+
if p == "--guaranteed" and i + 1 < len(parts):
|
|
70
|
+
try:
|
|
71
|
+
nego["guaranteed_amount"] = float(parts[i + 1])
|
|
72
|
+
except ValueError:
|
|
73
|
+
pass
|
|
74
|
+
elif p == "--share" and i + 1 < len(parts):
|
|
75
|
+
try:
|
|
76
|
+
nego["revenue_share_pct"] = float(parts[i + 1])
|
|
77
|
+
except ValueError:
|
|
78
|
+
pass
|
|
79
|
+
await self._run_realty_agent("contract_rules", project_id, {
|
|
80
|
+
"negotiation": nego,
|
|
81
|
+
"asset_info": {"name": project_id},
|
|
82
|
+
"operator_info": {},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
async def cmd_revenue_calc(self, args: str):
|
|
86
|
+
parts = args.split() if args else []
|
|
87
|
+
if len(parts) < 2:
|
|
88
|
+
_p("用法: /revenue-calc <project_id> <总流水> [退款] 例: /revenue-calc proj_001 200000", "dim")
|
|
89
|
+
return
|
|
90
|
+
project_id = parts[0]
|
|
91
|
+
try:
|
|
92
|
+
gross = float(parts[1])
|
|
93
|
+
refunds = float(parts[2]) if len(parts) > 2 else 0.0
|
|
94
|
+
except ValueError:
|
|
95
|
+
_p("流水金额必须为数字", "error")
|
|
96
|
+
return
|
|
97
|
+
api_url = self.terminal.config.get("api_url", "http://localhost:8000")
|
|
98
|
+
rules = {}
|
|
99
|
+
try:
|
|
100
|
+
import aiohttp
|
|
101
|
+
async with aiohttp.ClientSession() as sess:
|
|
102
|
+
async with sess.get(f"{api_url}/api/realty/contracts/{project_id}",
|
|
103
|
+
timeout=aiohttp.ClientTimeout(total=5)) as resp:
|
|
104
|
+
if resp.status == 200:
|
|
105
|
+
body = await resp.json()
|
|
106
|
+
rules = body.get("data", {})
|
|
107
|
+
except Exception:
|
|
108
|
+
pass
|
|
109
|
+
if not rules:
|
|
110
|
+
_p(f"[dim]未找到 {project_id} 的合同规则,使用默认值演示[/dim]")
|
|
111
|
+
rules = {"guaranteed_monthly": 30000, "revenue_share_pct": 10,
|
|
112
|
+
"revenue_share_base": 0, "platform_fee_pct": 5,
|
|
113
|
+
"risk_reserve_pct": 3, "settlement_cycle": "monthly"}
|
|
114
|
+
await self._run_realty_agent("revenue_share", project_id, {
|
|
115
|
+
"contract_rules": rules,
|
|
116
|
+
"transaction_data": {"gross_revenue": gross, "refunds": refunds},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
async def cmd_realty_risk_scan(self, args: str):
|
|
120
|
+
project_id = args.strip() or "demo_project"
|
|
121
|
+
if HAS_RICH:
|
|
122
|
+
console.print(f"\n [bold]风险扫描[/bold] 项目: [cyan]{project_id}[/cyan]")
|
|
123
|
+
api_url = self.terminal.config.get("api_url", "http://localhost:8000")
|
|
124
|
+
try:
|
|
125
|
+
import aiohttp
|
|
126
|
+
async with aiohttp.ClientSession() as sess:
|
|
127
|
+
async with sess.get(
|
|
128
|
+
f"{api_url}/api/realty/risks/scan/{project_id}",
|
|
129
|
+
timeout=aiohttp.ClientTimeout(total=10)
|
|
130
|
+
) as resp:
|
|
131
|
+
if resp.status == 200:
|
|
132
|
+
data = await resp.json()
|
|
133
|
+
_print_risk_scan(data)
|
|
134
|
+
return
|
|
135
|
+
except Exception:
|
|
136
|
+
pass
|
|
137
|
+
await self._run_realty_team(["cashflow_verify", "energy_anomaly", "fulfillment_risk"], project_id, {})
|
|
138
|
+
|
|
139
|
+
async def cmd_ops_report(self, args: str):
|
|
140
|
+
project_id = args.strip() or "demo_project"
|
|
141
|
+
api_url = self.terminal.config.get("api_url", "http://localhost:8000")
|
|
142
|
+
project_info = {"name": project_id, "area": 0, "business_type": "未知"}
|
|
143
|
+
performance_data = {}
|
|
144
|
+
marketing_data = {}
|
|
145
|
+
try:
|
|
146
|
+
import aiohttp
|
|
147
|
+
async with aiohttp.ClientSession() as sess:
|
|
148
|
+
async with sess.get(
|
|
149
|
+
f"{api_url}/api/realty/assets/{project_id}",
|
|
150
|
+
timeout=aiohttp.ClientTimeout(total=5)
|
|
151
|
+
) as resp:
|
|
152
|
+
if resp.status == 200:
|
|
153
|
+
raw = (await resp.json()).get("data", {})
|
|
154
|
+
project_info = {
|
|
155
|
+
"name": raw.get("name", project_id),
|
|
156
|
+
"area": raw.get("area_sqm", 0),
|
|
157
|
+
"business_type": raw.get("current_business_type", "未知"),
|
|
158
|
+
"open_date": raw.get("open_date", ""),
|
|
159
|
+
}
|
|
160
|
+
async with sess.get(
|
|
161
|
+
f"{api_url}/api/realty/revenue/splits?project_id={project_id}&page_size=3",
|
|
162
|
+
timeout=aiohttp.ClientTimeout(total=5)
|
|
163
|
+
) as resp2:
|
|
164
|
+
if resp2.status == 200:
|
|
165
|
+
splits = (await resp2.json()).get("data", {}).get("splits", [])
|
|
166
|
+
if splits:
|
|
167
|
+
revenues = [s["split_result"].get("gross_revenue", 0) for s in splits]
|
|
168
|
+
avg_rev = sum(revenues) / len(revenues)
|
|
169
|
+
performance_data = {"monthly_revenue": avg_rev, "daily_visits": 0}
|
|
170
|
+
_p(f"已加载近 {len(splits)} 期分账数据,月均流水 {avg_rev:,.0f}元", "ok")
|
|
171
|
+
except Exception:
|
|
172
|
+
pass
|
|
173
|
+
if not performance_data:
|
|
174
|
+
_p("[dim]提示: 未找到运营数据,建议先录入分账记录后再运行此命令[/dim]")
|
|
175
|
+
await self._run_realty_agent("ops_optimize", project_id, {
|
|
176
|
+
"project_info": project_info,
|
|
177
|
+
"performance_data": performance_data,
|
|
178
|
+
"marketing_data": marketing_data,
|
|
179
|
+
"peer_benchmarks": {"revenue_per_sqm": 300},
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
async def cmd_exit_calc(self, args: str):
|
|
183
|
+
parts = args.split() if args else []
|
|
184
|
+
project_id = parts[0] if parts else "demo_project"
|
|
185
|
+
reason = "到期终止"
|
|
186
|
+
for i, p in enumerate(parts):
|
|
187
|
+
if p == "--reason" and i + 1 < len(parts):
|
|
188
|
+
reason = " ".join(parts[i + 1:])
|
|
189
|
+
break
|
|
190
|
+
api_url = self.terminal.config.get("api_url", "http://localhost:8000")
|
|
191
|
+
project_info = {"name": project_id}
|
|
192
|
+
financials = {"deposit_amount": 0, "unpaid_invoices": 0,
|
|
193
|
+
"guaranteed_monthly": 0, "exit_penalty_months": 3,
|
|
194
|
+
"prepayment_received": 0, "renovation_cost": 0}
|
|
195
|
+
try:
|
|
196
|
+
import aiohttp
|
|
197
|
+
async with aiohttp.ClientSession() as sess:
|
|
198
|
+
async with sess.get(
|
|
199
|
+
f"{api_url}/api/realty/contracts/{project_id}",
|
|
200
|
+
timeout=aiohttp.ClientTimeout(total=5)
|
|
201
|
+
) as resp:
|
|
202
|
+
if resp.status == 200:
|
|
203
|
+
ctr = (await resp.json()).get("data", {})
|
|
204
|
+
from datetime import date
|
|
205
|
+
start = ctr.get("start_date", "")
|
|
206
|
+
used_months = 0
|
|
207
|
+
if start:
|
|
208
|
+
try:
|
|
209
|
+
from dateutil.relativedelta import relativedelta
|
|
210
|
+
d0 = date.fromisoformat(start)
|
|
211
|
+
delta = relativedelta(date.today(), d0)
|
|
212
|
+
used_months = delta.years * 12 + delta.months
|
|
213
|
+
except Exception:
|
|
214
|
+
pass
|
|
215
|
+
project_info.update({
|
|
216
|
+
"contract_years": ctr.get("contract_years", 1),
|
|
217
|
+
"used_months": used_months,
|
|
218
|
+
"contract_end": ctr.get("end_date", ""),
|
|
219
|
+
})
|
|
220
|
+
financials.update({
|
|
221
|
+
"deposit_amount": ctr.get("deposit_amount", 0),
|
|
222
|
+
"guaranteed_monthly": ctr.get("guaranteed_monthly", 0),
|
|
223
|
+
"exit_penalty_months": ctr.get("exit_penalty_months", 3),
|
|
224
|
+
})
|
|
225
|
+
_p(f"已加载合同规则: 保底 {ctr.get('guaranteed_monthly',0):,}元/月", "ok")
|
|
226
|
+
async with sess.get(
|
|
227
|
+
f"{api_url}/api/realty/invoices?project_id={project_id}&status=unpaid",
|
|
228
|
+
timeout=aiohttp.ClientTimeout(total=5)
|
|
229
|
+
) as resp2:
|
|
230
|
+
if resp2.status == 200:
|
|
231
|
+
body2 = await resp2.json()
|
|
232
|
+
summary = body2.get("data", {}).get("summary", {})
|
|
233
|
+
unpaid = summary.get("total_amount", 0) - summary.get("paid_amount", 0)
|
|
234
|
+
financials["unpaid_invoices"] = unpaid
|
|
235
|
+
if unpaid > 0:
|
|
236
|
+
_p(f"发现未结账单合计: {unpaid:,.2f}元", "ok")
|
|
237
|
+
except Exception:
|
|
238
|
+
pass
|
|
239
|
+
await self._run_realty_agent("exit_settlement", project_id, {
|
|
240
|
+
"project_info": project_info,
|
|
241
|
+
"financials": financials,
|
|
242
|
+
"asset_condition": {},
|
|
243
|
+
"exit_reason": reason,
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
async def _run_realty_agent(self, agent_name: str, project_id: str, input_data: dict):
|
|
247
|
+
if HAS_RICH:
|
|
248
|
+
with console.status(f"[dim]运行 {agent_name} Agent...[/dim]", spinner="dots"):
|
|
249
|
+
result = await self._call_realty_agent(agent_name, project_id, input_data)
|
|
250
|
+
else:
|
|
251
|
+
print(f"Running {agent_name}...")
|
|
252
|
+
result = await self._call_realty_agent(agent_name, project_id, input_data)
|
|
253
|
+
if result:
|
|
254
|
+
_print_realty_result(result, agent_name)
|
|
255
|
+
|
|
256
|
+
async def _run_realty_team(self, agents: list, project_id: str, input_data: dict):
|
|
257
|
+
import asyncio
|
|
258
|
+
if HAS_RICH:
|
|
259
|
+
with console.status(f"[dim]并行扫描 {', '.join(agents)}...[/dim]", spinner="dots"):
|
|
260
|
+
tasks = [self._call_realty_agent(n, project_id, input_data) for n in agents]
|
|
261
|
+
results = await asyncio.gather(*tasks, return_exceptions=False)
|
|
262
|
+
else:
|
|
263
|
+
tasks = [self._call_realty_agent(n, project_id, input_data) for n in agents]
|
|
264
|
+
results = await asyncio.gather(*tasks, return_exceptions=False)
|
|
265
|
+
for res, name in zip(results, agents):
|
|
266
|
+
if res:
|
|
267
|
+
_print_realty_result(res, name)
|
|
268
|
+
|
|
269
|
+
async def _call_realty_agent(self, agent_name: str, project_id: str, input_data: dict):
|
|
270
|
+
try:
|
|
271
|
+
from agents.registry import get_registry
|
|
272
|
+
cls = get_registry().get(agent_name)
|
|
273
|
+
if not cls:
|
|
274
|
+
_p(f"Agent '{agent_name}' 未注册", "error")
|
|
275
|
+
return None
|
|
276
|
+
llm = None
|
|
277
|
+
try:
|
|
278
|
+
from providers.llm.registry import list_available_providers, get_provider
|
|
279
|
+
avail = [p for p in list_available_providers() if p.get("available")]
|
|
280
|
+
if avail:
|
|
281
|
+
llm = get_provider(avail[0]["name"])
|
|
282
|
+
except Exception:
|
|
283
|
+
pass
|
|
284
|
+
agent = cls(llm_provider=llm)
|
|
285
|
+
result = await agent.analyze(project_id, input_data)
|
|
286
|
+
return result
|
|
287
|
+
except Exception as e:
|
|
288
|
+
_p(f"Agent {agent_name} 执行失败: {e}", "error")
|
|
289
|
+
return None
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Shared CLI command metadata.
|
|
2
|
+
|
|
3
|
+
This module is intentionally UI-free so future channel adapters such as Feishu
|
|
4
|
+
or a local gateway can reuse the same command categories without importing the
|
|
5
|
+
large terminal implementation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import FrozenSet, Tuple
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class DirectCommandSpec:
|
|
16
|
+
name: str
|
|
17
|
+
method_name: str
|
|
18
|
+
async_method: bool = False
|
|
19
|
+
watchable: bool = False
|
|
20
|
+
aliases: Tuple[str, ...] = ()
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def names(self) -> Tuple[str, ...]:
|
|
24
|
+
return (self.name, *self.aliases)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DIRECT_COMMANDS: Tuple[DirectCommandSpec, ...] = (
|
|
28
|
+
DirectCommandSpec("quote", "cmd_quote", async_method=True, watchable=True),
|
|
29
|
+
DirectCommandSpec("backtest", "cmd_backtest", async_method=True),
|
|
30
|
+
DirectCommandSpec("health", "cmd_health", async_method=True, watchable=True),
|
|
31
|
+
DirectCommandSpec("doctor", "cmd_doctor"),
|
|
32
|
+
DirectCommandSpec("tools", "cmd_tools"),
|
|
33
|
+
DirectCommandSpec("skills", "cmd_skills"),
|
|
34
|
+
DirectCommandSpec("sessions", "cmd_sessions"),
|
|
35
|
+
DirectCommandSpec("watch", "cmd_watch", aliases=("watchlist",)),
|
|
36
|
+
DirectCommandSpec("export", "cmd_export", async_method=True),
|
|
37
|
+
DirectCommandSpec("tv", "cmd_tv", async_method=True),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
DIRECT_COMMAND_MAP = {
|
|
42
|
+
alias: spec
|
|
43
|
+
for spec in DIRECT_COMMANDS
|
|
44
|
+
for alias in spec.names
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
WATCHABLE_DIRECT_COMMANDS: FrozenSet[str] = frozenset(
|
|
49
|
+
alias
|
|
50
|
+
for spec in DIRECT_COMMANDS
|
|
51
|
+
if spec.watchable
|
|
52
|
+
for alias in spec.names
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Commands shown by default in /help. Other slash commands remain executable but
|
|
57
|
+
# are hidden to keep the startup surface compact.
|
|
58
|
+
VISIBLE_SLASH_COMMANDS: FrozenSet[str] = frozenset({
|
|
59
|
+
# Session
|
|
60
|
+
"/help", "/clear", "/compact", "/cost", "/status", "/health",
|
|
61
|
+
"/regen", "/undo", "/copy", "/recap", "/btw",
|
|
62
|
+
# Sessions
|
|
63
|
+
"/save", "/load", "/sessions", "/recall", "/export",
|
|
64
|
+
# Config
|
|
65
|
+
"/model", "/thinking", "/config", "/permissions", "/privacy",
|
|
66
|
+
# Setup & discovery
|
|
67
|
+
"/setup", "/apikey", "/doctor", "/architecture", "/mcp", "/skills", "/tools", "/packages",
|
|
68
|
+
# Auth
|
|
69
|
+
"/login", "/logout", "/whoami",
|
|
70
|
+
# Persistent data (direct writes)
|
|
71
|
+
"/alert", "/journal", "/watch", "/note", "/todo", "/memory",
|
|
72
|
+
# Broker
|
|
73
|
+
"/broker", "/account", "/positions", "/orders", "/paper", "/trade",
|
|
74
|
+
# Code & project
|
|
75
|
+
"/project", "/init", "/review", "/code", "/plan", "/run", "/tasks", "/completions", "/lsp",
|
|
76
|
+
# Research
|
|
77
|
+
"/team", "/deep",
|
|
78
|
+
# Quant
|
|
79
|
+
"/backtest", "/wf", "/tv",
|
|
80
|
+
# UI generation
|
|
81
|
+
"/ui",
|
|
82
|
+
# Other
|
|
83
|
+
"/artifacts", "/vision", "/upload-image", "/file", "/strategy", "/accuracy",
|
|
84
|
+
})
|