Skip to content

fix(providers): get_assets 的 currency 过滤在路由层被丢弃,真实 provider 恒返回全部币种 #197

Description

@wxrbyte

get_assets 的 currency 过滤在路由层被静默丢弃,真实 provider 恒返回全部币种

问题与复现

get_assets 工具(能力 portfolio.assets)在四处都声明了「可按币种过滤」:

层 位置 声明
工具 schema packages/shared/src/capabilities/manifests/phase-two.ts:387 currency: Type.Optional(Type.String({ description: 'Filter by currency, e.g. USD, HKD' }))
能力 fetchers packages/shared/src/capabilities/fetchers.ts:93、providers/router-fetchers.ts:95 getAssets: (currency?: string) => Promise<AccountAssets[]>
路由入口 packages/shared/src/providers/router-fetchers.ts:199 getAssets: (currency) => fetch(router, 'portfolio.assets', { currency })
底层 CLI packages/longbridge-tools/src/tools/assets.ts:6-9 if (currency) args.push('--currency', currency)

但链路在 ProviderRouter 处断掉:

// packages/shared/src/providers/router.ts:505-512
private async invokeBroker(provider, capabilityId, input, signal) {
  const accountId = readAccountId(input);   // 只读 accountId
  switch (capabilityId) {
    ...
    case 'portfolio.assets':
      return provider.getAssets(accountId, signal);   // currency 未传

而长桥 broker 实现也没有 currency 形参,于是底层永远以 getAssets(undefined) 调用:

// packages/shared/src/providers/longbridge/broker.ts:102
getAssets(accountId?: string, signal?: AbortSignal) {
  return runProviderCall<AccountAssets[]>(() => getAssets(), signal);
}

反例:调用 get_assets { currency: 'HKD' }

  • 实际:router.execute('portfolio.assets', { currency: 'HKD' }) → provider 收到 currency === undefined → 底层执行 longbridge assets --format json → 返回全部报告币种。
  • 期望:底层执行 longbridge assets --currency HKD --format json,结果只含 HKD 条目。

AccountAssets 是按报告币种一条记录的(packages/core/src/account.ts:90,字段 currency),工具描述本身也写着 “per-currency cash breakdown”,因此该过滤是有意义的入参而不是装饰。

为什么一直没有被发现

  • packages/longbridge-tools/src/phase-two.test.ts:244 有 getAssets forwards optional currency,但它直接调用底层函数 getAssets('HKD'),覆盖不到 shared 的 broker provider / router 这一层。
  • packages/shared/src/providers/providers.test.ts:104 的替身 getAssets(accountId?, signal?) 只返回 [],不校验参数是否抵达。
  • packages/shared/src/capabilities/phase-two.test.ts 的 fake fetchers 用 getAssets: async () => [],同样不校验。

即:每层单测各自「签名对得上」,但没有一条断言 currency 真的走完全程。

预期与修复方向

让 currency 走完全程,且不破坏既有的 getAssets(accountId, signal) 调用形态:

  1. packages/core/src/provider.ts:BrokerAccountProvider.getAssets 增加可选第三形参 currency?: string(可选参数,既有实现少传参仍然类型兼容)。
  2. packages/shared/src/providers/router.ts:portfolio.assets 分支读取并透传 input.currency。
  3. packages/shared/src/providers/longbridge/broker.ts:getAssets 接收 currency 并交给底层 CLI。

复现证据

  • 环境:Windows 11 + Bun 1.4.2,main 8eca711130f7cd60c2e84c7e0e9bcee5d6bb384d。
  • 新增回归测试 packages/shared/src/providers/broker-assets-currency.test.ts(5 例)。
  • 修复前(main 原始字节):2 pass / 3 fail,其中端到端一例为
    Expected: ["HKD"] / Received: ["USD", "HKD"],参数一例为
    Expected: { accountId: "A1", currency: "USD" } / Received: { accountId: "A1", currency: undefined }。
  • 修复后:5 pass / 0 fail。
  • bun test packages/shared/src/providers --isolate:120 pass / 0 fail。
  • bun test packages/core --isolate:51 pass / 0 fail。
  • bun run typecheck:core / i18n / shared / ui / electron 五个工作区 exit 0。
  • bun test packages/shared --isolate:1093 pass / 4 fail;4 项失败为既有基线
    (ExperimentService ×1、langfuse ×1、ResearchService ×2),已做 A/B 对照:
    把本次改动的三个生产文件还原为 main 原始字节后重跑同一批文件,失败项与数量逐条一致(52 pass / 4 fail)。

范围

  • 只修 portfolio.assets 一条链路的 currency 透传,不改其他 broker 能力的行为。
  • 不新增依赖、不改 CLI 参数格式。
  • 无可见 UI 样式/布局变化(仅当调用方传入 currency 时,资产列表按币种收敛)。
  • 与 [Finance Data] Define freshness, market-session, currency, and adjustment semantics #24(定义 currency 语义契约)不重叠:本 issue 只修「已声明的过滤参数在路由层丢失」这一实现缺陷,不改契约定义。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

claimedClaimed by a contributor and currently in progress

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions