Skip to content

fix(gateway): set duration on Telegram voice/audio so long clips don'… - #36020

Closed
szafranski wants to merge 3 commits into
NousResearch:mainfrom
szafranski:fix/telegram-voice-duration
Closed

fix(gateway): set duration on Telegram voice/audio so long clips don'…#36020
szafranski wants to merge 3 commits into
NousResearch:mainfrom
szafranski:fix/telegram-voice-duration

Conversation

@szafranski

@szafranski szafranski commented May 31, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Telegram only auto-derives a voice/audio clip's duration from container metadata for
short recordings. Clips longer than ~4:50 are delivered with duration 0 and render as
0:00 in the player. This probes the clip length locally and passes duration
explicitly to sendVoice / sendAudio, so the bubble shows the real time. When the
length can't be determined we omit duration and fall back to Telegram's prior behavior,
so this never regresses the short-clip path.

This extracts and hardens the Telegram-only part of the stale, Piper-bundled PR #7815
(ffprobe-only, predates the current send_voice retry/anchor refactor).

Related Issue

Fixes #36005
Relates to #8508. Supersedes the Telegram portion of #7815.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • plugins/platforms/telegram/adapter.py:
    • Add _probe_voice_duration_seconds() — best-effort length via stdlib wave
      mutagenffprobe (the ffprobe branch is guarded by shutil.which).
    • Add _coerce_duration_seconds() — round to whole positive seconds, else None.
    • send_voice now runs the probe off-thread (asyncio.to_thread) and forwards
      duration to both the .ogg/.opus (send_voice) and .mp3/.m4a (send_audio) paths.
    • No new hard dependency: mutagen is used opportunistically (it isn't a declared
      dep), with wave/ffprobe covering its absence.
  • tests/gateway/test_telegram_voice_duration.py: new hermetic test suite (20 tests).

How to Test

  1. scripts/run_tests.sh tests/gateway/test_telegram_voice_duration.py → 20 passed (macOS & Linux).
  2. End-to-end on Telegram: send a voice/audio reply with audio longer than ~4:50.
  3. Before: the bubble shows 0:00. After: it shows the real length. Verified on Linux —
    a 293 s clip now renders as 4:53 (screenshot below).

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(gateway): ...)
  • I searched for existing PRs to make sure this isn't a duplicate (found feat(tts): add Piper setup support and fix Telegram voice note metadata #7815 — stale/bundled; this is the focused extract)
  • My PR contains only changes related to this fix (no unrelated commits)
  • I've run the test suite and all related tests pass (scripts/run_tests.sh tests/gateway/test_telegram_voice_duration.py → 20/20)
  • I've added tests for my changes
  • I've tested on my platform: macOS 15 (Darwin 25.5) and Linux

Documentation & Housekeeping

  • I've updated relevant documentation — N/A (internal helper, no public API/docs change)
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A (no config keys)
  • I've updated CONTRIBUTING.md / AGENTS.md if I changed architecture — N/A
  • I've considered cross-platform impact — shutil.which guards ffprobe; wave is stdlib; mutagen is optional. Validated on macOS + Linux.
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Screenshots / Logs

image

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/telegram Telegram bot adapter tool/tts Text-to-speech and transcription labels May 31, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Competing with #36009 — both fix #36005. This PR (ffprobe→mutagen→wave probe chain with off-thread execution) appears more thorough than #36009 (mutagen+file-size fallback only).

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

tl;dr: The implementation is clean and correct. One blocker before merging: the branch needs a rebase onto current main.


Stale base

git diff origin/main...pr-36020 --name-only reports 4,203 changed files — this is entirely stale-base pollution. The branch forked off an old commit (10b4cfeacfix history leakage) that is hundreds of commits behind main. The actual PR commit (34bc595) contains exactly 2 files, 265 lines net-new. Please rebase onto main so CI can run cleanly and reviewers see only the real diff.


Implementation review (gateway/platforms/telegram.py, +77 lines)

_coerce_duration_seconds() — correct and tight. Handles float-string inputs (ffprobe returns a string), rejects 0/negative, returns None for anything unusable.

_probe_voice_duration_seconds() — the three-tier fallback chain is well-structured:

  1. stdlib wave for .wav — zero external deps, early-exit on success.
  2. mutagen — opportunistic; the bare except Exception: pass correctly keeps it optional.
  3. ffprobe subprocess guarded by shutil.which — correct 5-second timeout.

All three tiers degrade gracefully to None, which restores pre-PR behavior (Telegram's own metadata, possibly absent). Non-breaking ✓.

send_voice integrationasyncio.to_thread is the right call for the blocking probe before touching the file handle. duration=_duration_secs is passed to both the send_voice (.ogg/.opus) and send_audio (.mp3/.m4a) paths. When _duration_secs is None, python-telegram-bot omits the field from the API request, matching prior behavior. ✓

One minor note: the wave import inside the function is redundant — wave is imported at module level in the test file and is stdlib, but the in-function import is harmless. No action needed.


Tests (tests/gateway/test_telegram_voice_duration.py, +188 lines)

Hermetic and thorough:

  • 4× parametrized WAV rounding cases using real stdlib wave writes.
  • mutagen path exercised via sys.modules injection (no real dep required).
  • None-length, 0-length, missing mutagen, and missing ffprobe all return None.
  • _coerce_duration_seconds contract cases.
  • Async integration tests confirm duration reaches bot.send_voice / bot.send_audio kwargs.
  • Graceful-fallback test confirms duration=None when probe fails.

20/20 coverage as claimed. ✓


Summary

The fix is correct, targeted, and well-tested. Blocking item: please rebase onto current main — once that's done and CI is green, this is ready to merge.

@szafranski
szafranski force-pushed the fix/telegram-voice-duration branch from 34bc595 to ef7fcb2 Compare June 11, 2026 07:49
austinpickett
austinpickett previously approved these changes Jun 11, 2026

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix is correct and the rebase landed clean — mergeable: MERGEABLE. Setting duration on Telegram voice/audio so long clips aren't cut off is a real fix. Approving.

@austinpickett

Copy link
Copy Markdown
Collaborator

Almost there — only one failing check is blocking merge: Contributor Attribution Check.

The commit message includes a Co-Authored-By: Claude Opus 4.8 trailer, which is what tripped it. All other checks (tests ×6, lint, typecheck, supply chain, Docker, e2e) are green.

Quick fix — amend the commit to drop that line and force-push:

git commit --amend  # remove the Co-Authored-By: Claude Opus 4.8 line, save
git push --force-with-lease

Once that lands and the attribution check goes green, this is ready to merge.

@szafranski
szafranski force-pushed the fix/telegram-voice-duration branch from ef7fcb2 to 1daaee6 Compare June 11, 2026 11:52
@szafranski

szafranski commented Jun 11, 2026 via email

Copy link
Copy Markdown
Contributor Author

@austinpickett

Copy link
Copy Markdown
Collaborator

Almost there — the last failing check is Contributor Attribution, and the fix is one line you'll need to add since it maps your email to your GitHub username.

The check found your commit-author email p.fabiszewski@gmail.com (szafranski) isn't yet in AUTHOR_MAP in scripts/release.py. This map is what generates correct release-note attribution, so new contributors add themselves once.

Please add this line to the AUTHOR_MAP dict in scripts/release.py (it starts around line 47; keep it alphabetical-ish with the other gmail.com entries):

    "p.fabiszewski@gmail.com": "szafranski",

If your actual GitHub username differs from szafranski, use that instead — it should be your real login so the release notes link correctly.

Commit + push that to this branch and the attribution check goes green. Everything else (tests, lint, typecheck, the duration fix itself) is already passing — this is the only blocker. Thanks!

@austinpickett

Copy link
Copy Markdown
Collaborator

Attribution check still failing

CI is all green except check-attribution: the contributor email p.fabiszewski@gmail.com is not in the AUTHOR_MAP in scripts/release.py.

This is a maintainer-side fix. Please add:

"p.fabiszewski@gmail.com": "szafranski",

to the AUTHOR_MAP dict in scripts/release.py (around line 47) and push the commit. Once that lands, the check will pass and this PR is merge-ready.

(My APPROVED review from earlier still stands — the fix itself is correct.)

@szafranski

szafranski commented Jun 11, 2026 via email

Copy link
Copy Markdown
Contributor Author

@szafranski

Copy link
Copy Markdown
Contributor Author

Sorry for bothering but I just wanted to ask is there anything more I should do?

@szafranski
szafranski force-pushed the fix/telegram-voice-duration branch from 2b50c6d to 0c441a9 Compare June 15, 2026 10:53
@szafranski

Copy link
Copy Markdown
Contributor Author

Sorry for the repeated attribution-check issue on my side.

I cleaned up the branch history and force-pushed a linear branch on top of current upstream/main. The AI co-author trailer has been removed from the AUTHOR_MAP commit, and the PR diff is now limited to the intended Telegram duration fix, its regression tests, and the AUTHOR_MAP entry.

The new workflow runs are currently waiting for maintainer approval/re-run (action_required).

@szafranski

Copy link
Copy Markdown
Contributor Author

@austinpickett please excuse but I wanted to make sure I didn't make some mess here, since I see some "dismissed" info above (image attached). Should I do anything more right now?
obraz

@szafranski
szafranski force-pushed the fix/telegram-voice-duration branch 2 times, most recently from acab62e to d30b698 Compare July 1, 2026 08:47
@alt-glitch alt-glitch added the sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages label Jul 1, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.
Competing with #36009 for the same issue #36005 — both pass explicit duration to send_voice/send_audio. This PR uses a more thorough wavemutagenffprobe probe chain run off-thread; #36009 uses mutagen + file-size fallback. Related, not a duplicate — maintainer to pick.

…t show 0:00

Telegram only auto-derives a voice/audio clip's duration from container
metadata for short recordings; clips longer than ~4:50 are delivered with
duration 0 and render as 0:00 in the player. Probe the length locally
(stdlib wave -> mutagen -> ffprobe) and pass duration explicitly to
sendVoice/sendAudio. Best-effort: when nothing can read the file we omit
duration and fall back to Telegram's prior behavior.

Extracts and hardens the Telegram-only part of the stale, Piper-bundled
PR NousResearch#7815 (ffprobe-only, predates the send_voice retry/anchor refactor);
relates to NousResearch#8508.
@szafranski
szafranski force-pushed the fix/telegram-voice-duration branch from d30b698 to f800325 Compare July 11, 2026 09:58

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for isolating the duration probe and preserving the adapter's retry/anchor flow. The reported adapter behavior is still present on current main: plugins/platforms/telegram/adapter.py:5861-5869 and :5887-5895 call the Bot API without duration, so this targets a live defect.

Problems

  • The fix does not cover the standalone Telegram sender. tools/send_message_tool.py:849-856 routes Telegram sends to _send_telegram, including non-gateway contexts as documented in tests/tools/test_send_message_telegram_proxy.py:3-5. Its voice/audio sends and retry sends at tools/send_message_tool.py:1327-1333 and :1357-1363 still omit duration. Long clips sent through that path retain the reported behavior.

Suggested changes

  • Share the probe between the adapter and _send_telegram, then forward the result through both initial and retry send_voice / send_audio calls.
  • Add standalone _send_telegram .ogg and .mp3 regression coverage alongside the adapter tests.

Automated hermes-sweeper review.

@@ -5864,6 +5938,7 @@ async def send_voice(
"voice": audio_file,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please apply the same duration propagation to the standalone Telegram sender. tools/send_message_tool.py:849-856 routes non-gateway Telegram sends through _send_telegram, whose send_voice/send_audio initial and retry calls at lines 1327-1333 and 1357-1363 still omit duration.

@alt-glitch alt-glitch added comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have and removed comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists labels Jul 13, 2026
@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 13, 2026
@szafranski

Copy link
Copy Markdown
Contributor Author

Addressed: the standalone send_message_tool path now reuses the Telegram duration probe for .ogg sendVoice and .mp3 sendAudio, including the topic retry path. Verified with .venv/bin/python -m pytest tests/gateway/test_telegram_voice_duration.py -q (22 passed) and ruff.

@teknium1

Copy link
Copy Markdown
Contributor

Merged via #65535 (rebase-merged onto current main, head commit d73a6f5) — all three of your commits were cherry-picked with your authorship preserved in git log. Thanks for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have platform/telegram Telegram bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform 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.

[Bug]: Telegram voice/audio longer than ~4:50 shows 0:00 (duration not set on send)

4 participants