fix: retry text-only on image schema errors#8500
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to detect and handle unsupported image content errors from OpenAI-compatible providers. It adds the _is_unsupported_image_content_error method to identify these errors and updates _handle_api_error to fall back to text-only payloads and retry. A corresponding unit test has also been added. The review feedback suggests improving the robustness of the error string matching in _is_unsupported_image_content_error by normalizing different quote characters (backticks, single quotes, double quotes) before checking for "expected text" to handle varying provider error formats.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def _is_unsupported_image_content_error(self, error: Exception) -> bool: | ||
| for text in self._extract_error_text_candidates(error): | ||
| text = text.lower() | ||
| if "image_url" not in text: | ||
| continue | ||
| if "unknown variant" in text or "expected `text`" in text: | ||
| return True | ||
| if "expected text" in text: | ||
| return True | ||
| return False |
There was a problem hiding this comment.
The current implementation checks for expected text with backticks and expected text without quotes separately. However, if another OpenAI-compatible provider returns single quotes (e.g., expected 'text') or double quotes (e.g., expected "text"), the substring check "expected text" in text will fail because of the intervening quote characters.
To make this check robust across different providers and quoting styles, we can normalize the error text by removing backticks, single quotes, and double quotes before performing the substring checks. We can use chr(96) to represent the backtick character safely.
| def _is_unsupported_image_content_error(self, error: Exception) -> bool: | |
| for text in self._extract_error_text_candidates(error): | |
| text = text.lower() | |
| if "image_url" not in text: | |
| continue | |
| if "unknown variant" in text or "expected `text`" in text: | |
| return True | |
| if "expected text" in text: | |
| return True | |
| return False | |
| def _is_unsupported_image_content_error(self, error: Exception) -> bool: | |
| for text in self._extract_error_text_candidates(error): | |
| text = text.lower() | |
| if "image_url" not in text: | |
| continue | |
| # Normalize quotes and backticks to handle different provider error formats robustly | |
| normalized = text.replace(chr(96), "").replace("'", "").replace('"', "") | |
| if "unknown variant" in normalized or "expected text" in normalized: | |
| return True | |
| return False |
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- In
_is_unsupported_image_content_error, consider consolidating the multiple string checks for theexpected textvariants into a single, clearer condition (e.g., using a small helper or regex) so the matching logic is easier to follow and extend if providers change wording slightly. - The new
_is_unsupported_image_content_errorand the existing_is_invalid_attachment_errorboth iterate over_extract_error_text_candidates; you might factor out a small shared helper for scanning error texts to keep the error-type predicates lean and reduce duplication if you add more cases later.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `_is_unsupported_image_content_error`, consider consolidating the multiple string checks for the `expected text` variants into a single, clearer condition (e.g., using a small helper or regex) so the matching logic is easier to follow and extend if providers change wording slightly.
- The new `_is_unsupported_image_content_error` and the existing `_is_invalid_attachment_error` both iterate over `_extract_error_text_candidates`; you might factor out a small shared helper for scanning error texts to keep the error-type predicates lean and reduce duplication if you add more cases later.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
2bf4cc0 to
30cd16a
Compare
Summary
image_urlcontent with a schema errorunknown variant image_url, expected texterror from [Bug]关于更新V4.25.2新版本之后,使用DeepSeek V4 pro(非多模态)模型时,图片消息导致API 400报错 #8489To verify
uv run --dev pytest tests\test_openai_source.py::test_handle_api_error_unsupported_image_content_retries_text_only tests\test_openai_source.py::test_handle_api_error_invalid_attachment_removes_images_and_retries_text_only -q --basetemp .tmp\pytestuv run --dev ruff check astrbot\core\provider\sources\openai_source.py tests\test_openai_source.pyuv run --dev ruff format --check astrbot\core\provider\sources\openai_source.py tests\test_openai_source.pyuv run --dev python -m py_compile astrbot\core\provider\sources\openai_source.py tests\test_openai_source.pygit diff --checkFixes #8489
Summary by Sourcery
Handle OpenAI-compatible schema errors for image content by falling back to text-only retries using the existing image-removal path.
Bug Fixes:
unknown variant image_url, expected textschema errors as unsupported image content and retry the request without images.Enhancements:
Tests:
image_urlschema errors trigger a text-only retry via image removal.