Repository: griptape-ai/griptape

Issue:
Reduce `max_attempts` default value
- [x] I have read and agree to the [contributing guidelines](https://github.com/griptape-ai/griptape#contributing).

Currently `max_attempts` is set to 10, which can make it appear that the framework is hung when it's really just trying something a number of times.

Would like to discuss reducing the default number to 2 or 3. Usually if something is going to fail, one or two retries is fine - and if the user needs more they can always increase it.

Discussion:
@collindutter: I wonder if we should retry at all? Should it be opt-in?@shhlife: good point, maybe not? Usually things fail due to something that retrying wouldn't resolve.. like an invalid api key.@collindutter: Or maybe we just need to refine the [ignored exception types](https://github.com/griptape-ai/griptape/blob/36d5d2c0c22ffabc85b7e017952582905ee94554/griptape/drivers/prompt/openai_chat_prompt_driver.py?plain=1#L84-L96).@shhlife: yeah, that would help :)

Solution patch:
diff --git a/docs/examples/src/multiple_agent_shared_memory_1.py b/docs/examples/src/multiple_agent_shared_memory_1.py
index 518a632d..333532a3 100644
--- a/docs/examples/src/multiple_agent_shared_memory_1.py
+++ b/docs/examples/src/multiple_agent_shared_memory_1.py
@@ -19,7 +19,6 @@ MONGODB_INDEX_NAME = os.environ["MONGODB_INDEX_NAME"]
 MONGODB_VECTOR_PATH = os.environ["MONGODB_VECTOR_PATH"]
 MONGODB_CONNECTION_STRING = f"mongodb+srv://{MONGODB_USERNAME}:{MONGODB_PASSWORD}@{MONGODB_HOST}/{MONGODB_DATABASE_NAME}?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000"
 
-
 embedding_driver = AzureOpenAiEmbeddingDriver(
     model="text-embedding-ada-002",
     azure_endpoint=AZURE_OPENAI_ENDPOINT_1,
@@ -37,6 +36,7 @@ mongo_driver = AzureMongoDbVectorStoreDriver(
 
 Defaults.drivers_config = AzureOpenAiDriversConfig(
     azure_endpoint=AZURE_OPENAI_ENDPOINT_1,
+    api_key=AZURE_OPENAI_API_KEY_1,
     vector_store_driver=mongo_driver,
     embedding_driver=embedding_driver,
 )
diff --git a/docs/griptape-framework/drivers/src/image_generation_drivers_2.py b/docs/griptape-framework/drivers/src/image_generation_drivers_2.py
index 4b72eb6a..1b088b0a 100644
--- a/docs/griptape-framework/drivers/src/image_generation_drivers_2.py
+++ b/docs/griptape-framework/drivers/src/image_generation_drivers_2.py
@@ -11,7 +11,7 @@ model_driver = BedrockStableDiffusionImageGenerationModelDriver(
 
 driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=model_driver,
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 
diff --git a/docs/griptape-framework/structures/src/tasks_12.py b/docs/griptape-framework/structures/src/tasks_12.py
index c4e58e5e..22d0ca2c 100644
--- a/docs/griptape-framework/structures/src/tasks_12.py
+++ b/docs/griptape-framework/structures/src/tasks_12.py
@@ -9,7 +9,7 @@ from griptape.tasks import VariationImageGenerationTask
 # Create a driver configured to use Stable Diffusion via Bedrock.
 driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=BedrockStableDiffusionImageGenerationModelDriver(),
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 
diff --git a/docs/griptape-framework/structures/src/tasks_13.py b/docs/griptape-framework/structures/src/tasks_13.py
index cb50e761..ff3ae776 100644
--- a/docs/griptape-framework/structures/src/tasks_13.py
+++ b/docs/griptape-framework/structures/src/tasks_13.py
@@ -9,7 +9,7 @@ from griptape.tasks import InpaintingImageGenerationTask
 # Create a driver configured to use Stable Diffusion via Bedrock.
 driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=BedrockStableDiffusionImageGenerationModelDriver(),
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 # Load input image artifacts.
diff --git a/docs/griptape-framework/structures/src/tasks_14.py b/docs/griptape-framework/structures/src/tasks_14.py
index 38cf145f..c6f79bd3 100644
--- a/docs/griptape-framework/structures/src/tasks_14.py
+++ b/docs/griptape-framework/structures/src/tasks_14.py
@@ -9,7 +9,7 @@ from griptape.tasks import OutpaintingImageGenerationTask
 # Create a driver configured to use Stable Diffusion via Bedrock.
 driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=BedrockStableDiffusionImageGenerationModelDriver(),
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 
diff --git a/docs/griptape-framework/tools/official-tools/src/inpainting_image_generation_tool_1.py b/docs/griptape-framework/tools/official-tools/src/inpainting_image_generation_tool_1.py
index a5478ab9..69dcec8b 100644
--- a/docs/griptape-framework/tools/official-tools/src/inpainting_image_generation_tool_1.py
+++ b/docs/griptape-framework/tools/official-tools/src/inpainting_image_generation_tool_1.py
@@ -8,7 +8,7 @@ from griptape.tools import InpaintingImageGenerationTool
 # Create a driver configured to use Stable Diffusion via Bedrock.
 driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=BedrockStableDiffusionImageGenerationModelDriver(),
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 # Create a tool configured to use the engine.
diff --git a/docs/griptape-framework/tools/official-tools/src/outpainting_image_generation_tool_1.py b/docs/griptape-framework/tools/official-tools/src/outpainting_image_generation_tool_1.py
index 1c3b9dd5..a6535331 100644
--- a/docs/griptape-framework/tools/official-tools/src/outpainting_image_generation_tool_1.py
+++ b/docs/griptape-framework/tools/official-tools/src/outpainting_image_generation_tool_1.py
@@ -8,7 +8,7 @@ from griptape.tools import OutpaintingImageGenerationTool
 # Create a driver configured to use Stable Diffusion via Bedrock.
 driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=BedrockStableDiffusionImageGenerationModelDriver(),
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 # Create a tool configured to use the engine.
diff --git a/docs/griptape-framework/tools/official-tools/src/prompt_image_generation_tool_1.py b/docs/griptape-framework/tools/official-tools/src/prompt_image_generation_tool_1.py
index 434d883f..952554cf 100644
--- a/docs/griptape-framework/tools/official-tools/src/prompt_image_generation_tool_1.py
+++ b/docs/griptape-framework/tools/official-tools/src/prompt_image_generation_tool_1.py
@@ -8,7 +8,7 @@ from griptape.tools import PromptImageGenerationTool
 # Create a driver configured to use Stable Diffusion via Bedrock.
 driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=BedrockStableDiffusionImageGenerationModelDriver(),
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 
diff --git a/docs/griptape-framework/tools/official-tools/src/rest_api_tool_1.py b/docs/griptape-framework/tools/official-tools/src/rest_api_tool_1.py
index cc39312d..de5f27f2 100644
--- a/docs/griptape-framework/tools/official-tools/src/rest_api_tool_1.py
+++ b/docs/griptape-framework/tools/official-tools/src/rest_api_tool_1.py
@@ -9,7 +9,7 @@ from griptape.tasks import PromptTask
 from griptape.tools import RestApiTool
 
 Defaults.drivers_config = DriversConfig(
-    prompt_driver=OpenAiChatPromptDriver(model="gpt-4o", temperature=0.1),
+    prompt_driver=OpenAiChatPromptDriver(model="gpt-4o", temperature=0.1, use_native_tools=False),
 )
 
 posts_client = RestApiTool(
diff --git a/docs/griptape-framework/tools/official-tools/src/variation_image_generation_tool_1.py b/docs/griptape-framework/tools/official-tools/src/variation_image_generation_tool_1.py
index 115bc868..b33bf774 100644
--- a/docs/griptape-framework/tools/official-tools/src/variation_image_generation_tool_1.py
+++ b/docs/griptape-framework/tools/official-tools/src/variation_image_generation_tool_1.py
@@ -10,7 +10,7 @@ driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=BedrockStableDiffusionImageGenerationModelDriver(
         style_preset="pixel-art",
     ),
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 # Create a tool configured to use the engine.
diff --git a/docs/griptape-framework/tools/official-tools/src/variation_image_generation_tool_2.py b/docs/griptape-framework/tools/official-tools/src/variation_image_generation_tool_2.py
index baa8074f..82c367ed 100644
--- a/docs/griptape-framework/tools/official-tools/src/variation_image_generation_tool_2.py
+++ b/docs/griptape-framework/tools/official-tools/src/variation_image_generation_tool_2.py
@@ -10,7 +10,7 @@ driver = AmazonBedrockImageGenerationDriver(
     image_generation_model_driver=BedrockStableDiffusionImageGenerationModelDriver(
         style_preset="pixel-art",
     ),
-    model="stability.stable-diffusion-xl-v0",
+    model="stability.stable-diffusion-xl-v1",
 )
 
 # Create a prompt image generation client configured to use the driver.
diff --git a/griptape/drivers/prompt/openai_chat_prompt_driver.py b/griptape/drivers/prompt/openai_chat_prompt_driver.py
index 70a58076..3e377cb4 100644
--- a/griptape/drivers/prompt/openai_chat_prompt_driver.py
+++ b/griptape/drivers/prompt/openai_chat_prompt_driver.py
@@ -346,7 +346,7 @@ class OpenAiChatPromptDriver(BasePromptDriver):
 
         if response.content is not None:
             content.append(TextMessageContent(TextArtifact(response.content)))
-        if response.audio is not None:
+        if hasattr(response, "audio") and response.audio is not None:
             content.append(
                 AudioMessageContent(
                     AudioArtifact(
diff --git a/griptape/engines/extraction/csv_extraction_engine.py b/griptape/engines/extraction/csv_extraction_engine.py
index c27501fa..e949c772 100644
--- a/griptape/engines/extraction/csv_extraction_engine.py
+++ b/griptape/engines/extraction/csv_extraction_engine.py
@@ -20,8 +20,12 @@ class CsvExtractionEngine(BaseExtractionEngine):
     column_names: list[str] = field(kw_only=True)
     generate_system_template: J2 = field(default=Factory(lambda: J2("engines/extraction/csv/system.j2")), kw_only=True)
     generate_user_template: J2 = field(default=Factory(lambda: J2("engines/extraction/csv/user.j2")), kw_only=True)
-    format_header: Callable[[list[str]], str] = field(default=lambda value: ",".join(value), kw_only=True)
-    format_row: Callable[[dict], str] = field(default=lambda value: ",".join(value.values()), kw_only=True)
+    format_header: Callable[[list[str]], str] = field(
+        default=Factory(lambda: lambda value: ",".join(value)), kw_only=True
+    )
+    format_row: Callable[[dict], str] = field(
+        default=Factory(lambda: lambda value: ",".join([value or "" for value in value.values()])), kw_only=True
+    )
 
     def extract_artifacts(
         self,
diff --git a/griptape/mixins/exponential_backoff_mixin.py b/griptape/mixins/exponential_backoff_mixin.py
index 3fb57599..5f4ecf26 100644
--- a/griptape/mixins/exponential_backoff_mixin.py
+++ b/griptape/mixins/exponential_backoff_mixin.py
@@ -12,7 +12,7 @@ from tenacity import Retrying, retry_if_not_exception_type, stop_after_attempt,
 class ExponentialBackoffMixin(ABC):
     min_retry_delay: float = field(default=2, kw_only=True)
     max_retry_delay: float = field(default=10, kw_only=True)
-    max_attempts: int = field(default=10, kw_only=True)
+    max_attempts: int = field(default=2, kw_only=True)
     after_hook: Callable = field(default=lambda s: logging.warning(s), kw_only=True)
     ignored_exception_types: tuple[type[Exception], ...] = field(factory=tuple, kw_only=True)
 
diff --git a/griptape/tasks/prompt_task.py b/griptape/tasks/prompt_task.py
index e555647f..6dea29e3 100644
--- a/griptape/tasks/prompt_task.py
+++ b/griptape/tasks/prompt_task.py
@@ -305,9 +305,7 @@ class PromptTask(
         task_input: str | tuple | list | BaseArtifact | Callable[[BaseTask], BaseArtifact],
     ) -> BaseArtifact:
         if isinstance(task_input, TextArtifact):
-            task_input.value = J2().render_from_string(task_input.value, **self.full_context)
-
-            return task_input
+            return TextArtifact(J2().render_from_string(task_input.value, **self.full_context), meta=task_input.meta)
         elif isinstance(task_input, Callable):
             return self._process_task_input(task_input(self))
         elif isinstance(task_input, ListArtifact):


Test patch:
diff --git a/tests/integration/test_code_blocks.py b/tests/integration/test_code_blocks.py
index 562ddf99..9b6d54a2 100644
--- a/tests/integration/test_code_blocks.py
+++ b/tests/integration/test_code_blocks.py
@@ -13,6 +13,7 @@ SKIP_FILES = [
     "docs/griptape-framework/drivers/src/image_generation_drivers_8.py",
     "docs/griptape-framework/drivers/src/image_generation_drivers_9.py",
     "docs/griptape-framework/drivers/src/prompt_drivers_4.py",
+    "docs/griptape-framework/drivers/src/prompt_drivers_10.py",
     "docs/griptape-framework/drivers/src/prompt_drivers_12.py",
     "docs/griptape-framework/drivers/src/prompt_drivers_14.py",
     "docs/griptape-framework/drivers/src/observability_drivers_1.py",
diff --git a/tests/unit/tasks/test_prompt_task.py b/tests/unit/tasks/test_prompt_task.py
index 5d6a8c95..04177f7c 100644
--- a/tests/unit/tasks/test_prompt_task.py
+++ b/tests/unit/tasks/test_prompt_task.py
@@ -10,7 +10,7 @@ from griptape.memory.structure.run import Run
 from griptape.rules import Rule
 from griptape.rules.json_schema_rule import JsonSchemaRule
 from griptape.rules.ruleset import Ruleset
-from griptape.structures import Pipeline
+from griptape.structures import Agent, Pipeline
 from griptape.tasks import PromptTask
 from tests.mocks.mock_prompt_driver import MockPromptDriver
 from tests.mocks.mock_tool.tool import MockTool
@@ -134,6 +134,19 @@ class TestPromptTask:
 
         assert task.input.value == str({"default": "test"})
 
+    def test_input_run(self):
+        agent = Agent(tasks=[PromptTask(TextArtifact("{{ args[0] }}", meta={"foo": "bar"}))])
+
+        agent.run("1")
+
+        assert agent.task.input.value == "1"
+        assert agent.task.input.meta["foo"] == "bar"
+
+        agent.run("2")
+
+        assert agent.task.input.value == "2"
+        assert agent.task.input.meta["foo"] == "bar"
+
     def test_input_context(self):
         pipeline = Pipeline(
             tasks=[