fix(audio): keep the selected command on voice/STT-dictated messages - #2992
Open
RoyBA wants to merge 2 commits into
Open
fix(audio): keep the selected command on voice/STT-dictated messages#2992RoyBA wants to merge 2 commits into
RoyBA wants to merge 2 commits into
Conversation
Auto-attach the UI-selected command to the user message produced during an audio turn, so voice-dictated messages carry the command just like typed ones. The command is sent with audio_start, stored on the session for the turn, cleared on audio_end, and attached in Message.__post_init__. Co-Authored-By: GitHub Copilot <noreply@github.com>
RoyBA
requested review from
asvishnyakov,
hayescode and
sandangel
as code owners
July 28, 2026 20:16
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Message.from_dict deserializes incoming client messages and resumed thread history. Because the audio-command fallback lives in the shared __post_init__, a command-less typed message (or resumed step) created during an active audio turn would wrongly inherit the turn's command. Reset the command from the payload in from_dict so deserialized messages stay authoritative; app-constructed transcription messages still inherit as intended. Addresses PR review feedback. Co-Authored-By: GitHub Copilot <noreply@github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a command is selected in the composer (e.g.
/search) and the userdictates their message with the voice/STT feature instead of typing it, the
message is sent without the selected command. Typing the exact same message
correctly includes the command.
Root cause
The selected command lives in frontend state (
persistentCommandState) and isattached to the message only in the text-composer submit path (the
client_messageevent). The audio path (audio_start→audio_chunk→audio_end) never transmits the command, and the transcribed user message iscreated on the backend (in the app's
on_audio_endhandler), so it has noknowledge of the UI selection.
Fix
Carry the selected command through the audio turn and auto-attach it to the user
message produced during that turn.
Frontend — send the selected command with
audio_start:VoiceButtonreadspersistentCommandStateand passesselectedCommand?.idto
startConversation.useAudio.startConversation(command?)→useChatInteract.startAudioStream(command?)→
socket.emit('audio_start', { command }).Backend — store it for the turn and attach it:
BaseSessiongains acurrent_commandfield.audio_startstoressession.current_command;audio_endclears it, so itonly applies to that audio turn.
Message.__post_init__auto-attachescurrent_commandtouser_messagesthat don't already carry a command.
Backward compatibility
Fully backward compatible. The
audio_startpayload is optional (old clientsthat emit no payload keep working), the new frontend arguments are optional, and
the existing
Message.commandfield is reused. Typed messages and assistantmessages are unchanged.
Testing
backend/tests/test_message.py: auto-attach onuser_message, explicit command takes precedence, no-op when no command isset, and assistant messages never inherit the command.
mypy,ruff,pnpm type-check, ESLint, and Prettierall pass.
user message carries the command, identical to typing it.
Out of scope
modeshas the same gap (also only attached in the text submit path). Left for afollow-up to keep this PR focused on commands.
Summary by cubic
Keep the selected command on voice/STT messages so dictated messages behave like typed ones. The composer’s command is sent with the audio session, stored for the turn, and auto-attached to the transcribed user message.
VoiceButtonreadspersistentCommandStateand passesselectedCommand?.idtouseAudio.startConversation;@chainlit/react-clienthooks (useAudio,useChatInteract) emitaudio_startwith{ command }.session.current_commandonaudio_start, clear onaudio_end;Message.__post_init__attaches it touser_messagewhen missing;Message.from_dictkeeps payload command authoritative so typed/resumed messages never inherit an active audio turn’s command.audio_startpayload is optional; typed messages and existingMessage.commandbehavior are unchanged.Written for commit 05a82f5. Summary will update on new commits.