Repository: deepset-ai/haystack
Pull request: feat: Extend AnswerBuilder for Agent
## Why:

Enhances the AnswerBuilder component to preserve conversation history when processing Agent outputs.
- fixes https://github.com/deepset-ai/haystack/issues/9330

- Addresses the need to maintain agent message context in GeneratedAnswer objects
- Enables seamless integration between Agent components and the AnswerBuilder

## What:
- Updates AnswerBuilder to store entire conversation history in metadata
- Adds "all_messages" field to GeneratedAnswer metadata containing all messages
- Preserves backward compatibility with existing pipelines
- Updates tests to verify the new functionality

## How can it be used:
Connect Agent and AnswerBuilder in a pipeline to maintain conversation context:

```python
from haystack import Pipeline
from haystack.components.builders import AnswerBuilder
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator

# Create components
agent = Agent(chat_generator=OpenAIChatGenerator())
answer_builder = AnswerBuilder()

# Create pipeline
pipeline = Pipeline()
pipeline.add_component("agent", agent)
pipeline.add_component("answer_builder", answer_builder)

# Connect components
pipeline.connect("agent.messages", "answer_builder.replies")

# Run the pipeline
result = pipeline.run(data={"agent": {"messages": [user_message]}})

# Conversation history is preserved in the answer metadata
conversation_history = result["answer_builder"]["answers"][0].meta["all_messages"]
```

## How did you test it:
- Unit tests verify that conversation history is properly stored in metadata
- Added helper function to test metadata while ignoring the new field
- Tests ensure backward compatibility with existing code



Problem statement:
Extend AnswerBuilder to convert only last message for agentic pipelines
**Is your feature request related to a problem? Please describe.**
As most agents are expected to be run as a Chat application, returning exactly one answer per interaction/request/pipeline.run seems to be a defacto pattern.
The current `Agent` always returns all messages that have been accumulated during the agent invocation. To render a final answer (e.g. for end users), in most cases, we are only interested in the last message. Currently we need to use an OutputAdapter for this, which makes standard Agentic pipelines more complex than necessary.

**Describe the solution you'd like**
Add a flag to `AnswerBuilder` which allows to select only the last message to be converted as Answer.
Optional: Set any other messages to Answer's meta (e.g. for tool call inspections)

**Describe alternatives you've considered**
Adding the flag to `Agent` would come with the downside that any tool call / iteration messages will be lost.

**Additional context**
Add any other context or screenshots about the feature request here.

Hints from issue discussion:
Maybe an alternative or in addition to this could be to add an additional output to the Agent called final_message of type ChatMessage which always contains the last message? 

@bilgeyucel also mentioned for standalone uses of Agent that this would be helpful. 
Agree with @sjrl here. Additionally, Haystack users don't use `AnswerBuilder` as they get a similar benefit through Pipeline's `include_outputs_from` parameter. Handling this in `AnswerBuilder` would force users to add AnswerBuilder to their pipeline
Interesting, I thought about this briefly too, but rejected it because 
- it doesn't allow one to add additional information from the rest of the messages, e.g. tool calls. For this you'd need all messages again. AnswerParser could take care of this, e.g. by filling Answer's meta fields with tool call info.
- when using AnswerBuilder, returning a single message would require an OutputAdapter again, as AnswerBuilder expects list[Message] (not a single Message).
> Interesting, I thought about this briefly too, but rejected it because
> 
> * it doesn't allow one to add additional information from the rest of the messages, e.g. tool calls. For this you'd need all messages again. AnswerParser could take care of this, e.g. by filling Answer's meta fields with tool call info.
> * when using AnswerBuilder, returning a single message would require an OutputAdapter again, as AnswerBuilder expects list[Message] (not a single Message).

That's fair! I think it might be worth doing all of the following:
- Update AnswerBuilder to convert last message from Agent and put the rest into metadata for the reasons @tstadel outlined
- Add a `final_message` of type ChatMessage (or `final_answer` of type string) output to Agent. This should be helpful for users who want to 1) use the component standalone 2) easily connect the final_message as input to another component other than AnswerBuilder in a Pipeline and 3) use Agent as a tool in ComponentTool where usually you'd want just the final answer as the tool output. 
Hey @tstadel @sjrl, I sketched out what needs to be done in AnswerBuilder and it’s turning it into a complicated beast—right now AnswerBuilder verifies that every chat message contains valid text and throws a ValueError when it doesn’t and this is going to be problematic for agent messages that have tool calls and responses. We are mixing a lot of domain logic here, all the way from RAG, pattern matching, reference creation, and now on top of this we are pulling in agent logic into AnswerBuilder. We’d be better off spinning up a separate AgentAnswerBuilder that can look at the agent’s state and handle everything needed to produce the answer, however it needs to look. LMK

Commit message: Add a better unit test

Patch to review:
diff --git a/haystack/components/builders/answer_builder.py b/haystack/components/builders/answer_builder.py
index 9a465cafc3..20f3754611 100644
--- a/haystack/components/builders/answer_builder.py
+++ b/haystack/components/builders/answer_builder.py
@@ -31,7 +31,9 @@ class AnswerBuilder:
     ```
     """
 
-    def __init__(self, pattern: Optional[str] = None, reference_pattern: Optional[str] = None):
+    def __init__(
+        self, pattern: Optional[str] = None, reference_pattern: Optional[str] = None, last_message_only: bool = False
+    ):
         """
         Creates an instance of the AnswerBuilder component.
 
@@ -50,12 +52,17 @@ def __init__(self, pattern: Optional[str] = None, reference_pattern: Optional[st
             If not specified, no parsing is done, and all documents are referenced.
             References need to be specified as indices of the input documents and start at [1].
             Example: `\\[(\\d+)\\]` finds "1" in a string "this is an answer[1]".
+
+        :param last_message_only:
+            If True, only the last message is used as the answer.
+            If False, all messages are used as the answer.
         """
         if pattern:
             AnswerBuilder._check_num_groups_in_regex(pattern)
 
         self.pattern = pattern
         self.reference_pattern = reference_pattern
+        self.last_message_only = last_message_only
 
     @component.output_types(answers=List[GeneratedAnswer])
     def run(  # pylint: disable=too-many-positional-arguments
@@ -111,17 +118,26 @@ def run(  # pylint: disable=too-many-positional-arguments
         pattern = pattern or self.pattern
         reference_pattern = reference_pattern or self.reference_pattern
         all_answers = []
-        for reply, given_metadata in zip(replies, meta):
+
+        replies_to_iterate = replies
+        meta_to_iterate = meta
+
+        if self.last_message_only and replies:
+            replies_to_iterate = replies[-1:]
+            meta_to_iterate = meta[-1:]
+
+        for reply, given_metadata in zip(replies_to_iterate, meta_to_iterate):
             # Extract content from ChatMessage objects if reply is a ChatMessages, else use the string as is
             if isinstance(reply, ChatMessage):
-                if reply.text is None:
-                    raise ValueError(f"The provided ChatMessage has no text. ChatMessage: {reply}")
-                extracted_reply = reply.text
+                extracted_reply = ""
+                if reply.text:
+                    extracted_reply = reply.text
             else:
                 extracted_reply = str(reply)
             extracted_metadata = reply.meta if isinstance(reply, ChatMessage) else {}
 
             extracted_metadata = {**extracted_metadata, **given_metadata}
+            extracted_metadata["all_messages"] = replies
 
             referenced_docs = []
             if documents:
diff --git a/releasenotes/notes/adjust-answer-builder-for-agent-f4428aea59e32809.yaml b/releasenotes/notes/adjust-answer-builder-for-agent-f4428aea59e32809.yaml
new file mode 100644
index 0000000000..2c3b695613
--- /dev/null
+++ b/releasenotes/notes/adjust-answer-builder-for-agent-f4428aea59e32809.yaml
@@ -0,0 +1,4 @@
+---
+enhancements:
+  - |
+    The AnswerBuilder component now stores all generated messages in the `meta` field of the GeneratedAnswer objects under an `all_messages` key. This enhancement improves traceability and debugging capabilities for agent workflows by providing access to the complete history of generated messages.
diff --git a/test/components/builders/test_answer_builder.py b/test/components/builders/test_answer_builder.py
index 73f20b6d97..610cb65208 100644
--- a/test/components/builders/test_answer_builder.py
+++ b/test/components/builders/test_answer_builder.py
@@ -10,6 +10,12 @@
 from haystack.dataclasses.chat_message import ChatMessage, ChatRole
 
 
+def _check_metadata_excluding_all_messages(actual_meta, expected_meta):
+    """Helper function to check metadata while ignoring the all_messages field"""
+    actual_without_messages = {k: v for k, v in actual_meta.items() if k != "all_messages"}
+    assert actual_without_messages == expected_meta
+
+
 class TestAnswerBuilder:
     def test_run_unmatching_input_len(self):
         component = AnswerBuilder()
@@ -21,7 +27,8 @@ def test_run_without_meta(self):
         output = component.run(query="query", replies=["reply1"])
         answers = output["answers"]
         assert answers[0].data == "reply1"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta  # Check that all_messages exists
         assert answers[0].query == "query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -31,7 +38,8 @@ def test_run_meta_is_an_empty_list(self):
         output = component.run(query="query", replies=["reply1"], meta=[])
         answers = output["answers"]
         assert answers[0].data == "reply1"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -41,7 +49,8 @@ def test_run_with_meta(self):
         output = component.run(query="query", replies=["reply1"], meta=[{"test": "meta"}])
         answers = output["answers"]
         assert answers[0].data == "reply1"
-        assert answers[0].meta == {"test": "meta"}
+        _check_metadata_excluding_all_messages(answers[0].meta, {"test": "meta"})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -52,7 +61,8 @@ def test_run_without_pattern(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "Answer: AnswerString"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -63,7 +73,8 @@ def test_run_with_pattern_with_capturing_group(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "AnswerString"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -74,7 +85,8 @@ def test_run_with_pattern_without_capturing_group(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "'AnswerString'"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -89,7 +101,8 @@ def test_run_with_pattern_set_at_runtime(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "AnswerString"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -105,7 +118,8 @@ def test_run_with_documents_without_reference_pattern(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "Answer: AnswerString"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "test query"
         assert len(answers[0].documents) == 2
         assert answers[0].documents[0].content == "test doc 1"
@@ -122,7 +136,8 @@ def test_run_with_documents_with_reference_pattern(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "Answer: AnswerString[2]"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "test query"
         assert len(answers[0].documents) == 1
         assert answers[0].documents[0].content == "test doc 2"
@@ -139,7 +154,8 @@ def test_run_with_documents_with_reference_pattern_and_no_match(self, caplog):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "Answer: AnswerString[3]"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "test query"
         assert len(answers[0].documents) == 0
         assert "Document index '3' referenced in Generator output is out of range." in caplog.text
@@ -156,7 +172,8 @@ def test_run_with_reference_pattern_set_at_runtime(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "Answer: AnswerString[2][3]"
-        assert answers[0].meta == {}
+        _check_metadata_excluding_all_messages(answers[0].meta, {})
+        assert "all_messages" in answers[0].meta
         assert answers[0].query == "test query"
         assert len(answers[0].documents) == 2
         assert answers[0].documents[0].content == "test doc 2"
@@ -177,7 +194,12 @@ def test_run_with_chat_message_replies_without_pattern(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "Answer: AnswerString"
-        assert answers[0].meta == message_meta
+
+        # Check metadata excluding all_messages
+        expected_meta = message_meta.copy()
+        _check_metadata_excluding_all_messages(answers[0].meta, expected_meta)
+        assert "all_messages" in answers[0].meta
+
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -197,7 +219,12 @@ def test_run_with_chat_message_replies_with_pattern(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "AnswerString"
-        assert answers[0].meta == message_meta
+
+        # Check metadata excluding all_messages
+        expected_meta = message_meta.copy()
+        _check_metadata_excluding_all_messages(answers[0].meta, expected_meta)
+        assert "all_messages" in answers[0].meta
+
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -221,7 +248,12 @@ def test_run_with_chat_message_replies_with_documents(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "Answer: AnswerString[2]"
-        assert answers[0].meta == message_meta
+
+        # Check metadata excluding all_messages
+        expected_meta = message_meta.copy()
+        _check_metadata_excluding_all_messages(answers[0].meta, expected_meta)
+        assert "all_messages" in answers[0].meta
+
         assert answers[0].query == "test query"
         assert len(answers[0].documents) == 1
         assert answers[0].documents[0].content == "test doc 2"
@@ -240,7 +272,12 @@ def test_run_with_chat_message_replies_with_pattern_set_at_runtime(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "AnswerString"
-        assert answers[0].meta == message_meta
+
+        # Check metadata excluding all_messages
+        expected_meta = message_meta.copy()
+        _check_metadata_excluding_all_messages(answers[0].meta, expected_meta)
+        assert "all_messages" in answers[0].meta
+
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -259,13 +296,18 @@ def test_run_with_chat_message_replies_with_meta_set_at_run_time(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "AnswerString"
-        assert answers[0].meta == {
+
+        # Check metadata excluding all_messages
+        expected_meta = {
             "model": "gpt-4o-mini",
             "index": 0,
             "finish_reason": "stop",
             "usage": {"prompt_tokens": 32, "completion_tokens": 153, "total_tokens": 185},
             "test": "meta",
         }
+        _check_metadata_excluding_all_messages(answers[0].meta, expected_meta)
+        assert "all_messages" in answers[0].meta
+
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
@@ -278,7 +320,56 @@ def test_run_with_chat_message_no_meta_with_meta_set_at_run_time(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "AnswerString"
-        assert answers[0].meta == {"test": "meta"}
+
+        # Check metadata excluding all_messages
+        _check_metadata_excluding_all_messages(answers[0].meta, {"test": "meta"})
+        assert "all_messages" in answers[0].meta
+
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
+
+    @pytest.mark.parametrize("last_message_only", [True, False])
+    def test_conversation_history_in_all_messages(self, last_message_only):
+        """Test behavior with last_message_only flag"""
+
+        # Test with two messages in replies
+        component = AnswerBuilder(last_message_only=last_message_only)
+        replies = [
+            ChatMessage.from_user("What is Haystack?"),
+            ChatMessage.from_assistant("Haystack is a framework for building LLM applications."),
+        ]
+        output = component.run(query="test query", replies=replies)
+
+        answers = output["answers"]
+
+        if last_message_only:
+            assert len(answers) == 1  # Only one answer when last_message_only=True
+            # Check that the answer is the last message
+            assert answers[0].data == "Haystack is a framework for building LLM applications."
+        else:
+            assert len(answers) == 2  # One answer for each message in replies
+            assert answers[0].data == "What is Haystack?"
+            assert answers[1].data == "Haystack is a framework for building LLM applications."
+
+        # Check that each answer contains the full conversation history
+        for answer in answers:
+            assert "all_messages" in answer.meta
+            assert len(answer.meta["all_messages"]) == 2
+            assert answer.meta["all_messages"] == replies
+
+        # Test with one message in replies
+        component = AnswerBuilder(last_message_only=last_message_only)
+        replies = [ChatMessage.from_user("What is Haystack?")]
+        output = component.run(query="test query", replies=replies)
+
+        answers = output["answers"]
+        assert len(answers) == 1  # Always one answer when there's only one message
+
+        # Check that the answer is the message
+        assert answers[0].data == "What is Haystack?"
+
+        # Check that the conversation history is stored in all_messages
+        assert "all_messages" in answers[0].meta
+        assert answers[0].meta["all_messages"] == replies
+        assert len(answers[0].meta["all_messages"]) == 1
diff --git a/test/core/pipeline/features/test_run.py b/test/core/pipeline/features/test_run.py
index 2a088be309..7ee32dbf6e 100644
--- a/test/core/pipeline/features/test_run.py
+++ b/test/core/pipeline/features/test_run.py
@@ -942,7 +942,7 @@ def pipeline_that_has_a_component_with_only_default_inputs(pipeline_class):
                                         score=1.2536639934227616,
                                     ),
                                 ],
-                                meta={},
+                                meta={"all_messages": ["Paris"]},
                             )
                         ]
                     }
@@ -2711,13 +2711,13 @@ def that_has_an_answer_joiner_variadic_component(pipeline_class):
                                 data="This is a test answer",
                                 query="What's Natural Language Processing?",
                                 documents=[],
-                                meta={},
+                                meta={"all_messages": ["This is a test answer"]},
                             ),
                             GeneratedAnswer(
                                 data="This is a second test answer",
                                 query="What's Natural Language Processing?",
                                 documents=[],
-                                meta={},
+                                meta={"all_messages": ["This is a second test answer"]},
                             ),
                         ]
                     }
@@ -2746,7 +2746,7 @@ def that_has_an_answer_joiner_variadic_component(pipeline_class):
                                     data="This is a test answer",
                                     query="What's Natural Language Processing?",
                                     documents=[],
-                                    meta={},
+                                    meta={"all_messages": ["This is a test answer"]},
                                 )
                             ],
                             [
@@ -2754,7 +2754,7 @@ def that_has_an_answer_joiner_variadic_component(pipeline_class):
                                     data="This is a second test answer",
                                     query="What's Natural Language Processing?",
                                     documents=[],
-                                    meta={},
+                                    meta={"all_messages": ["This is a second test answer"]},
                                 )
                             ],
                         ],
@@ -2999,7 +2999,11 @@ def run(self, prompt: str):
                     "answer_builder": {"query": question},
                 },
                 expected_outputs={
-                    "answer_builder": {"answers": [GeneratedAnswer(data="42", query=question, documents=[])]}
+                    "answer_builder": {
+                        "answers": [
+                            GeneratedAnswer(data="42", query=question, documents=[], meta={"all_messages": ["42"]})
+                        ]
+                    }
                 },
                 expected_component_calls={
                     ("answer_builder", 1): {
@@ -3454,7 +3458,14 @@ def run(self, query: str):
                 },
                 expected_outputs={
                     "answer_builder": {
-                        "answers": [GeneratedAnswer(data="answer: here is my answer", query=query, documents=[])]
+                        "answers": [
+                            GeneratedAnswer(
+                                data="answer: here is my answer",
+                                query=query,
+                                documents=[],
+                                meta={"all_messages": ["answer: here is my answer"]},
+                            )
+                        ]
                     }
                 },
                 expected_component_calls={
@@ -3676,7 +3687,13 @@ def run(self, prompt: str):
             PipelineRunData(
                 inputs={"code_prompt": {"task": task}, "answer_builder": {"query": task}},
                 expected_outputs={
-                    "answer_builder": {"answers": [GeneratedAnswer(data="valid code", query=task, documents=[])]}
+                    "answer_builder": {
+                        "answers": [
+                            GeneratedAnswer(
+                                data="valid code", query=task, documents=[], meta={"all_messages": ["valid code"]}
+                            )
+                        ]
+                    }
                 },
                 expected_component_calls={
                     ("answer_builder", 1): {
@@ -3841,7 +3858,13 @@ def run(self, prompt: str):
             PipelineRunData(
                 inputs={"code_prompt": {"task": task}, "answer_builder": {"query": task}},
                 expected_outputs={
-                    "answer_builder": {"answers": [GeneratedAnswer(data="valid code", query=task, documents=[])]}
+                    "answer_builder": {
+                        "answers": [
+                            GeneratedAnswer(
+                                data="valid code", query=task, documents=[], meta={"all_messages": ["valid code"]}
+                            )
+                        ]
+                    }
                 },
                 expected_component_calls={
                     ("answer_builder", 1): {
@@ -4673,7 +4696,7 @@ def run(self, prompt: str):
     class AnswerBuilderWithPrompt:
         @component.output_types(answers=List[GeneratedAnswer])
         def run(self, replies: List[str], query: str, prompt: Optional[str] = None) -> Dict[str, Any]:
-            answer = GeneratedAnswer(data=replies[0], query=query, documents=[])
+            answer = GeneratedAnswer(data=replies[0], query=query, documents=[], meta={"all_messages": replies})
 
             if prompt is not None:
                 answer.meta["prompt"] = prompt
@@ -4767,7 +4790,10 @@ def generate_santa_sleigh():
                     "answer_builder": {
                         "answers": [
                             GeneratedAnswer(
-                                data=valid_response, query=task, documents=[], meta={"prompt": expected_prompt}
+                                data=valid_response,
+                                query=task,
+                                documents=[],
+                                meta={"prompt": expected_prompt, "all_messages": [valid_response]},
                             )
                         ]
                     }


Code review comments:
diff_hunk: @@ -278,7 +320,56 @@ def test_run_with_chat_message_no_meta_with_meta_set_at_run_time(self):
         answers = output["answers"]
         assert len(answers) == 1
         assert answers[0].data == "AnswerString"
-        assert answers[0].meta == {"test": "meta"}
+
+        # Check metadata excluding all_messages
+        _check_metadata_excluding_all_messages(answers[0].meta, {"test": "meta"})
+        assert "all_messages" in answers[0].meta
+
         assert answers[0].query == "test query"
         assert answers[0].documents == []
         assert isinstance(answers[0], GeneratedAnswer)
+
+    @pytest.mark.parametrize("last_message_only", [True, False])
path: test/components/builders/test_answer_builder.py
text: @user1:
nit: instead of parametrizing, I would find it clearer to have two separate test cases

diff_hunk: @@ -111,17 +118,26 @@ def run(  # pylint: disable=too-many-positional-arguments
         pattern = pattern or self.pattern
         reference_pattern = reference_pattern or self.reference_pattern
         all_answers = []
-        for reply, given_metadata in zip(replies, meta):
+
+        replies_to_iterate = replies
+        meta_to_iterate = meta
+
+        if self.last_message_only and replies:
+            replies_to_iterate = replies[-1:]
+            meta_to_iterate = meta[-1:]
+
+        for reply, given_metadata in zip(replies_to_iterate, meta_to_iterate):
             # Extract content from ChatMessage objects if reply is a ChatMessages, else use the string as is
             if isinstance(reply, ChatMessage):
-                if reply.text is None:
-                    raise ValueError(f"The provided ChatMessage has no text. ChatMessage: {reply}")
-                extracted_reply = reply.text
+                extracted_reply = ""
+                if reply.text:
+                    extracted_reply = reply.text
path: haystack/components/builders/answer_builder.py
text: @user1:
```suggestion
                extracted_reply = reply.text or ""
```