# SPDX-FileCopyrightText: 2022-present deepset GmbH # # 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"}}} ], }