Conversation
…al model' rejections Text-only serving endpoints (Crusoe serverless, vLLM text-only checkpoints) reject any request whose history contains an image with HTTP 400 '<model-id> is not a multimodal model'. That phrasing was missing from _IMAGE_REJECTION_PHRASES, so the images stayed in history, every retry failed identically, and the session was permanently poisoned. block/buzz hit this exact failure live (block/buzz#5318: 8 wedged benchmark trials, 12.7h aggregate idle time) and widened their classifier; this ports the same phrase coverage to our strip-images-and-retry fallback.
૮ >ﻌ< ა ci reviewran on da67b21 — Port from block/buzz#5318: recover text-only endpoint 'not a
|
fix(agent): recover 'not a multimodal model' image rejections from text-only endpoints (port from block/buzz#5318)
|
|
Cross-reference: #69104 (open, CI green) handles an adjacent xAI wording of image-history poisoning — HTTP 400 |
Summary
Text-only serving endpoints (Crusoe serverless, vLLM deployments of text-only checkpoints, other OpenAI-compatible hosts) reject any request whose history contains an image with HTTP 400
"<model-id> is not a multimodal model"— and that phrasing was missing from_IMAGE_REJECTION_PHRASES, so the image stayed in history, every retry failed identically, and the session was permanently poisoned. This PR adds the phrase so the existing strip-images-and-retry fallback fires.Ported from block/buzz#5318, where Block hit this exact failure live: their classifier only matched OpenRouter's 404 wording, the Crusoe 400 fell through to terminal, and 8 benchmark trials wedged with 12.7h aggregate idle-after-poison before they widened the match. Same bug shape exists in our
agent/conversation_loop.pyphrase gate — verified by feeding the Crusoe body through the current tuple (no match on main; matches with this PR).Ours vs theirs
replace_unsupported_images()strips + placeholders + continues turn_strip_images_from_messages()+ session-wide_vision_supported=False+ retry — already presentnot a multimodal modelwordingis_unsupported_image_input_error()_IMAGE_REJECTION_PHRASES(this PR)Their PR's second half (unbounding benchmark agent rounds) is bench-harness-specific and not ported.
Changes
agent/conversation_loop.py: add"not a multimodal model"to_IMAGE_REJECTION_PHRASES(matches "is not a multimodal model" and bare variants; the existing 4xx status gate still applies)tests/run_agent/test_image_rejection_fallback.py: positive test for the Crusoe/vLLM body; negative test that_MULTIMODAL_TOOL_CONTENT_PATTERNSshapes ("tool content must be a string", "text is not set") do NOT trip this gate — they have their own cheaper recoveryValidation
"crusoeai/GLM-5.2-NVFP4 is not a multimodal model"tests/run_agent/test_image_rejection_fallback.pytest_multimodal_tool_content_recovery.py+test_error_classifier.pyInfographic