diff --git a/src/google/adk/models/llm_response.py b/src/google/adk/models/llm_response.py index 56eb6318..0264ca53 100644 --- a/src/google/adk/models/llm_response.py +++ b/src/google/adk/models/llm_response.py @@ -148,7 +148,9 @@ class LlmResponse(BaseModel): usage_metadata = generate_content_response.usage_metadata if generate_content_response.candidates: candidate = generate_content_response.candidates[0] - if candidate.content and candidate.content.parts: + if ( + candidate.content and candidate.content.parts + ) or candidate.finish_reason == types.FinishReason.STOP: return LlmResponse( content=candidate.content, grounding_metadata=candidate.grounding_metadata, diff --git a/tests/unittests/models/test_llm_response.py b/tests/unittests/models/test_llm_response.py index 8ab5e6aa..167841c8 100644 --- a/tests/unittests/models/test_llm_response.py +++ b/tests/unittests/models/test_llm_response.py @@ -317,3 +317,20 @@ def test_llm_response_create_error_case_with_citation_metadata(): assert ( response.error_message == 'Response blocked due to recitation triggered' ) + + +def test_llm_response_create_empty_content_with_stop_reason(): + """Test LlmResponse.create() with empty content and stop finish reason.""" + generate_content_response = types.GenerateContentResponse( + candidates=[ + types.Candidate( + content=types.Content(parts=[]), + finish_reason=types.FinishReason.STOP, + ) + ] + ) + + response = LlmResponse.create(generate_content_response) + + assert response.error_code is None + assert response.content is not None diff --git a/tests/unittests/utils/test_streaming_utils.py b/tests/unittests/utils/test_streaming_utils.py index 057c05e9..8ed9375f 100644 --- a/tests/unittests/utils/test_streaming_utils.py +++ b/tests/unittests/utils/test_streaming_utils.py @@ -179,3 +179,24 @@ class TestStreamingResponseAggregator: assert closed_response.content.parts[0].text == "Error" assert closed_response.error_code == types.FinishReason.RECITATION assert closed_response.error_message == "Recitation error" + + @pytest.mark.asyncio + async def test_process_response_with_none_content(self): + """Test that StreamingResponseAggregator handles content=None.""" + aggregator = streaming_utils.StreamingResponseAggregator() + response = types.GenerateContentResponse( + candidates=[ + types.Candidate( + content=types.Content(parts=[]), + finish_reason=types.FinishReason.STOP, + ) + ] + ) + results = [] + async for r in aggregator.process_response(response): + results.append(r) + assert len(results) == 1 + assert results[0].content is not None + + closed_response = aggregator.close() + assert closed_response is None