mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-19 04:52:06 +00:00
fix(agent): catch ChatGPT-account Codex data-URL rejection so images are stripped instead of cascading to compression (#23602)
When the user's main provider is openai-codex on the ChatGPT-account backend (https://chatgpt.com/backend-api/codex), sending a native image attachment encodes it as data:image/...base64,... in the input_image field. The OpenAI Responses API on the public endpoint accepts that, but the ChatGPT-account variant rejects it with HTTP 400: Invalid 'input[N].content[K].image_url'. Expected a valid URL, but got a value with an invalid format. Hermes' image-rejection phrase list didn't include this wording, so the error escaped the strip-and-retry branch and fell through to the generic recovery path: model fallback → context-too-large → compression cascade → auxiliary OpenRouter 402 spam (issue #23570). Add a NARROW phrase keyed on the field-path apostrophe used by the Codex Responses error format: "image_url'. expected". This matches the actual error format without false-tripping on generic 'Expected a valid URL' errors from unrelated tools (webhooks, redirect_uri, etc.). Once matched, the existing branch strips images from history, sets _vision_supported= False for the session, and retries text-only. Refs #23570 (1 of 3 image-replay improvements; persistence rewrite to store image PATHS instead of inlined base64 is a separate follow-up)
This commit is contained in:
parent
3e7145e0bb
commit
7026af4e23
2 changed files with 39 additions and 0 deletions
15
run_agent.py
15
run_agent.py
|
|
@ -13117,6 +13117,21 @@ class AIAgent:
|
|||
"does not support multimodal",
|
||||
"does not support vision",
|
||||
"model does not support image",
|
||||
# ChatGPT-account Codex backend
|
||||
# (https://chatgpt.com/backend-api/codex) rejects
|
||||
# data:image/...base64 URLs in input_image fields
|
||||
# with HTTP 400 "Invalid 'input[N].content[K].image_url'.
|
||||
# Expected a valid URL, but got a value with an
|
||||
# invalid format." The OpenAI Responses API on the
|
||||
# public endpoint accepts data URLs, but the
|
||||
# ChatGPT-account variant does not. Without this
|
||||
# phrase the agent cascaded into compression /
|
||||
# context-too-large recovery instead of just
|
||||
# stripping the images. Match is narrow on
|
||||
# purpose — keyed on the field-path apostrophe so
|
||||
# we don't false-trip on other URL validation
|
||||
# errors. (issue #23570)
|
||||
"image_url'. expected",
|
||||
)
|
||||
_err_lower = _err_body.lower()
|
||||
_looks_like_image_rejection = any(
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ class TestImageRejectionPhraseIsolation:
|
|||
"does not support multimodal",
|
||||
"does not support vision",
|
||||
"model does not support image",
|
||||
"image_url'. expected",
|
||||
)
|
||||
|
||||
def _matches(self, body: str) -> bool:
|
||||
|
|
@ -238,6 +239,29 @@ class TestImageRejectionPhraseIsolation:
|
|||
"This model does not support images",
|
||||
"vision is not supported on this endpoint",
|
||||
"model does not support image input",
|
||||
# ChatGPT-account Codex backend (issue #23570) — rejects
|
||||
# data:image/...base64 URLs in input_image fields. Without this
|
||||
# match the agent cascaded into compression / context-too-large
|
||||
# recovery instead of just stripping the images.
|
||||
"Invalid 'input[56].content[1].image_url'. Expected a valid URL, but got a value with an invalid format.",
|
||||
]
|
||||
for body in bodies:
|
||||
assert self._matches(body) is True, f"false negative on: {body}"
|
||||
|
||||
def test_codex_data_url_rejection_does_not_false_match_other_url_errors(self):
|
||||
"""The narrow 'image_url'. expected' phrase (keyed on the
|
||||
field-path apostrophe used in the Codex Responses error format)
|
||||
must NOT trip on URL validation errors that aren't about
|
||||
image_url specifically. See issue #23570 for the original error.
|
||||
"""
|
||||
bodies = [
|
||||
# Generic URL validation errors — should NOT trip
|
||||
"Invalid webhook_url. Must be a valid URL.",
|
||||
"Expected a valid URL but got an empty string.",
|
||||
"redirect_uri does not look like a valid URL.",
|
||||
# An image_url error worded differently — also should not trip
|
||||
# the narrow phrase (a separate phrase would be needed)
|
||||
"image_url field cannot be empty",
|
||||
]
|
||||
for body in bodies:
|
||||
assert self._matches(body) is False, f"false positive on: {body}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue