diff --git a/google/generativeai/types/generation_types.py b/google/generativeai/types/generation_types.py index 84689a922..23e7fb1d8 100644 --- a/google/generativeai/types/generation_types.py +++ b/google/generativeai/types/generation_types.py @@ -412,14 +412,22 @@ def parts(self): """ candidates = self.candidates if not candidates: - raise ValueError( + msg = ( "Invalid operation: The `response.parts` quick accessor requires a single candidate, " - "but none were returned. Please check the `response.prompt_feedback` to determine if the prompt was blocked." + "but but `response.candidates` is empty." ) + if self.prompt_feedback: + raise ValueError( + msg + "\nThis appears to be caused by a blocked prompt, " + f"see `response.prompt_feedback`: {self.prompt_feedback}" + ) + else: + raise ValueError(msg) + if len(candidates) > 1: raise ValueError( - "Invalid operation: The `response.parts` quick accessor requires a single candidate. " - "For multiple candidates, please use `result.candidates[index].text`." + "Invalid operation: The `response.parts` quick accessor retrieves the parts for a single candidate. " + "This response contains multiple candidates, please use `result.candidates[index].text`." ) parts = candidates[0].content.parts return parts @@ -433,10 +441,53 @@ def text(self): """ parts = self.parts if not parts: - raise ValueError( - "Invalid operation: The `response.text` quick accessor requires the response to contain a valid `Part`, " - "but none were returned. Please check the `candidate.safety_ratings` to determine if the response was blocked." + candidate = self.candidates[0] + + fr = candidate.finish_reason + FinishReason = protos.Candidate.FinishReason + + msg = ( + "Invalid operation: The `response.text` quick accessor requires the response to contain a valid " + "`Part`, but none were returned. The candidate's " + f"[finish_reason](https://ai.google.dev/api/generate-content#finishreason) is {fr}." ) + if candidate.finish_message: + msg += 'The `finish_message` is "{candidate.finish_message}".' + + if fr is FinishReason.FINISH_REASON_UNSPECIFIED: + raise ValueError(msg) + elif fr is FinishReason.STOP: + raise ValueError(msg) + elif fr is FinishReason.MAX_TOKENS: + raise ValueError(msg) + elif fr is FinishReason.SAFETY: + raise ValueError( + msg + f" The candidate's safety_ratings are: {candidate.safety_ratings}.", + candidate.safety_ratings, + ) + elif fr is FinishReason.RECITATION: + raise ValueError( + msg + " Meaning that the model was reciting from copyrighted material." + ) + elif fr is FinishReason.LANGUAGE: + raise ValueError(msg + " Meaning the response was using an unsupported language.") + elif fr is FinishReason.OTHER: + raise ValueError(msg) + elif fr is FinishReason.BLOCKLIST: + raise ValueError(msg) + elif fr is FinishReason.PROHIBITED_CONTENT: + raise ValueError(msg) + elif fr is FinishReason.SPII: + raise ValueError(msg + " SPII - Sensitive Personally Identifiable Information.") + elif fr is FinishReason.MALFORMED_FUNCTION_CALL: + raise ValueError( + msg + " Meaning that model generated a `FunctionCall` that was invalid. " + "Setting the " + "[Function calling mode](https://ai.google.dev/gemini-api/docs/function-calling#function_calling_mode) " + "to `ANY` can fix this because it enables constrained decoding." + ) + else: + raise ValueError(msg) texts = [] for part in parts: diff --git a/setup.py b/setup.py index b4b05e619..29841ba1d 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ def get_version(): release_status = "Development Status :: 5 - Production/Stable" dependencies = [ - "google-ai-generativelanguage==0.6.6", + "google-ai-generativelanguage==0.6.9", "google-api-core", "google-api-python-client", "google-auth>=2.15.0", # 2.15 adds API key auth support diff --git a/tests/test_generative_models.py b/tests/test_generative_models.py index cccea9d48..79c1ac36f 100644 --- a/tests/test_generative_models.py +++ b/tests/test_generative_models.py @@ -1120,13 +1120,13 @@ def test_repr_error_info_for_chat_streaming_unexpected_stop(self): "usage_metadata": {} }), ), - error= index: 0 - content { + error= content { parts { text: "abc" } } finish_reason: SAFETY + index: 0 citation_metadata { } """