Skip to content

Phase 4B:接入 RNNoise/Silero 节流并拆分 ASR 结束权 - #2398

Draft
MomiJiSan wants to merge 16 commits into
Project-N-E-K-O:codex/phase4-a-voice-settingsfrom
MomiJiSan:codex/phase4-b-throttling
Draft

Phase 4B:接入 RNNoise/Silero 节流并拆分 ASR 结束权#2398
MomiJiSan wants to merge 16 commits into
Project-N-E-K-O:codex/phase4-a-voice-settingsfrom
MomiJiSan:codex/phase4-b-throttling

Conversation

@MomiJiSan

@MomiJiSan MomiJiSan commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

改动概述

本 PR 已 rebase 到 #2396 2d6a6bec3,是其上的 Phase 4B 数据面:提供与 Provider 无关的 RNNoise/Silero 活动证据、资源节流和语音生命周期,并明确拆分两条结束判断路径。

  • 流式 ASR(Qwen、Soniox、Step、Grok):RNNoise/Silero 只负责空闲跳帧、预热、暂停和 transport 生命周期;logical final 由供应商原生 endpoint/final 发布,完全不加载、不 pin、也不评估 SmartTurn。Soniox transport 建联失败只重建 Soniox candidate,不切换 core-follow Provider。
  • 分段式 ASR(GLM、Gemini):RNNoise/Silero 负责同样的资源节流;SmartTurn 动态加载/卸载并以 COMPLETE 判断逻辑轮结束。
  • 默认维持“RNNoise 预热 + Silero confirm/pause”,不关闭 Silero,也不把 RNNoise-only 设为生产默认。
  • 修复 SmartTurn COMPLETE 与下一轮 onset 的竞态:以 completion fence 固定旧 candidate 和已消费 PCM 边界,先轮换语义身份,再发布旧轮完成;推理期间已消费的 successor PCM 会在新轮重放。WARM_IDLE 下则坚持“非 ACTIVE 不上云”,continuous wake 生效前只缓冲 PCM。

关键技术决策

  • 节流层不接收 Provider key,不选择 Provider,也不拥有 endpoint/final 权限。
  • RNNoise 输出按音频块聚合的 peak/mean/last/EMA;Silero 提供确认和 pause 事件。优化关闭时连续上传,但 endpoint authority 不变。
  • 流式 Provider 直接使用 Provider endpoint readiness;只有分段 Provider 才加载 SmartTurn。
  • PREWARMING 的模型加载和 transport 连接不占用 PCM 入口锁;异步结果必须通过 detector epoch、session epoch、audio generation 和对象身份校验。
  • PREWARMING 未确认时使用 session config 的 idle_transport_close_ms;Provider final 后的 WARM_IDLE 使用 provider_policy.warm_transport_ms。Soniox 因而保持默认 25 秒 WSS 保温,不新增 Provider 特判;旧 epoch 或旧 lifecycle 的计时器不能关闭 successor。
  • Detector completion 通过统一入口消费 candidate binding:正常与 deferred 顺序都会在发布时移除 _bound_turns;重复、旧 epoch 和 callback 异常不会恢复或保留旧 binding,也不新增随 turn 增长的 completed set。
  • Soniox 在持续模式启动和 connect_transport() 两个入口均使用 3 次总尝试与 250/500 ms 退避;每次只用原始 Soniox selection 创建新 candidate,失败 candidate 必须关闭。重试不改写 _asr_transport_selection_asr_providerlifecycle.provider_policy;耗尽后进入 BLOCKED,通过统一终止入口只发送一次 ASR_INDEPENDENT_PROVIDER_UNAVAILABLE
  • final 后统一调用 release_deferred_turn():解除旧轮屏障,但不清空可能已经到达的下一轮音频。SmartTurn COMPLETE 通过 SmartTurnCompletionFence 固定旧 candidate;fail-open 仅触发通用 continuous wake,不能越过 lifecycle 直接发送 PCM。
  • shadow 指标只记录低基数决策,不记录 PCM、概率序列或可恢复音频特征。

“关闭 Silero”消融结论

该实验只回答“分段式 ASR 能否移除 Silero”,不用于决定流式 ASR 的 endpoint。

方案 关键结果 决策
RNNoise 单帧 252/307;67 个无人声片段中 55 个误启动 淘汰
RNNoise 连续确认 100ms 302/307;235/240 语音检出,67/67 无人声未误启动 继续做 onset/prewarm shadow,不负责结束
RNNoise + Silero + SmartTurn 约 66.3ms CPU、约 51MB;SmartTurn 约 15.1 次/分钟;无提前结束 当前分段式可靠基线
RNNoise + 每 500ms SmartTurn、无 Silero 仅节省约 9.8MB,但 CPU +11.6%、SmartTurn 调用近翻倍;167/240 提前结束、63/240 未完成 淘汰

因此当前不关闭 Silero。节省的内存不足以抵消 CPU、调用压力和结束可靠性回归。

RNNoise / Silero 评估与发布门槛

仓库语料按原始来源拆分 calibration/holdout;同一 TTS 的 clean 与不同 SNR 变体不跨集合:

  • calibration:230 片段、80 个原始来源;
  • holdout:77 片段、27 个原始来源;
  • 原始来源交集:0;
  • calibration 选择 RNNoise 连续 100ms、阈值 0.7;
  • holdout:60/60 语音召回、17/17 负样本拒绝,onset median/p95 为 130/233ms;fan/game_sfx/impulse/pink/silence/white 均进入留出集且无误触发。

这些数据仍来自仓库 TTS 与合成噪声。评测工具已支持匿名 --real-device-manifest,报告不写入录音路径或 PCM;当前没有带标签真实麦克风语料,所以明确记录 real_device_metrics.available=false。在多设备、长时间 false-prewarm、onset P95 和首字丢失率达到门槛前,不切换 RNNoise-only。

不拆分理由 / Why Not Split

RNNoise/Silero 决策、SmartTurn 动态生命周期、transport generation 屏障和 pre-roll/首字保护共同构成同一条数据面状态机。若拆成独立 PR,中间状态可能出现“已节流但 endpoint 未就绪”“旧 transport 接受新音频”或“final 清空下一轮首帧”等不可安全发布组合。

评测工具与报告保留在本 PR 中,用来约束默认策略仍为 RNNoise + Silero,并防止代码先切换到尚未通过真实设备门槛的 RNNoise-only。声纹属于独立身份 gate,不进入本 PR。

回归报告 / Regression Report

  • 改动:统一 RNNoise/Silero 活动证据与节流;拆分流式/分段式 endpoint;增加 transport/audio-generation 屏障;加入 SmartTurn completion fence 与 successor PCM 重放;按状态所有权拆分 PREWARMING 与 WARM_IDLE TTL;在 completion 发布时消费 Detector binding;补回流式 Provider 的 transport-only prewarm 传播,并以有界 Provider candidate fence 收敛本地 candidate completion;同步 Phase 3:接入生产独立 ASR、GLM/Gemini 与动态 Smart Turn #2345 的 Soniox Provider 不变契约到新的 transport 状态机;在 DRAINING successor overflow 时只撤销 successor 本地检测状态,保留旧 Provider final fence,并把 final acceptance 移到 fence 成功消费之后。
  • 理由 / 必要性:旧语义会让流式 Provider 也尝试 pin SmartTurn,旧 final reset 可能清除下一轮首字;流式 feed() 曾丢失 RNNoise PREWARM 决策,Provider final 也没有完成本地 candidate;统一 5 秒计时又会过早关闭 Provider warm transport,完成后的 binding 则会在长会话中按 turn 累积;DRAINING overflow 过去会完整 reset Detector、销毁旧 Provider fence,使旧 final 被标记 accepted 后静默丢弃并最终触发 watchdog。
  • 前后表现:流式路径完全不启动 SmartTurn;RNNoise 高置信 onset 可提前建立原 Provider transport,但 Silero 确认前 Provider wire PCM 为零。Provider final 后,无 successor 时恢复 idle throttle;有 successor 时保留其活动和有界 pre-roll。PREWARMING 继续 5 秒回收,Soniox WARM_IDLE 保持 25 秒保温。正常与 deferred completion 都会释放旧 binding,100 轮会话容量保持稳定。Soniox retry candidate 均来自同一 selection,耗尽后只发送一次专属 fail-closed 状态。DRAINING overflow 现在只推进一个有界 discard watermark;旧 final 正常发布一次,overflow 后的新 successor 仍可保留,fence 异常则一次 ASR_ENDPOINTING_FAILED fail-closed。
  • 验证结果:完整 ASR 组合回归 434 passedruntime.pydetector_runtime.py 合计覆盖率 82.49%;VoiceTurn 回归 65 passed / 2 skipped。新增覆盖 Soniox/Qwen DRAINING overflow、旧 final 单次交付、watchdog 不触发、overflow 后 successor 保留、重复 discard、stale fence、Silero reset 异常、两种 final/overflow 锁竞争、session/lifecycle/detector identity 替换与 100 轮有界性。Ruff、git diff --check 通过;GitNexus detect_changes 为 LOW / 0 affected flows
  • 潜在回归点:持续上传的网络占用、PREWARMING/WARM_IDLE 计时器替换、旧 lifecycle/transport 重连、DRAINING 期间新轮到达、deferred completion、callback 异常、DRAINING overflow 与 Provider final 的锁竞争、accepted transcript 在 successor abort 前的交付顺序,以及真实设备标签质量。

风险与边界

本 PR 不实现声纹,不修改小游戏,不允许 RNNoise/Silero 发布 logical final,也不回退 Omni 或其他 ASR Provider 做独立 ASR 的故障兜底;不修改 workers/soniox.py 的已连接后 recovery/replay、Provider <end>、partial/final 或错误分类。TTL 修复不改变 Provider selection、重试和 endpoint;binding 修复不改变 SmartTurn 决策、candidate generation 或 PCM replay。Transport-only prewarm 只拥有资源预热权;Provider candidate fence 只封存和完成本地资源边界,两者均不取得 Provider endpoint/final 权限。用户明确关闭独立 ASR 时仍保留 Core/Omni 原生语音路径。

Phase 4 收敛为 3 个 PR:4A(#2396,控制面)、4B(本 PR,节流和生命周期数据面)、4C(#2408,声纹 shadow/可选 gate)。小游戏迁移待接口稳定后单独实施。

@MomiJiSan
MomiJiSan requested a review from wehos as a code owner July 18, 2026 16:37
@coderabbitai

coderabbitai Bot commented Jul 18, 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: 6db5b443-8bba-4e6f-b748-ce5d43882f79

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.

@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds provider-neutral local voice throttling and lifecycle guards. The main changes are:

  • Adds RNNoise chunk evidence and throttle decisions.
  • Adds detector prewarm and continuous-upload events.
  • Adds SmartTurn pinning and transport generation barriers.
  • Extends independent-ASR lifecycle and audio routing tests.

Confidence Score: 5/5

This looks mergeable after addressing the turn-boundary onset-audio loss.

  • A SmartTurn completion can clear local activity before the next candidate is admitted.
  • Generation checks and lifecycle cleanup otherwise protect stale transport and PCM paths.

main_logic/asr_client/detector_runtime.py

Important Files Changed

Filename Overview
main_logic/asr_client/throttle_policy.py Adds policy decisions, baseline tracking, and shadow metrics for local voice throttling.
main_logic/asr_client/detector_runtime.py Connects throttle decisions to detector candidates, prewarm events, and SmartTurn session pinning.
main_logic/core/asr_runtime.py Adds independent-ASR prewarm handling, lifecycle cleanup, and transport/audio generation checks.
main_logic/core/streaming.py Carries ingress identity through hot-swap audio caching and validates frames before replay.
main_logic/asr_client/audio_pipeline.py Builds per-chunk RNNoise evidence while preserving existing normalized PCM output.
utils/audio_processor.py Records RNNoise peak, mean, last-frame, and EMA statistics for each processed chunk.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Mic as Microphone
    participant Pipe as Audio Pipeline
    participant Det as Detector Runtime
    participant Core as ASR Runtime
    participant Tx as Provider Transport
    Mic->>Pipe: PCM block
    Pipe->>Det: RNNoise evidence + PCM
    Det->>Core: prewarm or continuous event
    Core->>Det: prepare/pin SmartTurn
    Core->>Tx: connect transport
    Det->>Core: speech activity / completion
    Core->>Tx: send validated turn PCM
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Mic as Microphone
    participant Pipe as Audio Pipeline
    participant Det as Detector Runtime
    participant Core as ASR Runtime
    participant Tx as Provider Transport
    Mic->>Pipe: PCM block
    Pipe->>Det: RNNoise evidence + PCM
    Det->>Core: prewarm or continuous event
    Core->>Det: prepare/pin SmartTurn
    Core->>Tx: connect transport
    Det->>Core: speech activity / completion
    Core->>Tx: send validated turn PCM
Loading

Reviews (1): Last reviewed commit: "feat(asr): add provider-neutral voice re..." | Re-trigger Greptile

Comment thread main_logic/asr_client/detector_runtime.py Outdated

@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: 7a8c638153

ℹ️ 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 main_logic/asr_client/detector_runtime.py
@MomiJiSan
MomiJiSan marked this pull request as draft July 18, 2026 16:57

MomiJiSan commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

已补充并复核 RNNoise / Silero 语音存在检测预备报告 和可复现评测工具。

二次审计修正了三个口径:RNNoise 持续条件现在直接捕获真实 10ms frame;Silero 使用生产 gate(0.5 onset、<0.35 reset、至少7个窗口);性能同时记录 CPU/wall,并拆分共享 ONNX Runtime 与 Silero session RSS。

核心结果(307 个固定片段、767.5 秒):

  • Phase 4B:接入 RNNoise/Silero 节流并拆分 ASR 结束权 #2398 RNNoise 自适应策略(fresh session):speech recall 100%,负样本误触发 82.09%,balanced accuracy 58.96%,不能单独作为当前默认节流策略。
  • RNNoise 严格连续 100ms、阈值 0.7(探索点):recall 97.92%,负样本误触发 0%,balanced accuracy 98.96%,onset median/p95 为 110/346ms。
  • Silero raw 生产 gate:recall 95.83%,负样本误触发 0%;Silero after RNNoise:recall 96.67%,误触发 0%。
  • 完整 RNNoise 桌面管线约 43.7ms CPU/每秒音频、RSS +0.45MiB;Silero约3.8ms CPU/每秒音频,session warm RSS +21.6MiB;首次共享 ONNX Runtime另约 +10.5MiB。

确认后的结论:

  1. RNNoise-only 值得继续做 LOCAL_LISTEN shadow,但现在不能设为生产默认:探索阈值与统计来自同一语料,ru组召回仅89.58%,且缺少真实房间、电视/回声、远场和独立holdout。
  2. Phase 4B:接入 RNNoise/Silero 节流并拆分 ASR 结束权 #2398_process_without_vad() 已有每500ms请求SmartTurn的fallback,所以“没有Silero就技术上无法endpoint”不成立;全程不加载Silero是可实验方案。
  3. 仍需单独测 periodic SmartTurn 的CPU/队列压力、长语音误判、evaluation合并率和完成延迟。该报告通过前保留Silero生产路径。
  4. 边界不变:RNNoise/Silero只提供资源证据,SmartTurn COMPLETE仍是唯一逻辑endpoint;SmartTurn未READY时Provider wire audio=0;PCM不进入Core/Omni/consumer,不增加Omni fallback。

MomiJiSan commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

关于“关闭 Silero”实验图的口径与最终决策(已按 3 个 PR 收敛)

图片中的表格不是独立 ASR 整体准确率评测,而是分段式 ASR 关闭 Silero 的消融实验,分别验证 RNNoise 能否负责 onset,以及移除 Silero 后能否靠每 500ms 轮询 SmartTurn 判断结束。

方案 关键结果 决策
RNNoise 单帧 252/307;67 个无人声片段中 55 个误启动 淘汰
RNNoise 连续确认 100ms 302/307;235/240 语音检出,67/67 无人声未误启动 继续做 onset/prewarm shadow,不负责结束
RNNoise + Silero + SmartTurn 约 66.3ms CPU、约 51MB;SmartTurn 约 15.1 次/分钟;无提前结束 当前分段式可靠基线
RNNoise + 每 500ms SmartTurn、无 Silero 仅节省约 9.8MB,但 CPU +11.6%、SmartTurn 调用近翻倍;167/240 提前结束、63/240 未完成 淘汰

架构与决策

  • 流式 ASR:RNNoise/Silero 只做节流,Provider 原生 endpoint/final 负责结束,完全不加载 SmartTurn;图片中的 SmartTurn 数据不适用于该路径。
  • 分段式 ASR:维持 RNNoise 预热 + Silero confirm/pause + SmartTurn COMPLETE。
  • 当前不关闭 Silero,也不切换 RNNoise-only;RNNoise 连续 100ms 继续作为 onset/prewarm shadow,等待多设备真实麦克风、长期 false-prewarm、onset P95 和首字丢失率验收。
  • 声纹只回答“是不是目标用户”,不能替代语音存在检测或结束判断。

Phase 4 收敛方案

Phase 4 总计 3 个 PR:

  1. Phase 4A — Phase 4A:建立受控 ASR consumer routing 与语音设置 #2396:控制面——独立 ASR 默认值、资源优化开关和设置 UI;
  2. Phase 4B — Phase 4B:接入 RNNoise/Silero 节流并拆分 ASR 结束权 #2398:数据面——RNNoise/Silero 节流、流式/分段式 endpoint 分流、SmartTurn 动态生命周期、首字保护,以及支撑默认策略的 holdout/真实设备评测入口;
  3. Phase 4C — 后续新 PR:声纹 shadow/可选 gate、录入与隐私边界、误拒绝和多人/电视声评测。

不再为 RNNoise 真实设备验证单开 Phase 4 PR;评测与策略边界保持在 #2398。小游戏迁移不计入 Phase 4,待 ASR 接口稳定后单独实施。

@MomiJiSan MomiJiSan changed the title 新增与供应商无关的语音资源节流 Phase 4B:接入 RNNoise/Silero 节流并拆分 ASR 结束权 Jul 20, 2026
@MomiJiSan
MomiJiSan force-pushed the codex/phase4-b-throttling branch from 5d382a2 to abb7b18 Compare July 20, 2026 08:12
@MomiJiSan
MomiJiSan force-pushed the codex/phase4-a-voice-settings branch from 99b67c3 to aa13f85 Compare July 20, 2026 08:15
@MomiJiSan

Copy link
Copy Markdown
Contributor Author

两项跨轮竞态已收敛(当前 head c324e53f

针对 review 提出的两个阻塞问题,本 PR 已在既定边界内完成修复:

  1. SmartTurn 完成后的下一轮 onset 丢失

    • 通过 completion fence / candidate generation 隔离旧轮完成结果;
    • SmartTurn 接受旧轮 COMPLETE 时先在串行控制通道轮换状态,再异步通知 Core;
    • 旧轮 completion 不再清除 fence 之后已经到达的 successor candidate、Silero activity 或 pre-roll;重复/过期 completion 保持幂等丢弃。
  2. 关闭资源优化后的 WARM_IDLE 唤醒竞态

    • 生命周期现在只有 ACTIVE 可以向 Provider 转发 PCM;
    • LOCAL_LISTEN / WARM_IDLE 的当前块先进入 pre-roll,continuous wake 生效并进入 ACTIVE 后再按原顺序 drain;
    • fail-open 只表示检测节流不可用时仍可唤醒,不再允许绕过 lifecycle / turn identity 直接发送音频。

对应提交:

  • 82da2039fix(asr): preserve successor candidate across SmartTurn completion
  • c324e53ffix(asr): buffer continuous wake audio until lifecycle activation

边界保持不变:未修改 #2345、Provider Worker/registry、RNNoise 100ms 与 Silero 阈值、SmartTurn 模型、声纹、UI、小游戏或任何 fallback。

回归结果:ASR/VoiceInput 相关 388 tests passed,目标覆盖率 81.66%;ruff、async blocking 与变更范围检查通过。新增确定性测试覆盖旧轮 completion 与新轮首帧交错,以及 dispatcher 暂停期间 WARM_IDLE PCM 只缓冲不越界发送。

当前继续保持 Draft,等待基于最新 head 的 review/check;最终仍按既定堆叠顺序 rebase,不把这两项修复下沉到 #2345

@MomiJiSan
MomiJiSan force-pushed the codex/phase4-a-voice-settings branch from aa13f85 to fa31299 Compare July 21, 2026 13:00
@MomiJiSan
MomiJiSan force-pushed the codex/phase4-b-throttling branch from c324e53 to 4240984 Compare July 21, 2026 13:00

Copy link
Copy Markdown
Contributor Author

本次已把 Phase 4B 对旧碎片模块的修改迁入 #2345 确立的 owner:音频证据并入 audio.py,detector 契约进 detector.py,生命周期进 lifecycle.py,运行时进 runtime.py / detector_runtime.py;仅保留职责独立的 throttle_policy.py。旧模块和旧 import 零引用,asr_client 不反向依赖 core。专项 169 passed,完整堆叠回归 486 passed;GitNexus LOW、0 affected processes。

Copy link
Copy Markdown
Contributor Author

已复核 #2396 的两个 P2 修复(14fe8f5a)对本 PR 的影响。

#2398 的自有提交没有修改 static/app/app-audio-capture.js,也没有重新实现弹窗 portal/disposer 或设置 Pending 生命周期,因此不需要在 4B 复制同一修复。该逻辑继续由 4A 单点拥有,4B 保持 RNNoise/Silero 节流、lifecycle 与数据面边界。

upstream/codex/phase4-a-voice-settings 已快进到 14fe8f5a,本 PR 的合并基线会包含该修复。后续做精确堆叠整理时,只需将 #2398 rebase 到该基线并重跑组合回归,不新增第二套前端实现。

@MomiJiSan MomiJiSan left a comment

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.

Soniox fail-closed 边界需要在 transport 重构中保持。

Comment thread main_logic/asr_client/runtime.py Outdated
@MomiJiSan
MomiJiSan force-pushed the codex/phase4-b-throttling branch from 4240984 to 4f48780 Compare July 22, 2026 03:01

MomiJiSan commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Soniox failure contract stacked 收敛

Soniox failure contract 已按 stacked owner 完成收敛:

回归覆盖同 Provider 重试成功、250/500 ms 退避、重试耗尽、Provider selection/持久配置不变、core-follow resolver 零调用、取消与四类过期连接、失败 candidate 迟到 callback、pending speech 恢复、Provider endpoint authority、SmartTurn 零 pin,以及用户可见 Soniox unavailable 状态。

最终验证:

#2398#2408 的 GitNexus detect_changes 均为 LOW、0 affected flows。建议仍按 #2345#2396#2398#2408 顺序合并。

@MomiJiSan
MomiJiSan force-pushed the codex/phase4-a-voice-settings branch 2 times, most recently from 0fb3c77 to 2d6a6be Compare July 22, 2026 03:31
@MomiJiSan
MomiJiSan force-pushed the codex/phase4-b-throttling branch from 4f48780 to b9e4098 Compare July 22, 2026 03:34
@MomiJiSan

Copy link
Copy Markdown
Contributor Author

已在 ece43f639 收敛连续模式的最终状态所有权。

Soniox 三次建联失败后,restart_transport() 现在通过统一 fail-closed 清理入口完成 RETRIES_EXHAUSTED → BLOCKED、session epoch/audio generation 失效、dispatcher 清理和资源关闭,并且只发送一次 ASR_INDEPENDENT_PROVIDER_UNAVAILABLE / soniox。连续启动层会先检查 session epoch 与 lifecycle identity;如果下层已经终止,就直接返回,不再追加 ASR_INDEPENDENT_FAILEDASR_INDEPENDENT_READY

新增回归覆盖关闭资源优化时三次同 selection 失败、250/500 ms 退避、三个 candidate 全部关闭、Soniox unavailable 恰好一次,以及第三次成功、endpointing 失败、非 Soniox 失败、stale epoch 和 cancellation。ASR 回归为 405 passed,runtime.py coverage 80.00%,Ruff、diff check 和 GitNexus 影响检查通过。

本提交只处理双重最终状态;未修改重试次数、Provider selection/fallback、用户配置、Soniox worker/endpoint、SmartTurn、TTL、_bound_turns、前端/i18n 或 PR Description。

@MomiJiSan

Copy link
Copy Markdown
Contributor Author

Phase 4B 的两个资源生命周期问题已分别收敛:

  • PREWARMING 继续使用 idle_transport_close_ms 的 5 秒误预热回收时间;Provider final 后的 WARM_IDLE 改为使用 provider_policy.warm_transport_ms,Soniox 保持默认 25 秒 WSS 保温。旧 session epoch 或旧 lifecycle 的计时器不能关闭 successor。
  • Detector completion 在正常与 deferred 两种顺序下都会通过统一入口消费 _bound_turns;重复、旧 epoch 和 callback 异常不会恢复或保留旧 binding,100 轮长会话容量保持稳定。

两项修改均未改变 Provider selection、Soniox retry、endpoint、SmartTurn authority、candidate generation、PCM replay 或 lifecycle 状态转换。定向与组合回归共 413 项通过;runtime.py 覆盖率 80%,detector_runtime.py 覆盖率 84%,合计 82.20%;Ruff、diff check 通过,GitNexus compare 为 LOW / 0 affected flows。

#2408 已 rebase 到本 PR 最新 head,仅执行 Speaker Shadow 组合回归,没有复制 TTL 或 binding cleanup 实现。

Copy link
Copy Markdown
Contributor Author

Phase 4B 的两个检测边界问题已完成收敛(4a8f34a50):

  • 流式 Provider 的 RNNoise 高置信 onset 现在只发布 transport-only prewarm,并复用既有 connect_transport() 提前连接原 Provider。Silero 确认前不激活 audio dispatcher、不发送 Provider wire PCM,也不会加载或 pin SmartTurn;重复高置信帧不会重复建联,未确认预热仍按 5 秒回收。
  • Provider endpoint 只 seal 本地 candidate fence,Provider final 消费旧 candidate。无 successor 时恢复 idle throttle、静音跳帧和 ambient baseline;有 successor 时保留其活动与有界 pre-roll。stale/duplicate fence 直接忽略,100 轮 completion 后 fence/binding 容量保持稳定。

两项修改均未改变 Provider selection、Soniox retry/fail-closed、fallback、Provider endpoint/final authority、SmartTurn authority 或 5/25 秒 TTL。#2408 已 rebase 到本 head(3ed1ccca3),冲突只保留 Speaker Shadow 观测 wiring,没有复制 throttle_action、transport prewarm 或 Provider candidate fence,也不再由 shadow 推进 Provider candidate generation。

验证结果:#2398464 passed / 84.47% coverage#2408 完整组合为 521 passed / 3 skipped / 84.89% coverage。两边 Ruff、git diff --check 均通过;#2408 GitNexus compare 为 LOW / 20 个预期文件 / 0 affected processes

@MomiJiSan

Copy link
Copy Markdown
Contributor Author

Phase 4B 的 DRAINING successor overflow / Provider final fence 已按状态所有权完成收敛:

  • Provider-endpoint 路径不再对 DRAINING 中的 successor overflow 调用完整 DetectorRuntime.reset();现在只重置 successor 的 Silero/throttle activity,并以单一 discard watermark 忽略已丢弃 sequence,旧 sealed turn、Provider fence、final watchdog 和原 transport 均保持不变。
  • Provider final 只有在 complete_provider_candidate() 成功消费 fence 后才写入 accepted-final;fence 缺失、过期或局部 reset 异常会退出 final lock 后一次 ASR_ENDPOINTING_FAILED fail-closed,不再静默吞掉 final。
  • final/overflow 两种锁竞争均已覆盖:final 停在 WARM_IDLE 时清空坏 pending 但保留 transport;final 已激活 successor 时先交付旧 accepted transcript,再执行原 ACTIVE backpressure abort。session epoch、lifecycle 或 detector 被替换时旧 overflow 不触碰 successor session。

本次没有修改 Soniox retry/fallback、Provider selection、endpoint/final authority、worker replay、RNNoise prewarm、5/25 秒 TTL、SmartTurn completion fence、PCM replay、buffer 阈值、前端状态码或 i18n,也没有新增 Provider 名称特判或增长型 completed set。

验证:完整 ASR 组合回归 434 passed,runtime.py + detector_runtime.py 覆盖率 82.49%;VoiceTurn 65 passed / 2 skipped;Ruff、git diff --check 通过;GitNexus detect_changes 为 LOW / 0 affected flows。提交:3a1e124de

@MomiJiSan
MomiJiSan force-pushed the codex/phase4-b-throttling branch from 3a1e124 to b35c325 Compare July 23, 2026 03:39
@MomiJiSan
MomiJiSan force-pushed the codex/phase4-a-voice-settings branch from 2d6a6be to 392e89e Compare July 23, 2026 03:49

Copy link
Copy Markdown
Contributor Author

Restack 完成回执

本 PR 已完成 restack:

重放时按新版 #2345/#2396 架构完成了边界适配:

  • MicLease、consumer identity 与 transcript routing 继续由 Core/voice_input 层负责;
  • asr_client 只保留 provider-neutral 活动证据、节流、prewarm、provider final fence 与生命周期逻辑;
  • 没有重新引入旧版任意字符串 consumer、MicLease owner 逻辑或错误回落;
  • 没有修改 TTS,也没有接入插件运行时;
  • 原 14 个提交重放为 13 个有效提交;旧的 Soniox terminal-failure 提交已被新版统一错误处理吸收,因此未制造空提交。

验证结果:

  • ASR / voice-input 回归:488 passed
  • ruff 通过;
  • Core 分层契约检查通过;
  • GitNexus 最终变更范围为预期的 ASR runtime / detector / lifecycle 路径;
  • CodeRabbit check 通过。

上游 codex/phase4-a-voice-settings stacked-base 镜像已同步到 392e89e0a,因此本 PR 与 #2396 的实际祖先关系和 GitHub base 状态一致。

@MomiJiSan
MomiJiSan force-pushed the codex/phase4-b-throttling branch from 362f999 to 4a2cbde Compare July 24, 2026 02:17

Copy link
Copy Markdown
Contributor Author

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

  • 已将分支 rebase 到 Phase 4A:建立受控 ASR consumer routing 与语音设置 #2396 当前 base 392e89e
  • Head:362f9994a2cbde;GitHub 当前为 MERGEABLE / CLEAN
  • Git 自动跳过 5 个 base 已包含的等价提交,成功重放 16 个独有提交;rebase 前后最终文件树完全一致,没有人工冲突取舍或语义改动。
  • 聚焦回归:439 passed;Ruff 与 git diff --check 通过。
  • GitNexus:MEDIUM,覆盖 26 个文件、785 个符号及 5 条 Independent ASR 启动/错误/关闭流程;rebase 未扩大既有影响范围。

下一步继续按 stacked 顺序处理 #2408

@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: 4a2cbdeac5

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

self._asr_provider or "unknown",
status_code="ASR_ENDPOINTING_FAILED",
)
return True

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 Return an AsrSubmitResult on dispatcher failure

When detector dispatcher backpressure happens while the throttle-unavailable/fail-open path is opening, this branch returns a bare bool even though submit() otherwise returns AsrSubmitResult and Core immediately reads result.status after awaiting it. That turns the handled endpointing failure into an AttributeError: 'bool' object has no attribute 'status' in main_logic/core/asr_runtime.py instead of the intended unavailable submit result.

Useful? React with 👍 / 👎.

sample_rate_hz: int
speech_probability: float | None
rnnoise_available: bool = False
rnnoise_evidence: RnnoiseEvidence | None = None

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 Propagate RNNoise evidence through Core

Although the pipeline now attaches rnnoise_evidence, the production microphone route drops it before IndependentAsrRuntime.submit(): Core forwards only speech_probability/rnnoise_available into _route_microphone_audio and then recreates ProcessedVoiceFrame without this field. As a result live 48 kHz audio falls back to synthetic one-frame evidence, so the new adaptive baseline and shadow metrics do not use the real RNNoise frame_count, mean, or ema values this change computes.

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