[feature](weaapp) add agent

This commit is contained in:
williamhliu
2023-08-20 18:17:00 +08:00
parent c93e60ced7
commit aa218898ff
40 changed files with 1928 additions and 316 deletions

View File

@@ -0,0 +1,109 @@
import { Form, Modal, Input, Button, Switch } from 'antd';
import { AgentType } from './type';
import { useEffect, useState } from 'react';
import styles from './style.less';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { uuid } from '@/utils/utils';
const FormItem = Form.Item;
const { TextArea } = Input;
type Props = {
editAgent?: AgentType;
onSaveAgent: (agent: AgentType) => Promise<void>;
onCancel: () => void;
};
const AgentModal: React.FC<Props> = ({ editAgent, onSaveAgent, onCancel }) => {
const [saveLoading, setSaveLoading] = useState(false);
const [examples, setExamples] = useState<{ id: string; question?: string }[]>([]);
const [form] = Form.useForm();
useEffect(() => {
if (editAgent) {
form.setFieldsValue({ ...editAgent, enableSearch: editAgent.enableSearch !== 0 });
if (editAgent.examples) {
setExamples(editAgent.examples.map((question) => ({ id: uuid(), question })));
}
} else {
form.resetFields();
}
}, [editAgent]);
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 14 },
};
const onOk = async () => {
const values = await form.validateFields();
setSaveLoading(true);
await onSaveAgent({
id: editAgent?.id,
...(editAgent || {}),
...values,
examples: examples.map((example) => example.question),
enableSearch: values.enableSearch ? 1 : 0,
});
setSaveLoading(false);
};
return (
<Modal
open
title={editAgent ? '编辑助理' : '新建助理'}
confirmLoading={saveLoading}
width={800}
onOk={onOk}
onCancel={onCancel}
>
<Form {...layout} form={form} initialValues={{ enableSearch: true }}>
<FormItem name="name" label="名称" rules={[{ required: true, message: '请输入助理名称' }]}>
<Input placeholder="请输入助理名称" />
</FormItem>
<FormItem name="enableSearch" label="支持联想" valuePropName="checked">
<Switch checkedChildren="开启" unCheckedChildren="关闭" />
</FormItem>
<FormItem name="examples" label="示例问题">
<div className={styles.paramsSection}>
{examples.map((example) => {
const { id, question } = example;
return (
<div className={styles.filterRow} key={id}>
<Input
placeholder="示例问题"
value={question}
className={styles.questionExample}
onChange={(e) => {
example.question = e.target.value;
setExamples([...examples]);
}}
allowClear
/>
<DeleteOutlined
onClick={() => {
setExamples(examples.filter((item) => item.id !== id));
}}
/>
</div>
);
})}
<Button
onClick={() => {
setExamples([...examples, { id: uuid() }]);
}}
>
<PlusOutlined />
</Button>
</div>
</FormItem>
<FormItem name="description" label="描述">
<TextArea placeholder="请输入助理描述" />
</FormItem>
</Form>
</Modal>
);
};
export default AgentModal;

View File

@@ -0,0 +1,144 @@
import { DeleteOutlined, EditOutlined, PlusOutlined, UserOutlined } from '@ant-design/icons';
import { Button, Input, Popconfirm, Switch } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import styles from './style.less';
import { AgentType } from './type';
const { Search } = Input;
type Props = {
agents: AgentType[];
currentAgent?: AgentType;
loading: boolean;
onSelectAgent: (agent: AgentType) => void;
onDeleteAgent: (id: number) => void;
onEditAgent: (agent?: AgentType) => void;
onSaveAgent: (agent: AgentType, noTip?: boolean) => Promise<void>;
};
const AgentsSection: React.FC<Props> = ({
agents,
currentAgent,
onSelectAgent,
onDeleteAgent,
onEditAgent,
onSaveAgent,
}) => {
// const [searchName, setSearchName] = useState('');
const [showAgents, setShowAgents] = useState<AgentType[]>([]);
useEffect(() => {
setShowAgents(agents);
}, [agents]);
return (
<div className={styles.agentsSection}>
{/* <div className={styles.sectionTitle}>问答助理</div> */}
<div className={styles.content}>
<div className={styles.searchBar}>
{/* <Search
placeholder="请输入助理名称搜索"
className={styles.searchControl}
value={searchName}
onChange={(e) => {
setSearchName(e.target.value);
}}
onSearch={(value) => {
setShowAgents(
agents.filter((agent) =>
agent.name?.trim().toLowerCase().includes(value.trim().toLowerCase()),
),
);
}}
/> */}
<Button
type="primary"
onClick={() => {
onEditAgent(undefined);
}}
>
<PlusOutlined />
</Button>
</div>
<div className={styles.agentsContainer}>
{showAgents.map((agent) => {
const agentItemClass = classNames(styles.agentItem, {
[styles.agentActive]: agent.id === currentAgent?.id,
});
return (
<div
className={agentItemClass}
key={agent.id}
onClick={() => {
onSelectAgent(agent);
}}
>
<UserOutlined className={styles.agentIcon} />
<div className={styles.agentContent}>
<div className={styles.agentNameBar}>
<div className={styles.agentName}>{agent.name}</div>
<div className={styles.operateIcons}>
<EditOutlined
className={styles.operateIcon}
onClick={(e) => {
e.stopPropagation();
onEditAgent(agent);
}}
/>
<Popconfirm
title="确定删除吗?"
onCancel={(e) => {
e?.stopPropagation();
}}
onConfirm={(e) => {
e?.stopPropagation();
onDeleteAgent(agent.id!);
}}
>
<DeleteOutlined
className={styles.operateIcon}
onClick={(e) => {
e.stopPropagation();
}}
/>
</Popconfirm>
</div>
</div>
<div className={styles.bottomBar}>
<div className={styles.agentDescription} title={agent.description}>
{agent.description}
</div>
<div className={styles.toggleStatus}>
{agent.status === 0 ? (
'已禁用'
) : (
<span className={styles.online}></span>
)}
<span
onClick={(e) => {
e.stopPropagation();
}}
>
<Switch
size="small"
defaultChecked={agent.status === 1}
onChange={(value) => {
onSaveAgent({ ...agent, status: value ? 1 : 0 }, true);
}}
/>
</span>
</div>
</div>
</div>
</div>
);
})}
</div>
</div>
</div>
);
};
export default AgentsSection;

View File

@@ -0,0 +1,264 @@
import { Form, Modal, Input, Select, Button } from 'antd';
import {
AgentToolType,
AgentToolTypeEnum,
AGENT_TOOL_TYPE_LIST,
MetricOptionType,
MetricType,
ModelType,
QUERY_MODE_LIST,
} from './type';
import { useEffect, useState } from 'react';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import styles from './style.less';
import { getLeafList, uuid } from '@/utils/utils';
import { getMetricList, getModelList } from './service';
import { PluginType } from '../ChatPlugin/type';
import { getPluginList } from '../ChatPlugin/service';
const FormItem = Form.Item;
type Props = {
editTool?: AgentToolType;
onSaveTool: (tool: AgentToolType) => Promise<void>;
onCancel: () => void;
};
const ToolModal: React.FC<Props> = ({ editTool, onSaveTool, onCancel }) => {
const [toolType, setToolType] = useState<AgentToolTypeEnum>();
const [modelList, setModelList] = useState<ModelType[]>([]);
const [saveLoading, setSaveLoading] = useState(false);
const [examples, setExamples] = useState<{ id: string; question?: string }[]>([]);
const [metricOptions, setMetricOptions] = useState<MetricOptionType[]>([]);
const [modelMetricList, setModelMetricList] = useState<MetricType[]>([]);
const [plugins, setPlugins] = useState<PluginType[]>([]);
const [form] = Form.useForm();
const initModelList = async () => {
const res = await getModelList();
setModelList([{ id: -1, name: '默认' }, ...getLeafList(res.data)]);
};
const initPluginList = async () => {
const res = await getPluginList({});
setPlugins(res.data || []);
};
useEffect(() => {
initModelList();
initPluginList();
}, []);
const initModelMetrics = async (params: any) => {
const res = await getMetricList(params[0].modelId);
setModelMetricList(res.data.list);
};
useEffect(() => {
if (editTool) {
form.setFieldsValue({ ...editTool, plugins: editTool.plugins?.[0] });
setToolType(editTool.type);
setExamples(
(editTool.exampleQuestions || []).map((item) => ({ id: uuid(), question: item })),
);
setMetricOptions(editTool.metricOptions || []);
if (editTool.metricOptions && editTool.metricOptions.length > 0) {
initModelMetrics(editTool.metricOptions || []);
}
} else {
form.resetFields();
}
}, [editTool]);
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 14 },
};
const onOk = async () => {
const values = await form.validateFields();
setSaveLoading(true);
await onSaveTool({
id: editTool?.id,
...values,
exampleQuestions: examples.map((item) => item.question).filter((item) => item),
plugins: values.plugins ? [values.plugins] : undefined,
metricOptions: metricOptions.map((item) => ({ modelId: values.modelId, ...item })),
});
setSaveLoading(false);
};
const updateMetricList = async (value: number) => {
if (modelMetricList[value]) {
return;
}
const res = await getMetricList(value);
setModelMetricList(res.data.list);
};
return (
<Modal
open
title={editTool ? '编辑工具' : '新建工具'}
confirmLoading={saveLoading}
width={800}
onOk={onOk}
onCancel={onCancel}
>
<Form {...layout} form={form}>
<FormItem name="type" label="类型" rules={[{ required: true, message: '请选择工具类型' }]}>
<Select
options={AGENT_TOOL_TYPE_LIST}
placeholder="请选择工具类型"
onChange={setToolType}
/>
</FormItem>
<FormItem name="name" label="名称">
<Input placeholder="请输入工具名称" />
</FormItem>
{toolType === AgentToolTypeEnum.DSL && (
<>
<FormItem name="modelIds" label="主题域">
<Select
options={modelList.map((model) => ({ label: model.name, value: model.id }))}
placeholder="请选择主题域"
mode="multiple"
/>
</FormItem>
<FormItem name="exampleQuestions" label="示例问题">
<div className={styles.paramsSection}>
{examples.map((example) => {
const { id, question } = example;
return (
<div className={styles.filterRow} key={id}>
<Input
placeholder="示例问题"
value={question}
className={styles.questionExample}
onChange={(e) => {
example.question = e.target.value;
setExamples([...examples]);
}}
allowClear
/>
<DeleteOutlined
onClick={() => {
setExamples(examples.filter((item) => item.id !== id));
}}
/>
</div>
);
})}
<Button
onClick={() => {
setExamples([...examples, { id: uuid() }]);
}}
>
<PlusOutlined />
</Button>
</div>
</FormItem>
</>
)}
{toolType === AgentToolTypeEnum.INTERPRET && (
<>
<FormItem name="modelId" label="主题域">
<Select
options={modelList.map((model) => ({ label: model.name, value: model.id }))}
showSearch
filterOption={(input, option) =>
((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
}
placeholder="请选择主题域"
onChange={(value) => {
setMetricOptions([...metricOptions]);
updateMetricList(value);
}}
/>
</FormItem>
<FormItem name="params" label="指标">
<div className={styles.paramsSection}>
{metricOptions.map((filter: any) => {
return (
<div className={styles.filterRow} key={filter.id}>
<Select
placeholder="请选择指标,需先选择主题域"
options={(modelMetricList || []).map((metric) => ({
label: metric.name,
value: `${metric.id}`,
}))}
showSearch
className={styles.filterParamValueField}
filterOption={(input, option) =>
((option?.label ?? '') as string)
.toLowerCase()
.includes(input.toLowerCase())
}
allowClear
value={filter.metricId}
onChange={(value) => {
filter.metricId = value;
setMetricOptions([...metricOptions]);
}}
/>
<DeleteOutlined
onClick={() => {
setMetricOptions(metricOptions.filter((item) => item.id !== filter.id));
}}
/>
</div>
);
})}
<Button
onClick={() => {
setMetricOptions([
...metricOptions,
{ id: uuid(), metricId: undefined, modelId: undefined },
]);
}}
>
<PlusOutlined />
</Button>
</div>
</FormItem>
</>
)}
{toolType === AgentToolTypeEnum.PLUGIN && (
<FormItem name="plugins" label="插件">
<Select
placeholder="请选择插件"
options={plugins.map((plugin) => ({ label: plugin.name, value: plugin.id }))}
showSearch
filterOption={(input, option) =>
((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
}
onChange={(value) => {
const plugin = plugins.find((item) => item.id === value);
if (plugin) {
form.setFieldsValue({ name: plugin.name });
}
}}
/>
</FormItem>
)}
{toolType === AgentToolTypeEnum.RULE && (
<FormItem name="queryModes" label="查询模式">
<Select
placeholder="请选择查询模式"
options={QUERY_MODE_LIST}
showSearch
mode="multiple"
filterOption={(input, option) =>
((option?.label ?? '') as string).toLowerCase().includes(input.toLowerCase())
}
/>
</FormItem>
)}
</Form>
</Modal>
);
};
export default ToolModal;

View File

@@ -0,0 +1,196 @@
import { uuid } from '@/utils/utils';
import {
ArrowLeftOutlined,
DeleteOutlined,
EditOutlined,
PlusOutlined,
ToolOutlined,
} from '@ant-design/icons';
import { Button, Empty, Popconfirm, Space, Switch, Tag } from 'antd';
import { useState } from 'react';
import styles from './style.less';
import ToolModal from './ToolModal';
import { AgentToolType, AgentType, AGENT_TOOL_TYPE_LIST } from './type';
type Props = {
currentAgent?: AgentType;
onSaveAgent: (agent: AgentType, noTip?: boolean) => Promise<void>;
onEditAgent: (agent?: AgentType) => void;
goBack: () => void;
};
const ToolsSection: React.FC<Props> = ({ currentAgent, onSaveAgent, onEditAgent, goBack }) => {
const [modalVisible, setModalVisible] = useState(false);
const [editTool, setEditTool] = useState<AgentToolType>();
const agentConfig = currentAgent?.agentConfig ? JSON.parse(currentAgent.agentConfig as any) : {};
const saveAgent = async (agent: AgentType) => {
await onSaveAgent(agent);
setModalVisible(false);
};
const onSaveTool = async (tool: AgentToolType) => {
const newAgentConfig = agentConfig || ({} as any);
if (!newAgentConfig.tools) {
newAgentConfig.tools = [];
}
if (tool.id) {
const index = newAgentConfig.tools.findIndex((item: AgentToolType) => item.id === tool.id);
newAgentConfig.tools[index] = tool;
} else {
newAgentConfig.tools.push({ ...tool, id: uuid() });
}
await saveAgent({
...currentAgent,
agentConfig: JSON.stringify(newAgentConfig) as any,
});
setModalVisible(false);
};
const onDeleteTool = async (tool: AgentToolType) => {
const newAgentConfig = agentConfig || ({} as any);
if (!newAgentConfig.tools) {
newAgentConfig.tools = [];
}
newAgentConfig.tools = newAgentConfig.tools.filter(
(item: AgentToolType) => item.id !== tool.id,
);
await saveAgent({
...currentAgent,
agentConfig: JSON.stringify(newAgentConfig) as any,
});
};
return (
<div className={styles.toolsSection}>
<div className={styles.toolsSectionTitleBar}>
<ArrowLeftOutlined className={styles.backIcon} onClick={goBack} />
<div className={styles.agentTitle}>{currentAgent?.name}</div>
<div className={styles.toggleStatus}>
{currentAgent?.status === 0 ? '已禁用' : <span className={styles.online}></span>}
<span
onClick={(e) => {
e.stopPropagation();
}}
>
<Switch
size="small"
defaultChecked={currentAgent?.status === 1}
onChange={(value) => {
onSaveAgent({ ...currentAgent, status: value ? 1 : 0 }, true);
}}
/>
</span>
</div>
</div>
<div className={styles.basicInfo}>
<div className={styles.basicInfoTitle}>
<Button
type="primary"
onClick={() => {
onEditAgent(currentAgent);
}}
>
</Button>
</div>
<div className={styles.infoContent}>
<div className={styles.infoItem}>
<Space>
{currentAgent?.examples?.map((item) => (
<Tag key={item}>{item}</Tag>
))}
</Space>
</div>
<div className={styles.infoItem}>{currentAgent?.description}</div>
</div>
</div>
<div className={styles.toolSection}>
<div className={styles.toolSectionTitleBar}>
<div className={styles.toolSectionTitle}></div>
<Button
type="primary"
onClick={() => {
setEditTool(undefined);
setModalVisible(true);
}}
>
<PlusOutlined />
</Button>
</div>
{agentConfig?.tools && agentConfig?.tools?.length > 0 ? (
<div className={styles.toolsContent}>
{agentConfig.tools.map((tool: AgentToolType) => {
const toolType = AGENT_TOOL_TYPE_LIST.find((item) => item.value === tool.type)?.label;
return (
<div
className={styles.toolItem}
key={tool.id}
onClick={() => {
setEditTool(tool);
setModalVisible(true);
}}
>
<ToolOutlined className={styles.toolIcon} />
<div className={styles.toolContent}>
<div className={styles.toolTopSection}>
<div className={styles.toolType}>{tool.name || toolType}</div>
<div className={styles.toolOperateIcons}>
<EditOutlined
className={styles.toolOperateIcon}
onClick={(e) => {
e.stopPropagation();
setEditTool(tool);
setModalVisible(true);
}}
/>
<Popconfirm
title="确定删除吗?"
onCancel={(e) => {
e?.stopPropagation();
}}
onConfirm={(e) => {
e?.stopPropagation();
onDeleteTool(tool);
}}
>
<DeleteOutlined
className={styles.toolOperateIcon}
onClick={(e) => {
e.stopPropagation();
}}
/>
</Popconfirm>
</div>
</div>
<div className={styles.toolDesc} title={toolType}>
{toolType}
</div>
</div>
</div>
);
})}
</div>
) : (
<div className={styles.emptyHolder}>
<Empty description={`${currentAgent?.name}】暂无工具,请新增工具`} />
</div>
)}
</div>
{modalVisible && (
<ToolModal
editTool={editTool}
onSaveTool={onSaveTool}
onCancel={() => {
setModalVisible(false);
}}
/>
)}
</div>
);
};
export default ToolsSection;

View File

@@ -0,0 +1,94 @@
import { message } from 'antd';
import { useEffect, useState } from 'react';
import AgentsSection from './AgentsSection';
import AgentModal from './AgentModal';
import { deleteAgent, getAgentList, saveAgent } from './service';
import styles from './style.less';
import ToolsSection from './ToolsSection';
import { AgentType } from './type';
const Agent = () => {
const [agents, setAgents] = useState<AgentType[]>([]);
const [loading, setLoading] = useState(false);
const [currentAgent, setCurrentAgent] = useState<AgentType>();
const [modalVisible, setModalVisible] = useState(false);
const [editAgent, setEditAgent] = useState<AgentType>();
const updateData = async () => {
setLoading(true);
const res = await getAgentList();
setLoading(false);
setAgents(res.data || []);
if (!res.data?.length) {
return;
}
if (currentAgent) {
const agent = res.data.find((item) => item.id === currentAgent.id);
if (agent) {
setCurrentAgent(agent);
} else {
setCurrentAgent(res.data[0]);
}
}
};
useEffect(() => {
updateData();
}, []);
const onSaveAgent = async (agent: AgentType, noTip?: boolean) => {
await saveAgent(agent);
if (!noTip) {
message.success('保存成功');
}
setModalVisible(false);
updateData();
};
const onDeleteAgent = async (id: number) => {
await deleteAgent(id);
message.success('删除成功');
updateData();
};
const onEditAgent = (agent?: AgentType) => {
setEditAgent(agent);
setModalVisible(true);
};
return (
<div className={styles.agent}>
{!currentAgent ? (
<AgentsSection
agents={agents}
currentAgent={currentAgent}
loading={loading}
onSelectAgent={setCurrentAgent}
onEditAgent={onEditAgent}
onDeleteAgent={onDeleteAgent}
onSaveAgent={onSaveAgent}
/>
) : (
<ToolsSection
currentAgent={currentAgent}
onSaveAgent={onSaveAgent}
onEditAgent={onEditAgent}
goBack={() => {
setCurrentAgent(undefined);
}}
/>
)}
{modalVisible && (
<AgentModal
editAgent={editAgent}
onSaveAgent={onSaveAgent}
onCancel={() => {
setModalVisible(false);
}}
/>
)}
</div>
);
};
export default Agent;

View File

@@ -0,0 +1,36 @@
import { request } from "umi";
import { AgentType, MetricType, ModelType } from "./type";
export function getAgentList() {
return request<Result<AgentType[]>>('/api/chat/agent/getAgentList');
}
export function saveAgent(agent: AgentType) {
return request<Result<any>>('/api/chat/agent', {
method: agent?.id ? 'PUT' : 'POST',
data: {...agent, status: agent.status !== undefined ? agent.status : 1},
});
}
export function deleteAgent(id: number) {
return request<Result<any>>(`/api/chat/agent/${id}`, {
method: 'DELETE',
});
}
export function getModelList() {
return request<Result<ModelType[]>>('/api/chat/conf/modelList', {
method: 'GET',
});
}
export function getMetricList(modelId: number) {
return request<Result<{list: MetricType[]}>>('/api/semantic/metric/queryMetric', {
method: 'POST',
data: {
modelIds: [modelId],
current: 1,
pageSize: 2000
}
});
}

View File

@@ -0,0 +1,292 @@
.agent {
// background: #fff;
height: calc(100vh - 48px);
}
.agentsSection {
padding: 20px 40px;
background: #fff;
height: calc(100vh - 48px);
.sectionTitle {
font-size: 24px;
font-weight: 700;
padding-bottom: 30px;
}
}
.content {
margin-top: 20px;
.searchBar {
display: flex;
align-items: center;
column-gap: 20px;
margin-bottom: 40px;
.searchControl {
width: 500px;
}
}
.agentsContainer {
margin-top: 20px;
margin-bottom: 20px;
display: flex;
flex-wrap: wrap;
gap: 16px;
.agentItem {
display: flex;
width: 290px;
align-items: center;
border-radius: 10px;
border: 1px solid #e8e8e8;
padding: 12px;
cursor: pointer;
&:hover {
border-color: var(--chat-blue);
}
&.agentActive {
border-color: var(--chat-blue);
}
.agentIcon {
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: var(--chat-blue);
width: 40px;
height: 40px;
border-radius: 50%;
background: #e8e8e8;
}
.agentContent {
margin-left: 12px;
flex: 1;
.agentNameBar {
display: flex;
align-items: center;
justify-content: space-between;
.agentName {
font-size: 14px;
font-weight: 500;
color: var(--text-color);
}
.operateIcons {
display: flex;
align-items: center;
column-gap: 12px;
.operateIcon {
color: var(--text-color-fourth);
cursor: pointer;
&:hover {
color: var(--chat-blue);
}
}
}
}
.bottomBar {
display: flex;
align-items: center;
margin-top: 10px;
width: 210px;
.agentDescription {
width: 120px;
margin-right: 10px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: 12px;
margin-top: 4px;
color: var(--text-color-third)
}
}
}
}
}
}
.toggleStatus {
display: flex;
align-items: center;
column-gap: 12px;
font-size: 12px;
.online {
color: var(--chat-blue);
}
}
.toolsSection {
.toolsSectionTitleBar {
padding: 20px 30px;
background-color: #fff;
display: flex;
align-items: center;
column-gap: 20px;
.backIcon {
font-size: 20px;
color: var(--text-color);
cursor: pointer;
&:hover {
color: var(--chat-blue);
}
}
.agentTitle {
font-size: 24px;
font-weight: 700;
}
}
}
.paramsSection {
display: flex;
flex-direction: column;
row-gap: 12px;
.filterRow {
display: flex;
align-items: center;
column-gap: 12px;
.filterParamName {
width: 120px;
}
.filterParamValueField {
width: 230px;
}
.questionExample {
width: 100%;
}
}
}
.basicInfo {
margin: 20px;
background: #fff;
border-radius: 6px;
.basicInfoTitle {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 30px;
font-size: 16px;
font-weight: 500;
padding: 14px 20px;
border-bottom: 1px solid #e8e8e8;
}
.infoContent {
padding: 20px;
display: flex;
flex-direction: column;
row-gap: 12px;
}
}
.toolSection {
margin: 20px;
background: #fff;
border-radius: 6px;
.toolSectionTitleBar {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 30px;
padding: 14px 20px;
border-bottom: 1px solid #e8e8e8;
.toolSectionTitle {
font-size: 16px;
font-weight: 500;
}
}
.emptyHolder {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
}
.toolsContent {
display: flex;
flex-wrap: wrap;
gap: 16px;
padding: 20px 20px 30px;
.toolItem {
width: 300px;
padding: 12px;
border-radius: 10px;
border: 1px solid #e8e8e8;
display: flex;
align-items: center;
column-gap: 12px;
cursor: pointer;
.toolIcon {
font-size: 24px;
color: var(--chat-blue);
}
.toolContent {
flex: 1;
.toolTopSection {
display: flex;
align-items: center;
justify-content: space-between;
.toolType {
flex: 1;
color: var(--text-color);
font-weight: 500;
font-size: 16px;
}
.toolOperateIcons {
display: flex;
align-items: center;
column-gap: 10px;
.toolOperateIcon {
color: var(--text-color-third);
cursor: pointer;
&:hover {
color: var(--chat-blue);
}
}
}
}
.toolDesc {
margin-top: 2px;
width: 220px;
font-size: 13px;
color: var(--text-color-third);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
}
}

View File

@@ -0,0 +1,119 @@
export type MetricOptionType = {
id: string;
metricId?: number;
modelId?: number;
}
export enum AgentToolTypeEnum {
RULE = 'RULE',
DSL = 'DSL',
PLUGIN = 'PLUGIN',
INTERPRET = 'INTERPRET'
}
export enum QueryModeEnum {
ENTITY_DETAIL = 'ENTITY_DETAIL',
ENTITY_LIST_FILTER = 'ENTITY_LIST_FILTER',
ENTITY_ID = 'ENTITY_ID',
METRIC_ENTITY = 'METRIC_ENTITY',
METRIC_FILTER = 'METRIC_FILTER',
METRIC_GROUPBY = 'METRIC_GROUPBY',
METRIC_MODEL = 'METRIC_MODEL',
METRIC_ORDERBY = 'METRIC_ORDERBY'
}
export const AGENT_TOOL_TYPE_LIST = [
{
label: '规则',
value: AgentToolTypeEnum.RULE
},
{
label: 'LLM语义解析',
value: AgentToolTypeEnum.DSL
},
{
label: '指标解读',
value: AgentToolTypeEnum.INTERPRET
},
{
label: '插件',
value: AgentToolTypeEnum.PLUGIN
},
]
export const QUERY_MODE_LIST = [
{
label: '实体明细(查询维度信息)',
value: QueryModeEnum.ENTITY_DETAIL
},
{
label: '实体圈选',
value: QueryModeEnum.ENTITY_LIST_FILTER
},
{
label: '实体查询(按ID查询)',
value: QueryModeEnum.ENTITY_ID
},
{
label: '指标查询(带实体)',
value: QueryModeEnum.METRIC_ENTITY
},
{
label: '指标查询(带条件)',
value: QueryModeEnum.METRIC_FILTER
},
{
label: '指标查询(按维度分组)',
value: QueryModeEnum.METRIC_GROUPBY
},
{
label: '指标查询(不带条件)',
value: QueryModeEnum.METRIC_MODEL
},
{
label: '按指标排序',
value: QueryModeEnum.METRIC_ORDERBY
}
];
export type AgentToolType = {
id?: string;
type: AgentToolTypeEnum;
name: string;
queryModes?: QueryModeEnum[];
plugins?: number[];
metricOptions?: MetricOptionType[];
exampleQuestions?: string[];
modelIds?: number[];
}
export type AgentConfigType = {
tools: AgentToolType[];
}
export type AgentType = {
id?: number;
name?: string;
description?: string;
createdBy?: string;
updatedBy?: string;
createdAt?: string;
updatedAt?: string;
examples?: string[];
status?: 0 | 1;
enableSearch?: 0 | 1;
agentConfig?: AgentConfigType;
}
export type ModelType = {
id: number | string;
parentId: number;
name: string;
bizName: string;
};
export type MetricType = {
id: number;
name: string;
bizName: string;
};