| | |
| | | <div class="relative h-full w-full" id="main-canvas" @drop="handleOnDrop" @dragover="handleOnDragOver"> |
| | | <VueFlow v-model="elements" :node-types="nodeTypes" :connection-mode="ConnectionMode.Strict"> |
| | | <template #node-start="startNodeProps"> |
| | | <StartNode v-bind="startNodeProps" :isViewMode="isViewMode" /> |
| | | <StartNode ref="nodeRef" v-bind="startNodeProps" :isViewMode="isViewMode" /> |
| | | </template> |
| | | <template #node-condition="conditionNodeProps"> |
| | | <ConditionNode v-bind="conditionNodeProps" :isViewMode="isViewMode" /> |
| | | <ConditionNode ref="nodeRef" v-bind="conditionNodeProps" :isViewMode="isViewMode" /> |
| | | </template> |
| | | <template #node-output_msg="outputNodeProps"> |
| | | <OutputNode v-bind="outputNodeProps" :isViewMode="isViewMode" /> |
| | | <OutputNode ref="nodeRef" v-bind="outputNodeProps" :isViewMode="isViewMode" /> |
| | | </template> |
| | | |
| | | <template #node-end="endNodeProps"> |
| | | <EndNode v-bind="endNodeProps" :isViewMode="isViewMode" /> |
| | | <EndNode ref="nodeRef" v-bind="endNodeProps" :isViewMode="isViewMode" /> |
| | | </template> |
| | | |
| | | <template #edge-custom="customEdgeProps"> |
| | |
| | | </template> |
| | | |
| | | <template #node-agent="agentNodeProps"> |
| | | <AgentNode v-bind="agentNodeProps" :agentNames="agentNames" :isViewMode="isViewMode" /> |
| | | <AgentNode ref="nodeRef" v-bind="agentNodeProps" :agentNames="agentNames" :isViewMode="isViewMode" /> |
| | | </template> |
| | | <template #node-func="funcNodeProps"> |
| | | <FuncNode v-bind="funcNodeProps" :funcNames="funcNames" :isViewMode="isViewMode" /> |
| | | <FuncNode ref="nodeRef" v-bind="funcNodeProps" :funcNames="funcNames" :isViewMode="isViewMode" /> |
| | | </template> |
| | | |
| | | <template #node-code="codeNodeProps"> |
| | | <CodeNode v-bind="codeNodeProps" :isViewMode="isViewMode" /> |
| | | <CodeNode ref="nodeRef" v-bind="codeNodeProps" :isViewMode="isViewMode" /> |
| | | </template> |
| | | <template #node-python_code="pythonCodeNodeProps"> |
| | | <PythonCodeNode v-bind="pythonCodeNodeProps" :isViewMode="isViewMode" /> |
| | | <PythonCodeNode ref="nodeRef" v-bind="pythonCodeNodeProps" :isViewMode="isViewMode" /> |
| | | </template> |
| | | <template #node-text_resource="textResourceNodeProps"> |
| | | <TextResourceNode v-bind="textResourceNodeProps" :isViewMode="isViewMode" /> |
| | | <TextResourceNode ref="nodeRef" v-bind="textResourceNodeProps" :isViewMode="isViewMode" /> |
| | | </template> |
| | | |
| | | <template #node-analysis="analysisNodeProps"> |
| | | <AnalysisNode v-bind="analysisNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" /> |
| | | <AnalysisNode ref="nodeRef" v-bind="analysisNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" /> |
| | | </template> |
| | | |
| | | <template #node-LLM="llmNodeProps"> |
| | | <LLMNode v-bind="llmNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" /> |
| | | <LLMNode ref="nodeRef" v-bind="llmNodeProps" :llmInfoList="llmInfoList" :isViewMode="isViewMode" /> |
| | | </template> |
| | | <Controls :showInteractive="false"/> |
| | | <Controls :showInteractive="false" /> |
| | | <Background /> |
| | | <MiniMap pannable zoomable /> |
| | | </VueFlow> |
| | |
| | | import { Controls } from '@vue-flow/controls'; |
| | | import { MiniMap } from '@vue-flow/minimap'; |
| | | |
| | | import type { Dimensions, Elements, FlowOptions, FlowProps } from '@vue-flow/core'; |
| | | import type { Dimensions, Elements, FlowProps } from '@vue-flow/core'; |
| | | import { ConnectionMode, MarkerType, useVueFlow, VueFlow } from '@vue-flow/core'; |
| | | import { nextTick, onMounted, ref, watch } from 'vue'; |
| | | import { Test_data } from './testData'; |
| | |
| | | import AgentNode from './ui/nodes/AgentNode.vue'; |
| | | import AnalysisNode from './ui/nodes/AnalysisNode.vue'; |
| | | import CodeNode from './ui/nodes/CodeNode.vue'; |
| | | import PythonCodeNode from './ui/nodes/PythonCodeNode.vue'; |
| | | import ConditionNode from './ui/nodes/ConditionNode.vue'; |
| | | import EndNode from './ui/nodes/EndNode.vue'; |
| | | import FuncNode from './ui/nodes/FuncNode.vue'; |
| | | import LLMNode from './ui/nodes/LLMNode.vue'; |
| | | import OutputNode from './ui/nodes/OutputNode.vue'; |
| | | import PythonCodeNode from './ui/nodes/PythonCodeNode.vue'; |
| | | import StartNode from './ui/nodes/StartNode.vue'; |
| | | import TextResourceNode from './ui/nodes/TextResourceNode.vue'; |
| | | import { NodeType, nodeTypeMap } from './vueFlowEnum'; |
| | |
| | | |
| | | const nodeTypes = {}; |
| | | const elements = ref<Elements>(); |
| | | const { findNode, nodes, addNodes, addEdges, project, vueFlowRef, onConnect, setNodes, setEdges, setViewport, fitView,setInteractive } = useVueFlow( |
| | | const { |
| | | findNode, |
| | | nodes, |
| | | addNodes, |
| | | addEdges, |
| | | project, |
| | | vueFlowRef, |
| | | onConnect, |
| | | setNodes, |
| | | setEdges, |
| | | setViewport, |
| | | fitView, |
| | | setInteractive, |
| | | } = useVueFlow( |
| | | Object.keys(props.flowJson).length > 0 |
| | | ? props.flowJson |
| | | : ({ |
| | |
| | | }, |
| | | ], |
| | | edges: [], |
| | | |
| | | |
| | | } as FlowProps) |
| | | ); |
| | | |
| | |
| | | |
| | | // addEdges()); |
| | | }); |
| | | const initFlowStatus = () =>{ |
| | | const initFlowStatus = () => { |
| | | fitView(); |
| | | |
| | | setInteractive(!props.isViewMode); |
| | | } |
| | | }; |
| | | onMounted(() => { |
| | | setTimeout(() => { |
| | | initFlowStatus(); |
| | |
| | | event.dataTransfer.dropEffect = 'move'; |
| | | } |
| | | } |
| | | |
| | | const nodeRef = ref(); |
| | | const validateForm = () => { |
| | | // nodeRef.value.validateForm(); |
| | | console.log('🚀 ~ nodeRef.value:', nodeRef.value); |
| | | }; |
| | | defineExpose({ |
| | | validateForm, |
| | | }); |
| | | </script> |
| | | <style lang="scss"> |
| | | @import '@vue-flow/core/dist/style.css'; |
| | |
| | | :isViewMode="isViewMode" |
| | | > |
| | | <Handle :id="targetHandleId" type="target" :position="Position.Left" /> |
| | | |
| | | <FieldLayout :title="VueFlowHelper.getConfigValue(modelSetting, 'name', '模型设置')"> |
| | | <div class="w-full flex-items-center gap-x-2"> |
| | | <div class="flex-column gap-y-1.5"> |
| | | <span class="text-gray-500">{{ VueFlowHelper.getParams(modelSetting, 'llm_model').label }}</span> |
| | | <el-tree-select |
| | | v-model="VueFlowHelper.getParams(modelSetting, 'llm_model').value" |
| | | :data="llmInfoList" |
| | | :props="{ label: 'name', value: 'id' }" |
| | | :render-after-expand="false" |
| | | :check-strictly="false" |
| | | :show-checkbox="false" |
| | | :accordion="true" |
| | | :disabled="isViewMode" |
| | | filterable |
| | | :placeholder="VueFlowHelper.getParams(modelSetting, 'llm_model').placeholder" |
| | | /> |
| | | <el-form :model="data" :rules="formRules" label-position="right" label-width="60px" status-icon> |
| | | <FieldLayout :title="VueFlowHelper.getConfigValue(modelSetting, 'name', '模型设置')"> |
| | | <div class="w-full flex-items-center gap-x-2"> |
| | | <div class="flex-column gap-y-1.5"> |
| | | <el-form-item |
| | | labelPosition="top" |
| | | :label="VueFlowHelper.getParams(modelSetting, 'llm_model').label" |
| | | prop="group_params.0.params.0.value" |
| | | > |
| | | <el-tree-select |
| | | v-model="VueFlowHelper.getParams(modelSetting, 'llm_model').value" |
| | | :data="llmInfoList" |
| | | :props="{ label: 'name', value: 'id' }" |
| | | :render-after-expand="false" |
| | | :check-strictly="false" |
| | | :show-checkbox="false" |
| | | :accordion="true" |
| | | :disabled="isViewMode" |
| | | filterable |
| | | clearable |
| | | :placeholder="VueFlowHelper.getParams(modelSetting, 'llm_model').placeholder" |
| | | /> |
| | | </el-form-item> |
| | | </div> |
| | | <div class="flex-column gap-y-1.5"> |
| | | <el-form-item |
| | | labelPosition="top" |
| | | :label="VueFlowHelper.getParams(modelSetting, 'temperature').label" |
| | | prop="group_params.0.params.1.value" |
| | | > |
| | | <el-input-number |
| | | class="w-full" |
| | | controls-position="right" |
| | | v-model="VueFlowHelper.getParams(modelSetting, 'temperature').value" |
| | | :readonly="isViewMode" |
| | | placeholder="" |
| | | ></el-input-number> |
| | | </el-form-item> |
| | | </div> |
| | | </div> |
| | | <div class="flex-column gap-y-1.5"> |
| | | <span class="text-gray-500">{{ VueFlowHelper.getParams(modelSetting, 'temperature').label }}</span> |
| | | <el-input-number |
| | | class="w-full" |
| | | controls-position="right" |
| | | v-model="VueFlowHelper.getParams(modelSetting, 'temperature').value" |
| | | </FieldLayout> |
| | | <FieldLayout :title="prompt.name"> |
| | | <CodeEditor |
| | | :title="prompt.name" |
| | | class="nowheel" |
| | | v-model:editValue="VueFlowHelper.getParams(prompt, 'prompt').value" |
| | | :maxHeight="180" |
| | | :disabled="isViewMode" |
| | | /> |
| | | </FieldLayout> |
| | | <FieldLayout :title="outputParam.name" > |
| | | <el-form-item labelWidth="0" prop="group_params.2.params.0.value"> |
| | | <el-input |
| | | class="w-full flex-0" |
| | | v-model="outputParam.params[0].value" |
| | | placeholder="输出参数名" |
| | | :readonly="isViewMode" |
| | | placeholder="" |
| | | ></el-input-number> |
| | | </div> |
| | | </div> |
| | | </FieldLayout> |
| | | <FieldLayout :title="prompt.name"> |
| | | <CodeEditor |
| | | :title="prompt.name" |
| | | class="nowheel" |
| | | v-model:editValue="VueFlowHelper.getParams(prompt, 'prompt').value" |
| | | :maxHeight="180" |
| | | :disabled="isViewMode" |
| | | /> |
| | | </FieldLayout> |
| | | <FieldLayout :title="outputParam.name"> |
| | | <el-input class="w-full flex-0" v-model="outputParam.params[0].value" placeholder="输出参数名" :readonly="isViewMode"></el-input> |
| | | </FieldLayout> |
| | | ></el-input> |
| | | </el-form-item> |
| | | </FieldLayout> |
| | | </el-form> |
| | | |
| | | <Handle :id="sourceHandleId" type="source" :position="Position.Right" /> |
| | | </NodeBasicLayout> |
| | | </template> |
| | |
| | | const targetHandleId = ref(VueFlowHelper.getHandleId(node.node, 'target')); |
| | | |
| | | const data = ref(node.node.data); |
| | | console.log('🚀 ~ data.value:', data.value); |
| | | const modelSetting = ref(VueFlowHelper.getGroupParam(data.value, 0)); |
| | | const prompt = ref(VueFlowHelper.getGroupParam(data.value, 1)); |
| | | const outputParam = ref(VueFlowHelper.getGroupParam(data.value, 2)); |
| | | VueFlowHelper.getConfigValue(VueFlowHelper.getParams(prompt.value, 'prompt'), 'label', ''); |
| | | |
| | | const formRules = ref({ |
| | | 'group_params.0.params.0.value': [{ required: true, message: '请选择模型', trigger: 'change' }], |
| | | // 'group_params.0.params.1.value': [{ required: true, message: '请输入温度', trigger: 'blur' }], |
| | | // 'group_params.2.params.0.value': [{ required: true, message: '请输入输出参数名', trigger: 'blur' }], |
| | | }); |
| | | |
| | | const formRef = ref(); |
| | | const validateForm = async () => { |
| | | const valid = await formRef.value.validate(); |
| | | if (valid) { |
| | | console.log('submit!'); |
| | | } |
| | | }; |
| | | |
| | | defineExpose({ |
| | | validateForm, |
| | | }); |
| | | </script> |
| | |
| | | <template> |
| | | <div class="flex flex-col" :class="[`gap-${gapLen}`]"> |
| | | <div class="flex-items-center justify-between"> |
| | | <div class="text-base" :class="[level === '1' ? 'font-bold' : 'text-secondary font-bold']">{{ title }}</div> |
| | | <div class="text-base" :class="[level === '1' ? 'font-bold' : 'text-secondary font-bold', { 'required-field': required }]"> |
| | | {{ title }} |
| | | </div> |
| | | <div class="flex-items-center gap-3" v-if="$slots.right"> |
| | | <slot name="right"></slot> |
| | | </div> |
| | |
| | | type: String as () => HeaderLevel, |
| | | default: '1', |
| | | }, |
| | | required: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | }); |
| | | |
| | | const gapLen = computed(() => { |
| | |
| | | } |
| | | }); |
| | | </script> |
| | | <style scoped lang="scss"></style> |
| | | <style scoped lang="scss"> |
| | | .required-field::before { |
| | | color: var(--el-color-danger); |
| | | content: '*'; |
| | | margin-right: 4px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="absolute bottom-0 left-0 right-0 top-0 bg-page text-base"> |
| | | <div class="relative flex h-full w-full flex-col"> |
| | | <Header :isViewMode="isViewMode" v-if="flowAgent" :flowAgent="flowAgent" :queryId="queryId" /> |
| | | <Header :isViewMode="isViewMode" v-if="flowAgent" :flowAgent="flowAgent" :queryId="queryId" @saveClick="validateForm" /> |
| | | <main class="relative flex h-full w-full flex-1"> |
| | | <Sidebar v-if="!isViewMode" class="w-52" @dragstart="handleOnDragStart" /> |
| | | <div class="relative h-full flex-1 overflow-hidden" v-if="flowJson"> |
| | | <MainCanvas |
| | | ref="mainCanvasRef" |
| | | :isViewMode="isViewMode" |
| | | :flowJson="flowJson" |
| | | :agentNames="agentNames" |
| | |
| | | llmInfoList.value = logicTree; |
| | | }; |
| | | |
| | | const mainCanvasRef = ref(); |
| | | const validateForm = () => { |
| | | mainCanvasRef.value.validateForm(); |
| | | }; |
| | | |
| | | |
| | | onMounted(() => { |
| | | if (!queryId.value) return; |
| | | handleGetJSON(queryId.value); |
| | |
| | | default: false, |
| | | }, |
| | | }); |
| | | const emit = defineEmits(['export']); |
| | | const emit = defineEmits(['export','saveClick']); |
| | | const { toObject, fromObject, nodes, edges, removeEdges, removeNodes } = useVueFlow(); |
| | | const { files, open, reset, onCancel, onChange } = useFileDialog({ |
| | | // accept: 'image/*', // Set to accept only image files |
| | |
| | | json_flow: jsonStr, |
| | | }); |
| | | ElMessage.success('保存成功!'); |
| | | emit('saveClick'); |
| | | }; |
| | | |
| | | onChange((file) => { |