feat: 增加 Owner 声纹录入与本地 Profile - #2459
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (3)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
Greptile Summary此 PR 增加 Owner 声纹录入与本地 Profile 管理喵。
Confidence Score: 5/5在本次跟进审查允许的既有线程范围内,没有剩余阻塞问题,PR 看起来可以安全合并喵。 没有符合跟进审查资格且需要阻止合并的失败喵。 Important Files Changed
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2316db40c1
ℹ️ 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".
| def next_profile_revision(self) -> int: | ||
| with self._lock: | ||
| profile = self._profile | ||
| return 0 if profile is None else profile.profile_revision + 1 |
There was a problem hiding this comment.
Keep profile revisions monotonic across deletion
After a profile has been activated, deleting it clears the registry but leaves each manager's VoiceIdentitySession alive with its previous _last_profile_revision. This method then restarts the next enrollment at revision 0; activating that replacement fails the session's profile_revision <= _last_profile_revision check, so the commit rolls back and returns voice_identity_unavailable. Thus, delete-then-re-enroll is unusable until the server restarts; retain a monotonic revision counter even when the current profile is cleared.
Useful? React with 👍 / 👎.
| async def set_filter_enabled(self, enabled: bool) -> EnrollmentStatus: | ||
| async with self._lock: | ||
| self._ensure_open() | ||
| self._registry.set_filter_enabled(enabled) | ||
| return self._status_locked() |
There was a problem hiding this comment.
Connect the filter toggle to ASR admission
Enabling this flag only changes registry/UI state. A repo-wide search shows filter_enabled is never read by the ASR or detector path, while the installed speaker runtime is explicitly observation-only, so every candidate is still forwarded to the provider exactly as when the toggle is off. Users enabling the advertised Voice filter therefore receive a successful response but no reduction in non-Owner ASR usage; wire the result into the admission decision (with the documented fail-open cases) or do not expose the toggle.
Useful? React with 👍 / 👎.
2316db4 to
65c5705
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 65c5705404
ℹ️ 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".
| self._registry.install_profile(profile) | ||
| await self._activate_profile(profile) | ||
| except Exception as exc: | ||
| if persisted: | ||
| await self._rollback_activation(session, profile) |
There was a problem hiding this comment.
Avoid retaining a stale profile revision after rollback
When activation fails after a successful save, _rollback_activation activates the previous profile at failed_profile.profile_revision + 1, but the still-active enrollment session retains profile at the older revision. A retry of /enrollment/commit therefore reinstalls and activates that stale profile; every already-updated VoiceIdentitySession rejects it because revisions must strictly advance, so a transient activation failure makes the session uncommittable until the user cancels and re-enrolls. Update or discard the temporary profile/session after rollback so a retry cannot reuse the obsolete revision.
Useful? React with 👍 / 👎.
| self._registry.clear_profile() | ||
| await self._activate_profile(None) |
There was a problem hiding this comment.
Do not clear the registry before deactivation succeeds
If deactivation fails in any character manager (for example, its verifier runtime cannot close), this clears the registry and reports the profile as deleted before _activate_profile(None) raises. The failing manager can retain its already-created verifier in the live ASR runtime, while subsequent status calls say no profile exists and the persisted recovery copy/key has already been removed. Deactivate all managers successfully before clearing the registry, or force-close/otherwise reconcile failed managers before returning deletion success/state.
Useful? React with 👍 / 👎.
65c5705 to
479b6e6
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 479b6e6e4c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| account = hashlib.sha256( | ||
| str(self._path.parent.resolve()).encode("utf-8") | ||
| ).hexdigest() | ||
| self._protector = protector or KeyringSecretProtector(account=account) |
There was a problem hiding this comment.
Keep the profile key account stable across storage moves
When a user moves the storage root, the existing migration code copies the whole config directory (utils/cloudsave_runtime/legacy_migration.py:139-145), so voice_identity.profile moves to a new parent path. Because the keyring account is derived from self._path.parent.resolve() here, the restored store looks under a different keyring account than the one used to encrypt the copied profile, creates/uses the wrong key, and loads the profile as invalid; users lose their enrolled Owner profile after a supported storage relocation. Use a stable per-installation account or explicitly migrate/delete the key with the profile.
Useful? React with 👍 / 👎.
| elements.reenroll.disabled = state.busy || state.recording; | ||
| elements.delete.disabled = !state.profileAvailable || state.busy || state.recording; | ||
| elements.filter.checked = state.filterEnabled; | ||
| elements.filter.disabled = !state.profileAvailable || state.busy || state.recording; |
There was a problem hiding this comment.
Hide the filter switch until it affects ASR routing
When a profile exists, this line enables the filter switch and /api/voice-identity/filter accepts enabled: true, but a repo-wide search for filter_enabled/filterEnabled shows no ASR routing or verifier path reads that flag—only status/router/tests do, while the speaker shadow is created whenever a profile exists. Users can therefore turn on the advertised ASR-saving filter and still have all audio routed exactly as before; leave the control disabled/hidden until the soft gate is wired.
Useful? React with 👍 / 👎.
概要
这是 #2450 之上的第一个新增堆栈 PR,只实现 Owner 声纹录入、本地加密 Profile 与设置页面;测试版软拦截将在后续堆栈 PR 中实现。
包含 3 个逻辑 commit:
/voice_identity设置页面、Web/Electron 录音流程及 8 个 locale。边界
验证结果
git diff --check通过。Regression Report
Scope
变更只触及本地 Owner Profile、录入控制面和设置 UI。运行时新增的音频 gate 仅在用户明确进入录入 session 且持有 lease 时生效,结束后恢复原路径。
Risk
Provider Matrix
Web / Electron
同一路由和向导状态机覆盖 Web 与 Electron;Electron 单窗口最小化、最大化和关闭行为已验证。页面关闭会取消未完成录入并释放临时 embedding。
Persistence / Privacy
仅保存 normalized embedding、模型标识、维度、profile revision、格式版本和必要时间;不保存 PCM、录音文件、转写文本、固定文案或相似度历史。密钥不与密文 Profile 以明文相邻存放。