mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-11 13:58:58 +00:00
feat: Remove unused code for agent, models, and knowledge chunk pages (#1873)
This commit is contained in:
@@ -1,34 +0,0 @@
|
|||||||
import MarketPlugins from '@/components/agent/market-plugins';
|
|
||||||
import MyPlugins from '@/components/agent/my-plugins';
|
|
||||||
import { Tabs } from 'antd';
|
|
||||||
import { useMemo, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
function Agent() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [activeKey, setActiveKey] = useState('market');
|
|
||||||
|
|
||||||
const items: Required<Parameters<typeof Tabs>[0]['items']> = useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
key: 'market',
|
|
||||||
label: t('Market_Plugins'),
|
|
||||||
children: <MarketPlugins />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'my',
|
|
||||||
label: t('My_Plugins'),
|
|
||||||
children: activeKey === 'market' ? null : <MyPlugins />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[t, activeKey],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-screen p-4 md:p-6 overflow-y-auto">
|
|
||||||
<Tabs activeKey={activeKey} items={items} onChange={setActiveKey} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Agent;
|
|
@@ -1,114 +0,0 @@
|
|||||||
import AppModal from '@/components/app/app-modal';
|
|
||||||
import AppCard from '@/components/app/app-card';
|
|
||||||
import { Button, Spin, Tabs, TabsProps } from 'antd';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { apiInterceptors, getAppList } from '@/client/api';
|
|
||||||
import { IApp } from '@/types/app';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import MyEmpty from '@/components/common/MyEmpty';
|
|
||||||
|
|
||||||
type TabKey = 'app' | 'collected';
|
|
||||||
|
|
||||||
type ModalType = 'edit' | 'add';
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
|
||||||
const [spinning, setSpinning] = useState<boolean>(false);
|
|
||||||
const [activeKey, setActiveKey] = useState<TabKey>('app');
|
|
||||||
const [apps, setApps] = useState<IApp[]>([]);
|
|
||||||
const [curApp, setCurApp] = useState<IApp>();
|
|
||||||
const [modalType, setModalType] = useState<ModalType>('add');
|
|
||||||
|
|
||||||
const handleCreate = () => {
|
|
||||||
setModalType('add');
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEdit = (app: any) => {
|
|
||||||
setModalType('edit');
|
|
||||||
setCurApp(app);
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTabChange = (activeKey: string) => {
|
|
||||||
setActiveKey(activeKey as TabKey);
|
|
||||||
if (activeKey === 'collected') {
|
|
||||||
initData({ is_collected: true });
|
|
||||||
} else {
|
|
||||||
initData();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = async (params = {}) => {
|
|
||||||
setSpinning(true);
|
|
||||||
const [error, data] = await apiInterceptors(getAppList(params));
|
|
||||||
if (error) {
|
|
||||||
setSpinning(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!data) return;
|
|
||||||
|
|
||||||
setApps(data.app_list || []);
|
|
||||||
setSpinning(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const renderAppList = (data: { isCollected: boolean }) => {
|
|
||||||
const isNull = data.isCollected ? apps.every((item) => !item.is_collected) : apps.length === 0;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{!data.isCollected && (
|
|
||||||
<Button onClick={handleCreate} type="primary" className="mb-4" icon={<PlusOutlined />}>
|
|
||||||
{t('create')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{!isNull ? (
|
|
||||||
<div className=" w-full flex flex-wrap pb-0 gap-4">
|
|
||||||
{apps.map((app, index) => {
|
|
||||||
return <AppCard handleEdit={handleEdit} key={index} app={app} updateApps={initData} isCollected={activeKey === 'collected'} />;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<MyEmpty />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const items: TabsProps['items'] = [
|
|
||||||
{
|
|
||||||
key: 'app',
|
|
||||||
label: t('App'),
|
|
||||||
children: renderAppList({ isCollected: false }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'collected',
|
|
||||||
label: t('collected'),
|
|
||||||
children: renderAppList({ isCollected: true }),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Spin spinning={spinning}>
|
|
||||||
<div className="h-screen w-full p-4 md:p-6 overflow-y-auto">
|
|
||||||
<Tabs defaultActiveKey="app" items={items} onChange={handleTabChange} />
|
|
||||||
{open && (
|
|
||||||
<AppModal app={modalType === 'edit' ? curApp : {}} type={modalType} updateApps={initData} open={open} handleCancel={handleCancel} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,233 +0,0 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
|
||||||
import { useAsyncEffect } from 'ahooks';
|
|
||||||
import { Badge, Button, Card, Drawer, Empty, Modal, message, Spin } from 'antd';
|
|
||||||
import FormDialog from '@/components/database/form-dialog';
|
|
||||||
import { apiInterceptors, getDbList, getDbSupportType, postDbDelete, postDbRefresh } from '@/client/api';
|
|
||||||
import { DeleteFilled, EditFilled, PlusOutlined, RedoOutlined } from '@ant-design/icons';
|
|
||||||
import { DBOption, DBType, DbListResponse, DbSupportTypeResponse } from '@/types/db';
|
|
||||||
import MuiLoading from '@/components/common/loading';
|
|
||||||
import { dbMapper } from '@/utils';
|
|
||||||
import GPTCard from '@/components/common/gpt-card';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
type DBItem = DbListResponse[0];
|
|
||||||
|
|
||||||
export function isFileDb(dbTypeList: DBOption[], dbType: DBType) {
|
|
||||||
return dbTypeList.find((item) => item.value === dbType)?.isFileDb;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Database() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const [dbList, setDbList] = useState<DbListResponse>([]);
|
|
||||||
const [dbSupportList, setDbSupportList] = useState<DbSupportTypeResponse>([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [modal, setModal] = useState<{ open: boolean; info?: DBItem; dbType?: DBType }>({ open: false });
|
|
||||||
const [draw, setDraw] = useState<{ open: boolean; dbList?: DbListResponse; name?: string; type?: DBType }>({ open: false });
|
|
||||||
const [refreshLoading, setRefreshLoading] = useState(false);
|
|
||||||
|
|
||||||
const getDbSupportList = async () => {
|
|
||||||
const [, data] = await apiInterceptors(getDbSupportType());
|
|
||||||
setDbSupportList(data ?? []);
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshDbList = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
const [, data] = await apiInterceptors(getDbList());
|
|
||||||
setDbList(data ?? []);
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dbTypeList = useMemo(() => {
|
|
||||||
const supportDbList = dbSupportList.map((item) => {
|
|
||||||
const { db_type, is_file_db } = item;
|
|
||||||
|
|
||||||
return { ...dbMapper[db_type], value: db_type, isFileDb: is_file_db };
|
|
||||||
}) as DBOption[];
|
|
||||||
const unSupportDbList = Object.keys(dbMapper)
|
|
||||||
.filter((item) => !supportDbList.some((db) => db.value === item))
|
|
||||||
.map((item) => ({ ...dbMapper[item], value: dbMapper[item].label, disabled: true })) as DBOption[];
|
|
||||||
return [...supportDbList, ...unSupportDbList];
|
|
||||||
}, [dbSupportList]);
|
|
||||||
|
|
||||||
const onModify = (item: DBItem) => {
|
|
||||||
setModal({ open: true, info: item });
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDelete = (item: DBItem) => {
|
|
||||||
Modal.confirm({
|
|
||||||
title: 'Tips',
|
|
||||||
content: `Do you Want to delete the ${item.db_name}?`,
|
|
||||||
onOk() {
|
|
||||||
return new Promise<void>(async (resolve, reject) => {
|
|
||||||
try {
|
|
||||||
const [err] = await apiInterceptors(postDbDelete(item.db_name));
|
|
||||||
if (err) {
|
|
||||||
message.error(err.message);
|
|
||||||
reject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.success('success');
|
|
||||||
refreshDbList();
|
|
||||||
resolve();
|
|
||||||
} catch (e: any) {
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onRefresh = async (item: DBItem) => {
|
|
||||||
setRefreshLoading(true);
|
|
||||||
const [, res] = await apiInterceptors(postDbRefresh({ db_name: item.db_name, db_type: item.db_type }));
|
|
||||||
if (res) message.success(t('refreshSuccess'));
|
|
||||||
setRefreshLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dbListByType = useMemo(() => {
|
|
||||||
const mapper = dbTypeList.reduce((acc, item) => {
|
|
||||||
acc[item.value] = dbList.filter((dbConn) => dbConn.db_type === item.value);
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<DBType, DbListResponse>);
|
|
||||||
return mapper;
|
|
||||||
}, [dbList, dbTypeList]);
|
|
||||||
|
|
||||||
useAsyncEffect(async () => {
|
|
||||||
await refreshDbList();
|
|
||||||
await getDbSupportList();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleDbTypeClick = (info: DBOption) => {
|
|
||||||
const dbItems = dbList.filter((item) => item.db_type === info.value);
|
|
||||||
setDraw({ open: true, dbList: dbItems, name: info.label, type: info.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative p-4 md:p-6 min-h-full overflow-y-auto">
|
|
||||||
<MuiLoading visible={loading} />
|
|
||||||
<div className="mb-4">
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
className="flex items-center"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
setModal({ open: true });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('create')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-wrap gap-2 md:gap-4">
|
|
||||||
{dbTypeList.map((item) => (
|
|
||||||
<Badge key={item.value} count={dbListByType[item.value].length} className="min-h-fit">
|
|
||||||
<GPTCard
|
|
||||||
className="h-full"
|
|
||||||
title={item.label}
|
|
||||||
desc={item.desc ?? ''}
|
|
||||||
disabled={item.disabled}
|
|
||||||
icon={item.icon}
|
|
||||||
onClick={() => {
|
|
||||||
if (item.disabled) return;
|
|
||||||
handleDbTypeClick(item);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<FormDialog
|
|
||||||
open={modal.open}
|
|
||||||
dbTypeList={dbTypeList}
|
|
||||||
choiceDBType={modal.dbType}
|
|
||||||
editValue={modal.info}
|
|
||||||
dbNames={dbList.map((item) => item.db_name)}
|
|
||||||
onSuccess={() => {
|
|
||||||
setModal({ open: false });
|
|
||||||
refreshDbList();
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setModal({ open: false });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Drawer
|
|
||||||
title={draw.name}
|
|
||||||
placement="right"
|
|
||||||
onClose={() => {
|
|
||||||
setDraw({ open: false });
|
|
||||||
}}
|
|
||||||
open={draw.open}
|
|
||||||
>
|
|
||||||
{draw.type && dbListByType[draw.type] && dbListByType[draw.type].length ? (
|
|
||||||
<Spin spinning={refreshLoading}>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
className="mb-4 flex items-center"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
setModal({ open: true, dbType: draw.type });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Create
|
|
||||||
</Button>
|
|
||||||
{dbListByType[draw.type].map((item) => (
|
|
||||||
<Card
|
|
||||||
key={item.db_name}
|
|
||||||
title={item.db_name}
|
|
||||||
extra={
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<RedoOutlined
|
|
||||||
style={{ color: 'gray' }}
|
|
||||||
onClick={() => {
|
|
||||||
onRefresh(item);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<EditFilled
|
|
||||||
className="mr-2"
|
|
||||||
style={{ color: '#1b7eff' }}
|
|
||||||
onClick={() => {
|
|
||||||
onModify(item);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<DeleteFilled
|
|
||||||
style={{ color: '#ff1b2e' }}
|
|
||||||
onClick={() => {
|
|
||||||
onDelete(item);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
className="mb-4"
|
|
||||||
>
|
|
||||||
{item.db_path ? (
|
|
||||||
<p>path: {item.db_path}</p>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<p>host: {item.db_host}</p>
|
|
||||||
<p>username: {item.db_user}</p>
|
|
||||||
<p>port: {item.db_port}</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<p>remark: {item.comment}</p>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</Spin>
|
|
||||||
) : (
|
|
||||||
<Empty image={Empty.PRESENTED_IMAGE_DEFAULT}>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
className="flex items-center mx-auto"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
setModal({ open: true, dbType: draw.type });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Create Now
|
|
||||||
</Button>
|
|
||||||
</Empty>
|
|
||||||
)}
|
|
||||||
</Drawer>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Database;
|
|
@@ -1,246 +0,0 @@
|
|||||||
import { apiInterceptors, getFlowById, importFlow } from '@/client/api';
|
|
||||||
import MuiLoading from '@/components/common/loading';
|
|
||||||
import AddNodes from '@/components/flow/add-nodes';
|
|
||||||
import ButtonEdge from '@/components/flow/button-edge';
|
|
||||||
import CanvasNode from '@/components/flow/canvas-node';
|
|
||||||
import { IFlowData, IFlowUpdateParam } from '@/types/flow';
|
|
||||||
import { checkFlowDataRequied, getUniqueNodeId, mapUnderlineToHump } from '@/utils/flow';
|
|
||||||
import { ExportOutlined, FrownOutlined, ImportOutlined, SaveOutlined } from '@ant-design/icons';
|
|
||||||
import { Divider, Space, Tooltip, message, notification } from 'antd';
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
|
||||||
import React, { DragEvent, useCallback, useEffect, useRef, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import ReactFlow, { Background, Connection, Controls, ReactFlowProvider, addEdge, useEdgesState, useNodesState, useReactFlow, Node } from 'reactflow';
|
|
||||||
import 'reactflow/dist/style.css';
|
|
||||||
import { SaveFlowModal, ExportFlowModal, ImportFlowModal } from '@/components/flow/canvas-modal';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
// Define your component props here
|
|
||||||
}
|
|
||||||
const nodeTypes = { customNode: CanvasNode };
|
|
||||||
const edgeTypes = { buttonedge: ButtonEdge };
|
|
||||||
|
|
||||||
const Canvas: React.FC<Props> = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const id = searchParams?.get('id') || '';
|
|
||||||
const reactFlow = useReactFlow();
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
|
||||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
|
||||||
const [flowInfo, setFlowInfo] = useState<IFlowUpdateParam>();
|
|
||||||
const [isSaveFlowModalOpen, setIsSaveFlowModalOpen] = useState(false);
|
|
||||||
const [isExportFlowModalOpen, setIsExportFlowModalOpen] = useState(false);
|
|
||||||
const [isImportModalOpen, setIsImportFlowModalOpen] = useState(false);
|
|
||||||
|
|
||||||
async function getFlowData() {
|
|
||||||
setLoading(true);
|
|
||||||
const [_, data] = await apiInterceptors(getFlowById(id));
|
|
||||||
if (data) {
|
|
||||||
const flowData = mapUnderlineToHump(data.flow_data);
|
|
||||||
setFlowInfo(data);
|
|
||||||
setNodes(flowData.nodes);
|
|
||||||
setEdges(flowData.edges);
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
id && getFlowData();
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
||||||
event.returnValue = message;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function onNodesClick(event: any, clickedNode: Node) {
|
|
||||||
reactFlow.setNodes((nds) =>
|
|
||||||
nds.map((node) => {
|
|
||||||
if (node.id === clickedNode.id) {
|
|
||||||
node.data = {
|
|
||||||
...node.data,
|
|
||||||
selected: true,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
node.data = {
|
|
||||||
...node.data,
|
|
||||||
selected: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onConnect(connection: Connection) {
|
|
||||||
const newEdge = {
|
|
||||||
...connection,
|
|
||||||
type: 'buttonedge',
|
|
||||||
id: `${connection.source}|${connection.target}`,
|
|
||||||
};
|
|
||||||
setEdges((eds) => addEdge(newEdge, eds));
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDrop = useCallback(
|
|
||||||
(event: DragEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
const reactFlowBounds = reactFlowWrapper.current!.getBoundingClientRect();
|
|
||||||
let nodeStr = event.dataTransfer.getData('application/reactflow');
|
|
||||||
if (!nodeStr || typeof nodeStr === 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const nodeData = JSON.parse(nodeStr);
|
|
||||||
const position = reactFlow.screenToFlowPosition({
|
|
||||||
x: event.clientX - reactFlowBounds.left,
|
|
||||||
y: event.clientY - reactFlowBounds.top,
|
|
||||||
});
|
|
||||||
const nodeId = getUniqueNodeId(nodeData, reactFlow.getNodes());
|
|
||||||
nodeData.id = nodeId;
|
|
||||||
const newNode = {
|
|
||||||
id: nodeId,
|
|
||||||
position,
|
|
||||||
type: 'customNode',
|
|
||||||
data: nodeData,
|
|
||||||
};
|
|
||||||
setNodes((nds) =>
|
|
||||||
nds.concat(newNode).map((node) => {
|
|
||||||
if (node.id === newNode.id) {
|
|
||||||
node.data = {
|
|
||||||
...node.data,
|
|
||||||
selected: true,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
node.data = {
|
|
||||||
...node.data,
|
|
||||||
selected: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[reactFlow],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onDragOver = useCallback((event: DragEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
event.dataTransfer.dropEffect = 'move';
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function onSave() {
|
|
||||||
const flowData = reactFlow.toObject() as IFlowData;
|
|
||||||
const [check, node, message] = checkFlowDataRequied(flowData);
|
|
||||||
if (!check && message) {
|
|
||||||
setNodes((nds) =>
|
|
||||||
nds.map((item) => {
|
|
||||||
if (item.id === node?.id) {
|
|
||||||
item.data = {
|
|
||||||
...item.data,
|
|
||||||
invalid: true,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
item.data = {
|
|
||||||
...item.data,
|
|
||||||
invalid: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
return notification.error({ message: 'Error', description: message, icon: <FrownOutlined className="text-red-600" /> });
|
|
||||||
}
|
|
||||||
setIsSaveFlowModalOpen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onExport() {
|
|
||||||
setIsExportFlowModalOpen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onImport() {
|
|
||||||
setIsImportFlowModalOpen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MuiLoading visible={loading} />
|
|
||||||
<Space className="my-2 mx-4 flex flex-row justify-end">
|
|
||||||
{[
|
|
||||||
{ title: 'import', icon: <ImportOutlined className="block text-xl" onClick={onImport} /> },
|
|
||||||
{ title: 'export', icon: <ExportOutlined className="block text-xl" onClick={onExport} /> },
|
|
||||||
{ title: 'save', icon: <SaveOutlined className="block text-xl" onClick={onSave} /> },
|
|
||||||
].map(({ title, icon }) => (
|
|
||||||
<Tooltip
|
|
||||||
key={title}
|
|
||||||
title={title}
|
|
||||||
className="w-8 h-8 rounded-md bg-stone-300 dark:bg-zinc-700 dark:text-zinc-200 hover:text-blue-500 dark:hover:text-zinc-100"
|
|
||||||
>
|
|
||||||
{icon}
|
|
||||||
</Tooltip>
|
|
||||||
))}
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
<Divider className="mt-0 mb-0" />
|
|
||||||
<div className="h-[calc(100vh-60px)] w-full" ref={reactFlowWrapper}>
|
|
||||||
<ReactFlow
|
|
||||||
nodes={nodes}
|
|
||||||
edges={edges}
|
|
||||||
nodeTypes={nodeTypes}
|
|
||||||
edgeTypes={edgeTypes}
|
|
||||||
onNodesChange={onNodesChange}
|
|
||||||
onEdgesChange={onEdgesChange}
|
|
||||||
onNodeClick={onNodesClick}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onDrop={onDrop}
|
|
||||||
onDragOver={onDragOver}
|
|
||||||
minZoom={0.1}
|
|
||||||
fitView
|
|
||||||
deleteKeyCode={['Backspace', 'Delete']}
|
|
||||||
>
|
|
||||||
<Controls className="flex flex-row items-center" position="bottom-center" />
|
|
||||||
<Background color="#aaa" gap={16} />
|
|
||||||
<AddNodes />
|
|
||||||
</ReactFlow>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SaveFlowModal
|
|
||||||
reactFlow={reactFlow}
|
|
||||||
flowInfo={flowInfo}
|
|
||||||
isSaveFlowModalOpen={isSaveFlowModalOpen}
|
|
||||||
setIsSaveFlowModalOpen={setIsSaveFlowModalOpen}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ExportFlowModal
|
|
||||||
reactFlow={reactFlow}
|
|
||||||
flowInfo={flowInfo}
|
|
||||||
isExportFlowModalOpen={isExportFlowModalOpen}
|
|
||||||
setIsExportFlowModalOpen={setIsExportFlowModalOpen}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImportFlowModal
|
|
||||||
setNodes={setNodes}
|
|
||||||
setEdges={setEdges}
|
|
||||||
isImportModalOpen={isImportModalOpen}
|
|
||||||
setIsImportFlowModalOpen={setIsImportFlowModalOpen}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function CanvasWrapper() {
|
|
||||||
return (
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<Canvas />
|
|
||||||
</ReactFlowProvider>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -1,143 +0,0 @@
|
|||||||
import { apiInterceptors, getFlows, addFlow } from '@/client/api';
|
|
||||||
import MyEmpty from '@/components/common/MyEmpty';
|
|
||||||
import MuiLoading from '@/components/common/loading';
|
|
||||||
import FlowCard from '@/components/flow/flow-card';
|
|
||||||
import { IFlow, IFlowUpdateParam } from '@/types/flow';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Checkbox, Form, Input, Modal, message, Pagination, PaginationProps } from 'antd';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useEffect, useState, useRef } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
function Flow() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const [showModal, setShowModal] = useState(false);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [flowList, setFlowList] = useState<Array<IFlow>>([]);
|
|
||||||
const [deploy, setDeploy] = useState(false);
|
|
||||||
const [page, setPage] = useState<number>(1);
|
|
||||||
const [pageSize, setPageSize] = useState<number>(10);
|
|
||||||
const [total, setTotal] = useState<number>(0);
|
|
||||||
|
|
||||||
const [messageApi, contextHolder] = message.useMessage();
|
|
||||||
const [form] = Form.useForm<Pick<IFlow, 'label' | 'name'>>();
|
|
||||||
const copyFlowTemp = useRef<IFlow>();
|
|
||||||
|
|
||||||
async function getFlowList() {
|
|
||||||
setLoading(true);
|
|
||||||
const [_, data] = await apiInterceptors(getFlows(page, pageSize));
|
|
||||||
setTotal(data?.total_count ?? 0);
|
|
||||||
setLoading(false);
|
|
||||||
setFlowList(data?.items ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getFlowList();
|
|
||||||
}, [page, pageSize]);
|
|
||||||
|
|
||||||
function updateFlowList(uid: string) {
|
|
||||||
setFlowList((flows) => flows.filter((flow) => flow.uid !== uid));
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCopy = (flow: IFlow) => {
|
|
||||||
copyFlowTemp.current = flow;
|
|
||||||
form.setFieldValue('label', `${flow.label} Copy`);
|
|
||||||
form.setFieldValue('name', `${flow.name}_copy`);
|
|
||||||
setDeploy(false);
|
|
||||||
setShowModal(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFinish = async (val: { name: string; label: string }) => {
|
|
||||||
if (!copyFlowTemp.current) return;
|
|
||||||
const { source, uid, dag_id, gmt_created, gmt_modified, state, ...params } = copyFlowTemp.current;
|
|
||||||
const data: IFlowUpdateParam = {
|
|
||||||
...params,
|
|
||||||
editable: true,
|
|
||||||
state: deploy ? 'deployed' : 'developing',
|
|
||||||
...val,
|
|
||||||
};
|
|
||||||
const [err] = await apiInterceptors(addFlow(data));
|
|
||||||
if (!err) {
|
|
||||||
messageApi.success(t('save_flow_success'));
|
|
||||||
setShowModal(false);
|
|
||||||
getFlowList();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageChange: PaginationProps['onChange'] = (page: number, pageSize: number) => {
|
|
||||||
setPage(page);
|
|
||||||
setPageSize(pageSize);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col justify-between relative p-4 md:p-6 min-h-full overflow-y-auto">
|
|
||||||
{contextHolder}
|
|
||||||
<MuiLoading visible={loading} />
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<Link href="/flow/canvas">
|
|
||||||
<Button type="primary" className="flex items-center" icon={<PlusOutlined />}>
|
|
||||||
New AWEL Flow
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col justify-between flex-1">
|
|
||||||
<div className="flex flex-wrap gap-2 md:gap-4 justify-start items-stretch">
|
|
||||||
{flowList.map((flow) => (
|
|
||||||
<FlowCard key={flow.uid} flow={flow} deleteCallback={updateFlowList} onCopy={handleCopy} />
|
|
||||||
))}
|
|
||||||
{flowList.length === 0 && <MyEmpty description="No flow found" />}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end mt-6">
|
|
||||||
<Pagination
|
|
||||||
showQuickJumper
|
|
||||||
showSizeChanger
|
|
||||||
defaultPageSize={10}
|
|
||||||
defaultCurrent={1}
|
|
||||||
current={page}
|
|
||||||
total={total}
|
|
||||||
showTotal={(total) => `Total ${total} items`}
|
|
||||||
onChange={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
open={showModal}
|
|
||||||
title="Copy AWEL Flow"
|
|
||||||
onCancel={() => {
|
|
||||||
setShowModal(false);
|
|
||||||
}}
|
|
||||||
footer={false}
|
|
||||||
>
|
|
||||||
<Form form={form} onFinish={onFinish} className="mt-6">
|
|
||||||
<Form.Item name="name" label="Name" rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="label" label="Label" rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label="Deploy">
|
|
||||||
<Checkbox
|
|
||||||
value={deploy}
|
|
||||||
onChange={(e) => {
|
|
||||||
const val = e.target.checked;
|
|
||||||
setDeploy(val);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Button type="primary" htmlType="submit">
|
|
||||||
{t('Submit')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Flow;
|
|
@@ -1,106 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { Breadcrumb, Card, Empty, Pagination, Spin } from 'antd';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { apiInterceptors, getChunkList } from '@/client/api';
|
|
||||||
import DocIcon from '@/components/knowledge/doc-icon';
|
|
||||||
|
|
||||||
const DEDAULT_PAGE_SIZE = 10;
|
|
||||||
|
|
||||||
function ChunkList() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [chunkList, setChunkList] = useState<any>([]);
|
|
||||||
const [total, setTotal] = useState<number>(0);
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const {
|
|
||||||
query: { id, spaceName },
|
|
||||||
} = useRouter();
|
|
||||||
|
|
||||||
const fetchChunks = async () => {
|
|
||||||
const [_, data] = await apiInterceptors(
|
|
||||||
getChunkList(spaceName as string, {
|
|
||||||
document_id: id as string,
|
|
||||||
page: 1,
|
|
||||||
page_size: DEDAULT_PAGE_SIZE,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
setChunkList(data?.data);
|
|
||||||
setTotal(data?.total!);
|
|
||||||
};
|
|
||||||
|
|
||||||
const loaderMore = async (page: number, page_size: number) => {
|
|
||||||
setLoading(true);
|
|
||||||
const [_, data] = await apiInterceptors(
|
|
||||||
getChunkList(spaceName as string, {
|
|
||||||
document_id: id as string,
|
|
||||||
page,
|
|
||||||
page_size,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
setChunkList(data?.data || []);
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
spaceName && id && fetchChunks();
|
|
||||||
}, [id, spaceName]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-full overflow-y-scroll relative px-2">
|
|
||||||
<Breadcrumb
|
|
||||||
className="m-6"
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
title: 'Knowledge',
|
|
||||||
onClick() {
|
|
||||||
router.back();
|
|
||||||
},
|
|
||||||
path: '/knowledge',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: spaceName,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<Spin spinning={loading}>
|
|
||||||
<div className="flex justify-center flex-col">
|
|
||||||
{chunkList?.length > 0 ? (
|
|
||||||
chunkList?.map((chunk: any) => {
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
key={chunk.id}
|
|
||||||
className="mt-2"
|
|
||||||
title={
|
|
||||||
<>
|
|
||||||
<DocIcon type={chunk.doc_type} />
|
|
||||||
<span>{chunk.doc_name}</span>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<p className="font-semibold">{t('Content')}:</p>
|
|
||||||
<p>{chunk?.content}</p>
|
|
||||||
<p className="font-semibold">{t('Meta_Data')}: </p>
|
|
||||||
<p>{chunk?.meta_info}</p>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<Empty image={Empty.PRESENTED_IMAGE_DEFAULT}></Empty>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
<Pagination
|
|
||||||
className="mx-2 my-4 float-right right-6 bottom-4"
|
|
||||||
defaultCurrent={1}
|
|
||||||
defaultPageSize={DEDAULT_PAGE_SIZE}
|
|
||||||
total={total}
|
|
||||||
showTotal={(total) => `Total ${total} items`}
|
|
||||||
onChange={loaderMore}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ChunkList;
|
|
@@ -1,137 +0,0 @@
|
|||||||
import React, { useEffect,useRef, useState } from 'react';
|
|
||||||
import cytoscape from 'cytoscape';
|
|
||||||
import euler from 'cytoscape-euler';
|
|
||||||
import { Button } from 'antd';
|
|
||||||
import { RollbackOutlined } from '@ant-design/icons';
|
|
||||||
cytoscape.use(euler)
|
|
||||||
import { apiInterceptors,getGraphVis } from '@/client/api';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
|
|
||||||
const LAYOUTCONFIG = {
|
|
||||||
name: 'euler',
|
|
||||||
springLength: 340,
|
|
||||||
fit: false,
|
|
||||||
springCoeff: 0.0008,
|
|
||||||
mass: 20,
|
|
||||||
dragCoeff: 1,
|
|
||||||
gravity: -20,
|
|
||||||
pull: 0.009,
|
|
||||||
randomize: false,
|
|
||||||
padding: 0,
|
|
||||||
maxIterations: 1000,
|
|
||||||
maxSimulationTime: 4000,
|
|
||||||
}
|
|
||||||
|
|
||||||
function GraphVis() {
|
|
||||||
const myRef = useRef<HTMLDivElement>(null);
|
|
||||||
const LIMIT = 500
|
|
||||||
const router = useRouter();
|
|
||||||
const fetchGraphVis = async () => {
|
|
||||||
const [_, data] = await apiInterceptors(getGraphVis(spaceName as string,{limit:LIMIT}))
|
|
||||||
if(myRef.current && data){
|
|
||||||
let processedData = processResult(data)
|
|
||||||
renderGraphVis(processedData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const processResult = (data:{nodes:Array<any>,edges:Array<any>}) => {
|
|
||||||
let nodes:any[] = []
|
|
||||||
let edges:any[] = []
|
|
||||||
data.nodes.forEach((node:any)=>{
|
|
||||||
let n = {
|
|
||||||
data:{
|
|
||||||
id:node.vid,
|
|
||||||
displayName:node.vid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nodes.push(n)
|
|
||||||
})
|
|
||||||
data.edges.forEach((edge:any)=>{
|
|
||||||
let e = {
|
|
||||||
data:{
|
|
||||||
id:edge.src+'_'+edge.dst+'_'+edge.label,
|
|
||||||
source:edge.src,
|
|
||||||
target:edge.dst,
|
|
||||||
displayName:edge.label
|
|
||||||
}
|
|
||||||
}
|
|
||||||
edges.push(e)
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
nodes,
|
|
||||||
edges
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const renderGraphVis = (data:any)=> {
|
|
||||||
let dom = myRef.current as HTMLDivElement
|
|
||||||
let cy = cytoscape(
|
|
||||||
{
|
|
||||||
container:myRef.current,
|
|
||||||
elements:data,
|
|
||||||
zoom:0.3,
|
|
||||||
pixelRatio: 'auto',
|
|
||||||
style:[
|
|
||||||
{
|
|
||||||
selector: 'node',
|
|
||||||
style: {
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
color: '#fff',
|
|
||||||
'text-outline-color': '#37D4BE',
|
|
||||||
'text-outline-width': 2,
|
|
||||||
'text-valign': 'center',
|
|
||||||
'text-halign': 'center',
|
|
||||||
'background-color': '#37D4BE',
|
|
||||||
'label': 'data(displayName)'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: 'edge',
|
|
||||||
style: {
|
|
||||||
'width': 1,
|
|
||||||
color: '#fff',
|
|
||||||
'label': 'data(displayName)',
|
|
||||||
'line-color': '#66ADFF',
|
|
||||||
'font-size': 14,
|
|
||||||
'target-arrow-shape': 'vee',
|
|
||||||
'control-point-step-size': 40,
|
|
||||||
'curve-style': 'bezier',
|
|
||||||
'text-background-opacity': 1,
|
|
||||||
'text-background-color': '#66ADFF',
|
|
||||||
'target-arrow-color': '#66ADFF',
|
|
||||||
'text-background-shape': 'roundrectangle',
|
|
||||||
'text-border-color': '#000',
|
|
||||||
'text-wrap': 'wrap',
|
|
||||||
'text-valign': 'top',
|
|
||||||
'text-halign': 'center',
|
|
||||||
'text-background-padding':'5',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
cy.layout(LAYOUTCONFIG).run()
|
|
||||||
cy.pan({
|
|
||||||
x: dom.clientWidth / 2,
|
|
||||||
y: dom.clientHeight / 2
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const back = ()=>{
|
|
||||||
router.push(`/knowledge`);
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
query: { spaceName },
|
|
||||||
} = useRouter();
|
|
||||||
useEffect(()=>{
|
|
||||||
spaceName && fetchGraphVis()
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<div className="p-4 h-full overflow-y-scroll relative px-2">
|
|
||||||
<div>
|
|
||||||
<Button onClick={back} icon={<RollbackOutlined />}> Back </Button>
|
|
||||||
</div>
|
|
||||||
<div className='h-full w-full' ref={myRef}></div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GraphVis;
|
|
@@ -1,123 +0,0 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Modal, Steps } from 'antd';
|
|
||||||
import SpaceCard from '@/components/knowledge/space-card';
|
|
||||||
import { File, ISpace, StepChangeParams, IStorage, SpaceConfig } from '@/types/knowledge';
|
|
||||||
import { apiInterceptors, getSpaceList, getSpaceConfig } from '@/client/api';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import DocUploadForm from '@/components/knowledge/doc-upload-form';
|
|
||||||
import SpaceForm from '@/components/knowledge/space-form';
|
|
||||||
import DocTypeForm from '@/components/knowledge/doc-type-form';
|
|
||||||
import Segmentation from '@/components/knowledge/segmentation';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
const Knowledge = () => {
|
|
||||||
const [spaceList, setSpaceList] = useState<Array<ISpace> | null>([]);
|
|
||||||
const [isAddShow, setIsAddShow] = useState<boolean>(false);
|
|
||||||
const [activeStep, setActiveStep] = useState<number>(0);
|
|
||||||
const [spaceName, setSpaceName] = useState<string>('');
|
|
||||||
const [files, setFiles] = useState<Array<File>>([]);
|
|
||||||
const [docType, setDocType] = useState<string>('');
|
|
||||||
const [spaceConfig, setSpaceConfig] = useState<IStorage | null>(null);
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const addKnowledgeSteps = [
|
|
||||||
{ title: t('Knowledge_Space_Config') },
|
|
||||||
{ title: t('Choose_a_Datasource_type') },
|
|
||||||
{ title: t('Upload') },
|
|
||||||
{ title: t('Segmentation') },
|
|
||||||
];
|
|
||||||
|
|
||||||
async function getSpaces() {
|
|
||||||
const [_, data] = await apiInterceptors(getSpaceList());
|
|
||||||
setSpaceList(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSpaceConfigs() {
|
|
||||||
const [_, data] = await apiInterceptors(getSpaceConfig());
|
|
||||||
if (!data) return null;
|
|
||||||
setSpaceConfig(data.storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getSpaces();
|
|
||||||
getSpaceConfigs();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleStepChange = ({ label, spaceName, docType = '', files, pace = 1 }: StepChangeParams) => {
|
|
||||||
if (label === 'finish') {
|
|
||||||
setIsAddShow(false);
|
|
||||||
getSpaces();
|
|
||||||
setSpaceName('');
|
|
||||||
setDocType('');
|
|
||||||
} else if (label === 'forward') {
|
|
||||||
activeStep === 0 && getSpaces();
|
|
||||||
setActiveStep((step) => step + pace);
|
|
||||||
} else {
|
|
||||||
setActiveStep((step) => step - pace);
|
|
||||||
}
|
|
||||||
files && setFiles(files);
|
|
||||||
spaceName && setSpaceName(spaceName);
|
|
||||||
docType && setDocType(docType);
|
|
||||||
};
|
|
||||||
|
|
||||||
function onAddDoc(spaceName: string) {
|
|
||||||
const space = spaceList?.find((item) => item?.name === spaceName);
|
|
||||||
setSpaceName(spaceName);
|
|
||||||
setActiveStep(space?.domain_type === 'FinancialReport' ? 2 : 1);
|
|
||||||
setIsAddShow(true);
|
|
||||||
if (space?.domain_type === 'FinancialReport') {
|
|
||||||
setDocType('DOCUMENT');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-[#FAFAFA] dark:bg-transparent w-full h-full">
|
|
||||||
<div className="page-body p-4 md:p-6 h-full overflow-auto">
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
className="flex items-center"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
setIsAddShow(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Create
|
|
||||||
</Button>
|
|
||||||
<div className="flex flex-wrap mt-4 gap-2 md:gap-4">
|
|
||||||
{spaceList?.map((space: ISpace) => (
|
|
||||||
<SpaceCard key={space.id} space={space} onAddDoc={onAddDoc} getSpaces={getSpaces} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Modal
|
|
||||||
title="Add Knowledge"
|
|
||||||
centered
|
|
||||||
open={isAddShow}
|
|
||||||
destroyOnClose={true}
|
|
||||||
onCancel={() => {
|
|
||||||
setIsAddShow(false);
|
|
||||||
}}
|
|
||||||
width={1000}
|
|
||||||
afterClose={() => {
|
|
||||||
setActiveStep(0);
|
|
||||||
getSpaces();
|
|
||||||
}}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<Steps current={activeStep} items={addKnowledgeSteps} />
|
|
||||||
{activeStep === 0 && <SpaceForm handleStepChange={handleStepChange} spaceConfig={spaceConfig} />}
|
|
||||||
{activeStep === 1 && <DocTypeForm handleStepChange={handleStepChange} />}
|
|
||||||
<DocUploadForm
|
|
||||||
className={classNames({ hidden: activeStep !== 2 })}
|
|
||||||
spaceName={spaceName}
|
|
||||||
docType={docType}
|
|
||||||
handleStepChange={handleStepChange}
|
|
||||||
/>
|
|
||||||
{activeStep === 3 && <Segmentation spaceName={spaceName} docType={docType} uploadFiles={files} handleStepChange={handleStepChange} />}
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Knowledge;
|
|
@@ -1,62 +0,0 @@
|
|||||||
import { apiInterceptors, getModelList } from '@/client/api';
|
|
||||||
import ModelCard from '@/components/model/model-card';
|
|
||||||
import ModelForm from '@/components/model/model-form';
|
|
||||||
import { IModelData } from '@/types/model';
|
|
||||||
import { Button, Modal } from 'antd';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
function Models() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [models, setModels] = useState<Array<IModelData>>([]);
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
|
|
||||||
async function getModels() {
|
|
||||||
const [, res] = await apiInterceptors(getModelList());
|
|
||||||
setModels(res ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getModels();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-4 md:p-6 overflow-y-auto">
|
|
||||||
<Button
|
|
||||||
className="mb-4"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
setIsModalOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('create_model')}
|
|
||||||
</Button>
|
|
||||||
<div className="flex flex-wrap gap-2 md:gap-4">
|
|
||||||
{models.map((item) => (
|
|
||||||
<ModelCard info={item} key={item.model_name} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<Modal
|
|
||||||
width={800}
|
|
||||||
open={isModalOpen}
|
|
||||||
title={t('create_model')}
|
|
||||||
onCancel={() => {
|
|
||||||
setIsModalOpen(false);
|
|
||||||
}}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<ModelForm
|
|
||||||
onCancel={() => {
|
|
||||||
setIsModalOpen(false);
|
|
||||||
}}
|
|
||||||
onSuccess={() => {
|
|
||||||
setIsModalOpen(false);
|
|
||||||
getModels();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Models;
|
|
@@ -1,181 +0,0 @@
|
|||||||
import { useState, useEffect, useRef, Ref } from 'react';
|
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
|
||||||
import type { FormInstance, MenuProps } from 'antd';
|
|
||||||
import { Menu, Table, Button, Tooltip, Modal } from 'antd';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import GroupsIcon from '@mui/icons-material/Groups';
|
|
||||||
import PersonIcon from '@mui/icons-material/Person';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { addPrompt, apiInterceptors, getPromptList, postScenes, updatePrompt } from '@/client/api';
|
|
||||||
import { IPrompt } from '@/types/prompt';
|
|
||||||
import PromptForm from '@/components/prompt/prompt-form';
|
|
||||||
import { TFunction } from 'i18next';
|
|
||||||
|
|
||||||
const getItems = (t: TFunction) => [
|
|
||||||
{
|
|
||||||
label: t('Public') + ' Prompts',
|
|
||||||
key: 'common',
|
|
||||||
icon: <GroupsIcon />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('Private') + ' Prompts',
|
|
||||||
key: 'private',
|
|
||||||
icon: <PersonIcon />,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const getColumns = (t: TFunction, handleEdit: (prompt: IPrompt) => void): ColumnsType<IPrompt> => [
|
|
||||||
{
|
|
||||||
title: t('Prompt_Info_Name'),
|
|
||||||
dataIndex: 'prompt_name',
|
|
||||||
key: 'prompt_name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('Prompt_Info_Scene'),
|
|
||||||
dataIndex: 'chat_scene',
|
|
||||||
key: 'chat_scene',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('Prompt_Info_Sub_Scene'),
|
|
||||||
dataIndex: 'sub_chat_scene',
|
|
||||||
key: 'sub_chat_scene',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('Prompt_Info_Content'),
|
|
||||||
dataIndex: 'content',
|
|
||||||
key: 'content',
|
|
||||||
render: (content) => (
|
|
||||||
<Tooltip placement="topLeft" title={content}>
|
|
||||||
{content}
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('Operation'),
|
|
||||||
dataIndex: 'operate',
|
|
||||||
key: 'operate',
|
|
||||||
render: (_, record) => (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
handleEdit(record);
|
|
||||||
}}
|
|
||||||
type="primary"
|
|
||||||
>
|
|
||||||
{t('Edit')}
|
|
||||||
</Button>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
type FormType = Ref<FormInstance<any>> | undefined;
|
|
||||||
|
|
||||||
const Prompt = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const [promptType, setPromptType] = useState<string>('common');
|
|
||||||
const [promptList, setPromptList] = useState<Array<IPrompt>>();
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [prompt, setPrompt] = useState<IPrompt>();
|
|
||||||
const [showModal, setShowModal] = useState<boolean>(false);
|
|
||||||
const [scenes, setScenes] = useState<Array<Record<string, string>>>();
|
|
||||||
const formRef = useRef<FormType>();
|
|
||||||
|
|
||||||
const getPrompts = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
const body = {
|
|
||||||
prompt_type: promptType,
|
|
||||||
current: 1,
|
|
||||||
pageSize: 1000,
|
|
||||||
hideOnSinglePage: true,
|
|
||||||
showQuickJumper: true,
|
|
||||||
};
|
|
||||||
const [_, data] = await apiInterceptors(getPromptList(body));
|
|
||||||
setPromptList(data!);
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getScenes = async () => {
|
|
||||||
const [, res] = await apiInterceptors(postScenes());
|
|
||||||
setScenes(res?.map((scene) => ({ value: scene.chat_scene, label: scene.scene_name })));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFinish = async (newPrompt: IPrompt) => {
|
|
||||||
if (prompt) {
|
|
||||||
await apiInterceptors(updatePrompt({ ...newPrompt, prompt_type: promptType }));
|
|
||||||
} else {
|
|
||||||
await apiInterceptors(addPrompt({ ...newPrompt, prompt_type: promptType }));
|
|
||||||
}
|
|
||||||
getPrompts();
|
|
||||||
handleClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEditBtn = (prompt: IPrompt) => {
|
|
||||||
setPrompt(prompt);
|
|
||||||
setShowModal(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddBtn = () => {
|
|
||||||
setShowModal(true);
|
|
||||||
setPrompt(undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setShowModal(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMenuChange: MenuProps['onClick'] = (e) => {
|
|
||||||
const type = e.key;
|
|
||||||
setPromptType(type);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getPrompts();
|
|
||||||
}, [promptType]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getScenes();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Menu onClick={handleMenuChange} selectedKeys={[promptType]} mode="horizontal" items={getItems(t)} />
|
|
||||||
<div className="px-6 py-4">
|
|
||||||
<div className="flex flex-row-reverse mb-4">
|
|
||||||
<Button className="flex items-center" onClick={handleAddBtn}>
|
|
||||||
<PlusOutlined />
|
|
||||||
{t('Add')} Prompts
|
|
||||||
</Button>
|
|
||||||
{promptType === 'common' && (
|
|
||||||
<Button className="mr-2 flex items-center" disabled>
|
|
||||||
<PlusOutlined />
|
|
||||||
{t('Add')} Prompts {t('template')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Table
|
|
||||||
columns={getColumns(t, handleEditBtn)}
|
|
||||||
dataSource={promptList}
|
|
||||||
loading={loading}
|
|
||||||
rowKey={(record) => record.prompt_name}
|
|
||||||
scroll={{ y: 600 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Modal
|
|
||||||
title={`${prompt ? t('Edit') : t('Add')} Prompts`}
|
|
||||||
destroyOnClose
|
|
||||||
open={showModal}
|
|
||||||
onCancel={handleClose}
|
|
||||||
cancelText={t('cancel')}
|
|
||||||
okText={t('submit')}
|
|
||||||
onOk={() => {
|
|
||||||
// @ts-ignore
|
|
||||||
formRef.current?.submit();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PromptForm scenes={scenes} ref={formRef as FormType} prompt={prompt} onFinish={onFinish} />
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Prompt;
|
|
Reference in New Issue
Block a user