Skip to content

fix: Harden browser audio and session cleanup#15

Open
lanyue-llk wants to merge 3 commits into
lex-mainfrom
codex/browser-aec-baseline
Open

fix: Harden browser audio and session cleanup#15
lanyue-llk wants to merge 3 commits into
lex-mainfrom
codex/browser-aec-baseline

Conversation

@lanyue-llk

@lanyue-llk lanyue-llk commented Jul 10, 2026

Copy link
Copy Markdown

Goal

Harden the Browser audio baseline and session lifecycle so audio processing is observable and repeated cleanup cannot surface a false runtime failure.

Design

  • Keep the existing Browser capture constraints for echo cancellation, noise suppression, and automatic gain control.
  • Record requested constraints and effective MediaStreamTrack.getSettings() values after capture.
  • Stop session startup when AEC is supported but not effective instead of silently continuing.
  • Log the active output participant, track, and audio element count to detect duplicate playback paths.
  • Document that integrated runs receive one environment from the LexVoice backend root .env; frontend .env.local is standalone-only.
  • Treat only LiveKit 404/not_found room deletion as an already-completed stop. Authentication, network, and other LiveKit failures remain fatal.

Validation

  • pnpm test

    ✅ passed: 122 tests from clean commit ee1836b

  • pnpm exec tsc --noEmit

    ✅ passed: TypeScript

  • pnpm lint

    ✅ passed: ESLint

  • pnpm exec prettier --check app/api/session/stop/route.ts lib/session-stop.ts tests/session-stop.test.mjs .env.example

    ✅ passed: Prettier

  • Repeated /api/session/stop against an already absent room

    ✅ passed: HTTP 200, status=stopped, LiveKit room result marked skipped

Risk

  • Audio capture, playback, routing, and AEC behavior are unchanged when the browser accepts the requested constraints.
  • Browsers that report AEC support but return an ineffective setting fail session startup with a diagnostic error.
  • Repeated stop requests no longer fail solely because the LiveKit room has already gone away; all non-404/not_found cleanup errors still return failure.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 2m 53s —— View job


任务清单

  • 获取 PR diff (origin/lex-main...HEAD)
  • 阅读改动文件 (filtered-audio-renderer.tsx / useBrowserSourceClient.ts / browser-audio-capture.ts / 测试)
  • 输出审查结论

Review 结论

有条件通过

问题及建议

  • [P1] lib/browser-audio-capture.ts:58-66hooks/useBrowserSourceClient.ts:544assertBrowserEchoCancellationActive 失败时直接 throw 终止会话,但没有接入项目已有的 recordFrontendObservability / FRONTEND_EVENTS(对比同文件 BROWSER_AUDIO_TRACK_PUBLISHEDBROWSER_AUDIO_VAD_PROBE_UNAVAILABLE 等均有埋点),只写了 console.warn/console.info。这是 PR 自述的"有意的显式失败策略",但线上无法统计触发率/影响面。建议新增一个 FRONTEND_EVENTS.BROWSER_AUDIO_AEC_INEFFECTIVE 之类事件,在 throw 前上报诊断数据。
  • [P1] hooks/useBrowserSourceClient.ts:117-202ensureAudioPublished):fail-fast 依赖 getSettings().echoCancellationcreateLocalAudioTrack resolve 后立刻准确反映真实生效状态。部分浏览器/驱动(如设备切换瞬间、部分 Safari 版本)该值可能短暂不可靠,一旦命中就直接阻断会话启动、无重试或降级路径。影响范围目前仅限 browserSourceClient.enabled 场景(未影响默认 setMicrophoneEnabled 路径),风险可控,但建议先以观测埋点方式收集一段时间数据评估误报率,或加开关灰度后再做硬阻断。
  • [P2] components/livekit/filtered-audio-renderer.tsx:403-411:每次 playing 事件都会 Array.from(audioElements.values()) 遍历全部音频元素计算 activeAudioElementCount,元素较多或事件触发频繁时有轻微开销,可接受但可考虑仅在需要 warn 时计算。
  • [P2] 新增的 Error('Browser echo cancellation was requested but is not active...') 最终经 useRoom.tsrecoverFromStartError 处理,用户侧大概率只看到通用失败提示,无法区分是 AEC 未生效还是其他连接问题。建议保留原始 error 信息以便日志/支持排查(配合上面第一条埋点一起解决)。

残余风险:该 PR 未修改现有音频转发路由/AEC participant,改动范围符合描述;测试覆盖了约束、诊断、fail-fast 及播放计数场景,逻辑本身(诊断先于 publishTrack、失败时 audioTrack.stop() 并清理 runtime)是正确的。核心保留意见是新增的强制失败路径缺少可观测性埋点,建议合并前补上。

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 1m 32s —— View job


任务清单

  • 获取 PR diff (origin/lex-main...HEAD)
  • 阅读改动文件 (filtered-audio-renderer.tsx / useBrowserSourceClient.ts / browser-audio-capture.ts / 测试 / .env.example)
  • 输出审查结论

Review 结论

有条件通过

本次 sync 新增的 7e3af05 只改了 .env.example 中的两行注释(提醒不要在集成模式下创建 .env、修正 sandbox 参考文件名),纯文档变更,无功能风险。核心代码自上次审查(30abf83)以来未变化,以下问题仍未解决:

问题及建议

  • [P1] hooks/useBrowserSourceClient.ts:551-558assertBrowserEchoCancellationActive 触发前):AEC fail-fast 路径只有 console.info/console.warn,未调用 recordFrontendObservability。对比同函数内 BROWSER_AUDIO_TRACK_PUBLISHEDBROWSER_AUDIO_VAD_PROBE_UNAVAILABLE 均有埋点,lib/observability.tsFRONTEND_EVENTS 也具备扩展能力。建议新增 FRONTEND_EVENTS.BROWSER_AUDIO_AEC_INEFFECTIVE,在 throw 前上报,否则线上无法统计该硬阻断的触发率。
  • [P1] hooks/useBrowserSourceClient.ts:117-142ensureAudioPublished):getSettings().echoCancellationcreateLocalAudioTrack resolve 后立即读取即视为最终结果,无重试/宽限期。部分浏览器(设备切换瞬间、部分 Safari 版本)该值可能短暂不准,命中即直接阻断会话启动,且无灰度开关。建议先以纯观测方式上线一段时间评估误报率,再决定是否保留硬阻断。
  • [P2] components/livekit/filtered-audio-renderer.tsx:403-411:每次 playingArray.from(audioElements.values()) 遍历全部元素算 activeAudioElementCount,仅用于日志分级。元素多或触发频繁时有轻微开销,可接受,但可考虑仅在需要 warn 时再计算。
  • [P2] lib/browser-audio-capture.ts:58-66Error('...is not active on the microphone track.') 最终经 useRoom.tsrecoverFromStartError 处理,用户侧大概率只看到通用失败提示。建议保留原始 error(或搭配上面第一条埋点)便于排查是否为 AEC 未生效导致。

残余风险:该 PR 未修改现有音频转发路由 / AEC participant,改动范围符合描述;测试(tests/browser-audio-capture.test.mjs)覆盖了约束、诊断、fail-fast、浏览器不支持 AEC 和播放计数场景,逻辑本身(诊断先于 publishTrack,失败时 audioTrack.stop() 并清理 runtime)正确。核心保留意见仍是新增的强制失败路径缺少可观测性埋点,建议合并前补上。

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 3m 31s —— View job


任务清单

  • 获取 PR diff (origin/lex-main...HEAD)
  • 阅读改动文件(本次 sync 新增 ee1836bapp/api/session/stop/route.ts / lib/session-stop.ts / tests/session-stop.test.mjs
  • 输出审查结论

Review 结论

有条件通过

问题及建议

  • [P1] hooks/useBrowserSourceClient.ts:549-550lib/browser-audio-capture.ts:58-66:前两轮审查提出的核心问题仍未处理——AEC fail-fast 只有 console.info/console.warn,没有接入本文件内其余场景都在用的 recordFrontendObservability(对比 147 行 BROWSER_AUDIO_TRACK_PUBLISHED)。建议新增 FRONTEND_EVENTS.BROWSER_AUDIO_AEC_INEFFECTIVE 并在 throw 前上报,否则线上无法统计该硬阻断的触发率。
  • [P1] hooks/useBrowserSourceClient.ts:117-137ensureAudioPublished):仍是 createLocalAudioTrack resolve 后立即读取 getSettings().echoCancellation 即视为最终结果,无重试/宽限期,也无灰度开关。部分浏览器(设备切换瞬间等)该值可能短暂不准,命中即直接阻断会话启动。建议先以纯观测方式上线评估误报率。
  • [P2] lib/session-stop.ts:36-42(新增 isLiveKitRoomNotFoundError)与 app/api/session/stop/route.ts:249-271:此次 sync 新增的会话停止幂等性修复与 PR 标题描述的 AEC 改动无关,属于顺带夹带的改动,建议拆到独立 PR,便于回滚和定位问题。
  • [P2] lib/session-stop.ts:36-42:判定条件硬编码依赖 livekit-server-sdk2.13.3)抛出的 error 携带 status===404 && code==='not_found'。仓库内其余 deleteRoom 调用(如 app/api/session/dispatch/route.tsdeleteLiveKitRoomQuietly)都是裸 catch {} 忽略,没有先例验证过该 SDK 版本抛出 error 的真实字段结构;新增测试 tests/session-stop.test.mjs 也只用手写 mock 对象验证逻辑分支,未覆盖真实 SDK 抛出的 error 实例。若字段名/值与实际不符,该修复会静默失效、退回旧的失败路径(不算回归,但达不到"幂等停止"的目的)。建议合并前用真实 SDK 触发一次 404 手动验证,或补充基于 SDK 导出的错误类型的类型级校验。
  • [P2] components/livekit/filtered-audio-renderer.tsx:403-411:仍是每次 playing 事件遍历全部 audioElements 计算 activeAudioElementCount,可考虑仅在需要 warn 时再计算。

残余风险:AEC 相关核心逻辑自 30abf83 起未变化,测试覆盖(约束、诊断、fail-fast、播放计数)完整;本次新增的 session-stop 幂等修复未触碰音频转发路径,风险面独立且可控。

@lanyue-llk lanyue-llk changed the title fix: Verify browser echo cancellation fix: Harden browser audio and session cleanup Jul 20, 2026
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