amoghrajesh commented on code in PR #76917:
URL: https://github.com/apache/airflow/pull/76917#discussion_r4563528712


##########
providers/common/ai/src/airflow/providers/common/ai/operators/agent.py:
##########
@@ -761,8 +818,11 @@ def __init__(
 
         self.durable = durable
         self.cache_prompt = cache_prompt
+        # Whether this run is durable. ``durable=None`` leaves it to 
``_decide_durable`` when the
+        # task runs, since the task state store and the task log exist only 
then.
+        self._durable = durable is True

Review Comment:
   `self._durable` next to `self.durable` is easy to mix up, and the PR uses 
both on the same lines. Suggest a rename `_durable` to `_is_durable_run`.



##########
providers/common/ai/src/airflow/providers/common/ai/operators/agent.py:
##########
@@ -1079,6 +1150,27 @@ def prepare(toolset: AbstractToolset[Any] | 
ToolsetFunc[Any]) -> AbstractToolset
             **extra_kwargs,
         )
 
+    def _build_agent_as_decided(self, *, approval_context: Context | None = 
None) -> Agent[object, Any]:
+        """
+        Build the agent, durable if the run was decided durable.
+
+        pydantic-ai checks a durable agent when it builds it and refuses some 
shapes the checks in
+        ``_durable_problem`` do not cover, such as a capability with durable 
operations and no
+        ``id``. Under the default, such an agent is built without durable 
execution and the task
+        log gives pydantic-ai's reason; ``durable=True`` keeps the error.
+        """
+        try:
+            return self._build_agent(approval_context=approval_context)
+        except UserError as error:

Review Comment:
   Misisng test - Nothing covers a `UserError` that pydantic-ai raises at run 
time under the default 



##########
providers/common/ai/src/airflow/providers/common/ai/operators/agent.py:
##########
@@ -837,16 +892,39 @@ def __init__(
             tool_approval_assigned_users, param="tool_approval_assigned_users"
         )
 
-    def _require_durable_toolset_ids(self) -> None:
+    def _durable_problem(self) -> str | None:
+        """
+        Say why this task's agent cannot run durably, or return ``None``.
+
+        ``durable=True`` raises it when the Dag loads; the default turns 
durable execution off
+        and logs it when the task runs. A ``SandboxToolset`` is refused, 
attached or not: a
+        replayed tool result is not executed again, so a replayed 
``write_file`` leaves no file
+        behind and the first call that is not replayed runs against whatever 
the sandbox holds.
+        The toolset is looked for inside wrappers, combinations and every 
capability's toolset;
+        one resolved per run from a callable cannot be inspected here.
+        """
+        if _contains_capability(self._declared_capabilities, 
AirflowDurability):
+            return "durable=True attaches AirflowDurability itself; pass 
durable=True or the capability, not both."
+        if self._durable_sandbox_toolsets():
+            return (
+                "durable=True cannot be used with a SandboxToolset: cached 
tool results would be "
+                "replayed without touching the sandbox, so the workspace would 
not match the "
+                "transcript. Drop durable=True, or move the sandbox work into 
its own task."

Review Comment:
   Same on line 950



##########
providers/common/ai/src/airflow/providers/common/ai/operators/agent.py:
##########
@@ -837,16 +892,39 @@ def __init__(
             tool_approval_assigned_users, param="tool_approval_assigned_users"
         )
 
-    def _require_durable_toolset_ids(self) -> None:
+    def _durable_problem(self) -> str | None:
+        """
+        Say why this task's agent cannot run durably, or return ``None``.
+
+        ``durable=True`` raises it when the Dag loads; the default turns 
durable execution off
+        and logs it when the task runs. A ``SandboxToolset`` is refused, 
attached or not: a
+        replayed tool result is not executed again, so a replayed 
``write_file`` leaves no file
+        behind and the first call that is not replayed runs against whatever 
the sandbox holds.
+        The toolset is looked for inside wrappers, combinations and every 
capability's toolset;
+        one resolved per run from a callable cannot be inspected here.
+        """
+        if _contains_capability(self._declared_capabilities, 
AirflowDurability):
+            return "durable=True attaches AirflowDurability itself; pass 
durable=True or the capability, not both."
+        if self._durable_sandbox_toolsets():
+            return (
+                "durable=True cannot be used with a SandboxToolset: cached 
tool results would be "
+                "replayed without touching the sandbox, so the workspace would 
not match the "
+                "transcript. Drop durable=True, or move the sandbox work into 
its own task."

Review Comment:
   Under the default, `_durable_problem`'s messages are logged as is, so a user 
who never set durable reads "durable=True cannot be used with a SandboxToolset 
... Drop durable=True" pins that wording. Word the reasons as "durable 
execution cannot ...", and let the `durable=True` path add "Drop durable=True".



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to