|
17 | 17 |
|
18 | 18 | import copy
|
19 | 19 | import io
|
| 20 | +import json |
20 | 21 | import pathlib
|
21 | 22 | from typing import (
|
22 | 23 | Any,
|
@@ -1658,8 +1659,23 @@ def text(self) -> str:
|
1658 | 1659 | " Use `response.candidate[i].text` to get text of a particular candidate."
|
1659 | 1660 | )
|
1660 | 1661 | if not self.candidates:
|
1661 |
| - raise ValueError("Response has no candidates (and no text).") |
1662 |
| - return self.candidates[0].text |
| 1662 | + raise ValueError( |
| 1663 | + "Response has no candidates (and thus no text)." |
| 1664 | + " The response is likely blocked by the safety filters.\n" |
| 1665 | + "Response:\n" |
| 1666 | + + _dict_to_pretty_string(self.to_dict()) |
| 1667 | + ) |
| 1668 | + try: |
| 1669 | + return self.candidates[0].text |
| 1670 | + except (ValueError, AttributeError) as e: |
| 1671 | + # Enrich the error message with the whole Response. |
| 1672 | + # The Candidate object does not have full information. |
| 1673 | + raise ValueError( |
| 1674 | + "Cannot get the response text.\n" |
| 1675 | + f"{e}\n" |
| 1676 | + "Response:\n" |
| 1677 | + + _dict_to_pretty_string(self.to_dict()) |
| 1678 | + ) from e |
1663 | 1679 |
|
1664 | 1680 | @property
|
1665 | 1681 | def prompt_feedback(
|
@@ -1728,7 +1744,17 @@ def citation_metadata(self) -> gapic_content_types.CitationMetadata:
|
1728 | 1744 | # GenerationPart properties
|
1729 | 1745 | @property
|
1730 | 1746 | def text(self) -> str:
|
1731 |
| - return self.content.text |
| 1747 | + try: |
| 1748 | + return self.content.text |
| 1749 | + except (ValueError, AttributeError) as e: |
| 1750 | + # Enrich the error message with the whole Candidate. |
| 1751 | + # The Content object does not have full information. |
| 1752 | + raise ValueError( |
| 1753 | + "Cannot get the Candidate text.\n" |
| 1754 | + f"{e}\n" |
| 1755 | + "Candidate:\n" |
| 1756 | + + _dict_to_pretty_string(self.to_dict()) |
| 1757 | + ) from e |
1732 | 1758 |
|
1733 | 1759 | @property
|
1734 | 1760 | def function_calls(self) -> Sequence[gapic_tool_types.FunctionCall]:
|
@@ -1799,7 +1825,12 @@ def text(self) -> str:
|
1799 | 1825 | if len(self.parts) > 1:
|
1800 | 1826 | raise ValueError("Multiple content parts are not supported.")
|
1801 | 1827 | if not self.parts:
|
1802 |
| - raise AttributeError("Content has no parts.") |
| 1828 | + raise ValueError( |
| 1829 | + "Response candidate content has no parts (and thus no text)." |
| 1830 | + " The candidate is likely blocked by the safety filters.\n" |
| 1831 | + "Content:\n" |
| 1832 | + + _dict_to_pretty_string(self.to_dict()) |
| 1833 | + ) |
1803 | 1834 | return self.parts[0].text
|
1804 | 1835 |
|
1805 | 1836 |
|
@@ -1886,7 +1917,11 @@ def to_dict(self) -> Dict[str, Any]:
|
1886 | 1917 | @property
|
1887 | 1918 | def text(self) -> str:
|
1888 | 1919 | if "text" not in self._raw_part:
|
1889 |
| - raise AttributeError("Part has no text.") |
| 1920 | + raise AttributeError( |
| 1921 | + "Response candidate content part has no text.\n" |
| 1922 | + "Part:\n" |
| 1923 | + + _dict_to_pretty_string(self.to_dict()) |
| 1924 | + ) |
1890 | 1925 | return self._raw_part.text
|
1891 | 1926 |
|
1892 | 1927 | @property
|
@@ -2188,6 +2223,11 @@ def _append_gapic_part(
|
2188 | 2223 | base_part._pb = copy.deepcopy(new_part._pb)
|
2189 | 2224 |
|
2190 | 2225 |
|
| 2226 | +def _dict_to_pretty_string(d: dict) -> str: |
| 2227 | + """Format dict as a pretty-printed JSON string.""" |
| 2228 | + return json.dumps(d, indent=2) |
| 2229 | + |
| 2230 | + |
2191 | 2231 | _FORMAT_TO_MIME_TYPE = {
|
2192 | 2232 | "png": "image/png",
|
2193 | 2233 | "jpeg": "image/jpeg",
|
|
0 commit comments