Files
supersonic/webapp/packages/supersonic-fe/src/pages/Agent/MemorySection.tsx
2024-06-29 19:55:35 +08:00

282 lines
8.3 KiB
TypeScript

import type { ProColumns } from '@ant-design/pro-components';
import { EditableProTable } from '@ant-design/pro-components';
import React, { useState } from 'react';
import { MemoryType, ReviewEnum, StatusEnum } from './type';
import { getMemeoryList, saveMemory } from './service';
import { Popover, Input, Badge, Radio, Select } from 'antd';
import styles from './style.less';
const { TextArea, Search } = Input;
const RadioGroup = Radio.Group;
type Props = {
agentId: number;
};
const MemorySection = ({ agentId }: Props) => {
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
const [dataSource, setDataSource] = useState<readonly MemoryType[]>([]);
const [loading, setLoading] = useState(false);
const [filters, setFilters] = useState<any>({});
const { question, status, llmReviewRet, humanReviewRet } = filters;
const columns: ProColumns<MemoryType>[] = [
{
title: '用户问题',
dataIndex: 'question',
readonly: true,
},
{
title: 'Schema映射',
dataIndex: 'dbSchema',
width: 220,
valueType: 'textarea',
renderFormItem: (_, { record }) => (
<TextArea rows={3} disabled={record?.status === StatusEnum.ENABLED} />
),
},
{
title: '大模型解析SQL',
dataIndex: 's2sql',
width: 220,
valueType: 'textarea',
renderFormItem: (_, { record }) => (
<TextArea rows={3} disabled={record?.status === StatusEnum.ENABLED} />
),
},
{
title: '大模型评估意见',
dataIndex: 'llmReviewCmt',
readonly: true,
width: 200,
render: (value) => {
return (
<Popover trigger="hover" content={<div className={styles.commentPopover}>{value}</div>}>
<div className={styles.reviewComment}>{value}</div>
</Popover>
);
},
},
{
title: '大模型评估结果',
key: 'llmReviewRet',
dataIndex: 'llmReviewRet',
readonly: true,
width: 150,
valueEnum: {
[ReviewEnum.POSITIVE]: {
text: '正确',
status: 'Success',
},
[ReviewEnum.NEGATIVE]: {
text: '错误',
status: 'Error',
},
},
},
{
title: '管理员评估意见',
dataIndex: 'humanReviewCmt',
valueType: 'textarea',
renderFormItem: (_, { record }) => (
<TextArea rows={12} disabled={record?.status === StatusEnum.ENABLED} />
),
render: (value) => {
return value === '-' ? (
'-'
) : (
<Popover trigger="hover" content={<div className={styles.commentPopover}>{value}</div>}>
<div className={styles.reviewComment}>{value}</div>
</Popover>
);
},
},
{
title: '管理员评估结果',
key: 'humanReviewRet',
dataIndex: 'humanReviewRet',
width: 150,
valueType: 'radio',
renderFormItem: (_, { record }) => (
<RadioGroup
disabled={record?.status === StatusEnum.ENABLED}
options={[
{ label: '正确', value: ReviewEnum.POSITIVE },
{ label: '错误', value: ReviewEnum.NEGATIVE },
]}
/>
),
valueEnum: {
[ReviewEnum.POSITIVE]: {
text: '正确',
status: 'Success',
},
[ReviewEnum.NEGATIVE]: {
text: '错误',
status: 'Error',
},
},
},
{
title: '状态',
key: 'status',
dataIndex: 'status',
valueType: 'radio',
width: 120,
tooltip:
'若启用,将会把这条记录加入到向量库中作为样例召回供大模型参考以及作为相似问题推荐给用户',
valueEnum: {
[StatusEnum.PENDING]: { text: '待定' },
[StatusEnum.ENABLED]: {
text: '启用',
},
[StatusEnum.DISABLED]: {
text: '禁用',
},
},
render: (_, record) => {
const { status } = record;
if (status === StatusEnum.PENDING) {
return <Badge status="default" text="待定" />;
} else if (status === StatusEnum.ENABLED) {
return <Badge status="success" text="已启用" />;
} else {
return <Badge status="error" text="已禁用" />;
}
},
},
{
title: '操作',
valueType: 'option',
width: 150,
render: (text, record, _, action) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}
>
</a>,
],
},
];
const loadMemoryList = async ({
filtersValue,
current,
}: { filtersValue?: any; current?: number } = {}) => {
setLoading(true);
const res = await getMemeoryList(agentId, filtersValue || filters, current || 1);
setLoading(false);
const { list, total } = res.data;
return {
data: list,
total: total,
success: true,
};
};
const onSave = async (_: any, data: any) => {
await saveMemory(data);
};
const onSearch = () => {
loadMemoryList();
};
return (
<div className={styles.memorySection}>
<div className={styles.filterSection}>
<div className={styles.filterItem}>
<div className={styles.filterItemTitle}></div>
<Search
className={styles.filterItemControl}
placeholder="请输入用户问题"
value={question}
onChange={(e) => {
setFilters({ ...filters, question: e.target.value });
}}
onSearch={onSearch}
/>
</div>
<div className={styles.filterItem}>
<div className={styles.filterItemTitle}></div>
<Select
className={styles.filterItemControl}
placeholder="请选择大模型评估结果"
options={[
{ label: '正确', value: ReviewEnum.POSITIVE },
{ label: '错误', value: ReviewEnum.NEGATIVE },
]}
value={llmReviewRet}
allowClear
onChange={(value: ReviewEnum) => {
const filtersValue = { ...filters, llmReviewRet: value };
setFilters(filtersValue);
loadMemoryList({ filtersValue });
}}
/>
</div>
<div className={styles.filterItem}>
<div className={styles.filterItemTitle}></div>
<Select
className={styles.filterItemControl}
placeholder="请选择管理员评估结果"
options={[
{ label: '正确', value: ReviewEnum.POSITIVE },
{ label: '错误', value: ReviewEnum.NEGATIVE },
]}
value={humanReviewRet}
allowClear
onChange={(value: ReviewEnum) => {
const filtersValue = { ...filters, humanReviewRet: value };
setFilters(filtersValue);
loadMemoryList({ filtersValue });
}}
/>
</div>
<div className={styles.filterItem}>
<div className={styles.filterItemTitle}></div>
<Select
className={styles.filterItemControl}
placeholder="请选择状态"
options={[
{ label: '待定', value: StatusEnum.PENDING },
{ label: '已启用', value: StatusEnum.ENABLED },
{ label: '已禁用', value: StatusEnum.DISABLED },
]}
value={status}
allowClear
onChange={(value: ReviewEnum) => {
const filtersValue = { ...filters, status: value };
setFilters(filtersValue);
loadMemoryList({ filtersValue });
}}
/>
</div>
</div>
<EditableProTable<MemoryType>
rowKey="id"
recordCreatorProps={false}
loading={loading}
columns={columns}
request={loadMemoryList}
value={dataSource}
onChange={setDataSource}
pagination={{ pageSize: 10 }}
sticky={{ offsetHeader: 0 }}
editable={{
type: 'multiple',
editableKeys,
actionRender: (row, config, defaultDom) => [defaultDom.save, defaultDom.cancel],
onSave,
onChange: setEditableRowKeys,
}}
/>
</div>
);
};
export default MemorySection;