2 Commits
Author SHA1 Message Date
664633f32d fix(asr): Apple Speech 保留停顿分段,修复停顿前内容丢失 (#824)
* fix(asr): Apple Speech 保留停顿分段,修复停顿前内容丢失

Apple 设备端识别会在语音停顿处把音频切成多个话段(utterance),每个话段
的回调只携带该话段的文本,随后 partial 从空重新累计;isFinal 通常只跟着
最后一个话段出现。原实现「拿到第一个 isFinal 就返回」,导致停顿之前的所有
话段被整段丢弃——用户表现为「说话中途停顿思考,前面说的全没了」。

- 新增 SegmentAccumulator:以 speechRecognitionMetadata 非空作为话段边界
  信号,逐话段落袋,结束后按 CJK / 空格语言规则拼接返回。
- 结束判定由「见到第一个 final 就收工」改为轮询 SFSpeechRecognitionTask
  .state == completed(辅以 isFinal 后静默的后备条件),兼容部分系统按话段
  多次发 isFinal;completed 后留 250ms 收尾窗口,避免竞态截掉尾段。
- 三重防丢 / 防重:partial 骤缩时判定静默话段重置并抢救上一段;引擎重放
  累计全文时按前缀 / 空白不敏感规则去重;识别中途报错时返回已累积话段而非
  整段作废。
- 等待预算由固定 60s 硬顶改为 max(60s, 音频时长 + 30s),长录音不再被
  「等待语音识别结果超时」截断。
- 新增 12 个单元测试覆盖多话段累积、逐话段 final、静默重置抢救、去重、
  拼接规则与等待预算。

单话段(无停顿)场景输出与修复前逐字节相同,云端识别路径不受影响,因此不
存在回归风险。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(asr): harden Apple Speech segment lifecycle

* fix(asr): preserve repeated final-only utterances

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: baiqing <lbx12309@icloud.com>
2026-07-15 16:01:02 +08:00
eb099c4f41 build(macos): 校验打包产物声明语音识别用途 (#825)
* build(macos): 校验打包产物声明语音识别用途

build-mac.sh 已在校验 NSMicrophoneUsageDescription 与 audio-input
entitlement,但 Apple Speech provider 引入的 NSSpeechRecognitionUsage-
Description 没有对应护栏。该 key 一旦缺失,构建静默成功,而选择
apple-speech 的用户会在调用 Speech.framework 时被 macOS TCC 以 SIGABRT
直接杀掉进程(崩溃报告 termination namespace = TCC)。

对打包产物 .app/Contents/Info.plist(而非源码 Info.plist)新增一行
PlistBuddy 校验:脚本开头的 set -euo pipefail 会让 key 缺失时非零退出、
当场中止构建。key 存在时输出被丢弃,行为与现在完全一致,产物逐字节不变。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(macos): enforce speech usage description contract

* fix(macos): reject Unicode-only speech descriptions

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: sim <sim@local>
2026-07-15 15:26:48 +08:00