mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-12 20:51:48 +00:00
(feature)(supersonic-fe) add memory manage (#1279)
This commit is contained in:
@@ -183,5 +183,5 @@ export default defineConfig({
|
|||||||
alias: {
|
alias: {
|
||||||
'supersonic-chat-sdk': path.resolve(__dirname, '../../chat-sdk/src/'),
|
'supersonic-chat-sdk': path.resolve(__dirname, '../../chat-sdk/src/'),
|
||||||
},
|
},
|
||||||
// esbuildMinifyIIFE: true,
|
esbuildMinifyIIFE: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { uuid, jsonParse, encryptPassword, decryptPassword } from '@/utils/utils
|
|||||||
import ToolsSection from './ToolsSection';
|
import ToolsSection from './ToolsSection';
|
||||||
import globalStyles from '@/global.less';
|
import globalStyles from '@/global.less';
|
||||||
import { testLLMConn } from './service';
|
import { testLLMConn } from './service';
|
||||||
|
import MemorySection from './MemorySection';
|
||||||
|
|
||||||
const FormItem = Form.Item;
|
const FormItem = Form.Item;
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
@@ -273,6 +274,11 @@ const AgentForm: React.FC<Props> = ({ editAgent, onSaveAgent, onCreateToolBtnCli
|
|||||||
key: 'tools',
|
key: 'tools',
|
||||||
children: <ToolsSection currentAgent={editAgent} onSaveAgent={onSaveAgent} />,
|
children: <ToolsSection currentAgent={editAgent} onSaveAgent={onSaveAgent} />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '记忆管理',
|
||||||
|
key: 'memory',
|
||||||
|
children: <MemorySection />,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -288,6 +294,7 @@ const AgentForm: React.FC<Props> = ({ editAgent, onSaveAgent, onCreateToolBtnCli
|
|||||||
<Tabs
|
<Tabs
|
||||||
tabBarExtraContent={
|
tabBarExtraContent={
|
||||||
<Space>
|
<Space>
|
||||||
|
{activeKey !== 'memory' && (
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
loading={saveLoading}
|
loading={saveLoading}
|
||||||
@@ -297,6 +304,7 @@ const AgentForm: React.FC<Props> = ({ editAgent, onSaveAgent, onCreateToolBtnCli
|
|||||||
>
|
>
|
||||||
保 存
|
保 存
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
{activeKey === 'tools' && (
|
{activeKey === 'tools' && (
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|||||||
176
webapp/packages/supersonic-fe/src/pages/Agent/MemorySection.tsx
Normal file
176
webapp/packages/supersonic-fe/src/pages/Agent/MemorySection.tsx
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
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 } from 'antd';
|
||||||
|
import styles from './style.less';
|
||||||
|
|
||||||
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
const MemorySection = () => {
|
||||||
|
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
|
||||||
|
const [dataSource, setDataSource] = useState<readonly MemoryType[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const columns: ProColumns<MemoryType>[] = [
|
||||||
|
{
|
||||||
|
title: '用户问题',
|
||||||
|
dataIndex: 'question',
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Schema映射',
|
||||||
|
dataIndex: 'dbSchema',
|
||||||
|
width: 300,
|
||||||
|
valueType: 'textarea',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '大模型解析SQL',
|
||||||
|
dataIndex: 's2sql',
|
||||||
|
width: 300,
|
||||||
|
valueType: 'textarea',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '大模型评估意见',
|
||||||
|
dataIndex: 'llmReviewCmt',
|
||||||
|
readonly: true,
|
||||||
|
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: () => <TextArea rows={12} />,
|
||||||
|
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',
|
||||||
|
valueEnum: {
|
||||||
|
[ReviewEnum.POSITIVE]: {
|
||||||
|
text: '正确',
|
||||||
|
status: 'Success',
|
||||||
|
},
|
||||||
|
[ReviewEnum.NEGATIVE]: {
|
||||||
|
text: '错误',
|
||||||
|
status: 'Error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'status',
|
||||||
|
dataIndex: 'status',
|
||||||
|
valueType: 'radio',
|
||||||
|
width: 120,
|
||||||
|
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 () => {
|
||||||
|
setLoading(true);
|
||||||
|
const res = await getMemeoryList();
|
||||||
|
setLoading(false);
|
||||||
|
const { list, total } = res.data;
|
||||||
|
return {
|
||||||
|
data: list,
|
||||||
|
total: total,
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = async (_: any, data: any) => {
|
||||||
|
await saveMemory(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableProTable<MemoryType>
|
||||||
|
rowKey="id"
|
||||||
|
className={styles.memorySection}
|
||||||
|
recordCreatorProps={false}
|
||||||
|
loading={loading}
|
||||||
|
columns={columns}
|
||||||
|
request={loadMemoryList}
|
||||||
|
value={dataSource}
|
||||||
|
onChange={setDataSource}
|
||||||
|
pagination={{}}
|
||||||
|
editable={{
|
||||||
|
type: 'multiple',
|
||||||
|
editableKeys,
|
||||||
|
actionRender: (row, config, defaultDom) => [defaultDom.save, defaultDom.cancel],
|
||||||
|
onSave,
|
||||||
|
onChange: setEditableRowKeys,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MemorySection;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { request } from 'umi';
|
import { request } from 'umi';
|
||||||
import { AgentType, MetricType, ModelType } from './type';
|
import { AgentType, MemoryType, MetricType, ModelType } from './type';
|
||||||
|
|
||||||
export function getAgentList() {
|
export function getAgentList() {
|
||||||
return request<Result<AgentType[]>>('/api/chat/agent/getAgentList');
|
return request<Result<AgentType[]>>('/api/chat/agent/getAgentList');
|
||||||
@@ -41,3 +41,20 @@ export function testLLMConn(data: any) {
|
|||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMemeoryList() {
|
||||||
|
return request<Result<{ list: MetricType[] }>>('/api/chat/memory/pageMemories', {
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
current: 1,
|
||||||
|
pageSize: 2000,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveMemory(data: MemoryType) {
|
||||||
|
return request<Result<string>>('/api/chat/memory/updateMemory', {
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
color: var(--text-color-third)
|
color: var(--text-color-third);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,3 +316,24 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 12px;
|
column-gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.memorySection {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.ant-pro-card-body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reviewComment {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.commentPopover {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,3 +102,31 @@ export type MetricType = {
|
|||||||
name: string;
|
name: string;
|
||||||
bizName: string;
|
bizName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum StatusEnum {
|
||||||
|
PENDING = 'PENDING',
|
||||||
|
ENABLED = 'ENABLED',
|
||||||
|
DISABLED = 'DISABLED',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ReviewEnum {
|
||||||
|
POSITIVE = 'POSITIVE',
|
||||||
|
NEGATIVE = 'NEGATIVE',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MemoryType = {
|
||||||
|
id: number;
|
||||||
|
question: string;
|
||||||
|
agent_id: number;
|
||||||
|
db_schema: string;
|
||||||
|
s2_sql: string;
|
||||||
|
status: StatusEnum;
|
||||||
|
llm_review: ReviewEnum;
|
||||||
|
llm_comment: string;
|
||||||
|
human_review: ReviewEnum;
|
||||||
|
human_comment: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
created_by: string;
|
||||||
|
updated_by: string;
|
||||||
|
};
|
||||||
|
|||||||
30735
webapp/pnpm-lock.yaml
generated
30735
webapp/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user