Skip to content

Commit 85dc6f1

Browse files
committed
Refactor to more fns; add edge conversion
Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>
1 parent bedca32 commit 85dc6f1

File tree

2 files changed

+93
-31
lines changed

2 files changed

+93
-31
lines changed

common/interfaces.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export type CreateIndexNode = TemplateNode & {
8888

8989
export type TemplateEdge = {
9090
source: string;
91-
target: string;
91+
dest: string;
9292
};
9393

9494
export type TemplateFlow = {

public/pages/workflow_detail/utils/workflow_to_template_utils.ts

+92-30
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
import { FormikValues } from 'formik';
67
import {
78
WorkspaceFlowState,
89
ReactFlowComponent,
@@ -17,9 +18,10 @@ import {
1718
componentDataToFormik,
1819
ReactFlowEdge,
1920
CreateIndexNode,
21+
TemplateFlow,
22+
TemplateEdge,
2023
} from '../../../../common';
2124

22-
// TODO: improve to make more generic
2325
/**
2426
* Given a ReactFlow workspace flow with fully populated input values,
2527
* generate a backend-compatible set of sub-workflows.
@@ -28,22 +30,62 @@ import {
2830
export function toTemplateFlows(
2931
workspaceFlow: WorkspaceFlowState
3032
): TemplateFlows {
31-
const curNodes = workspaceFlow.nodes;
33+
const { ingestNodes, ingestEdges } = getIngestNodesAndEdges(
34+
workspaceFlow.nodes,
35+
workspaceFlow.edges
36+
);
37+
const provisionFlow = toProvisionTemplateFlow(ingestNodes, ingestEdges);
38+
39+
// TODO: support beyond provision
40+
return {
41+
provision: provisionFlow,
42+
};
43+
}
44+
45+
function getIngestNodesAndEdges(
46+
allNodes: ReactFlowComponent[],
47+
allEdges: ReactFlowEdge[]
48+
): { ingestNodes: ReactFlowComponent[]; ingestEdges: ReactFlowEdge[] } {
49+
const ingestParentId = allNodes.find(
50+
(node) => node.type === NODE_CATEGORY.INGEST_GROUP
51+
)?.id as string;
52+
const ingestNodes = allNodes.filter(
53+
(node) => node.parentNode === ingestParentId
54+
);
55+
const ingestIds = ingestNodes.map((node) => node.id);
56+
const ingestEdges = allEdges.filter(
57+
(edge) => ingestIds.includes(edge.source) || ingestIds.includes(edge.target)
58+
);
59+
return {
60+
ingestNodes,
61+
ingestEdges,
62+
};
63+
}
64+
65+
// Generates the end-to-end provision subflow, if applicable
66+
function toProvisionTemplateFlow(
67+
nodes: ReactFlowComponent[],
68+
edges: ReactFlowEdge[]
69+
): TemplateFlow {
3270
const prevNodes = [] as ReactFlowComponent[];
3371
const templateNodes = [] as TemplateNode[];
34-
curNodes.forEach((node) => {
35-
const templateNode = toTemplateNode(node, prevNodes, workspaceFlow.edges);
72+
const templateEdges = [] as TemplateEdge[];
73+
nodes.forEach((node) => {
74+
const templateNode = toTemplateNode(node, prevNodes, edges);
75+
// it may be undefined if the node is not convertible for some reason
3676
if (templateNode) {
3777
templateNodes.push(templateNode);
3878
prevNodes.push(node);
3979
}
4080
});
4181

42-
console.log('final template nodes: ', templateNodes);
82+
edges.forEach((edge) => {
83+
templateEdges.push(toTemplateEdge(edge));
84+
});
85+
4386
return {
44-
provision: {
45-
nodes: templateNodes,
46-
},
87+
nodes: templateNodes,
88+
edges: templateEdges,
4789
};
4890
}
4991

@@ -52,17 +94,20 @@ function toTemplateNode(
5294
prevNodes: ReactFlowComponent[],
5395
edges: ReactFlowEdge[]
5496
): TemplateNode | undefined {
55-
if (flowNode.type === NODE_CATEGORY.CUSTOM) {
56-
if (flowNode.data.baseClasses?.includes(COMPONENT_CLASS.ML_TRANSFORMER)) {
57-
return toIngestPipelineNode(flowNode);
58-
} else if (flowNode.data.baseClasses?.includes(COMPONENT_CLASS.INDEXER)) {
59-
return toIndexerNode(flowNode, prevNodes, edges);
60-
}
61-
} else {
62-
return undefined;
97+
if (flowNode.data.baseClasses?.includes(COMPONENT_CLASS.ML_TRANSFORMER)) {
98+
return toIngestPipelineNode(flowNode);
99+
} else if (flowNode.data.baseClasses?.includes(COMPONENT_CLASS.INDEXER)) {
100+
return toIndexerNode(flowNode, prevNodes, edges);
63101
}
64102
}
65103

104+
function toTemplateEdge(flowEdge: ReactFlowEdge): TemplateEdge {
105+
return {
106+
source: flowEdge.source,
107+
dest: flowEdge.target,
108+
};
109+
}
110+
66111
// General fn to process all ML transform nodes. Convert into a final
67112
// ingest pipeline with a processor specific to the final class of the node.
68113
function toIngestPipelineNode(
@@ -77,7 +122,7 @@ function toIngestPipelineNode(
77122
default: {
78123
const { modelId, inputField, vectorField } = componentDataToFormik(
79124
flowNode.data
80-
) as { modelId: string; inputField: string; vectorField: string };
125+
);
81126

82127
return {
83128
id: flowNode.data.id,
@@ -107,8 +152,7 @@ function toIngestPipelineNode(
107152
}
108153
}
109154

110-
// General fn to process all indexer nodes. Convert into a final
111-
// ingest pipeline with a processor specific to the final class of the node.
155+
// General fn to convert an indexer node to a final CreateIndexNode template node.
112156
function toIndexerNode(
113157
flowNode: ReactFlowComponent,
114158
prevNodes: ReactFlowComponent[],
@@ -117,21 +161,19 @@ function toIndexerNode(
117161
switch (flowNode.data.type) {
118162
case COMPONENT_CLASS.KNN_INDEXER:
119163
default: {
120-
const { indexName } = componentDataToFormik(flowNode.data) as {
121-
indexName: string;
122-
};
164+
const { indexName } = componentDataToFormik(flowNode.data);
123165
// TODO: remove hardcoded logic here that is assuming each indexer node has
124-
// exactly 1 directly connected predecessor node
166+
// exactly 1 directly connected create_ingest_pipeline predecessor node that
167+
// contains an inputField and vectorField
125168
const directlyConnectedNodeId = getDirectlyConnectedNodes(
126169
flowNode,
127170
edges
128171
)[0];
129-
const directlyConnectedNode = prevNodes.find(
130-
(prevNode) => prevNode.id === directlyConnectedNodeId
131-
) as ReactFlowComponent;
132-
const { inputField, vectorField } = componentDataToFormik(
133-
directlyConnectedNode.data
134-
) as { inputField: string; vectorField: string };
172+
const { inputField, vectorField } = getDirectlyConnectedNodeInputs(
173+
flowNode,
174+
prevNodes,
175+
edges
176+
);
135177

136178
return {
137179
id: flowNode.data.id,
@@ -143,7 +185,7 @@ function toIndexerNode(
143185
index_name: indexName,
144186
configurations: {
145187
settings: {
146-
default_pipeline: '${{create_ingest_pipeline.pipeline_id}}',
188+
default_pipeline: `\${{${directlyConnectedNodeId}.pipeline_id}}`,
147189
},
148190
mappings: {
149191
properties: {
@@ -169,6 +211,26 @@ function toIndexerNode(
169211
}
170212
}
171213

214+
// Fetch all directly connected predecessor node inputs
215+
function getDirectlyConnectedNodeInputs(
216+
node: ReactFlowComponent,
217+
prevNodes: ReactFlowComponent[],
218+
edges: ReactFlowEdge[]
219+
): FormikValues {
220+
const directlyConnectedNodeIds = getDirectlyConnectedNodes(node, edges);
221+
const directlyConnectedNodes = prevNodes.filter((prevNode) =>
222+
directlyConnectedNodeIds.includes(prevNode.id)
223+
);
224+
let values = {} as FormikValues;
225+
directlyConnectedNodes.forEach((node) => {
226+
values = {
227+
...values,
228+
...componentDataToFormik(node.data),
229+
};
230+
});
231+
return values;
232+
}
233+
172234
// Simple utility fn to fetch all direct predecessor node IDs for a given node
173235
function getDirectlyConnectedNodes(
174236
flowNode: ReactFlowComponent,

0 commit comments

Comments
 (0)