Skip to content

Commit c8685aa

Browse files
authored
refactor: update components to access ChatMessage.text instead of content (#8589)
* introduce text property and deprecate content * release note * use chatmessage.text * release note * linting
1 parent fb1baf4 commit c8685aa

File tree

15 files changed

+53
-42
lines changed

15 files changed

+53
-42
lines changed

haystack/components/builders/answer_builder.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ def run( # pylint: disable=too-many-positional-arguments
113113
all_answers = []
114114
for reply, given_metadata in zip(replies, meta):
115115
# Extract content from ChatMessage objects if reply is a ChatMessages, else use the string as is
116-
extracted_reply = reply.content if isinstance(reply, ChatMessage) else str(reply)
116+
if isinstance(reply, ChatMessage):
117+
if reply.text is None:
118+
raise ValueError(f"The provided ChatMessage has no text. ChatMessage: {reply}")
119+
extracted_reply = reply.text
120+
else:
121+
extracted_reply = str(reply)
117122
extracted_metadata = reply.meta if isinstance(reply, ChatMessage) else {}
118123

119124
extracted_metadata = {**extracted_metadata, **given_metadata}

haystack/components/builders/chat_prompt_builder.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ def __init__(
129129
for message in template:
130130
if message.is_from(ChatRole.USER) or message.is_from(ChatRole.SYSTEM):
131131
# infer variables from template
132-
ast = self._env.parse(message.content)
132+
if message.text is None:
133+
raise ValueError(f"The provided ChatMessage has no text. ChatMessage: {message}")
134+
ast = self._env.parse(message.text)
133135
template_variables = meta.find_undeclared_variables(ast)
134136
variables += list(template_variables)
135137
self.variables = variables
@@ -192,8 +194,9 @@ def run(
192194
for message in template:
193195
if message.is_from(ChatRole.USER) or message.is_from(ChatRole.SYSTEM):
194196
self._validate_variables(set(template_variables_combined.keys()))
195-
196-
compiled_template = self._env.from_string(message.content)
197+
if message.text is None:
198+
raise ValueError(f"The provided ChatMessage has no text. ChatMessage: {message}")
199+
compiled_template = self._env.from_string(message.text)
197200
rendered_content = compiled_template.render(template_variables_combined)
198201
# deep copy the message to avoid modifying the original message
199202
rendered_message: ChatMessage = deepcopy(message)

haystack/components/connectors/openapi_service.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,15 +161,17 @@ def _parse_message(self, message: ChatMessage) -> List[Dict[str, Any]]:
161161
:raises ValueError: If the content is not valid JSON or lacks required fields.
162162
"""
163163
function_payloads = []
164+
if message.text is None:
165+
raise ValueError(f"The provided ChatMessage has no text.\nChatMessage: {message}")
164166
try:
165-
tool_calls = json.loads(message.content)
167+
tool_calls = json.loads(message.text)
166168
except json.JSONDecodeError:
167-
raise ValueError("Invalid JSON content, expected OpenAI tools message.", message.content)
169+
raise ValueError("Invalid JSON content, expected OpenAI tools message.", message.text)
168170

169171
for tool_call in tool_calls:
170172
# this should never happen, but just in case do a sanity check
171173
if "type" not in tool_call:
172-
raise ValueError("Message payload doesn't seem to be a tool invocation descriptor", message.content)
174+
raise ValueError("Message payload doesn't seem to be a tool invocation descriptor", message.text)
173175

174176
# In OpenAPIServiceConnector we know how to handle functions tools only
175177
if tool_call["type"] == "function":

haystack/components/generators/openai.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,7 @@ def run(
240240
for response in completions:
241241
self._check_finish_reason(response)
242242

243-
return {
244-
"replies": [message.content for message in completions],
245-
"meta": [message.meta for message in completions],
246-
}
243+
return {"replies": [message.text for message in completions], "meta": [message.meta for message in completions]}
247244

248245
@staticmethod
249246
def _create_message_from_chunks(

haystack/components/generators/openai_utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ def _convert_message_to_openai_format(message: ChatMessage) -> Dict[str, str]:
1818
- `content`
1919
- `name` (optional)
2020
"""
21-
openai_msg = {"role": message.role.value, "content": message.content}
21+
if message.text is None:
22+
raise ValueError(f"The provided ChatMessage has no text. ChatMessage: {message}")
23+
24+
openai_msg = {"role": message.role.value, "content": message.text}
2225
if message.name:
2326
openai_msg["name"] = message.name
2427

haystack/components/validators/json_schema.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,18 +141,20 @@ def run(
141141
dictionaries.
142142
"""
143143
last_message = messages[-1]
144-
if not is_valid_json(last_message.content):
144+
if last_message.text is None:
145+
raise ValueError(f"The provided ChatMessage has no text. ChatMessage: {last_message}")
146+
if not is_valid_json(last_message.text):
145147
return {
146148
"validation_error": [
147149
ChatMessage.from_user(
148-
f"The message '{last_message.content}' is not a valid JSON object. "
150+
f"The message '{last_message.text}' is not a valid JSON object. "
149151
f"Please provide only a valid JSON object in string format."
150152
f"Don't use any markdown and don't add any comment."
151153
)
152154
]
153155
}
154156

155-
last_message_content = json.loads(last_message.content)
157+
last_message_content = json.loads(last_message.text)
156158
json_schema = json_schema or self.json_schema
157159
error_template = error_template or self.error_template or self.default_error_template
158160

@@ -182,16 +184,11 @@ def run(
182184
error_template = error_template or self.default_error_template
183185

184186
recovery_prompt = self._construct_error_recovery_message(
185-
error_template,
186-
str(e),
187-
error_path,
188-
error_schema_path,
189-
validation_schema,
190-
failing_json=last_message.content,
187+
error_template, str(e), error_path, error_schema_path, validation_schema, failing_json=last_message.text
191188
)
192189
return {"validation_error": [ChatMessage.from_user(recovery_prompt)]}
193190

194-
def _construct_error_recovery_message(
191+
def _construct_error_recovery_message( # pylint: disable=too-many-positional-arguments
195192
self,
196193
error_template: str,
197194
error_message: str,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
enhancements:
3+
- |
4+
Replace usage of `ChatMessage.content` with `ChatMessage.text` across the codebase.
5+
This is done in preparation for the removal of `content` in Haystack 2.9.0.

test/components/builders/test_chat_prompt_builder.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def test_init(self):
1818
]
1919
)
2020
assert builder.required_variables == []
21-
assert builder.template[0].content == "This is a {{ variable }}"
22-
assert builder.template[1].content == "This is a {{ variable2 }}"
21+
assert builder.template[0].text == "This is a {{ variable }}"
22+
assert builder.template[1].text == "This is a {{ variable2 }}"
2323
assert builder._variables is None
2424
assert builder._required_variables is None
2525

@@ -62,7 +62,7 @@ def test_init_with_required_variables(self):
6262
template=[ChatMessage.from_user("This is a {{ variable }}")], required_variables=["variable"]
6363
)
6464
assert builder.required_variables == ["variable"]
65-
assert builder.template[0].content == "This is a {{ variable }}"
65+
assert builder.template[0].text == "This is a {{ variable }}"
6666
assert builder._variables is None
6767
assert builder._required_variables == ["variable"]
6868

@@ -84,7 +84,7 @@ def test_init_with_custom_variables(self):
8484
builder = ChatPromptBuilder(template=template, variables=variables)
8585
assert builder.required_variables == []
8686
assert builder._variables == variables
87-
assert builder.template[0].content == "Hello, {{ var1 }}, {{ var2 }}!"
87+
assert builder.template[0].text == "Hello, {{ var1 }}, {{ var2 }}!"
8888
assert builder._required_variables is None
8989

9090
# we have inputs that contain: template, template_variables + variables

test/components/connectors/test_openapi_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def test_run_with_mix_params_request_body(self, openapi_mock, test_files_path):
182182
# verify call went through on the wire
183183
mock_service.call_greet.assert_called_once_with(parameters={"name": "John"}, data={"message": "Hello"})
184184

185-
response = json.loads(result["service_response"][0].content)
185+
response = json.loads(result["service_response"][0].text)
186186
assert response == "Hello, John"
187187

188188
@patch("haystack.components.connectors.openapi_service.OpenAPI")
@@ -259,7 +259,7 @@ def test_run_with_complex_types(self, openapi_mock, test_files_path):
259259
}
260260
)
261261

262-
response = json.loads(result["service_response"][0].content)
262+
response = json.loads(result["service_response"][0].text)
263263
assert response == {"result": "accepted"}
264264

265265
@patch("haystack.components.connectors.openapi_service.OpenAPI")

test/components/generators/chat/test_azure.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def test_live_run(self):
113113
results = component.run(chat_messages)
114114
assert len(results["replies"]) == 1
115115
message: ChatMessage = results["replies"][0]
116-
assert "Paris" in message.content
116+
assert "Paris" in message.text
117117
assert "gpt-4o-mini" in message.meta["model"]
118118
assert message.meta["finish_reason"] == "stop"
119119

0 commit comments

Comments
 (0)