diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 86d036f5..4dfd9b2b 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -1,14 +1,6 @@
-import React, { useEffect } from 'react';
-import { useDispatch } from 'react-redux';
-import { fetchNodeTypes } from './store/nodeTypesSlice';
+import React from 'react';
const App = () => {
- const dispatch = useDispatch();
-
- useEffect(() => {
- dispatch(fetchNodeTypes());
- }, [dispatch]);
-
return (
);
diff --git a/frontend/src/components/canvas/AddNodePopoverCanvas.jsx b/frontend/src/components/canvas/AddNodePopoverCanvas.jsx
index 1207133c..f8cc1c46 100644
--- a/frontend/src/components/canvas/AddNodePopoverCanvas.jsx
+++ b/frontend/src/components/canvas/AddNodePopoverCanvas.jsx
@@ -1,7 +1,7 @@
import { addNode, connect, deleteEdge } from '../../store/flowSlice';
import { createNode } from '../../utils/nodeFactory';
-export const addNodeWithoutConnection = (nodeType, reactFlowInstance, dispatch) => {
+export const addNodeWithoutConnection = (nodeTypes, nodeType, reactFlowInstance, dispatch) => {
const id = `node_${Date.now()}`;
const center = reactFlowInstance.screenToFlowPosition({
x: window.innerWidth / 2,
@@ -13,18 +13,18 @@ export const addNodeWithoutConnection = (nodeType, reactFlowInstance, dispatch)
y: center.y,
};
- const newNode = createNode(nodeType, id, position);
+ const newNode = createNode(nodeTypes, nodeType, id, position);
dispatch(addNode({ node: newNode }));
};
-export const addNodeBetweenNodes = (nodeType, sourceNode, targetNode, edgeId, reactFlowInstance, dispatch, setVisible) => {
+export const addNodeBetweenNodes = (nodeTypes, nodeType, sourceNode, targetNode, edgeId, reactFlowInstance, dispatch, setVisible) => {
const id = `node_${Date.now()}`;
const newPosition = {
x: (sourceNode.position.x + targetNode.position.x) / 2,
y: (sourceNode.position.y + targetNode.position.y) / 2,
};
- const newNode = createNode(nodeType, id, newPosition);
+ const newNode = createNode(nodeTypes, nodeType, id, newPosition);
dispatch(deleteEdge({ edgeId }));
dispatch(addNode({ node: newNode }));
diff --git a/frontend/src/components/canvas/FlowCanvas.jsx b/frontend/src/components/canvas/FlowCanvas.jsx
index 577fcf93..4a518e24 100644
--- a/frontend/src/components/canvas/FlowCanvas.jsx
+++ b/frontend/src/components/canvas/FlowCanvas.jsx
@@ -17,7 +17,6 @@ import NodeSidebar from '../nodes/nodeSidebar/NodeSidebar';
import { Dropdown, DropdownMenu, DropdownSection, DropdownItem } from '@nextui-org/react';
import DynamicNode from '../nodes/DynamicNode';
import { v4 as uuidv4 } from 'uuid';
-import { nodeTypes as nodeTypesConfig } from '../../constants/nodeTypes';
import { addNodeBetweenNodes } from './AddNodePopoverCanvas';
import { useKeyboardShortcuts } from '../../hooks/useKeyboardShortcuts'; // Import the new hook
import CustomEdge from './edges/CustomEdge';
@@ -27,45 +26,28 @@ import useCopyPaste from '../../utils/useCopyPaste';
import { useModeStore } from '../../store/modeStore';
import { initializeFlow } from '../../store/flowSlice'; // Import the new action
// Import the new API function
-import { getNodeTypes } from '../../utils/api';
import InputNode from '../nodes/InputNode';
import { useSaveWorkflow } from '../../hooks/useSaveWorkflow';
-const useNodeTypes = () => {
- const [nodeTypes, setNodeTypes] = useState({});
- const [isLoading, setIsLoading] = useState(true);
-
- useEffect(() => {
- const fetchNodeTypes = async () => {
- setIsLoading(true);
- try {
- const nodeTypesConfig = await getNodeTypes();
- const dynamicNodeTypes = Object.keys(nodeTypesConfig).reduce((acc, category) => {
- nodeTypesConfig[category].forEach(node => {
- if (node.name === 'InputNode') {
- acc[node.name] = InputNode;
- } else {
- acc[node.name] = (props) => {
- return ;
- };
- }
- });
- return acc;
- }, {});
-
- setNodeTypes({
- ...dynamicNodeTypes
- });
- } catch (error) {
- console.error('Error fetching node types:', error);
- } finally {
- setIsLoading(false);
- }
- };
-
- fetchNodeTypes();
- }, []);
+const useNodeTypes = ({nodeTypesConfig}) => {
+ const nodeTypes = useMemo(() => {
+ if (!nodeTypesConfig) return {};
+
+ return Object.keys(nodeTypesConfig).reduce((acc, category) => {
+ nodeTypesConfig[category].forEach(node => {
+ if (node.name === 'InputNode') {
+ acc[node.name] = InputNode;
+ } else {
+ acc[node.name] = (props) => {
+ return ;
+ };
+ }
+ });
+ return acc;
+ }, {});
+ }, [nodeTypesConfig]);
+ const isLoading = !nodeTypesConfig;
return { nodeTypes, isLoading };
};
@@ -86,6 +68,8 @@ const FlowCanvasContent = (props) => {
const dispatch = useDispatch();
+ const nodeTypesConfig = useSelector((state) => state.nodeTypes.data);
+
useEffect(() => {
if (workflowData) {
// if the input node already has a schema add it to the workflowInputVariables
@@ -103,12 +87,12 @@ const FlowCanvasContent = (props) => {
}
}
}
- dispatch(initializeFlow({ ...workflowData, workflowID }));
+ dispatch(initializeFlow({ ...workflowData, workflowID, nodeTypes: nodeTypesConfig }));
}
}, [dispatch, workflowData, workflowID]);
- const { nodeTypes, isLoading } = useNodeTypes();
+ const { nodeTypes, isLoading } = useNodeTypes({ nodeTypesConfig });
const nodes = useSelector((state) => state.flow.nodes);
const edges = useSelector((state) => state.flow.edges);
diff --git a/frontend/src/components/canvas/footer/operator/AddNodePopoverFooter.jsx b/frontend/src/components/canvas/footer/operator/AddNodePopoverFooter.jsx
index 4c8fced0..be7a81e7 100644
--- a/frontend/src/components/canvas/footer/operator/AddNodePopoverFooter.jsx
+++ b/frontend/src/components/canvas/footer/operator/AddNodePopoverFooter.jsx
@@ -1,19 +1,20 @@
import React from 'react';
import { Icon } from '@iconify/react';
import { Button, Dropdown, DropdownTrigger, DropdownMenu, DropdownSection, DropdownItem } from '@nextui-org/react';
-import { nodeTypes } from '../../../../constants/nodeTypes';
-import { useDispatch } from 'react-redux';
+import { useSelector, useDispatch } from 'react-redux';
import { addNodeWithoutConnection } from '../../AddNodePopoverCanvas';
import { useReactFlow } from '@xyflow/react';
import TipPopup from '../../../TipPopUp';
const AddNodePopoverFooter = () => {
- const reactFlowInstance = useReactFlow();
const dispatch = useDispatch();
+ const nodeTypes = useSelector((state) => state.nodeTypes.data);
+
+ const reactFlowInstance = useReactFlow();
const handleAddNode = (nodeName) => {
if (reactFlowInstance) {
- addNodeWithoutConnection(nodeName, reactFlowInstance, dispatch);
+ addNodeWithoutConnection(nodeTypes, nodeName, reactFlowInstance, dispatch);
}
};
diff --git a/frontend/src/components/nodes/nodeSidebar/NodeSidebar.jsx b/frontend/src/components/nodes/nodeSidebar/NodeSidebar.jsx
index fe658915..2c34fc33 100644
--- a/frontend/src/components/nodes/nodeSidebar/NodeSidebar.jsx
+++ b/frontend/src/components/nodes/nodeSidebar/NodeSidebar.jsx
@@ -3,7 +3,6 @@ import { useDispatch, useSelector } from 'react-redux';
import { updateNodeData, selectNodeById, setSidebarWidth, setSelectedNode } from '../../../store/flowSlice';
import NumberInput from '../../NumberInput';
import CodeEditor from '../../CodeEditor';
-import { nodeTypes } from '../../../constants/nodeTypes';
import { jsonOptions } from '../../../constants/jsonOptions';
import FewShotEditor from '../../textEditor/FewShotEditor';
import PromptEditor from '../../textEditor/PromptEditor';
@@ -13,6 +12,7 @@ import NodeStatus from "../NodeStatusDisplay";
import SchemaEditor from './SchemaEditor';
const NodeSidebar = ({ nodeID }) => {
const dispatch = useDispatch();
+ const nodeTypes = useSelector((state) => state.nodeTypes.data);
const node = useSelector((state) => selectNodeById(state, nodeID));
// Get the width from Redux store
const storedWidth = useSelector((state) => state.flow.sidebarWidth);
diff --git a/frontend/src/pages/index.jsx b/frontend/src/pages/index.jsx
index 9e758cfc..951276c5 100644
--- a/frontend/src/pages/index.jsx
+++ b/frontend/src/pages/index.jsx
@@ -1,8 +1,16 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import Header from '../components/Header'; // Import the Header component
+import { useDispatch } from 'react-redux';
import Dashboard from '../components/Dashboard';
+import { fetchNodeTypes } from '../store/nodeTypesSlice';
const Home = () => {
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ dispatch(fetchNodeTypes());
+ }, []);
+
return (
diff --git a/frontend/src/store/flowSlice.js b/frontend/src/store/flowSlice.js
index b0811b08..f4174fbf 100644
--- a/frontend/src/store/flowSlice.js
+++ b/frontend/src/store/flowSlice.js
@@ -20,13 +20,14 @@ const flowSlice = createSlice({
initialState,
reducers: {
initializeFlow: (state, action) => {
- const { workflowID, definition, name } = action.payload;
+ const { workflowID, definition, nodeTypes, name } = action.payload;
+ console.log("action", action);
state.workflowID = workflowID;
state.projectName = name;
const { nodes, links } = definition;
// Map nodes to the expected format
let mappedNodes = nodes.map(node =>
- createNode(node.node_type, node.id, { x: node.coordinates.x, y: node.coordinates.y }, { userconfig: node.config })
+ createNode(nodeTypes, node.node_type, node.id, { x: node.coordinates.x, y: node.coordinates.y }, { userconfig: node.config })
);
state.nodes = mappedNodes;
diff --git a/frontend/src/store/nodeTypesSlice.js b/frontend/src/store/nodeTypesSlice.js
index d1b22f2f..028d8dd2 100644
--- a/frontend/src/store/nodeTypesSlice.js
+++ b/frontend/src/store/nodeTypesSlice.js
@@ -10,7 +10,7 @@ export const fetchNodeTypes = createAsyncThunk('nodeTypes/fetchNodeTypes', async
const nodeTypesSlice = createSlice({
name: 'nodeTypes',
initialState: {
- types: [],
+ data: [],
status: 'idle',
error: null,
},
@@ -22,7 +22,7 @@ const nodeTypesSlice = createSlice({
})
.addCase(fetchNodeTypes.fulfilled, (state, action) => {
state.status = 'succeeded';
- state.types = action.payload;
+ state.data = action.payload;
})
.addCase(fetchNodeTypes.rejected, (state, action) => {
state.status = 'failed';
diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js
index 6624b584..c8d4f9bf 100644
--- a/frontend/src/store/store.js
+++ b/frontend/src/store/store.js
@@ -1,20 +1,24 @@
-import { configureStore } from '@reduxjs/toolkit';
+import { combineReducers, configureStore } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import flowReducer from './flowSlice';
+import nodeTypesReducer from './nodeTypesSlice';
const persistConfig = {
key: 'root',
storage,
- whitelist: ['nodes', 'edges', 'projectName'],
+ whitelist: ['nodes', 'edges', 'nodeTypes'],
};
-const persistedReducer = persistReducer(persistConfig, flowReducer);
+const rootReducer = combineReducers({
+ flow: flowReducer,
+ nodeTypes: nodeTypesReducer,
+});
+
+const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({
- reducer: {
- flow: persistedReducer,
- },
+ reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
diff --git a/frontend/src/utils/nodeFactory.js b/frontend/src/utils/nodeFactory.js
index f13dfd07..f3361bdd 100644
--- a/frontend/src/utils/nodeFactory.js
+++ b/frontend/src/utils/nodeFactory.js
@@ -1,8 +1,7 @@
-import { nodeTypes } from '../constants/nodeTypes';
import cloneDeep from 'lodash/cloneDeep';
// Function to create a node based on its type
-export const createNode = (type, id, position, additionalData = {}) => {
+export const createNode = (nodeTypes, type, id, position, additionalData = {}) => {
let nodeType = null;
for (const category in nodeTypes) {
const found = nodeTypes[category].find((node) => node.name === type);