From 54dfef479677cf4f9c9ee1ebc6998b8ead162238 Mon Sep 17 00:00:00 2001 From: wangzongming Date: Fri, 31 May 2024 22:40:34 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E3=80=81=E5=88=A0=E9=99=A4=E6=8E=A5=E5=8F=A3=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.env.example | 3 + frontend/next.config.mjs | 2 +- frontend/src/app/api/knowledge/add/route.ts | 15 +++++ frontend/src/app/api/knowledge/del/route.ts | 15 +++++ frontend/src/app/api/knowledge/list/route.ts | 7 ++ .../src/app/api/knowledge/接口存在问题.txt | 3 + .../(desktop)/features/KnowledgeCard.tsx | 30 +++++++-- .../(desktop)/features/KnowledgeList.tsx | 67 ++++++++++++++----- .../features/ModalCreateKnowledge.tsx | 67 +++++++++++++------ .../src/app/knowledge/(desktop)/index.tsx | 5 +- frontend/src/config/server/provider.ts | 6 +- frontend/src/services/_url.ts | 5 ++ frontend/src/services/knowledge.ts | 36 ++++++++++ frontend/src/store/knowledge/action.ts | 43 ++++++++++++ frontend/src/store/knowledge/index.ts | 3 + frontend/src/store/knowledge/store.ts | 22 ++++++ frontend/src/types/knowledge.ts | 12 ++++ 17 files changed, 294 insertions(+), 47 deletions(-) create mode 100644 frontend/src/app/api/knowledge/add/route.ts create mode 100644 frontend/src/app/api/knowledge/del/route.ts create mode 100644 frontend/src/app/api/knowledge/list/route.ts create mode 100644 frontend/src/app/api/knowledge/接口存在问题.txt create mode 100644 frontend/src/services/knowledge.ts create mode 100644 frontend/src/store/knowledge/action.ts create mode 100644 frontend/src/store/knowledge/index.ts create mode 100644 frontend/src/store/knowledge/store.ts create mode 100644 frontend/src/types/knowledge.ts diff --git a/frontend/.env.example b/frontend/.env.example index e072464f..d956ff19 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -101,6 +101,9 @@ OPENAI_API_KEY = sk-xxxxxxxxx # you can use ChatChat.The local/remote ChatChat service url CHATCHAT_PROXY_URL = 'http://localhost:7861/v1' +# Knowledge Base Service. Default as follows +KNOWLEDGE_PROXY_URL = 'http://localhost:7861/knowledge_base' + # The LobeChat plugins store index url # PLUGINS_INDEX_URL=https://chat-plugins.lobehub.com diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index 2de05fb4..f2d2d632 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -58,7 +58,7 @@ const nextConfig = { { source: '/docs', destination: `${docsBasePath}/docs` }, { source: '/docs/zh', destination: `${docsBasePath}/docs/zh` }, { source: '/docs/en', destination: `${docsBasePath}/docs/en` }, - { source: '/docs/:path*', destination: `${docsBasePath}/docs/:path*` }, + { source: '/docs/:path*', destination: `${docsBasePath}/docs/:path*` } ], reactStrictMode: true, diff --git a/frontend/src/app/api/knowledge/add/route.ts b/frontend/src/app/api/knowledge/add/route.ts new file mode 100644 index 00000000..76c52c33 --- /dev/null +++ b/frontend/src/app/api/knowledge/add/route.ts @@ -0,0 +1,15 @@ + +import { getServerConfig } from '@/config/server'; +const { KNOWLEDGE_PROXY_URL } = getServerConfig(); + +export const POST = async (request: Request) => { + const params = await request.json(); + const fetchRes = await fetch(`${KNOWLEDGE_PROXY_URL}/create_knowledge_base`, { + body: JSON.stringify(params), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }); + return fetchRes +}; diff --git a/frontend/src/app/api/knowledge/del/route.ts b/frontend/src/app/api/knowledge/del/route.ts new file mode 100644 index 00000000..716079b5 --- /dev/null +++ b/frontend/src/app/api/knowledge/del/route.ts @@ -0,0 +1,15 @@ + +import { getServerConfig } from '@/config/server'; +const { KNOWLEDGE_PROXY_URL } = getServerConfig(); + +export const POST = async (request: Request) => { + const params = await request.text(); + const fetchRes = await fetch(`${KNOWLEDGE_PROXY_URL}/delete_knowledge_base`, { + body: params, + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }); + return fetchRes +}; diff --git a/frontend/src/app/api/knowledge/list/route.ts b/frontend/src/app/api/knowledge/list/route.ts new file mode 100644 index 00000000..73b75fdf --- /dev/null +++ b/frontend/src/app/api/knowledge/list/route.ts @@ -0,0 +1,7 @@ +import { getServerConfig } from '@/config/server'; +const { KNOWLEDGE_PROXY_URL } = getServerConfig(); + +export const GET = async (request: Request) => { + const fetchRes = await fetch(`${KNOWLEDGE_PROXY_URL}/list_knowledge_bases`); + return fetchRes; +}; diff --git a/frontend/src/app/api/knowledge/接口存在问题.txt b/frontend/src/app/api/knowledge/接口存在问题.txt new file mode 100644 index 00000000..4547f0d7 --- /dev/null +++ b/frontend/src/app/api/knowledge/接口存在问题.txt @@ -0,0 +1,3 @@ + + +1. 创建接口没有简介字段 diff --git a/frontend/src/app/knowledge/(desktop)/features/KnowledgeCard.tsx b/frontend/src/app/knowledge/(desktop)/features/KnowledgeCard.tsx index 428b5e82..81d9bf03 100644 --- a/frontend/src/app/knowledge/(desktop)/features/KnowledgeCard.tsx +++ b/frontend/src/app/knowledge/(desktop)/features/KnowledgeCard.tsx @@ -1,7 +1,8 @@ -import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; -import { Card, Skeleton } from 'antd'; +import { DeleteOutlined, EditOutlined, ExclamationCircleOutlined } from '@ant-design/icons'; +import { Card, Skeleton, message, Modal } from 'antd'; import { useRouter } from 'next/navigation'; import React, { useState } from 'react'; +import { useKnowledgeStore } from '@/store/knowledge'; const { Meta } = Card; @@ -9,18 +10,39 @@ interface KnowLedgeCardProps { intro: string; name: string; } -const KnowledgeCard: React.FC = (props: KnowLedgeCardProps) => { +const KnowledgeCard: React.FC = (props: KnowLedgeCardProps) => { + + const [useFetchKnowledgeDel] = useKnowledgeStore((s) => [ + s.useFetchKnowledgeDel + ]); + const [loading, setLoading] = useState(false); const { name, intro } = props; const router = useRouter(); const handleCardEditClick = () => { router.push('/knowledge/1/base'); }; + const delClick = async () => { + Modal.confirm({ + title: `确认 ${name} 删除吗?`, + icon: , + async onOk() { + const { code: resCode, msg: resMsg } = await useFetchKnowledgeDel(name) + if (resCode !== 200) { + message.error(resMsg) + } else { + message.success(resMsg) + } + return Promise.resolve(); + }, + }); + + }; return ( , - , + , ]} bordered={false} style={{ marginTop: 16, width: 300 }} diff --git a/frontend/src/app/knowledge/(desktop)/features/KnowledgeList.tsx b/frontend/src/app/knowledge/(desktop)/features/KnowledgeList.tsx index 927a8308..3ecb7737 100644 --- a/frontend/src/app/knowledge/(desktop)/features/KnowledgeList.tsx +++ b/frontend/src/app/knowledge/(desktop)/features/KnowledgeList.tsx @@ -1,22 +1,59 @@ import React, { memo } from 'react'; - +import { Empty, Spin } from 'antd'; +import { createStyles } from 'antd-style'; import KnowledgeCard from './KnowledgeCard'; +import { useKnowledgeStore } from '@/store/knowledge'; +import { Flexbox } from 'react-layout-kit'; -const list = [ - { intro: '知识库简介', name: '知识库名称' }, - { intro: '知识库简介', name: '知识库名称' }, - { intro: '知识库简介', name: '知识库名称' }, - { intro: '知识库简介', name: '知识库名称' }, - { intro: '知识库简介', name: '知识库名称' }, - { intro: '知识库简介', name: '知识库名称' }, - { intro: '知识库简介', name: '知识库名称' }, -]; +const useStyles = createStyles(({ css, token, stylish }) => ({ + wrap: css` + min-height: 200px; + height: 100%; + width: 100%; + `, + null: css` + display: block; + position: absolute; + top: 0px; bottom: 0px; left: 0px; right: 0px; + margin: auto; + height: 100px; + `, +})); -const RenderList = memo(() => - list.map((item, index) => { - return ; - }), -); +// const list = [ +// { intro: '知识库简介', name: '知识库名称' }, +// { intro: '知识库简介', name: '知识库名称' }, +// ]; + +const RenderList = memo(() => { + const { styles } = useStyles(); + const [listData, useFetchKnowledgeList] = useKnowledgeStore((s) => [ + s.listData, s.useFetchKnowledgeList + ]); + const { isLoading } = useFetchKnowledgeList(); + + const list = listData.map((item) => ({ + intro: '知识库简介', + // 等接口更改... + name: item as unknown as string + })) + if (!isLoading && !listData.length) { + return
+ +
+ } + return
+ +
+ + {list.map((item, index) => { + return ; + })} + +
+
+
+}); const KnowledgeCardList = memo(() => { return ; diff --git a/frontend/src/app/knowledge/(desktop)/features/ModalCreateKnowledge.tsx b/frontend/src/app/knowledge/(desktop)/features/ModalCreateKnowledge.tsx index 34efcb7f..68442543 100644 --- a/frontend/src/app/knowledge/(desktop)/features/ModalCreateKnowledge.tsx +++ b/frontend/src/app/knowledge/(desktop)/features/ModalCreateKnowledge.tsx @@ -1,21 +1,38 @@ import { Modal, type ModalProps } from '@lobehub/ui'; -import { Form, Input, Select } from 'antd'; -import { memo } from 'react'; +import { Form, Input, Select, FormInstance, message } from 'antd'; +import { memo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; -const DEFAULT_FIELD_VALUE = { - // imageType: ImageType.JPG, - withBackground: true, - withFooter: false, - withPluginInfo: false, - withSystemRole: false, +import { useKnowledgeStore } from '@/store/knowledge'; + +const DEFAULT_FIELD_VALUE = { + vector_store_type: "faiss", + embed_model:"bge-large-zh-v1.5" }; interface ModalCreateKnowledgeProps extends ModalProps { toggleModal: (open: boolean) => void; } const CreateKnowledgeBase = memo(({ toggleModal, open }) => { const { t } = useTranslation('chat'); + const antdFormInstance = useRef(); + const [useFetchKnowledgeAdd] = useKnowledgeStore((s) => [ + s.useFetchKnowledgeAdd + ]); + + const onSubmit = async () => { + if(!antdFormInstance.current) return; + const fieldsError = await antdFormInstance.current.validateFields(); + if(fieldsError.length) return; + const values = antdFormInstance.current.getFieldsValue(true); + + const { code:resCode, data: resData, msg: resMsg } = await useFetchKnowledgeAdd({ ...values }) + if(resCode !== 200){ + message.error(resMsg) + return; + } + toggleModal(false); + } return ( (({ toggleModal, open centered={false} maxHeight={false} onCancel={() => toggleModal(false)} - onOk={() => toggleModal(false)} + onOk={onSubmit} open={open} title="创建知识库" > -
- - + + + - +
- - + faiss + milvus + zilliz + pg + es + chromadb
- - + bge-large-zh-v1.5 + text-embedding-v1 + Bert + Word2Vec + FastText
diff --git a/frontend/src/app/knowledge/(desktop)/index.tsx b/frontend/src/app/knowledge/(desktop)/index.tsx index eca8c602..bc9357a9 100644 --- a/frontend/src/app/knowledge/(desktop)/index.tsx +++ b/frontend/src/app/knowledge/(desktop)/index.tsx @@ -4,7 +4,6 @@ import { FloatButton } from 'antd'; import { Plus } from 'lucide-react'; import dynamic from 'next/dynamic'; import { memo, useState } from 'react'; -import { Flexbox } from 'react-layout-kit'; import KnowledgeCardList from './features/KnowledgeList'; // import CreateKnowledgeBase from './features/createKnowledgeBase'; @@ -17,9 +16,7 @@ const DesktopPage = memo(() => { return ( <> - - - + } onClick={() => setShowModal(true)}> 新建知识库 diff --git a/frontend/src/config/server/provider.ts b/frontend/src/config/server/provider.ts index 037d9bc6..8e5b41f4 100644 --- a/frontend/src/config/server/provider.ts +++ b/frontend/src/config/server/provider.ts @@ -49,6 +49,9 @@ declare global { // ChatChat CHATCHAT_PROXY_URL?: string; + + // knowledeg + KNOWLEDGE_PROXY_URL?: string; } } } @@ -76,7 +79,7 @@ export const getProviderConfig = () => { if (process.env.OPENAI_FUNCTION_REGIONS) { regions = process.env.OPENAI_FUNCTION_REGIONS.split(','); } - + return { CUSTOM_MODELS: process.env.CUSTOM_MODELS, @@ -119,5 +122,6 @@ export const getProviderConfig = () => { OLLAMA_PROXY_URL: process.env.OLLAMA_PROXY_URL || '', CHATCHAT_PROXY_URL: process.env.CHATCHAT_PROXY_URL || '', + KNOWLEDGE_PROXY_URL: process.env.KNOWLEDGE_PROXY_URL || 'http://localhost:7861/knowledge_base', }; }; diff --git a/frontend/src/services/_url.ts b/frontend/src/services/_url.ts index 9e863329..fe826e5e 100644 --- a/frontend/src/services/_url.ts +++ b/frontend/src/services/_url.ts @@ -44,4 +44,9 @@ export const API_ENDPOINTS = mapWithBasePath({ tts: '/api/openai/tts', edge: '/api/tts/edge-speech', microsoft: '/api/tts/microsoft-speech', + + // knowledge + knowledgeList: '/api/knowledge/list', + knowledgeAdd: '/api/knowledge/add', + knowledgeDel: '/api/knowledge/del' }); diff --git a/frontend/src/services/knowledge.ts b/frontend/src/services/knowledge.ts new file mode 100644 index 00000000..23815d88 --- /dev/null +++ b/frontend/src/services/knowledge.ts @@ -0,0 +1,36 @@ + +import { KnowledgeList, KnowledgeFormFields, Reseponse } from '@/types/knowledge'; + +import { API_ENDPOINTS } from './_url'; + +class KnowledgeService { + getList = async (): Promise> => { + const res = await fetch(`${API_ENDPOINTS.knowledgeList}`); + const data = await res.json(); + return data; + }; + + add = async (formValues: KnowledgeFormFields) => { + const res = await fetch(`${API_ENDPOINTS.knowledgeAdd}`, { + body: JSON.stringify(formValues), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }); + return res.json(); + }; + + del = async (name: string) => { + const res = await fetch(`${API_ENDPOINTS.knowledgeDel}`, { + body: JSON.stringify(name), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }); + return res.json(); + }; +} + +export const knowledgeService = new KnowledgeService(); diff --git a/frontend/src/store/knowledge/action.ts b/frontend/src/store/knowledge/action.ts new file mode 100644 index 00000000..ca3db06a --- /dev/null +++ b/frontend/src/store/knowledge/action.ts @@ -0,0 +1,43 @@ + +import useSWR, { SWRResponse } from 'swr'; +import type { StateCreator } from 'zustand/vanilla'; + +import { knowledgeService } from '@/services/knowledge'; + +import { globalHelpers } from '@/store/global/helpers'; +import { KnowledgeFormFields, KnowledgeList, Reseponse } from '@/types/knowledge'; + +import type { Store } from './store'; + +export interface StoreAction { + listData: KnowledgeList; + useFetchKnowledgeList: () => SWRResponse>; + useFetchKnowledgeAdd: (arg: KnowledgeFormFields) => Promise>; + useFetchKnowledgeDel: (name: string)=> Promise>; +} + +export const createKnowledgeAction: StateCreator< + Store, + [['zustand/devtools', never]], + [], + StoreAction +> = (set, get) => ({ + listData: [], + useFetchKnowledgeList: () => { + return useSWR>( + globalHelpers.getCurrentLanguage(), + knowledgeService.getList, + { + onSuccess: (res) => { + set({ listData: res.data }) + }, + }, + ) + }, + useFetchKnowledgeAdd: async (formValues) => { + return await knowledgeService.add(formValues) + }, + useFetchKnowledgeDel: async (name) => { + return await knowledgeService.del(name) + }, +}); diff --git a/frontend/src/store/knowledge/index.ts b/frontend/src/store/knowledge/index.ts new file mode 100644 index 00000000..c718aee1 --- /dev/null +++ b/frontend/src/store/knowledge/index.ts @@ -0,0 +1,3 @@ +export * from './store'; +export { useKnowledgeStore } from './store'; +export { type Store } from './store'; diff --git a/frontend/src/store/knowledge/store.ts b/frontend/src/store/knowledge/store.ts new file mode 100644 index 00000000..56d5fd48 --- /dev/null +++ b/frontend/src/store/knowledge/store.ts @@ -0,0 +1,22 @@ +import { subscribeWithSelector, devtools, persist } from 'zustand/middleware'; +import { shallow } from 'zustand/shallow'; +import { createWithEqualityFn } from 'zustand/traditional'; +import type { StateCreator } from 'zustand/vanilla'; + +import { isDev } from '@/utils/env'; + +import { type StoreAction, createKnowledgeAction } from './action'; +export type Store = StoreAction; + +const createStore: StateCreator = (...parameters) => ({ + ...createKnowledgeAction(...parameters), +}); + +export const useKnowledgeStore = createWithEqualityFn()( + subscribeWithSelector( + devtools(createStore, { + name: 'ChatChat_Chat' + (isDev ? '_DEV' : ''), + }), + ), + shallow, +); diff --git a/frontend/src/types/knowledge.ts b/frontend/src/types/knowledge.ts new file mode 100644 index 00000000..567c71be --- /dev/null +++ b/frontend/src/types/knowledge.ts @@ -0,0 +1,12 @@ +export interface Reseponse { code: number; msg: string; data: T } + +export interface KnowledgeFormFields { + knowledge_base_name: string; + vector_store_type: string; + kb_info?: string; + embed_model: string; +} + +export interface KnowledgeListItemFields extends KnowledgeFormFields { } + +export type KnowledgeList = KnowledgeListItemFields[];