Skip to content

fix(feishu): native voice bubble support for TTS audio messages - #53157

Closed
LLQWQ wants to merge 3 commits into
NousResearch:mainfrom
LLQWQ:fix/feishu-native-voice-message
Closed

fix(feishu): native voice bubble support for TTS audio messages#53157
LLQWQ wants to merge 3 commits into
NousResearch:mainfrom
LLQWQ:fix/feishu-native-voice-message

Conversation

@LLQWQ

@LLQWQ LLQWQ commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Problem

Feishu users receive TTS audio as file attachments instead of native voice bubbles. Three issues prevent native voice delivery:

  1. TTS generates .mp3 instead of .ogg for Feishuwant_opus guard in tts_tool.py only includes Telegram
  2. File upload missing duration — Feishu's file upload API requires duration for opus audio
  3. Audio messages fail in threadsmsg_type='audio' with receive_id_type='thread_id' returns error 99992402

Related: #18831, #16524, #45637

Changes

1. tools/tts_tool.py — opus format for Feishu

Add "feishu" to the want_opus guard so TTS generates .ogg (opus) format for Feishu, matching the existing Telegram behavior.

2. plugins/platforms/feishu/adapter.py — upload with duration

Add _get_audio_duration_ms() that parses the OGG container (pure Python, no ffprobe dependency) and pass duration to CreateFileRequestBody when uploading opus files.

3. plugins/platforms/feishu/adapter.py — thread routing fallback

When audio send fails with 99992402 in a thread:

  • First try the reply API by fetching the last message in the thread via im/v1/message.list
  • Fall back to chat_id routing (main chat) if reply also fails

Testing

Verified on Feishu DM with threads:

  • ✅ TTS generates .ogg format
  • ✅ File upload succeeds with duration
  • ✅ Voice bubble appears in thread (via reply API fallback)
  • ✅ Voice bubble appears in main chat (via chat_id fallback when reply fails)

Notes

  • The OGG duration parser is ~25 lines of pure Python using struct, no external dependencies
  • The thread fallback is needed because Feishu's API doesn't support receive_id_type='thread_id' for msg_type='audio'

Three changes to enable native Feishu voice bubbles instead of file attachments:

1. tools/tts_tool.py: Add 'feishu' to want_opus guard so TTS generates
   .ogg (opus) format for Feishu, matching the existing Telegram behavior.
   Related: NousResearch#45637

2. plugins/platforms/feishu/adapter.py - upload duration:
   Include audio duration in CreateFileRequestBody when uploading opus
   files. Duration is extracted by parsing the OGG container (pure Python,
   no ffprobe dependency).

3. plugins/platforms/feishu/adapter.py - thread routing fallback:
   Feishu's create message API rejects msg_type='audio' with
   receive_id_type='thread_id' (error 99992402). As a workaround,
   when audio send fails in a thread:
   a) Try the reply API by fetching the last message in the thread
   b) Fall back to chat_id routing (main chat) if reply also fails

Fixes NousResearch#18831
Fixes NousResearch#16524
@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery platform/feishu Feishu / Lark adapter tool/tts Text-to-speech and transcription P2 Medium — degraded but workaround exists labels Jun 26, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for tracing this through both TTS generation and the Feishu media pipeline. The first two gaps remain on current main: tools/tts_tool.py:2202 still restricts want_opus to Telegram, and plugins/platforms/feishu/adapter.py:4559-4563 still builds an Opus upload without duration.

Problems

  • This PR adds no regression tests. The existing voice test at tests/gateway/test_feishu.py:2596-2644 writes b"opus" and only asserts file/message types; it cannot verify OGG duration extraction, the upload duration field, or either thread fallback branch.
  • Existing TTS routing coverage is Telegram-only at tests/tools/test_tts_opus_routing.py:47-70; it does not cover the changed Feishu platform condition.

Suggested changes

  • Add Feishu TTS routing coverage mirroring the Telegram test.
  • Add focused duration and 99992402 retry/fallback tests in tests/gateway/test_feishu.py.

This is an automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
teknium1 pushed a commit that referenced this pull request Jul 28, 2026
…bubbles

Salvaged from PR #53157 (@LLQWQ). Three changes for native Feishu voice
bubble delivery:

1. Include audio duration in the file-upload body when uploading opus
   files — Feishu renders 0:00 bubbles without it. Duration is extracted
   by parsing the OGG container's last granule position (pure Python,
   no ffprobe dependency).
2. Thread routing fallback: Feishu's create-message API rejects
   msg_type='audio' with receive_id_type='thread_id' (error 99992402);
   retry via the reply API against the thread's last message, then fall
   back to chat_id routing.
3. (dropped) tools/tts_tool.py want_opus hunk — superseded by main's
   OPUS_VOICE_PLATFORMS, which already includes feishu. The PR's stray
   scripts/release.py hunk was also dropped (frozen AUTHOR_MAP policy;
   mapping added under contributors/emails/ instead).

Fixes #45557
Refs #18831 #16524
teknium1 pushed a commit that referenced this pull request Jul 28, 2026
…bubbles

Salvaged from PR #53157 (@LLQWQ). Three changes for native Feishu voice
bubble delivery:

1. Include audio duration in the file-upload body when uploading opus
   files — Feishu renders 0:00 bubbles without it. Duration is extracted
   by parsing the OGG container's last granule position (pure Python,
   no ffprobe dependency).
2. Thread routing fallback: Feishu's create-message API rejects
   msg_type='audio' with receive_id_type='thread_id' (error 99992402);
   retry via the reply API against the thread's last message, then fall
   back to chat_id routing.
3. (dropped) tools/tts_tool.py want_opus hunk — superseded by main's
   OPUS_VOICE_PLATFORMS, which already includes feishu. The PR's stray
   scripts/release.py hunk was also dropped (frozen AUTHOR_MAP policy;
   mapping added under contributors/emails/ instead).

Fixes #45557
Refs #18831 #16524
@teknium1

Copy link
Copy Markdown
Contributor

Merged into main via consolidated salvage PR #73508 (merge f440a44753). Your Feishu duration parser and thread-routing fallback were cherry-picked with your authorship.

Your contribution is credited to you in git history. Thank you! Closing this PR as merged-via-salvage.

@teknium1 teknium1 closed this Jul 29, 2026
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…bubbles

Salvaged from PR NousResearch#53157 (@LLQWQ). Three changes for native Feishu voice
bubble delivery:

1. Include audio duration in the file-upload body when uploading opus
   files — Feishu renders 0:00 bubbles without it. Duration is extracted
   by parsing the OGG container's last granule position (pure Python,
   no ffprobe dependency).
2. Thread routing fallback: Feishu's create-message API rejects
   msg_type='audio' with receive_id_type='thread_id' (error 99992402);
   retry via the reply API against the thread's last message, then fall
   back to chat_id routing.
3. (dropped) tools/tts_tool.py want_opus hunk — superseded by main's
   OPUS_VOICE_PLATFORMS, which already includes feishu. The PR's stray
   scripts/release.py hunk was also dropped (frozen AUTHOR_MAP policy;
   mapping added under contributors/emails/ instead).

Fixes NousResearch#45557
Refs NousResearch#18831 NousResearch#16524
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/feishu Feishu / Lark adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages tool/tts Text-to-speech and transcription type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants