feat(web): agent support add tools
This commit is contained in:
@@ -18,7 +18,9 @@ import type {
|
||||
Variable,
|
||||
MemoryConfig,
|
||||
AiPromptModalRef,
|
||||
Source
|
||||
Source,
|
||||
ToolModalRef,
|
||||
ToolOption
|
||||
} from './types'
|
||||
import type { Model } from '@/views/ModelManagement/types'
|
||||
import { getModelList } from '@/api/models';
|
||||
@@ -31,6 +33,8 @@ import { memoryConfigListUrl } from '@/api/memory'
|
||||
import CustomSelect from '@/components/CustomSelect'
|
||||
import aiPrompt from '@/assets/images/application/aiPrompt.png'
|
||||
import AiPromptModal from './components/AiPromptModal'
|
||||
import ToolModal from './components/ToolModal'
|
||||
import ToolList from './components/ToolList'
|
||||
|
||||
const DescWrapper: FC<{desc: string, className?: string}> = ({desc, className}) => {
|
||||
return (
|
||||
@@ -47,12 +51,12 @@ const LabelWrapper: FC<{title: string, className?: string; children?: ReactNode}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const SwitchWrapper: FC<{ title: string, desc: string, name: string }> = ({ title, desc, name }) => {
|
||||
const SwitchWrapper: FC<{ title: string, desc?: string, name: string | string[]; needTransition?: boolean; }> = ({ title, desc, name, needTransition = true }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="rb:flex rb:items-center rb:justify-between">
|
||||
<LabelWrapper title={t(`application.${title}`)}>
|
||||
<DescWrapper desc={t(`application.${desc}`)} className="rb:mt-2" />
|
||||
<LabelWrapper title={needTransition ? t(`application.${title}`) : title}>
|
||||
{desc && <DescWrapper desc={needTransition ? t(`application.${desc}`) : desc} className="rb:mt-2" />}
|
||||
</LabelWrapper>
|
||||
<Form.Item
|
||||
name={name}
|
||||
@@ -100,11 +104,11 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
const [formData, setFormData] = useState<{
|
||||
default_model_config_id?: string,
|
||||
model_parameters?: Config['model_parameters'],
|
||||
tools: ToolOption[],
|
||||
} | null>(null)
|
||||
const values = Form.useWatch<{
|
||||
memoryEnabled: boolean;
|
||||
memory_content?: string | number;
|
||||
webSearch: boolean;
|
||||
} & Config>([], form)
|
||||
|
||||
const [knowledgeConfig, setKnowledgeConfig] = useState<KnowledgeConfig>({ knowledge_bases: [] })
|
||||
@@ -149,17 +153,21 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
setLoading(true)
|
||||
getApplicationConfig(id as string).then(res => {
|
||||
const response = res as Config
|
||||
setData(response)
|
||||
setData({
|
||||
...response,
|
||||
tools: Array.isArray(response.tools) ? response.tools : []
|
||||
})
|
||||
const { memory, tools } = response
|
||||
form.setFieldsValue({
|
||||
...response,
|
||||
memoryEnabled: memory?.enabled || false,
|
||||
memory_content: memory?.memory_content ? Number(memory?.memory_content) : undefined,
|
||||
webSearch: tools?.web_search?.enabled || false,
|
||||
tools: Array.isArray(tools) ? tools : []
|
||||
})
|
||||
setFormData({
|
||||
default_model_config_id: response.default_model_config_id,
|
||||
model_parameters: response.model_parameters || {},
|
||||
tools: Array.isArray(tools) ? tools : []
|
||||
})
|
||||
if (response?.knowledge_retrieval?.knowledge_bases?.length) {
|
||||
getDefaultKnowledgeList(response)
|
||||
@@ -260,8 +268,9 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
// 保存Agent配置
|
||||
const handleSave = (flag = true) => {
|
||||
if (!isSave || !data) return Promise.resolve()
|
||||
const { memoryEnabled, memory_content, webSearch, ...rest } = values
|
||||
const { memoryEnabled, memory_content, ...rest } = values
|
||||
const { knowledge_bases = [], ...knowledgeRest } = knowledgeConfig || {}
|
||||
|
||||
|
||||
// 从原数据中获取memory的其他必要属性
|
||||
const originalMemory = data.memory || ({} as MemoryConfig)
|
||||
@@ -285,15 +294,10 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
...(item.config || {})
|
||||
}))
|
||||
} as KnowledgeConfig : null,
|
||||
tools: {
|
||||
web_search: {
|
||||
enabled: webSearch,
|
||||
config: {
|
||||
web_search: webSearch
|
||||
}
|
||||
}
|
||||
}
|
||||
tools: toolList
|
||||
}
|
||||
|
||||
console.log('params', rest, params)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
saveAgentConfig(data.app_id, params)
|
||||
@@ -342,6 +346,19 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
const updatePrompt = (value: string) => {
|
||||
form.setFieldValue('system_prompt', value)
|
||||
}
|
||||
|
||||
const toolModalRef = useRef<ToolModalRef>(null)
|
||||
const [toolList, setToolList] = useState<ToolOption[]>([])
|
||||
const handleAddTool = () => {
|
||||
toolModalRef.current?.handleOpen()
|
||||
}
|
||||
const updateTools = (tool: ToolOption) => {
|
||||
const tools = [...toolList, tool]
|
||||
setToolList(tools)
|
||||
form.setFieldValue('tools', tools)
|
||||
}
|
||||
|
||||
console.log('toolList', toolList)
|
||||
return (
|
||||
<>
|
||||
{loading && <Spin fullscreen></Spin>}
|
||||
@@ -410,14 +427,12 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
data={data?.variables}
|
||||
onUpdate={setVariableList}
|
||||
/>
|
||||
|
||||
{/* 工具配置 */}
|
||||
<Card title={t('application.toolConfiguration')}>
|
||||
<Space size={24} direction='vertical' style={{ width: '100%' }}>
|
||||
<SwitchWrapper title="webSearch" desc="webSearchDesc" name="webSearch" />
|
||||
{/* <SwitchWrapper title="codeExecutor" desc="codeExecutorDesc" name="codeExecutor" />
|
||||
<SwitchWrapper title="imageGeneration" desc="imageGenerationDesc" name="imageGeneration" /> */}
|
||||
</Space>
|
||||
</Card>
|
||||
<ToolList
|
||||
data={data?.tools || []}
|
||||
onUpdate={setToolList}
|
||||
/>
|
||||
</Space>
|
||||
</Form>
|
||||
</Col>
|
||||
@@ -454,6 +469,10 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
defaultModel={defaultModel}
|
||||
refresh={updatePrompt}
|
||||
/>
|
||||
<ToolModal
|
||||
ref={toolModalRef}
|
||||
refresh={updateTools}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -31,10 +31,6 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig)
|
||||
setEditConfig({ ...(data || {}) })
|
||||
const knowledge_bases = [...(data.knowledge_bases || [])]
|
||||
setKnowledgeList(knowledge_bases)
|
||||
onUpdate(prev => ({
|
||||
...prev,
|
||||
knowledge_bases: knowledge_bases,
|
||||
}))
|
||||
}
|
||||
}, [data])
|
||||
|
||||
@@ -47,10 +43,10 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig)
|
||||
const handleDeleteKnowledge = (id: string) => {
|
||||
const list = knowledgeList.filter(item => item.id !== id)
|
||||
setKnowledgeList([...list])
|
||||
onUpdate(prev => ({
|
||||
...prev,
|
||||
onUpdate({
|
||||
...editConfig,
|
||||
knowledge_bases: [...list],
|
||||
}))
|
||||
})
|
||||
}
|
||||
const handleEditKnowledge = (item: KnowledgeBase) => {
|
||||
knowledgeConfigModalRef.current?.handleOpen(item)
|
||||
@@ -69,10 +65,10 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig)
|
||||
list = [...values as KnowledgeBase[]]
|
||||
}
|
||||
setKnowledgeList([...list])
|
||||
onUpdate(prev => ({
|
||||
...prev,
|
||||
onUpdate({
|
||||
...editConfig,
|
||||
knowledge_bases: [...list],
|
||||
}))
|
||||
})
|
||||
} else if (type === 'knowledgeConfig') {
|
||||
const index = knowledgeList.findIndex(item => item.id === (values as KnowledgeBase).kb_id)
|
||||
const list = [...knowledgeList]
|
||||
@@ -81,18 +77,19 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig)
|
||||
config: {...values as KnowledgeConfigForm}
|
||||
}
|
||||
setKnowledgeList([...list])
|
||||
onUpdate(prev => ({
|
||||
...prev,
|
||||
onUpdate({
|
||||
...editConfig,
|
||||
knowledge_bases: [...list],
|
||||
}))
|
||||
})
|
||||
} else if (type === 'rerankerConfig') {
|
||||
setEditConfig(prev => ({ ...prev, ...(values as RerankerConfig) }))
|
||||
onUpdate(prev => ({
|
||||
...prev,
|
||||
...values,
|
||||
reranker_id: values.rerank_model ? values.reranker_id : undefined,
|
||||
reranker_top_k: values.rerank_model ? values.reranker_top_k : undefined,
|
||||
}))
|
||||
const rerankerValues = values as RerankerConfig
|
||||
setEditConfig(prev => ({ ...prev, ...rerankerValues }))
|
||||
onUpdate({
|
||||
...editConfig,
|
||||
...rerankerValues,
|
||||
reranker_id: rerankerValues.rerank_model ? rerankerValues.reranker_id : undefined,
|
||||
reranker_top_k: rerankerValues.rerank_model ? rerankerValues.reranker_top_k : undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
return (
|
||||
@@ -102,8 +99,8 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig)
|
||||
<Button style={{padding: '0 8px', height: '24px'}} onClick={() => handleKnowledgeConfig()}>{t('application.globalConfig')}</Button>
|
||||
}
|
||||
>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-[12px]">
|
||||
<div className="rb:font-medium rb:leading-[20px]">{t('application.associatedKnowledgeBase')}</div>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-3">
|
||||
<div className="rb:font-medium rb:leading-5">{t('application.associatedKnowledgeBase')}</div>
|
||||
<Button style={{padding: '0 8px', height: '24px'}} onClick={handleAddKnowledge}>+{t('application.addKnowledgeBase')}</Button>
|
||||
</div>
|
||||
|
||||
@@ -115,21 +112,21 @@ const Knowledge: FC<{data: KnowledgeConfig; onUpdate: (config: KnowledgeConfig)
|
||||
dataSource={knowledgeList}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<div key={item.id} className="rb:flex rb:items-center rb:justify-between rb:p-[12px_16px] rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-[8px]">
|
||||
<div className="rb:font-medium rb:leading-[16px]">
|
||||
<div key={item.id} className="rb:flex rb:items-center rb:justify-between rb:p-[12px_16px] rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg">
|
||||
<div className="rb:font-medium rb:leading-4">
|
||||
{item.name}
|
||||
<Tag color={item.status === 1 ? 'success' : item.status === 0 ? 'default' : 'error'} className="rb:ml-[8px]">
|
||||
<Tag color={item.status === 1 ? 'success' : item.status === 0 ? 'default' : 'error'} className="rb:ml-2">
|
||||
{item.status === 1 ? t('common.enable') : item.status === 0 ? t('common.disabled') : t('common.deleted')}
|
||||
</Tag>
|
||||
<div className="rb:mt-[4px] rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-[20px]">{t('application.contains', {include_count: item.doc_num})}</div>
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-5">{t('application.contains', {include_count: item.doc_num})}</div>
|
||||
</div>
|
||||
<Space size={12}>
|
||||
<div
|
||||
className="rb:w-[24px] rb:h-[24px] rb:cursor-pointer rb:bg-[url('@/assets/images/editBorder.svg')] rb:hover:bg-[url('@/assets/images/editBg.svg')]"
|
||||
className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/editBorder.svg')] rb:hover:bg-[url('@/assets/images/editBg.svg')]"
|
||||
onClick={() => handleEditKnowledge(item)}
|
||||
></div>
|
||||
<div
|
||||
className="rb:w-[24px] rb:h-[24px] rb:cursor-pointer rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
|
||||
className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
|
||||
onClick={() => handleDeleteKnowledge(item.id)}
|
||||
></div>
|
||||
</Space>
|
||||
|
||||
149
web/src/views/ApplicationConfig/components/ToolList.tsx
Normal file
149
web/src/views/ApplicationConfig/components/ToolList.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
import { type FC, useRef, useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Space, Button, List, Switch } from 'antd'
|
||||
import Card from './Card'
|
||||
import type {
|
||||
ToolModalRef,
|
||||
ToolOption
|
||||
} from '../types'
|
||||
import Empty from '@/components/Empty'
|
||||
import ToolModal from './ToolModal'
|
||||
import { getToolMethods, getToolDetail } from '@/api/tools'
|
||||
|
||||
const ToolList: FC<{ data: ToolOption[]; onUpdate: (config: ToolOption[]) => void}> = ({data, onUpdate}) => {
|
||||
const { t } = useTranslation()
|
||||
const toolModalRef = useRef<ToolModalRef>(null)
|
||||
const [toolList, setToolList] = useState<ToolOption[]>([])
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const processedData = data.map(async (item) => {
|
||||
if (!item.label && item.tool_id) {
|
||||
try {
|
||||
const [toolDetail, methods] = await Promise.all([
|
||||
getToolDetail(item.tool_id),
|
||||
getToolMethods(item.tool_id)
|
||||
])
|
||||
|
||||
switch ((toolDetail as any).tool_type) {
|
||||
case 'mcp':
|
||||
const mcpFilterItem = (methods as any[]).find(vo => vo.name === item.operation)
|
||||
return {
|
||||
...item,
|
||||
label: mcpFilterItem?.description,
|
||||
method_id: mcpFilterItem?.method_id,
|
||||
value: mcpFilterItem?.name,
|
||||
description: mcpFilterItem?.description,
|
||||
parameters: mcpFilterItem?.parameters
|
||||
}
|
||||
break
|
||||
case 'builtin':
|
||||
if ((methods as any[]).length > 1) {
|
||||
const builtinFilterItem = (methods as any[]).find(vo => vo.name === item.operation)
|
||||
return {
|
||||
...item,
|
||||
label: builtinFilterItem?.description,
|
||||
method_id: builtinFilterItem?.method_id,
|
||||
value: builtinFilterItem?.name,
|
||||
description: builtinFilterItem?.description,
|
||||
parameters: builtinFilterItem?.parameters
|
||||
}
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
label: (methods as any[])[0]?.description,
|
||||
method_id: (methods as any[])[0]?.method_id,
|
||||
value: (methods as any[])[0]?.name,
|
||||
description: (methods as any[])[0]?.description,
|
||||
parameters: (methods as any[])[0]?.parameters
|
||||
}
|
||||
break
|
||||
default:
|
||||
const customFilterItem = (methods as any[]).find(vo => vo.method_id === item.operation)
|
||||
return {
|
||||
...item,
|
||||
label: customFilterItem?.name,
|
||||
method_id: customFilterItem?.method_id,
|
||||
value: customFilterItem?.name,
|
||||
description: customFilterItem?.description,
|
||||
parameters: customFilterItem?.parameters
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return item
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
|
||||
Promise.all(processedData).then(setToolList)
|
||||
}
|
||||
}, [data])
|
||||
|
||||
console.log('toolList', toolList)
|
||||
|
||||
const handleAddTool = () => {
|
||||
toolModalRef.current?.handleOpen()
|
||||
}
|
||||
const updateTools = (tool: ToolOption) => {
|
||||
const list = [...toolList, tool]
|
||||
setToolList(list)
|
||||
onUpdate(list)
|
||||
}
|
||||
const handleDeleteTool = (index: number) => {
|
||||
const list = toolList.filter((_item, idx) => idx !== index)
|
||||
setToolList([...list])
|
||||
onUpdate(list)
|
||||
}
|
||||
const handleChangeEnabled = (index: number) => {
|
||||
const list = toolList.map((item, idx) => {
|
||||
if (idx === index) {
|
||||
return {
|
||||
...item,
|
||||
enabled: !item.enabled
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
setToolList([...list])
|
||||
onUpdate(list)
|
||||
}
|
||||
return (
|
||||
<Card
|
||||
title={t('application.toolConfiguration')}
|
||||
extra={
|
||||
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handleAddTool}>+{t('application.addTool')}</Button>
|
||||
}
|
||||
>
|
||||
|
||||
{toolList.length === 0
|
||||
? <Empty size={88} />
|
||||
:
|
||||
<List
|
||||
grid={{ gutter: 12, column: 1 }}
|
||||
dataSource={toolList}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<div key={index} className="rb:flex rb:items-center rb:justify-between rb:p-[12px_16px] rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg">
|
||||
<div className="rb:font-medium rb:leading-4">
|
||||
{item.label}
|
||||
</div>
|
||||
<Space size={12}>
|
||||
<div
|
||||
className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
|
||||
onClick={() => handleDeleteTool(index)}
|
||||
></div>
|
||||
<Switch checked={item.enabled} onChange={() => handleChangeEnabled(index)} />
|
||||
</Space>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
<ToolModal
|
||||
ref={toolModalRef}
|
||||
refresh={updateTools}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
export default ToolList
|
||||
145
web/src/views/ApplicationConfig/components/ToolModal.tsx
Normal file
145
web/src/views/ApplicationConfig/components/ToolModal.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Cascader, type CascaderProps } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ToolModalRef, ToolOption } from '../types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
import { getToolMethods, getTools } from '@/api/tools'
|
||||
import type { ToolType, ToolItem } from '@/views/ToolManagement/types'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
interface ToolModalProps {
|
||||
refresh: (tool: ToolOption) => void;
|
||||
}
|
||||
|
||||
const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
|
||||
refresh,
|
||||
}, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [optionList, setOptionList] = useState<ToolOption[]>([
|
||||
{ value: 'mcp', label: t('tool.mcp'), isLeaf: false },
|
||||
{ value: 'builtin', label: t('tool.inner'), isLeaf: false },
|
||||
{ value: 'custom', label: t('tool.custom'), isLeaf: false },
|
||||
])
|
||||
const [selectdTools, setSelectedTools] = useState<ToolOption[]>([])
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
setSelectedTools([])
|
||||
};
|
||||
|
||||
const handleOpen = () => {
|
||||
setVisible(true);
|
||||
form.resetFields();
|
||||
setSelectedTools([])
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
const handleSave = () => {
|
||||
form.validateFields().then(() => {
|
||||
setLoading(false)
|
||||
let operation: any = undefined
|
||||
if (selectdTools[0].value === 'mcp' || (selectdTools[0].value === 'builtin' && selectdTools[1]?.children && selectdTools[1].children.length > 1)) {
|
||||
operation = selectdTools[2].value
|
||||
} else if (selectdTools[0].value === 'custom') {
|
||||
operation = selectdTools[2].method_id
|
||||
}
|
||||
|
||||
const tool = {
|
||||
...selectdTools[2],
|
||||
label: selectdTools[0].value === 'custom' ? selectdTools[2].label : selectdTools[2].description,
|
||||
tool_id: selectdTools[1].value as string,
|
||||
enabled: true
|
||||
}
|
||||
if (operation) {
|
||||
tool.operation = operation
|
||||
}
|
||||
refresh(tool)
|
||||
handleClose()
|
||||
})
|
||||
}
|
||||
const loadData = (selectedOptions: ToolOption[]) => {
|
||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||
if (selectedOptions.length === 1) {
|
||||
getTools({ tool_type: targetOption.value as ToolType })
|
||||
.then(res => {
|
||||
const response = res as ToolItem[]
|
||||
targetOption.children = response.map((vo: any) => {
|
||||
return {
|
||||
value: vo.id,
|
||||
label: vo.name,
|
||||
isLeaf: response.length === 0,
|
||||
}
|
||||
})
|
||||
setOptionList([...optionList])
|
||||
})
|
||||
} else {
|
||||
getToolMethods(targetOption.value as string)
|
||||
.then(res => {
|
||||
const response = res as Array<{ method_id: string; name: string }>
|
||||
targetOption.children = response.map((vo: any) => {
|
||||
return {
|
||||
value: vo.name,
|
||||
label: vo.name,
|
||||
description: vo.description,
|
||||
isLeaf: true,
|
||||
method_id: vo.method_id,
|
||||
parameters: vo.parameters
|
||||
}
|
||||
})
|
||||
setOptionList([...optionList])
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange: CascaderProps<ToolOption>['onChange'] = (_value, selectedOptions) => {
|
||||
console.log('selectedOptions', selectedOptions)
|
||||
setSelectedTools(selectedOptions)
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
}));
|
||||
|
||||
return (
|
||||
<RbModal
|
||||
title={t(`application.addTool`)}
|
||||
open={visible}
|
||||
onCancel={handleClose}
|
||||
okText={t('common.save')}
|
||||
onOk={handleSave}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
>
|
||||
<FormItem
|
||||
name="agent_id"
|
||||
label={t('application.tool')}
|
||||
rules={[
|
||||
{ required: true, message: t('common.pleaseSelect') },
|
||||
]}
|
||||
>
|
||||
<Cascader
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={optionList}
|
||||
loadData={loadData}
|
||||
onChange={handleChange}
|
||||
changeOnSelect={false}
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</RbModal>
|
||||
);
|
||||
});
|
||||
|
||||
export default ToolModal;
|
||||
@@ -78,14 +78,7 @@ export interface Config extends MultiAgentConfig {
|
||||
knowledge_retrieval: KnowledgeConfig | null;
|
||||
memory?: MemoryConfig;
|
||||
variables: Variable[];
|
||||
tools: {
|
||||
web_search: {
|
||||
enabled: boolean;
|
||||
config: {
|
||||
web_search: boolean;
|
||||
}
|
||||
}
|
||||
};
|
||||
tools: ToolOption[];
|
||||
is_active: boolean;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
@@ -211,4 +204,31 @@ export interface AiPromptForm {
|
||||
model_id?: string;
|
||||
message?: string;
|
||||
current_prompt?: string;
|
||||
}
|
||||
export interface ToolModalRef {
|
||||
handleOpen: () => void;
|
||||
}
|
||||
|
||||
export interface ToolOption {
|
||||
value?: string | number | null;
|
||||
label?: React.ReactNode;
|
||||
description?: string;
|
||||
children?: ToolOption[];
|
||||
isLeaf?: boolean;
|
||||
method_id?: string;
|
||||
operation?: string;
|
||||
parameters?: Parameter[];
|
||||
tool_id?: string;
|
||||
enabled?: boolean;
|
||||
}
|
||||
export interface Parameter {
|
||||
name: string;
|
||||
type: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
default: any;
|
||||
enum: null | string[];
|
||||
minimum: number;
|
||||
maximum: number;
|
||||
pattern: null | string;
|
||||
}
|
||||
Reference in New Issue
Block a user