chore: import upstream snapshot with attribution
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
Docker image release / Build base image (push) Has been cancelled
Sync docs with Docusaurus / sync (push) Has been cancelled
Tests / Check if changed (push) Has been cancelled
Tests / format (push) Has been cancelled
Tests / check-imports (push) Has been cancelled
Tests / Unit / macos-latest (push) Has been cancelled
Tests / Unit / ubuntu-latest (push) Has been cancelled
Tests / Unit / windows-latest (push) Has been cancelled
Tests / mypy (push) Has been cancelled
Tests / Integration / ubuntu-latest (push) Has been cancelled
Tests / Integration / macos-latest (push) Has been cancelled
Tests / Integration / windows-latest (push) Has been cancelled
Tests / notify-slack-on-failure (push) Has been cancelled
Tests / Mark tests as completed (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
Docker image release / Build base image (push) Has been cancelled
Sync docs with Docusaurus / sync (push) Has been cancelled
Tests / Check if changed (push) Has been cancelled
Tests / format (push) Has been cancelled
Tests / check-imports (push) Has been cancelled
Tests / Unit / macos-latest (push) Has been cancelled
Tests / Unit / ubuntu-latest (push) Has been cancelled
Tests / Unit / windows-latest (push) Has been cancelled
Tests / mypy (push) Has been cancelled
Tests / Integration / ubuntu-latest (push) Has been cancelled
Tests / Integration / macos-latest (push) Has been cancelled
Tests / Integration / windows-latest (push) Has been cancelled
Tests / notify-slack-on-failure (push) Has been cancelled
Tests / Mark tests as completed (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
|
||||
from haystack.dataclasses import ChatMessage, ChatRole, ImageContent, ReasoningContent, TextContent, ToolCall
|
||||
from haystack.dataclasses.chat_message import ToolCallResult
|
||||
from haystack.utils.hf import convert_message_to_hf_format
|
||||
|
||||
|
||||
def test_convert_message_to_hf_format():
|
||||
message = ChatMessage.from_system("You are good assistant")
|
||||
assert convert_message_to_hf_format(message) == {"role": "system", "content": "You are good assistant"}
|
||||
|
||||
message = ChatMessage.from_user("I have a question")
|
||||
assert convert_message_to_hf_format(message) == {"role": "user", "content": "I have a question"}
|
||||
|
||||
message = ChatMessage.from_assistant(text="I have an answer", meta={"finish_reason": "stop"})
|
||||
assert convert_message_to_hf_format(message) == {"role": "assistant", "content": "I have an answer"}
|
||||
|
||||
message = ChatMessage.from_assistant(
|
||||
tool_calls=[ToolCall(id="123", tool_name="weather", arguments={"city": "Paris"})]
|
||||
)
|
||||
assert convert_message_to_hf_format(message) == {
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{"id": "123", "type": "function", "function": {"name": "weather", "arguments": {"city": "Paris"}}}
|
||||
],
|
||||
}
|
||||
|
||||
message = ChatMessage.from_assistant(tool_calls=[ToolCall(tool_name="weather", arguments={"city": "Paris"})])
|
||||
assert convert_message_to_hf_format(message) == {
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [{"type": "function", "function": {"name": "weather", "arguments": {"city": "Paris"}}}],
|
||||
}
|
||||
|
||||
tool_result = '{"weather": "sunny", "temperature": "25"}'
|
||||
message = ChatMessage.from_tool(
|
||||
tool_result=tool_result, origin=ToolCall(id="123", tool_name="weather", arguments={"city": "Paris"})
|
||||
)
|
||||
assert convert_message_to_hf_format(message) == {"role": "tool", "content": tool_result, "tool_call_id": "123"}
|
||||
|
||||
message = ChatMessage.from_tool(
|
||||
tool_result=tool_result, origin=ToolCall(tool_name="weather", arguments={"city": "Paris"})
|
||||
)
|
||||
assert convert_message_to_hf_format(message) == {"role": "tool", "content": tool_result}
|
||||
|
||||
|
||||
def test_convert_message_to_hf_invalid():
|
||||
message = ChatMessage(_role=ChatRole.ASSISTANT, _content=[])
|
||||
with pytest.raises(ValueError):
|
||||
convert_message_to_hf_format(message)
|
||||
|
||||
message = ChatMessage(
|
||||
_role=ChatRole.USER,
|
||||
_content=[
|
||||
TextContent(text="I have an answer"),
|
||||
ToolCallResult(
|
||||
result="result!",
|
||||
origin=ToolCall(id="123", tool_name="weather", arguments={"city": "Paris"}),
|
||||
error=None, # type: ignore[arg-type]
|
||||
),
|
||||
],
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
convert_message_to_hf_format(message)
|
||||
|
||||
|
||||
def test_convert_message_to_hf_format_with_multiple_images(base64_image_string):
|
||||
image1 = ImageContent(base64_image=base64_image_string)
|
||||
image2 = ImageContent(base64_image=base64_image_string)
|
||||
message = ChatMessage.from_user(content_parts=["Compare these images", image1, image2])
|
||||
|
||||
result = convert_message_to_hf_format(message)
|
||||
expected = {
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "Compare these images"},
|
||||
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_image_string}"}},
|
||||
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_image_string}"}},
|
||||
],
|
||||
}
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_convert_message_to_hf_format_skips_reasoning_content():
|
||||
"""
|
||||
Test that ReasoningContent is properly skipped during conversion.
|
||||
|
||||
ReasoningContent is for human transparency only and is not sent to the HuggingFace API
|
||||
because the API (which follows OpenAI-compatible format) does not support reasoning
|
||||
in input messages.
|
||||
"""
|
||||
# Assistant message with text and reasoning
|
||||
message = ChatMessage(
|
||||
_role=ChatRole.ASSISTANT,
|
||||
_content=[
|
||||
TextContent(text="The answer is 42."),
|
||||
ReasoningContent(reasoning_text="Let me think step by step..."),
|
||||
],
|
||||
)
|
||||
result = convert_message_to_hf_format(message)
|
||||
# ReasoningContent should be skipped, only text should be in the output
|
||||
assert result == {"role": "assistant", "content": "The answer is 42."}
|
||||
|
||||
|
||||
def test_convert_message_to_hf_format_with_tool_call_and_reasoning():
|
||||
"""
|
||||
Test that ReasoningContent is skipped when message contains tool calls.
|
||||
"""
|
||||
message = ChatMessage(
|
||||
_role=ChatRole.ASSISTANT,
|
||||
_content=[
|
||||
ReasoningContent(reasoning_text="I need to check the weather."),
|
||||
ToolCall(id="123", tool_name="weather", arguments={"city": "Paris"}),
|
||||
],
|
||||
)
|
||||
result = convert_message_to_hf_format(message)
|
||||
# ReasoningContent should be skipped
|
||||
assert result == {
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{"id": "123", "type": "function", "function": {"name": "weather", "arguments": {"city": "Paris"}}}
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user