Repository: griptape-ai/griptape

Issue:
Structure/Task Run Args/Kwargs
`Structure.run` should accept args _and_ kwargs.`BaseTask.run` should support the same.

Discussion:

Solution patch:
diff --git a/griptape/tasks/base_task.py b/griptape/tasks/base_task.py
index 9ab1bda1..4b292e9f 100644
--- a/griptape/tasks/base_task.py
+++ b/griptape/tasks/base_task.py
@@ -43,6 +43,11 @@ class BaseTask(FuturesExecutorMixin, SerializableMixin, RunnableMixin["BaseTask"
 
     output: Optional[T] = field(default=None, init=False)
     context: dict[str, Any] = field(factory=dict, kw_only=True, metadata={"serializable": True})
+    _execution_args: tuple = field(factory=tuple, init=False)
+
+    @property
+    def execution_args(self) -> tuple:
+        return self._execution_args
 
     def __rshift__(self, other: BaseTask | list[BaseTask]) -> BaseTask | list[BaseTask]:
         if isinstance(other, list):
@@ -163,8 +168,10 @@ class BaseTask(FuturesExecutorMixin, SerializableMixin, RunnableMixin["BaseTask"
                 ),
             )
 
-    def run(self) -> T:
+    def run(self, *args) -> T:
         try:
+            self._execution_args = args
+
             self.state = BaseTask.State.RUNNING
 
             self.before_run()
@@ -212,6 +219,7 @@ class BaseTask(FuturesExecutorMixin, SerializableMixin, RunnableMixin["BaseTask"
     def reset(self) -> BaseTask:
         self.state = BaseTask.State.PENDING
         self.output = None
+        self._execution_args = ()
 
         return self
 
@@ -222,7 +230,9 @@ class BaseTask(FuturesExecutorMixin, SerializableMixin, RunnableMixin["BaseTask"
     def full_context(self) -> dict[str, Any]:
         # Need to deep copy so that the serialized context doesn't contain non-serializable data
         context = deepcopy(self.context)
-        if self.structure is not None:
+        if self.structure is None:
+            context.update({"args": self._execution_args})
+        else:
             context.update(self.structure.context(self))
 
         return context


Test patch:
diff --git a/tests/unit/tasks/test_base_task.py b/tests/unit/tasks/test_base_task.py
index 8ceea4c7..b5eae319 100644
--- a/tests/unit/tasks/test_base_task.py
+++ b/tests/unit/tasks/test_base_task.py
@@ -231,6 +231,13 @@ class TestBaseTask:
         task.structure._execution_args = ("foo", "bar")
 
         assert task.full_context == {"args": ("foo", "bar"), "structure": task.structure}
+        assert task.structure.execution_args == ("foo", "bar")
+
+        task.structure = None
+        task._execution_args = ("foo", "bar")
+
+        assert task.full_context == {"args": ("foo", "bar")}
+        assert task.execution_args == ("foo", "bar")
 
     def test_is_pending(self, task):
         task.state = task.State.PENDING
@@ -249,3 +256,15 @@ class TestBaseTask:
         assert str(task) == "foobar"
         task.output = None
         assert str(task) == ""
+
+    def test_run_args(self, task):
+        task.run("foo", "bar")
+
+        assert task._execution_args == ("foo", "bar")
+
+    def test_args_full_context(self):
+        task = MockTask()
+        task.context = {"foo": "buzz"}
+        task.run("foo", "bar")
+
+        assert task.full_context["args"] == ("foo", "bar")
diff --git a/tests/unit/tasks/test_base_text_input_task.py b/tests/unit/tasks/test_base_text_input_task.py
index 1be3904c..9cf9357e 100644
--- a/tests/unit/tasks/test_base_text_input_task.py
+++ b/tests/unit/tasks/test_base_text_input_task.py
@@ -31,9 +31,13 @@ class TestBaseTextInputTask:
         subtask = MockTextInputTask("test", context={"foo": "bar"})
         child = MockTextInputTask("child")
 
-        assert parent.full_context == {}
-        assert subtask.full_context == {"foo": "bar"}
-        assert child.full_context == {}
+        assert parent.full_context == {
+            "args": (),
+        }
+        assert subtask.full_context == {"args": (), "foo": "bar"}
+        assert child.full_context == {
+            "args": (),
+        }
 
         pipeline = Pipeline()