Skip to content

Commit f9e9c72

Browse files
committed
feat: 完善AI工作流运行测试(通义千问)
1 parent bc6fadc commit f9e9c72

File tree

3 files changed

+204
-5
lines changed

3 files changed

+204
-5
lines changed

src/api/ai/workflow/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ export const deleteWorkflow = async (id) => {
2020
return await request.delete({ url: '/ai/workflow/delete?id=' + id })
2121
}
2222

23-
export const updateWorkflowModel = async (data) => {
24-
return await request.put({ url: '/ai/workflow/updateWorkflowModel', data })
23+
export const testWorkflow = async (data) => {
24+
return await request.post({ url: '/ai/workflow/test', data })
2525
}

src/views/ai/workflow/form/WorkflowDesign.vue

Lines changed: 200 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,206 @@
1313
测试
1414
</el-button>
1515
</div>
16+
17+
<!-- 测试窗口 -->
18+
<el-drawer v-model="showTestDrawer" title="工作流测试" :modal="false">
19+
<fieldset>
20+
<legend class="ml-15px"><h3>运行参数配置</h3></legend>
21+
<div class="p-20px">
22+
<div
23+
class="flex justify-around mb-10px"
24+
v-for="(param, index) in params4Test"
25+
:key="index"
26+
>
27+
<el-select class="w-200px!" v-model="param.key" placeholder="参数名">
28+
<el-option
29+
v-for="(value, key) in paramsOfStartNode"
30+
:key="key"
31+
:label="value?.description || key"
32+
:value="key"
33+
:disabled="!!value?.disabled"
34+
/>
35+
</el-select>
36+
<el-input class="w-200px!" v-model="param.value" placeholder="参数值" />
37+
<el-button type="danger" plain :icon="Delete" circle @click="removeParam(index)" />
38+
</div>
39+
<el-button type="primary" plain @click="addParam">添加参数</el-button>
40+
</div>
41+
</fieldset>
42+
<fieldset class="mt-20px bg-#f8f9fa">
43+
<legend class="ml-15px"><h3>运行结果</h3></legend>
44+
<div class="p-20px">
45+
<div v-if="loading"> <el-text type="primary">执行中...</el-text></div>
46+
<div v-else-if="error">
47+
<el-text type="danger">{{ error }}</el-text>
48+
</div>
49+
<pre v-else-if="testResult" class="result-content"
50+
>{{ JSON.stringify(testResult, null, 2) }}
51+
</pre>
52+
<div v-else> <el-text type="info">点击运行查看结果</el-text> </div>
53+
</div>
54+
</fieldset>
55+
<el-button class="mt-20px w-100%" size="large" type="success" @click="goRun"
56+
>运行流程</el-button
57+
>
58+
</el-drawer>
1659
</div>
1760
</template>
1861

1962
<script setup lang="ts">
2063
import Tinyflow from '@/components/Tinyflow/Tinyflow.vue'
64+
import * as WorkflowApi from '@/api/ai/workflow'
65+
import { Delete } from '@element-plus/icons-vue'
2166
2267
defineProps<{
2368
provider: any
2469
}>()
2570
2671
const tinyflowRef = ref()
2772
const workflowData = inject('workflowData') as Ref
73+
const showTestDrawer = ref(false)
74+
const params4Test = ref([])
75+
const paramsOfStartNode = ref({})
76+
const testResult = ref(null)
77+
const loading = ref(false)
78+
const error = ref(null)
2879
80+
/** 展示工作流测试抽屉 */
2981
const testWorkflowModel = () => {
30-
// TODO @lesan 测试
82+
showTestDrawer.value = !showTestDrawer.value
83+
}
84+
85+
/** 运行流程 */
86+
const goRun = async () => {
87+
try {
88+
const val = tinyflowRef.value.getData()
89+
loading.value = true
90+
error.value = null
91+
testResult.value = null
92+
/// 查找start节点
93+
const startNode = getStartNode()
94+
95+
// 获取参数定义
96+
const parameters = startNode.data?.parameters || []
97+
const paramDefinitions = {}
98+
parameters.forEach((param) => {
99+
paramDefinitions[param.name] = param.dataType
100+
})
101+
102+
// 参数类型转换
103+
const convertedParams = {}
104+
for (const { key, value } of params4Test.value) {
105+
const paramKey = key.trim()
106+
if (!paramKey) continue
107+
108+
let dataType = paramDefinitions[paramKey]
109+
if (!dataType) {
110+
dataType = 'String'
111+
}
112+
113+
try {
114+
convertedParams[paramKey] = convertParamValue(value, dataType)
115+
} catch (e) {
116+
throw new Error(`参数 ${paramKey} 转换失败: ${e.message}`)
117+
}
118+
}
119+
120+
const data = {
121+
graph: JSON.stringify(val),
122+
params: convertedParams
123+
}
124+
125+
const response = await WorkflowApi.testWorkflow(data)
126+
testResult.value = response
127+
} catch (err) {
128+
error.value = err.response?.data?.message || '运行失败,请检查参数和网络连接'
129+
} finally {
130+
loading.value = false
131+
}
132+
}
133+
134+
/** 监听测试抽屉的开启,获取开始节点参数列表 */
135+
watch(showTestDrawer, (value) => {
136+
if (!value) return
137+
138+
/// 查找start节点
139+
const startNode = getStartNode()
140+
141+
// 获取参数定义
142+
const parameters = startNode.data?.parameters || []
143+
const paramDefinitions = {}
144+
145+
// 加入参数选项方便用户添加非必须参数
146+
parameters.forEach((param) => {
147+
paramDefinitions[param.name] = param
148+
})
149+
150+
function mergeIfRequiredButNotSet(target) {
151+
let needPushList = []
152+
for (let key in paramDefinitions) {
153+
let param = paramDefinitions[key]
154+
155+
if (param.required) {
156+
let item = target.find((item) => item.key === key)
157+
158+
if (!item) {
159+
needPushList.push({ key: param.name, value: param.defaultValue || '' })
160+
}
161+
}
162+
}
163+
target.push(...needPushList)
164+
}
165+
// 自动装载需必填的参数
166+
mergeIfRequiredButNotSet(params4Test.value)
167+
168+
paramsOfStartNode.value = paramDefinitions
169+
})
170+
171+
/** 获取开始节点 */
172+
const getStartNode = () => {
173+
const val = tinyflowRef.value.getData()
174+
const startNode = val.nodes.find((node) => node.type === 'startNode')
175+
if (!startNode) {
176+
throw new Error('流程缺少开始节点')
177+
}
178+
return startNode
179+
}
180+
181+
/** 添加参数项 */
182+
const addParam = () => {
183+
params4Test.value.push({ key: '', value: '' })
184+
}
185+
186+
/** 删除参数项 */
187+
const removeParam = (index) => {
188+
params4Test.value.splice(index, 1)
189+
}
190+
191+
/** 类型转换函数 */
192+
const convertParamValue = (value, dataType) => {
193+
if (value === '') return null // 空值处理
194+
195+
switch (dataType) {
196+
case 'String':
197+
return String(value)
198+
case 'Number':
199+
const num = Number(value)
200+
if (isNaN(num)) throw new Error('非数字格式')
201+
return num
202+
case 'Boolean':
203+
if (value.toLowerCase() === 'true') return true
204+
if (value.toLowerCase() === 'false') return false
205+
throw new Error('必须为 true/false')
206+
case 'Object':
207+
case 'Array':
208+
try {
209+
return JSON.parse(value)
210+
} catch (e) {
211+
throw new Error(`JSON格式错误: ${e.message}`)
212+
}
213+
default:
214+
throw new Error(`不支持的类型: ${dataType}`)
215+
}
31216
}
32217
33218
/** 表单校验 */
@@ -47,3 +232,17 @@ defineExpose({
47232
validate
48233
})
49234
</script>
235+
236+
<style lang="css" scoped>
237+
.result-content {
238+
background: white;
239+
padding: 12px;
240+
border-radius: 4px;
241+
max-height: 300px;
242+
overflow: auto;
243+
font-family: Monaco, Consolas, monospace;
244+
font-size: 14px;
245+
line-height: 1.5;
246+
white-space: pre-wrap;
247+
}
248+
</style>

src/views/ai/workflow/form/index.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ import { CommonStatusEnum } from '@/utils/constants'
7373
import * as WorkflowApi from '@/api/ai/workflow'
7474
import BasicInfo from './BasicInfo.vue'
7575
import WorkflowDesign from './WorkflowDesign.vue'
76-
import { ApiKeyApi } from '@/api/ai/model/apiKey'
76+
import { ModelApi } from '@/api/ai/model/model'
7777
7878
const router = useRouter()
7979
const { delView } = useTagsViewStore()
@@ -118,7 +118,7 @@ const initData = async () => {
118118
workflowData.value = JSON.parse(formData.value.graph)
119119
}
120120
121-
const apiKeys = await ApiKeyApi.getApiKeySimpleList()
121+
const apiKeys = await ModelApi.getModelSimpleList(1)
122122
provider.value = {
123123
llm: () =>
124124
apiKeys.map(({ id, name }) => ({

0 commit comments

Comments
 (0)