Skip to content

Commit 80f53ec

Browse files
feat: AI dialogue nodes support calling tools configured in the system (#3896)
1 parent aac0f29 commit 80f53ec

File tree

7 files changed

+205
-61
lines changed

7 files changed

+205
-61
lines changed

ui/src/locales/lang/en-US/views/tool.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ export default {
33
all: 'All',
44
createTool: 'Create Tool',
55
editTool: 'Edit Tool',
6+
createMcpTool: 'Create MCP',
7+
editMcpTool: 'Edit MCP',
68
copyTool: 'Copy Tool',
79
importTool: 'Import Tool',
10+
settingTool: 'Set Tool',
811
toolStore: {
912
title: 'Tool Store',
1013
createFromToolStore: 'Create from Tool Store',

ui/src/locales/lang/zh-CN/views/tool.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default {
77
editMcpTool: '编辑MCP',
88
copyTool: '复制工具',
99
importTool: '导入工具',
10+
settingTool: '设置工具',
1011
toolStore: {
1112
title: '工具商店',
1213
createFromToolStore: '从工具商店创建',

ui/src/locales/lang/zh-Hant/views/tool.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ export default {
33
all: '全部',
44
createTool: '建立工具',
55
editTool: '編輯工具',
6+
createMcpTool: '建立MCP',
7+
editMcpTool: '編輯MCP',
68
copyTool: '複製工具',
79
importTool: '匯入工具',
10+
settingTool: '設定工具',
811
toolStore: {
912
title: '工具商店',
1013
createFromToolStore: '從工具商店創建',

ui/src/styles/app.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ div:focus {
5656
}
5757

5858
ul {
59-
list-style: circle;
59+
list-style: none;
6060
margin: 0;
6161
padding: 0;
6262
}

ui/src/styles/md-editor.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
border: 0 !important;
2222
max-width: 360px !important;
2323
}
24+
ul {
25+
list-style: circle;
26+
}
2427
}
2528

2629
@media only screen and (max-width: 768px) {
Lines changed: 193 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,227 @@
11
<template>
22
<el-dialog
3-
align-center
4-
:title="$t('common.setting')"
53
v-model="dialogVisible"
6-
style="width: 550px"
4+
width="1000"
75
append-to-body
6+
class="addTool-dialog"
7+
align-center
88
:close-on-click-modal="false"
99
:close-on-press-escape="false"
1010
>
11-
<el-form
12-
label-position="top"
13-
ref="paramFormRef"
14-
:model="form"
15-
require-asterisk-position="right"
16-
>
17-
<el-form-item>
18-
<el-select v-model="form.tool_ids" filterable multiple>
19-
<el-option
20-
v-for="mcpTool in toolSelectOptions"
21-
:key="mcpTool.id"
22-
:label="mcpTool.name"
23-
:value="mcpTool.id"
24-
>
25-
<span>{{ mcpTool.name }}</span>
26-
<el-tag v-if="mcpTool.scope === 'SHARED'" type="info" class="info-tag ml-8 mt-4">
27-
{{ $t('views.shared.title') }}
28-
</el-tag>
29-
</el-option>
30-
</el-select>
31-
</el-form-item>
32-
</el-form>
11+
<template #header="{ titleId, titleClass }">
12+
<div class="flex-between mb-8">
13+
<div class="flex">
14+
<h4 :id="titleId" :class="titleClass" class="mr-8">
15+
{{ $t('views.tool.settingTool') }}
16+
</h4>
17+
</div>
3318

34-
<template #footer>
35-
<span class="dialog-footer">
36-
<el-button @click.prevent="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
37-
<el-button type="primary" @click="submit()" :loading="loading">
38-
{{ $t('common.save') }}
19+
<el-button link class="mr-24" @click="refresh">
20+
<el-icon :size="18"><Refresh /></el-icon>
3921
</el-button>
40-
</span>
22+
</div>
23+
</template>
24+
<LayoutContainer class="application-manage">
25+
<template #left>
26+
<div class="p-8">
27+
<folder-tree
28+
:data="folderList"
29+
:currentNodeKey="currentFolder?.id"
30+
@handleNodeClick="folderClickHandle"
31+
v-loading="folderLoading"
32+
:canOperation="false"
33+
showShared
34+
:shareTitle="$t('views.shared.shared_tool')"
35+
:treeStyle="{ height: 'calc(100vh - 240px)' }"
36+
/>
37+
</div>
38+
</template>
39+
<div class="layout-bg">
40+
<div class="flex-between p-16 ml-8">
41+
<h4>{{ currentFolder?.name }}</h4>
42+
<el-input
43+
v-model="searchValue"
44+
:placeholder="$t('common.search')"
45+
prefix-icon="Search"
46+
class="w-240 mr-8"
47+
clearable
48+
/>
49+
</div>
50+
51+
<el-scrollbar>
52+
<div class="p-16-24 pt-0" style="height: calc(100vh - 200px)">
53+
<el-row :gutter="12" v-loading="apiLoading" v-if="searchData.length">
54+
<el-col :span="12" v-for="(item, index) in searchData" :key="index" class="mb-16">
55+
<CardCheckbox
56+
value-field="id"
57+
:data="item"
58+
v-model="checkList"
59+
@change="changeHandle"
60+
>
61+
<template #icon>
62+
<el-avatar
63+
v-if="item?.icon"
64+
shape="square"
65+
:size="32"
66+
style="background: none"
67+
class="mr-8"
68+
>
69+
<img :src="resetUrl(item?.icon)" alt="" />
70+
</el-avatar>
71+
<ToolIcon v-else :size="32" :type="item?.tool_type" />
72+
</template>
73+
<span class="ellipsis cursor ml-12" :title="item.name"> {{ item.name }}</span>
74+
</CardCheckbox>
75+
</el-col>
76+
</el-row>
77+
<el-empty :description="$t('common.noData')" v-else />
78+
</div>
79+
</el-scrollbar>
80+
</div>
81+
</LayoutContainer>
82+
83+
<template #footer>
84+
<div class="flex-between">
85+
<div class="flex">
86+
<el-text type="info" class="color-secondary mr-8" v-if="checkList.length > 0">
87+
{{ $t('common.selected') }} {{ checkList.length }}
88+
</el-text>
89+
<el-button link type="primary" v-if="checkList.length > 0" @click="clearCheck">
90+
{{ $t('common.clear') }}
91+
</el-button>
92+
</div>
93+
<span>
94+
<el-button @click.prevent="dialogVisible = false">
95+
{{ $t('common.cancel') }}
96+
</el-button>
97+
<el-button type="primary" @click="submitHandle">
98+
{{ $t('common.add') }}
99+
</el-button>
100+
</span>
101+
</div>
41102
</template>
42103
</el-dialog>
43104
</template>
44105
<script setup lang="ts">
45-
import {ref, watch} from 'vue'
106+
import { computed, ref, watch } from 'vue'
107+
import { useRoute } from 'vue-router'
108+
import useStore from '@/stores'
109+
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
110+
import { uniqueArray } from '@/utils/array'
111+
import { resetUrl } from '@/utils/common'
112+
const route = useRoute()
46113
47114
const emit = defineEmits(['refresh'])
48-
49-
const paramFormRef = ref()
50-
51-
const form = ref<any>({
52-
tool_ids: [],
115+
const { folder, user } = useStore()
116+
const apiType = computed(() => {
117+
if (route.path.includes('shared')) {
118+
return 'systemShare'
119+
} else if (route.path.includes('resource-management')) {
120+
return 'systemManage'
121+
} else {
122+
return 'workspace'
123+
}
53124
})
54125
55-
const toolSelectOptions = ref<any[]>([])
56-
57126
const dialogVisible = ref<boolean>(false)
58-
59-
const loading = ref(false)
127+
const checkList = ref<Array<string>>([])
128+
const searchValue = ref('')
129+
const searchData = ref<Array<any>>([])
130+
const toolList = ref<Array<any>>([])
131+
const apiLoading = ref(false)
60132
61133
watch(dialogVisible, (bool) => {
62134
if (!bool) {
63-
form.value = {
64-
tool_ids: [],
65-
}
135+
checkList.value = []
136+
searchValue.value = ''
137+
searchData.value = []
138+
toolList.value = []
139+
}
140+
})
141+
142+
watch(searchValue, (val) => {
143+
if (val) {
144+
searchData.value = toolList.value.filter((v) => v.name.includes(val))
145+
} else {
146+
searchData.value = toolList.value
66147
}
67148
})
68149
150+
function changeHandle() {}
151+
function clearCheck() {
152+
checkList.value = []
153+
}
69154
70-
const open = (data: any, selectOptions: any) => {
71-
form.value = {...form.value, ...data}
155+
const open = (checked: any) => {
156+
checkList.value = checked || []
157+
getFolder()
72158
dialogVisible.value = true
73-
toolSelectOptions.value = selectOptions
74159
}
75160
76-
const submit = () => {
77-
paramFormRef.value.validate().then((valid: any) => {
78-
if (valid) {
79-
emit('refresh', form.value)
80-
dialogVisible.value = false
81-
}
161+
const submitHandle = () => {
162+
emit('refresh', {
163+
tool_ids: checkList.value,
82164
})
165+
dialogVisible.value = false
166+
}
167+
168+
const refresh = () => {
169+
searchValue.value = ''
170+
toolList.value = []
171+
getList()
83172
}
84173
174+
const folderList = ref<any[]>([])
175+
const currentFolder = ref<any>({})
176+
const folderLoading = ref(false)
177+
// 文件
178+
function folderClickHandle(row: any) {
179+
if (row.id === currentFolder.value?.id) {
180+
return
181+
}
182+
currentFolder.value = row
183+
getList()
184+
}
85185
186+
function getFolder() {
187+
const params = {}
188+
folder.asyncGetFolder('TOOL', params, folderLoading).then((res: any) => {
189+
folderList.value = res.data
190+
currentFolder.value = res.data?.[0] || {}
191+
getList()
192+
})
193+
}
86194
87-
defineExpose({open})
195+
function getList() {
196+
const folder_id = currentFolder.value?.id || user.getWorkspaceId()
197+
loadSharedApi({
198+
type: 'tool',
199+
systemType: apiType.value,
200+
})
201+
.getToolList({ folder_id }, apiLoading)
202+
.then((res: any) => {
203+
toolList.value = uniqueArray([...toolList.value, ...res.data.tools], 'id')
204+
searchData.value = res.data.tools
205+
})
206+
}
207+
208+
defineExpose({ open })
88209
</script>
89-
<style lang="scss" scoped></style>
210+
<style lang="scss">
211+
.addTool-dialog {
212+
padding: 0;
213+
.el-dialog__header {
214+
padding: 12px 20px 4px 24px;
215+
border-bottom: 1px solid var(--el-border-color-light);
216+
}
217+
.el-dialog__footer {
218+
padding: 12px 24px 12px 24px;
219+
border-top: 1px solid var(--el-border-color-light);
220+
}
221+
222+
.el-dialog__headerbtn {
223+
top: 2px;
224+
right: 6px;
225+
}
226+
}
227+
</style>

ui/src/workflow/nodes/ai-chat-node/index.vue

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ const model_change = (model_id?: string) => {
247247
}
248248
}
249249
250-
// @ts-ignore
251250
const defaultPrompt = `${t('views.applicationWorkflow.nodes.aiChatNode.defaultPrompt')}:
252251
{{${t('views.applicationWorkflow.nodes.searchKnowledgeNode.label')}.data}}
253252
${t('views.problem.title')}:
@@ -364,10 +363,7 @@ function submitMcpServersDialog(config: any) {
364363
365364
const toolDialogRef = ref()
366365
function openToolDialog() {
367-
const config = {
368-
tool_ids: chat_data.value.tool_ids,
369-
}
370-
toolDialogRef.value.open(config, toolSelectOptions.value)
366+
toolDialogRef.value.open(chat_data.value.tool_ids)
371367
}
372368
function submitToolDialog(config: any) {
373369
set(props.nodeModel.properties.node_data, 'tool_ids', config.tool_ids)

0 commit comments

Comments
 (0)