Skip to content

feat(asr): 支持讯飞实时语音转写(RTASR)提供商(#842) - #894

Merged
H-Chris233 merged 1 commit into
Open-Less:betafrom
H-Chris233:codex/feat/xfyun-asr
Aug 3, 2026
Merged

feat(asr): 支持讯飞实时语音转写(RTASR)提供商(#842)#894
H-Chris233 merged 1 commit into
Open-Less:betafrom
H-Chris233:codex/feat/xfyun-asr

Conversation

@H-Chris233

@H-Chris233 H-Chris233 commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

User description

概述

实现 #842:支持讯飞开放平台**实时语音转写(RTASR,标准版)**流式 ASR 提供商,听写体验与火山/百炼流式对齐。

实现要点

  • 新增 asr/xfyun.rs:RTASR 流式客户端
    • 鉴权:signa = Base64(HmacSHA1(MD5(appid + ts), apiKey)),与官方文档公式逐字节验证一致
    • 端点 wss://rtasr.xfyun.cn/v1/ws,音频 16k/16bit/mono PCM(与 recorder 输出一致,零转码)
    • 1280B/40ms 分块发送;{"end": true} 二进制收尾(pending_sends 保证末帧顺序)
    • 结果按 seg_id 去重拼接,type=0 最终 / type=1 中间(兼容数字形态);断连 partial 兜底
    • 错误分类可读:10105/10110 鉴权、10800 并发受限、超时
  • 全链路接入:dictation 主流程、QA 面板、重新转录、取消路径、设置页凭据预检与「验证」探活
  • 凭据xfyun.app_id / xfyun.api_key 存系统凭据库,不落日志;CredentialsSnapshot 不向 IPC 暴露
  • 前端:设置页 ASR 下拉新增「讯飞实时语音转写」(AppID + API Key 两字段),5 种语言 i18n
  • 文档docs/xfyun-asr.md(服务开通/凭证/音频约束/IP 白名单/排障)+ README/README.zh/USAGE 同步

决策说明

  • 首期实现 RTASR 标准版实时流式(issue 推荐优先,与 open_session → consume_pcm_chunk → send_last_frame → await_final_result 模式对齐)
  • 已知限制:标准版 RTASR 无请求级热词参数(词典在讯飞控制台配置),语种默认中文普通话——已在 UI 与文档说明

验收对照(#842

  • 设置页新增讯飞选项(AppID + API Key)
  • RTASR 流式集成,与现有流水线对齐
  • 录音 → 转写 → 润色 → 插入主流程(含 QA / 重新转录)
  • 错误信息可读
  • 开发者文档
  • Secret 不落日志
  • [~] 热词:标准版 RTASR 不支持请求级参数(符合「若对应 API 支持」条款)

验证

  • cargo check ✅;cargo test 902 通过(xfyun 模块 12/12,含官方 signa 向量)
  • tsc --noEmit + vite build
  • 2 个失败用例为既有环境问题(Windows 符号链接权限 OS 1314 / 并行时序抖动),与本次改动无关

PR Type

Enhancement, Tests, Documentation


Description

  • Add asr/xfyun.rs RTASR WebSocket client with signa auth

  • Wire into dictation, QA, re-transcription, cancel paths

  • Add AppID/APIKey credentials and settings UI with i18n

  • Document setup, audio constraints, error codes, limitations


Diagram Walkthrough

flowchart LR
  A["Recorder 16k PCM"] --> B["XfyunStreamingASR"]
  B --> C["wss://rtasr.xfyun.cn/v1/ws (signa auth)"]
  C --> D["result events"]
  D --> E["transcript"]
  B --> F["open_session / send_last_frame / await_final_result"]
  F --> E
Loading

File Walkthrough

Relevant files
Enhancement
12 files
mod.rs
Register and export xfyun ASR module                                         
+2/-0     
xfyun.rs
Add iFlytek RTASR client with auth and tests                         
+863/-0 
credentials.rs
Support xfyun AppID and APIKey credentials                             
+7/-0     
providers.rs
Add xfyun ASR provider validation flow                                     
+35/-0   
coordinator.rs
Add Xfyun active ASR variant and wiring                                   
+32/-0   
asr_wiring.rs
Wire xfyun preflight and QA startup                                           
+38/-0   
dictation.rs
Integrate xfyun into dictation session flow                           
+106/-1 
qa_session.rs
Handle xfyun in QA transcription paths                                     
+39/-0   
resources.rs
Add xfyun cancel support in resources                                       
+1/-0     
credentials.rs
Persist xfyun AppID and APIKey fields                                       
+30/-0   
ProvidersSection.tsx
Add xfyun credential fields in settings                                   
+21/-0   
shared.tsx
Register iflytek ASR provider preset                                         
+3/-0     
I18n
1 files
{en,ja,ko,zh-CN,zh-TW}.ts
Add iFlytek labels across five locales                                     
Documentation
4 files
README.md
Document iFlytek ASR support and hotword note                       
+8/-8     
README.zh.md
Update Chinese README for iFlytek ASR                                       
+7/-7     
USAGE.md
Add pointer to xfyun ASR setup doc                                             
+2/-0     
xfyun-asr.md
Add iFlytek ASR setup and troubleshooting guide                   
+39/-0   
Dependencies
1 files
Cargo.toml
Add md-5 and sha1 dependencies for signa                                 
+4/-0     
Additional files
5 files
en.ts +4/-0     
ja.ts +4/-0     
ko.ts +4/-0     
zh-CN.ts +4/-0     
zh-TW.ts +4/-0     

- 新增 asr/xfyun.rs RTASR 流式客户端:signa = Base64(HmacSHA1(MD5(appid+ts), apiKey))
  鉴权,1280B/40ms 分块发送,{"end": true} 收尾,seg_id 去重拼接,断连 partial 兜底,
  鉴权/限流/超时错误分类可读。
- 全链路接入:dictation 主流程、QA 面板、重新转录、取消、设置页凭据预检与
  "验证"探活;凭据 xfyun.app_id / xfyun.api_key 存系统凭据库,不落日志。
- 设置页新增「讯飞实时语音转写」选项(AppID + API Key),5 种语言 i18n。
- 文档:docs/xfyun-asr.md + README/README.zh/USAGE 同步。

已知限制:标准版 RTASR 无请求级热词参数(在讯飞控制台配置),语种默认中文普通话。
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

842 - Partially compliant

Compliant requirements:

  • 设置页新增「讯飞实时语音转写」选项,包含 AppID 与 API Key 凭据字段。
  • 实现了 RTASR 实时流式集成,对齐 open_session → consume_pcm_chunk → send_last_frame → await_final_result 模式。
  • 听写主流程已接入(录音 → 转写 → 润色 → 插入/剪贴板),并覆盖 QA 面板与重新转录路径。
  • 错误分类为用户可读消息(鉴权 10105/10110、并发 10800、超时等)。
  • 新增 docs/xfyun-asr.md,说明服务开通、凭证获取、音频约束与 IP 白名单。
  • 凭据存入系统凭据库,未发现写入日志或历史记录的路径。

Non-compliant requirements:

  • 用户词典热词未向讯飞传递(标准版 RTASR 无请求级热词参数,PR 已通过 UI/文档说明需在讯飞控制台配置)。

Requires further human verification:

  • 使用真实讯飞账号进行端到端验证(录音→转写→润色→插入)及「验证」按钮探活。
  • 设置页 ASR 下拉、凭据字段与 i18n 文案的实际 UI 效果需人工确认。
  • 方言/语种、IP 白名单等账号维度限制对实际连接的影响需真实环境验证。
  • 需确认新增的 CredentialsSnapshot 字段(xfyun_api_key)不会通过 IPC 序列化暴露给前端。
⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 Security concerns

Sensitive information exposure:
CredentialsSnapshot 新增了 xfyun_app_id 与 xfyun_api_key 字段。若该 snapshot 会通过 IPC 序列化给前端(从现有字段模式看很可能),则新增的讯飞 API Key 会被暴露给 renderer 进程,与 PR 描述「CredentialsSnapshot 不向 IPC 暴露」不符。建议确认是否存在脱敏视图;若确实会序列化,应将该字段从 IPC 暴露中排除。

⚡ Recommended focus areas for review

Possible Issue

断连兜底会丢失尾部 partial 结果。finish_with_partial_or_error 只要存在任何已识别内容(last_result_text 或 partial_segments 非空)就调用 finish_success,但 finish_success 在 final_segments 非空时只拼接 final_segments,忽略 partial_segments 与 last_result_text。若服务端已下发某个 seg 的最终结果、随后在最后一个 seg 只有中间结果时断开,最后一句将被丢弃。

let text = if st.final_segments.is_empty() {
    st.last_result_text.clone()
} else {
    let segments: Vec<String> = st.final_segments.values().cloned().collect();
    super::mimo::join_transcript_chunks(&segments)
};
Potential Hang

send_binary 对 ws.send 没有超时,send_last_frame 收尾阶段直接调用它发送 {"end": true};若 worker 正持有 writer 锁并阻塞在 TCP 发送,或对端网络卡死,end_session 会一直停在该处,无法进入最终结果等待。open_session 已为握手加了超时,这里也应加保护。

async fn send_binary(writer: &SharedWriter, data: Vec<u8>) -> Result<(), XfyunASRError> {
    let mut guard = writer.lock().await;
    let Some(ws) = guard.as_mut() else {
        return Err(XfyunASRError::ConnectionFailed(
            "websocket not open".to_string(),
        ));
    };
    ws.send(Message::Binary(data))
        .await
        .map_err(|e| XfyunASRError::ConnectionFailed(e.to_string()))
}

@H-Chris233
H-Chris233 merged commit 5be28b1 into Open-Less:beta Aug 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant