feat: ExternalToolEditor 支持 JSON 全量编辑与双向同步
parent
f1553be178
commit
49b48df182
|
|
@ -1,5 +1,5 @@
|
|||
import { CopyOutlined, DownOutlined, MinusCircleOutlined, PlusOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { App as AntApp, Button, Card, Form, Input, Modal, Select, Space } from 'antd';
|
||||
import { App as AntApp, Button, Card, Form, Input, Modal, Select, Space, Tabs } from 'antd';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { AgentAPI, ExternalToolApi, ExternalToolApiRouting, ExternalToolPlugin, ExternalToolPluginPayload } from '../api';
|
||||
|
||||
|
|
@ -150,9 +150,19 @@ export default function ExternalToolEditor({ open, agentId, plugin, onClose, onS
|
|||
const [form] = Form.useForm<ToolPluginFormValue>();
|
||||
const isEditing = Boolean(plugin);
|
||||
const apis = Form.useWatch('apis', form) || [];
|
||||
const allValues = Form.useWatch([], form);
|
||||
const [expandedApiIndexes, setExpandedApiIndexes] = useState<number[]>([]);
|
||||
const [jsonContent, setJsonContent] = useState('');
|
||||
const [activeTab, setActiveTab] = useState('visual');
|
||||
const [isSyncing, setIsSyncing] = useState(false);
|
||||
const previousApiCountRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (allValues && !isSyncing) {
|
||||
setJsonContent(JSON.stringify(allValues, null, 2));
|
||||
}
|
||||
}, [allValues, isSyncing]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
previousApiCountRef.current = 0;
|
||||
|
|
@ -181,6 +191,21 @@ export default function ExternalToolEditor({ open, agentId, plugin, onClose, onS
|
|||
previousApiCountRef.current = apiCount;
|
||||
}, [apis.length, open]);
|
||||
|
||||
const handleJsonChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const val = e.target.value;
|
||||
setJsonContent(val);
|
||||
try {
|
||||
const parsed = JSON.parse(val);
|
||||
if (parsed && typeof parsed === 'object') {
|
||||
setIsSyncing(true);
|
||||
form.setFieldsValue(parsed);
|
||||
setTimeout(() => setIsSyncing(false), 0);
|
||||
}
|
||||
} catch (e) {
|
||||
// 格式不正确时不更新表单
|
||||
}
|
||||
};
|
||||
|
||||
const toggleApiCard = (index: number) => {
|
||||
setExpandedApiIndexes((current) =>
|
||||
current.includes(index) ? current.filter((item) => item !== index) : [...current, index],
|
||||
|
|
@ -280,265 +305,298 @@ export default function ExternalToolEditor({ open, agentId, plugin, onClose, onS
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Form form={form} layout="vertical" requiredMark="optional">
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item label="工具集名称" name="name" rules={[{ required: true, message: '请输入工具集名称' }]}>
|
||||
<Input placeholder="例如:Hoyidata 工具集" />
|
||||
</Form.Item>
|
||||
<Form.Item label="API 基础地址" name="baseUrl" rules={[{ required: true, message: '请输入 API 基础地址' }]}>
|
||||
<Input placeholder="https://api.hoyidata.com" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item label="工具集描述" name="description">
|
||||
<Input.TextArea rows={2} placeholder="说明该工具集提供的能力" />
|
||||
</Form.Item>
|
||||
<Form.Item label="统一请求头(JSON 或对象)" name="headers">
|
||||
<Input.TextArea
|
||||
rows={3}
|
||||
className="agent-editor-code-input"
|
||||
placeholder={'{\n "Authorization": "Bearer YOUR_TOKEN"\n}'}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item label="认证方式" name="authType" rules={[{ required: true }]}>
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'none', label: '无需认证' },
|
||||
{ value: 'bearer', label: 'Bearer Token' },
|
||||
{ value: 'basic', label: 'Basic Auth' },
|
||||
{ value: 'apiKey', label: 'API Key' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="认证配置(JSON 或对象)" name="authConfig">
|
||||
<Input.TextArea rows={3} className="agent-editor-code-input" placeholder={'{\n token: "YOUR_API_KEY"\n}'} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form form={form} layout="vertical" requiredMark="optional" preserve={true}>
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onChange={setActiveTab}
|
||||
items={[
|
||||
{
|
||||
key: 'visual',
|
||||
label: '可视化编辑',
|
||||
children: (
|
||||
<>
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item label="工具集名称" name="name" rules={[{ required: true, message: '请输入工具集名称' }]}>
|
||||
<Input placeholder="例如:Hoyidata 工具集" />
|
||||
</Form.Item>
|
||||
<Form.Item label="API 基础地址" name="baseUrl" rules={[{ required: true, message: '请输入 API 基础地址' }]}>
|
||||
<Input placeholder="https://api.hoyidata.com" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item label="工具集描述" name="description">
|
||||
<Input.TextArea rows={2} placeholder="说明该工具集提供的能力" />
|
||||
</Form.Item>
|
||||
<Form.Item label="统一请求头(JSON 或对象)" name="headers">
|
||||
<Input.TextArea
|
||||
rows={3}
|
||||
className="agent-editor-code-input"
|
||||
placeholder={'{\n "Authorization": "Bearer YOUR_TOKEN"\n}'}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item label="认证方式" name="authType" rules={[{ required: true }]}>
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'none', label: '无需认证' },
|
||||
{ value: 'bearer', label: 'Bearer Token' },
|
||||
{ value: 'basic', label: 'Basic Auth' },
|
||||
{ value: 'apiKey', label: 'API Key' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="认证配置(JSON 或对象)" name="authConfig">
|
||||
<Input.TextArea rows={3} className="agent-editor-code-input" placeholder={'{\n token: "YOUR_API_KEY"\n}'} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<Form.List name="apis">
|
||||
{(fields, { add, remove }) => (
|
||||
<Space direction="vertical" size={12} className="agent-editor-tool-list">
|
||||
<div className="agent-editor-tool-list-header">
|
||||
<div>
|
||||
<strong>API 功能</strong>
|
||||
<div className="agent-editor-tool-help">一个工具集可同时绑定多个 API 功能。</div>
|
||||
</div>
|
||||
<Button type="dashed" icon={<PlusOutlined />} onClick={() => add({ ...EMPTY_API })}>
|
||||
添加 API
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{fields.map((field, index) => {
|
||||
const isExpanded = expandedApiIndexes.includes(index);
|
||||
const apiName = apis[index]?.name;
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={field.key}
|
||||
size="small"
|
||||
title={
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={() => toggleApiCard(index)}
|
||||
style={{ padding: 0, fontWeight: 500 }}
|
||||
icon={isExpanded ? <DownOutlined /> : <RightOutlined />}
|
||||
>
|
||||
{apiName || `API-${index}`}
|
||||
</Button>
|
||||
}
|
||||
className="agent-editor-tool-card"
|
||||
extra={
|
||||
<Space>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CopyOutlined />}
|
||||
onClick={() => {
|
||||
const currentApis = form.getFieldValue('apis') || [];
|
||||
const apiToCopy = currentApis[field.name];
|
||||
if (apiToCopy) {
|
||||
add({
|
||||
...apiToCopy,
|
||||
name: `${apiToCopy.name}-copy`,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
复制
|
||||
</Button>
|
||||
{fields.length > 1 ? (
|
||||
<Button type="text" danger size="small" icon={<MinusCircleOutlined />} onClick={() => remove(field.name)}>
|
||||
删除
|
||||
<Form.List name="apis">
|
||||
{(fields, { add, remove }) => (
|
||||
<Space direction="vertical" size={12} className="agent-editor-tool-list">
|
||||
<div className="agent-editor-tool-list-header">
|
||||
<div>
|
||||
<strong>API 功能</strong>
|
||||
<div className="agent-editor-tool-help">一个工具集可同时绑定多个 API 功能。</div>
|
||||
</div>
|
||||
<Button type="dashed" icon={<PlusOutlined />} onClick={() => add({ ...EMPTY_API })}>
|
||||
添加 API
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{fields.map((field, index) => {
|
||||
const isExpanded = expandedApiIndexes.includes(index);
|
||||
const apiName = apis[index]?.name;
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={field.key}
|
||||
size="small"
|
||||
title={
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={() => toggleApiCard(index)}
|
||||
style={{ padding: 0, fontWeight: 500 }}
|
||||
icon={isExpanded ? <DownOutlined /> : <RightOutlined />}
|
||||
>
|
||||
{apiName || `API-${index}`}
|
||||
</Button>
|
||||
}
|
||||
className="agent-editor-tool-card"
|
||||
extra={
|
||||
<Space>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<CopyOutlined />}
|
||||
onClick={() => {
|
||||
const currentApis = form.getFieldValue('apis') || [];
|
||||
const apiToCopy = currentApis[field.name];
|
||||
if (apiToCopy) {
|
||||
add({
|
||||
...apiToCopy,
|
||||
name: `${apiToCopy.name}-copy`,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
复制
|
||||
</Button>
|
||||
{fields.length > 1 ? (
|
||||
<Button type="text" danger size="small" icon={<MinusCircleOutlined />} onClick={() => remove(field.name)}>
|
||||
删除
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{isExpanded ? (
|
||||
<>
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item
|
||||
label="name(工具名)"
|
||||
name={[field.name, 'name']}
|
||||
rules={[
|
||||
{ required: true, message: '请输入工具名' },
|
||||
{ pattern: /^[A-Za-z0-9_]+$/, message: '仅支持字母、数字和下划线' },
|
||||
]}
|
||||
>
|
||||
<Input placeholder="query_hot_selling_products" />
|
||||
</Form.Item>
|
||||
<Form.Item label="method(调用方法)" name={[field.name, 'method']} rules={[{ required: true }]}>
|
||||
<Select options={['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].map((value) => ({ value, label: value }))} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item
|
||||
label="description(描述)"
|
||||
name={[field.name, 'description']}
|
||||
rules={[{ validator: validateTrimmedText('请输入描述') }]}
|
||||
>
|
||||
<Input.TextArea rows={2} placeholder="描述调用时机和工具能力" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="path(API 地址)"
|
||||
name={[field.name, 'path']}
|
||||
rules={[{ validator: validateTrimmedText('请输入 API 地址') }]}
|
||||
>
|
||||
<Input placeholder="/v1/products/hot-selling" />
|
||||
</Form.Item>
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item label="headers(请求头 JSON 或对象)" name={[field.name, 'headers']}>
|
||||
<Input.TextArea rows={7} className="agent-editor-code-input" placeholder={'{\n "X-Custom-Source": "aura-agent"\n}'} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="parametersSchema(依赖参数 JSON 或对象)"
|
||||
name={[field.name, 'parametersSchema']}
|
||||
rules={[{ required: true, message: '请输入依赖参数 Schema' }]}
|
||||
>
|
||||
<Input.TextArea rows={7} className="agent-editor-code-input" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Card size="small" title="routing(路由规则)">
|
||||
<Form.Item label="summary" name={[field.name, 'routing', 'summary']}>
|
||||
<Input placeholder="查询商品维度数据" />
|
||||
</Form.Item>
|
||||
<Form.List name={[field.name, 'routing', 'useWhen']} rules={[{ validator: validateRoutingList('至少添加一条 useWhen', 1) }]}>
|
||||
{(routingFields, { add: addUseWhen, remove: removeUseWhen }, { errors }) => (
|
||||
<Space direction="vertical" size={8} className="agent-editor-tool-list">
|
||||
<div className="agent-editor-tool-list-header">
|
||||
<div>
|
||||
<strong>useWhen</strong>
|
||||
<div className="agent-editor-tool-help">至少一项,描述什么情况下应该调用这个 API。</div>
|
||||
</div>
|
||||
<Button type="dashed" icon={<PlusOutlined />} onClick={() => addUseWhen('')}>
|
||||
添加条件
|
||||
</Button>
|
||||
</div>
|
||||
{routingFields.map((routingField) => (
|
||||
<Space key={routingField.key} align="start" className="agent-editor-tool-list">
|
||||
<Form.Item
|
||||
name={routingField.name}
|
||||
className="flex-1 mb-0"
|
||||
rules={[{ validator: validateTrimmedText('条件不能为空') }]}
|
||||
>
|
||||
<Input placeholder="用户明确要求商品数据" style={{ width: 600 }} />
|
||||
</Form.Item>
|
||||
<Button
|
||||
danger
|
||||
type="text"
|
||||
icon={<MinusCircleOutlined />}
|
||||
onClick={() => removeUseWhen(routingField.name)}
|
||||
disabled={routingFields.length <= 1}
|
||||
/>
|
||||
</Space>
|
||||
))}
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Space>
|
||||
)}
|
||||
</Form.List>
|
||||
<Form.List name={[field.name, 'routing', 'doNotUseWhen']} rules={[{ validator: validateRoutingList('列表项不能为空') }]}>
|
||||
{(routingFields, { add: addDoNotUseWhen, remove: removeDoNotUseWhen }, { errors }) => (
|
||||
<Space direction="vertical" size={8} className="agent-editor-tool-list" style={{ marginTop: 16 }}>
|
||||
<div className="agent-editor-tool-list-header">
|
||||
<div>
|
||||
<strong>doNotUseWhen</strong>
|
||||
<div className="agent-editor-tool-help">可为空,描述什么情况下不要调用这个 API。</div>
|
||||
</div>
|
||||
<Button type="dashed" icon={<PlusOutlined />} onClick={() => addDoNotUseWhen('')}>
|
||||
添加条件
|
||||
</Button>
|
||||
</div>
|
||||
{routingFields.map((routingField) => (
|
||||
<Space key={routingField.key} align="start" className="agent-editor-tool-list">
|
||||
<Form.Item
|
||||
name={routingField.name}
|
||||
className="flex-1 mb-0"
|
||||
rules={[{ validator: validateTrimmedText('条件不能为空') }]}
|
||||
>
|
||||
<Input placeholder="当前问题只需要其他维度数据" style={{ width: 600 }} />
|
||||
</Form.Item>
|
||||
<Button danger type="text" icon={<MinusCircleOutlined />} onClick={() => removeDoNotUseWhen(routingField.name)} />
|
||||
</Space>
|
||||
))}
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Space>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
<div className="agent-editor-tool-grid" style={{ marginTop: 16 }}>
|
||||
<Form.Item label="domains(领域)" name={[field.name, 'routing', 'domains']}>
|
||||
<Select mode="tags" placeholder="例如:e-commerce, logistics" />
|
||||
</Form.Item>
|
||||
<Form.Item label="intents(意图)" name={[field.name, 'routing', 'intents']}>
|
||||
<Select mode="tags" placeholder="例如:query_order, cancel_order" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item label="requiredSlots(必填槽位)" name={[field.name, 'routing', 'requiredSlots']}>
|
||||
<Select mode="tags" placeholder="例如:order_id, user_id" />
|
||||
</Form.Item>
|
||||
<Form.Item label="optionalSlots(可选槽位)" name={[field.name, 'routing', 'optionalSlots']}>
|
||||
<Select mode="tags" placeholder="例如:start_date, end_date" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<Form.List name={[field.name, 'routing', 'examples']}>
|
||||
{(exampleFields, { add: addExample, remove: removeExample }) => (
|
||||
<Space direction="vertical" size={8} className="agent-editor-tool-list" style={{ marginTop: 16 }}>
|
||||
<div className="agent-editor-tool-list-header">
|
||||
<div>
|
||||
<strong>examples(示例)</strong>
|
||||
<div className="agent-editor-tool-help">描述用户可能的提问方式。</div>
|
||||
</div>
|
||||
<Button type="dashed" icon={<PlusOutlined />} onClick={() => addExample('')}>
|
||||
添加示例
|
||||
</Button>
|
||||
</div>
|
||||
{exampleFields.map((exampleField) => (
|
||||
<Space key={exampleField.key} align="start" className="agent-editor-tool-list">
|
||||
<Form.Item
|
||||
name={exampleField.name}
|
||||
className="flex-1 mb-0"
|
||||
rules={[{ validator: validateTrimmedText('示例不能为空') }]}
|
||||
>
|
||||
<Input placeholder="我想查一下最近的订单" style={{ width: 600 }} />
|
||||
</Form.Item>
|
||||
<Button danger type="text" icon={<MinusCircleOutlined />} onClick={() => removeExample(exampleField.name)} />
|
||||
</Space>
|
||||
))}
|
||||
</Space>
|
||||
)}
|
||||
</Form.List>
|
||||
</Card>
|
||||
</>
|
||||
) : null}
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{isExpanded ? (
|
||||
<>
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item
|
||||
label="name(工具名)"
|
||||
name={[field.name, 'name']}
|
||||
rules={[
|
||||
{ required: true, message: '请输入工具名' },
|
||||
{ pattern: /^[A-Za-z0-9_]+$/, message: '仅支持字母、数字和下划线' },
|
||||
]}
|
||||
>
|
||||
<Input placeholder="query_hot_selling_products" />
|
||||
</Form.Item>
|
||||
<Form.Item label="method(调用方法)" name={[field.name, 'method']} rules={[{ required: true }]}>
|
||||
<Select options={['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].map((value) => ({ value, label: value }))} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item
|
||||
label="description(描述)"
|
||||
name={[field.name, 'description']}
|
||||
rules={[{ validator: validateTrimmedText('请输入描述') }]}
|
||||
>
|
||||
<Input.TextArea rows={2} placeholder="描述调用时机和工具能力" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="path(API 地址)"
|
||||
name={[field.name, 'path']}
|
||||
rules={[{ validator: validateTrimmedText('请输入 API 地址') }]}
|
||||
>
|
||||
<Input placeholder="/v1/products/hot-selling" />
|
||||
</Form.Item>
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item label="headers(请求头 JSON 或对象)" name={[field.name, 'headers']}>
|
||||
<Input.TextArea rows={7} className="agent-editor-code-input" placeholder={'{\n "X-Custom-Source": "aura-agent"\n}'} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="parametersSchema(依赖参数 JSON 或对象)"
|
||||
name={[field.name, 'parametersSchema']}
|
||||
rules={[{ required: true, message: '请输入依赖参数 Schema' }]}
|
||||
>
|
||||
<Input.TextArea rows={7} className="agent-editor-code-input" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Card size="small" title="routing(路由规则)">
|
||||
<Form.Item label="summary" name={[field.name, 'routing', 'summary']}>
|
||||
<Input placeholder="查询商品维度数据" />
|
||||
</Form.Item>
|
||||
<Form.List name={[field.name, 'routing', 'useWhen']} rules={[{ validator: validateRoutingList('至少添加一条 useWhen', 1) }]}>
|
||||
{(routingFields, { add: addUseWhen, remove: removeUseWhen }, { errors }) => (
|
||||
<Space direction="vertical" size={8} className="agent-editor-tool-list">
|
||||
<div className="agent-editor-tool-list-header">
|
||||
<div>
|
||||
<strong>useWhen</strong>
|
||||
<div className="agent-editor-tool-help">至少一项,描述什么情况下应该调用这个 API。</div>
|
||||
</div>
|
||||
<Button type="dashed" icon={<PlusOutlined />} onClick={() => addUseWhen('')}>
|
||||
添加条件
|
||||
</Button>
|
||||
</div>
|
||||
{routingFields.map((routingField) => (
|
||||
<Space key={routingField.key} align="start" className="agent-editor-tool-list">
|
||||
<Form.Item
|
||||
name={routingField.name}
|
||||
className="flex-1 mb-0"
|
||||
rules={[{ validator: validateTrimmedText('条件不能为空') }]}
|
||||
>
|
||||
<Input placeholder="用户明确要求商品数据" style={{ width: 600 }} />
|
||||
</Form.Item>
|
||||
<Button
|
||||
danger
|
||||
type="text"
|
||||
icon={<MinusCircleOutlined />}
|
||||
onClick={() => removeUseWhen(routingField.name)}
|
||||
disabled={routingFields.length <= 1}
|
||||
/>
|
||||
</Space>
|
||||
))}
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Space>
|
||||
)}
|
||||
</Form.List>
|
||||
<Form.List name={[field.name, 'routing', 'doNotUseWhen']} rules={[{ validator: validateRoutingList('列表项不能为空') }]}>
|
||||
{(routingFields, { add: addDoNotUseWhen, remove: removeDoNotUseWhen }, { errors }) => (
|
||||
<Space direction="vertical" size={8} className="agent-editor-tool-list" style={{ marginTop: 16 }}>
|
||||
<div className="agent-editor-tool-list-header">
|
||||
<div>
|
||||
<strong>doNotUseWhen</strong>
|
||||
<div className="agent-editor-tool-help">可为空,描述什么情况下不要调用这个 API。</div>
|
||||
</div>
|
||||
<Button type="dashed" icon={<PlusOutlined />} onClick={() => addDoNotUseWhen('')}>
|
||||
添加条件
|
||||
</Button>
|
||||
</div>
|
||||
{routingFields.map((routingField) => (
|
||||
<Space key={routingField.key} align="start" className="agent-editor-tool-list">
|
||||
<Form.Item
|
||||
name={routingField.name}
|
||||
className="flex-1 mb-0"
|
||||
rules={[{ validator: validateTrimmedText('条件不能为空') }]}
|
||||
>
|
||||
<Input placeholder="当前问题只需要其他维度数据" style={{ width: 600 }} />
|
||||
</Form.Item>
|
||||
<Button danger type="text" icon={<MinusCircleOutlined />} onClick={() => removeDoNotUseWhen(routingField.name)} />
|
||||
</Space>
|
||||
))}
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Space>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
<div className="agent-editor-tool-grid" style={{ marginTop: 16 }}>
|
||||
<Form.Item label="domains(领域)" name={[field.name, 'routing', 'domains']}>
|
||||
<Select mode="tags" placeholder="例如:e-commerce, logistics" />
|
||||
</Form.Item>
|
||||
<Form.Item label="intents(意图)" name={[field.name, 'routing', 'intents']}>
|
||||
<Select mode="tags" placeholder="例如:query_order, cancel_order" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="agent-editor-tool-grid">
|
||||
<Form.Item label="requiredSlots(必填槽位)" name={[field.name, 'routing', 'requiredSlots']}>
|
||||
<Select mode="tags" placeholder="例如:order_id, user_id" />
|
||||
</Form.Item>
|
||||
<Form.Item label="optionalSlots(可选槽位)" name={[field.name, 'routing', 'optionalSlots']}>
|
||||
<Select mode="tags" placeholder="例如:start_date, end_date" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<Form.List name={[field.name, 'routing', 'examples']}>
|
||||
{(exampleFields, { add: addExample, remove: removeExample }) => (
|
||||
<Space direction="vertical" size={8} className="agent-editor-tool-list" style={{ marginTop: 16 }}>
|
||||
<div className="agent-editor-tool-list-header">
|
||||
<div>
|
||||
<strong>examples(示例)</strong>
|
||||
<div className="agent-editor-tool-help">描述用户可能的提问方式。</div>
|
||||
</div>
|
||||
<Button type="dashed" icon={<PlusOutlined />} onClick={() => addExample('')}>
|
||||
添加示例
|
||||
</Button>
|
||||
</div>
|
||||
{exampleFields.map((exampleField) => (
|
||||
<Space key={exampleField.key} align="start" className="agent-editor-tool-list">
|
||||
<Form.Item
|
||||
name={exampleField.name}
|
||||
className="flex-1 mb-0"
|
||||
rules={[{ validator: validateTrimmedText('示例不能为空') }]}
|
||||
>
|
||||
<Input placeholder="我想查一下最近的订单" style={{ width: 600 }} />
|
||||
</Form.Item>
|
||||
<Button danger type="text" icon={<MinusCircleOutlined />} onClick={() => removeExample(exampleField.name)} />
|
||||
</Space>
|
||||
))}
|
||||
</Space>
|
||||
)}
|
||||
</Form.List>
|
||||
</Card>
|
||||
</>
|
||||
) : null}
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
)}
|
||||
</Form.List>
|
||||
)}
|
||||
</Form.List>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'json',
|
||||
label: 'JSON 编辑',
|
||||
children: (
|
||||
<div style={{ padding: '4px 0 16px' }}>
|
||||
<div style={{ marginBottom: 8, color: '#666', fontSize: 12 }}>
|
||||
提示:您可以直接编辑下方 JSON 内容,可视化表单将自动同步更新。
|
||||
</div>
|
||||
<Input.TextArea
|
||||
value={jsonContent}
|
||||
onChange={handleJsonChange}
|
||||
rows={25}
|
||||
className="agent-editor-code-input"
|
||||
placeholder="请输入完整的工具集配置 JSON"
|
||||
style={{ fontFamily: 'monospace' }}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue