From 52dd76c62b960108d59811cc189323a5fd6a7c3a Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 10 Aug 2026 14:19:06 -0700 Subject: [PATCH] Pull the ai workflow through serializer and check for required triggers --- src/documents/serialisers.py | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 73d535dbf..bf9db78c6 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -3186,6 +3186,9 @@ class WorkflowActionSerializer(serializers.ModelSerializer[WorkflowAction]): "email", "webhook", "passwords", + "ai_suggestion_fields", + "ai_create_missing", + "ai_overwrite_existing", ] def validate(self, attrs): @@ -3243,6 +3246,23 @@ class WorkflowActionSerializer(serializers.ModelSerializer[WorkflowAction]): "Passwords are required for password removal actions", ) + if ( + "type" in attrs + and attrs["type"] == WorkflowAction.WorkflowActionType.APPLY_AI_SUGGESTIONS + ): + fields = attrs.get("ai_suggestion_fields") + valid_fields = set(WorkflowAction.AISuggestionField.values) + if ( + fields is None + or not isinstance(fields, list) + or len(fields) == 0 + or any(field not in valid_fields for field in fields) + ): + raise serializers.ValidationError( + "At least one valid field is required for apply AI " + f"suggestions actions, options are: {sorted(valid_fields)}", + ) + return attrs @@ -3271,24 +3291,43 @@ class WorkflowSerializer(serializers.ModelSerializer[Workflow]): action.get("type") == WorkflowAction.WorkflowActionType.REMOTE_OCR for action in attrs["actions"] ) + has_ai_suggestions_action = any( + action.get("type") + == WorkflowAction.WorkflowActionType.APPLY_AI_SUGGESTIONS + for action in attrs["actions"] + ) else: has_remote_ocr_action = self.instance is not None and ( self.instance.actions.filter( type=WorkflowAction.WorkflowActionType.REMOTE_OCR, ).exists() ) + has_ai_suggestions_action = self.instance is not None and ( + self.instance.actions.filter( + type=WorkflowAction.WorkflowActionType.APPLY_AI_SUGGESTIONS, + ).exists() + ) if "triggers" in attrs: has_consumption_trigger = any( trigger.get("type") == WorkflowTrigger.WorkflowTriggerType.CONSUMPTION for trigger in attrs["triggers"] ) + has_non_consumption_trigger = any( + trigger.get("type") != WorkflowTrigger.WorkflowTriggerType.CONSUMPTION + for trigger in attrs["triggers"] + ) else: has_consumption_trigger = self.instance is not None and ( self.instance.triggers.filter( type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, ).exists() ) + has_non_consumption_trigger = self.instance is not None and ( + self.instance.triggers.exclude( + type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, + ).exists() + ) # Remote OCR can only work with consumption triggers if has_remote_ocr_action and not has_consumption_trigger: @@ -3296,6 +3335,14 @@ class WorkflowSerializer(serializers.ModelSerializer[Workflow]): "Remote OCR actions require a consumption started trigger", ) + # Suggestions are made from the document content, which does not exist + # until after consumption has finished + if has_ai_suggestions_action and not has_non_consumption_trigger: + raise serializers.ValidationError( + "Apply AI suggestions actions require a trigger other than " + "consumption started", + ) + return attrs def update_triggers_and_actions(