MoonshotAI / MoonshotAI/kimi-code

fix(tui): /provider 命令 UI 文案承诺 refresh 但未实现入口,且 refresh 端点对无 source 的 provider 是 no-op

Open
#2,111 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
7.5k
Forks
1.2k
Avg merge
11h 53m
Merged PRs (30d)
350

Description

现象

TUI 里 /provider 命令打开的 Provider Manager,文案明明写着可以 refresh,但 UI 上找不到任何 refresh 操作,只能 add / delete。手动调 kap-server 的 POST /api/v1/providers:refresh 端点,对没存 source 的 provider 也是空打(orchestrator 直接跳过、零网络请求),UI state 也不会更新。要让模型列表真正刷新,只能删掉再重新 add("reconnect")才行,绕了一大圈。

复现步骤里详细列了三条独立的失败路径。

版本

  • kimi --version 输出(0.29.0)
  • kap-server:http://127.0.0.1:58627,单实例 daemon 模式
  • 触发问题的 provider(本机环境样例,不代表所有用户):
    • minimax-cn-coding-plan(type: anthropic,无 source 字段)
    • opencode-go(type: openai,无 source 字段)
    • managed:kimi-code(type: kimi,OAuth-managed)

平台

macOS(arch64),loopback daemon。

期望行为

  1. /provider UI 里有一个显式的 refresh 操作(按键 + 底部 hint 提示),点了之后真的去拉远端模型列表并刷新 UI。
  2. POST /api/v1/providers:refresh(含 :refresh_oauth 和单 provider 的 {id}:refresh)对所有已配置 provider 都能产生预期效果:对内置 managed / OAuth / 自定义 registry 的 provider 拉远端 /models;对其他类型的 provider 至少给出明确说明("此 provider 无可刷新的远端来源"),而不是静默 no-op。
  3. 远端刷新后 TUI 端的 availableProviders / availableModels 内存 state 应当跟着 reload,不管 orchestrator 的 changed 列表是否为空。

复现步骤

步骤 1 — UX 文案与实现不一致

打开 TUI,输入 /provider

  • 顶部 hint 实际显示:↑↓ navigate · D delete · Esc cancelapps/kimi-code/src/tui/components/dialogs/provider-manager.ts:48
  • 命令注册描述却写着:Manage AI providers (add / delete / refresh)apps/kimi-code/src/tui/commands/registry.ts:197

→ "refresh" 在命令描述里被承诺,但 UI 既没有 R 键也没有对应 callback(ProviderManagerOptions 接口只有 onAdd / onDeleteSource / onClosehandleInput 只识别方向键 / Enter / D / Esc)。

步骤 2 — 后台 refresh 对 UI 不可见,且对多数 provider 是 no-op

apps/kimi-code/src/tui/kimi-tui.ts:731-735

await this.authFlow.refreshAvailableModels();
void this.refreshProviderModelsInBackground();

后台 fire-and-forget 调用 AuthFlowController.refreshProviderModels()(scope=all)。但底层 refreshProviderModels()packages/oauth/src/refreshProviderModels.ts:377-766)的 4 个分支是:

  1. source.kind === 'apiJson'(自定义 registry)
  2. Managed Kimi Code OAuth
  3. Open Platform
  4. Managed Kimi Code endpoint API-key(强匹配 type=kimibase_url 命中 managed endpoint)

minimax-cn-coding-plan / opencode-go 这类普通带 apiKey 的 anthropic/openai provider,4 个分支一个都不命中,orchestrator 直接跳过、零网络请求。

步骤 3 — 即使真的改了 config.toml,TUI 也不刷 UI state

apps/kimi-code/src/tui/controllers/auth-flow.ts:168-187

private async refreshProviderModelsWithScope(scope) {
  const result = await refreshAllProviderModels(...);
  if (result.changed.length > 0) {
    await this.refreshAvailableModels();   // 只有 changed 才 setAppState
  }
  return result;
}

→ 假设一个 provider 真的能刷新(比如 managed:kimi-code),但 orchestrator 判断"没有变化"时,TUI 端的 availableProviders / availableModels 内存 state 也不会被 reload。

步骤 4 — Reconnect 之所以有效,是因为它绕过上面所有判定

apps/kimi-code/src/tui/commands/provider.ts:248

await host.authFlow.refreshConfigAfterLogin();

refreshConfigAfterLogin()auth-flow.ts:118-139无条件 setAppState({ availableModels, availableProviders, ... }),不依赖 result.changed 判定,强制覆盖内存 state。同时 applyCatalogProvider() 把整个 model alias 列表重写一遍,所以 UI 立刻对了。

端点复现

TOK=$(cat ~/.kimi-code/server.token)
curl -sS -X POST -H "Authorization: Bearer $TOK" \
  http://127.0.0.1:58627/api/v1/providers:refresh | python3 -m json.tool

实际响应里 minimax-cn-coding-planopencode-go 要么 status: "unchanged",要么根本不在 results 列表里 —— 因为 orchestrator 没尝试刷它们。

根因小结

问题
/provider UI 命令描述文案承诺 refresh,但 ProviderManagerComponent 没绑 R 键,没 onRefresh callback
refresh orchestrator 只覆盖 4 类可识别 provider,对无 source 的 provider 静默 no-op
TUI state 同步 refreshProviderModelsWithScope 只在 changed.length > 0 时调 refreshAvailableModels(),UI state 容易过期
"reconnect" 路径 绕过所有上述判定,强制 setAppState,所以看起来"work"

建议方向(仅供参考,等 maintainer 拍板)

最小修复(仅 UX 一致性 + state 同步):

  1. ProviderManagerComponentR 键、onRefresh callback、HEADER_HINT· R refresh
  2. ProviderManagerOptions 接口加 onRefresh: (providerIds) => void
  3. 命令侧接到 authFlow.refreshProviderModels({ providerIds })无论 changed 与否都跑一次 refreshAvailableModels()

根本修复(同时解决 orchestrator 不认的问题):

  1. applyCatalogProvider()packages/node-sdk/src/catalog.ts:130)写入 provider.source = { kind: 'modelsDev', url },保留 provenance
  2. refreshProviderModels.ts 新增 source.kind === 'modelsDev' 分支:按 URL 分组 fetch 整个 catalog,只 apply 对应 provider(避免 N 个 provider 重复请求 N 次)
  3. 旧配置迁移要谨慎 —— 历史 import 项没 source,不建议按 provider id 盲猜是 models.dev 来的;让用户首次显式 refresh 时再补 provenance

第 4–6 条跟 catalog 的 provenance 缺失相关,可以参考现存的相关讨论(如 issue #1966 关于 custom registry api.json 的扩展)。

备注

  • 我没在 v2 engine 上复现这条问题(packages/agent-core-v2IProviderDiscoveryService 也走同一套 refreshProviderModels),但 web UI 通过 apps/kimi-web/src/App.vue:420-422 调同一个 REST 端点,理论上同样会受影响。
  • 按 CONTRIBUTING.md "Discuss first" 原则,先开 issue,等 maintainer 反馈后再开 PR。

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with apps/kimi-code/src/tui/components/dialogs/provider-manager.ts, commands/registry.ts, and commands/provider.ts to trace the provider UI and refresh entry point. Then read apps/kimi-code/src/tui/controllers/auth-flow.ts, packages/oauth/src/refreshProviderModels.ts, and packages/node-sdk/src/catalog.ts, using the documented curl request to inspect endpoint results. Done means the refresh action, provider handling, and TUI available-model state behave consistently for supported and unsupported providers.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend, cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.