Skip to content

refactor(config):收敛内置模型来源并简化自定义模型管理#508

Merged
phantom5099 merged 3 commits into1024XEngineer:mainfrom
phantom5099:main
Apr 29, 2026
Merged

refactor(config):收敛内置模型来源并简化自定义模型管理#508
phantom5099 merged 3 commits into1024XEngineer:mainfrom
phantom5099:main

Conversation

@phantom5099
Copy link
Copy Markdown
Collaborator

解决问题

1. 内置 provider 依赖 /models discovery,模型目录不稳定

原实现里,builtin provider 的配置仍然保留了 discovery 语义,模型列表在 catalog 层仍可能与远端 /models 绑定。这带来几个问题:

  • 内置模型会受到远端列表漂移影响,不利于产品层稳定控制默认模型。
  • context_windowmax_output_tokenscapability_hints 这些真正影响预算推导和能力判断的字段,无法由项目稳定维护。
  • 即使项目已经支持模型元数据,这些字段也很难在 builtin provider 上发挥作用。

2. custom provider 的 catalog 使用 TTL + 后台刷新,行为不透明

原实现中,catalog 会基于 24 小时 TTL 判断缓存是否过期,并在部分路径下静默后台刷新。这会带来:

  • 用户看到的模型列表可能来自旧缓存,但系统已经在后台更新,行为不可预期。
  • “打开模型选择器”与“强制刷新远端模型列表”混在一起,用户很难理解何时真的请求了 /models
  • 一旦供应商模型已下架但缓存未失效,用户仍可能继续使用过时模型。

3. 用户可写模型与远端元数据混在一起,配置风险高

之前 provider.yaml 中的 models 允许写入 context_windowmax_output_tokens 等字段,这有两个直接问题:

  • 用户可以填错元数据,而这些值会进一步影响 budget 解析和运行时行为。
  • “用户声明自己想要哪些模型”和“远端 discovery 告诉系统这些模型具有什么能力”是两种不同职责,但原结构把它们混在了一个持久化入口里。

解决方案概述

1. builtin provider 改成仓库内静态模型清单

本 PR 将 builtin provider 的模型来源切换为仓库内静态清单:

  • builtin provider 不再依赖 /models discovery
  • ProviderConfig.Models 直接携带静态模型列表
  • 静态列表中补齐项目真正需要的字段:
    • id
    • name
    • description
    • context_window
    • max_output_tokens
    • capability_hints

这样 builtin provider 终于有了“稳定、可测试、可审核”的模型目录。

2. custom provider catalog 去掉 TTL 和后台自动刷新

catalog 层改为更直接的语义:

  • 有缓存就直接返回缓存
  • 无缓存时同步 discovery 一次
  • 不再使用 TTL
  • 不再做后台自动刷新
  • 新增显式刷新接口 RefreshProviderModels

这意味着“普通读取模型列表”和“强制刷新模型列表”被明确区分开了。

3. 用户可写模型严格收窄为 id/name

本 PR 将 custom provider 的用户可写模型结构收窄为:

  • id
  • name

以下字段不再允许出现在 provider.yaml models 中:

  • context_window
  • max_output_tokens
  • description
  • capability_hints

这些元数据只允许来自 discovery 缓存或 builtin 静态清单,避免用户手填错误值污染运行时行为。

4. discovery 缓存只保存规范化后的必要字段

本 PR 没有把远端原始完整 JSON 原样落盘,而是只保存规范化后的白名单字段:

  • id
  • name
  • description
  • context_window
  • max_output_tokens
  • capability_hints

这样既保留了预算与能力判断所需信息,也避免缓存承担“保存上游任意字段”的职责。

具体修改

A. builtin provider 静态化

涉及文件:

  • internal/config/provider.go
  • internal/config/provider_test.go

主要变化:

  • openaigeminiqiniumodelscope 增加静态 Models
  • builtin provider 不再设置 ModelSourceDiscoveryEndpointPath
  • 新增 builtin 默认模型必须存在于静态模型清单中的校验
  • 新增 builtin 静态模型 metadata 完整性测试

当前静态模型中,modelscope 默认模型已经从不可用的 Qwen/Qwen2.5-* 切换为:

  • 默认:deepseek-ai/DeepSeek-V3.2
  • 备选:MiniMax/MiniMax-M2.5

这一步也顺带补齐了 builtin 模型的 ContextWindowMaxOutputTokensCapabilityHints

B. catalog 语义改为“按需发现 + 显式刷新”

涉及文件:

  • internal/provider/catalog/service.go
  • internal/provider/catalog/store.go
  • internal/provider/catalog/service_test.go
  • internal/provider/catalog/additional_test.go
  • internal/provider/catalog/store_test.go

主要变化:

  • 删除 TTL 相关逻辑
  • 删除 ExpiresAt
  • 删除后台刷新队列与去重逻辑
  • ListProviderModelsSnapshot 改为纯快照读取
  • ListProviderModels 仅在 cache miss 时同步 discovery
  • 新增 RefreshProviderModels
  • catalog schema 从 3 提升到 4

这一改动让 catalog 行为从“半缓存、半自动刷新”变成了“清晰的本地快照 + 显式远端同步”。

C. custom provider 持久化结构收窄

涉及文件:

  • internal/config/provider_loader.go
  • internal/config/provider_custom_normalize.go
  • internal/config/state/service_provider_create.go
  • internal/tui/core/app/update.go
  • 对应测试文件

主要变化:

  • customProviderModelFile 只保留 id/name
  • provider add 手工模型 JSON 只允许 id/name
  • 加载和归一化阶段严格拒绝 metadata 字段
  • SaveCustomProviderWithModels 在有 models 时都会持久化 id/name

这一步把“用户声明模型”与“系统推导模型元数据”彻底拆开了。

D. config-state 层补上显式刷新能力

涉及文件:

  • internal/config/state/model.go
  • internal/config/state/service.go
  • internal/config/state/service_test.go
  • internal/config/state/model_test.go
  • internal/config/state/model_additional_test.go

主要变化:

  • ModelCatalog 接口新增 RefreshProviderModels
  • catalogInputFromProvider 对 builtin provider 强制 DisableDiscovery: true
  • Service.RefreshModels(ctx) 新增强制刷新当前 provider 模型列表的能力
  • 刷新后若 current_model 不再有效,会重新修正当前选择

这一步把“强制刷新”正式提升为服务层能力,而不再依赖 catalog 内部隐式行为。

E. 文档与测试同步

涉及文件:

  • docs/config-management-detail-design.md
  • 多个 *_test.go

主要变化:

  • 文档明确 builtin provider 不再走 /models
  • 文档明确 provider.yaml models 只允许 id/name
  • 测试从“metadata 可写”切换为“metadata 必须来自 discovery/builtin”
  • 测试从“TTL / 背景刷新”切换为“cache miss discovery / explicit refresh”

预期收益

1. 模型目录更稳定

  • builtin provider 的模型列表不再被远端 /models 漂移影响
  • 默认模型和展示模型都由项目自己掌控

2. 模型元数据终于真正可用

  • builtin 静态模型直接提供 ContextWindow
  • budget 解析不再经常退回 fallback_prompt_budget
  • CapabilityHints 可以更稳定地参与能力判断

3. custom provider 行为更容易理解

  • “读缓存”就是读缓存
  • “没缓存就发现一次”就是一次同步发现
  • “强制刷新”由显式接口承担,不再夹在普通读取流程里

4. 降低错误配置风险

  • 用户不能再手填 context_windowmax_output_tokens 等高风险字段
  • 运行时关键元数据只来自 builtin 静态目录或 discovery 规范化结果

5. 后续 UI 扩展空间更清晰

  • 服务层已经具备显式刷新能力
  • 后续如果新增 /model refresh 或模型编辑入口,可以直接复用当前分层结果

reviewer 建议关注点

  • builtin provider 静态模型 metadata 是否足够可信、是否还需要继续补充
  • modelscope 当前默认模型是否符合产品预期
  • MiniMax/MiniMax-M2.5 当前只补了确认过的 ContextWindow,未强行填写 MaxOutputTokens,是否接受这种保守策略
  • custom provider 用户模型只保留 id/name 后,是否完全符合预期的产品边界
  • catalog 移除 TTL 与后台刷新后,当前 TUI 行为是否与预期一致

验证情况

  • 已通过:go test ./internal/config/...
  • 已通过:go test ./internal/tui/core/app/...
  • 全量执行:go test ./...

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Copy link
Copy Markdown

@fennoai fennoai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Focused review completed across code quality, performance, security, and documentation passes.

I found one noteworthy regression that should be addressed before merge.

@@ -97,8 +98,9 @@ func catalogInputFromProvider(cfg config.ProviderConfig) (provider.CatalogInput,
input := provider.CatalogInput{
Identity: identity,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DisableDiscovery is now forced to true for all builtin providers. This breaks the existing ModelScope guide verification flow (runModelScopeGuideSubmit -> providerSvc.ListModels) because ListModels can now return static models without any remote call/auth check. As a result, an invalid token may still pass verification and get persisted. Please keep discovery enabled (or explicitly call a forced-refresh/auth-check path) for the token verification workflow.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

@phantom5099
Copy link
Copy Markdown
Collaborator Author

/code 提高测试覆盖率,提交PR到fork仓库

@fennoai
Copy link
Copy Markdown

fennoai Bot commented Apr 28, 2026

完成情况

已按要求提升测试覆盖率,并提交到 fork 分支。

变更内容:

  • 新增 internal/provider/catalog/service_test.go 用例:
    • TestRefreshProviderModelsReturnsConfiguredModelsWhenDiscoveryDisabled
    • TestRefreshProviderModelsDiscoversAndPersists
  • 新增 internal/config/state/service_test.go 用例:
    • TestSelectionServiceRefreshModelsUsesRefreshPathAndRepairsCurrentModel
    • TestSelectionServiceRefreshModelsReturnsNoModelsAvailable

验证结果:

  • go test ./internal/provider/catalog ./internal/config/state 通过
  • go test ./... 全量通过

提交与推送:

View job run

xgopilot and others added 2 commits April 28, 2026 16:42
Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
test(config): improve refresh model coverage
@pionxe
Copy link
Copy Markdown
Collaborator

pionxe commented Apr 29, 2026

  1. 现在是硬编码内置模型?如果这部分的模型被下架了,是不是不利于维护?
  2. 现在的模型还会自动获取吗?和之前有什么区别?
  3. 你提到“用户不能再手填 context_window、max_output_tokens 等高风险字段”,这个做法是不是大大减低了用户自行配置的操作?如果真的有需要要自行配置呢?

@phantom5099
Copy link
Copy Markdown
Collaborator Author

phantom5099 commented Apr 29, 2026

  1. 现在是硬编码内置模型?如果这部分的模型被下架了,是不是不利于维护?
  2. 现在的模型还会自动获取吗?和之前有什么区别?
  3. 你提到“用户不能再手填 context_window、max_output_tokens 等高风险字段”,这个做法是不是大大减低了用户自行配置的操作?如果真的有需要要自行配置呢?

1 .是的,但这样的话如果有模型下架,我们只需要手动发布新版本就能跟上,而不是被动地像过去一样等待24小时有效期过去获取新信息,而且实际上,就比如魔搭的那个模型列表老是有没用的模型信息,他又不及时更新,就只能我们手动更新
2. 会,内置供应商不行,用户自定义供应商的时候可以选择这个方式,减少搜寻填写模型信息的步骤,只是和以前相比,这份信息不在设置有效期(24小时),且只有在找不到缓存或是用户手动刷新的时候才能再次自动获取。获取到的模型用户可以自由增减,方便剔除无效模型
3. 实际上我是考虑到,用户很有可能会填入一个与实际信息不符的值,比如说一个模型的上下文窗口100k,用户却给他设置200k,这对于上下文压缩等流程都会造成不必要的麻烦,而我们也没法去验证信息是否真实。如果用户真的有需要去配置这些信息,那就去配置文件配置这些信息吧

@pionxe
Copy link
Copy Markdown
Collaborator

pionxe commented Apr 29, 2026

每个模型的上下文都可能不同,这个上下文窗口现在是自行向提供商获取吗?还是说又是我们硬编码?

@phantom5099
Copy link
Copy Markdown
Collaborator Author

每个模型的上下文都可能不同,这个上下文窗口现在是自行向提供商获取吗?还是说又是我们硬编码?

硬编码,还有用户自定义的供应商在自动获取模型信息的时候如果刚好能获取到这几个值,就填上。不然按照之前的办法,实际上很多供应商,以及所有我们提供的openai协议的供应商,这些值根本没提供,就只能硬编码了

@pionxe
Copy link
Copy Markdown
Collaborator

pionxe commented Apr 29, 2026

每个模型的上下文都可能不同,这个上下文窗口现在是自行向提供商获取吗?还是说又是我们硬编码?

硬编码,还有用户自定义的供应商在自动获取模型信息的时候如果刚好能获取到这几个值,就填上。不然按照之前的办法,实际上很多供应商,以及所有我们提供的openai协议的供应商,这些值根本没提供,就只能硬编码了

我觉得我们可以内置一个硬编码,但是还是需要给用户自定义配置的接口。比如我们内置的硬编码是500kb,但是现在模型都提升至了1M,难道每次你都需要重新更改编译,然后要求用户更新吗?

@phantom5099
Copy link
Copy Markdown
Collaborator Author

每个模型的上下文都可能不同,这个上下文窗口现在是自行向提供商获取吗?还是说又是我们硬编码?

硬编码,还有用户自定义的供应商在自动获取模型信息的时候如果刚好能获取到这几个值,就填上。不然按照之前的办法,实际上很多供应商,以及所有我们提供的openai协议的供应商,这些值根本没提供,就只能硬编码了

我觉得我们可以内置一个硬编码,但是还是需要给用户自定义配置的接口。比如我们内置的硬编码是500kb,但是现在模型都提升至了1M,难道每次你都需要重新更改编译,然后要求用户更新吗?

我的思路确实是这样,内置的供应商确实不让用户更改,如果你觉得这部分需要让用户更改的话,后面再另提一个PR

@phantom5099 phantom5099 merged commit 98b8379 into 1024XEngineer:main Apr 29, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants