Repository: griptape-ai/griptape

Issue:
Incorrect Deprecation Warning
```python
from griptape.structures import Agent

agent = Agent()

agent.run()
```

```
/Users/collindutter/Documents/griptape/griptape-playground/.venv/lib/python3.12/site-packages/attr/setters.py:51: UserWarning: `Agent.prompt_driver` is set, but `Agent.stream` was provided. `Agent.stream` will be ignored. This will be an error in the future.
  v(instance, attrib, new_value)
[03/04/25 15:08:38] INFO     PromptTask ea9935f4e91d4e108c1ae1f2bf42c48c
                             Input:
[03/04/25 15:08:39] INFO     PromptTask ea9935f4e91d4e108c1ae1f2bf42c48c
                             Output: Hello! How can I assist you today?
```

Discussion:

Solution patch:
diff --git a/griptape/structures/agent.py b/griptape/structures/agent.py
index f301b2af..94a94c91 100644
--- a/griptape/structures/agent.py
+++ b/griptape/structures/agent.py
@@ -3,14 +3,13 @@ from __future__ import annotations
 import warnings
 from typing import TYPE_CHECKING, Callable, Optional, Union
 
-from attrs import Attribute, define, evolve, field
+from attrs import Attribute, define, evolve, field, validators
 
 from griptape.artifacts.text_artifact import TextArtifact
 from griptape.common import observable
 from griptape.configs import Defaults
 from griptape.structures import Structure
 from griptape.tasks import PromptTask
-from griptape.utils.decorators import lazy_property
 
 if TYPE_CHECKING:
     from pydantic import BaseModel
@@ -27,8 +26,8 @@ class Agent(Structure):
     input: Union[str, list, tuple, BaseArtifact, Callable[[BaseTask], BaseArtifact]] = field(
         default=lambda task: task.full_context["args"][0] if task.full_context["args"] else TextArtifact(value=""),
     )
-    _stream: Optional[bool] = field(default=None, kw_only=True, alias="stream")
-    _prompt_driver: Optional[BasePromptDriver] = field(default=None, kw_only=True, alias="prompt_driver")
+    stream: Optional[bool] = field(default=None, kw_only=True)
+    prompt_driver: Optional[BasePromptDriver] = field(default=None, kw_only=True)
     output_schema: Optional[Union[Schema, type[BaseModel]]] = field(default=None, kw_only=True)
     tools: list[BaseTool] = field(factory=list, kw_only=True)
     max_meta_memory_entries: Optional[int] = field(default=20, kw_only=True)
@@ -37,20 +36,12 @@ class Agent(Structure):
         factory=list, kw_only=True, alias="tasks", metadata={"serializable": True}
     )
 
-    @lazy_property()
-    def prompt_driver(self) -> BasePromptDriver:
-        return evolve(Defaults.drivers_config.prompt_driver, stream=self.stream)
-
-    @lazy_property()
-    def stream(self) -> bool:
-        return Defaults.drivers_config.prompt_driver.stream
-
     @fail_fast.validator  # pyright: ignore[reportAttributeAccessIssue]
     def validate_fail_fast(self, _: Attribute, fail_fast: bool) -> None:  # noqa: FBT001
         if fail_fast:
             raise ValueError("Agents cannot fail fast, as they can only have 1 task.")
 
-    @_prompt_driver.validator  # pyright: ignore[reportAttributeAccessIssue, reportOptionalMemberAccess]
+    @prompt_driver.validator  # pyright: ignore[reportAttributeAccessIssue, reportOptionalMemberAccess]
     def validate_prompt_driver(self, _: Attribute, prompt_driver: Optional[BasePromptDriver]) -> None:  # noqa: FBT001
         if prompt_driver is not None and self.stream is not None:
             warnings.warn(
@@ -99,9 +90,20 @@ class Agent(Structure):
         return self
 
     def _init_task(self) -> None:
+        if self.stream is None:
+            with validators.disabled():
+                self.stream = Defaults.drivers_config.prompt_driver.stream
+
+        if self.prompt_driver is None:
+            with validators.disabled():
+                prompt_driver = evolve(Defaults.drivers_config.prompt_driver, stream=self.stream)
+                self.prompt_driver = prompt_driver
+        else:
+            prompt_driver = self.prompt_driver
+
         task = PromptTask(
             self.input,
-            prompt_driver=self.prompt_driver,
+            prompt_driver=prompt_driver,
             tools=self.tools,
             output_schema=self.output_schema,
             max_meta_memory_entries=self.max_meta_memory_entries,


Test patch:
diff --git a/tests/unit/structures/test_agent.py b/tests/unit/structures/test_agent.py
index 442f654d..e349d51c 100644
--- a/tests/unit/structures/test_agent.py
+++ b/tests/unit/structures/test_agent.py
@@ -1,3 +1,4 @@
+import warnings
 from unittest.mock import Mock
 
 import pytest
@@ -295,6 +296,16 @@ class TestAgent:
         with pytest.warns(UserWarning, match="`Agent.tasks` is set, but `Agent.prompt_driver` was provided."):
             Agent(prompt_driver=MockPromptDriver(), tasks=[PromptTask()])
 
+    def test_valid_agent_no_warnings(self):
+        with warnings.catch_warnings():
+            warnings.simplefilter("error")
+            assert Agent()
+            assert Agent(
+                tasks=[PromptTask()],
+            )
+            assert Agent(stream=True)
+            assert Agent(prompt_driver=MockPromptDriver())
+
     def test_field_hierarchy(self):
         # Test that stream on its own propagates to the task.
         agent = Agent(stream=True)