jbxl-workflow
Version:
流程图
589 lines (582 loc) • 25 kB
JavaScript
import {Node} from '@antv/x6'
import { register } from '@antv/x6-vue-shape';
import StartNodeComponent from "@/components/StartNode.vue";
import EndNodeComponent from "@/components/EndNode.vue";
import InputParamsList from "@/components/InputParams.vue";
import OutputParamsList from '@/components/OutputParams.vue'
import LLMNodeComponent from "@/components/LLMNode.vue";
import PluginNodeComponent from '@/components/PluginNode.vue'
import ParameterNodeComponent from '@/components/ParameterNode.vue'
import ParameterExtractWrap from '@/components/ParameterExtractWrap.vue'
import {BasicInputParamsList, NodeParamItem, StartNodeParamsList, PluginNodeInputParamsList, ParameterNodeInputParamsList} from "@/ParamsList.class";
import {deepClone} from "@/utils/common";
import {generateRandomId} from "@dang_8899/xl-ui"
import {BasicPorts} from "@/Port.class";
import ModelSelect from "@/components/ModelSelect.vue";
import CurlyBracesTextarea from "@/components/CurlyBracesTextarea.vue";
import {ref} from "vue";
/**
* @class 基础节点
* @param title {string} 标题
* @param description {string} 描述
* @param ports {Array<Port>} 节点
* @param paramsList {Array<NodeParamItem>} 输入参数列表
* @param 节点类型(根据节点类型生成不同的节点类)
* @param x: number; 节点x坐标
* @param y: number; 节点y坐标
* @param width: number; 节点宽度
* @param height: number; 节点高度
* @param nodeStartColor: string; 节点开始颜色
* @param nodeEndColor: string; 节点结束颜色
* */
class BasicNode extends Node{
_chunks = [] // 循环渲染的区块
_ports = [] // 端口
constructor(obj) {
super(obj);
this.whiteList = ['x', 'y', 'width', 'height', 'title', 'description', 'modelList', 'allIsOpen', 'options','nextList', 'currentModel', 'portsData', '_prevList', '_prevNode', 'paramsData', 'outputParamsData', 'outputType', 'temperatureValue', 'maxTokensValue', 'currRoadInstruction', 'currRoadInstructionRichText', 'systemPrompt', 'systemPromptRichText', 'pluginId', 'chatType', 'courtUuidList', 'list', 'basicDataList'];
const {title, description, ports, type, shapeType, x, y, width, height, allIsOpen, options, modelList, id} = obj || {}
// this.$shape = shape || 'html' // 到时需要改写
this.id = id || `${generateRandomId()}${shapeType}`;
this.nodeId = '' // 从
this.type = type;
this.runDebugStatus = '' // success running error
this.title = title;
this.modelList = modelList
this.validateRes = false
this.vueInstance = null;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.description = description;
this.shapeType = shapeType;
// this.shape = shapeType;
this.paramsData = null
this.outputParamsData = null
this.allIsOpen = allIsOpen;
this.nextList = []; // 下一个节点列表
this.portsData = null; // 当前节点的端口列表,从Port.vue的args传来,用于获取
this.setData(obj, options)
this._prevList = []; //上游节点列表;
this._prevNode = null; //上一个节点
// 输入参数里面的参数值的列表,用于渲染参数输入框
this.paramValList = [{
value: 2 , label: '引用'
}, {value: 1 , label: '输入'}]
}
setChunks (chunks) {
this._chunks = chunks;
}
getChunks () {
return deepClone(this._chunks);
}
getData() {
const portsData = this.getPortsList();
const paramsData = this.paramsData?.getList() && typeof this.paramsData?.getList === 'function' ? this.paramsData.getList() : null;
const outputParamsData = this.outputParamsData?.getList && typeof this.outputParamsData.getList === 'function' ? this.outputParamsData.getList() : null;
return {
portsData: portsData ? structuredClone(portsData) : null,
paramsData: paramsData ? structuredClone(paramsData) : null,
outputParamsData: outputParamsData ? structuredClone(outputParamsData) : null,
}
}
// 设置数据,用于更新节点信息
// 设置数据,用于更新节点信息
setData (data, options = {overwrite: true }) {
if (!data) return this;
// 先处理白名单内的属性
const superData = {};
for (let key in data) {
if (this.whiteList.includes(key)) {
// 直接赋值给当前实例
this[key] = data[key];
}
// 所有属性都添加到要传给父类的数据中
// superData[key] = data[key];
}
// 只调用一次父类的 setData
super.setData(superData, options);
return this;
}
setPortsList (ports) {
this._ports = deepClone(ports);
}
getPortsList() {
return deepClone(this._ports);
}
// 初始化
create () {
throw new Error("节点类create方法必须实现");
}
// 获取graph.addNode需要的参数
getGraphPureParams (obj) {
const {title, description, ports, type, shapeType, x, y, width, height, allIsOpen, options} = this;
// return {}
return {
data: {
self: obj || this,
title, description, ports, type, shapeType, x, y, width, height, allIsOpen, options
},
shape: shapeType,
// type: this.type,
x,
y
}
}
// 设置下一个节点
async setNext (downStreamNode) {
if (Array.isArray(this.nextList) && this.nextList.every(item => item.id !== downStreamNode.id)) {
this.nextList.push(downStreamNode);
}
}
removeNextNode(downStreamNodeId) {
if (Array.isArray(this.nextList)) {
const index = this.nextList.findIndex(item => item.id === downStreamNodeId)
this.nextList.splice(index, 1)
}
}
async setPrev (upStreamNode) {
if (Array.isArray(this._prevList)) {
this._prevList = [...this._prevList || [], ...upStreamNode];
} else {
this._prevList = [...this._prevList || [], upStreamNode];
}
}
// 校验逻辑
async validate () {
if (this.vueInstance) {
const {validate} = this.vueInstance.exposed || {};
if (typeof validate === 'function') {
return await validate();
}
}
}
// 调试执行逻辑
async runDebug (obj) {
if (this.vueInstance) {
const {runDebug} = this.vueInstance.exposed || {};
if (typeof runDebug === 'function') {
return await runDebug(obj);
}
}
}
// 显示结果
async showResult (show) {
if (this.vueInstance) {
const {showResult} = this.vueInstance.exposed || {};
if (typeof showResult === 'function') {
return await showResult(show);
}
}
}
// 设置运行结果状态,时间,输出参数,输入参数
/**
* @param status {string} 状态,时间,输出参数,输入参数
*
* @param type {string} 类型,默认为status, 其他状态为 time、 output、 input
* */
async setRunResultStatus (status, type = 'status') {
if (this.vueInstance) {
const {setRunResultStatus, setTime, setOutputParams, setInputParams} = this.vueInstance.exposed || {};
// 设置运行结果状态
if (typeof setRunResultStatus === 'function' && type === 'status') {
return await setRunResultStatus(status);
}
// 设置运行结果时间
if (typeof setTime === 'function' && type === 'time') {
return await setTime(status);
}
// 设置运行结果输出参数
if (typeof setOutputParams === 'function' && type === 'output') {
return await setOutputParams(status);
}
// 设置运行结果输入参数
if (typeof setInputParams === 'function' && type === 'input') {
return await setInputParams(status);
}
}
}
// async setRunResultParams (params) {
// if (this.vueInstance) {
// const {setRunResultParams} = this.vueInstance.exposed || {};
// if (typeof setRunResultParams === 'function') {
// return await setRunResultParams(params);
// }
// }
// }
next () {
this.validate().then(res => {
if (res) {
this.nextList.forEach(node => node.validate());
}
})
}
}
/**
* @params paramsDataList {Array<NodeParamItem>} 输入参数列表
*
* */
class StartNode extends BasicNode {
constructor({title, description, ports, type, shape, x, y, width, height, shapeType, id, allIsOpen, nodeId, paramsDataList}) {
super({title, description, ports, type, shape, x, y, width, height, shapeType: shapeType || 'start-node-shape', id, allIsOpen, nodeId, paramsDataList});
this.paramsDataList = paramsDataList
this.initParamsList()
this.create()
}
add () {
this.paramsData.add()
}
getParamsData () {
return this.paramsData.getList()
}
getOutputParamsData () {
return this.outputParamsData.getList()
}
initParamsList () {
this.paramsData = new StartNodeParamsList();
console.log('this.paramsDataList', this.paramsDataList)
this.paramsData.init({showInputParamType: true, showInputParamVal: false, showDesc: true, paramsDataList: this.paramsDataList || []})
}
// 注册节点
create() {
// 生成开始节点port
const startNodePorts = new BasicPorts({type: 'startNode', dx: 560})
// 赋值开始节点port列表
this.setPortsList(startNodePorts.getPortsList()) // 把port赋值给当前节点
const options = {
shape: 'start-node-shape',
width: this.width,
height: this.height,
component: StartNodeComponent,
data: {
paramsData: this.paramsData,
},
ports: {
// 获取port列表,并合并到ports中
...this.getPortsList(),
}
// component: ProgressNode,
}
register(options);
}
}
/**
* modelList 大模型列表
* @class 大模型列表 大部分属性通常
* @param currentModel {string} 当前模型
* @param modelList {string} 模型列表
* @param maxTokensValue {number} 最大token值
* @param temperatureValue {number} 温度值
* @param systemPrompt {string} 系统提示词
* @param currRoadInstruction {string} 本轮指令
* @param chunks {array} chunks列表
* @param paramsDataList {Array<NodeParamItem>} 传入的list
* @param showSystemPrompt {boolean} 是否显示系统提示词
* @param outputParamsDataList {Array<NodeParamItem>} 输出参数列表
* @param systemPromptRichText {string} 系统提示词富文本
* @param currRoadInstructionRichText {string} 本轮指令富文本
* */
class LLMNode extends BasicNode {
constructor({title, description, ports, type, shape, x, y, width, height, shapeType, id, currentModel, paramsDataList, outputParamsDataList, systemPrompt, systemPromptRichText, currRoadInstructionRichText, currRoadInstruction, maxTokensValue, temperatureValue, showSystemPrompt}) {
super({title, description, ports, type, shape, x, y, width, height, shapeType: shapeType || 'llm-node-shape', id, currentModel, paramsDataList, outputParamsDataList});
this.paramsData = new BasicInputParamsList(); // 初始化输入参数列表
this.paramsData.init({showInputParamType: false, showInputParamVal: true, paramsDataList})
// 输出参数
this.outputParamsData = new BasicInputParamsList(); // 初始化输出参数列表
// 初始化输出参数列表
const outputParamsItem = new NodeParamItem({name: 'context', paramTypeVal: this.outputParamsData.inputParamTypeList[0].value, showDesc: true, description: '模型输出结果', showRequired: false, showInputParamVal: false, disabled: true, isDefault: true, type: 'output'})
this.outputParamsData.init({showInputParamVal: false, showInputParamType: true, showDesc: true, paramsItem: outputParamsItem, paramsDataList: outputParamsDataList})
// this.initChunks()
this.currentModel = currentModel || '' // 当前模型
this.temperatureValue = temperatureValue || 1.0 // 温度值
this.maxTokensValue = maxTokensValue || 512
this.modelType = '' // 默认的大语言模型类型
this.systemPrompt = systemPrompt || '' // 系统提示词
this.currRoadInstruction = currRoadInstruction || '' // 本轮指令
this.systemPromptRichText = systemPromptRichText || '' // 系统提示词富文本
this.currRoadInstructionRichText = currRoadInstructionRichText || '' // 本轮指令富文本
this.showSystemPrompt = typeof showSystemPrompt === 'boolean' ? showSystemPrompt : false // 是否显示系统提示词
this.create()
}
// 获取默认的chunks
getDefaultChunks ({modelList, currentModel = '', }) {
return [
{
title: '模型', isOpen: true, component: ModelSelect, options: modelList || this.modelList, id: generateRandomId(), currentModel: this.currentModel || currentModel, key: 'model'
},
{
title: '输入参数', isOpen: true, component: InputParamsList, paramsData: this.paramsData, currentModel: '', id: generateRandomId()
},
/**
* modelValue放置已经处理好的字符串,inputValue放置原始字符串,modelValue是处理后的html标签
* */
{
title: '系统提示词', isOpen: this.showSystemPrompt, id: generateRandomId(), component: CurlyBracesTextarea, hasParams: true, paramsData: this.paramsData, inputValue: this.systemPromptRichText, modelValue: this.systemPrompt, key: 'systemPrompt', show: this.showSystemPrompt
},
{
title: '本轮指令', isOpen: true, id: generateRandomId(), component: CurlyBracesTextarea, hasParams: true, paramsData: this.paramsData, inputValue: this.currRoadInstructionRichText, modelValue: this.currRoadInstruction, key: 'currRoadInstruction', show: true
},
{
title: '输出参数', isOpen: true, id: generateRandomId(), component: InputParamsList, paramsData: this.outputParamsData, type: 'output'
}
]
}
changeChunks () {
const type = this.modelList?.find(item => item.value === this.currentModel)?.type
this.modelType = type
if (type === 'image') {
const chunks = this.getDefaultChunks({currentModel : this.currentModel})
const list = chunks.filter(item => item.key !== 'systemPrompt')
this.setChunks(list)
} else {
this.setChunks(this.getDefaultChunks({currentModel : this.currentModel}))
}
}
// 初始化chunks, 在LLMNode.vue生成
initChunks ({modelList} = {}) {
// this.setModelList(modelList)
this.setChunks(this.getDefaultChunks({modelList}))
}
create () {
const startNodePorts = new BasicPorts({dx: 546})
// 赋值开始节点port列表
this.setPortsList(startNodePorts.getPortsList())
register({
shape: 'llm-node-shape',
width: this.width,
height: this.height,
component: LLMNodeComponent,
data: {
paramsData: this.paramsData,
modelList: this.modelList,
outputParamsData: this.outputParamsData,
currentModel: this.currentModel,
systemPrompt: this.systemPrompt,
currRoadInstruction: this.currRoadInstruction,
},
ports: {
// 获取port列表,并合并到ports中
...this.getPortsList(),
}
// component: ProgressNode,
});
}
}
// 插件节点
class PluginNode extends BasicNode {
constructor(opts) {
opts = Object.assign({
shapeType: 'plugin-node-shape'
}, opts)
super(opts)
this.pluginId = opts.pluginId || ''
this.nextList = []
this.paramsData = new PluginNodeInputParamsList()
this.outputParamsData = new BasicInputParamsList()
this.paramsDataList = opts.paramsDataList || []
this.outputParamsDataList = opts.outputParamsDataList || []
if (opts.pluginId) {
this.initChunks({
inputParamsList: this.paramsDataList.map(item => {
return {
...item,
value: item.paramVal === 1 || item.id ? item.value : ''
}
}),
outputParamsList: this.outputParamsDataList
})
}
this.create()
}
create() {
const basicPorts = new BasicPorts({
dx: 546
})
this.setPortsList(basicPorts.getPortsList())
register({
shape: 'plugin-node-shape',
width: this.width,
height: this.height,
ports: {
// 获取port列表,并合并到 ports 中
...this.getPortsList(),
},
component: PluginNodeComponent
})
}
initChunks({ inputParamsList, outputParamsList }) {
this.paramsData.list = inputParamsList?.map(item => {
return new NodeParamItem({
...item
})
})
this.outputParamsData.list = this.getOutputParamsDataList(outputParamsList)
this.setChunks([
{
id: generateRandomId(),
title: '输入参数',
isOpen: true,
collapsePaddingTop: 12,
paramsData: this.paramsData,
component: InputParamsList
},
{
id: generateRandomId(),
title: '输出参数',
isOpen: true,
collapsePaddingTop: 8,
paramsData: this.outputParamsData,
component: OutputParamsList
}
])
}
getOutputParamsDataList(outputParamsList) {
return outputParamsList?.map(item => {
return new NodeParamItem({
...item,
children: item.children?.length ? this.getOutputParamsDataList(item.children) : []
})
})
}
}
// 参数提取节点
class ParameterNode extends BasicNode {
constructor(opts) {
opts = Object.assign({
shapeType: 'parameter-node-shape'
}, opts)
super(opts)
this.nextList = []
this.paramsData = new ParameterNodeInputParamsList(opts)
this.outputParamsData = new BasicInputParamsList()
this.paramsDataList = opts.paramsDataList || []
this.outputParamsDataList = opts.outputParamsDataList || []
if (this.paramsDataList?.length || this.outputParamsDataList?.length) {
this.setInputParamsDataValue(this.paramsDataList[0].id ? this.paramsDataList[0].value : '')
this.addParameterExtractItem(this.outputParamsDataList)
}
this.create()
}
create() {
const basicPorts = new BasicPorts({
dx: 546
})
this.setPortsList(basicPorts.getPortsList())
register({
shape: 'parameter-node-shape',
width: this.width,
height: this.height,
ports: {
// 获取port列表,并合并到 ports 中
...this.getPortsList(),
},
component: ParameterNodeComponent
})
}
initChunks() {
this.setChunks([
{
id: generateRandomId(),
type: 'input',
title: '输入参数',
isOpen: true,
collapsePaddingTop: 12,
paramsData: this.paramsData,
component: InputParamsList
},
{
id: generateRandomId(),
type: 'extract',
title: '提取参数',
isOpen: true,
collapsePaddingTop: 8,
paramsData: this.outputParamsData,
component: ParameterExtractWrap
}
])
}
setInputParamsDataValue(value) {
this.paramsData.list[0].value = value
}
addParameterExtractItem(paramList) {
const paramDataList = []
const paramNameList = this.outputParamsData.list.map(item => item.name)
paramList?.forEach(param => {
let paramName = param.name
if (paramNameList.includes(paramName)) {
const defaultParamName = paramName
let num = 1
while (paramNameList.includes(paramName)) { // 通过在参数名后面加上下划线和累加的数字来处理相同的参数名,直到不重复为止
paramName = `${defaultParamName}_${num++}`
}
}
paramDataList.push(new NodeParamItem({
...param,
name: paramName
}))
paramNameList.push(paramName)
})
this.outputParamsData.list = paramDataList.concat(this.outputParamsData.list)
}
editParameterExtractItem(param) {
const index = this.outputParamsData.list.findIndex(item => item.id === param.id)
if (index > -1) {
Object.assign(this.outputParamsData.list[index], param)
}
}
deleteParameterExtractItem(param) {
const index = this.outputParamsData.list.findIndex(item => item.id === param.id)
if (index > -1) {
this.outputParamsData.list.splice(index, 1)
}
}
}
/**
* @param paramsDataList {Array<NodeParamItem>} 传入的list
* @param basicDataList {Array<NodeParamItem>} 基础数据list,再运行提交的时候,会把基础数据list和outputParamsDataList合并
* */
class EndNode extends BasicNode {
constructor({title, description, ports, type, shape, x, y, width, height, shapeType, id, allIsOpen, nodeId, outputParamsDataList, outputType, chatType, courtUuidList, basicDataList}) {
super({title, description, ports, type, shape, x, y, width, height, shapeType: shapeType || 'start-node-shape', id, allIsOpen, nodeId, outputParamsDataList});
this.outputParamsDataList = outputParamsDataList
this.outputType = outputType || 3 // 输出类型,0: 输出api参数列表;1:普通报表 2: 物业报表 3:输出接口
// this.nextList = []
// TODO: 目前先写死courtUuidList
this.courtUuidList = courtUuidList?.[0] || [] // 选择所属小区
this.basicDataList = basicDataList || [] // 基础数据列表
this.chatType = chatType || 0 // 聊天类型
this.initParamsFunc({outputParamsDataList: outputParamsDataList || []}) // 默认是加载输出api参数
this.create()
}
// 初始化参数
initParamsFunc ({outputParamsDataList = []}) {
const outputParamsData = new BasicInputParamsList(); // 初始化输出参数列表
const outputParamsItem = new NodeParamItem({name: '', showDesc: false, showRequired: false, showInputParamVal: true, isDefault: false, type: 'output', tableHeaderTitle: ''})
outputParamsData.init({paramsItem: outputParamsItem, showDesc: false, showInputParamVal: true, showInputParamType: false, showTableHeaderTitle: this.outputType !== 3, nameFormItemWidth: 100, paramsDataList: outputParamsDataList})
this.setData({outputParamsData})
}
create () {
// 生成结束节点port
const endNodePorts = new BasicPorts({type: 'endNode'})
this.setPortsList(endNodePorts.getPortsList())
const options = {
shape: 'end-node-shape',
width: this.width,
height: this.height,
component: EndNodeComponent,
data: {
paramsData: this.paramsData,
outputParamsData: this.outputParamsData,
showTableHeaderTitle: false
},
ports: {
// 获取port列表,并合并到ports中
...this.getPortsList(),
}
// component: ProgressNode,
}
register(options);
}
}
export {StartNode, EndNode, LLMNode, PluginNode, ParameterNode}