feat: Remove unused code for agent, models, and knowledge chunk pages (#1873)

This commit is contained in:
Dreammy23
2024-08-23 11:22:25 +08:00
committed by GitHub
10 changed files with 0 additions and 1379 deletions

View File

@@ -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;

View File

@@ -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>
</>
);
}

View File

@@ -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;

View File

@@ -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>
);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;