balibabu commited on
Commit
3927930
·
1 Parent(s): 4eaa0b4

feat: send question with retrieval api #2247 (#2272)

Browse files

### What problem does this PR solve?
feat: send question with retrieval api #2247

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/src/components/image/index.less ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ .image {
2
+ width: 100px;
3
+ object-fit: contain;
4
+ }
5
+
6
+ .imagePreview {
7
+ display: block;
8
+ max-width: 45vw;
9
+ max-height: 40vh;
10
+ }
web/src/components/{image.tsx → image/index.tsx} RENAMED
@@ -1,4 +1,7 @@
1
  import { api_host } from '@/utils/api';
 
 
 
2
 
3
  interface IImage {
4
  id: string;
@@ -17,3 +20,14 @@ const Image = ({ id, className, ...props }: IImage) => {
17
  };
18
 
19
  export default Image;
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { api_host } from '@/utils/api';
2
+ import { Popover } from 'antd';
3
+
4
+ import styles from './index.less';
5
 
6
  interface IImage {
7
  id: string;
 
20
  };
21
 
22
  export default Image;
23
+
24
+ export const ImageWithPopover = ({ id }: { id: string }) => {
25
+ return (
26
+ <Popover
27
+ placement="left"
28
+ content={<Image id={id} className={styles.imagePreview}></Image>}
29
+ >
30
+ <Image id={id} className={styles.image}></Image>
31
+ </Popover>
32
+ );
33
+ };
web/src/hooks/chat-hooks.ts CHANGED
@@ -5,7 +5,10 @@ import {
5
  IStats,
6
  IToken,
7
  } from '@/interfaces/database/chat';
8
- import { IFeedbackRequestBody } from '@/interfaces/request/chat';
 
 
 
9
  import i18n from '@/locales/config';
10
  import { IClientConversation } from '@/pages/chat/interface';
11
  import chatService from '@/services/chat-service';
@@ -477,3 +480,23 @@ export const useFetchNextSharedConversation = (conversationId: string) => {
477
  };
478
 
479
  //#endregion
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  IStats,
6
  IToken,
7
  } from '@/interfaces/database/chat';
8
+ import {
9
+ IAskRequestBody,
10
+ IFeedbackRequestBody,
11
+ } from '@/interfaces/request/chat';
12
  import i18n from '@/locales/config';
13
  import { IClientConversation } from '@/pages/chat/interface';
14
  import chatService from '@/services/chat-service';
 
480
  };
481
 
482
  //#endregion
483
+
484
+ //#region search page
485
+
486
+ export const useFetchMindMap = () => {
487
+ const {
488
+ data,
489
+ isPending: loading,
490
+ mutateAsync,
491
+ } = useMutation({
492
+ mutationKey: ['fetchMindMap'],
493
+ mutationFn: async (params: IAskRequestBody) => {
494
+ const { data } = await chatService.getMindMap(params);
495
+
496
+ return data;
497
+ },
498
+ });
499
+
500
+ return { data, loading, fetchMindMap: mutateAsync };
501
+ };
502
+ //#endregion
web/src/hooks/knowledge-hooks.ts CHANGED
@@ -209,7 +209,7 @@ export const useTestChunkRetrieval = (): ResponsePostType<ITestingResult> & {
209
  mutationFn: async (values: any) => {
210
  const { data } = await kbService.retrieval_test({
211
  ...values,
212
- kb_id: knowledgeBaseId,
213
  page,
214
  size: pageSize,
215
  });
 
209
  mutationFn: async (values: any) => {
210
  const { data } = await kbService.retrieval_test({
211
  ...values,
212
+ kb_id: values.kb_id ?? knowledgeBaseId,
213
  page,
214
  size: pageSize,
215
  });
web/src/hooks/logic-hooks.ts CHANGED
@@ -243,6 +243,10 @@ export const useSendMessageWithSse = (
243
  const x = await reader?.read();
244
  if (x) {
245
  const { done, value } = x;
 
 
 
 
246
  try {
247
  const val = JSON.parse(value?.data || '');
248
  const d = val?.data;
@@ -256,10 +260,6 @@ export const useSendMessageWithSse = (
256
  } catch (e) {
257
  console.warn(e);
258
  }
259
- if (done) {
260
- console.info('done');
261
- break;
262
- }
263
  }
264
  }
265
  console.info('done?');
 
243
  const x = await reader?.read();
244
  if (x) {
245
  const { done, value } = x;
246
+ if (done) {
247
+ console.info('done');
248
+ break;
249
+ }
250
  try {
251
  const val = JSON.parse(value?.data || '');
252
  const d = val?.data;
 
260
  } catch (e) {
261
  console.warn(e);
262
  }
 
 
 
 
263
  }
264
  }
265
  console.info('done?');
web/src/interfaces/database/knowledge.ts CHANGED
@@ -98,6 +98,7 @@ export interface ITestingChunk {
98
  term_similarity: number;
99
  vector: number[];
100
  vector_similarity: number;
 
101
  }
102
 
103
  export interface ITestingDocument {
 
98
  term_similarity: number;
99
  vector: number[];
100
  vector_similarity: number;
101
+ highlight: string;
102
  }
103
 
104
  export interface ITestingDocument {
web/src/interfaces/request/chat.ts CHANGED
@@ -3,3 +3,8 @@ export interface IFeedbackRequestBody {
3
  thumbup?: boolean;
4
  feedback?: string;
5
  }
 
 
 
 
 
 
3
  thumbup?: boolean;
4
  feedback?: string;
5
  }
6
+
7
+ export interface IAskRequestBody {
8
+ questionkb_ids: string;
9
+ kb_ids: string[];
10
+ }
web/src/layouts/components/header/index.tsx CHANGED
@@ -27,9 +27,9 @@ const RagHeader = () => {
27
  () => [
28
  { path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon },
29
  { path: '/chat', name: t('chat'), icon: MessageOutlined },
 
30
  { path: '/flow', name: t('flow'), icon: GraphIcon },
31
  { path: '/file', name: t('fileManager'), icon: FileIcon },
32
- { path: '/search', name: t('search'), icon: FileIcon },
33
  ],
34
  [t],
35
  );
 
27
  () => [
28
  { path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon },
29
  { path: '/chat', name: t('chat'), icon: MessageOutlined },
30
+ // { path: '/search', name: t('search'), icon: SearchOutlined },
31
  { path: '/flow', name: t('flow'), icon: GraphIcon },
32
  { path: '/file', name: t('fileManager'), icon: FileIcon },
 
33
  ],
34
  [t],
35
  );
web/src/pages/search/hooks.ts ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { MessageType } from '@/constants/chat';
2
+ import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks';
3
+ import { useSendMessageWithSse } from '@/hooks/logic-hooks';
4
+ import api from '@/utils/api';
5
+ import { useCallback, useEffect, useMemo, useState } from 'react';
6
+ import { IMessage } from '../chat/interface';
7
+
8
+ export const useSendQuestion = (kbIds: string[]) => {
9
+ const { send, answer, done } = useSendMessageWithSse(api.ask);
10
+ const { testChunk, loading } = useTestChunkRetrieval();
11
+ const [sendingLoading, setSendingLoading] = useState(false);
12
+
13
+ const message: IMessage = useMemo(() => {
14
+ return {
15
+ id: '',
16
+ content: answer.answer,
17
+ role: MessageType.Assistant,
18
+ reference: answer.reference,
19
+ };
20
+ }, [answer]);
21
+
22
+ const sendQuestion = useCallback(
23
+ (question: string) => {
24
+ setSendingLoading(true);
25
+ send({ kb_ids: kbIds, question });
26
+ testChunk({ kb_id: kbIds, highlight: true, question });
27
+ },
28
+ [send, testChunk, kbIds],
29
+ );
30
+
31
+ useEffect(() => {
32
+ if (done) {
33
+ setSendingLoading(false);
34
+ }
35
+ }, [done]);
36
+
37
+ return { sendQuestion, message, loading, sendingLoading };
38
+ };
web/src/pages/search/index.less CHANGED
@@ -1,9 +1,17 @@
 
 
 
 
1
  .searchSide {
2
- height: calc(100vh - 72px);
3
- position: fixed !important;
 
 
 
 
 
 
4
  inset-inline-start: 0;
5
- top: 72px;
6
- bottom: 0;
7
 
8
  .modelForm {
9
  display: flex;
@@ -16,13 +24,29 @@
16
  }
17
  .list {
18
  width: 100%;
19
- height: 100%;
 
20
  overflow: auto;
21
  }
22
  .checkbox {
23
  width: 100%;
24
  }
25
  .knowledgeName {
26
- width: 120px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
28
  }
 
1
+ .searchPage {
2
+ // height: 100%;
3
+ }
4
+
5
  .searchSide {
6
+ // height: calc(100vh - 72px);
7
+ position: relative;
8
+ // position: fixed !important;
9
+ // top: 72px;
10
+ // bottom: 0;
11
+ :global(.ant-layout-sider-children) {
12
+ height: auto;
13
+ }
14
  inset-inline-start: 0;
 
 
15
 
16
  .modelForm {
17
  display: flex;
 
24
  }
25
  .list {
26
  width: 100%;
27
+ // height: 100%;
28
+ height: calc(100vh - 152px);
29
  overflow: auto;
30
  }
31
  .checkbox {
32
  width: 100%;
33
  }
34
  .knowledgeName {
35
+ width: 130px;
36
+ }
37
+ }
38
+
39
+ .content {
40
+ height: 100%;
41
+ .main {
42
+ width: 60%;
43
+ // background-color: aqua;
44
+ overflow: auto;
45
+ padding: 10px;
46
+ }
47
+
48
+ .graph {
49
+ width: 40%;
50
+ background-color: bisque;
51
  }
52
  }
web/src/pages/search/index.tsx CHANGED
@@ -1,37 +1,65 @@
1
- import { Layout } from 'antd';
2
- import React from 'react';
 
 
 
 
 
 
3
  import SearchSidebar from './sidebar';
4
 
5
- const { Header, Content, Footer } = Layout;
 
 
 
6
 
7
  const SearchPage = () => {
 
 
 
 
 
8
  return (
9
- <Layout hasSider>
10
- <SearchSidebar></SearchSidebar>
11
- <Layout style={{ marginInlineStart: 200 }}>
12
- <Header style={{ padding: 0 }} />
13
- <Content style={{ margin: '24px 16px 0', overflow: 'initial' }}>
14
- <div
15
- style={{
16
- padding: 24,
17
- textAlign: 'center',
18
- }}
19
- >
20
- <p>long content</p>
21
- {
22
- // indicates very long content
23
- Array.from({ length: 100 }, (_, index) => (
24
- <React.Fragment key={index}>
25
- {index % 20 === 0 && index ? 'more' : '...'}
26
- <br />
27
- </React.Fragment>
28
- ))
29
- }
30
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  </Content>
32
- <Footer style={{ textAlign: 'center' }}>
33
- Ant Design ©{new Date().getFullYear()} Created by Ant UED
34
- </Footer>
35
  </Layout>
36
  </Layout>
37
  );
 
1
+ import HightLightMarkdown from '@/components/highlight-markdown';
2
+ import { ImageWithPopover } from '@/components/image';
3
+ import MessageItem from '@/components/message-item';
4
+ import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
5
+ import { IReference } from '@/interfaces/database/chat';
6
+ import { Card, Flex, Input, Layout, List, Space } from 'antd';
7
+ import { useState } from 'react';
8
+ import { useSendQuestion } from './hooks';
9
  import SearchSidebar from './sidebar';
10
 
11
+ import styles from './index.less';
12
+
13
+ const { Content } = Layout;
14
+ const { Search } = Input;
15
 
16
  const SearchPage = () => {
17
+ const [checkedList, setCheckedList] = useState<string[]>([]);
18
+ const list = useSelectTestingResult();
19
+ const { sendQuestion, message, sendingLoading } =
20
+ useSendQuestion(checkedList);
21
+
22
  return (
23
+ <Layout className={styles.searchPage}>
24
+ <SearchSidebar
25
+ checkedList={checkedList}
26
+ setCheckedList={setCheckedList}
27
+ ></SearchSidebar>
28
+ <Layout>
29
+ <Content>
30
+ <Flex className={styles.content}>
31
+ <section className={styles.main}>
32
+ <Search
33
+ placeholder="input search text"
34
+ onSearch={sendQuestion}
35
+ size="large"
36
+ />
37
+ <MessageItem
38
+ item={message}
39
+ nickname="You"
40
+ reference={message.reference ?? ({} as IReference)}
41
+ loading={sendingLoading}
42
+ index={0}
43
+ ></MessageItem>
44
+ <List
45
+ dataSource={list.chunks}
46
+ renderItem={(item) => (
47
+ <List.Item>
48
+ <Card>
49
+ <Space>
50
+ <ImageWithPopover id={item.img_id}></ImageWithPopover>
51
+ <HightLightMarkdown>
52
+ {item.highlight}
53
+ </HightLightMarkdown>
54
+ </Space>
55
+ </Card>
56
+ </List.Item>
57
+ )}
58
+ />
59
+ </section>
60
+ <section className={styles.graph}></section>
61
+ </Flex>
62
  </Content>
 
 
 
63
  </Layout>
64
  </Layout>
65
  );
web/src/pages/search/sidebar.tsx CHANGED
@@ -1,20 +1,30 @@
1
  import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
2
  import type { CheckboxProps } from 'antd';
3
- import { Checkbox, Layout, List, Typography } from 'antd';
4
  import { CheckboxValueType } from 'antd/es/checkbox/Group';
5
- import { useCallback, useMemo, useState } from 'react';
 
 
 
 
 
 
6
 
 
7
  import { CheckboxChangeEvent } from 'antd/es/checkbox';
8
  import styles from './index.less';
9
 
10
  const { Sider } = Layout;
11
 
12
- const SearchSidebar = () => {
 
 
 
 
 
13
  const { list } = useNextFetchKnowledgeList();
14
  const ids = useMemo(() => list.map((x) => x.id), [list]);
15
 
16
- const [checkedList, setCheckedList] = useState<string[]>(ids);
17
-
18
  const checkAll = list.length === checkedList.length;
19
 
20
  const indeterminate =
@@ -31,8 +41,12 @@ const SearchSidebar = () => {
31
  [ids],
32
  );
33
 
 
 
 
 
34
  return (
35
- <Sider className={styles.searchSide} theme={'light'} width={260}>
36
  <Checkbox
37
  className={styles.modelForm}
38
  indeterminate={indeterminate}
@@ -53,12 +67,15 @@ const SearchSidebar = () => {
53
  renderItem={(item) => (
54
  <List.Item>
55
  <Checkbox value={item.id} className={styles.checkbox}>
56
- <Typography.Text
57
- ellipsis={{ tooltip: item.name }}
58
- className={styles.knowledgeName}
59
- >
60
- {item.name}
61
- </Typography.Text>
 
 
 
62
  </Checkbox>
63
  </List.Item>
64
  )}
 
1
  import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
2
  import type { CheckboxProps } from 'antd';
3
+ import { Avatar, Checkbox, Layout, List, Space, Typography } from 'antd';
4
  import { CheckboxValueType } from 'antd/es/checkbox/Group';
5
+ import {
6
+ Dispatch,
7
+ SetStateAction,
8
+ useCallback,
9
+ useEffect,
10
+ useMemo,
11
+ } from 'react';
12
 
13
+ import { UserOutlined } from '@ant-design/icons';
14
  import { CheckboxChangeEvent } from 'antd/es/checkbox';
15
  import styles from './index.less';
16
 
17
  const { Sider } = Layout;
18
 
19
+ interface IProps {
20
+ checkedList: string[];
21
+ setCheckedList: Dispatch<SetStateAction<string[]>>;
22
+ }
23
+
24
+ const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => {
25
  const { list } = useNextFetchKnowledgeList();
26
  const ids = useMemo(() => list.map((x) => x.id), [list]);
27
 
 
 
28
  const checkAll = list.length === checkedList.length;
29
 
30
  const indeterminate =
 
41
  [ids],
42
  );
43
 
44
+ useEffect(() => {
45
+ setCheckedList(ids);
46
+ }, [ids]);
47
+
48
  return (
49
+ <Sider className={styles.searchSide} theme={'light'} width={240}>
50
  <Checkbox
51
  className={styles.modelForm}
52
  indeterminate={indeterminate}
 
67
  renderItem={(item) => (
68
  <List.Item>
69
  <Checkbox value={item.id} className={styles.checkbox}>
70
+ <Space>
71
+ <Avatar size={30} icon={<UserOutlined />} src={item.avatar} />
72
+ <Typography.Text
73
+ ellipsis={{ tooltip: item.name }}
74
+ className={styles.knowledgeName}
75
+ >
76
+ {item.name}
77
+ </Typography.Text>
78
+ </Space>
79
  </Checkbox>
80
  </List.Item>
81
  )}
web/src/pages/user-setting/setting-model/index.less CHANGED
@@ -20,6 +20,9 @@
20
  :global(.ant-card-body) {
21
  padding: 10px 24px;
22
  }
 
 
 
23
  }
24
  .addedCard {
25
  border-radius: 18px;
 
20
  :global(.ant-card-body) {
21
  padding: 10px 24px;
22
  }
23
+ .addButton {
24
+ padding: 0;
25
+ }
26
  }
27
  .addedCard {
28
  border-radius: 18px;
web/src/pages/user-setting/setting-model/index.tsx CHANGED
@@ -293,13 +293,20 @@ const UserSettingModel = () => {
293
  children: (
294
  <List
295
  grid={{
296
- gutter: 24,
 
 
 
 
 
 
 
297
  xs: 1,
298
- sm: 2,
299
- md: 3,
300
- lg: 4,
301
  xl: 4,
302
- xxl: 10,
303
  }}
304
  dataSource={factoryList}
305
  renderItem={(item) => (
@@ -315,7 +322,11 @@ const UserSettingModel = () => {
315
  </Flex>
316
  </Flex>
317
  <Divider className={styles.modelDivider}></Divider>
318
- <Button type="link" onClick={() => handleAddModel(item.name)}>
 
 
 
 
319
  {t('addTheModel')}
320
  </Button>
321
  </Card>
 
293
  children: (
294
  <List
295
  grid={{
296
+ gutter: {
297
+ xs: 8,
298
+ sm: 10,
299
+ md: 12,
300
+ lg: 16,
301
+ xl: 20,
302
+ xxl: 24,
303
+ },
304
  xs: 1,
305
+ sm: 1,
306
+ md: 2,
307
+ lg: 3,
308
  xl: 4,
309
+ xxl: 8,
310
  }}
311
  dataSource={factoryList}
312
  renderItem={(item) => (
 
322
  </Flex>
323
  </Flex>
324
  <Divider className={styles.modelDivider}></Divider>
325
+ <Button
326
+ type="link"
327
+ onClick={() => handleAddModel(item.name)}
328
+ className={styles.addButton}
329
+ >
330
  {t('addTheModel')}
331
  </Button>
332
  </Card>
web/src/services/chat-service.ts CHANGED
@@ -23,6 +23,8 @@ const {
23
  deleteMessage,
24
  thumbup,
25
  tts,
 
 
26
  } = api;
27
 
28
  const methods = {
@@ -106,6 +108,14 @@ const methods = {
106
  url: tts,
107
  method: 'post',
108
  },
 
 
 
 
 
 
 
 
109
  } as const;
110
 
111
  const chatService = registerServer<keyof typeof methods>(methods, request);
 
23
  deleteMessage,
24
  thumbup,
25
  tts,
26
+ ask,
27
+ mindmap,
28
  } = api;
29
 
30
  const methods = {
 
108
  url: tts,
109
  method: 'post',
110
  },
111
+ ask: {
112
+ url: ask,
113
+ method: 'post',
114
+ },
115
+ getMindMap: {
116
+ url: mindmap,
117
+ method: 'post',
118
+ },
119
  } as const;
120
 
121
  const chatService = registerServer<keyof typeof methods>(methods, request);
web/src/utils/api.ts CHANGED
@@ -66,6 +66,8 @@ export default {
66
  deleteMessage: `${api_host}/conversation/delete_msg`,
67
  thumbup: `${api_host}/conversation/thumbup`,
68
  tts: `${api_host}/conversation/tts`,
 
 
69
  // chat for external
70
  createToken: `${api_host}/api/new_token`,
71
  listToken: `${api_host}/api/token_list`,
 
66
  deleteMessage: `${api_host}/conversation/delete_msg`,
67
  thumbup: `${api_host}/conversation/thumbup`,
68
  tts: `${api_host}/conversation/tts`,
69
+ ask: `${api_host}/conversation/ask`,
70
+ mindmap: `${api_host}/conversation/mindmap`,
71
  // chat for external
72
  createToken: `${api_host}/api/new_token`,
73
  listToken: `${api_host}/api/token_list`,