From ec68ef5ff239ad93b19ca515e16735e059220471 Mon Sep 17 00:00:00 2001 From: Martin Horn Date: Wed, 10 Dec 2025 21:06:23 +0100 Subject: [PATCH 1/2] AP-25344: Option to remove failed tools from combined tools workflow AP-25344 (Properly handle failing tools in combined tools workflow) --- .../knime/python3/nodes/CloseablePythonNodeProxy.java | 2 +- .../org/knime/python3/nodes/DefaultViewContext.java | 4 ++-- .../java/org/knime/python3/nodes/ToolExecutor.java | 10 +++++----- .../knime/python3/nodes/proxy/PythonToolContext.java | 4 +++- .../src/main/python/_node_backend_launcher.py | 6 ++++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/CloseablePythonNodeProxy.java b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/CloseablePythonNodeProxy.java index 780c86326..6bf2d85ab 100644 --- a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/CloseablePythonNodeProxy.java +++ b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/CloseablePythonNodeProxy.java @@ -555,7 +555,7 @@ public PythonToolResult execute_tool(final PurePythonTablePortObject toolTable, @Override public CombinedToolsWorkflowInfo init_combined_tools_workflow(final List inputs, - final String execMode) { + final String execMode, final boolean removeFailedTools) { throw new UnsupportedOperationException( "Initializing a combined tools workflow is not supported here."); } diff --git a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/DefaultViewContext.java b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/DefaultViewContext.java index a18f45fa9..e30535988 100644 --- a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/DefaultViewContext.java +++ b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/DefaultViewContext.java @@ -114,8 +114,8 @@ public PythonToolResult execute_tool(final PurePythonTablePortObject toolTable, @Override public CombinedToolsWorkflowInfo init_combined_tools_workflow(final List inputs, - final String execMode) { - return m_toolExecutor.initCombinedToolsWorkflow(inputs, execMode); + final String execMode, final boolean removeFailedTools) { + return m_toolExecutor.initCombinedToolsWorkflow(inputs, execMode, removeFailedTools); } @Override diff --git a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/ToolExecutor.java b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/ToolExecutor.java index 8e495378c..f8465e91e 100644 --- a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/ToolExecutor.java +++ b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/ToolExecutor.java @@ -181,7 +181,7 @@ PythonToolResult executeTool(final PurePythonTablePortObject pythonToolTable, fi } CombinedToolsWorkflowInfo initCombinedToolsWorkflow(final List inputs, - final String execModeString) { + final String execModeString, final boolean removeFailedTools) { m_fileStoreSwitcher.switchFileStoreHandler(m_nodeContainer, false); Map dummyFileStoreMap = Map.of(); @@ -194,7 +194,7 @@ CombinedToolsWorkflowInfo initCombinedToolsWorkflow(final List .toArray(PortObject[]::new); m_workflowExecutor = WorkflowSegmentExecutor.builder(m_nodeContainer, execMode, "Combined tools workflow", warning -> { - }, m_exec, false).combined(inputPortObjects).build(); + }, m_exec, false).combined(inputPortObjects).removeFailedSegments(removeFailedTools).build(); var combinedToolsWorkflowId = m_workflowExecutor.getWorkflow().getID(); m_disposeCombinedToolsWorkflowOnClose = true; m_combinedToolsWorkflow = new VirtualProject(combinedToolsWorkflowId); @@ -203,7 +203,8 @@ CombinedToolsWorkflowInfo initCombinedToolsWorkflow(final List } else { m_workflowExecutor = WorkflowSegmentExecutor.builder(m_nodeContainer, execMode, "Combined tools workflow", warning -> { - }, m_exec, false).combined(m_combinedToolsWorkflow.loadAndGetWorkflow()).build(); + }, m_exec, false).combined(m_combinedToolsWorkflow.loadAndGetWorkflow()) + .removeFailedSegments(removeFailedTools).build(); assert inputs.isEmpty(); inputPortIds = List.of(); } @@ -411,8 +412,7 @@ private static WorkflowSegment createWorkflowSegmentWithRemovedIONodes(final Wor segmentWfm = createEmptyWorkflow("workflow_segment"); var copyContent = WorkflowCopyContent.builder() .setNodeIDs(wfm.getNodeContainers().stream().map(NodeContainer::getID).toArray(NodeID[]::new)).build(); - var persistor = wfm.copy(copyContent); - segmentWfm.paste(persistor); + segmentWfm.copyFromAndPasteHere(wfm, copyContent); List inputs = new ArrayList<>(); List outputs = new ArrayList<>(); removeAndCollectContainerInputsAndOutputs(segmentWfm, inputs, inputIds, outputs, outputIds); diff --git a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/proxy/PythonToolContext.java b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/proxy/PythonToolContext.java index 953a41916..b53221bea 100644 --- a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/proxy/PythonToolContext.java +++ b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/proxy/PythonToolContext.java @@ -100,9 +100,11 @@ public record CombinedToolsWorkflowInfo(String projectId, String workflowId, Lis * * @param inputs the source inputs of the combined-tools workflow * @param execMode DEFAUTL, DETACHED or DEBUG + * @param removeFailedTools whether to remove failed tools from the combined workflow * @return info about the initialized combined-tools workflow */ - CombinedToolsWorkflowInfo init_combined_tools_workflow(List inputs, String execMode); + CombinedToolsWorkflowInfo init_combined_tools_workflow(List inputs, String execMode, + boolean removeFailedTools); /** * @return the combined-tools workflow as a Python port object diff --git a/org.knime.python3.nodes/src/main/python/_node_backend_launcher.py b/org.knime.python3.nodes/src/main/python/_node_backend_launcher.py index 72d9d4afe..dd0d9333b 100644 --- a/org.knime.python3.nodes/src/main/python/_node_backend_launcher.py +++ b/org.knime.python3.nodes/src/main/python/_node_backend_launcher.py @@ -1473,13 +1473,15 @@ def execute_tool(self, tool, parameters: dict, inputs: List, execution_hints: di return result.message(), outputs, result.viewNodeIds() - def init_combined_tools_workflow(self, inputs: List, execution_mode: str): + def init_combined_tools_workflow( + self, inputs: List, execution_mode: str, remove_failed_tools: bool + ): prepared_inputs = [] for input in inputs: prepared_input = self._type_registry.table_from_python(input) prepared_inputs.append(prepared_input) result = self._java_ctx.init_combined_tools_workflow( - prepared_inputs, execution_mode + prepared_inputs, execution_mode, remove_failed_tools ) return result.projectId(), result.workflowId(), result.inputPortIds() From 85a0a6510cb5269757359ab200ae8ca5d85dab88 Mon Sep 17 00:00:00 2001 From: Adrian Nembach <12174376+AtR1an@users.noreply.github.com> Date: Thu, 18 Dec 2025 12:33:07 +0100 Subject: [PATCH 2/2] AP-25344: Fix typo in init_combined_tools_workflow javadoc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../java/org/knime/python3/nodes/proxy/PythonToolContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/proxy/PythonToolContext.java b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/proxy/PythonToolContext.java index b53221bea..431068118 100644 --- a/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/proxy/PythonToolContext.java +++ b/org.knime.python3.nodes/src/main/java/org/knime/python3/nodes/proxy/PythonToolContext.java @@ -99,7 +99,7 @@ public record CombinedToolsWorkflowInfo(String projectId, String workflowId, Lis * Initializes the combined-tools workflow for the given inputs. * * @param inputs the source inputs of the combined-tools workflow - * @param execMode DEFAUTL, DETACHED or DEBUG + * @param execMode DEFAULT, DETACHED or DEBUG * @param removeFailedTools whether to remove failed tools from the combined workflow * @return info about the initialized combined-tools workflow */