Studio: Fix image-only chat requests failing validation (#5212)

* fix: allow image-only chat messages

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* test: deduplicate empty content validation coverage

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Lee Jackson 2026-04-28 22:49:13 +01:00 committed by GitHub
commit ff759ba7e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 20 additions and 10 deletions

View file

@ -396,7 +396,10 @@ class ChatMessage(BaseModel):
if self.name is not None and self.role != "tool":
raise ValueError('"name" is only valid on role="tool" messages.')
# Per-role content requirements.
# Per-role content requirements. OpenAI-compatible clients may send
# ``content=""`` for image-only turns when the image travels in a
# companion field such as Studio's ``image_base64`` extension, so treat
# empty strings as present content for user/system messages.
if self.role == "tool":
if not self.tool_call_id:
raise ValueError(
@ -411,10 +414,8 @@ class ChatMessage(BaseModel):
'role="assistant" messages require either "content" or "tool_calls".'
)
else: # "user" | "system"
if not self.content:
raise ValueError(
f'role="{self.role}" messages require non-empty "content".'
)
if self.content is None or self.content == []:
raise ValueError(f'role="{self.role}" messages require "content".')
return self

View file

@ -144,13 +144,14 @@ class TestChatMessageToolRoles:
# ── Role-aware content requirements ────────────────────────────
def test_user_empty_content_rejected(self):
with pytest.raises(ValidationError):
ChatMessage(role = "user", content = "")
@pytest.mark.parametrize("role", ["user", "system"])
def test_empty_string_content_allowed(self, role):
msg = ChatMessage(role = role, content = "")
assert msg.content == ""
def test_system_empty_content_rejected(self):
def test_user_missing_content_rejected(self):
with pytest.raises(ValidationError):
ChatMessage(role = "system", content = "")
ChatMessage(role = "user")
def test_user_empty_list_content_rejected(self):
with pytest.raises(ValidationError):
@ -226,6 +227,14 @@ class TestChatCompletionRequestToolFields:
assert len(req.tools) == 1
assert req.tools[0]["function"]["name"] == "get_weather"
def test_image_base64_allows_empty_user_text(self):
req = ChatCompletionRequest(
messages = [{"role": "user", "content": ""}],
image_base64 = "aW1hZ2U=",
)
assert req.messages[0].content == ""
assert req.image_base64 == "aW1hZ2U="
def test_tool_choice_string_auto(self):
assert self._make(tool_choice = "auto").tool_choice == "auto"