Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.

fix(core): canonicalize grouped model variants - #14

Merged
makoMakoGo merged 5 commits into
personal/local-clientsfrom
fix/canonical-model-normalization
May 17, 2026
Merged

fix(core): canonicalize grouped model variants#14
makoMakoGo merged 5 commits into
personal/local-clientsfrom
fix/canonical-model-normalization

Conversation

@makoMakoGo

@makoMakoGo makoMakoGo commented May 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • Canonicalize Claude grouping labels so hyphen/dot/date/tier variants converge to stable IDs like claude-opus-4.6 and claude-sonnet-4.
  • Strip free-tier markers from all model grouping labels, including -free, (free), :free, and variants followed by tier suffixes.
  • Bump the TUI cache schema and update affected cache/data grouping tests.

Fixes #13.

Validation

  • cargo fmt --check
  • cargo test

Summary by CodeRabbit

发布说明

  • 新特性

    • 新增对多个供应商别名的显示名称支持(Z.AI、XiaoMi、MiniMax、Kimi、Doubao、Alibaba、Tencent)
  • 改进

    • 优化模型名称识别与归一化逻辑,更准确地处理模型版本和标记
    • 更新缓存架构版本,旧缓存将自动刷新
    • 统一模型显示标识,改善数据聚合展示的准确性

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 86c640dc-0dea-428b-a938-4a74bc3e65c3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

核心为模型分组归一化升级:新增 free 尾标记清理工具和 Claude 版本规范化逻辑,调整归一化流程顺序;升级缓存 schema 版本强制刷新,同步更新测试夹具和断言;扩展 provider 别名映射覆盖编码/计划类变体。

Changes

模型分组与缓存更新

Layer / File(s) Summary
Free 标记常量与字符串处理工具
crates/tokscale-core/src/lib.rs
新增 FREE_MARKER_TRAILING_SUFFIXESGROUPING_NOISE_SUFFIXESCLAUDE_GROUPING_NOISE_SUFFIXES 常量;实现 strip_trailing_free_markerstrip_direct_free_markerstrip_suffix_once 工具函数,用于识别和剥离尾部 free 变体。
核心归一化流程与通用噪声剥离
crates/tokscale-core/src/lib.rs
normalize_model_for_grouping 新增在推理档位处理前后的 free 尾标记剥离;strip_grouping_noise_tail 改为复用 strip_suffix_once 以统一后缀剥离逻辑。
Claude 分组归一化重构
crates/tokscale-core/src/lib.rs
重写 normalize_claude_model_for_grouping,对 opus/sonnet/haiku 版本格式化与噪声尾缀规则更严格;strip_claude_grouping_noise_tail 改为复用 strip_suffix_once;移除旧的 dots-only 归一化 fallback。
模型归一化测试:Claude、Free 标记与工作空间分组
crates/tokscale-core/src/lib.rs
更新 Claude 版本(如 claude-sonnet-4-5-* 归一为 claude-sonnet-4)、高/思维档位组合的期望结果;新增覆盖 -free:free(free) 及组合的 free 标记剥离测试;同步工作空间分组测试中的归一化模型期望。
缓存 Schema 版本升级与测试夹具更新
crates/tokscale-cli/src/tui/cache.rs
CACHE_SCHEMA_VERSION 从 7 升级至 8;更新多处缓存 JSON 测试夹具的 schemaVersion 字段;将 sourceBreakdown 中的模型键从 claude-sonnet-4-5 改为 claude-sonnet-4 并同步相关字段。
数据聚合测试断言更新
crates/tokscale-cli/src/tui/data/mod.rs
在工作空间+模型分组、每日拆分、消歧、client provider 维度、跨 source 一致性等多个聚合测试用例中,将模型标识期望值从 claude-sonnet-4-5 同步更新为 claude-sonnet-4
Provider 别名映射扩展
crates/tokscale-cli/src/tui/ui/widgets.rs
get_provider_display_name 新增编码/计划类 provider 别名匹配规则(zai/zhipuZ.AIxiaomiXiaoMiminimaxMiniMaxkimi-codeKimi 等);新增对应单测覆盖别名与后缀组合。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #13: 本 PR 直接实现了 issue #13 中对模型分组规范化的需求——free 尾部标记清理(glm-4.7-freeglm-4.7)、Claude 版本规范化(claude-opus-4-6claude-opus-4.6)、避免盲目转换点分为横杠。

Possibly related PRs

  • makoMakoGo/tokscale#5: 两个 PR 同时修改 crates/tokscale-cli/src/tui/ui/widgets.rs 中的 get_provider_display_name 函数,一个扩展编码/计划别名映射,另一个可能涉及相关的显示格式调整。
  • makoMakoGo/tokscale#2: 本 PR 的 crates/tokscale-core/src/lib.rsnormalize_model_for_grouping 的重构与 PR #2 中对同一函数的规范化逻辑修改直接重叠。

Poem

🐰 free 的尾巴要剪掉,
Claude 的版本来规范,
缓存更新一声号,
模型分组齐整洁,
提供商别名更多了!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR title accurately reflects the main change: canonicalizing grouped model variants for consistent grouping/display normalization.
Linked Issues check ✅ Passed All requirements from issue #13 are addressed: Claude model canonicalization with dotted versions preserved, free-tier marker stripping (-free, :free, (free)), and focused test coverage for normalize_model_for_grouping().
Out of Scope Changes check ✅ Passed Changes are within scope: core model normalization refactoring, TUI cache schema version bump, provider display aliases for coding-plan providers, and synchronized test updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/canonical-model-normalization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates the model normalization logic to handle 'free' markers and refines the canonicalization of Claude models, specifically mapping Sonnet and Haiku version 4 models to a base version while maintaining minor versions for Opus. It also increments the cache schema version to 8 and updates relevant test cases. Feedback suggests expanding the FREE_MARKER_TRAILING_SUFFIXES list to include missing noise suffixes like -thinking and -sub2api-pro, and updating colorKey values in test data to maintain consistency with the new model IDs.

Comment thread crates/tokscale-core/src/lib.rs
Comment thread crates/tokscale-cli/src/tui/cache.rs Outdated
Comment thread crates/tokscale-cli/src/tui/cache.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
crates/tokscale-core/src/lib.rs (2)

149-179: ⚡ Quick win

建议为复杂的嵌套循环逻辑添加注释说明。

strip_trailing_free_marker 函数实现了一个多层嵌套的剥离算法:外层循环持续处理直到稳定,内部探测循环通过逐步剥离后缀来寻找被遮蔽的 free 标记。这个算法逻辑正确但较为复杂,建议在以下关键位置添加注释:

  1. 外层 loop 开始处说明"重复剥离直到无变化"的意图
  2. Lines 164-172 探测循环前说明"通过剥离尾缀来暴露隐藏的 free 标记"
  3. 说明为什么需要 trim_end() 和空值检查

这将显著提升代码可读性与可维护性。

📝 建议添加的注释示例
 fn strip_trailing_free_marker(mut name: &str) -> &str {
+    // 重复剥离 free 标记及其关联后缀,直到字符串稳定
     loop {
         let trimmed = name.trim_end();
         if trimmed.len() != name.len() {
             name = trimmed;
             continue;
         }

+        // 尝试直接剥离 free 标记(-free, :free, (free) 等)
         if let Some(value) = strip_direct_free_marker(name) {
             if !value.is_empty() {
                 name = value.trim_end();
                 continue;
             }
         }

+        // 探测:逐步剥离尾缀以暴露被遮蔽的 free 标记
+        // 例如 "model-free-high" -> 剥离 "-high" -> 发现 "-free"
         let mut probe = name;
         let mut exposed_free = None;
         while let Some(stripped_probe) = strip_suffix_once(probe, FREE_MARKER_TRAILING_SUFFIXES) {
             probe = stripped_probe.trim_end();
             if let Some(value) = strip_direct_free_marker(probe) {
                 exposed_free = Some(value.trim_end());
                 break;
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tokscale-core/src/lib.rs` around lines 149 - 179, Add explanatory
comments inside the strip_trailing_free_marker function: at the start of the
outer loop describe the intent "repeat stripping until stable/no-change", before
the inner probe loop (the while let Some(stripped_probe) =
strip_suffix_once(...)) explain "strip trailing suffixes iteratively to expose a
hidden direct free marker", and add a brief note near uses of trim_end() and the
empty checks to explain they remove trailing whitespace and guard against empty
results so we don't loop forever or return misleading empty names; reference the
function name strip_trailing_free_marker and the inner probe loop that uses
strip_suffix_once and strip_direct_free_marker when placing these comments.

265-267: 💤 Low value

Claude 版本 4 的特殊处理规则已硬编码。

Lines 265-267 对 claude-sonnet-4.xclaude-haiku-4.x 强制归一化为 claude-{family}-4,但 claude-opus-4.x 保留次版本号。这符合当前 PR 目标和测试预期。

然而,这个规则是硬编码的版本号判断。若未来发布 Claude 5.x 或其他主版本,需要重新评估是否需要类似的特殊处理逻辑。建议在代码注释中记录这个设计决策的背景。

💡 建议添加的注释
+    // 特殊规则:Sonnet 4.x 和 Haiku 4.x 归一化为版本 4(无次版本号)
+    // 但 Opus 4.x 保留次版本号以区分不同变体
+    // 此规则基于 Anthropic 的官方命名约定(截至 2025 年)
     if major == "4" && matches!(family, "sonnet" | "haiku") {
         return Some(format!("claude-{family}-4"));
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tokscale-core/src/lib.rs` around lines 265 - 267, Add a brief
explanatory comment above the special-case block that checks major == "4" and
matches!(family, "sonnet" | "haiku") (the clause that returns
Some(format!("claude-{family}-4"))) describing why Claude 4.x sonnet/haiku are
being normalized to `claude-{family}-4`, that `claude-opus-4.x` intentionally
preserves the minor version, and note this is a deliberate, version-specific
decision that may need reevaluation for future major releases (e.g., Claude
5.x); include guidance to revisit the rule if new major versions are released.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/tokscale-core/src/lib.rs`:
- Around line 149-179: Add explanatory comments inside the
strip_trailing_free_marker function: at the start of the outer loop describe the
intent "repeat stripping until stable/no-change", before the inner probe loop
(the while let Some(stripped_probe) = strip_suffix_once(...)) explain "strip
trailing suffixes iteratively to expose a hidden direct free marker", and add a
brief note near uses of trim_end() and the empty checks to explain they remove
trailing whitespace and guard against empty results so we don't loop forever or
return misleading empty names; reference the function name
strip_trailing_free_marker and the inner probe loop that uses strip_suffix_once
and strip_direct_free_marker when placing these comments.
- Around line 265-267: Add a brief explanatory comment above the special-case
block that checks major == "4" and matches!(family, "sonnet" | "haiku") (the
clause that returns Some(format!("claude-{family}-4"))) describing why Claude
4.x sonnet/haiku are being normalized to `claude-{family}-4`, that
`claude-opus-4.x` intentionally preserves the minor version, and note this is a
deliberate, version-specific decision that may need reevaluation for future
major releases (e.g., Claude 5.x); include guidance to revisit the rule if new
major versions are released.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0334f54-1283-4689-ad7f-61b036d354ed

📥 Commits

Reviewing files that changed from the base of the PR and between 4cc5625 and 3b78697.

📒 Files selected for processing (3)
  • crates/tokscale-cli/src/tui/cache.rs
  • crates/tokscale-cli/src/tui/data/mod.rs
  • crates/tokscale-core/src/lib.rs

@makoMakoGo

Copy link
Copy Markdown
Owner Author

Added the requested provider display aliases after checking OMP/OpenCode provider IDs:

  • zai* / zhipu* -> Z.AI
  • xiaomi* -> XiaoMi
  • minimax* -> MiniMax
  • kimi-code / kimi-for-coding -> Kimi
  • doubao* -> Doubao
  • alibaba* -> Alibaba
  • tencent* / tecent* -> Tencent

Validation: cargo fmt --check, focused provider-display test, and full cargo test all pass.

@makoMakoGo

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/tokscale-core/src/lib.rs`:
- Around line 66-78: The Claude-specific normalization misses a common suffix
and the date-strip pass happens only once before the Claude branch; update the
CLAUDE_GROUPING_NOISE_SUFFIXES constant to include "-sub2api-pro" and change the
Claude normalization flow so date/provider/tier normalization runs after suffix
removal (or perform the date-strip / normalization iteratively until stable) in
the Claude branch handling code (the block that currently uses
CLAUDE_GROUPING_NOISE_SUFFIXES and the provider/tier/date normalization around
the Claude branch).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45d88f7d-3a28-40f8-aaf9-0680bd8dc7da

📥 Commits

Reviewing files that changed from the base of the PR and between 3b78697 and e669637.

📒 Files selected for processing (2)
  • crates/tokscale-cli/src/tui/ui/widgets.rs
  • crates/tokscale-core/src/lib.rs

Comment thread crates/tokscale-core/src/lib.rs
@makoMakoGo
makoMakoGo merged commit 8cacdd0 into personal/local-clients May 17, 2026
3 checks passed
@makoMakoGo
makoMakoGo deleted the fix/canonical-model-normalization branch May 17, 2026 18:44
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant