feat(feishu): add voice message support and streaming text reply

- Receive audio messages: map msg_type=audio to ContextType.VOICE and
    download opus file via lazy _prepare_fn for STT pipeline
  - Send voice replies: upload opus audio via Feishu file API, auto-convert
    non-opus formats (e.g. mp3) using pydub before upload
  - Streaming text reply: inject on_event callback into context; send a
  card
    placeholder on first delta, then PATCH-update it in-place at a
    configurable interval (feishu_stream_interval_ms) to achieve typewriter
    effect; set feishu_streamed=True to suppress duplicate send()
  - Enable NOT_SUPPORT_REPLYTYPE=[] to unblock voice and image reply types
  - Fix AudioSegment mutation bug in audio_convert.py: set_frame_rate /
    set_channels return new objects and must be reassigned
  - Add -nostdin to ffmpeg invocation to prevent stdin deadlock in daemon
  - Add feishu_bot_name, feishu_stream_reply, feishu_stream_interval_ms
    config keys to config-template.json
This commit is contained in:
ooaaooaa123
2026-04-30 16:14:57 +08:00
parent c9c99de3d9
commit 4d1f059c0d
4 changed files with 235 additions and 4 deletions

View File

@@ -73,9 +73,14 @@ def any_to_wav(any_path, wav_path):
return
if any_path.endswith(".sil") or any_path.endswith(".silk") or any_path.endswith(".slk"):
return sil_to_wav(any_path, wav_path)
audio = AudioSegment.from_file(any_path)
audio.set_frame_rate(8000) # 百度语音转写支持8000采样率, pcm_s16le, 单通道语音识别
audio.set_channels(1)
# pydub 0.23.0+ 会将 parameters 追加到 ffmpeg 命令的输出文件 `-` 之后,
# 因此 -nostdin 可能被当作"尾部选项"处理,是否生效取决于 ffmpeg 版本。
# 目的是防止后台服务中 ffmpeg 子进程继承父进程的 stdin避免死锁。
audio = AudioSegment.from_file(any_path, parameters=["-nostdin"])
# AudioSegment 是不可变对象set_frame_rate/set_channels 返回新对象,不修改原对象。
# 必须将返回值重新赋给 audio否则修改不会生效。
audio = audio.set_frame_rate(8000) # 百度语音转写支持8000采样率, pcm_s16le, 单通道语音识别
audio = audio.set_channels(1)
audio.export(wav_path, format="wav", codec='pcm_s16le')