Repository: griptape-ai/griptape (language: python)

Issue:
SummaryConversationMemory displays summary when using Streaming
- [x] I have read and agree to the [contributing guidelines](https://github.com/griptape-ai/griptape#contributing).

**Describe the bug**
When using SummaryConversationMemory and Streaming, the output will contain the summary of the conversation, even though it doesn't end up in the conversation memory runs.

This gets displayed to the user as something like:

"The human introduced themselves and the assistant responded".

**To Reproduce**

It happens both with Chat and with the Stream utility. The extra information isn't showing up in the logs though.

Here's how to reproduce it with streaming:

```python
from griptape.memory.structure import SummaryConversationMemory
from griptape.structures import Agent
from griptape.utils import Stream

conversation_memory = SummaryConversationMemory(max_runs=2, autoprune=True)
agent = Agent(conversation_memory=conversation_memory, stream=True)

for artifact in Stream(agent).run("hi"):
    print(artifact.value, end="", flush=True)

for artifact in Stream(agent).run("My name is jason, what's yours?"):
    print(artifact.value, end="", flush=True)
```


You'll get output like:

```bash
[12/13/24 21:53:04] INFO     PromptTask f8faec278598460dac5cd76c72190bc0
                             Input: hi
Hello! How can I assist you today?[12/13/24 21:53:05] INFO     PromptTask f8faec278598460dac5cd76c72190bc0              

                             Output: Hello! How can I assist you today?

                    INFO     PromptTask f8faec278598460dac5cd76c72190bc0
                             Input: My name is jason, what's yours?
Hi Jason! I don't have a personal name, but you can call me Assistant. How can I help you today?[12/13/24 21:53:06] INFO
     PromptTask f8faec278598460dac5cd76c72190bc0
                             Output: Hi Jason! I don't have a personal name, but you can call me Assistant. How can I   
                             help you today?

The human greeted the assistant with "hi," and the assistant responded with a friendly "Hello!" and asked how they could assist the human.
```

The organization is a bit messy, but you can see the Output doesn't have the extra message, but it comes through when streaming. If I set logging to ERROR only, you get:

```bash
Hello! How can I assist you today?
Nice to meet you, Jason! I don't have a personal name, but you can call me Assistant. How can I help you today?
The human greeted the assistant with "hi," and the assistant responded with a friendly "Hello!" and asked how they could assist the human.
```

**Expected behavior**
The summary shouldn't be output back to the user




Discussion / hints:
`SummaryConversationMemory` [uses the default prompt driver](https://github.com/griptape-ai/griptape/blob/main/griptape/memory/structure/summary_conversation_memory.py#L69-L71) to summarize, which is sending streaming events back. might be able to disable streaming for this particular use of the prompt driver?

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5d81a27aa..fe7b7707b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,10 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## Unreleased
 
-## Fixed
-
-- Exception when calling `Structure.to_json()` after it has run.
-
 ### Added
 
 - `PromptTask.conversation_memory` for setting the Conversation Memory on a Prompt Task.
@@ -19,6 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - `BranchTask` for selecting which Tasks (if any) to run based on a condition.
 - Support for `BranchTask` in `StructureVisualizer`.
 
+### Fixed
+
+- Exception when calling `Structure.to_json()` after it has run.
+- `Agent` unintentionally modifying `stream` for all Prompt Drivers.
+
 ## [1.0.0] - 2024-12-09
 
 ### Added
diff --git a/griptape/structures/agent.py b/griptape/structures/agent.py
index be1b73e34..128c02faa 100644
--- a/griptape/structures/agent.py
+++ b/griptape/structures/agent.py
@@ -2,7 +2,7 @@
 
 from typing import TYPE_CHECKING, Callable, Optional, Union
 
-from attrs import Attribute, Factory, define, field
+from attrs import Attribute, Factory, define, evolve, field
 
 from griptape.artifacts.text_artifact import TextArtifact
 from griptape.common import observable
@@ -24,7 +24,10 @@ class Agent(Structure):
     )
     stream: bool = field(default=Factory(lambda: Defaults.drivers_config.prompt_driver.stream), kw_only=True)
     prompt_driver: BasePromptDriver = field(
-        default=Factory(lambda: Defaults.drivers_config.prompt_driver), kw_only=True
+        default=Factory(
+            lambda self: evolve(Defaults.drivers_config.prompt_driver, stream=self.stream), takes_self=True
+        ),
+        kw_only=True,
     )
     tools: list[BaseTool] = field(factory=list, kw_only=True)
     max_meta_memory_entries: Optional[int] = field(default=20, kw_only=True)


Test patch:
diff --git a/tests/unit/structures/test_agent.py b/tests/unit/structures/test_agent.py
index 27211f29e..387910f40 100644
--- a/tests/unit/structures/test_agent.py
+++ b/tests/unit/structures/test_agent.py
@@ -276,3 +276,11 @@ def test_is_running(self):
         task.state = BaseTask.State.RUNNING
 
         assert agent.is_running()
+
+    def test_stream_mutation(self):
+        prompt_driver = MockPromptDriver()
+        agent = Agent(prompt_driver=MockPromptDriver(), stream=True)
+
+        assert isinstance(agent.tasks[0], PromptTask)
+        assert agent.tasks[0].prompt_driver.stream is True
+        assert agent.tasks[0].prompt_driver is not prompt_driver