feat: 完善AI工作流运行测试(通义千问)

This commit is contained in:
Lesan 2025-04-29 14:15:43 +08:00
parent bc6fadca41
commit f9e9c72969
3 changed files with 204 additions and 5 deletions

View File

@ -20,6 +20,6 @@ export const deleteWorkflow = async (id) => {
return await request.delete({ url: '/ai/workflow/delete?id=' + id }) return await request.delete({ url: '/ai/workflow/delete?id=' + id })
} }
export const updateWorkflowModel = async (data) => { export const testWorkflow = async (data) => {
return await request.put({ url: '/ai/workflow/updateWorkflowModel', data }) return await request.post({ url: '/ai/workflow/test', data })
} }

View File

@ -13,11 +13,56 @@
测试 测试
</el-button> </el-button>
</div> </div>
<!-- 测试窗口 -->
<el-drawer v-model="showTestDrawer" title="工作流测试" :modal="false">
<fieldset>
<legend class="ml-15px"><h3>运行参数配置</h3></legend>
<div class="p-20px">
<div
class="flex justify-around mb-10px"
v-for="(param, index) in params4Test"
:key="index"
>
<el-select class="w-200px!" v-model="param.key" placeholder="参数名">
<el-option
v-for="(value, key) in paramsOfStartNode"
:key="key"
:label="value?.description || key"
:value="key"
:disabled="!!value?.disabled"
/>
</el-select>
<el-input class="w-200px!" v-model="param.value" placeholder="参数值" />
<el-button type="danger" plain :icon="Delete" circle @click="removeParam(index)" />
</div>
<el-button type="primary" plain @click="addParam">添加参数</el-button>
</div>
</fieldset>
<fieldset class="mt-20px bg-#f8f9fa">
<legend class="ml-15px"><h3>运行结果</h3></legend>
<div class="p-20px">
<div v-if="loading"> <el-text type="primary">执行中...</el-text></div>
<div v-else-if="error">
<el-text type="danger">{{ error }}</el-text>
</div>
<pre v-else-if="testResult" class="result-content"
>{{ JSON.stringify(testResult, null, 2) }}
</pre>
<div v-else> <el-text type="info">点击运行查看结果</el-text> </div>
</div>
</fieldset>
<el-button class="mt-20px w-100%" size="large" type="success" @click="goRun"
>运行流程</el-button
>
</el-drawer>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import Tinyflow from '@/components/Tinyflow/Tinyflow.vue' import Tinyflow from '@/components/Tinyflow/Tinyflow.vue'
import * as WorkflowApi from '@/api/ai/workflow'
import { Delete } from '@element-plus/icons-vue'
defineProps<{ defineProps<{
provider: any provider: any
@ -25,9 +70,149 @@ defineProps<{
const tinyflowRef = ref() const tinyflowRef = ref()
const workflowData = inject('workflowData') as Ref const workflowData = inject('workflowData') as Ref
const showTestDrawer = ref(false)
const params4Test = ref([])
const paramsOfStartNode = ref({})
const testResult = ref(null)
const loading = ref(false)
const error = ref(null)
/** 展示工作流测试抽屉 */
const testWorkflowModel = () => { const testWorkflowModel = () => {
// TODO @lesan showTestDrawer.value = !showTestDrawer.value
}
/** 运行流程 */
const goRun = async () => {
try {
const val = tinyflowRef.value.getData()
loading.value = true
error.value = null
testResult.value = null
/// start
const startNode = getStartNode()
//
const parameters = startNode.data?.parameters || []
const paramDefinitions = {}
parameters.forEach((param) => {
paramDefinitions[param.name] = param.dataType
})
//
const convertedParams = {}
for (const { key, value } of params4Test.value) {
const paramKey = key.trim()
if (!paramKey) continue
let dataType = paramDefinitions[paramKey]
if (!dataType) {
dataType = 'String'
}
try {
convertedParams[paramKey] = convertParamValue(value, dataType)
} catch (e) {
throw new Error(`参数 ${paramKey} 转换失败: ${e.message}`)
}
}
const data = {
graph: JSON.stringify(val),
params: convertedParams
}
const response = await WorkflowApi.testWorkflow(data)
testResult.value = response
} catch (err) {
error.value = err.response?.data?.message || '运行失败,请检查参数和网络连接'
} finally {
loading.value = false
}
}
/** 监听测试抽屉的开启,获取开始节点参数列表 */
watch(showTestDrawer, (value) => {
if (!value) return
/// start
const startNode = getStartNode()
//
const parameters = startNode.data?.parameters || []
const paramDefinitions = {}
// 便
parameters.forEach((param) => {
paramDefinitions[param.name] = param
})
function mergeIfRequiredButNotSet(target) {
let needPushList = []
for (let key in paramDefinitions) {
let param = paramDefinitions[key]
if (param.required) {
let item = target.find((item) => item.key === key)
if (!item) {
needPushList.push({ key: param.name, value: param.defaultValue || '' })
}
}
}
target.push(...needPushList)
}
//
mergeIfRequiredButNotSet(params4Test.value)
paramsOfStartNode.value = paramDefinitions
})
/** 获取开始节点 */
const getStartNode = () => {
const val = tinyflowRef.value.getData()
const startNode = val.nodes.find((node) => node.type === 'startNode')
if (!startNode) {
throw new Error('流程缺少开始节点')
}
return startNode
}
/** 添加参数项 */
const addParam = () => {
params4Test.value.push({ key: '', value: '' })
}
/** 删除参数项 */
const removeParam = (index) => {
params4Test.value.splice(index, 1)
}
/** 类型转换函数 */
const convertParamValue = (value, dataType) => {
if (value === '') return null //
switch (dataType) {
case 'String':
return String(value)
case 'Number':
const num = Number(value)
if (isNaN(num)) throw new Error('非数字格式')
return num
case 'Boolean':
if (value.toLowerCase() === 'true') return true
if (value.toLowerCase() === 'false') return false
throw new Error('必须为 true/false')
case 'Object':
case 'Array':
try {
return JSON.parse(value)
} catch (e) {
throw new Error(`JSON格式错误: ${e.message}`)
}
default:
throw new Error(`不支持的类型: ${dataType}`)
}
} }
/** 表单校验 */ /** 表单校验 */
@ -47,3 +232,17 @@ defineExpose({
validate validate
}) })
</script> </script>
<style lang="css" scoped>
.result-content {
background: white;
padding: 12px;
border-radius: 4px;
max-height: 300px;
overflow: auto;
font-family: Monaco, Consolas, monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap;
}
</style>

View File

@ -73,7 +73,7 @@ import { CommonStatusEnum } from '@/utils/constants'
import * as WorkflowApi from '@/api/ai/workflow' import * as WorkflowApi from '@/api/ai/workflow'
import BasicInfo from './BasicInfo.vue' import BasicInfo from './BasicInfo.vue'
import WorkflowDesign from './WorkflowDesign.vue' import WorkflowDesign from './WorkflowDesign.vue'
import { ApiKeyApi } from '@/api/ai/model/apiKey' import { ModelApi } from '@/api/ai/model/model'
const router = useRouter() const router = useRouter()
const { delView } = useTagsViewStore() const { delView } = useTagsViewStore()
@ -118,7 +118,7 @@ const initData = async () => {
workflowData.value = JSON.parse(formData.value.graph) workflowData.value = JSON.parse(formData.value.graph)
} }
const apiKeys = await ApiKeyApi.getApiKeySimpleList() const apiKeys = await ModelApi.getModelSimpleList(1)
provider.value = { provider.value = {
llm: () => llm: () =>
apiKeys.map(({ id, name }) => ({ apiKeys.map(({ id, name }) => ({