from copy import deepcopy from app.models.workflow import ( OptimizationResponse, OptimizationSuggestion, WorkflowDocument, ) class WorkflowOptimizer: def optimize(self, workflow: WorkflowDocument) -> OptimizationResponse: optimized = deepcopy(workflow) suggestions: list[OptimizationSuggestion] = [] http_nodes = [ node for node in optimized.nodes if node.data.type == "n8n-nodes-base.httpRequest" ] for node in http_nodes: options = node.data.parameters.setdefault("options", {}) if isinstance(options, dict) and "retry" not in options: options.update({"retry": {"maxTries": 3, "waitBetweenTries": 1000}}) suggestions.append( OptimizationSuggestion( title="Add API retries", description=f"Added bounded retry behavior to {node.data.label}.", impact="high", nodeIds=[node.id], ) ) if not optimized.settings.get("saveExecutionProgress"): optimized.settings["saveExecutionProgress"] = True suggestions.append( OptimizationSuggestion( title="Enable execution recovery", description="Save execution progress so long-running workflows can recover.", impact="medium", ) ) linear_pairs = [] for edge in optimized.edges: source = next((node for node in optimized.nodes if node.id == edge.source), None) target = next((node for node in optimized.nodes if node.id == edge.target), None) if source and target and source.data.type == target.data.type == "n8n-nodes-base.set": linear_pairs.append((source, target)) if linear_pairs: suggestions.append( OptimizationSuggestion( title="Combine adjacent Edit Fields nodes", description="Adjacent field transformations can be handled by one node.", impact="medium", nodeIds=[node.id for pair in linear_pairs for node in pair], ) ) branches = {} for edge in optimized.edges: branches[edge.source] = branches.get(edge.source, 0) + 1 parallel = [node_id for node_id, count in branches.items() if count > 1] if parallel: suggestions.append( OptimizationSuggestion( title="Review parallel branches", description="Independent branches can execute concurrently; merge only when required.", impact="medium", nodeIds=parallel, ) ) if not suggestions: suggestions.append( OptimizationSuggestion( title="Workflow is already compact", description="No deterministic optimization could be applied safely.", impact="low", ) ) return OptimizationResponse(workflow=optimized, suggestions=suggestions) optimizer = WorkflowOptimizer()