📝 Preface
The immense popularity of large language models (LLMs) in 2023 looks set to continue this year. Following OpenAI's groundbreaking releases of GPT-3 and subsequent iterations, LLMs have become the cutting edge in natural language processing. Numerous tech firms and developers are racing to bring their own LLM offerings to market, capitalizing on intense interest in this burgeoning field. Harnessing the power of LLMs appears to be the wise course forward for those seeking to remain at the forefront of AI capabilities. 🤖
My employer has opted to develop its own product in this sphere, integrating proprietary APIs across the organization. The workflow chart constitutes the crux of this endeavor, empowering users to tailor personalized processes. In the following article, I will illuminate the pivotal steps to constructing such workflow diagrams within web interfaces. It is my sincere aspiration that these insights provide some modicum of assistance to readers pursuing similar objectives. 🙏
📚 Finding the Right Workflow Library
At my present employer, the predominant front-end technology stack comprises Vue3
and TypeScript
. Consequently, I sought out a workflow library grounded in Vue3
and ideally authored in TypeScript
. Despite utilizing LogicFlow previously, its integration with Vue3
proved insufficiently seamless.Therefore, for this demonstration, I opted for VueFlow, which boasts exceptional support for Vue3
.
⚙️ Core Capabilities to Implement
- 🖱 Drag & Drop Elements to Create New Nodes
- 🎨 Customizing Nodes
- ➡️ Connect Nodes with Drag-Based Edges
- 📤 Pass Data Between Nodes
- 💾 Save/Load Workflow State
🖱 Drag & Drop Elements to Create New Nodes
Set the element draggable
and add dragstart
event handler:
<script setup>
function handleDragStart(event, nodeType) {
event.dataTransfer.setData("node", nodeType);
}
</script>
<template>
<div
draggable
@dragstart="handleDragStart($event, 'someNode')">
...
</div>
</template>
Define drop zone with ondragover
and ondrop
handlers:
<template>
<div
@drop="handleDrop"
@dragover="handleDragOver">
<VueFlow>
...
</VueFlow>
</div>
</template>
<script>
function handleDrop(event) {
// Create node from dragged data
const nodeType = event.dataTransfer.getData('node')
const newNode = {
type: nodeType,
...
}
addNode(newNode)
}
function handleOnDragOver(event) {
event.preventDefault();
if (event.dataTransfer) {
event.dataTransfer.dropEffect = "move";
}
}
</script>
This enables dragging elements into the flowchart canvas to create new nodes programmatically. 💡
As the code above shows, we can do our business logic in the drop event handler. In this demo, we just add our custom node to the workflow canvas with addNodes. By the way, we can use the useVueFlow
hook provided by VueFlow to conveniently add/delete nodes & edges in our app.
🎨 Customizing Nodes
Two approaches exist for defining customized nodes. The first employs template slots, whereby dynamic resolution furnishes a slot denominated #node-custom
when the custom type is anticipated. The second leverages the node-types
prop. In my estimation, the latter approach promotes superior organization. Thus, I opted to utilize the node-types
technique for node definition.
Constructing a bespoke node is remarkably straightforward - simply codify the node UI as an ordinary Vue component. This convenience is quite remarkable.
<script setup lang="ts">
import StartNode from '@/components/vue-flow/nodes/start-node.vue'
import EndNode from '@/components/vue-flow/nodes/end-node.vue'
import LLMNode from '@/components/vue-flow/nodes/LLM-node.vue'
import CodeNode from '@/components/vue-flow/nodes/code-node.vue'
import KnowledgeNode from '@/components/vue-flow/nodes/knowledge-node.vue'
import ApiNode from '@/components/vue-flow/nodes/api-node.vue'
const nodeTypes = {
start: markRaw(StartNode),
end: markRaw(EndNode),
LLM: markRaw(LLMNode),
code: markRaw(CodeNode),
knowledge: markRaw(KnowledgeNode),
api: markRaw(ApiNode)
}
</script>
<template>
<div class="relative h-full w-full">
<VueFlow :node-types="nodeTypes">
</VueFlow>
</div>
</template>
➡️ Connect Nodes with Drag-Based Edges
- Employ the
onConnect
hook, invoked upon establishing a connection between two nodes. - Within the callback, call
addEdges
to forge an edge linking the nodes. - When instantiating the novel node, the
isValidTargetPos
attribute can be defined to constrain permissible connections to specific target nodes. ```Typescript
const { onConnect, addEdges } = useVueFlow()
onConnect((params) => {
addEdges(params)
})
Automatically connect nodes on drag.
## 📤 Pass Data Between Nodes
Our envisioned workflow platform necessitates obtanining data from preceding nodes and replaying it to subsequent nodes. This imposes the requirement of devising a technique to retrieve a node's antecedent data.
The current node's attributes incorporate a `connectedEdges` field. By filtering for connected nodes where the current node constitutes the `target` node type, we can identify the direct predecessor. The `findNode` function can then be invoked upon that node to extract its bound data for passage downstream.
This leverages existing node properties and methods to fulfill the prerequisite of data sharing between ajacent nodes in the workflows.
```Typescript
import { useNode, useVueFlow } from "@vue-flow/core";
const node = useNode();
const { findNode } = useVueFlow();
watchEffect(() => {
if (node.connectedEdges && node.connectedEdges.value.length > 0) {
const filteredEdges = node.connectedEdges.value.filter(
(item) => item.target === node.id
);
referenceOptions.value = filteredEdges.map((edge) => {
const node = findNode(edge.source);
const currentItem: Option = {
groupName: node?.data.title ?? node?.label,
options: [],
};
if (node?.data.output) {
node?.data.output
.filter((item: any) => Boolean(item.name))
.forEach((option: any) => {
currentItem.options.push({
label: option.name,
value: option.name,
});
});
} else {
currentItem.options = [];
}
return currentItem;
});
}
});
💾 Save/Load Workflow State
To enable integration with back-end APIs, persisting the current workflow state is requisite. Retrieving the diagram data proves straightforward - simply invoke the toObject
method form the useVueFlow
hook. Similarly, restoring a saved workflow is readily achievable by applying the appropriate utilities furnished by the hook.
Leveraging the capabilities surface by useVueFlow provides a clean and convenient approach to serializing and deserializing the workflow for server communication and storage.
For illustration, the workflow data can be obtained, persisted, and restored as follows:
// Retrieve workflow data
const data = toObject(workflow);
// Save data to backend
saveWorkflow(data);
// Reconstruct workflow from data
setNodes(data.nodes);
setEdges(data.edges);
Through this approach, the current workflow state can be serialized and sent to the backend for storage. Later, it can be fetched and deserialized to recreate the diagram programmatically by re-instantiating the nodes and edges.
This enables full persistence of workflows to facilitate saving progress and resuming from previous sessions. The useVueFlow
hook provides convenient utility methods to capture and restore the diagram state.
Additional Beneficial Capabilities
- Copying, deleting, and otherwise modifying nodes through handy utility functions.
VueFlow furnishes abundant helper methods to effortlessly copy, remove, or otherwise alter nodes within the workflow diagram. This greatly simplifies tasks like duplicating portions of the workflow.
- Supplementary plugins for expanded functionality.
A range of optional VueFlow plugins exist that introduce additional capabilities including minimaps, grid backgrounds, node resizer, and more. These afford further ways to customize the environment to suit specific needs.
In summary, VueFlow comes well-equipped with built-in utilities and an ecosystem of plugins to enable advanced workflows and streamlined customization beyond core dragging and connecting of nodes.
🏁 Conclusion
At its core, constructing a web-based flowchart entails thoughtfully organizing components into a cohesive application. The crux lies with the components themselves, irrespective of the framework such as React or Vue. The process simply involves adhering to documentation, logically structuring components, and conveniently adding/removing them as needed. I hope these insights offer useful pointers on building flowchart tools.
Through creating this demo, I've gained significant clarity around implementing web apps centered on flowcharts. The heart of such applications remains the components - whether using React, Vue, or any other JavaScript framework. It's a matter of fitting components into the framework's architecture. This principle persists unchanged; the steps are to comprehend documentation, keep the framework tidy, and seamlessly inject or remove components. I hope this article📚 provides helpful tips on flowchart apps. Thank you for reading - have an excellent day!😃
🚀 Live Demo: https://chat-bot-workflow.vercel.app/
📚 Original Article: https://www.monsterpi13.dev/writing/vue-flow-quickstart-and-best-practices
📂 GitHub Repo: https://github.com/MonsterPi13/chatBot-workflow
Top comments (3)
感谢博主分享,受教了
你好博主,我是国内的开发者,很高兴能在此看到你的文章,整篇文章对我收获颇多。我目前的公司也是从事AI领域相关应用的开发,我本身也是要一名web开发者。希望再次看到你的文章和项目,祝君好。
谢谢鼓励,很开心能对你有帮助。