Skip to content

feat(manager): 模型元数据标签化体验优化 - #2307

Open
dongyu23 wants to merge 19 commits into
BigPizzaV3:mainfrom
dongyu23:fix/water-fallback
Open

dongyu23 wants to merge 19 commits into
BigPizzaV3:mainfrom
dongyu23:fix/water-fallback

Conversation

@dongyu23

@dongyu23 dongyu23 commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

一句话总结

这个 PR 把「模型元数据」从生成链里的隐形配置,变成管理器界面上一套看得见、读得懂、改得动的标签系统:模型处于什么状态、生成时会发生什么、还能做什么修改,全部直接写在用户眼前。

之前的问题

模型的元数据来自三层查找(精调/供应商兼容层 → 官方运行时缓存 → bundled 静态资产),状态组合很复杂,但界面上完全不可见:

结果就是:用户对元数据「不敢改、不会改、改了也不知道生效没有」。

之后:三枚标签,把全部状态空间摊开

每个模型的导入面板渲染三枚正交的状态徽标(横向排列、窄屏自动折行),简洁易懂地覆盖所有可能状态:

徽标 颜色 含义
匹配:{来源} 绿色 命中内置元数据,并标明来源(Kimi / DeepSeek / 官方内置…)
回退:{模板} 灰色 未命中内置,明确告知生成将回退到哪个官方模板
自定义 主色 已导入自定义元数据,生成时覆盖内置

徽标以结构化 source 字段下发(不再把渲染文案当数据传),调用方 localize 时统一走 t()/tf() 拼装。未命中内置 + 已有自定义时只显示 [自定义](自定义已覆盖,「回退」不再是当前事实,避免误导)。

徽标之外是恒定渲染的四个操作(重新匹配 / 清除 / 取消 / 保存):按钮永远在同一位置,只用置灰表达可用性,不再出现「点一个键少一个键」;保存按钮的文案与 hover 说明随内容变化(「保存为自定义配置」「保存后恢复内置」……),按钮承诺与执行动作严格一致——任何时刻都能回答三个问题:现在用的是什么?保存会发生什么?还能做什么?

更自由的修改,且每一修改都安全

标签让状态可见,语义重构让修改无顾虑:

  • 清除 = 只清空面板草稿:面板保持打开,不摘已保存配置、不回滚行列。随时清掉内置预填、粘贴最新供应商元数据保存为自定义;想回到内置,走「清除 → 重新匹配 → 保存」或直接「清除 → 保存」;
  • 取消 = 事务回滚:撤销窗口/压缩列的实时写回,不写入任何配置;面板关闭时所有在飞的内置查询作废,迟到响应不会把面板重新打开;
  • 保存 = 唯一写入点:面板内容与内置条目全字段一致时,目标态就是「用内置」,绝不把内置数据复制成一份自定义覆盖;哪怕只改了压缩百分比也算部分编辑,如实落成自定义;
  • 改名、后缀、大小写不再是陷阱:元数据键全链路规范化(剥 [1M] 后缀 + 大小写不敏感),改名未提交时按行解析配置落点,清除/保存不再失明;历史数据里同名变体(如 DeepSeek-V4-Pro 与 deepseek-v4-pro[512K])两侧统一按「后写者胜」归并,界面显示与生成 catalog 不再各选一份。

内置命中,行列立即同步

上游获取、手动提交行名、打开导入面板三个入口共用一条回填裁决:内置命中且列为空时,立即回填上下文窗口(内置 context_window)与自动压缩(Codex++ 默认 90%;厂商条目若提供 auto_compact_token_limit 则优先换算)。[1M] 后缀与用户已填值是显式意图,任何回填/导入都不得覆盖——所见即生成所得。

支撑标签系统的底层重构

标签能被信任,前提是它与生成行为严格同源:

  • 解析统一:新增 resolve_builtin_metadata 单一解析器,生成链、UI 查询、判定、索引四个入口全部收敛到同一条三层查找链,UI 展示与生成的 catalog 不再可能分叉;
  • [1M] 后缀全链路一致(模型元数据严重缺失:主流第三方模型全部回落 gpt-5.5 兜底模板,建议内置供应商元数据 #2279):后缀窗口在生成链、导入面板、实时同步、元数据键归一中语义统一,后缀作为用户显式意图拥有最高优先级(行内后缀优先于 model_windows map,有测试锁定);
  • 健壮性:移除 unsafe 裸指针生命周期缓存,改为按路径 + (mtime, 长度) 指纹的运行时缓存;索引不再对每个 slug 重复读盘;内置查询区分「命中 / 未命中 / IPC 失败」三态,索引查询失败可诊断(console.warn),不再静默吞掉;builtin_model_metadata_index 透传 auto_compact_token_limit;
  • macos-dmg 测试在 Windows 上动态定位 bash.exe。

资产元数据更新(12 个 assets/*.json)

  • 新增 7 个 slug:gpt-5.3-codex-spark、deepseek-v4-flash-0731、deepseek-v4-pro-0813、deepseek-v4-flash-vision、glm-5v-turbo、kimi-k2.7-code-highspeed、MiniMax-M2.7-highspeed;
  • 事实数据修正 8 处:doubao-seed-2.1-pro 与 qwen3-coder-plus 上下文 256K→1M(会改变这两款的 auto compact 阈值);kimi-k3、kimi-k2.8-preview、mimo-v2.5 的 max_output_tokens 修正为 131072;kimi-k2.8-preview priority 7→9 并补 base_instructions;gemini-3.8/3.7-flash、gemini-3.5-flash-lite、step-5-preview 补 max_output_tokens: 65536;
  • 描述文案:约 10 处补输入模态(video/audio/PDF)与语境说明。

审计修复(2026-09-26)

对本 PR 做了一轮子智能体交叉审计,随后修复确认的问题:

  • 恢复被 cherry-pick 事故误删的 7 个仍有调用点的英文翻译;「关闭官方低额度提示」改用新文案;建议上游后续把 tools/i18n-verify.mjs 接入 CI(当前存在存量缺口,本次 8 个回归正是从该盲区漏掉的);
  • 移除未渲染的导入状态行死代码(构建逻辑 + CSS + 10 条测试断言,PR 描述同步修订);
  • 修复内置查询竞态:跟随改名的 effect 在 await 后立即短路;begin/rematch 引入请求代数守卫,取消面板后迟到响应不再重开面板;
  • Rust 侧 parse_model_metadata_map 归一化与前端对齐(同名变体后写者胜);
  • 补回被 02ee176 无意删除的 sol/luna 元数据覆盖测试,三个直接读运行时缓存的测试补 RUNTIME_CACHE_ENV_LOCK,新增行内后缀优先级测试;
  • .gitignore 补 .zcodetmp/。

测试

  • cargo test -p codex-plus-core 全部测试二进制通过(0 失败;CI 另有 cargo test --workspace)
  • cargo clippy -p codex-plus-core --all-targets 无新增告警
  • 前端 npm test 274 用例全部通过,npm run check(tsc)无错误
  • node tools/i18n-verify.mjs:本 PR 引入的 8 个 MISSING 已清零(余量为 main 存量)
  • 新增用例锁定:状态徽标文案、内置查询三态、导入草稿事务、回填裁决边界、全字段比较、空条目往返、元数据键后写者胜、行内后缀优先级等
  • e2e 脚本断言随「仅改窗口/压缩也算部分编辑」口径同步更新

License

AGPL-3.0-only

后缀适配(P1):
- 前端新增 suffixWindowTokens/modelSlugFromRowName/suffixWindowString,
  三处 parseModelMetadataDocument 调用点先剥 [1M] 后缀再解析导入文档
- 后端 parse_model_suffix 用 let-chains 折叠嵌套,窗口映射 1M=1e6/256K=256000

元数据健壮性(P2/P3):
- 查询链 fallback 不再写死 gpt-5.5/272000:新增 fallback_template_info()
  从 bundled 模板实时取;前端 metadataSourceTags/importPanelControls 接
  fallbackSlug 字段
- vendor JSON 重复解析加 OnceLock 索引缓存(find_catalog_entry 热路径)
- 新增 find_catalog_entry_for_test 钩子 + 2 个测试:精确匹配优先于大小写
  变体、运行时缓存层大小写不敏感命中
- kimi-k2.8-preview 资产修正:max_output_tokens 131072、priority 9、补 base_instructions
- 测试锁 SerializeCodexHomeTests::lock() 重构为 poison-tolerant,清除 22 处双重锁
- e2e_codex_real_cli 模型计数改为动态断言,codex 二进制缺失时 panic 带指引
- i18n:回退/匹配徽标文案全部走 tf() 模板,删除失效的 EN_PLAIN 硬编码

工程卫生:
- 删除入库的浏览器自动化调试脚本,.gitignore 补 .zcode/ 等工作区产物规则
- clippy/fmt 对 PR 触及文件清零

验证:model_suffix 29/29、relay_config 163/163、lib 436/436、
前端 node --test 36/36、tsc --noEmit 通过
- importSaveDecision 对 documentBlank+imported 与 matchesBuiltin 两种情况
  都返回「保存此模型」,语义统一为「保存成内置态」
- 面板按钮组从按状态显隐改为四按钮恒定渲染 + 置灰(importPanelControls)
- runtime cache 测试 slug 改用 runtime-only-model,避开供应商资产新增条目
- builtinEntryToImportDocument 接受行名参数:预填文档的 slug 用用户
  输入拼写(剥掉 [1M] 后缀),不再显示资产里的小写规范名
- 来源徽标从 i18n key + 参数改回纯文本下发(文案在 helper 内维护),
  保存按钮文案统一为「保存此模型」,效果说明放 tooltip
- importPanelControls 的 fallback 默认值改为 gpt-5.5,不再依赖调用方传
- 补回 main 上 customHeaders 功能块缺失(c00177e 整文件覆盖造成回退)
cherry-pick 冲突解决时误删了 Tauri 命令注册对应的函数体,从 main 恢复
对照用户认可的旧分支设计(3de63f4/c00177e)还原:

1. 来源徽标(标签):补回 .relay-model-source-badge 胶囊样式及
   builtin(绿)/custom(主色)/fallback(中性)三色变体,共 83 行
   CSS。cherry-pick 时整文件覆盖丢了这段样式,导致面板里只有
   裸文字没有标签胶囊。

2. workbench 顶部状态行:移除 .relay-model-import-status 渲染。
   徽标已承载「当前用什么元数据」的信息,顶部一行说明文字属于
   早期过渡设计,用户确认不要。

3. 导入按钮颜色:恢复 className 三元表达式
   (imported → relay-model-import-custom、命中内置 →
   relay-model-import-builtin),variant 恒为 ghost。
   c8ed14b 重构时误把颜色 class 丢成纯 relay-model-import-button。

验证:tsc 通过、37 个前端测试全过;CDP 冒烟确认 28 个模型行
中命中内置的按钮带绿色边 class、面板内徽标渲染
「匹配:gpt-5.6 兼容」、状态行不再出现。
用户反馈保存键被置灰、清除逻辑不对——上一轮还原旧分支 UI 时把
按钮语义也带回了实验版(needsSave=false 时保存置灰、清除停在
空文档状态),偏离了 main 上日常使用的交互。

对齐 main 语义:

1. 保存键:disabled 只取决于 JSON 解析是否成功(!parseOk),
   不再因「内容与内置一致、无自定义可写」而置灰。内容==内置时
   点保存 = 确认使用内置并关闭面板(applyModelMetadataImport
   原有的 needsSave 短路逻辑不变,不写自定义)。

2. 清除键:摘除该模型自定义配置后直接关闭面板(保留上下文
   窗口列),与 main 的「清除导入配置」一步到位一致;不再停在
   「文档已清空、保存键置灰」的死胡同。title 同步更新。

3. importSaveDecision 的「内置一致且无自定义」分支 title 改为
   「内容与内置元数据一致,保存后继续使用内置元数据」,与可点
   的按钮状态一致。

CDP 实测:面板打开保存键即可点;改 display_name 保存为自定义
后按钮变「更新此模型配置」;点清除后面板关闭、自定义被摘除、
行图标恢复 builtin 绿边。37 个前端测试全过,tsc 无错误。
- 三枚状态徽标(匹配来源/回退模板/自定义)配合恒定四按钮与动态保存文案,元数据匹配现状一目了然
- 清除=只清空面板草稿(面板保持打开、不摘配置);保存=唯一写入点;与内置全字段一致不写自定义,部分编辑(含仅改窗口/压缩)如实落自定义
- 导入草稿身份只用行索引,元数据键按行解析(改名未提交不再失明);[1M] 行现有配置文档可正常解析
- 内置命中即回填空窗口/压缩列(上游获取/手动提交/打开面板三入口共用裁决;后缀与已填值不覆盖)
- 内置元数据索引透传 auto_compact_token_limit;e2e 断言随口径同步
.zcode/ 已在 .gitignore 声明不入库,此前误跟踪的两个 CDP 调试脚本与 .zcodeignore 一并从仓库移除(本地文件保留);.zcodeignore 补充进忽略列表防止再次混入。
@dongyu23 dongyu23 changed the title feat(manager): 模型元数据标签化体验与 [1M] 后缀全链路适配 feat(manager): 模型元数据标签化体验优化 Sep 25, 2026
cherry-pick 整文件覆盖 i18n-en.ts 后手工恢复时漏了 8 个仍有调用点的
EN_PLAIN 条目,英文界面下这 9 处调用点直接露中文。恢复其中 7 个
(热重载脚本、应用本地脚本及开关…、自定义请求头、请求头名称、
请求头值、自定义请求头会同时用于…、Host、Content-Length…);
「关闭官方低额度提示」的 hint 改用字典里已有的新文案
「关闭后仍可从 Codex 左下角账户菜单查看官方剩余额度。」,旧 key
随之废弃。3 个已无调用点的 key(部分脚本执行失败…等)确认删除。
i18n-verify 手动复核:8 个 MISSING 全部消失。
状态行(importPanelControls.status / importPanelStatus / ImportPanelStatus)
在 baed244 移除 JSX 渲染后已无任何消费点,但构建逻辑、CSS(.relay-model-
import-status 三色与 .relay-model-metadata-tags 系列)和 10 条测试断言
仍在锁一份不存在的 UI;importPanelControls 的 matchedSource/fallbackSlug
参数随之失去消费点一并移除。另删 styles.css 中与上文完全重复的
.relay-model-metadata-import-warning 块。
1. MetadataSourceTag 删 text/title 渲染文案、改发结构化 source:
   localizeMetadataSourceTag 不再用 slice("匹配:") 前缀切片和
   全角括号正则从渲染文本反解参数——来源名含全角括号时正则会抓错
   分组或静默降级,文案一改前缀切片就错位。fallbackSlug 空串兜底
   到 bundled 首条,避免渲染「回退:」空后缀。

2. 跟随改名的 useEffect:cancelled 检查提前到 await 之后第一行
   (try/catch 两分支,setBuiltinQueryState/setMetadataImportError
   一并纳入),旧查询的迟到 error 不再把当前正确匹配误藏成回退态。

3. beginModelMetadataImport/rematchBuiltinImport 引入请求代数 ref:
   命令式调用没有 effect cleanup 的 cancelled 通道,响应返回时代数
   不匹配即整体丢弃——修掉「取消面板后被迟到响应重新打开」(面板
   会带回陈旧文档)与「连点重新匹配互相覆盖」;关面板时代数自增,
   作废所有在飞查询。

4. queryBuiltinCommand 失败从静默吞改为 console.warn:索引失败是
   假阴性(行级标记/便利回填失效、不产错误数据),可诊断即可。
parse_model_metadata_map 现在把 key 统一剥 [1M] 后缀并做 ASCII 小写
归一、变体去重(后写者胜),与前端 canonicalizeModelMetadataMap 完全
一致;model_metadata_override_for_slug 相应从 find_map 首位命中简化为
归一 key 直接查表。

此前两侧消歧规则相反(前端末位胜 / Rust 首位胜,serde_json
preserve_order 下按 JSON 文本序),旧数据同时存在
"DeepSeek-V4-Pro" 与 "deepseek-v4-pro[512K]" 时,前端显示的覆盖
与生成 catalog 实际生效的覆盖不是同一份。仅旧数据触发:新写入链路
两侧都已归一。

新增 metadata_map_normalizes_keys_with_last_write_wins 锁归一化与
后写者胜语义;既有两个 overlay 测试改为经 parse_model_metadata_map
构造(与生产链路同管道)。
1. 补回被 02ee176 无意删除的
   gpt6_sol_luna_metadata_matches_official_efforts_fast_and_default_window
   (当时 commit message 未说明删除原因;资产与解析链路均未变化,
   断言原样保留),并持 RUNTIME_CACHE_ENV_LOCK——catalog 生成链现在
   会读 CODEX_HOME/models_cache.json。

2. builtin_model_metadata_matches_with_source /
   builtin_resolution_and_catalog_requirement_share_suffix_and_case_rules /
   builtin_model_metadata_index_covers_all_embedded_entries 三个测试
   补同一把锁:它们直接调 builtin_model_metadata/builtin_model_metadata_index,
   在装有真实 ~/.codex/models_cache.json 的机器上与临时改写 CODEX_HOME
   的并行测试互踩进程级环境变量。

3. 新增 collect_entries_prefers_inline_suffix_over_model_windows_map:
   锁定「行内 [1M] 后缀优先于 model_windows map」的语义(collect_catalog_entries
   本 PR 起先取行内后缀、再回落 map)。
.zcode/ 拦不住工作区实际出现的 apps/codex-plus-manager/.zcodetmp/
(前缀不同且不在 .zcode/ 之下)。
上一轮 run 36219041571 的 macOS DMG (x64) 在 DMG 已成功创建后
detach /dev/disk2 失败(重试循环含 force 卸载跑了约 29 秒仍未成功),
属 Intel runner 的 mds/Finder 占卷偶发问题:本轮 commit 未触碰
scripts/installer/macos/package-dmg.sh,上一轮同 job 为绿。
无上游 rerun 权限,推空 commit 触发全新 run。

This branch has not been deployed

No deployments
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.

1 participant