From 7e59b188fb37d48d39e8c35b8bbced3836f4dd6d Mon Sep 17 00:00:00 2001
From: wujingjing <gersonwu@qq.com>
Date: 星期二, 11 二月 2025 16:30:42 +0800
Subject: [PATCH] python code node

---
 vite.config.ts                                         |    2 
 src/components/input/codeEditor/types.ts               |    3 
 package-lock.json                                      |   23 ++++
 src/components/vue-flow/VueFlowHelper.ts               |  103 ++++++++++++++------
 src/components/input/codeEditor/useEditorExtensions.ts |   15 ++
 package.json                                           |    1 
 src/components/vue-flow/ui/nodes/PythonCodeNode.vue    |  118 +++++++++++++++++++++++
 src/components/vue-flow/ui/VueFlowConfig.ts            |   10 ++
 src/components/vue-flow/MainCanvas.vue                 |    4 
 src/components/vue-flow/vueFlowEnum.ts                 |    4 
 10 files changed, 249 insertions(+), 34 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index f5902da..af0981f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
 				"@antv/g6": "^5.0.27",
 				"@codemirror/lang-javascript": "^6.2.2",
 				"@codemirror/lang-json": "^6.0.1",
+				"@codemirror/lang-python": "^6.1.7",
 				"@codemirror/lang-sql": "^6.7.1",
 				"@codemirror/lang-xml": "^6.1.0",
 				"@codemirror/state": "6.x",
@@ -605,6 +606,18 @@
 			"dependencies": {
 				"@codemirror/language": "^6.0.0",
 				"@lezer/json": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-python": {
+			"version": "6.1.7",
+			"resolved": "https://registry.npmmirror.com/@codemirror/lang-python/-/lang-python-6.1.7.tgz",
+			"integrity": "sha512-mZnFTsL4lW5p9ch8uKNKeRU3xGGxr1QpESLilfON2E3fQzOa/OygEMkaDvERvXDJWJA9U9oN/D4w0ZuUzNO4+g==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.3.2",
+				"@codemirror/language": "^6.8.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.2.1",
+				"@lezer/python": "^1.1.4"
 			}
 		},
 		"node_modules/@codemirror/lang-sql": {
@@ -1548,6 +1561,16 @@
 				"@lezer/common": "^1.0.0"
 			}
 		},
+		"node_modules/@lezer/python": {
+			"version": "1.1.15",
+			"resolved": "https://registry.npmmirror.com/@lezer/python/-/python-1.1.15.tgz",
+			"integrity": "sha512-aVQ43m2zk4FZYedCqL0KHPEUsqZOrmAvRhkhHlVPnDD1HODDyyQv5BRIuod4DadkgBEZd53vQOtXTonNbEgjrQ==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
 		"node_modules/@lezer/xml": {
 			"version": "1.0.5",
 			"resolved": "https://registry.npmmirror.com/@lezer/xml/-/xml-1.0.5.tgz",
diff --git a/package.json b/package.json
index 830099d..a1b8af9 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
 		"@antv/g6": "^5.0.27",
 		"@codemirror/lang-javascript": "^6.2.2",
 		"@codemirror/lang-json": "^6.0.1",
+		"@codemirror/lang-python": "^6.1.7",
 		"@codemirror/lang-sql": "^6.7.1",
 		"@codemirror/lang-xml": "^6.1.0",
 		"@codemirror/state": "6.x",
diff --git a/src/components/input/codeEditor/types.ts b/src/components/input/codeEditor/types.ts
index 24df406..7d6188e 100644
--- a/src/components/input/codeEditor/types.ts
+++ b/src/components/input/codeEditor/types.ts
@@ -1,5 +1,6 @@
-export type TextType = 'text' | 'javascript';
+export type TextType = 'text' | 'javascript' |'python';
 export const textTypeMap = {
 	text: '鏂囨湰',
 	javascript: 'JavaScript',
+	python:'Python'
 };
\ No newline at end of file
diff --git a/src/components/input/codeEditor/useEditorExtensions.ts b/src/components/input/codeEditor/useEditorExtensions.ts
index 27a7db2..46914fc 100644
--- a/src/components/input/codeEditor/useEditorExtensions.ts
+++ b/src/components/input/codeEditor/useEditorExtensions.ts
@@ -1,4 +1,6 @@
 import { javascript } from '@codemirror/lang-javascript';
+import { python } from '@codemirror/lang-python';
+
 import { vscodeDark } from '@uiw/codemirror-theme-vscode';
 import type { Ref } from 'vue';
 import { computed, ref } from 'vue';
@@ -9,10 +11,21 @@
 };
 export const useEditorExtensions = (options: UseEditorExtensionsOptions) => {
 	const javascriptHighlight = javascript();
+	const pythonHighlight = python();
 	const { activeHighlight } = options;
 
 	const editorExtensions = computed(() => {
-		return activeHighlight.value !== 'text' ? [javascriptHighlight, vscodeDark] : [];
+		switch (activeHighlight.value) {
+			case 'text':
+				return [];
+			case 'javascript':
+				return [javascriptHighlight, vscodeDark];
+			case 'python':
+				return [pythonHighlight, vscodeDark];
+
+			default:
+				return [];
+		}
 	});
 
 	return {
diff --git a/src/components/vue-flow/MainCanvas.vue b/src/components/vue-flow/MainCanvas.vue
index 7ad78d8..1edec66 100644
--- a/src/components/vue-flow/MainCanvas.vue
+++ b/src/components/vue-flow/MainCanvas.vue
@@ -29,6 +29,9 @@
 			<template #node-code="codeNodeProps">
 				<CodeNode v-bind="codeNodeProps" :isViewMode="isViewMode" />
 			</template>
+			<template #node-python_code="pythonCodeNodeProps">
+				<PythonCodeNode v-bind="pythonCodeNodeProps" :isViewMode="isViewMode" />
+			</template>
 			<template #node-text_resource="textResourceNodeProps">
 				<TextResourceNode v-bind="textResourceNodeProps" :isViewMode="isViewMode" />
 			</template>
@@ -60,6 +63,7 @@
 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';
diff --git a/src/components/vue-flow/VueFlowHelper.ts b/src/components/vue-flow/VueFlowHelper.ts
index 9b05a81..b178d2c 100644
--- a/src/components/vue-flow/VueFlowHelper.ts
+++ b/src/components/vue-flow/VueFlowHelper.ts
@@ -66,7 +66,6 @@
 					description: '璋冪敤澶фā鍨嬪洖绛旂敤鎴烽棶棰樻垨鑰呭鐞嗕换鍔°��',
 				};
 				data[VueFlowConstant.GROUP_PARAMS_KEY] = [
-					
 					{
 						name: '妯″瀷璁剧疆',
 
@@ -88,37 +87,37 @@
 					},
 					{
 						name: '杈撳嚭鍙傛暟鍚�',
-						[VueFlowConstant.PARAMS_KEY]: [{ key: 'key',  type: 'input', value: '' }],
+						[VueFlowConstant.PARAMS_KEY]: [{ key: 'key', type: 'input', value: '' }],
 					},
 				];
 				break;
-				case NodeType.Analysis:
-					data = {
-						...data,
-						description: '',
-					};
-					data[VueFlowConstant.GROUP_PARAMS_KEY] = [
-						{
-							name: '鍒嗘瀽璁剧疆',
-	
-							[VueFlowConstant.PARAMS_KEY]: [
-								{
-									key: 'llm_model',
-									label: '妯″瀷',
-									type: 'llm_model',
-									value: '',
-									required: true,
-									placeholder: '璇烽�夋嫨妯″瀷',
-								},
-								{ key: 'temperature', label: '娓╁害', type: 'slide', scope: [0, 2], step: 0.1, value: 0.6 },
-							],
-						},
-						{
-							name: '鎻愮ず璇�',
-							[VueFlowConstant.PARAMS_KEY]: [{ key: 'prompt', label: '', type: 'textarea', value: '' }],
-						},
-					];
-					break;
+			case NodeType.Analysis:
+				data = {
+					...data,
+					description: '',
+				};
+				data[VueFlowConstant.GROUP_PARAMS_KEY] = [
+					{
+						name: '鍒嗘瀽璁剧疆',
+
+						[VueFlowConstant.PARAMS_KEY]: [
+							{
+								key: 'llm_model',
+								label: '妯″瀷',
+								type: 'llm_model',
+								value: '',
+								required: true,
+								placeholder: '璇烽�夋嫨妯″瀷',
+							},
+							{ key: 'temperature', label: '娓╁害', type: 'slide', scope: [0, 2], step: 0.1, value: 0.6 },
+						],
+					},
+					{
+						name: '鎻愮ず璇�',
+						[VueFlowConstant.PARAMS_KEY]: [{ key: 'prompt', label: '', type: 'textarea', value: '' }],
+					},
+				];
+				break;
 			case NodeType.Code:
 				data = {
 					...data,
@@ -163,6 +162,50 @@
 										{ key: 'result1', type: 'string' },
 										{ key: 'result2', type: 'string' },
 									],
+								},
+							],
+						},
+					],
+				};
+				break;
+
+			case NodeType.PythonCode:
+				data = {
+					...data,
+					description: '鑷畾涔夐渶瑕佹墽琛岀殑python浠g爜銆�',
+					[VueFlowConstant.GROUP_PARAMS_KEY]: [
+						{
+							name: '鍏ュ弬',
+							[VueFlowConstant.PARAMS_KEY]: [
+								{
+									key: 'code_input',
+									type: 'code_input',
+									required: true,
+									value: { type: 'input', label: '', value: '' },
+								},
+							],
+						},
+						{
+							name: '鍑哄弬',
+							[VueFlowConstant.PARAMS_KEY]: [
+								{
+									key: 'code_output',
+									type: 'code_output',
+									required: true,
+									value: { type: 'input', label: '', value: '' },
+								},
+							],
+						},
+						{
+							name: '鎵ц浠g爜',
+							[VueFlowConstant.PARAMS_KEY]: [
+								{
+									key: 'code',
+									type: 'code',
+									required: true,
+									value: 'const main = (arg1, arg2) =>{\n  return {\n    result1: arg1,\n    result2: arg2\n  }\n}',
+									language: ['python'],
+									defaultLanguage: 'python',
 								},
 							],
 						},
@@ -291,8 +334,6 @@
 
 		return val;
 	};
-
-
 
 	static getHandleId = (node: any, handleType: HandleType, order?: number) => {
 		const orderSuffix = order == undefined ? '' : `__${order + ''}`;
diff --git a/src/components/vue-flow/ui/VueFlowConfig.ts b/src/components/vue-flow/ui/VueFlowConfig.ts
index ce38640..fc21621 100644
--- a/src/components/vue-flow/ui/VueFlowConfig.ts
+++ b/src/components/vue-flow/ui/VueFlowConfig.ts
@@ -85,6 +85,16 @@
 			},
 		],
 		[
+			NodeType.PythonCode,
+			{
+				type: NodeType.PythonCode,
+				title: nodeTypeMap[NodeType.PythonCode],
+				icon: 'didaima',
+				fontSize: '18',
+				class: 'bg-[#0062be] !p-1',
+			},
+		],
+		[
 			NodeType.TextResource,
 			{
 				type: NodeType.TextResource,
diff --git a/src/components/vue-flow/ui/nodes/PythonCodeNode.vue b/src/components/vue-flow/ui/nodes/PythonCodeNode.vue
new file mode 100644
index 0000000..8647a82
--- /dev/null
+++ b/src/components/vue-flow/ui/nodes/PythonCodeNode.vue
@@ -0,0 +1,118 @@
+<template>
+	<NodeBasicLayout
+		v-model:title="data.title"
+		:type="NodeType.PythonCode"
+		:showOffset="false"
+		:description="data.description"
+		:isViewMode="isViewMode"
+	>
+		<Handle :id="targetHandleId" type="target" :position="Position.Left" />
+		<FieldLayout :title="codeInput.name">
+			<!-- codeInput.params[0].value -->
+			<el-input filterable class="w-[120px] flex-0" v-model="codeInput.params[0].value.value" placeholder="鍙傛暟鍚�" :readonly="isViewMode"></el-input>
+		</FieldLayout>
+
+		<FieldLayout :title="codeOutput.name">
+			<!-- codeOutput.params[0].value -->
+			<el-input filterable class="w-[120px] flex-0" v-model="codeOutput.params[0].value.value" placeholder="鍙傛暟鍚�" :readonly="isViewMode"></el-input>
+		</FieldLayout>
+
+		<FieldLayout :title="codeStr.name">
+			<CodeEditor
+				:title="codeStr.name"
+				:language="codeLanguage"
+				v-model:defaultLanguage="codeStr.params[0].defaultLanguage"
+				v-model:editValue="codeStr.params[0].value"
+				:disabled="isViewMode"
+			/>
+			<template #right>
+				<el-select :disabled="isViewMode" size="small" class="w-[100px]" v-model="codeStr.params[0].defaultLanguage">
+					<el-option v-for="item in codeLanguage" :key="item" :value="item" :label="textTypeMap[item]"></el-option>
+				</el-select>
+			</template>
+		</FieldLayout>
+		<Handle :id="sourceHandleId" type="source" :position="Position.Right" />
+	</NodeBasicLayout>
+</template>
+
+<script lang="ts" setup>
+import type { NodeProps } from '@vue-flow/core';
+import { Handle, Position, useNode } from '@vue-flow/core';
+import { ref } from 'vue';
+import { VueFlowHelper } from '../../VueFlowHelper';
+import { NodeType, ParameterType, parameterTypeMap } from '../../vueFlowEnum';
+import CodeEditor from '/@/components/input/codeEditor/index.vue';
+// import CodeEditDialog from './components/CodeEditDlg.vue';
+import FieldLayout from './components/FieldLayout.vue';
+import NodeBasicLayout from './components/NodeBasicLayout.vue';
+import type { LLMNodeData, LLMNodeEvents } from './index';
+import { textTypeMap } from '/@/components/input/codeEditor/types';
+defineProps<
+	NodeProps<LLMNodeData, LLMNodeEvents> & {
+		isViewMode?: boolean;
+	}
+>();
+const node = useNode();
+const targetHandleId = ref(VueFlowHelper.getHandleId(node.node, 'target'));
+const sourceHandleId = ref(VueFlowHelper.getHandleId(node.node, 'source'));
+const data = ref(node.node.data);
+const codeInput = ref(VueFlowHelper.getGroupParam(data.value, 0));
+const codeStr = ref(VueFlowHelper.getGroupParam(data.value, 2));
+const codeOutput = ref(VueFlowHelper.getGroupParam(data.value, 1));
+
+const codeLanguage = ref(VueFlowHelper.getConfigValue(codeStr.value.params[0], 'language', ['text', 'javascript']));
+
+// defaultLanguage 涓嶅瓨鍦紝璁剧疆榛樿鍊�
+!codeStr.value.params[0].defaultLanguage && (codeStr.value.params[0].defaultLanguage = 'javascript');
+
+const getInputEmptyItem = () => {
+	return {
+		key: '',
+		type: 'input',
+		label: '',
+		value: '',
+	};
+};
+
+const getOutputEmptyItem = () => {
+	return {
+		key: '',
+		type: ParameterType.String,
+	};
+};
+const delInputVarItem = (index) => {
+	if (!codeInput.value.params[0].value || codeInput.value.params[0].value.length === 0) {
+		return;
+	}
+	// if (codeInput.value.params[0].value?.length === 1) {
+	// 	codeInput.value.params[0].value = getEmptyItem();
+	// 	return;
+	// }
+	codeInput.value.params[0].value.splice(index, 1);
+};
+
+const addInputVarItem = () => {
+	if (!codeInput.value.params[0].value || codeInput.value.params[0].value.length === 0) {
+		codeInput.value.params[0].value = [];
+	}
+	codeInput.value.params[0].value.push(getInputEmptyItem());
+};
+
+const delOutputVarItem = (index) => {
+	if (!codeOutput.value.params[0].value || codeOutput.value.params[0].value.length === 0) {
+		return;
+	}
+	// if (codeOutput.value.params[0].value?.length === 1) {
+	// 	codeOutput.value.params[0].value = getOutputEmptyItem();
+	// 	return;
+	// }
+	codeOutput.value.params[0].value.splice(index, 1);
+};
+
+const addOutputVarItem = () => {
+	if (!codeOutput.value.params[0].value || codeOutput.value.params[0].value.length === 0) {
+		codeOutput.value.params[0].value = [];
+	}
+	codeOutput.value.params[0].value.push(getOutputEmptyItem());
+};
+</script>
diff --git a/src/components/vue-flow/vueFlowEnum.ts b/src/components/vue-flow/vueFlowEnum.ts
index ec1799e..c1f636a 100644
--- a/src/components/vue-flow/vueFlowEnum.ts
+++ b/src/components/vue-flow/vueFlowEnum.ts
@@ -20,6 +20,8 @@
 	Agent = 'agent',
 	Func = 'func',
 	Code = 'code',
+	PythonCode = 'python_code',
+
 	TextResource = 'text_resource',
 	Analysis = 'analysis',
 }
@@ -34,6 +36,8 @@
 	[NodeType.Agent]: '浠g悊',
 	[NodeType.Func]: '鎵ц鍔熻兘',
 	[NodeType.Code]: '浠g爜',
+	[NodeType.PythonCode]: 'python浠g爜',
+
 	[NodeType.TextResource]: '鏂囨湰璧勬簮',
 	[NodeType.Analysis]: '鍒嗘瀽',
 };
diff --git a/vite.config.ts b/vite.config.ts
index fa15255..a568226 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -45,7 +45,7 @@
 			host: '0.0.0.0',
 			port: env.VITE_PORT as unknown as number,
 			open: JSON.parse(env.VITE_OPEN),
-			hmr: true,
+			hmr: false,
 			proxy: {
 				'/gitee': {
 					target: 'https://gitee.com',

--
Gitblit v1.9.3