Skip to content

Commit 441b487

Browse files
authored
fix: ChatMessage bugfixes (#9682)
* fix: ChatMessage bugfixes * fix + release note * better condition
1 parent 323274e commit 441b487

3 files changed

Lines changed: 605 additions & 634 deletions

File tree

haystack/dataclasses/chat_message.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -368,20 +368,22 @@ def from_user(
368368
if text is not None and content_parts is not None:
369369
raise ValueError("Only one of text or content_parts can be provided.")
370370

371-
content: Sequence[Union[TextContent, ImageContent]] = []
371+
content: List[Union[TextContent, ImageContent]] = []
372372

373373
if text is not None:
374374
content = [TextContent(text=text)]
375375
elif content_parts is not None:
376-
content = [TextContent(el) if isinstance(el, str) else el for el in content_parts]
377-
if not any(isinstance(el, TextContent) for el in content):
378-
raise ValueError("The user message must contain at least one textual part.")
379-
380-
unsupported_parts = [el for el in content if not isinstance(el, (ImageContent, TextContent))]
381-
if unsupported_parts:
382-
raise ValueError(
383-
f"The user message must contain only text or image parts. Unsupported parts: {unsupported_parts}"
384-
)
376+
for part in content_parts:
377+
if isinstance(part, str):
378+
content.append(TextContent(text=part))
379+
elif isinstance(part, (TextContent, ImageContent)):
380+
content.append(part)
381+
else:
382+
raise ValueError(
383+
f"The user message must contain only text or image parts. Unsupported part: {part}"
384+
)
385+
if len(content) == 0:
386+
raise ValueError("The user message must contain at least one textual or image part.")
385387

386388
return cls(_role=ChatRole.USER, _content=content, _meta=meta or {}, _name=name)
387389

@@ -520,14 +522,16 @@ def to_openai_dict_format(self, require_tool_call_ids: bool = True) -> Dict[str,
520522
text_contents = self.texts
521523
tool_calls = self.tool_calls
522524
tool_call_results = self.tool_call_results
525+
images = self.images
523526

524-
if not text_contents and not tool_calls and not tool_call_results:
527+
if not text_contents and not tool_calls and not tool_call_results and not images:
525528
raise ValueError(
526-
"A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, or `ToolCallResult`."
529+
"A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, "
530+
"`ToolCallResult`, or `ImageContent`."
527531
)
528-
if len(text_contents) + len(tool_call_results) > 1:
532+
if len(tool_call_results) > 0 and len(self._content) > 1:
529533
raise ValueError(
530-
"For OpenAI compatibility, a `ChatMessage` can only contain one `TextContent` or one `ToolCallResult`."
534+
"For OpenAI compatibility, a `ChatMessage` with a `ToolCallResult` cannot contain any other content."
531535
)
532536

533537
openai_msg: Dict[str, Any] = {"role": self._role.value}
@@ -538,7 +542,7 @@ def to_openai_dict_format(self, require_tool_call_ids: bool = True) -> Dict[str,
538542

539543
# user message
540544
if openai_msg["role"] == "user":
541-
if len(self._content) == 1:
545+
if len(self._content) == 1 and isinstance(self._content[0], TextContent):
542546
openai_msg["content"] = self.text
543547
return openai_msg
544548

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
fixes:
3+
- |
4+
Addressed incorrect assumptions in the `ChatMessage` class that raised errors in valid usage scenario.
5+
1. `ChatMessage.from_user` with `content_parts`: Previously, at least one text part was required, even though some
6+
model providers support messages with only image parts. This restriction has been removed.
7+
If a provider has such a limitation, it should now be enforced in the provider's implementation.
8+
2. `ChatMessage.to_openai_dict_format`: Messages containing multiple text parts weren't supported, despite this
9+
being allowed by the OpenAI API. This has now been corrected.

0 commit comments

Comments
 (0)