Skip to content

fix(voice): 自动平移废弃的免费 YUI 预设音色,避免存量用户掉档到 qingchunshaonv#1665

Merged
wehos merged 5 commits into
mainfrom
claude/nervous-booth-855a05
Jun 6, 2026
Merged

fix(voice): 自动平移废弃的免费 YUI 预设音色,避免存量用户掉档到 qingchunshaonv#1665
wehos merged 5 commits into
mainfrom
claude/nervous-booth-855a05

Conversation

@wehos
Copy link
Copy Markdown
Contributor

@wehos wehos commented Jun 6, 2026

背景

#1639free_voices[\"yui_cn\"]voice-tone-R6NtLH3Hk0 换成 voice-tone-RcH2svtsrw,但只改了 config 模板和解析源,没有迁移已写进存量用户 characters.json 的旧 tone ID

问题(国内免费版静默掉档)

旧值 voice-tone-R6NtLH3Hk0 现在不在 free_voices 白名单里,于是每次 start_sessionmain_logic/core.py)和 app 初始化(app/main_server.py)跑的 cleanup_invalid_voice_ids 会把它判为 invalid 清空:

  1. validate_voice_id 三项落空(非云端克隆 / 非原生音色 / 不在 free_voices)→ 清空成 ''
  2. 没有任何东西在该路径重绑新 YUI(ensure_default_yui_voice_for_free_api 只在「保存 API 配置」「清除音色」两个用户主动动作里跑)
  3. 空 voice → free provider(inherits: step)的 default_voice = qingchunshaonv

结果:「一直吃默认 YUI、从没手动选过音色」的国内免费用户,下次启动就无声掉档到通用女声。海外免费版默认存的是字面量 yui(free_intl catalog 内,cleanup 会放行),不受影响。

改动

cleanup_invalid_voice_ids 判 invalid 之前先把废弃 YUI tone 平移到现役 yui_cn

  • 新增 _DEPRECATED_FREE_YUI_VOICE_IDS 集合 + is_deprecated_free_yui_voice_id / remap_deprecated_free_yui_voice_id(对偶既有 is_legacy_cosyvoice_id
  • 现役值解析不出(或仍落在废弃集合)时不乱换,交既有清空兜底,绝不废弃换废弃造成死循环
  • 迁移命中触发存盘;天然覆盖 app 初始化与每次 start_session 两条 cleanup 路径,存量用户无需任何手动操作
  • 将来再更替 YUI 音色,把被替换掉的旧值追加进集合即可

测试

tests/unit/test_deprecated_yui_voice_migration.py:平移命中、现役值不动且不存盘、无关 invalid 仍清空(回归)、现役不可解析时不乱换、谓词边界。本地 test_deprecated_yui_voice_migration + test_yui_free_api_default_voice + gemini/native/stepfun voice 套件共 64 passed。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 测试

    • 新增单元测试覆盖旧版免费 YUI 语音预设的识别、迁移与清理行为(含前后空白、海外/路由差异与不可解析场景),确保已为现役值时不变更且不写回,异常或无效值会被清空并计数。
  • 改进

    • 配置处理增强:识别并在合适场景下有条件迁移废弃免费 YUI 语音预设,增加迁移/清理计数与汇总日志,仅在有变动时写回配置,减少不必要的写入。

#1639 把 free_voices["yui_cn"] 从 voice-tone-R6NtLH3Hk0 换成 voice-tone-RcH2svtsrw,
但只改了 config 模板和解析源,没迁移已写进存量 characters.json 的旧 tone ID。旧值已
不在 free_voices 白名单 → cleanup_invalid_voice_ids 判 invalid 清空 → 空 voice 落到
free/step 的 default_voice(qingchunshaonv),导致「一直吃默认 YUI、从没手动选过音色」
的免费用户在音色 ID 更替后无声掉档到通用女声。

在 cleanup 判 invalid 前先把废弃 YUI tone 平移到现役 yui_cn:
- 新增 _DEPRECATED_FREE_YUI_VOICE_IDS 集合 + is_deprecated_free_yui_voice_id /
  remap_deprecated_free_yui_voice_id(对偶既有 is_legacy_cosyvoice_id)
- 现役值解析不出(或仍落废弃集合)时不乱换,交既有清空兜底,绝不废弃换废弃
- 迁移命中触发存盘;覆盖 app 初始化与每次 start_session 两条 cleanup 路径

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 6, 2026

Need an answer fast? Review this PR in Change Stack to ask focused questions about the PR or a changed range.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b8b0185f-f455-4fdb-8a7c-4e024e36c287

📥 Commits

Reviewing files that changed from the base of the PR and between af8e26e and 78a618b.

📒 Files selected for processing (2)
  • tests/unit/test_deprecated_yui_voice_migration.py
  • utils/config_manager.py

Walkthrough

新增废弃免费 YUI voice_id 常量与 remap/is_deprecated 方法;在 cleanup_invalid_voice_ids 中优先对命中废弃 ID 做 remap 并计入 migrated_count,随后在 cleaned_count 或 migrated_count 任一非零时保存 characters.json;新增单元测试覆盖多种迁移与清理边界场景喵。

Changes

废弃YUI音色迁移完整实现

Layer / File(s) Summary
迁移常量与判定/重映射方法
utils/config_manager.py
新增 _DEPRECATED_FREE_YUI_VOICE_IDS 常量集合与两个静态方法:is_deprecated_free_yui_voice_id() 判断是否为废弃 ID,remap_deprecated_free_yui_voice_id() 在命中且路由为 free 且非海外时尝试返回 free_voices['yui_cn'],目标缺失或仍在废弃集合时回退原值;海外与非 free 路由直接返回原值喵。
cleanup_invalid_voice_ids 的迁移分支与保存策略
utils/config_manager.py
cleanup_invalid_voice_ids() 在遍历角色 _reserved.voice_id 时先调用 remap;若发生迁移则更新字段、累计 migrated_count 并跳过后续无效校验与清空逻辑;最终当 cleaned_countmigrated_count 任一大于 0 时调用 save_characters() 并分别输出清理/迁移统计日志喵。
回归测试:废弃映射、空白处理与清理场景
tests/unit/test_deprecated_yui_voice_migration.py
新增测试文件含辅助构造函数与多项用例:验证旧 YUI ID(含前后空白)在国内 free 下会迁移为 yui_cn 并触发保存;海外/非 free 场景保持原值且在 validate 失败时会被清空计数;测试还覆盖 free_voices 缺少 yui_cn 或 free_voices 为空时的回退行为,以及 is_deprecated_free_yui_voice_id 谓词行为喵。

Sequence Diagram

sequenceDiagram
  participant Cleanup as cleanup_invalid_voice_ids()
  participant Detect as ConfigManager.is_deprecated_free_yui_voice_id()
  participant Remap as ConfigManager.remap_deprecated_free_yui_voice_id()
  participant FreePresets as utils.api_config_loader.get_free_voices()
  participant Character as character._reserved.voice_id
  participant Save as save_characters()

  Cleanup->>Character: 读取角色的 voice_id
  Cleanup->>Detect: 判定是否为废弃免费 YUI ID
  Detect-->>Cleanup: True / False
  alt 命中废弃集合
    Cleanup->>Remap: 调用 remap_deprecated_free_yui_voice_id(voice_id)
    Remap->>FreePresets: 读取 free_voices(判断 yui_cn)
    Remap-->>Cleanup: 返回替换值(解析到 yui_cn 或返回原值)
    Cleanup->>Character: 若替换成功则更新 voice_id;migrated_count += 1
  else 非废弃或替换失败
    Cleanup->>Cleanup: 进行原有校验;若无效则清空 voice_id;cleaned_count += 1
  end
  Cleanup->>Cleanup: 检查 cleaned_count 或 migrated_count
  alt 任一大于 0
    Cleanup->>Save: 调用 save_characters()
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🎧 旧声悄然落尘埃,yui_cn 轻步接替喵。
测试与日志为夜灯,断言闪烁把路指喵。
迁移计数轻声数,存盘一刻安心知喵。
喵~新音已就位,猫步轻盈又一回喵。

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 标题准确总结了主要改动:自动平移废弃的免费 YUI 预设音色,避免存量用户掉档问题,与代码变更高度相关。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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


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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 `@utils/config_manager.py`:
- Around line 2905-2920: The code currently compares the raw voice_id to
remap_deprecated_free_yui_voice_id() and may treat values that only differ by
whitespace as "remapped" and skip validation; fix by normalizing the original
before remap and only treat as migrated when the normalized original is actually
a deprecated value: retrieve voice_id via get_reserved, compute
normalized_original = voice_id.strip() (or other project normalization), then
call remap_deprecated_free_yui_voice_id(normalized_original); only if remapped
is non-empty and remapped != normalized_original call set_reserved(config,
'voice_id', remapped), increment migrated_count, log, and continue — otherwise
assign the normalized value back (or fall through to validate_voice_id) so
validate_voice_id can clean/clear truly invalid IDs.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 20d0cf1f-dae2-48c9-ac10-ea38f039b914

📥 Commits

Reviewing files that changed from the base of the PR and between 1c9e0d6 and 94fa15a.

📒 Files selected for processing (2)
  • tests/unit/test_deprecated_yui_voice_migration.py
  • utils/config_manager.py

Comment thread utils/config_manager.py
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94fa15a454

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread utils/config_manager.py
# 已废弃的免费 YUI 预设音色:先平移到现役 yui_cn,再 continue 跳过后续
# invalid 判定(新值在 free_voices 白名单内本就合法),保住默认 YUI 音色
remapped = self.remap_deprecated_free_yui_voice_id(voice_id)
if remapped and remapped != voice_id:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid treating every stripped voice ID as a YUI migration

When a stored non-YUI voice_id has leading/trailing whitespace, remap_deprecated_free_yui_voice_id() returns the stripped value, so this condition treats it as a successful deprecated-YUI migration and continues before validate_voice_id() runs. For example, a stale value like some-stale-clone is saved as some-stale-clone and reported as migrated instead of being cleared during this cleanup pass, leaving an invalid voice active until a later cleanup.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已采纳,33a14daa6 修复(与上面 CodeRabbit 同一处)。非废弃值现在原样返回原 voice_id、不再 strip,带空白的非废弃 voice_id 不会被当成 YUI 迁移而跳过清理。

Comment thread utils/config_manager.py Outdated
Comment on lines +2385 to +2387
current = _get_default_yui_free_voice_id()
if current and current not in _DEPRECATED_FREE_YUI_VOICE_IDS:
return current
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require yui_cn before remapping retired YUI IDs

In configurations where free_voices still contains other presets but no yui_cn entry, _get_default_yui_free_voice_id() falls back to cuteGirl or the first available preset. That makes this migration rewrite the retired YUI tone to an unrelated voice instead of leaving it for the existing validation/clear fallback, which is the documented behavior when the current YUI value cannot be resolved.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch,已采纳,33a14daa6 修复。remap 不再复用 _get_default_yui_free_voice_id(它带 cuteGirl/首个 preset 的宽松 fallback,那是给 ensure_default_yui_voice_for_free_api 的容错语义),改为直接读 free_voices["yui_cn"]——废弃值本就是 yui_cn 的旧版本,精确平移回它。yui_cn 缺失/为空/仍落在废弃集合时不迁移、交既有清空兜底,绝不借别的 preset 当替身把 YUI 串成别的音色。已加回归 test_remap_requires_yui_cn_not_other_preset

CodeRabbit/Codex: remap 对非废弃值做了 strip 归一化,带前后空白的无效 voice_id 会
因 remapped != voice_id 被误当「已迁移」continue,漏掉本轮 invalid 清理 → 改为非废弃
值原样返回原值(strip 收口在 _get_voice_id 源头,不是 remap 的职责)。

Codex: 复用 _get_default_yui_free_voice_id 会在 free_voices 缺 yui_cn 时 fallback 到
cuteGirl/首个 preset,把废弃 YUI 串成别的音色 → 改为直接读 free_voices["yui_cn"],
缺失/为空/仍落废弃集合时不迁移,交既有清空兜底。

新增「带空白废弃仍迁移」「带空白非废弃 invalid 仍清空」「缺 yui_cn 不借替身」三条回归。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/unit/test_deprecated_yui_voice_migration.py (1)

51-61: 💤 Low value

可选建议:补充存盘断言以保持测试一致性喵~

Line 60 验证了 voice_id 迁移成功,但没有像 Line 48 那样断言 mgr._saved.get("data") is character_data,检查迁移是否触发了存盘喵。虽然实现逻辑不太可能因空白处理破坏存盘触发,但保持断言一致性能让测试更完整呢喵!

✨ 可选补充
     assert cleaned == 0
     assert get_reserved(character_data["猫娘"]["YUI"], "voice_id", default="") == NEW_YUI_VOICE_ID
+    assert mgr._saved.get("data") is character_data
🤖 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 `@tests/unit/test_deprecated_yui_voice_migration.py` around lines 51 - 61, The
test function test_cleanup_migrates_whitespace_padded_deprecated_voice verifies
that voice_id migration occurred but is missing an assertion to confirm that the
save operation was triggered by the migration. Add an assertion after the
existing assertions (similar to what is done in the test at line 48) to verify
that mgr._saved.get("data") returns the character_data object, ensuring that the
cleanup operation properly triggered the save mechanism when migrating the
whitespace-padded deprecated voice ID.
🤖 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 `@tests/unit/test_deprecated_yui_voice_migration.py`:
- Around line 51-61: The test function
test_cleanup_migrates_whitespace_padded_deprecated_voice verifies that voice_id
migration occurred but is missing an assertion to confirm that the save
operation was triggered by the migration. Add an assertion after the existing
assertions (similar to what is done in the test at line 48) to verify that
mgr._saved.get("data") returns the character_data object, ensuring that the
cleanup operation properly triggered the save mechanism when migrating the
whitespace-padded deprecated voice ID.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dc6aaa91-9a2a-433c-8017-8cd07f1e88cf

📥 Commits

Reviewing files that changed from the base of the PR and between 94fa15a and 33a14da.

📒 Files selected for processing (2)
  • tests/unit/test_deprecated_yui_voice_migration.py
  • utils/config_manager.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • utils/config_manager.py

与 test_cleanup_migrates_deprecated_yui_voice 对偶,验证带前后空白的废弃值迁移
路径同样触发 save_characters,而非只改了内存对象。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a80476656d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread utils/config_manager.py
if not self.is_deprecated_free_yui_voice_id(voice_id):
return voice_id
from utils.api_config_loader import get_free_voices
current = str((get_free_voices() or {}).get("yui_cn") or "").strip()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate deprecated YUI remaps by route

When a free user is on the overseas lanlan.app route and still has this deprecated StepFun YUI tone in characters.json, remapping it unconditionally to free_voices["yui_cn"] preserves a domestic free preset where the overseas path expects the literal yui/Gemini catalog instead. I checked main_logic/core.py: _resolve_session_use_tts() only treats free presets as native on lanlan.tech, and otherwise a nonempty self.voice_id falls into external TTS, while native_voice_registry remaps free+lanlan.app to free_intl where yui_cn is not a native voice. Before this change the stale ID was cleared by validation, so this can regress those users from the intended overseas native/default voice path to an external TTS path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed,好 catch,af8e26e14 修复。你说的对:海外 lanlan.appfree_intl 不认 voice-tone-*,无条件 remap 成国内 yui_cn 会让非空 voice_id 在 _resolve_session_use_tts(free preset 只在 lanlan.tech 当 native)落进 external TTS,对从国内切到海外的存量用户是 regression。

已把 remap 改成按线路分流,对偶 ensure_default_yui_voice_for_free_api"yui"/yui_cn 二分:仅 core=free 才迁移(非 free 路由原样返回交清空兜底);海外 free(is_free_lanlan_app_route + _check_non_mainland 兜底)→ 品牌 sentinel "yui"(free_intl native,走海外原生语音);国内 free(lanlan.tech)→ 现役 free_voices["yui_cn"]。新增海外绑 yui / 地理兜底 / 非 free 不迁移三条回归。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

更正一下我上一条回复的方向:78a618b7a 起,海外 free 不再 remap 成 "yui"。经核对 PR #1643 的既有契约——海外 free(lanlan.app→free_intl)下 stale/未知 voice id 应交服务端默认音色 fallback,客户端不得注入 yui/native alias(free_intl 继承 Gemini-native provider,StepFun magic id 及其 alias 都不该漏进该 catalog)。所以现在只有国内 free(lanlan.tech)迁移到 yui_cn;海外 free 原样返回交既有 validate 清空 → 落服务端默认,与你最初指出的「按路由 gate、别保留国内 preset」一致。

Codex: 海外 free(lanlan.app→free_intl)用户若残留国内 StepFun YUI tone,无条件
remap 成 voice-tone yui_cn 仍是国内 preset;海外不认 voice-tone-*,非空 voice_id 会
从海外 native/default(yui) 路径 regress 到 external TTS(_resolve_session_use_tts 仅
在 lanlan.tech 把 free preset 当 native)。改前 stale id 本会被 validation 清空。

remap 改为对偶 ensure_default_yui_voice_for_free_api 的线路二分:
- 仅 core=free 才迁移;非 free 路由原样返回交清空兜底
- 海外 free(lanlan.app / _check_non_mainland 兜底)→ 品牌 sentinel "yui"
- 国内 free(lanlan.tech)→ 现役 free_voices["yui_cn"]

新增「海外绑 yui」「URL 仍 tech 但地理判海外兜底」「非 free 不迁移」三条用例。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 `@tests/unit/test_deprecated_yui_voice_migration.py`:
- Around line 6-8: The tests in
tests/unit/test_deprecated_yui_voice_migration.py currently assert that overseas
free IDs are remapped to "yui" which breaks the contract for free_intl; update
the affected test cases (those asserting voice_id == "yui" for
overseas/free_intl scenarios, and any tests around
ensure_default_yui_voice_for_free_api, cleanup, or StepFun ID handling) to
instead assert that no "yui" remapping occurs — i.e., that the original voice_id
is preserved (or equals the non-remap result produced by cleanup) for
free_intl/overseas routes; keep assertions explicit: check voice_id != "yui"
and/or voice_id == original_id (or the cleanup-returned id) rather than forcing
"yui".
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 952d6db7-6ae9-4aee-9417-9d9ed89ac45b

📥 Commits

Reviewing files that changed from the base of the PR and between a804766 and af8e26e.

📒 Files selected for processing (2)
  • tests/unit/test_deprecated_yui_voice_migration.py
  • utils/config_manager.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • utils/config_manager.py

Comment thread tests/unit/test_deprecated_yui_voice_migration.py Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: af8e26e142

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread utils/config_manager.py Outdated
Comment on lines +2406 to +2407
if overseas:
return "yui"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Remap persisted yui when returning domestic

For users whose deprecated YUI tone is first cleaned on the overseas lanlan.app route, this persists the route-specific sentinel "yui" and skips validation. If the same character later runs on the domestic lanlan.tech free route, validate_voice_id() does not accept "yui" there (the effective native provider is free, and free_voices contains the StepFun yui_cn tone instead), so the next cleanup clears the voice and the user falls back to qingchunshaonv; the original deprecated ID has already been overwritten, so it can no longer be mapped to yui_cn. Consider also migrating saved "yui" back to the current yui_cn on the domestic free route, or avoid persisting a route-specific sentinel as the only migration state.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已采纳你的第二个建议(不持久化 route-specific sentinel),78a618b7a 修复。海外 free 不再 remap 成 "yui",而是原样返回交既有 validate 清空成空串(而非 "yui")。空 voice 切回国内时由 update_core_config → ensure_default_yui_voice_for_free_api 重绑现役 yui_cn(与默认 YUI 首次绑定同源),不会停在 qingchunshaonv。关键区别:空值能触发 ensure_default 重绑,而 af8e26e 存的非空 "yui" 会让 ensure_default 因 voice 非空跳过、再被 cleanup 清空,正是你指出的掉档路径。

af8e26e 让海外 free 把废弃 StepFun YUI tone remap 成 "yui" sentinel,违反 PR #1643
确立的设计原则:海外 free(lanlan.app→free_intl)下未知/stale voice id 应交服务端默认
音色 fallback,客户端不得注入 yui 或 native alias(free_intl 继承 Gemini-native
provider,不能把 StepFun magic id/alias 漏进该 catalog)。

remap 改为只有国内 free(lanlan.tech)才迁移到现役 yui_cn;海外 free 与非 free 路由均
原样返回,交既有 validate 判 invalid 清空 → 落服务端默认。两种线路运行时都得到正确 YUI
音色,且 cross-region 往返更稳(切回国内由 ensure_default 兜底重绑)。

测试相应改为海外不 remap(原样返回 / cleanup 清空),删掉 "yui" sentinel 断言。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wehos wehos merged commit de8f2ec into main Jun 6, 2026
5 checks passed
@wehos wehos deleted the claude/nervous-booth-855a05 branch June 6, 2026 04:15
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