Skip to content

Commit

Permalink
refactor: ChatMessage - introduce text property and deprecate `co…
Browse files Browse the repository at this point in the history
…ntent` (#8588)

* introduce text property and deprecate content

* release note

* minor test refactoring

---------

Co-authored-by: Michele Pangrazzi <[email protected]>
  • Loading branch information
anakin87 and mpangrazzi authored Nov 28, 2024
1 parent 51c1390 commit fb1baf4
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 3 deletions.
21 changes: 21 additions & 0 deletions haystack/dataclasses/chat_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0

import warnings
from dataclasses import asdict, dataclass, field
from enum import Enum
from typing import Any, Dict, Optional
Expand Down Expand Up @@ -32,6 +33,26 @@ class ChatMessage:
name: Optional[str]
meta: Dict[str, Any] = field(default_factory=dict, hash=False)

@property
def text(self) -> Optional[str]:
"""
Returns the textual content of the message.
"""
# Currently, this property mirrors the `content` attribute. This will change in 2.9.0.
# The current actual return type is str. We are using Optional[str] to be ready for 2.9.0,
# when None will be a valid value for `text`.
return object.__getattribute__(self, "content")

def __getattribute__(self, name):
# this method is reimplemented to warn about the deprecation of the `content` attribute
if name == "content":
msg = (
"The `content` attribute of `ChatMessage` will be removed in Haystack 2.9.0. "
"Use the `text` property to access the textual value."
)
warnings.warn(msg, DeprecationWarning)
return object.__getattribute__(self, name)

def is_from(self, role: ChatRole) -> bool:
"""
Check if the message is from a specific role.
Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/chatmessage-text-5bd06f6ac70ac649.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
deprecations:
- |
In Haystack 2.9.0, the `ChatMessage` dataclass will be refactored to make it more flexible and future-proof.
As part of this change, the `content` attribute will be removed.
A new `text` property has been introduced to provide access to the textual value of the `ChatMessage`.
To ensure a smooth transition, start using the `text` property now in place of `content`.
32 changes: 29 additions & 3 deletions test/dataclasses/test_chat_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,40 @@ def test_from_assistant_with_valid_content():
content = "Hello, how can I assist you?"
message = ChatMessage.from_assistant(content)
assert message.content == content
assert message.text == content
assert message.role == ChatRole.ASSISTANT


def test_from_user_with_valid_content():
content = "I have a question."
message = ChatMessage.from_user(content)
assert message.content == content
assert message.text == content
assert message.role == ChatRole.USER


def test_from_system_with_valid_content():
content = "System message."
message = ChatMessage.from_system(content)
assert message.content == content
assert message.text == content
assert message.role == ChatRole.SYSTEM


def test_with_empty_content():
message = ChatMessage.from_user("")
assert message.content == ""
assert message.text == ""
assert message.role == ChatRole.USER


def test_from_function_with_empty_name():
content = "Function call"
message = ChatMessage.from_function(content, "")
assert message.content == content
assert message.text == content
assert message.name == ""
assert message.role == ChatRole.FUNCTION


def test_to_openai_format():
Expand Down Expand Up @@ -89,10 +96,15 @@ def test_apply_custom_chat_templating_on_chat_message():


def test_to_dict():
message = ChatMessage.from_user("content")
message.meta["some"] = "some"
content = "content"
role = "user"
meta = {"some": "some"}

message = ChatMessage.from_user(content)
message.meta.update(meta)

assert message.to_dict() == {"content": "content", "role": "user", "name": None, "meta": {"some": "some"}}
assert message.text == content
assert message.to_dict() == {"content": content, "role": role, "name": None, "meta": meta}


def test_from_dict():
Expand All @@ -105,3 +117,17 @@ def test_from_dict_with_meta():
assert ChatMessage.from_dict(
data={"content": "text", "role": "assistant", "name": None, "meta": {"something": "something"}}
) == ChatMessage.from_assistant("text", meta={"something": "something"})


def test_content_deprecation_warning(recwarn):
message = ChatMessage.from_user("my message")

# accessing the content attribute triggers the deprecation warning
_ = message.content
assert len(recwarn) == 1
wrn = recwarn.pop(DeprecationWarning)
assert "`content` attribute" in wrn.message.args[0]

# accessing the text property does not trigger a warning
assert message.text == "my message"
assert len(recwarn) == 0

0 comments on commit fb1baf4

Please sign in to comment.