Skip to content

Commit

Permalink
feat: magent-ui support knowledge curd operation
Browse files Browse the repository at this point in the history
  • Loading branch information
october-rain committed Aug 29, 2024
1 parent af2d2dd commit 5500303
Show file tree
Hide file tree
Showing 10 changed files with 378 additions and 14 deletions.
23 changes: 21 additions & 2 deletions packages/magent-ui/src/magent_ui/routers/knowledge/router.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import List
from fastapi import APIRouter
from typing import Annotated, List
from fastapi import APIRouter, Body, File, UploadFile
from agentuniverse_product.service.model.knowledge_dto import KnowledgeDTO
from agentuniverse_product.service.knowledge_service.knowledge_service import KnowledgeService

Expand All @@ -10,3 +10,22 @@
@router.get("/knowledge", response_model=List[KnowledgeDTO])
async def get_knowledge():
return KnowledgeService.get_knowledge_list()

@router.post("/knowledge", response_model=str)
async def create_knowledge(knowledge: KnowledgeDTO):
return KnowledgeService.create_knowledge(knowledge)

@router.put("/knowledge/{knowledge_id}", response_model=str)
async def update_knowledge(knowledge_id: str, knowledge: KnowledgeDTO):
knowledge.id = knowledge_id
return KnowledgeService.update_knowledge(knowledge)

@router.delete("/knowledge/{knowledge_id}", response_model=bool)
async def delete_knowledge(knowledge_id: str):
return KnowledgeService.delete_knowledge(knowledge_id)

@router.post("/knowledge/upload")
async def upload_knowledge_file(knowledge_id: Annotated[str, Body()], file: UploadFile = File(...)):
print('here')
return KnowledgeService.upload_knowledge_file(knowledge_id, file)

4 changes: 4 additions & 0 deletions web-apps/ui/config/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export default [
path: '/agent/:agentId/flow',
slot: 'magent-agent-flow-dev-slot',
},
{
path: '/portal/knowledge/:knowledgeId/upload',
slot: 'magent-knowledge-upload-slot',
},
],
},
];
21 changes: 21 additions & 0 deletions web-apps/ui/src/modules/knowledge/knowledge-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@ export class KnowledgeManager {
return defaultValue;
};

createKnowledge = async (nickname: string, description?: string): Promise<string> => {
const res = await this.axios.post<string>(`/api/v1/knowledge`, {
nickname: nickname,
description: description,
});
return res.data;
};

updateKnowledge = async (option: KnowledgeModelOption): Promise<string> => {
const res = await this.axios.put<string>(`/api/v1/knowledge/${option.id}`, {
nickname: option.nickname,
description: option.description,
});
return res.data;
};

deleteKnowledge = async (knowledge_id: string): Promise<boolean> => {
const res = await this.axios.delete<string>(`/api/v1/knowledge/${knowledge_id}`);
return Boolean(res.data);
};

getOrCreate = (option: KnowledgeModelOption): KnowledgeModel => {
const exist = this.cache.get(option.id);
if (exist) {
Expand Down
10 changes: 10 additions & 0 deletions web-apps/ui/src/views/knowledge/create-modal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ModalContribution, singleton } from '@difizen/mana-app';

import { KnowledgeModal } from './modal.js';

@singleton({ contrib: [ModalContribution] })
export class KnowledgeModalContribution implements ModalContribution {
registerModal() {
return KnowledgeModal;
}
}
143 changes: 143 additions & 0 deletions web-apps/ui/src/views/knowledge/create-modal/modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import type { ModalItem, ModalItemProps } from '@difizen/mana-app';
import { useInject, useMount } from '@difizen/mana-app';
import { Form, Input, message, Modal } from 'antd';
import React, { useCallback, useMemo, useState } from 'react';
import { history } from 'umi';

import { KnowledgeManager } from '../../../modules/knowledge/knowledge-manager.js';
import { KnowledgeSpace } from '../../../modules/knowledge/knowledge-space.js';
import { KnowledgeModalId } from '../protocol.js';
import { KnowledgeView } from '../view.js';

export const KnowledgeModalComponent = (
props: ModalItemProps<{ type: 'create' | 'edit'; knowledge_id?: string }>,
) => {
const knowledgeSpace = useInject(KnowledgeSpace);
const knowledgeManager = useInject(KnowledgeManager);
const instance = useInject(KnowledgeView);

const { visible, close, data } = props;

const [form] = Form.useForm();

const [nameValue, setName] = useState<string | undefined>(undefined);
const [idValue, setId] = useState<string | undefined>(undefined);

useMount(() => {
knowledgeSpace.update();
});

const strategiesValues = useMemo(
() => ({
create: {
title: '创建知识库',
okText: '创建',
},
edit: {
title: '编辑知识库',
okText: '确定',
},
}),
[],
);

const strategiesMethod = useMemo(
() => ({
create: async () => {
const formValues = form.getFieldsValue();
const id = await knowledgeManager.createKnowledge(
formValues.nickname,
formValues.description,
);
if (id) {
setId(id);
message.success('创建成功');
history.push(`/portal/knowledge/${id}/upload`);
}
close();
},
edit: async () => {
if (!data?.knowledge_id) {
return;
}
const formValues = form.getFieldsValue();
const id = await knowledgeManager.updateKnowledge({
id: data.knowledge_id,
nickname: formValues.nickname,
description: formValues.description,
});
if (id) {
setId(id);
message.success('更新成功');
await instance.update();
}
close();
},
}),
[close, data, form, instance, knowledgeManager],
);

const submit = useCallback(async () => {
const method = strategiesMethod[data?.type || 'create'];
if (method) {
await method();
} else {
console.error('Invalid type');
}
}, [data, strategiesMethod]);

return (
<Modal
className="magent-knowledge-create-modal-form"
open={visible}
width={640}
title={strategiesValues[data?.type || 'create'].title}
onCancel={() => close()}
cancelText="取消"
onOk={() => submit()}
okText={strategiesValues[data?.type || 'create'].okText}
>
<Form
layout="vertical"
form={form}
onFieldsChange={(changed) => {
const idChanged = changed.find(
(item) =>
item.name instanceof Array &&
item.name.length === 1 &&
item.name[0] === 'nickname',
);
if (idChanged && idChanged.validated) {
setName(idChanged.value);
}
}}
>
<Form.Item required label={'名称'} name="nickname">
<Input />
</Form.Item>
{/* <Form.Item name="avatar" label="头像">
<AvatarUpload
disabled={!nameValue}
data={(file) => {
const extname = path.extname(file.name);
const filename = `${nameValue}${extname}`;
return {
file,
filename,
};
}}
AvatarRender={UploadButton}
/>
</Form.Item> */}
<Form.Item label="描述" name="description">
<Input />
</Form.Item>
</Form>
</Modal>
);
};

export const KnowledgeModal: ModalItem = {
id: KnowledgeModalId,
component: KnowledgeModalComponent,
};
43 changes: 43 additions & 0 deletions web-apps/ui/src/views/knowledge/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
&-col {
display: flex;
align-items: center;

a {
text-decoration: none;
color: #1677ff;
}
}
}

Expand All @@ -50,6 +55,19 @@
height: 100%;
}

&-creation {
position: absolute;
top: -64px;
right: 24px;
height: 64px;
display: flex;
align-items: center;

button {
border: none;
}
}

.ant-list-items {
padding-bottom: 24px;
overflow-y: auto;
Expand All @@ -68,3 +86,28 @@
width: 100%;
}
}

.magent-knowledge-upload {
&-wrapper {
display: flex;
justify-content: center;
}

&-dragger {
display: block;
width: 800px;
margin: 128px 0;
background-color: rgba(247, 247, 250);

.ant-upload-drag {
border-width: 3px;
height: 200px;
}
}

&-dragger:hover {
.ant-upload-drag {
background-color: #4096ff15;
}
}
}
9 changes: 9 additions & 0 deletions web-apps/ui/src/views/knowledge/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { createViewPreference, ManaModule } from '@difizen/mana-app';

import { KnowledgeModule } from '@/modules/knowledge/module.js';

import { KnowledgeModalContribution } from './create-modal/index.js';
import { KnowledgeUploadView, uploadslot } from './upload-view.js';
import { KnowledgeView, slot } from './view.js';

export const KnowledgePageModule = ManaModule.create()
Expand All @@ -12,5 +14,12 @@ export const KnowledgePageModule = ManaModule.create()
view: KnowledgeView,
autoCreate: true,
}),
KnowledgeModalContribution,
KnowledgeUploadView,
createViewPreference({
slot: uploadslot,
view: KnowledgeUploadView,
autoCreate: true,
}),
)
.dependOn(KnowledgeModule);
1 change: 1 addition & 0 deletions web-apps/ui/src/views/knowledge/protocol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const KnowledgeModalId = 'knowledge.modal';
72 changes: 72 additions & 0 deletions web-apps/ui/src/views/knowledge/upload-view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { InboxOutlined } from '@ant-design/icons';
import { BaseView, inject, prop, singleton, view } from '@difizen/mana-app';
import { message, Upload } from 'antd';

import './index.less';
import { history } from 'umi';
import { useParams } from 'umi';

import { MainView } from '@/modules/base-layout/main-view.js';
import type { NavigatablePage } from '@/modules/base-layout/protocol.js';

const { Dragger } = Upload;

const viewId = 'magent-knowledge-upload';
export const uploadslot = `${viewId}-slot`;

const KnowledgeUploadComponent = () => {
const { knowledgeId } = useParams();

return (
<div className={`${viewId}-wrapper`}>
<Dragger
className={`${viewId}-dragger`}
name="file"
multiple
data={{
knowledge_id: knowledgeId,
}}
method="post"
action="/api/v1/knowledge/upload"
listType="picture"
onChange={(info) => {
const { status } = info.file;
if (status === 'done') {
message.success(`${info.file.name} file uploaded successfully.`);
} else if (status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
}}
// onDrop={(e) => {}}
>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
<p className="ant-upload-hint">
Support for a single or bulk upload. Strictly prohibited from uploading
company data or other banned files.
</p>
</Dragger>
</div>
);
};

@singleton()
@view(viewId)
export class KnowledgeUploadView extends BaseView implements NavigatablePage {
@inject(MainView) protected mainView: MainView;

override view = KnowledgeUploadComponent;

override onViewUnmount(): void {
this.mainView.active = undefined;
}
override onViewMount(): void {
this.mainView.active = this;
}

goBack = () => history.push('/portal/knowledge');

pageTitle = () => <>上传知识</>;
}
Loading

0 comments on commit 5500303

Please sign in to comment.