From ebd8e014c61d7389dd44b16d840abd7160d5854d Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Sun, 6 Jul 2025 15:52:53 +0800 Subject: [PATCH] feat: rag fe framework --- web/src/app/home/bots/BotDetailDialog.tsx | 2 - .../home/bots/components/bot-form/BotForm.tsx | 41 ----- web/src/app/home/bots/page.tsx | 2 +- web/src/app/home/knowledge/KBDetailDialog.tsx | 134 +++++++++++++- .../knowledge/components/kb-card/KBCard.tsx | 2 +- .../knowledge/components/kb-docs/KBDoc.tsx | 0 .../components/kb-form/ChooseEntity.ts | 4 + .../knowledge/components/kb-form/KBForm.tsx | 172 ++++++++++++++++++ web/src/app/home/knowledge/page.tsx | 84 ++++++++- web/src/i18n/locales/en-US.ts | 16 +- 10 files changed, 398 insertions(+), 59 deletions(-) create mode 100644 web/src/app/home/knowledge/components/kb-docs/KBDoc.tsx create mode 100644 web/src/app/home/knowledge/components/kb-form/ChooseEntity.ts create mode 100644 web/src/app/home/knowledge/components/kb-form/KBForm.tsx diff --git a/web/src/app/home/bots/BotDetailDialog.tsx b/web/src/app/home/bots/BotDetailDialog.tsx index 1c4a2403..cad04e7b 100644 --- a/web/src/app/home/bots/BotDetailDialog.tsx +++ b/web/src/app/home/bots/BotDetailDialog.tsx @@ -130,7 +130,6 @@ export default function BotDetailDialog({ onFormCancel={handleFormCancel} onBotDeleted={handleBotDeleted} onNewBotCreated={handleNewBotCreated} - hideButtons={true} /> @@ -202,7 +201,6 @@ export default function BotDetailDialog({ onFormCancel={handleFormCancel} onBotDeleted={handleBotDeleted} onNewBotCreated={handleNewBotCreated} - hideButtons={true} /> )} {activeMenu === 'logs' && botId && ( diff --git a/web/src/app/home/bots/components/bot-form/BotForm.tsx b/web/src/app/home/bots/components/bot-form/BotForm.tsx index 40a902c2..fe36d33b 100644 --- a/web/src/app/home/bots/components/bot-form/BotForm.tsx +++ b/web/src/app/home/bots/components/bot-form/BotForm.tsx @@ -67,14 +67,12 @@ export default function BotForm({ onFormCancel, onBotDeleted, onNewBotCreated, - hideButtons = false, }: { initBotId?: string; onFormSubmit: (value: z.infer>) => void; onFormCancel: () => void; onBotDeleted: () => void; onNewBotCreated: (botId: string) => void; - hideButtons?: boolean; }) { const { t } = useTranslation(); const formSchema = getFormSchema(t); @@ -527,45 +525,6 @@ export default function BotForm({ )} - - {!hideButtons && ( -
-
- {!initBotId && ( - - )} - {initBotId && ( - <> - - - - )} - -
-
- )} diff --git a/web/src/app/home/bots/page.tsx b/web/src/app/home/bots/page.tsx index d4305898..ad130fae 100644 --- a/web/src/app/home/bots/page.tsx +++ b/web/src/app/home/bots/page.tsx @@ -92,7 +92,7 @@ export default function BotConfigPage() { } return ( -
+
void; kbId?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any onFormSubmit: (value: z.infer) => void; onFormCancel: () => void; onKbDeleted: () => void; @@ -36,7 +48,7 @@ export default function KBDetailDialog({ const { t } = useTranslation(); const [kbId, setKbId] = useState(propKbId); const [activeMenu, setActiveMenu] = useState('metadata'); - const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + // const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); useEffect(() => { setKbId(propKbId); @@ -58,8 +70,8 @@ export default function KBDetailDialog({ ), }, { - key: 'files', - label: t('knowledge.files'), + key: 'documents', + label: t('knowledge.documents'), icon: (
- {t('knowledge.newKb')} + {t('knowledge.createKnowledgeBase')} +
+ {activeMenu === 'metadata' && ( + + )} + {activeMenu === 'documents' &&
documents
} +
+ {activeMenu === 'metadata' && ( + +
+ + +
+
+ )} ); } + + return ( + <> + + + + + + + + + {menu.map((item) => ( + + setActiveMenu(item.key)} + > + + {item.icon} + {item.label} + + + + ))} + + + + + +
+ + + {activeMenu === 'metadata' + ? t('knowledge.createKnowledgeBase') + : t('knowledge.editDocument')} + + +
+ {activeMenu === 'metadata' && ( + + )} + {activeMenu === 'documents' &&
documents
} +
+ {activeMenu === 'metadata' && ( + +
+ + + +
+
+ )} +
+
+
+
+ + ); } diff --git a/web/src/app/home/knowledge/components/kb-card/KBCard.tsx b/web/src/app/home/knowledge/components/kb-card/KBCard.tsx index 5d49e738..560b0497 100644 --- a/web/src/app/home/knowledge/components/kb-card/KBCard.tsx +++ b/web/src/app/home/knowledge/components/kb-card/KBCard.tsx @@ -26,7 +26,7 @@ export default function KBCard({ kbCardVO }: { kbCardVO: KnowledgeBaseVO }) {
- {t('knowledge.bases.updateTime')} + {t('knowledge.updateTime')} {kbCardVO.lastUpdatedTimeAgo}
diff --git a/web/src/app/home/knowledge/components/kb-docs/KBDoc.tsx b/web/src/app/home/knowledge/components/kb-docs/KBDoc.tsx new file mode 100644 index 00000000..e69de29b diff --git a/web/src/app/home/knowledge/components/kb-form/ChooseEntity.ts b/web/src/app/home/knowledge/components/kb-form/ChooseEntity.ts new file mode 100644 index 00000000..54f983e4 --- /dev/null +++ b/web/src/app/home/knowledge/components/kb-form/ChooseEntity.ts @@ -0,0 +1,4 @@ +export interface IEmbeddingModelEntity { + label: string; + value: string; +} diff --git a/web/src/app/home/knowledge/components/kb-form/KBForm.tsx b/web/src/app/home/knowledge/components/kb-form/KBForm.tsx new file mode 100644 index 00000000..9ae51656 --- /dev/null +++ b/web/src/app/home/knowledge/components/kb-form/KBForm.tsx @@ -0,0 +1,172 @@ +import { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { useTranslation } from 'react-i18next'; +import { Input } from '@/components/ui/input'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, + FormDescription, +} from '@/components/ui/form'; +import { IEmbeddingModelEntity } from './ChooseEntity'; +import { httpClient } from '@/app/infra/http/HttpClient'; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; + +const getFormSchema = (t: (key: string) => string) => + z.object({ + name: z.string().min(1, { message: t('knowledge.kbNameRequired') }), + description: z + .string() + .min(1, { message: t('knowledge.kbDescriptionRequired') }), + embeddingModelUUID: z + .string() + .min(1, { message: t('knowledge.embeddingModelUUIDRequired') }), + }); + +export default function KBForm({ + initKbId, + onFormSubmit, + onFormCancel, + onKbDeleted, + onNewKbCreated, +}: { + initKbId?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onFormSubmit: (value: any) => void; + onFormCancel: () => void; + onKbDeleted: () => void; + onNewKbCreated: (kbId: string) => void; +}) { + const { t } = useTranslation(); + const formSchema = getFormSchema(t); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + name: '', + description: t('knowledge.defaultDescription'), + embeddingModelUUID: '', + }, + }); + + const [embeddingModelNameList, setEmbeddingModelNameList] = useState< + IEmbeddingModelEntity[] + >([]); + + useEffect(() => { + getEmbeddingModelNameList(); + }, []); + + const getEmbeddingModelNameList = async () => { + const resp = await httpClient.getProviderEmbeddingModels(); + setEmbeddingModelNameList( + resp.models.map((item) => { + return { + label: item.name, + value: item.uuid, + }; + }), + ); + }; + + return ( + <> +
+ +
+ ( + + + {t('knowledge.kbName')} + * + + + + + + + )} + /> + ( + + + {t('knowledge.kbDescription')} + * + + + + + + + )} + /> + ( + + + {t('knowledge.embeddingModelUUID')} + * + + +
+ +
+
+ + {t('knowledge.embeddingModelDescription')} + + +
+ )} + /> +
+
+ + + ); +} diff --git a/web/src/app/home/knowledge/page.tsx b/web/src/app/home/knowledge/page.tsx index 7ee25eac..99de73d8 100644 --- a/web/src/app/home/knowledge/page.tsx +++ b/web/src/app/home/knowledge/page.tsx @@ -3,32 +3,102 @@ import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent'; import styles from './knowledgeBase.module.css'; import { useTranslation } from 'react-i18next'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { KnowledgeBaseVO } from '@/app/home/knowledge/components/kb-card/KBCardVO'; import KBCard from '@/app/home/knowledge/components/kb-card/KBCard'; +import KBDetailDialog from '@/app/home/knowledge/KBDetailDialog'; +import { httpClient } from '@/app/infra/http/HttpClient'; +import { KnowledgeBase } from '@/app/infra/entities/api'; export default function KnowledgePage() { const { t } = useTranslation(); const [knowledgeBaseList, setKnowledgeBaseList] = useState( [], ); + const [selectedKbId, setSelectedKbId] = useState(''); + const [detailDialogOpen, setDetailDialogOpen] = useState(false); + + useEffect(() => { + getKnowledgeBaseList(); + }, []); + + async function getKnowledgeBaseList() { + const resp = await httpClient.getKnowledgeBases(); + setKnowledgeBaseList( + resp.bases.map((kb: KnowledgeBase) => { + const currentTime = new Date(); + const lastUpdatedTimeAgo = Math.floor( + (currentTime.getTime() - + new Date(kb.updated_at ?? currentTime.getTime()).getTime()) / + 1000 / + 60 / + 60 / + 24, + ); + + const lastUpdatedTimeAgoText = + lastUpdatedTimeAgo > 0 + ? ` ${lastUpdatedTimeAgo} ${t('knowledge.daysAgo')}` + : t('knowledge.today'); + + return new KnowledgeBaseVO({ + id: kb.uuid || '', + name: kb.name, + description: kb.description, + embeddingModelUUID: kb.embedding_model_uuid, + lastUpdatedTimeAgo: lastUpdatedTimeAgoText, + }); + }), + ); + } const handleKBCardClick = (kbId: string) => { - // setIsEditForm(false); - // setModalOpen(true); + setSelectedKbId(kbId); + setDetailDialogOpen(true); + }; + + const handleCreateKBClick = () => { + setSelectedKbId(''); + setDetailDialogOpen(true); + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleFormSubmit = (value: any) => { + console.log('handleFormSubmit', value); + }; + + const handleFormCancel = () => { + setDetailDialogOpen(false); + }; + + const handleKbDeleted = () => { + getKnowledgeBaseList(); + setDetailDialogOpen(false); + }; + + const handleNewKbCreated = () => { + getKnowledgeBaseList(); + setDetailDialogOpen(false); }; return (
+ +
{ - // setIsEditForm(false); - // setModalOpen(true); - }} + onClick={handleCreateKBClick} /> {knowledgeBaseList.map((kb) => { diff --git a/web/src/i18n/locales/en-US.ts b/web/src/i18n/locales/en-US.ts index 7a1f79c8..e50e7cc7 100644 --- a/web/src/i18n/locales/en-US.ts +++ b/web/src/i18n/locales/en-US.ts @@ -232,9 +232,23 @@ const enUS = { }, knowledge: { title: 'Knowledge', + createKnowledgeBase: 'Create Knowledge Base', description: 'Configuring knowledge bases for improved LLM responses', metadata: 'Metadata', - files: 'Files', + documents: 'Documents', + kbNameRequired: 'Knowledge base name cannot be empty', + kbDescriptionRequired: 'Knowledge base description cannot be empty', + embeddingModelUUIDRequired: 'Embedding model cannot be empty', + daysAgo: 'days ago', + today: 'Today', + kbName: 'Knowledge Base Name', + kbDescription: 'Knowledge Base Description', + defaultDescription: 'A knowledge base', + embeddingModelUUID: 'Embedding Model', + selectEmbeddingModel: 'Select Embedding Model', + embeddingModelDescription: + 'Used to vectorize the text, you can configure it in the Models page', + updateTime: 'Updated ', }, register: { title: 'Initialize LangBot 👋',