Skip to content

refactor(provider/model): 修改 TUI 支持切换 current provider 和 model 的具体实现 #413

@Nomikfk1215

Description

@Nomikfk1215

背景

用户痛点不是“在 TUI 里管理 provider 配置”,而是“已经写进 bytemind 配置的 provider/model,不能在 TUI 里方便切换”。

这次设计保持克制:配置文件负责增加/删除 provider 和 model;TUI 只负责读取、展示、选择当前运行目标。

设计原则

  • 配置是 provider/model 列表的唯一来源。
  • TUI 不提供 /add model,也不提供 delete,避免误改或破坏配置文件。
  • 入口统一为 /model,不再拆成 /models/model picker
  • current_provider 必须始终指向当前实际运行模型所在的 provider。
  • Provider 和 model 是两个层级,但 TUI 可以用一个 provider/model 列表完成选择。
  • 尽量复用现有 provider_runtimeRunner.UpdateProviderRuntimeListModels、model modal 和 token usage 逻辑。

Provider / Model 层级

Provider 是服务商、协议、端点和账号这一层,例如 deepseekopenaianthropicgemini 或一个私有 OpenAI-compatible 网关。

Provider 负责:

  • 协议类型:OpenAI-compatible / Anthropic / Gemini。
  • base_urlapi_key / api_key_env、auth header/scheme。
  • provider-specific ListModels、usage 拉取、health check 等能力。
  • 当前 provider 下选中的 model。

Model 是 provider 下面的模型 ID 和模型能力这一层,例如 deepseek-v4-flashdeepseek-v4-progpt-5.4-mini

Model 负责:

  • 请求里的 model 字段。
  • context window / max output / tools / vision 等 metadata。
  • token budget 和后续可能的价格元数据。

因此,base_url 和 key 不属于 model,应该属于 provider。切换 provider 和切换 model 在内部不是一回事:

  • 同 provider 下切 model:只更新该 provider 的 model
  • 切到另一个 provider/model:更新 current_provider,并更新目标 provider 的 model

对用户来说,TUI 可以统一展示为选择一个 provider/model 目标;对实现来说,要保持 provider/model 两层边界。

配置设计

建议把 provider_runtime.current_provider 作为对外语义。为兼容现有配置,可读取旧的 default_provider/default_model 作为 fallback,但写回时优先统一成 current 语义。

当前实际运行模型由这两个值共同决定:

  • 当前 provider:provider_runtime.current_provider
  • 当前 model:provider_runtime.providers[current_provider].model

最小结构:

{
  "provider_runtime": {
    "current_provider": "deepseek",
    "providers": {
      "deepseek": {
        "type": "openai-compatible",
        "base_url": "https://api.deepseek.com",
        "api_key_env": "DEEPSEEK_API_KEY",
        "model": "deepseek-v4-flash",
        "models": ["deepseek-v4-flash", "deepseek-v4-pro"]
      },
      "openai": {
        "type": "openai-compatible",
        "base_url": "https://api.openai.com/v1",
        "api_key_env": "OPENAI_API_KEY",
        "model": "gpt-5.4-mini",
        "models": ["gpt-5.4-mini"]
      }
    }
  }
}

说明:

  • current_provider 决定当前运行 provider。
  • providers.<id>.model 是该 provider 当前选中的 model。
  • providers.<id>.models 是该 provider 可在 TUI 中切换的 model 列表。
  • 如果 models 为空或缺失,就把 model 当作唯一候选,保证旧配置可用。
  • 先不做 TUI 写入新 provider/model;用户要加 provider 或 model,就编辑配置文件。

默认 DeepSeek

默认配置内建 DeepSeek,后续可再改默认 provider:

  • current_provider: deepseek
  • model: deepseek-v4-flash
  • base_url: https://api.deepseek.com
  • api_key_env: DEEPSEEK_API_KEY

同步更新 config.example.json 和 DeepSeek 默认/fallback model,避免新用户默认落到旧模型名。

TUI 交互

合并当前 /models/model picker 为一个 /model 面板,行为类似 /session

  • 输入 /model 打开模型选择面板。
  • 面板自动读取当前配置文件中的所有 provider 和 model。
  • 上下键选择 provider/model
  • Enter 即切换当前运行目标。
  • 如果选择同 provider 下的 model,只保存该 provider 的 model
  • 如果选择另一个 provider 下的 model,保存 current_provider 和目标 provider 的 model
  • Esc 关闭。
  • 面板中标记当前运行项,例如 current
  • /models/model picker 如需兼容,可作为 /model 的别名,但不再有单独语义。
  • 移除或隐藏 /add model/delete model,避免 TUI 负责配置增删。

运行时切换

切换时继续复用现有链路,范围控制在“选择已有配置项”:

  • 从配置构建候选 provider/model 列表。
  • 选择后更新 current_provider 和对应 providers[provider].model
  • 重建当前 runtime client。
  • 调用 Runner.UpdateProviderRuntime
  • 写回配置文件。

如果配置中的 current_provider 不存在,应在启动或打开 /model 时给出明确错误,不要静默运行到别的 provider。

token usage 接入

切换后保持现有 token usage 行为:

  • 后续请求使用新的 provider/model。
  • usage record 记录新的 model 名称;如需要 provider 维度,记录或推导当前 provider。
  • provider-specific 的远端 usage 拉取应跟随 current_provider,因为不同 provider 的 usage API 不同。
  • token usage badge 不因切换模型清零。
  • token budget 根据当前 model metadata/context window 刷新;没有 metadata 时回退 token_quota

config.json 配置模板

{
  "provider_runtime": {
    "current_provider": "deepseek",
    "providers": {
      "deepseek": {
        "type": "openai-compatible",
        "base_url": "https://api.deepseek.com",
        "api_key_env": "DEEPSEEK_API_KEY",
        "model": "deepseek-v4-flash",
        "models": [
          "deepseek-v4-flash",
          "deepseek-v4-pro"
        ]
      },
      "openai": {
        "type": "openai-compatible",
        "base_url": "https://api.openai.com/v1",
        "api_key_env": "OPENAI_API_KEY",
        "model": "gpt-5.4-mini",
        "models": [
          "gpt-5.4-mini",
          "gpt-5.4"
        ]
      }
    }
  },
  "approval_policy": "on-request",
  "approval_mode": "interactive",
  "max_iterations": 32,
  "stream": true,
  "context_budget": {
    "warning_ratio": 0.85,
    "critical_ratio": 0.95,
    "max_reactive_retry": 1
  },
  "token_usage": {
    "storage_type": "file",
    "storage_path": "./.bytemind/token_usage.json",
    "backup_interval": "1m",
    "max_sessions": 10000,
    "alert_threshold": 1000000,
    "enable_realtime": true,
    "retention_days": 30,
    "monitor_interval": "30s",
    "database_driver": "sqlite3"
  }
}

非目标

  • 不在 TUI 中新增 provider/model。
  • 不在 TUI 中删除 provider/model。
  • 不做 provider 健康检查或自动 fallback 策略。
  • 不做复杂价格表或模型 catalog 维护。
  • 不新建完整设置页。

验收标准

  • 空配置或首次启动时,当前 provider 是 deepseek,当前 model 是 deepseek-v4-flash
  • 配置文件中写入多个 provider 和多个 model 后,/model 能自动列出全部候选项。
  • /model 面板中上下选择并按 Enter 后,当前 session 后续请求实际使用选中的 provider/model。
  • 同 provider 下切 model 时,不改变 current_provider,只更新该 provider 的 model
  • 切换到另一个 provider/model 时,配置写回后 current_provider 与当前运行模型所属 provider 保持一致。
  • URL、key、协议等 provider 字段不会因为切 model 被挪到 model 层或被重复维护。
  • TUI 不再引导用户通过 /add model/delete model 修改 provider/model 配置。
  • /models/model picker 如保留,只作为 /model 的兼容别名。
  • token usage 显示不回退;切换后 token budget 跟随当前模型,远端 usage 拉取跟随当前 provider。
  • 增加覆盖:配置兼容旧字段、配置读取多个 model、/model 面板选择、同 provider 切 model、跨 provider 切换、runner runtime 切换、配置写回、token budget 刷新。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions