feat: AI dialogue nodes support calling tools configured in the system (#3896)
This commit is contained in:
parent
aac0f297df
commit
80f53ec8fb
@ -3,8 +3,11 @@ export default {
|
|||||||
all: 'All',
|
all: 'All',
|
||||||
createTool: 'Create Tool',
|
createTool: 'Create Tool',
|
||||||
editTool: 'Edit Tool',
|
editTool: 'Edit Tool',
|
||||||
|
createMcpTool: 'Create MCP',
|
||||||
|
editMcpTool: 'Edit MCP',
|
||||||
copyTool: 'Copy Tool',
|
copyTool: 'Copy Tool',
|
||||||
importTool: 'Import Tool',
|
importTool: 'Import Tool',
|
||||||
|
settingTool: 'Set Tool',
|
||||||
toolStore: {
|
toolStore: {
|
||||||
title: 'Tool Store',
|
title: 'Tool Store',
|
||||||
createFromToolStore: 'Create from Tool Store',
|
createFromToolStore: 'Create from Tool Store',
|
||||||
|
|||||||
@ -7,6 +7,7 @@ export default {
|
|||||||
editMcpTool: '编辑MCP',
|
editMcpTool: '编辑MCP',
|
||||||
copyTool: '复制工具',
|
copyTool: '复制工具',
|
||||||
importTool: '导入工具',
|
importTool: '导入工具',
|
||||||
|
settingTool: '设置工具',
|
||||||
toolStore: {
|
toolStore: {
|
||||||
title: '工具商店',
|
title: '工具商店',
|
||||||
createFromToolStore: '从工具商店创建',
|
createFromToolStore: '从工具商店创建',
|
||||||
|
|||||||
@ -3,8 +3,11 @@ export default {
|
|||||||
all: '全部',
|
all: '全部',
|
||||||
createTool: '建立工具',
|
createTool: '建立工具',
|
||||||
editTool: '編輯工具',
|
editTool: '編輯工具',
|
||||||
|
createMcpTool: '建立MCP',
|
||||||
|
editMcpTool: '編輯MCP',
|
||||||
copyTool: '複製工具',
|
copyTool: '複製工具',
|
||||||
importTool: '匯入工具',
|
importTool: '匯入工具',
|
||||||
|
settingTool: '設定工具',
|
||||||
toolStore: {
|
toolStore: {
|
||||||
title: '工具商店',
|
title: '工具商店',
|
||||||
createFromToolStore: '從工具商店創建',
|
createFromToolStore: '從工具商店創建',
|
||||||
|
|||||||
@ -56,7 +56,7 @@ div:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style: circle;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,9 @@
|
|||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
max-width: 360px !important;
|
max-width: 360px !important;
|
||||||
}
|
}
|
||||||
|
ul {
|
||||||
|
list-style: circle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
@media only screen and (max-width: 768px) {
|
||||||
|
|||||||
@ -1,89 +1,227 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
align-center
|
|
||||||
:title="$t('common.setting')"
|
|
||||||
v-model="dialogVisible"
|
v-model="dialogVisible"
|
||||||
style="width: 550px"
|
width="1000"
|
||||||
append-to-body
|
append-to-body
|
||||||
|
class="addTool-dialog"
|
||||||
|
align-center
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
:close-on-press-escape="false"
|
:close-on-press-escape="false"
|
||||||
>
|
>
|
||||||
<el-form
|
<template #header="{ titleId, titleClass }">
|
||||||
label-position="top"
|
<div class="flex-between mb-8">
|
||||||
ref="paramFormRef"
|
<div class="flex">
|
||||||
:model="form"
|
<h4 :id="titleId" :class="titleClass" class="mr-8">
|
||||||
require-asterisk-position="right"
|
{{ $t('views.tool.settingTool') }}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-button link class="mr-24" @click="refresh">
|
||||||
|
<el-icon :size="18"><Refresh /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<LayoutContainer class="application-manage">
|
||||||
|
<template #left>
|
||||||
|
<div class="p-8">
|
||||||
|
<folder-tree
|
||||||
|
:data="folderList"
|
||||||
|
:currentNodeKey="currentFolder?.id"
|
||||||
|
@handleNodeClick="folderClickHandle"
|
||||||
|
v-loading="folderLoading"
|
||||||
|
:canOperation="false"
|
||||||
|
showShared
|
||||||
|
:shareTitle="$t('views.shared.shared_tool')"
|
||||||
|
:treeStyle="{ height: 'calc(100vh - 240px)' }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="layout-bg">
|
||||||
|
<div class="flex-between p-16 ml-8">
|
||||||
|
<h4>{{ currentFolder?.name }}</h4>
|
||||||
|
<el-input
|
||||||
|
v-model="searchValue"
|
||||||
|
:placeholder="$t('common.search')"
|
||||||
|
prefix-icon="Search"
|
||||||
|
class="w-240 mr-8"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-scrollbar>
|
||||||
|
<div class="p-16-24 pt-0" style="height: calc(100vh - 200px)">
|
||||||
|
<el-row :gutter="12" v-loading="apiLoading" v-if="searchData.length">
|
||||||
|
<el-col :span="12" v-for="(item, index) in searchData" :key="index" class="mb-16">
|
||||||
|
<CardCheckbox
|
||||||
|
value-field="id"
|
||||||
|
:data="item"
|
||||||
|
v-model="checkList"
|
||||||
|
@change="changeHandle"
|
||||||
>
|
>
|
||||||
<el-form-item>
|
<template #icon>
|
||||||
<el-select v-model="form.tool_ids" filterable multiple>
|
<el-avatar
|
||||||
<el-option
|
v-if="item?.icon"
|
||||||
v-for="mcpTool in toolSelectOptions"
|
shape="square"
|
||||||
:key="mcpTool.id"
|
:size="32"
|
||||||
:label="mcpTool.name"
|
style="background: none"
|
||||||
:value="mcpTool.id"
|
class="mr-8"
|
||||||
>
|
>
|
||||||
<span>{{ mcpTool.name }}</span>
|
<img :src="resetUrl(item?.icon)" alt="" />
|
||||||
<el-tag v-if="mcpTool.scope === 'SHARED'" type="info" class="info-tag ml-8 mt-4">
|
</el-avatar>
|
||||||
{{ $t('views.shared.title') }}
|
<ToolIcon v-else :size="32" :type="item?.tool_type" />
|
||||||
</el-tag>
|
</template>
|
||||||
</el-option>
|
<span class="ellipsis cursor ml-12" :title="item.name"> {{ item.name }}</span>
|
||||||
</el-select>
|
</CardCheckbox>
|
||||||
</el-form-item>
|
</el-col>
|
||||||
</el-form>
|
</el-row>
|
||||||
|
<el-empty :description="$t('common.noData')" v-else />
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</LayoutContainer>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<div class="flex-between">
|
||||||
<el-button @click.prevent="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
|
<div class="flex">
|
||||||
<el-button type="primary" @click="submit()" :loading="loading">
|
<el-text type="info" class="color-secondary mr-8" v-if="checkList.length > 0">
|
||||||
{{ $t('common.save') }}
|
{{ $t('common.selected') }} {{ checkList.length }}
|
||||||
|
</el-text>
|
||||||
|
<el-button link type="primary" v-if="checkList.length > 0" @click="clearCheck">
|
||||||
|
{{ $t('common.clear') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<span>
|
||||||
|
<el-button @click.prevent="dialogVisible = false">
|
||||||
|
{{ $t('common.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" @click="submitHandle">
|
||||||
|
{{ $t('common.add') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref, watch} from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import useStore from '@/stores'
|
||||||
|
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
|
||||||
|
import { uniqueArray } from '@/utils/array'
|
||||||
|
import { resetUrl } from '@/utils/common'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
|
const { folder, user } = useStore()
|
||||||
const paramFormRef = ref()
|
const apiType = computed(() => {
|
||||||
|
if (route.path.includes('shared')) {
|
||||||
const form = ref<any>({
|
return 'systemShare'
|
||||||
tool_ids: [],
|
} else if (route.path.includes('resource-management')) {
|
||||||
|
return 'systemManage'
|
||||||
|
} else {
|
||||||
|
return 'workspace'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const toolSelectOptions = ref<any[]>([])
|
|
||||||
|
|
||||||
const dialogVisible = ref<boolean>(false)
|
const dialogVisible = ref<boolean>(false)
|
||||||
|
const checkList = ref<Array<string>>([])
|
||||||
const loading = ref(false)
|
const searchValue = ref('')
|
||||||
|
const searchData = ref<Array<any>>([])
|
||||||
|
const toolList = ref<Array<any>>([])
|
||||||
|
const apiLoading = ref(false)
|
||||||
|
|
||||||
watch(dialogVisible, (bool) => {
|
watch(dialogVisible, (bool) => {
|
||||||
if (!bool) {
|
if (!bool) {
|
||||||
form.value = {
|
checkList.value = []
|
||||||
tool_ids: [],
|
searchValue.value = ''
|
||||||
}
|
searchData.value = []
|
||||||
|
toolList.value = []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(searchValue, (val) => {
|
||||||
|
if (val) {
|
||||||
|
searchData.value = toolList.value.filter((v) => v.name.includes(val))
|
||||||
|
} else {
|
||||||
|
searchData.value = toolList.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const open = (data: any, selectOptions: any) => {
|
function changeHandle() {}
|
||||||
form.value = {...form.value, ...data}
|
function clearCheck() {
|
||||||
dialogVisible.value = true
|
checkList.value = []
|
||||||
toolSelectOptions.value = selectOptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const submit = () => {
|
const open = (checked: any) => {
|
||||||
paramFormRef.value.validate().then((valid: any) => {
|
checkList.value = checked || []
|
||||||
if (valid) {
|
getFolder()
|
||||||
emit('refresh', form.value)
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitHandle = () => {
|
||||||
|
emit('refresh', {
|
||||||
|
tool_ids: checkList.value,
|
||||||
|
})
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refresh = () => {
|
||||||
|
searchValue.value = ''
|
||||||
|
toolList.value = []
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderList = ref<any[]>([])
|
||||||
|
const currentFolder = ref<any>({})
|
||||||
|
const folderLoading = ref(false)
|
||||||
|
// 文件
|
||||||
|
function folderClickHandle(row: any) {
|
||||||
|
if (row.id === currentFolder.value?.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentFolder.value = row
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFolder() {
|
||||||
|
const params = {}
|
||||||
|
folder.asyncGetFolder('TOOL', params, folderLoading).then((res: any) => {
|
||||||
|
folderList.value = res.data
|
||||||
|
currentFolder.value = res.data?.[0] || {}
|
||||||
|
getList()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getList() {
|
||||||
|
const folder_id = currentFolder.value?.id || user.getWorkspaceId()
|
||||||
|
loadSharedApi({
|
||||||
|
type: 'tool',
|
||||||
|
systemType: apiType.value,
|
||||||
|
})
|
||||||
|
.getToolList({ folder_id }, apiLoading)
|
||||||
|
.then((res: any) => {
|
||||||
|
toolList.value = uniqueArray([...toolList.value, ...res.data.tools], 'id')
|
||||||
|
searchData.value = res.data.tools
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ open })
|
defineExpose({ open })
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss">
|
||||||
|
.addTool-dialog {
|
||||||
|
padding: 0;
|
||||||
|
.el-dialog__header {
|
||||||
|
padding: 12px 20px 4px 24px;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-light);
|
||||||
|
}
|
||||||
|
.el-dialog__footer {
|
||||||
|
padding: 12px 24px 12px 24px;
|
||||||
|
border-top: 1px solid var(--el-border-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__headerbtn {
|
||||||
|
top: 2px;
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -247,7 +247,6 @@ const model_change = (model_id?: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const defaultPrompt = `${t('views.applicationWorkflow.nodes.aiChatNode.defaultPrompt')}:
|
const defaultPrompt = `${t('views.applicationWorkflow.nodes.aiChatNode.defaultPrompt')}:
|
||||||
{{${t('views.applicationWorkflow.nodes.searchKnowledgeNode.label')}.data}}
|
{{${t('views.applicationWorkflow.nodes.searchKnowledgeNode.label')}.data}}
|
||||||
${t('views.problem.title')}:
|
${t('views.problem.title')}:
|
||||||
@ -364,10 +363,7 @@ function submitMcpServersDialog(config: any) {
|
|||||||
|
|
||||||
const toolDialogRef = ref()
|
const toolDialogRef = ref()
|
||||||
function openToolDialog() {
|
function openToolDialog() {
|
||||||
const config = {
|
toolDialogRef.value.open(chat_data.value.tool_ids)
|
||||||
tool_ids: chat_data.value.tool_ids,
|
|
||||||
}
|
|
||||||
toolDialogRef.value.open(config, toolSelectOptions.value)
|
|
||||||
}
|
}
|
||||||
function submitToolDialog(config: any) {
|
function submitToolDialog(config: any) {
|
||||||
set(props.nodeModel.properties.node_data, 'tool_ids', config.tool_ids)
|
set(props.nodeModel.properties.node_data, 'tool_ids', config.tool_ids)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user