Skip to content

Voice Identity:建立 Independent ASR 生产基础链路 - #2450

Draft
MomiJiSan wants to merge 11 commits into
Project-N-E-K-O:codex/phase4-c-speaker-shadowfrom
MomiJiSan:codex/voice-identity-foundation-20260723
Draft

Voice Identity:建立 Independent ASR 生产基础链路#2450
MomiJiSan wants to merge 11 commits into
Project-N-E-K-O:codex/phase4-c-speaker-shadowfrom
MomiJiSan:codex/voice-identity-foundation-20260723

Conversation

@MomiJiSan

@MomiJiSan MomiJiSan commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

目标

#2408 的真实 CAM++ Shadow 之上收敛 provider-neutral 的生产基础链路,只打通:

固定测试 PCM → 真实 CAM++ enrollment → 内存 profile → Core 正式内部入口 → IndependentAsrRuntime → Detector candidate → 真实 CAM++ 评分 → 无执行权 observer → similarity observation → disable/close 清理。

本 PR 是 stacked Draft,base 仍为 #2408 分支。#2398 / #2408 尚未合并前不转 Ready,也不请求正式 Review。

本轮收敛

  • 增加正式内部 set_speaker_identity_observer(callback | None) 端口;测试不再修改 Core 的 callback 私有字段。
  • set_active_speaker_profile(profile | None) 明确返回 profile revision 或 None;CAM++ model ID、revision、192 维、finite 与 L2 合同会在激活时立即校验。
  • profile 激活不解析模型资产、不创建 ONNX session;模型缺失仍只在 runtime 创建时 fail-open。
  • profile revision 必须严格递增;更新期间旧 revision 立即失效,旧 observation 会 stale。
  • verifier 替换顺序固定为:解除旧 verifier → 失效旧 candidate → 完全关闭旧 runtime/backend → 安装新 verifier。
  • ASR route rebuild 会先从旧 Detector 转移 verifier 清理所有权;新 Detector 在旧 verifier 完全关闭前保持声纹关闭,清理完成后才按最新 factory 自动装配。
  • 旧 verifier 清理超时或失败时不安装新 verifier,声纹保持关闭;有界后台清理不会遗留协调 task,Provider PCM、ASR lifecycle 和 endpoint authority 继续工作。
  • 新增 wait_closed() 受控关闭合同,同时保留普通 close() 的快速返回/延后清理语义。
  • 增加低基数 replacement failure 指标,不记录 PCM、embedding 或身份信息。

真实完整链路证据

新增固定合成 PCM 的 process-level smoke,不提交 PCM、embedding 或模型权重:

CampPlusEmbeddingModel → build_campplus_speaker_profile → Core.set_active_speaker_profile → Core.set_speaker_identity_observer → IndependentAsrRuntime → DetectorRuntime → SpeakerShadowRuntime → CampPlusSpeakerShadowBackend → observation

验证结果:

  • 三段 1.5 秒 enrollment 生成真实 192 维内存 profile。
  • profile 激活阶段 candidate backend load 次数为 0。
  • 第一段满足条件的 candidate 才加载真实 CAM++。
  • 两次正式链路 similarity 均约为 1.0(相同固定合成输入,仅证明链路,不代表准确率)。
  • observation 不含 PCM 或 embedding。
  • disable 后 Detector 不再持有 verifier;close 后活动 CAM++ session 为 0。
  • profile 更新与 ASR route rebuild 两条路径的最大同时活动 CAM++/ONNX session 均为 1。

Provider / 生命周期隔离

固定不可变输入按 baseline → identity → identity → baseline 重放,并比较:

  • Provider 连续 PCM 总字节、拼接 SHA256 与字节顺序。
  • seal 次数、lifecycle state、Detector final state。
  • pending close task、dispatcher worker 与 candidate buffer。

上述合同一致;不比较 callback chunk 数量。profile 快速更新、ASR route rebuild、清理失败/超时、Detector reset/close、observer 更换与失败均有受控竞态测试。

Regression Report

  • 声纹、真实 CAM++、Detector、Core、结构与打包聚焦矩阵:249 passed
  • 全部 ASR / Core / VoiceTurn 单元回归:648 passed, 2 skipped
    • 两项 skip 仅因本机未准备 SmartTurn/Silero 可选真实资产;CAM++ 真实模型测试已实际执行并通过。
  • Ruff、compileall、async-blocking、startup lazy import、9 个 workflow YAML、git diff --check:通过。
  • Windows/Linux build-contract:通过;远端双平台正式构建/离线安装产物 smoke 仍由 stacked PR 最终 CI 验证。
  • GitNexus PR 全量变更审计:MEDIUM,20 个预期文件,5 条既有 Independent ASR start/close 执行流;未命中 Provider worker/registry、transport、SmartTurn 算法、native Omni、UI、数据库流程。

结构合同

  • voice_identity 不反向依赖 Core、Provider worker、transport 或 UI。
  • Core 不直接导入 voice_identity.campplus
  • ASR adapter 只依赖公开 voice_identity.contracts
  • voice_identity 依赖图无环。
  • 只有一份 CampPlusEmbeddingModel 和一处模型专属 ONNX session 创建实现。
  • asr_client/campplus.py 兼容 facade 已删除。
  • asr_client/speaker_shadow.py 只拥有 ASR candidate identity;SpeakerShadowConfigSpeakerShadowObservationSpeakerShadowRuntime 继续归 voice_identity

明确不包含

  • 产品 enrollment UI/API、自动录入、持久化、配置或数据库。
  • 生产阈值、hard block、transcript/Provider PCM 拦截。
  • 音频延迟、缓存、暂存、重放或 Provider 路由分支。
  • native Omni、攻击防御、录音回放、AI 克隆或多人处理。
  • owner/non-owner 批量录音,以及小声、疲劳、感冒、远场测试。
  • SmartTurn、RNNoise、Silero 算法或参数修改。
  • 云端 ASR 准确率测试。

停止点

本 PR 到“正式 Independent ASR 运行时可显式接收内存声纹,使用真实 CAM++ 对候选音频评分,并通过无执行权 observer 输出结果;profile 更新和 ASR route rebuild 时最多一个 ONNX session,且不影响 Provider/ASR”即停止扩展。

这仍不是面向用户的声纹安全功能,也不执行任何拦截。

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

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

🗂️ Base branches to auto review (3)
  • steam
  • react-rewrite
  • plugin

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f133f6e4-9f38-46e7-8f53-18a825a18ba3

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

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.

@MomiJiSan

Copy link
Copy Markdown
Contributor Author

基础链路收敛验证(2026-07-23)

  • 基线:Phase 4C:接入无执行权的声纹 Shadow Runtime #2408 head 3948947;本 PR 两个提交 a845218、3835e6873。
  • 正式链路:内存 profile → Core set_active_speaker_profile → IndependentAsrRuntime → DetectorRuntime → observation。
  • 组合 smoke:1.5 秒候选经正式入口产生唯一 similarity observation;profile=None 后当前 Detector verifier 被移除。
  • 生命周期:profile revision 必须递增;更新会关闭旧 runtime;旧 callback stale;session close 释放 profile/runtime。
  • 隔离:verifier 替换不改变 endpoint authority、Provider candidate generation、PCM 路由或 ASR final。
  • 测试:497 passed;聚焦组合回归 222 passed;真实模型与 build-contract 5 passed。
  • 静态检查:Ruff、compileall、async-blocking、startup lazy import、workflow YAML、diff check 全通过。
  • GitNexus:模块/adapter MEDIUM(仅 5 条既有 Independent ASR start/close/status 流);Core 组合入口 LOW(0 条受影响流)。

边界保持:Draft;无阈值、无拦截、无 UI/API、无持久化、无 Omni、无攻击防御、无 Provider/SmartTurn/RNNoise/Silero 行为修改。

MomiJiSan commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

#2450 基础链路收敛验证(head 4a9cd476b

本轮只完成正式 Independent ASR 内部声纹基础链路,不增加产品调用方,也不执行拦截。

已收敛

  • 正式 profile 入口:set_active_speaker_profile(profile | None) -> int | None
  • 正式 observation 入口:set_speaker_identity_observer(callback | None);测试不再修改 Core callback 私有字段。
  • CAM++ model ID / revision / 192 维 / finite / L2 合同在 profile 激活时立即校验;激活阶段不加载模型。
  • profile revision 必须递增;旧 runtime 的迟到结果因 revision 不匹配而 stale。
  • verifier 更新严格执行“先解除并完全关闭旧 verifier,再安装新 verifier”。
  • ASR route rebuild 会先转移旧 verifier 的清理所有权;新 Detector 在旧 verifier 完全关闭前保持声纹关闭,之后才自动装配最新 factory。
  • 清理失败或有界超时后不安装新 verifier,协调 task 会收束,ASR/Provider 继续。
  • close() 继续快速 fail-open;新增 wait_closed() 只用于 profile 更新和受控关闭。
  • profile 更新与 ASR route rebuild 两条路径的最大同时活动 CAM++/ONNX session 已由自动化证明为 1。

真实完整链路

固定合成 PCM 已实际跑通:

真实 CAM++ enrollment → 内存 profile → Core 正式 profile/observer 入口 → IndependentAsrRuntime → DetectorRuntime → SpeakerShadowRuntime → 真实 CAM++ candidate embedding/cosine → observation

结果:

  • 3 段 1.5 秒 enrollment;profile 为真实 192 维 CAM++ embedding。
  • profile activation 时 candidate backend load = 0。
  • 第一段达到条件的 candidate 才加载真实模型。
  • 两次正式链路 similarity 均约为 1.0(同一固定合成输入,仅证明链路正确,不代表识别准确率)。
  • observation 不包含 PCM 或 embedding。
  • disable 后不再持有 verifier;close 后 active session = 0。

主路径隔离

同一组 immutable frames 按 baseline → identity → identity → baseline 重放:

  • Provider 连续 PCM、拼接 SHA256、字节顺序一致。
  • seal 次数、lifecycle state、Detector final state 一致。
  • dispatcher/close task/candidate buffer 无残留。
  • ASR route rebuild 期间声纹可以暂时关闭,但 Provider PCM、ASR lifecycle 与 endpoint authority 继续。
  • 不比较 callback chunk 数量。

回归

  • 聚焦声纹 / 真实 CAM++ / Detector / Core / 结构 / 打包:249 passed
  • 全 ASR / Core / VoiceTurn:648 passed, 2 skipped
    • 2 项仅因本机未准备 SmartTurn/Silero 可选真实资产;CAM++ 真实模型测试已执行通过。
  • Ruff、compileall、async-blocking、startup lazy import、9 个 workflow YAML、git diff --check:通过。
  • GitNexus:MEDIUM,20 个预期文件,5 条既有 Independent ASR start/close 执行流;未触及 Provider worker/registry、transport、SmartTurn 算法、Omni、UI、数据库。
  • Windows/Linux build-contract 通过;远端双平台正式构建及离线安装产物 smoke 等待 stacked PR 最终 CI。

边界

这只能证明:正式 Independent ASR 运行时可以显式接收内存声纹,使用真实 CAM++ 对候选音频评分,并通过无执行权 observer 输出 similarity。

这仍不是面向用户的声纹安全功能,不包含产品 enrollment、持久化、生产阈值、hard block、Provider/transcript 拦截、Omni、回放/克隆防御或不同声音状态测试。

#2450 继续保持 Draft;#2398 / #2408 合并并完成最终重叠与双平台 CI 前不转 Ready。

@MomiJiSan
MomiJiSan force-pushed the codex/voice-identity-foundation-20260723 branch from 06e74f2 to 3835e68 Compare July 23, 2026 07:16

Copy link
Copy Markdown
Contributor Author

补充:当前基础声纹链路的运行语义(head 4a9cd476b

本阶段暂不解决“声纹从哪里录入”。运行时只接收调用方显式提供的、已经存在于内存中的 CampPlusSpeakerProfile,以便先把模型以下的基础结构和生命周期做正确。

当前正式基础链路为:

内存 profile → Core 私有组合入口 → IndependentAsrRuntime → Detector candidate → CAM++ verifier → 无执行权 observation

本次进一步收口了 ASR route rebuild:

  • 旧 Detector 先解除 verifier,并把清理所有权交给 Independent ASR runtime。
  • 旧 verifier/backend 完全关闭前,新 Detector 保持声纹关闭;Provider PCM、ASR lifecycle 和 endpoint authority 继续工作。
  • 清理完成后,才按照当前最新 factory 自动安装新 verifier。
  • 清理失败或超过有界等待时间时,不安装新 verifier,声纹保持 fail-detached;不会让失败传播到 ASR。
  • 自动化覆盖正常清理、清理异常和永久等待三种情况,证明 profile 更新与 route rebuild 期间最多只有一个活动 CAM++/ONNX session。

回归结果:聚焦矩阵 249 passed;完整 ASR/Core/VoiceTurn 648 passed, 2 skipped。两项 skip 仅为未准备的 SmartTurn/Silero 可选真实资产,CAM++ 真实模型链路已实际执行。

这条评论只确认基础链路与资源生命周期。它不引入产品 enrollment、持久化、阈值、hard block、Provider/transcript 拦截或 native Omni。#2450 继续保持 Draft。

@MomiJiSan
MomiJiSan force-pushed the codex/voice-identity-foundation-20260723 branch 2 times, most recently from cae2e80 to c07e05c Compare July 24, 2026 01:49

Copy link
Copy Markdown
Contributor Author

已完成 rebase 并解决冲突:

  • 已将 PR 分支 rebase 到最新 codex/phase4-c-speaker-shadow3948947)。
  • Head 已由 cae2e80 更新为 c07e05c,GitHub 状态为 MERGEABLE / CLEAN
  • 12 个 base 已包含的等价提交被自动跳过,PR 提交数收敛为 9;rebase 前后最终文件树完全一致,没有人工冲突取舍或代码语义变化。
  • 聚焦回归:245 passed, 4 skipped;Ruff 与 git diff --check 通过。
  • GitNexus 审计覆盖 24 个文件及 7 条 Independent ASR 生命周期流程;高风险来自 PR 既有 Core/ASR 改动,rebase 未扩大影响范围。

@MomiJiSan
MomiJiSan force-pushed the codex/voice-identity-foundation-20260723 branch from c07e05c to ed13288 Compare July 24, 2026 01:53
@MomiJiSan
MomiJiSan force-pushed the codex/voice-identity-foundation-20260723 branch from ed13288 to 85a54b3 Compare July 24, 2026 02:26

Copy link
Copy Markdown
Contributor Author

已完成本轮 rebase 并解决冲突:

  • 已按 GitHub 当前 base codex/phase4-c-speaker-shadow@3948947 完成 rebase。
  • Head:ed1328885a54b3;GitHub 当前为 MERGEABLE / CLEAN
  • Git 自动跳过 30 个 base 已包含的等价提交,成功重放 11 个独有提交;rebase 前后最终文件树完全一致,没有人工冲突取舍或语义改动。
  • 扩展聚焦回归:479 passed, 4 skipped;Ruff 与 git diff --check 通过。
  • GitNexus:HIGH,覆盖 31 个文件、505 个符号及 7 条 Independent ASR/Verifier 生命周期流程;高风险来自 PR 既有 Core/ASR 改动,本轮 rebase 未扩大影响范围。

本轮 MomiJiSan 冲突 PR 已按 #2398#2408#2450 顺序处理完成。

@MingTianSang

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85a54b3668

ℹ️ 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".

Comment on lines +290 to +293
item_id = event.get("item_id")
if isinstance(item_id, str):
item_keys.pop(item_id, None)
await _emit_error(

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 Ignore stale OpenAI failure events

When OpenAI sends a conversation.item.input_audio_transcription.failed for an item that was already cleared or belongs to an old route epoch, item_keys.pop(item_id, None) returns None, but the worker still emits ASR_OPENAI_TRANSCRIPTION_FAILED and exits. This differs from completed/delta handling, where unknown item IDs are ignored, so a late failure after clear/barge-in can unnecessarily kill an otherwise healthy ASR worker.

Useful? React with 👍 / 👎.

Comment on lines +687 to +688
if runtime is not None:
self._runtimes.add(runtime)

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 requiring weak-refable verifier runtimes

For a custom SpeakerVerifierRuntime implemented with __slots__ or as an unhashable dataclass, this WeakSet.add() raises TypeError even though the object satisfies the verifier protocol, causing profile activation/reconciliation to fail as soon as the runtime is created. Since the contract does not require weak-reference support, the session should not impose it implicitly.

Useful? React with 👍 / 👎.

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