feat: Standardize on Bloomberg as the default data source, update search UI with editable symbols, and enable web search capabilities.

This commit is contained in:
xucheng 2026-01-19 19:39:27 +08:00
parent b6222b9b4b
commit 653812a480
5 changed files with 38 additions and 26 deletions

View File

@ -1,7 +1,8 @@
{ {
"permissions": { "permissions": {
"allow": [ "allow": [
"mcp__zai-web-reader__webReader" "mcp__zai-web-reader__webReader",
"mcp__zai-web-search__webSearchPrime"
] ]
} }
} }

View File

@ -213,14 +213,25 @@ async def search_stock(request: StockSearchRequest):
prompt = f"""请利用Google搜索查找 "{request.query}" 对应的上市股票信息。 prompt = f"""请利用Google搜索查找 "{request.query}" 对应的上市股票信息。
返回最匹配的股票优先返回准确匹配的公司 返回最匹配的股票优先返回准确匹配的公司
**关键要求Symbol 必须遵循 Bloomberg 终端专用代码格式 (Ticker Only)**
请注意区分不同市场的代码后缀(用于确定market字段)以及某些市场的特殊代码规则例如印度股票通常以 IN 结尾代码可能是缩写也可能是全称请仔细核对 Bloomberg 标准
返回格式必须是 JSON 数组每个元素包含 返回格式必须是 JSON 数组每个元素包含
- market: 市场代码CH/HK/US/JP/VN例如腾讯是 HK茅台是 CH英伟达是 US - market: 市场代码CH/HK/US/JP/VN/IN/EU等
- symbol: 股票代码如果是CH通常是6位数字HK是5位US是字母 - symbol: **Bloomberg标准代码** (仅Ticker不要包含后缀)
- 香港: "700" (不要写00700)
- 中国: "600519"
- 美国: "NVDA"
- 日本: "7203"
- 印度: "UBBL" (例如 United Breweries不要只写 UBL)
- 欧洲: "NESN"
- company_name: 公司简称 - company_name: 公司简称
示例 示例
[{{"market": "HK", "symbol": "00700", "company_name": "腾讯控股"}}] [{{"market": "HK", "symbol": "700", "company_name": "腾讯控股"}},
{{"market": "US", "symbol": "NVDA", "company_name": "英伟达"}},
{{"market": "IN", "symbol": "UBBL", "company_name": "United Breweries"}}]
请直接返回 JSON不要添加任何其他文字最多返回5个结果""" 请直接返回 JSON不要添加任何其他文字最多返回5个结果"""

View File

@ -54,8 +54,8 @@ function HomeInner() {
// Close right sidebar on new selection // Close right sidebar on new selection
setIsRightSidebarOpen(false) setIsRightSidebarOpen(false)
// 如果没有传入数据源,则根据市场设置默认值 // 默认数据源强制为 Bloomberg
const targetDataSource = dataSource || (company.market === 'CN' ? 'Tushare' : 'iFinD') const targetDataSource = dataSource || 'Bloomberg'
setSelectedDataSource(targetDataSource) setSelectedDataSource(targetDataSource)
} }
@ -122,7 +122,7 @@ function HomeInner() {
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<h1 className="text-3xl font-bold tracking-tight"></h1> <h1 className="text-3xl font-bold tracking-tight"></h1>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
AI驱动的分析 AI驱动的分析 Bloomberg
</p> </p>
</div> </div>

View File

@ -3,7 +3,6 @@
import Link from "next/link" import Link from "next/link"
import { MonitorPlay } from "lucide-react" import { MonitorPlay } from "lucide-react"
import { useRouter, useSearchParams } from "next/navigation" import { useRouter, useSearchParams } from "next/navigation"
import { HeaderSearch } from "@/components/header-search"
import { useEffect, useState, Suspense } from "react" import { useEffect, useState, Suspense } from "react"
import { import {
Select, Select,
@ -255,10 +254,6 @@ function NavHeaderInner() {
</div> </div>
</div> </div>
<div className="w-[160px] ml-4">
<HeaderSearch defaultDataSource={dataSource} />
</div>
{/* Portal Target for Dynamic Content */} {/* Portal Target for Dynamic Content */}
<div id="header-portal-target" suppressHydrationWarning className="flex-1 flex items-center justify-end px-4 gap-4 min-w-0" /> <div id="header-portal-target" suppressHydrationWarning className="flex-1 flex items-center justify-end px-4 gap-4 min-w-0" />

View File

@ -17,20 +17,21 @@ interface SearchStockProps {
const DATA_SOURCES = { const DATA_SOURCES = {
CN: [ CN: [
{ value: "Bloomberg", label: "Bloomberg" },
{ value: "Tushare", label: "Tushare (CN)" }, { value: "Tushare", label: "Tushare (CN)" },
{ value: "iFinD", label: "iFinD" }, { value: "iFinD", label: "iFinD" }
{ value: "Bloomberg", label: "Bloomberg" }
], ],
GLOBAL: [ GLOBAL: [
{ value: "iFinD", label: "iFinD" }, { value: "Bloomberg", label: "Bloomberg" },
{ value: "Bloomberg", label: "Bloomberg" } { value: "iFinD", label: "iFinD" }
] ]
} }
function SearchResultItem({ result, onSelect }: { result: SearchResult, onSelect: (r: SearchResult, ds: string) => void }) { function SearchResultItem({ result, onSelect }: { result: SearchResult, onSelect: (r: SearchResult, ds: string) => void }) {
// 默认数据源逻辑:CN -> Tushare, 其他 -> iFinD // 默认数据源逻辑:Bloomberg
const defaultSource = result.market === "CN" ? "Tushare" : "iFinD" const defaultSource = "Bloomberg"
const [source, setSource] = useState(defaultSource) const [source, setSource] = useState(defaultSource)
const [symbol, setSymbol] = useState(result.symbol) // Editable symbol state
// 根据市场获取可用数据源列表 // 根据市场获取可用数据源列表
const availableSources = result.market === "CN" ? DATA_SOURCES.CN : DATA_SOURCES.GLOBAL const availableSources = result.market === "CN" ? DATA_SOURCES.CN : DATA_SOURCES.GLOBAL
@ -41,15 +42,19 @@ function SearchResultItem({ result, onSelect }: { result: SearchResult, onSelect
{/* 顶部:公司信息 */} {/* 顶部:公司信息 */}
<div className="flex-1"> <div className="flex-1">
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
<div className="min-w-0"> <div className="min-w-0 flex-1 mr-2">
<h3 className="font-bold text-base truncate" title={result.company_name}> <h3 className="font-bold text-base truncate" title={result.company_name}>
{result.company_name} {result.company_name}
</h3> </h3>
<p className="text-sm text-muted-foreground font-mono mt-0.5"> <div className="mt-1">
{result.symbol} <Input
</p> value={symbol}
onChange={(e) => setSymbol(e.target.value)}
className="h-7 text-xs font-mono bg-background/50 border-input/60"
/>
</div>
</div> </div>
<Badge variant="secondary" className="shrink-0 text-xs font-mono"> <Badge variant="secondary" className="shrink-0 text-xs font-mono h-6">
{result.market} {result.market}
</Badge> </Badge>
</div> </div>
@ -73,7 +78,7 @@ function SearchResultItem({ result, onSelect }: { result: SearchResult, onSelect
</Select> </Select>
</div> </div>
<Button <Button
onClick={() => onSelect(result, source)} onClick={() => onSelect({ ...result, symbol }, source)}
size="sm" size="sm"
className="h-8 px-3 shrink-0" className="h-8 px-3 shrink-0"
> >
@ -158,7 +163,7 @@ export function SearchStock({ onCompanySelect }: SearchStockProps = {}) {
<div className="flex gap-4 items-start"> <div className="flex gap-4 items-start">
<div className="flex-1 flex gap-2"> <div className="flex-1 flex gap-2">
<Input <Input
placeholder="例如: 腾讯, AAPL, 00700.HK" placeholder="例如: 700 HK, NVDA US, 600519 CH"
value={query} value={query}
onChange={(e) => setQuery(e.target.value)} onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSearch()} onKeyDown={(e) => e.key === "Enter" && handleSearch()}