Retry instead of raising error when Google returns 0 candidates#4125
Conversation
| def _process_response(self, response: GenerateContentResponse) -> ModelResponse: | ||
| if not response.candidates: | ||
| raise UnexpectedModelBehavior('Expected at least one candidate in Gemini response') # pragma: no cover | ||
| return ModelResponse(parts=[]) |
There was a problem hiding this comment.
We should still include all the other fields we build below, like finish reason, timestamp, provider response ID etc. Just parts should be [] (like in the if candidate.content is None or candidate.content.parts is None: branch below).
There was a problem hiding this comment.
Than should, we simplify the logic by extracting the candidate directly and tightening the null checks.
Proposed Logic:
- Extract Candidate: Use a conditional assignment to grab the first candidate.
- Simplify Initialization: Skip the
raw_reasonblock and initializefinalize_reason = None. - Update Guard Clause: Add
candidate is Noneto the existing safety check. - Metadata: Ensure
grounding_metadatais handled asNone.
candidate = response.candidates[0] if response.candidates else None
finalize_reason = None
if candidate is None or candidate.content is None or candidate.content.parts is None:
# ... handle null case ...
grounding_metadata = None|
|
||
| def test_google_process_response_empty_candidates(google_provider: GoogleProvider): | ||
| model = GoogleModel('gemini-2.5-pro', provider=google_provider) | ||
| response = _generate_response_with_texts(response_id='resp-456', texts=['', '', '']) |
There was a problem hiding this comment.
Why have the 3 texts here if we're going to reset the candidates?
There was a problem hiding this comment.
I believed it would generate a empty response. Should I remove it
There was a problem hiding this comment.
But we're resetting the candidates after anyway? So why would it matter if we create an empty response?
There was a problem hiding this comment.
Great point! I have my updated approach, I'm now creating the response directly with GenerateContentResponse.model_validate({'response_id': 'resp-456', 'candidates': []}) instead of creating it with text data and then resetting candidates.
| response.candidates = [] | ||
| result = model._process_response(response) # pyright: ignore[reportPrivateUsage] | ||
|
|
||
| assert result.parts == snapshot([]) |
There was a problem hiding this comment.
We should assert result == snapshot() so we see the entire model response.
|
@wassafshahzad Thank you! |
|
Thank you, much appreciated. |
Pre-Review Checklist
make formatandmake typecheck.Pre-Merge Checklist