diff --git a/web/src/components/image.tsx b/web/src/components/image.tsx deleted file mode 100644 index 7cb068968..000000000 --- a/web/src/components/image.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { api_host } from '@/utils/api'; - -interface IImage { - id: string; - className: string; -} - -const Image = ({ id, className, ...props }: IImage) => { - return ( - - ); -}; - -export default Image; diff --git a/web/src/components/image/index.less b/web/src/components/image/index.less new file mode 100644 index 000000000..55c48263a --- /dev/null +++ b/web/src/components/image/index.less @@ -0,0 +1,10 @@ +.image { + width: 100px; + object-fit: contain; +} + +.imagePreview { + display: block; + max-width: 45vw; + max-height: 40vh; +} diff --git a/web/src/components/image/index.tsx b/web/src/components/image/index.tsx new file mode 100644 index 000000000..942a6bc76 --- /dev/null +++ b/web/src/components/image/index.tsx @@ -0,0 +1,33 @@ +import { api_host } from '@/utils/api'; +import { Popover } from 'antd'; + +import styles from './index.less'; + +interface IImage { + id: string; + className: string; +} + +const Image = ({ id, className, ...props }: IImage) => { + return ( + + ); +}; + +export default Image; + +export const ImageWithPopover = ({ id }: { id: string }) => { + return ( + } + > + + + ); +}; diff --git a/web/src/hooks/chat-hooks.ts b/web/src/hooks/chat-hooks.ts index 08f75f6f6..6a152c62c 100644 --- a/web/src/hooks/chat-hooks.ts +++ b/web/src/hooks/chat-hooks.ts @@ -5,7 +5,10 @@ import { IStats, IToken, } from '@/interfaces/database/chat'; -import { IFeedbackRequestBody } from '@/interfaces/request/chat'; +import { + IAskRequestBody, + IFeedbackRequestBody, +} from '@/interfaces/request/chat'; import i18n from '@/locales/config'; import { IClientConversation } from '@/pages/chat/interface'; import chatService from '@/services/chat-service'; @@ -477,3 +480,23 @@ export const useFetchNextSharedConversation = (conversationId: string) => { }; //#endregion + +//#region search page + +export const useFetchMindMap = () => { + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: ['fetchMindMap'], + mutationFn: async (params: IAskRequestBody) => { + const { data } = await chatService.getMindMap(params); + + return data; + }, + }); + + return { data, loading, fetchMindMap: mutateAsync }; +}; +//#endregion diff --git a/web/src/hooks/knowledge-hooks.ts b/web/src/hooks/knowledge-hooks.ts index f26bfa689..859424ab8 100644 --- a/web/src/hooks/knowledge-hooks.ts +++ b/web/src/hooks/knowledge-hooks.ts @@ -209,7 +209,7 @@ export const useTestChunkRetrieval = (): ResponsePostType & { mutationFn: async (values: any) => { const { data } = await kbService.retrieval_test({ ...values, - kb_id: knowledgeBaseId, + kb_id: values.kb_id ?? knowledgeBaseId, page, size: pageSize, }); diff --git a/web/src/hooks/logic-hooks.ts b/web/src/hooks/logic-hooks.ts index 88a3b59ad..8842efa0f 100644 --- a/web/src/hooks/logic-hooks.ts +++ b/web/src/hooks/logic-hooks.ts @@ -243,6 +243,10 @@ export const useSendMessageWithSse = ( const x = await reader?.read(); if (x) { const { done, value } = x; + if (done) { + console.info('done'); + break; + } try { const val = JSON.parse(value?.data || ''); const d = val?.data; @@ -256,10 +260,6 @@ export const useSendMessageWithSse = ( } catch (e) { console.warn(e); } - if (done) { - console.info('done'); - break; - } } } console.info('done?'); diff --git a/web/src/interfaces/database/knowledge.ts b/web/src/interfaces/database/knowledge.ts index 3fc103678..ad9b02593 100644 --- a/web/src/interfaces/database/knowledge.ts +++ b/web/src/interfaces/database/knowledge.ts @@ -98,6 +98,7 @@ export interface ITestingChunk { term_similarity: number; vector: number[]; vector_similarity: number; + highlight: string; } export interface ITestingDocument { diff --git a/web/src/interfaces/request/chat.ts b/web/src/interfaces/request/chat.ts index 46c5f3df6..1cc0657e5 100644 --- a/web/src/interfaces/request/chat.ts +++ b/web/src/interfaces/request/chat.ts @@ -3,3 +3,8 @@ export interface IFeedbackRequestBody { thumbup?: boolean; feedback?: string; } + +export interface IAskRequestBody { + questionkb_ids: string; + kb_ids: string[]; +} diff --git a/web/src/layouts/components/header/index.tsx b/web/src/layouts/components/header/index.tsx index b3c8d536d..694840547 100644 --- a/web/src/layouts/components/header/index.tsx +++ b/web/src/layouts/components/header/index.tsx @@ -27,9 +27,9 @@ const RagHeader = () => { () => [ { path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon }, { path: '/chat', name: t('chat'), icon: MessageOutlined }, + // { path: '/search', name: t('search'), icon: SearchOutlined }, { path: '/flow', name: t('flow'), icon: GraphIcon }, { path: '/file', name: t('fileManager'), icon: FileIcon }, - { path: '/search', name: t('search'), icon: FileIcon }, ], [t], ); diff --git a/web/src/pages/search/hooks.ts b/web/src/pages/search/hooks.ts new file mode 100644 index 000000000..5b0dfc3b2 --- /dev/null +++ b/web/src/pages/search/hooks.ts @@ -0,0 +1,38 @@ +import { MessageType } from '@/constants/chat'; +import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks'; +import { useSendMessageWithSse } from '@/hooks/logic-hooks'; +import api from '@/utils/api'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { IMessage } from '../chat/interface'; + +export const useSendQuestion = (kbIds: string[]) => { + const { send, answer, done } = useSendMessageWithSse(api.ask); + const { testChunk, loading } = useTestChunkRetrieval(); + const [sendingLoading, setSendingLoading] = useState(false); + + const message: IMessage = useMemo(() => { + return { + id: '', + content: answer.answer, + role: MessageType.Assistant, + reference: answer.reference, + }; + }, [answer]); + + const sendQuestion = useCallback( + (question: string) => { + setSendingLoading(true); + send({ kb_ids: kbIds, question }); + testChunk({ kb_id: kbIds, highlight: true, question }); + }, + [send, testChunk, kbIds], + ); + + useEffect(() => { + if (done) { + setSendingLoading(false); + } + }, [done]); + + return { sendQuestion, message, loading, sendingLoading }; +}; diff --git a/web/src/pages/search/index.less b/web/src/pages/search/index.less index 576954d79..5f8ccf719 100644 --- a/web/src/pages/search/index.less +++ b/web/src/pages/search/index.less @@ -1,9 +1,17 @@ +.searchPage { + // height: 100%; +} + .searchSide { - height: calc(100vh - 72px); - position: fixed !important; + // height: calc(100vh - 72px); + position: relative; + // position: fixed !important; + // top: 72px; + // bottom: 0; + :global(.ant-layout-sider-children) { + height: auto; + } inset-inline-start: 0; - top: 72px; - bottom: 0; .modelForm { display: flex; @@ -16,13 +24,29 @@ } .list { width: 100%; - height: 100%; + // height: 100%; + height: calc(100vh - 152px); overflow: auto; } .checkbox { width: 100%; } .knowledgeName { - width: 120px; + width: 130px; + } +} + +.content { + height: 100%; + .main { + width: 60%; + // background-color: aqua; + overflow: auto; + padding: 10px; + } + + .graph { + width: 40%; + background-color: bisque; } } diff --git a/web/src/pages/search/index.tsx b/web/src/pages/search/index.tsx index 3e7e7089c..1ac10c7da 100644 --- a/web/src/pages/search/index.tsx +++ b/web/src/pages/search/index.tsx @@ -1,37 +1,65 @@ -import { Layout } from 'antd'; -import React from 'react'; +import HightLightMarkdown from '@/components/highlight-markdown'; +import { ImageWithPopover } from '@/components/image'; +import MessageItem from '@/components/message-item'; +import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; +import { IReference } from '@/interfaces/database/chat'; +import { Card, Flex, Input, Layout, List, Space } from 'antd'; +import { useState } from 'react'; +import { useSendQuestion } from './hooks'; import SearchSidebar from './sidebar'; -const { Header, Content, Footer } = Layout; +import styles from './index.less'; + +const { Content } = Layout; +const { Search } = Input; const SearchPage = () => { + const [checkedList, setCheckedList] = useState([]); + const list = useSelectTestingResult(); + const { sendQuestion, message, sendingLoading } = + useSendQuestion(checkedList); + return ( - - - - - - - long content - { - // indicates very long content - Array.from({ length: 100 }, (_, index) => ( - - {index % 20 === 0 && index ? 'more' : '...'} - - - )) - } - + + + + + + + + + ( + + + + + + {item.highlight} + + + + + )} + /> + + + - ); diff --git a/web/src/pages/search/sidebar.tsx b/web/src/pages/search/sidebar.tsx index de9d0b11a..68f5b6385 100644 --- a/web/src/pages/search/sidebar.tsx +++ b/web/src/pages/search/sidebar.tsx @@ -1,20 +1,30 @@ import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import type { CheckboxProps } from 'antd'; -import { Checkbox, Layout, List, Typography } from 'antd'; +import { Avatar, Checkbox, Layout, List, Space, Typography } from 'antd'; import { CheckboxValueType } from 'antd/es/checkbox/Group'; -import { useCallback, useMemo, useState } from 'react'; +import { + Dispatch, + SetStateAction, + useCallback, + useEffect, + useMemo, +} from 'react'; +import { UserOutlined } from '@ant-design/icons'; import { CheckboxChangeEvent } from 'antd/es/checkbox'; import styles from './index.less'; const { Sider } = Layout; -const SearchSidebar = () => { +interface IProps { + checkedList: string[]; + setCheckedList: Dispatch>; +} + +const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => { const { list } = useNextFetchKnowledgeList(); const ids = useMemo(() => list.map((x) => x.id), [list]); - const [checkedList, setCheckedList] = useState(ids); - const checkAll = list.length === checkedList.length; const indeterminate = @@ -31,8 +41,12 @@ const SearchSidebar = () => { [ids], ); + useEffect(() => { + setCheckedList(ids); + }, [ids]); + return ( - + { renderItem={(item) => ( - - {item.name} - + + } src={item.avatar} /> + + {item.name} + + )} diff --git a/web/src/pages/user-setting/setting-model/index.less b/web/src/pages/user-setting/setting-model/index.less index d55dee992..3ebf992c9 100644 --- a/web/src/pages/user-setting/setting-model/index.less +++ b/web/src/pages/user-setting/setting-model/index.less @@ -20,6 +20,9 @@ :global(.ant-card-body) { padding: 10px 24px; } + .addButton { + padding: 0; + } } .addedCard { border-radius: 18px; diff --git a/web/src/pages/user-setting/setting-model/index.tsx b/web/src/pages/user-setting/setting-model/index.tsx index c4a2a1cef..e2b5c85ef 100644 --- a/web/src/pages/user-setting/setting-model/index.tsx +++ b/web/src/pages/user-setting/setting-model/index.tsx @@ -293,13 +293,20 @@ const UserSettingModel = () => { children: ( ( @@ -315,7 +322,11 @@ const UserSettingModel = () => { - handleAddModel(item.name)}> + handleAddModel(item.name)} + className={styles.addButton} + > {t('addTheModel')} diff --git a/web/src/services/chat-service.ts b/web/src/services/chat-service.ts index fc8054e65..4b67185cb 100644 --- a/web/src/services/chat-service.ts +++ b/web/src/services/chat-service.ts @@ -23,6 +23,8 @@ const { deleteMessage, thumbup, tts, + ask, + mindmap, } = api; const methods = { @@ -106,6 +108,14 @@ const methods = { url: tts, method: 'post', }, + ask: { + url: ask, + method: 'post', + }, + getMindMap: { + url: mindmap, + method: 'post', + }, } as const; const chatService = registerServer(methods, request); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index 4f696fecb..635fbe322 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -66,6 +66,8 @@ export default { deleteMessage: `${api_host}/conversation/delete_msg`, thumbup: `${api_host}/conversation/thumbup`, tts: `${api_host}/conversation/tts`, + ask: `${api_host}/conversation/ask`, + mindmap: `${api_host}/conversation/mindmap`, // chat for external createToken: `${api_host}/api/new_token`, listToken: `${api_host}/api/token_list`,
long content