From 66a903f1fe662effa3c326b256690da438d50b98 Mon Sep 17 00:00:00 2001 From: bill Date: Fri, 6 Sep 2024 19:12:14 +0800 Subject: [PATCH] feat: Fetch mind map in search page #2247 --- .../indented-tree/indented-tree.tsx | 6 +- web/src/hooks/chat-hooks.ts | 21 +++- web/src/interfaces/request/chat.ts | 2 +- web/src/layouts/components/header/index.tsx | 4 +- web/src/pages/force-graph/index.tsx | 109 ------------------ web/src/pages/search/hooks.ts | 25 +++- web/src/pages/search/index.less | 6 +- web/src/pages/search/index.tsx | 35 +++++- web/src/pages/search/sidebar.tsx | 15 ++- web/src/services/chat-service.ts | 5 + web/src/utils/api.ts | 1 + 11 files changed, 102 insertions(+), 127 deletions(-) diff --git a/web/src/components/indented-tree/indented-tree.tsx b/web/src/components/indented-tree/indented-tree.tsx index 547439459e..770f132fd2 100644 --- a/web/src/components/indented-tree/indented-tree.tsx +++ b/web/src/components/indented-tree/indented-tree.tsx @@ -16,7 +16,7 @@ import { } from '@antv/g6'; import { TreeData } from '@antv/g6/lib/types'; import isEmpty from 'lodash/isEmpty'; -import { useCallback, useEffect, useRef } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; const rootId = 'root'; @@ -294,9 +294,10 @@ register( interface IProps { data: TreeData; show: boolean; + style?: React.CSSProperties; } -const IndentedTree = ({ data, show }: IProps) => { +const IndentedTree = ({ data, show, style = {} }: IProps) => { const containerRef = useRef(null); const graphRef = useRef(null); @@ -388,6 +389,7 @@ const IndentedTree = ({ data, show }: IProps) => { width: '90vw', height: '80vh', display: show ? 'block' : 'none', + ...style, }} /> ); diff --git a/web/src/hooks/chat-hooks.ts b/web/src/hooks/chat-hooks.ts index 6a152c62cb..1d4205c373 100644 --- a/web/src/hooks/chat-hooks.ts +++ b/web/src/hooks/chat-hooks.ts @@ -490,13 +490,32 @@ export const useFetchMindMap = () => { mutateAsync, } = useMutation({ mutationKey: ['fetchMindMap'], + gcTime: 0, mutationFn: async (params: IAskRequestBody) => { const { data } = await chatService.getMindMap(params); - return data; + return data?.data ?? []; }, }); return { data, loading, fetchMindMap: mutateAsync }; }; + +export const useFetchRelatedQuestions = () => { + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: ['fetchRelatedQuestions'], + gcTime: 0, + mutationFn: async (question: string): Promise => { + const { data } = await chatService.getRelatedQuestions({ question }); + + return data?.data ?? []; + }, + }); + + return { data, loading, fetchRelatedQuestions: mutateAsync }; +}; //#endregion diff --git a/web/src/interfaces/request/chat.ts b/web/src/interfaces/request/chat.ts index 1cc0657e51..a61af9fb6a 100644 --- a/web/src/interfaces/request/chat.ts +++ b/web/src/interfaces/request/chat.ts @@ -5,6 +5,6 @@ export interface IFeedbackRequestBody { } export interface IAskRequestBody { - questionkb_ids: string; + question: string; kb_ids: string[]; } diff --git a/web/src/layouts/components/header/index.tsx b/web/src/layouts/components/header/index.tsx index 694840547b..9729ba1874 100644 --- a/web/src/layouts/components/header/index.tsx +++ b/web/src/layouts/components/header/index.tsx @@ -9,7 +9,7 @@ import { useLocation } from 'umi'; import Toolbar from '../right-toolbar'; import { useFetchAppConf } from '@/hooks/logic-hooks'; -import { MessageOutlined } from '@ant-design/icons'; +import { MessageOutlined, SearchOutlined } from '@ant-design/icons'; import styles from './index.less'; const { Header } = Layout; @@ -27,7 +27,7 @@ const RagHeader = () => { () => [ { path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon }, { path: '/chat', name: t('chat'), icon: MessageOutlined }, - // { path: '/search', name: t('search'), icon: SearchOutlined }, + { path: '/search', name: t('search'), icon: SearchOutlined }, { path: '/flow', name: t('flow'), icon: GraphIcon }, { path: '/file', name: t('fileManager'), icon: FileIcon }, ], diff --git a/web/src/pages/force-graph/index.tsx b/web/src/pages/force-graph/index.tsx index 4c95d39f28..215e4a7d9b 100644 --- a/web/src/pages/force-graph/index.tsx +++ b/web/src/pages/force-graph/index.tsx @@ -1,112 +1,3 @@ -import { Graph } from '@antv/g6'; -import { useSize } from 'ahooks'; -import { useEffect, useRef } from 'react'; -import { graphData } from './constant'; import InputWithUpload from './input-upload'; -import styles from './index.less'; -import { Converter } from './util'; - -const converter = new Converter(); - -const nextData = converter.buildNodesAndCombos( - graphData.nodes, - graphData.edges, -); -console.log('🚀 ~ nextData:', nextData); - -const finalData = { ...graphData, ...nextData }; - -const ForceGraph = () => { - const containerRef = useRef(null); - const size = useSize(containerRef); - let graph: Graph; - - const render = () => { - graph = new Graph({ - container: containerRef.current!, - autoFit: 'view', - behaviors: [ - 'drag-element', - 'drag-canvas', - 'zoom-canvas', - 'collapse-expand', - { - type: 'hover-activate', - degree: 1, // 👈🏻 Activate relations. - }, - ], - plugins: [ - { - type: 'tooltip', - getContent: (e, items) => { - if (items.every((x) => x?.description)) { - let result = ``; - items.forEach((item) => { - if (item?.description) { - result += `

${item?.description}

`; - } - }); - return result; - } - return undefined; - }, - }, - ], - layout: { - type: 'combo-combined', - preventOverlap: true, - comboPadding: 1, - spacing: 20, - }, - node: { - style: { - size: 20, - labelText: (d) => d.id, - labelPadding: 30, - // labelOffsetX: 20, - // labelOffsetY: 5, - labelPlacement: 'center', - labelWordWrap: true, - }, - palette: { - type: 'group', - field: (d) => d.combo, - }, - // state: { - // highlight: { - // fill: '#D580FF', - // halo: true, - // lineWidth: 0, - // }, - // dim: { - // fill: '#99ADD1', - // }, - // }, - }, - edge: { - style: (model) => { - const { size, color } = model.data; - return { - stroke: color || '#99ADD1', - lineWidth: size || 1, - }; - }, - }, - // data: graphData, - }); - - graph.setData(finalData); - - graph.render(); - }; - - useEffect(() => { - console.info('rendered'); - render(); - }, []); - - return
; -}; - export default InputWithUpload; diff --git a/web/src/pages/search/hooks.ts b/web/src/pages/search/hooks.ts index 2b0d6a64cc..7025355363 100644 --- a/web/src/pages/search/hooks.ts +++ b/web/src/pages/search/hooks.ts @@ -1,3 +1,4 @@ +import { useFetchMindMap, useFetchRelatedQuestions } from '@/hooks/chat-hooks'; import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks'; import { useSendMessageWithSse } from '@/hooks/logic-hooks'; import { IAnswer } from '@/interfaces/database/chat'; @@ -10,6 +11,13 @@ export const useSendQuestion = (kbIds: string[]) => { const { testChunk, loading } = useTestChunkRetrieval(); const [sendingLoading, setSendingLoading] = useState(false); const [currentAnswer, setCurrentAnswer] = useState({} as IAnswer); + const { fetchRelatedQuestions, data: relatedQuestions } = + useFetchRelatedQuestions(); + const { + fetchMindMap, + data: mindMap, + loading: mindMapLoading, + } = useFetchMindMap(); const sendQuestion = useCallback( (question: string) => { @@ -17,8 +25,13 @@ export const useSendQuestion = (kbIds: string[]) => { setSendingLoading(true); send({ kb_ids: kbIds, question }); testChunk({ kb_id: kbIds, highlight: true, question }); + fetchMindMap({ + question, + kb_ids: kbIds, + }); + fetchRelatedQuestions(question); }, - [send, testChunk, kbIds], + [send, testChunk, kbIds, fetchRelatedQuestions, fetchMindMap], ); useEffect(() => { @@ -33,5 +46,13 @@ export const useSendQuestion = (kbIds: string[]) => { } }, [done]); - return { sendQuestion, loading, sendingLoading, answer: currentAnswer }; + return { + sendQuestion, + loading, + sendingLoading, + answer: currentAnswer, + relatedQuestions: relatedQuestions?.slice(0, 5) ?? [], + mindMap, + mindMapLoading, + }; }; diff --git a/web/src/pages/search/index.less b/web/src/pages/search/index.less index d20b2bfc76..dc1610c286 100644 --- a/web/src/pages/search/index.less +++ b/web/src/pages/search/index.less @@ -2,6 +2,10 @@ .card { width: 100%; } + .tag { + padding: 4px 8px; + font-size: 14px; + } } .searchSide { @@ -49,6 +53,6 @@ .graph { width: 40%; - background-color: bisque; + padding-right: 10px; } } diff --git a/web/src/pages/search/index.tsx b/web/src/pages/search/index.tsx index ce567360b7..48b3dd9804 100644 --- a/web/src/pages/search/index.tsx +++ b/web/src/pages/search/index.tsx @@ -2,12 +2,13 @@ import HightLightMarkdown from '@/components/highlight-markdown'; import { ImageWithPopover } from '@/components/image'; import { useSelectTestingResult } from '@/hooks/knowledge-hooks'; import { IReference } from '@/interfaces/database/chat'; -import { Card, Flex, Input, Layout, List, Space } from 'antd'; +import { Card, Flex, Input, Layout, List, Skeleton, Space, Tag } from 'antd'; import { useState } from 'react'; import MarkdownContent from '../chat/markdown-content'; import { useSendQuestion } from './hooks'; import SearchSidebar from './sidebar'; +import IndentedTree from '@/components/indented-tree/indented-tree'; import styles from './index.less'; const { Content } = Layout; @@ -16,7 +17,14 @@ const { Search } = Input; const SearchPage = () => { const [checkedList, setCheckedList] = useState([]); const list = useSelectTestingResult(); - const { sendQuestion, answer, sendingLoading } = useSendQuestion(checkedList); + const { + sendQuestion, + answer, + sendingLoading, + relatedQuestions, + mindMap, + mindMapLoading, + } = useSendQuestion(checkedList); return ( @@ -56,8 +64,29 @@ const SearchPage = () => { )} /> + {relatedQuestions?.length > 0 && ( + + + {relatedQuestions?.map((x, idx) => ( + + {x} + + ))} + + + )} + +
+ {mindMapLoading ? ( + + ) : ( + + )}
-
diff --git a/web/src/pages/search/sidebar.tsx b/web/src/pages/search/sidebar.tsx index 68f5b6385f..aa99d65713 100644 --- a/web/src/pages/search/sidebar.tsx +++ b/web/src/pages/search/sidebar.tsx @@ -30,20 +30,23 @@ const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => { const indeterminate = checkedList.length > 0 && checkedList.length < list.length; - const onChange = useCallback((list: CheckboxValueType[]) => { - setCheckedList(list as string[]); - }, []); + const onChange = useCallback( + (list: CheckboxValueType[]) => { + setCheckedList(list as string[]); + }, + [setCheckedList], + ); const onCheckAllChange: CheckboxProps['onChange'] = useCallback( (e: CheckboxChangeEvent) => { setCheckedList(e.target.checked ? ids : []); }, - [ids], + [ids, setCheckedList], ); useEffect(() => { setCheckedList(ids); - }, [ids]); + }, [ids, setCheckedList]); return ( @@ -53,7 +56,7 @@ const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => { onChange={onCheckAllChange} checked={checkAll} > - Check all + All (methods, request); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index 635fbe322f..df1cc42d2d 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -68,6 +68,7 @@ export default { tts: `${api_host}/conversation/tts`, ask: `${api_host}/conversation/ask`, mindmap: `${api_host}/conversation/mindmap`, + getRelatedQuestions: `${api_host}/conversation/related_questions`, // chat for external createToken: `${api_host}/api/new_token`, listToken: `${api_host}/api/token_list`,