Skip to content

Provider 系统

Provider 系统是 DeLive 对 ASR 后端的抽象层,将六种不同的语音识别服务统一在通用契约之后。

架构

ProviderRegistry(单例)
  ├── register(entry: { info, create })
  ├── getInfo(vendorId) → ASRProviderInfo
  └── createProvider(vendorId) → ASRProvider

ASRProvider(接口)
  ├── connect(config) → Promise<void>
  ├── disconnect() → Promise<void>
  ├── sendAudio(data: Blob | ArrayBuffer) → void
  └── events: onTokens, onPartial, onFinal, onError, onFinished

BaseASRProvider(抽象类)
  ├── 状态机: idle → connecting → connected → disconnecting
  ├── 事件发射器(Map of callbacks)
  └── 辅助方法: emitToken, emitPartial, emitFinal 等

WindowedBatchTranscriptionProvider<TChunk>(继承 BaseASRProvider)
  ├── RollingAudioBuffer(最长 45s 滑动窗口)
  ├── 调度: 定时或防抖
  ├── TranscriptStabilizer(提交稳定文本前缀)
  └── 抽象方法: transcribeWindow(), resolveAudioChunk()

Provider 注册

ProviderRegistry 是将 vendor ID 映射到工厂函数的单例。六个 Provider 在模块加载时通过 registerDefaultProviders() 注册。

ASRProviderInfo

每个 Provider 通过 ASRProviderInfo 声明其能力:

  • audioInputMode: 'media-recorder''pcm16'
  • audioProfile: 采样率、声道数、payload 格式、块时序
  • transport: 执行模式、捕获重启策略
  • 功能标志: supportsTranslationsupportsSpeakerDiarizationprefersTokenEvents

事件系统

事件Payload说明
onTokensTranscriptToken[]Token 级更新(Soniox)
onPartialstring部分/进行中文本
onFinalstring最终已提交文本
onErrorASRError带代码和消息的错误
onStateChangeASRProviderStateProvider 状态变化
onFinishedProvider 会话正常完成

prefersTokenEventstrue(Soniox)时,ProviderSessionManager 注册 onPartialonFinal 监听器 — 而是从 onTokens 派生文本。

窗口批处理转录

四个 Provider(Groq、硅基流动、本地 OpenAI、whisper.cpp)继承 WindowedBatchTranscriptionProvider

滚动音频缓冲区

RollingAudioBuffer<TChunk> 存储带时间戳的音频块,最大持续时间 45 秒。超出限制时裁剪最旧的块。

调度

  • 定时模式(Groq、硅基流动、whisper.cpp):每 1500ms setInterval 触发重新转录
  • 防抖模式(本地 OpenAI):每个新音频块重置 1200ms setTimeout(与 transcribeIntervalMs 同值)

稳定化

TranscriptStabilizer 比较连续转录结果:

  1. 找到新旧转录之间的最长公共前缀
  2. 将稳定前缀作为最终文本提交
  3. 将剩余部分作为部分文本发出

防止 UI 中相同音频窗口产生略有不同转录时的文本闪烁。

文本重叠处理

滚动缓冲区前进时,buildWindowedTranscriptSnapshot 剥离已提交文本尾部和新窗口转录之间最多 200 字符 的重叠,避免重复内容。

音频管线

CaptureManager 根据 capabilities.audioInputMode 选择音频路径:

MediaRecorder 路径

Soniox 和本地 OpenAI 兼容使用。

  1. getDisplayMedia({ audio: true })MediaStream
  2. MediaRecorder 使用 audio/webm;codecs=opus(或回退)
  3. ondataavailable 每 100ms → Blob
  4. 块转发到 provider.sendAudio(blob)

PCM16 路径

火山引擎、Groq、硅基流动和 whisper.cpp 使用。

  1. getDisplayMedia({ audio: true })MediaStream
  2. AudioProcessor 使用 AudioWorklet(首选)或 ScriptProcessorNode(回退)
  3. 重采样到 16 kHz 单声道
  4. Float32 → Int16 转换 → ArrayBuffer
  5. 块转发到 provider.sendAudio(buffer)

Released under the Apache 2.0 License.