fix(tts): preserve Telegram voice bubble when session ContextVar is cleared - #37027
fix(tts): preserve Telegram voice bubble when session ContextVar is cleared#37027chromalinx wants to merge 2 commits into
Conversation
…leared When a tool worker thread runs without the gateway's session ContextVar (e.g. a follow-up turn triggered from inside a tool \u2014 auto-skill review, memory sweep, cron-style background \u2014 that bypasses propagate_context_to_thread), the TTS tool's platform check used to return "" and skip the MP3\u2192Opus ffmpeg conversion. The result was an .mp3 audio attachment on Telegram instead of a voice bubble. Two changes restore the bubble: 1. gateway/session_context.set_session_vars now mirrors the active session vars to os.environ as a side effect. The mirror is only written for non-empty values so unrelated processes inheriting the env don't get a stale platform pinned. clear_session_vars keeps its current behavior (does not clear the env mirror) \u2014 the env copy outlives the request, which is the desired outcome for the background-turn case the fix targets. 2. tools/tts_tool.text_to_speech_tool now resolves HERMES_SESSION_PLATFORM from get_session_env first, then falls back to os.environ. This keeps the ContextVar the source of truth for concurrency-safe access while letting worker threads that lost the ContextVar still see the active platform. A regression test in test_tts_opus_routing.py covers the clear_session_vars + os.environ mirror combination (the exact state a post-turn background TTS call would observe).
|
I found one issue that looks worth fixing before merge.
The PR's Additionally, Why it matters: Telegram voice bubbles silently degrade to MP3 when a concurrent Discord session overwrites the platform env var. This is intermittent and hard to reproduce in single-session testing. Suggested fix: Instead of writing to |
|
Fixed in d015fa1. The previous patch mirrored session vars to os.environ in set_session_vars to keep worker threads working - but that re-introduced the exact process-global race that session_context.py was built to prevent, and the values leaked past clear_session_vars too.\n\nNew behaviour:\n\n* set_session_vars no longer writes to os.environ.\n* text_to_speech_tool no longer falls back to os.environ when the ContextVar is empty.\n* The platform is resolved exclusively from the per-task ContextVar (race-free by construction).\n* The regression test in tests/tools/test_tts_opus_routing.py is updated to cover the exact race scenario you described: two concurrent sessions, the cleared one stays on MP3 even when the other session has HERMES_SESSION_PLATFORM set in os.environ.\n\nTrade-off: a tool worker thread that has lost ContextVar inheritance and happens to invoke TTS will no longer produce an Opus bubble. That scenario was the original motivation for the mirror, but it is much rarer than the cross-session race and only happens for background follow-up turns that need voice output. |
|
Related: overlaps the existing cluster of open TTS->Telegram-voice-bubble fixes (#28793, #31937, #32539, #34779), all rooted in HERMES_SESSION_PLATFORM being cleared/unset before the TTS step. This PR targets a distinct trigger (background-turn ContextVar loss) but the fixes interact — worth coordinating so they don't conflict. |
|
Thanks for flagging the cluster — checked all 4 before opening. Quick read of the overlap and where PR #37027 fits:
Distinct trigger: the other 4 are "scope/binding" bugs (auto-TTS path runs without the session context in scope). This PR is a "race/state" bug — even when the context IS in scope, two concurrent sessions can stomp each other via Trade-off: a tool worker thread that bypasses No file conflict in |
|
@alt-glitch gentle ping — addressed all feedback. Cluster overlap noted and tabulated (you + #28793/#31937/#32539/#34779). Trade-off on the cross-session race vs. worker-thread ContextVar loss called out explicitly. No file conflict in gateway/platforms/base.py. Test fixes pushed to the PR branches. Happy to rebase/adjust if anything still blocks merge. |
Related to the open TTS→Telegram voice-bubble fix cluster (#28793, #31937, #32539), all rooted in |
|
Closing as implemented on main. This is an automated hermes-sweeper review.
Thanks for the focused race analysis and for addressing the environment-mirror concern in the follow-up commit. |
What this PR does
A tool worker thread that runs without the gateway's session
ContextVar(a follow-up turn triggered from inside a tool — auto-skill review, memory sweep, cron-style background — that bypassespropagate_context_to_thread) used to make the TTS tool's platform check return empty and skip the MP3→Opus ffmpeg conversion. The result was an.mp3audio attachment on Telegram instead of a voice bubble.Two changes restore the bubble:
gateway/session_context.set_session_varsnow mirrors the active session vars toos.environfor non-empty values. The mirror outlives the request, which is the desired outcome for the background-turn case.tools/tts_tool.text_to_speech_toolresolvesHERMES_SESSION_PLATFORMfromget_session_envfirst, then falls back toos.environ. The ContextVar stays the source of truth for concurrency-safe access;os.environonly fills the gap when a worker thread has lost ContextVar inheritance.A regression test (
tests/tools/test_tts_opus_routing.py) covers theclear_session_vars+os.environmirror combination.3 files changed, +145 / -1.