DocumentationAPI Reference📓 Tutorials🧑‍🍳 Cookbook🤝 Integrations💜 Discord🎨 Studio
Documentation

ChatMessage

ChatMessage is the central abstraction to represent a message for a LLM. It contains role, metadata and several types of content, including text, tool calls and tool calls results.

To create a ChatMessage instance, use from_userfrom_systemfrom_assistant, and from_tool class methods.

The content of the ChatMessage can then be inspected using the text, texts, tool_call, tool_calls, tool_call_result, and tool_call_results properties.

If you are looking for the details of this data class methods and parameters, head over to our API documentation.

Types of Content

ChatMessage currently supports TextContent, ToolCall and ToolCallResult types of content:

@dataclass
class TextContent:
    """
    The textual content of a chat message.

    :param text: The text content of the message.
    """

    text: str

@dataclass
class ToolCall:
    """
    Represents a Tool call prepared by the model, usually contained in an assistant message.

    :param tool_name: The name of the Tool to call.
    :param arguments: The arguments to call the Tool with.
    :param id: The ID of the Tool call.
    """

    tool_name: str
    arguments: Dict[str, Any]
    id: Optional[str] = None  # noqa: A003

@dataclass
class ToolCallResult:
    """
    Represents the result of a Tool invocation.

    :param result: The result of the Tool invocation.
    :param origin: The Tool call that produced this result.
    :param error: Whether the Tool invocation resulted in an error.
    """

    result: str
    origin: ToolCall
    error: bool

Working with a ChatMessage

The following examples demonstrate how to create a ChatMessage and inspect its properties.

from_user with TextContent

from haystack.dataclasses import ChatMessage

user_message = ChatMessage.from_user("What is the capital of Australia?")

print(user_message)
>>> ChatMessage(
>>>    _role=<ChatRole.USER: 'user'>,
>>>    _content=[TextContent(text='What is the capital of Australia?')],
>>>    _name=None,
>>>    _meta={}
>>>)

print(user_message.text)
>>> What is the capital of Australia?

print(user_message.texts)
>>> ['What is the capital of Australia?']

from_assistant with TextContent

from haystack.dataclasses import ChatMessage

assistant_message = ChatMessage.from_assistant("How can I assist you today?")

print(assistant_message)
>>> ChatMessage(
>>>    _role=<ChatRole.ASSISTANT: 'assistant'>,
>>>    _content=[TextContent(text='How can I assist you today?')],
>>>    _name=None,
>>>    _meta={}
>>>)

print(assistant_message.text)
>>> How can I assist you today?

print(assistant_message.texts)
>>> ['How can I assist you today?']

from_assistant with ToolCall

from haystack.dataclasses import ChatMessage, ToolCall

tool_call = ToolCall(tool_name="weather_tool", arguments={"location": "Rome"})

assistant_message_w_tool_call = ChatMessage.from_assistant(tool_calls=[tool_call])

print(assistant_message_w_tool_call)
>>> ChatMessage(
>>>    _role=<ChatRole.ASSISTANT: 'assistant'>,
>>>    _content=[ToolCall(tool_name='weather_tool', arguments={'location': 'Rome'}, id=None)],
>>>    _name=None,
>>>    _meta={}
>>>)

print(assistant_message_w_tool_call.text)
>>> None

print(assistant_message_w_tool_call.texts)
>>> []

print(assistant_message_w_tool_call.tool_call)
>>> ToolCall(tool_name='weather_tool', arguments={'location': 'Rome'}, id=None)

print(assistant_message_w_tool_call.tool_calls)
>>> [ToolCall(tool_name='weather_tool', arguments={'location': 'Rome'}, id=None)]

print(assistant_message_w_tool_call.tool_call_result)
>>> None

print(assistant_message_w_tool_call.tool_call_results)
>>> []

from_tool

from haystack.dataclasses import ChatMessage

tool_message = ChatMessage.from_tool(tool_result="temperature: 25°C", origin=tool_call, error=False)

print(tool_message)
>>> ChatMessage(
>>>    _role=<ChatRole.TOOL: 'tool'>,
>>>    _content=[ToolCallResult(
>>>							   result='temperature: 25°C',
>>>                origin=ToolCall(tool_name='weather_tool', arguments={'location': 'Rome'}, id=None),
>>>                error=False
>>>                )],
>>>    _name=None,
>>>    _meta={}
>>>)

print(tool_message.text)
>>> None

print(tool_message.texts)
>>> []

print(tool_message.tool_call)
>>> None

print(tool_message.tool_calls)
>>> []

print(tool_message.tool_call_result)
>>> ToolCallResult(
>>>     result='temperature: 25°C',
>>>     origin=ToolCall(tool_name='weather_tool', arguments={'location': 'Rome'}, id=None),
>>>     error=False
>>> )

print(tool_message.tool_call_results)
>>> [
>>>     ToolCallResult(
>>>         result='temperature: 25°C',
>>>         origin=ToolCall(tool_name='weather_tool', arguments={'location': 'Rome'}, id=None),
>>>         error=False
>>>     )
>>> ]

Migrating from Legacy ChatMessage (before v2.9)

In Haystack 2.9, we updated the ChatMessage data class for greater flexibility and support for multiple content types: text, tool calls, and tool call results.

There are some breaking changes involved, so we recommend reviewing this guide to migrate smoothly.

Creating a ChatMessage

You can no longer directly initialize ChatMessage using role, content, and meta.

  • Use the following class methods instead: from_assistant, from_user, from_system, and from_tool.
  • Replace the content parameter with text.
from haystack.dataclasses import ChatMessage

# LEGACY - DOES NOT WORK IN 2.9.0
message = ChatMessage(role=ChatRole.USER, content="Hello!")

# Use the class method instead
message = ChatMessage.from_user("Hello!")

Accessing ChatMessage Attributes

  • The legacy content attribute is now internal (_content).
  • Inspect ChatMessage attributes using the following properties:
    • role
    • meta
    • name
    • text and texts
    • tool_call and tool_calls
    • tool_call_result and tool_calls_results
from haystack.dataclasses import ChatMessage

message = ChatMessage.from_user("Hello!")

# LEGACY - DOES NOT WORK IN 2.9.0
print(message.content)

# Use the appropriate property instead
print(message.text)

Deserializing a Pipeline from YAML Containing a ChatPromptBuilder

If you have a pipeline containing a ChatPromptBuilder initialized with a template that consists of a list of ChatMessage and serialized using Haystack <2.9, deserialization will break.

Now, you should recreate your pipeline using Haystack 2.9 and serialize it again. Alternatively, you can manually update the serialized YAML files.

Here are the examples of a YAML of a Pipeline containing a ChatPromptBuilder, serialized after v2.9 and before v2.9 for comparison.

components:
  builder:
    init_parameters:
      required_variables: null
      template:
      # serialized List[ChatMessage]      
       - _content:
         - text: 'Translate to {{ target_language }}: {{ text }}'
         _meta: {}
         _name: null
         _role: user
      variables: null
    type: haystack.components.builders.chat_prompt_builder.ChatPromptBuilder
connections: []
max_runs_per_component: 100
metadata: {}
components:
  builder:
    init_parameters:
      required_variables: null
      template:
      # serialized List[ChatMessage]
      - content: 'Translate to {{ target_language }}: {{ text }}'
        meta: {}
        name: null
        role: user
      variables: null
    type: haystack.components.builders.chat_prompt_builder.ChatPromptBuilder
connections: []
max_runs_per_component: 100
metadata: {}

from_function vs from_tool methods

Starting from Haystack 2.9, the "function" role is being deprecated and will be completely removed in v2.10. Instead, you should use the "tool" role and create messages with the ChatMessage.from_tool method.

To ease the transition, the ChatMessage.from_function method attempts to create a valid message from a tool. However, this conversion may not work in all cases.

We recommend migrating to ChatMessage.from_tool.

If you continue using ChatMessage.from_function, carefully verify its outputs and keep in mind that this method will be removed in v2.10.