diff --git a/homeassistant/components/google_generative_ai_conversation/stt.py b/homeassistant/components/google_generative_ai_conversation/stt.py index 0a71d7e97540..9a0c5f43cba3 100644 --- a/homeassistant/components/google_generative_ai_conversation/stt.py +++ b/homeassistant/components/google_generative_ai_conversation/stt.py @@ -266,5 +266,22 @@ class GoogleGenerativeAISttEntity( response.text, stt.SpeechResultState.SUCCESS, ) + if response.prompt_feedback: + # block_reason_message is not supported by the Gemini API + # (only Vertex AI), so it is always None for this + # API-key-based client; block_reason is always populated. + LOGGER.error( + "STT response contained no text (block_reason=%s)", + response.prompt_feedback.block_reason, + ) + else: + finish_reason = ( + response.candidates[0].finish_reason + if response.candidates + else None + ) + LOGGER.error( + "STT response contained no text (finish_reason=%s)", finish_reason + ) return stt.SpeechResult(None, stt.SpeechResultState.ERROR) diff --git a/tests/components/google_generative_ai_conversation/test_stt.py b/tests/components/google_generative_ai_conversation/test_stt.py index aa5ae95eeb06..62b382d0a7f8 100644 --- a/tests/components/google_generative_ai_conversation/test_stt.py +++ b/tests/components/google_generative_ai_conversation/test_stt.py @@ -191,16 +191,48 @@ async def test_stt_process_audio_stream_api_error( assert result.text is None +@pytest.mark.parametrize( + ("response", "expected_log"), + [ + pytest.param( + types.GenerateContentResponse(candidates=[]), + "STT response contained no text (finish_reason=None)", + id="empty_response", + ), + pytest.param( + types.GenerateContentResponse( + candidates=[{"finish_reason": "STOP", "content": {"role": "model"}}] + ), + "STT response contained no text (finish_reason=FinishReason.STOP)", + id="blank_text_response", + ), + pytest.param( + types.GenerateContentResponse( + candidates=[], + prompt_feedback={ + "block_reason": "SAFETY", + # Not populated by the Gemini API in practice (only + # Vertex AI); included to prove it is not what gets + # logged. + "block_reason_message": "Blocked for safety reasons", + }, + ), + "STT response contained no text (block_reason=BlockedReason.SAFETY)", + id="blocked_prompt", + ), + ], +) @pytest.mark.usefixtures("setup_integration") -async def test_stt_process_audio_stream_empty_response( +async def test_stt_process_audio_stream_no_text_response( hass: HomeAssistant, mock_genai_client: AsyncMock, + caplog: pytest.LogCaptureFixture, + response: types.GenerateContentResponse, + expected_log: str, ) -> None: - """Test STT processing with an empty response from the API.""" + """Test STT logs when the API response contains no text.""" entity = hass.data[stt.DOMAIN].get_entity("stt.google_ai_stt") - mock_genai_client.aio.models.generate_content.return_value = ( - types.GenerateContentResponse(candidates=[]) - ) + mock_genai_client.aio.models.generate_content.return_value = response metadata = stt.SpeechMetadata( language="en-US", @@ -216,6 +248,7 @@ async def test_stt_process_audio_stream_empty_response( assert result.result == stt.SpeechResultState.ERROR assert result.text is None + assert expected_log in caplog.text @pytest.mark.usefixtures("mock_genai_client")