diff --git a/gateway/run.py b/gateway/run.py index cca9901cb4263..85954b4660b16 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -6613,6 +6613,29 @@ async def _handle_message(self, event: MessageEvent) -> Optional[str]: _pending_clarify = None if _pending_clarify is not None: _raw_clarify_reply = (event.text or "").strip() + if ( + not _raw_clarify_reply + and getattr(event, "media_urls", None) + and not event.get_command() + ): + try: + _raw_clarify_reply = ( + await self._prepare_inbound_message_text( + event=event, + source=source, + history=[], + ) + or "" + ).strip() + except Exception as exc: + logger.warning( + "Gateway failed to prepare media clarify response " + "(session=%s, id=%s): %s", + _quick_key, + _pending_clarify.clarify_id, + exc, + ) + _raw_clarify_reply = "" # Skip slash commands — the user clearly wanted to issue a # command, not answer the clarify. Leave the clarify pending # so the user can retry; if it times out, the agent unblocks diff --git a/tests/gateway/test_telegram_audio_vs_voice.py b/tests/gateway/test_telegram_audio_vs_voice.py index d8ad38e299c91..a9a4b81f19caa 100644 --- a/tests/gateway/test_telegram_audio_vs_voice.py +++ b/tests/gateway/test_telegram_audio_vs_voice.py @@ -18,7 +18,7 @@ from gateway.config import GatewayConfig, Platform from gateway.platforms.base import MessageEvent, MessageType -from gateway.session import SessionSource +from gateway.session import SessionSource, build_session_key def _make_runner(stt_enabled: bool = True) -> "GatewayRunner": # type: ignore[name-defined] @@ -182,3 +182,47 @@ def test_telegram_media_type_detection_audio_vs_voice(): assert MessageType.VOICE.value == "voice" # Sanity: they are distinct assert MessageType.AUDIO != MessageType.VOICE + + +@pytest.mark.asyncio +async def test_voice_reply_resolves_pending_clarify_with_transcript(): + """A voice answer to an open clarify must unblock clarify, not interrupt the run.""" + from gateway.run import GatewayRunner + from tools import clarify_gateway as cm + + with cm._lock: + cm._entries.clear() + cm._session_index.clear() + + runner = _make_runner(stt_enabled=True) + runner.session_store = None + + source = SessionSource( + platform=Platform.TELEGRAM, + chat_id="1", + chat_type="dm", + user_id="user1", + ) + session_key = build_session_key(source) + cm.register("cid-voice", session_key, "What should I do?", choices=None) + + event = MessageEvent( + text="", + message_type=MessageType.VOICE, + source=source, + media_urls=["/tmp/voice.ogg"], + media_types=["audio/ogg"], + internal=True, + ) + + with patch( + "tools.transcription_tools.transcribe_audio", + return_value={"success": True, "transcript": "kurz und knackig", "provider": "whisper"}, + ) as mock_transcribe: + result = await GatewayRunner._handle_message(runner, event) + + mock_transcribe.assert_called_once_with("/tmp/voice.ogg") + assert result == "" + response = cm.wait_for_response("cid-voice", timeout=0.01) + assert response is not None + assert "kurz und knackig" in response