feat(WS-133): 暗色金融前端重建 + 报告角色链契约(全栈接管) - #31
Merged
Conversation
Add tradingagents/utils/role_chain.py mapping the multi-agent graph final_state into a structured RoleChainReport (Risk Judge decision pinned -> Analyst Team -> Research Debate -> Trader -> Risk Debate -> summary). Integrate into report_formatter so /api/reports list/detail now serve a role_chain field (preview: decision+analysts+trading_decision label; detail: full chain). Persist role_chain into final_state at analysis completion. Heuristic priceBand/confidence/horizon degrade to null; risk-debate keys normalize fork risky/safe and upstream aggressive/ conservative. Includes docs/report-role-chain-contract.md. Verified: py_compile + sample-data end-to-end on both preview and detail. Co-authored-by: multica-agent <github@multica.ai>
…ole-chain report Rebuild the frontend around the approved dark-finance research design: Design system - Evolve Tailwind palette to deep-finance tokens (#060a10 base, mint #9ee5c9 / gold #e5bd72 / red #f48b8b / blue #81bbed) + verdict semantic group; add Noto Serif SC headings, tabular mono numbers, verdict pills, surface/hero/chain-rail utilities. New search-first app shell - SiteHeader (research / 公开榜单 / 订阅 / 本地模型 / 管理控制台 nav, auth-aware), SiteFooter with global non-execution disclaimer, reusable SearchBar, SiteLayout wrapper. Pages (all routed per the approved 11-page design) - / search-first home with hero search, features, recent public research - /research launch a multi-agent analysis from a query - /leaderboard public research feed with market filters - /report?id= full role-chain report view (decision pinned -> analysts -> debate -> trader -> risk debate -> summary), MD/PDF export, legacy fallback - /subscription per-analysis plans + billing notes - /settings local LLM key vault (browser-only, reuses useLocalLLMKeys) - /me, /me/billing, /me/preferences user workspace - auth via existing /login /register /auth (auto-reskinned) Report contract wiring - types/report.ts mirrors the backend RoleChainReport 1:1 - lib/api/reports.ts report list/public-feed/detail client consuming /api/reports role_chain field Verification - tsc --noEmit exit 0 (also fixed a pre-existing refetch typecheck in scheduled-tasks) - next build compiled successfully; all new routes prerendered as static content under output: export Co-authored-by: multica-agent <github@multica.ai>
There was a problem hiding this comment.
Pull request overview
This PR implements WS-133 by introducing a structured “role-chain” report contract on the backend (derived from TradingAgents graph final_state) and rebuilding the frontend into a dark-finance, search-first experience that can render the role-chain report (including export links) and related navigation/pages.
Changes:
- Backend: add
tradingagents/utils/role_chain.pyplus report API projection inweb/backend/services/report_formatter.py, and persist role-chain intofinal_stateon analysis completion. - Frontend: introduce new dark-finance design tokens (Tailwind + CSS utilities) and a new site shell (header/footer/layout/search).
- Frontend: add new pages for research launch, leaderboard, report rendering (role-chain + legacy fallback), subscription/settings/me workspace.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| web/frontend/tailwind.config.js | Updates dark-finance palette, verdict semantics, and typography tokens. |
| web/frontend/src/types/report.ts | Adds TS contract for role_chain report payload + UI mapping helpers. |
| web/frontend/src/lib/api/reports.ts | Adds report list/public feed/detail/export API helpers. |
| web/frontend/src/components/site/SiteLayout.tsx | New shared page layout wrapper. |
| web/frontend/src/components/site/SiteHeader.tsx | New navigation header with auth/admin awareness. |
| web/frontend/src/components/site/SiteFooter.tsx | New footer with global non-execution disclaimer. |
| web/frontend/src/components/site/SearchBar.tsx | Search-first entry component that routes to /research. |
| web/frontend/src/components/site/DisclaimerBanner.tsx | Shared disclaimer banner for report page. |
| web/frontend/src/components/report/RoleChainReport.tsx | Role-chain report renderer (decision → analysts → debates → plan → summary). |
| web/frontend/src/app/subscription/page.tsx | Subscription placeholder UI. |
| web/frontend/src/app/settings/page.tsx | Local LLM key management UI (browser-local). |
| web/frontend/src/app/scheduled-tasks/page.tsx | Hook signature update (adds refetch). |
| web/frontend/src/app/research/page.tsx | Research launch flow from query params + login gating. |
| web/frontend/src/app/report/page.tsx | Report detail page driven by ?id= + legacy fallback. |
| web/frontend/src/app/page.tsx | Replaces prior “workbench” home with search-first home + recent public research. |
| web/frontend/src/app/me/preferences/page.tsx | New preferences UI (currently example toggles). |
| web/frontend/src/app/me/page.tsx | “My analyses” listing UI. |
| web/frontend/src/app/me/billing/page.tsx | Billing ledger placeholder UI. |
| web/frontend/src/app/leaderboard/page.tsx | Public leaderboard UI with market filter. |
| web/frontend/src/app/layout.tsx | Updates app metadata/theme color/font loading. |
| web/frontend/src/app/globals.css | Adds dark-finance design-system utility classes (surface/pills/buttons/rail). |
| web/backend/services/report_formatter.py | Adds role_chain projection into preview/detail responses. |
| web/backend/analysis_task.py | Persists computed role_chain into final_state. |
| tradingagents/utils/role_chain.py | New backend role-chain builder and “empty chain” detection. |
| docs/report-role-chain-contract.md | Documents authoritative role-chain API contract and integration notes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+1114
to
+1122
| final_state["role_chain"] = build_role_chain( | ||
| report_sections, | ||
| ticker=ticker, | ||
| company=company_of_interest, | ||
| market=request_data.get("market"), | ||
| published_at=now_beijing.isoformat(), | ||
| model_id=request_data.get("deep_thinker") or request_data.get("shallow_thinker"), | ||
| summary=structured_report.get("summary") or str(decision), | ||
| ) |
Comment on lines
+92
to
+101
| final_state = _final_state(record) | ||
| chain = build_role_chain( | ||
| final_state, | ||
| ticker=record.ticker, | ||
| company=record.company_name or record.ticker, | ||
| market=record.market, | ||
| published_at=_iso(record.created_at), | ||
| model_id=getattr(record, "deep_thinker", "") or getattr(record, "shallow_thinker", ""), | ||
| summary=record.final_summary or record.trading_decision or "", | ||
| ) |
Comment on lines
+35
to
+41
| _VERDICT_RULES = [ | ||
| ("strong_buy", [r"strong\s*buy", r"强烈买入", r"大幅增持", r"重仓买入"]), | ||
| ("buy", [r"\bbuy\b", r"买入", r"增持", r"建仓"]), | ||
| ("overweight", [r"overweight", r"审慎增持", r"谨慎增持", r"适度增持"]), | ||
| ("reduce", [r"\bsell\b", r"卖出", r"减持", r"清仓"]), | ||
| ("watch", [r"\bhold\b", r"持有", r"观望", r"等待"]), | ||
| ] |
Comment on lines
+207
to
+216
| def _build_debate(final_state): | ||
| debate = _as_dict(final_state.get("investment_debate_state")) | ||
| bull = _debate_side(debate.get("bull_history"), debate.get("current_response") if debate.get("latest_speaker") == "bull" else None) | ||
| bear = _debate_side(debate.get("bear_history"), None) | ||
| manager_text = _to_str(debate.get("judge_decision")) | ||
| return { | ||
| "bull": bull, | ||
| "bear": bear, | ||
| "manager": {"summary": _clip(manager_text, 800) or "研究经理尚未给出裁决。"}, | ||
| } |
| <div className="surface-card p-5"> | ||
| <div className="flex flex-wrap items-center justify-between gap-3"> | ||
| <VerdictPill verdict={report.traderPlan.verdict} label={report.traderPlan.verdictLabel} /> | ||
| <PriceBandView band={report.traderPlan.priceBand} currency={report.market ? ({ US: 'USD', HKD: 'HKD', CN: 'CNY' } as Record<string, string>)[report.market] : undefined} /> |
Comment on lines
+12
to
+15
| const { data, isLoading } = useQuery({ | ||
| queryKey: ['public-reports', market], | ||
| queryFn: () => reportAPI.list({ limit: 30, ...(market ? { market } : {}) }), | ||
| }); |
Comment on lines
+58
to
+66
| // Full report detail incl. role_chain | ||
| get: async (id: string) => { | ||
| const res = await apiClient.get<{ data: ReportDetail }>(`/api/reports/${id}`); | ||
| return res.data.data; | ||
| }, | ||
|
|
||
| exportUrl: (id: string, format: 'md' | 'json' | 'pdf') => | ||
| `/api/reports/${id}/export?format=${format}`, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WS-133 全栈接管:暗色金融前端重建 + 报告角色链契约
接管并完成 WS-133 的实现:后端角色链数据契约 + 全新暗色金融搜索优先前端,全部落到 origin 并验证通过。
后端:报告角色链契约
tradingagents/utils/role_chain.py,把多智能体 graph 的final_state组装为结构化RoleChainReport(Risk Judge 裁决置顶 → 市场/舆情/新闻/基本面分析师 → 多空研究辩论 → Trader → 风险辩论 → 末尾总结)。report_formatter:/api/reports列表/详情新增role_chain字段(列表含 decision+analysts+trading_decision 标签,详情含完整链)。analysis_task完成时持久化role_chain到final_state。priceBand/confidence/horizon抽不到时返回 null(前端兜底"示例/延迟");风险辩论键名兼容 forkrisky/safe与上游aggressive/conservative。docs/report-role-chain-contract.md权威契约。py_compile+ 样例数据端到端(空态优雅降级、真实态正确抽取裁决/区间/置信度/风险等级)。前端:暗色金融搜索优先重建(按审核通过的 11 页设计)
#060a10底 + 薄荷#9ee5c9/金#e5bd72/红#f48b8b/蓝#81bbed+ verdict 语义色),Noto Serif SC 标题、等宽数字、verdict 徽标 / surface / hero / chain-rail 工具类。SiteHeader(研究/公开榜单/订阅/本地模型/管理控制台,鉴权感知)、SiteFooter全站非执行免责声明、可复用SearchBar、SiteLayout。/搜索优先首页(hero 搜索 + 能力 + 最新公开研究)/research从查询发起多智能体分析/leaderboard公开研究榜单(市场筛选)/report?id=完整角色链报告页(裁决置顶 → 分析师 → 辩论 → Trader → 风险辩论 → 总结)+ MD/PDF 导出 + 旧报告兜底/subscription按次订阅套餐 + 计费说明/settings本地模型 Key(仅浏览器,复用useLocalLLMKeys)/me/me/billing/me/preferences用户工作区/login/register/auth(自动换肤)types/report.ts与后端 1:1 对齐;lib/api/reports.ts消费/api/reports的role_chain。验证
tsc --noEmitexit 0(顺带修复 scheduled-tasks 一处遗留 refetch 类型错误)。next build编译成功,所有新路由以静态内容预渲染(适配output: export,报告页用查询参数规避动态路由)。说明 / 后续
/report?id=客户端路由(origin 无既有动态路由)。/subscription/me/billing暂用示例数据占位,后端落地后对接。