kaxil commented on code in PR #71073:
URL: https://github.com/apache/airflow/pull/71073#discussion_r3740565295


##########
providers/common/ai/src/airflow/providers/common/ai/operators/llm_branch.py:
##########
@@ -133,7 +143,13 @@ def execute(self, context: Context) -> str | Iterable[str] 
| None:
 
     def execute_complete(self, context: Context, generated_output: str, event: 
dict[str, Any]) -> Any:
         """Resume after human review, validating the reviewed choice before 
branching."""
-        output = super().execute_complete(context, generated_output, event)
+        try:
+            output = super().execute_complete(context, generated_output, event)
+        except HITLRejectException:
+            if self.fail_on_reject:
+                raise
+            self.log.info("Rejected. Skipping all downstream tasks...")

Review Comment:
   The exception being swallowed here carries the reviewer name (`Output was 
rejected by the reviewer <user>.`), and the task log was the only place that 
surfaced. `event["responded_by_user"]` is right here, so 
`self.log.info("Rejected by %s. Skipping all downstream tasks.", 
event.get("responded_by_user"))` would keep the attribution. Right now the log 
for a rejected gate no longer says who rejected it.



##########
providers/common/ai/src/airflow/providers/common/ai/operators/llm_branch.py:
##########
@@ -133,7 +143,13 @@ def execute(self, context: Context) -> str | Iterable[str] 
| None:
 
     def execute_complete(self, context: Context, generated_output: str, event: 
dict[str, Any]) -> Any:
         """Resume after human review, validating the reviewed choice before 
branching."""
-        output = super().execute_complete(context, generated_output, event)
+        try:
+            output = super().execute_complete(context, generated_output, event)
+        except HITLRejectException:
+            if self.fail_on_reject:
+                raise
+            self.log.info("Rejected. Skipping all downstream tasks...")
+            return self.do_branch(context, None)

Review Comment:
   `do_branch(context, None)` skips every direct downstream task, teardowns 
included, but `ApprovalOperator`'s reject path filters them out (`yield from (t 
for t in tasks if not t.is_teardown)`). I checked this on a real DAG with 
`branch >> [task_a, task_b, cleanup.as_teardown()]`: this path skips 
`['cleanup', 'task_a', 'task_b']` where `ApprovalOperator` skips `['task_a', 
'task_b']`. Before this change a reject failed the task and the teardown still 
ran on its `all_done`-style rule, so cleanup now silently stops running after a 
rejection.
   
   Either filter teardowns here, or drop the "matching `ApprovalOperator`" 
wording from the docstring and the rst.



-- 
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