balibabu commited on
Commit
8c4ec99
·
1 Parent(s): a8294f2

feat: create a chat assistant and extract SimilaritySlider (#67)

Browse files

* feat: extract SimilaritySlider

* feat: create a chat assistant

web/src/assets/svg/chat-app-cube.svg ADDED
web/src/components/similarity-slider/index.tsx ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Form, Slider } from 'antd';
2
+
3
+ type FieldType = {
4
+ similarity_threshold?: number;
5
+ vector_similarity_weight?: number;
6
+ };
7
+
8
+ const SimilaritySlider = () => {
9
+ return (
10
+ <>
11
+ <Form.Item<FieldType>
12
+ label="Similarity threshold"
13
+ name={'similarity_threshold'}
14
+ initialValue={0}
15
+ >
16
+ <Slider max={1} step={0.01} />
17
+ </Form.Item>
18
+ <Form.Item<FieldType>
19
+ label="Vector similarity weight"
20
+ name={'vector_similarity_weight'}
21
+ initialValue={0}
22
+ >
23
+ <Slider max={1} step={0.01} />
24
+ </Form.Item>
25
+ </>
26
+ );
27
+ };
28
+
29
+ export default SimilaritySlider;
web/src/constants/knowledge.ts CHANGED
@@ -11,3 +11,40 @@ export enum RunningStatus {
11
  DONE = '3', // need to refresh
12
  FAIL = '4', // need to refresh
13
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  DONE = '3', // need to refresh
12
  FAIL = '4', // need to refresh
13
  }
14
+
15
+ export enum ModelVariableType {
16
+ Improvise = 'Improvise',
17
+ Precise = 'Precise',
18
+ Balance = 'Balance',
19
+ }
20
+
21
+ export const settledModelVariableMap = {
22
+ [ModelVariableType.Improvise]: {
23
+ temperature: 0.9,
24
+ top_p: 0.9,
25
+ frequency_penalty: 0.2,
26
+ presence_penalty: 0.4,
27
+ max_tokens: 512,
28
+ },
29
+ [ModelVariableType.Precise]: {
30
+ temperature: 0.1,
31
+ top_p: 0.3,
32
+ frequency_penalty: 0.7,
33
+ presence_penalty: 0.4,
34
+ max_tokens: 215,
35
+ },
36
+ [ModelVariableType.Balance]: {
37
+ temperature: 0.5,
38
+ top_p: 0.5,
39
+ frequency_penalty: 0.7,
40
+ presence_penalty: 0.4,
41
+ max_tokens: 215,
42
+ },
43
+ };
44
+
45
+ export enum LlmModelType {
46
+ Embedding = 'embedding',
47
+ Chat = 'chat',
48
+ Image2text = 'image2text',
49
+ Speech2text = 'speech2text',
50
+ }
web/src/hooks/knowledgeHook.ts CHANGED
@@ -124,3 +124,22 @@ export const useFetchKnowledgeBaseConfiguration = () => {
124
  fetchKnowledgeBaseConfiguration();
125
  }, [fetchKnowledgeBaseConfiguration]);
126
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  fetchKnowledgeBaseConfiguration();
125
  }, [fetchKnowledgeBaseConfiguration]);
126
  };
127
+
128
+ export const useFetchKnowledgeList = (): IKnowledge[] => {
129
+ const dispatch = useDispatch();
130
+
131
+ const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
132
+ const { data = [] } = knowledgeModel;
133
+
134
+ const fetchList = useCallback(() => {
135
+ dispatch({
136
+ type: 'knowledgeModel/getList',
137
+ });
138
+ }, [dispatch]);
139
+
140
+ useEffect(() => {
141
+ fetchList();
142
+ }, [fetchList]);
143
+
144
+ return data;
145
+ };
web/src/hooks/llmHooks.ts ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { LlmModelType } from '@/constants/knowledge';
2
+ import { IThirdOAIModelCollection } from '@/interfaces/database/llm';
3
+ import { useCallback, useEffect, useMemo } from 'react';
4
+ import { useDispatch, useSelector } from 'umi';
5
+
6
+ export const useFetchLlmList = (modelType: LlmModelType) => {
7
+ const dispatch = useDispatch();
8
+
9
+ const fetchLlmList = useCallback(() => {
10
+ dispatch({
11
+ type: 'settingModel/llm_list',
12
+ payload: { model_type: modelType },
13
+ });
14
+ }, [dispatch, modelType]);
15
+
16
+ useEffect(() => {
17
+ fetchLlmList();
18
+ }, [fetchLlmList]);
19
+ };
20
+
21
+ export const useSelectLlmOptions = () => {
22
+ const llmInfo: IThirdOAIModelCollection = useSelector(
23
+ (state: any) => state.settingModel.llmInfo,
24
+ );
25
+
26
+ const embeddingModelOptions = useMemo(() => {
27
+ return Object.entries(llmInfo).map(([key, value]) => {
28
+ return {
29
+ label: key,
30
+ options: value.map((x) => ({
31
+ label: x.llm_name,
32
+ value: x.llm_name,
33
+ })),
34
+ };
35
+ });
36
+ }, [llmInfo]);
37
+
38
+ return embeddingModelOptions;
39
+ };
web/src/interfaces/database/chat.ts ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface PromptConfig {
2
+ empty_response: string;
3
+ parameters: Parameter[];
4
+ prologue: string;
5
+ system: string;
6
+ }
7
+
8
+ export interface Parameter {
9
+ key: string;
10
+ optional: boolean;
11
+ }
12
+
13
+ export interface LlmSetting {
14
+ Creative: Variable;
15
+ Custom: Variable;
16
+ Evenly: Variable;
17
+ Precise: Variable;
18
+ }
19
+
20
+ export interface Variable {
21
+ frequency_penalty: number;
22
+ max_tokens: number;
23
+ presence_penalty: number;
24
+ temperature: number;
25
+ top_p: number;
26
+ }
27
+
28
+ export interface IDialog {
29
+ create_date: string;
30
+ create_time: number;
31
+ description: string;
32
+ icon: string;
33
+ id: string;
34
+ kb_ids: string[];
35
+ kb_names: string[];
36
+ language: string;
37
+ llm_id: string;
38
+ llm_setting: LlmSetting;
39
+ llm_setting_type: string;
40
+ name: string;
41
+ prompt_config: PromptConfig;
42
+ prompt_type: string;
43
+ status: string;
44
+ tenant_id: string;
45
+ update_date: string;
46
+ update_time: number;
47
+ }
web/src/pages/add-knowledge/components/knowledge-file/rename-modal/index.tsx CHANGED
@@ -53,7 +53,7 @@ const RenameModal = () => {
53
 
54
  useEffect(() => {
55
  form.setFieldValue('name', initialName);
56
- }, [initialName, documentId]);
57
 
58
  return (
59
  <Modal
 
53
 
54
  useEffect(() => {
55
  form.setFieldValue('name', initialName);
56
+ }, [initialName, documentId, form]);
57
 
58
  return (
59
  <Modal
web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx CHANGED
@@ -17,13 +17,14 @@ import {
17
  UploadFile,
18
  } from 'antd';
19
  import pick from 'lodash/pick';
20
- import { useCallback, useEffect, useMemo } from 'react';
21
  import { useDispatch, useSelector } from 'umi';
22
 
 
23
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
24
  import { IKnowledge } from '@/interfaces/database/knowledge';
25
- import { IThirdOAIModelCollection } from '@/interfaces/database/llm';
26
  import { PlusOutlined } from '@ant-design/icons';
 
27
  import styles from './index.less';
28
 
29
  const { Title } = Typography;
@@ -35,9 +36,6 @@ const Configuration = () => {
35
  const knowledgeBaseId = useKnowledgeBaseId();
36
  const loading = useOneNamespaceEffectsLoading('kSModel', ['updateKb']);
37
 
38
- const llmInfo: IThirdOAIModelCollection = useSelector(
39
- (state: any) => state.settingModel.llmInfo,
40
- );
41
  const knowledgeDetails: IKnowledge = useSelector(
42
  (state: any) => state.kSModel.knowledgeDetails,
43
  );
@@ -51,17 +49,7 @@ const Configuration = () => {
51
 
52
  const parserList = useSelectParserList();
53
 
54
- const embeddingModelOptions = useMemo(() => {
55
- return Object.entries(llmInfo).map(([key, value]) => {
56
- return {
57
- label: key,
58
- options: value.map((x) => ({
59
- label: x.llm_name,
60
- value: x.llm_name,
61
- })),
62
- };
63
- });
64
- }, [llmInfo]);
65
 
66
  const onFinish = async (values: any) => {
67
  console.info(values);
@@ -86,13 +74,6 @@ const Configuration = () => {
86
  console.log('Failed:', errorInfo);
87
  };
88
 
89
- const fetchLlmList = useCallback(() => {
90
- dispatch({
91
- type: 'settingModel/llm_list',
92
- payload: { model_type: 'embedding' },
93
- });
94
- }, [dispatch]);
95
-
96
  useEffect(() => {
97
  const avatar = knowledgeDetails.avatar;
98
  let fileList: UploadFile[] = [];
@@ -115,9 +96,7 @@ const Configuration = () => {
115
  useFetchParserList();
116
  useFetchKnowledgeBaseConfiguration();
117
 
118
- useEffect(() => {
119
- fetchLlmList();
120
- }, [fetchLlmList]);
121
 
122
  return (
123
  <div className={styles.configurationWrapper}>
 
17
  UploadFile,
18
  } from 'antd';
19
  import pick from 'lodash/pick';
20
+ import { useEffect } from 'react';
21
  import { useDispatch, useSelector } from 'umi';
22
 
23
+ import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
24
  import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
25
  import { IKnowledge } from '@/interfaces/database/knowledge';
 
26
  import { PlusOutlined } from '@ant-design/icons';
27
+ import { LlmModelType } from '../../constant';
28
  import styles from './index.less';
29
 
30
  const { Title } = Typography;
 
36
  const knowledgeBaseId = useKnowledgeBaseId();
37
  const loading = useOneNamespaceEffectsLoading('kSModel', ['updateKb']);
38
 
 
 
 
39
  const knowledgeDetails: IKnowledge = useSelector(
40
  (state: any) => state.kSModel.knowledgeDetails,
41
  );
 
49
 
50
  const parserList = useSelectParserList();
51
 
52
+ const embeddingModelOptions = useSelectLlmOptions();
 
 
 
 
 
 
 
 
 
 
53
 
54
  const onFinish = async (values: any) => {
55
  console.info(values);
 
74
  console.log('Failed:', errorInfo);
75
  };
76
 
 
 
 
 
 
 
 
77
  useEffect(() => {
78
  const avatar = knowledgeDetails.avatar;
79
  let fileList: UploadFile[] = [];
 
96
  useFetchParserList();
97
  useFetchKnowledgeBaseConfiguration();
98
 
99
+ useFetchLlmList(LlmModelType.Embedding);
 
 
100
 
101
  return (
102
  <div className={styles.configurationWrapper}>
web/src/pages/add-knowledge/components/knowledge-testing/index.tsx CHANGED
@@ -16,14 +16,10 @@ const KnowledgeTesting = () => {
16
  const handleTesting = async () => {
17
  const values = await form.validateFields();
18
  console.info(values);
19
- const similarity_threshold = values.similarity_threshold / 100;
20
- const vector_similarity_weight = values.vector_similarity_weight / 100;
21
  dispatch({
22
  type: 'testingModel/testDocumentChunk',
23
  payload: {
24
  ...values,
25
- similarity_threshold,
26
- vector_similarity_weight,
27
  kb_id: knowledgeBaseId,
28
  },
29
  });
 
16
  const handleTesting = async () => {
17
  const values = await form.validateFields();
18
  console.info(values);
 
 
19
  dispatch({
20
  type: 'testingModel/testDocumentChunk',
21
  payload: {
22
  ...values,
 
 
23
  kb_id: knowledgeBaseId,
24
  },
25
  });
web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import {
2
  Button,
3
  Card,
@@ -6,22 +8,15 @@ import {
6
  Form,
7
  Input,
8
  Slider,
9
- SliderSingleProps,
10
  Space,
11
  Tag,
12
  } from 'antd';
13
-
14
- import { DeleteOutlined, HistoryOutlined } from '@ant-design/icons';
15
  import { FormInstance } from 'antd/lib';
 
16
  import styles from './index.less';
17
 
18
  const list = [1, 2, 3];
19
 
20
- const marks: SliderSingleProps['marks'] = {
21
- 0: '0',
22
- 100: '1',
23
- };
24
-
25
  type FieldType = {
26
  similarity_threshold?: number;
27
  vector_similarity_weight?: number;
@@ -29,12 +24,6 @@ type FieldType = {
29
  question: string;
30
  };
31
 
32
- const formatter = (value: number | undefined) => {
33
- return typeof value === 'number' ? value / 100 : 0;
34
- };
35
-
36
- const tooltip = { formatter };
37
-
38
  interface IProps {
39
  form: FormInstance;
40
  handleTesting: () => Promise<any>;
@@ -59,23 +48,12 @@ const TestingControl = ({ form, handleTesting }: IProps) => {
59
  layout="vertical"
60
  form={form}
61
  initialValues={{
62
- similarity_threshold: 20,
63
- vector_similarity_weight: 30,
64
  top_k: 1024,
65
  }}
66
  >
67
- <Form.Item<FieldType>
68
- label="Similarity threshold"
69
- name={'similarity_threshold'}
70
- >
71
- <Slider marks={marks} defaultValue={0} tooltip={tooltip} />
72
- </Form.Item>
73
- <Form.Item<FieldType>
74
- label="Vector similarity weight"
75
- name={'vector_similarity_weight'}
76
- >
77
- <Slider marks={marks} defaultValue={0} tooltip={tooltip} />
78
- </Form.Item>
79
  <Form.Item<FieldType> label="Top k" name={'top_k'}>
80
  <Slider marks={{ 0: 0, 2048: 2048 }} defaultValue={0} max={2048} />
81
  </Form.Item>
 
1
+ import SimilaritySlider from '@/components/similarity-slider';
2
+ import { DeleteOutlined, HistoryOutlined } from '@ant-design/icons';
3
  import {
4
  Button,
5
  Card,
 
8
  Form,
9
  Input,
10
  Slider,
 
11
  Space,
12
  Tag,
13
  } from 'antd';
 
 
14
  import { FormInstance } from 'antd/lib';
15
+
16
  import styles from './index.less';
17
 
18
  const list = [1, 2, 3];
19
 
 
 
 
 
 
20
  type FieldType = {
21
  similarity_threshold?: number;
22
  vector_similarity_weight?: number;
 
24
  question: string;
25
  };
26
 
 
 
 
 
 
 
27
  interface IProps {
28
  form: FormInstance;
29
  handleTesting: () => Promise<any>;
 
48
  layout="vertical"
49
  form={form}
50
  initialValues={{
51
+ similarity_threshold: 0.2,
52
+ vector_similarity_weight: 0.3,
53
  top_k: 1024,
54
  }}
55
  >
56
+ <SimilaritySlider></SimilaritySlider>
 
 
 
 
 
 
 
 
 
 
 
57
  <Form.Item<FieldType> label="Top k" name={'top_k'}>
58
  <Slider marks={{ 0: 0, 2048: 2048 }} defaultValue={0} max={2048} />
59
  </Form.Item>
web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx CHANGED
@@ -1,11 +1,20 @@
1
- import { Form, Input } from 'antd';
2
 
3
  import classNames from 'classnames';
4
  import { ISegmentedContentProps } from './interface';
5
 
 
6
  import styles from './index.less';
7
 
 
 
8
  const AssistantSetting = ({ show }: ISegmentedContentProps) => {
 
 
 
 
 
 
9
  return (
10
  <section
11
  className={classNames({
@@ -19,15 +28,43 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
19
  >
20
  <Input placeholder="e.g. Resume Jarvis" />
21
  </Form.Item>
22
- <Form.Item name={'avatar'} label="Assistant avatar">
23
  <Input />
24
  </Form.Item>
25
- <Form.Item name={'keywords'} label="Keywords">
26
- <Input.TextArea autoSize={{ minRows: 3 }} />
 
 
 
 
 
 
 
 
 
 
 
27
  </Form.Item>
28
- <Form.Item name={'opener'} label="Set an opener">
29
  <Input.TextArea autoSize={{ minRows: 5 }} />
30
  </Form.Item>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  </section>
32
  );
33
  };
 
1
+ import { Form, Input, Select } from 'antd';
2
 
3
  import classNames from 'classnames';
4
  import { ISegmentedContentProps } from './interface';
5
 
6
+ import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
7
  import styles from './index.less';
8
 
9
+ const { Option } = Select;
10
+
11
  const AssistantSetting = ({ show }: ISegmentedContentProps) => {
12
+ const knowledgeList = useFetchKnowledgeList();
13
+ const knowledgeOptions = knowledgeList.map((x) => ({
14
+ label: x.name,
15
+ value: x.id,
16
+ }));
17
+
18
  return (
19
  <section
20
  className={classNames({
 
28
  >
29
  <Input placeholder="e.g. Resume Jarvis" />
30
  </Form.Item>
31
+ <Form.Item name={'icon'} label="Assistant avatar">
32
  <Input />
33
  </Form.Item>
34
+ <Form.Item name={'language'} label="Language" initialValue={'Chinese'}>
35
+ <Select
36
+ options={[
37
+ { value: 'Chinese', label: 'Chinese' },
38
+ { value: 'English', label: 'English' },
39
+ ]}
40
+ />
41
+ </Form.Item>
42
+ <Form.Item
43
+ name={['prompt_config', 'empty_response']}
44
+ label="Empty response"
45
+ >
46
+ <Input placeholder="" />
47
  </Form.Item>
48
+ <Form.Item name={['prompt_config', 'prologue']} label="Set an opener">
49
  <Input.TextArea autoSize={{ minRows: 5 }} />
50
  </Form.Item>
51
+ <Form.Item
52
+ label="Select one context"
53
+ name="kb_ids"
54
+ rules={[
55
+ {
56
+ required: true,
57
+ message: 'Please select!',
58
+ type: 'array',
59
+ },
60
+ ]}
61
+ >
62
+ <Select
63
+ mode="multiple"
64
+ options={knowledgeOptions}
65
+ placeholder="Please select"
66
+ ></Select>
67
+ </Form.Item>
68
  </section>
69
  );
70
  };
web/src/pages/chat/chat-configuration-modal/constants.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ export const variableEnabledFieldMap = {
2
+ temperatureEnabled: 'temperature',
3
+ topPEnabled: 'top_p',
4
+ presencePenaltyEnabled: 'presence_penalty',
5
+ frequencyPenaltyEnabled: 'frequency_penalty',
6
+ maxTokensEnabled: 'max_tokens',
7
+ };
web/src/pages/chat/chat-configuration-modal/index.less CHANGED
@@ -41,3 +41,10 @@
41
  width: 0;
42
  margin: 0;
43
  }
 
 
 
 
 
 
 
 
41
  width: 0;
42
  margin: 0;
43
  }
44
+
45
+ .sliderInputNumber {
46
+ width: 80px;
47
+ }
48
+ .variableSlider {
49
+ width: 100%;
50
+ }
web/src/pages/chat/chat-configuration-modal/index.tsx CHANGED
@@ -2,17 +2,20 @@ import { ReactComponent as ChatConfigurationAtom } from '@/assets/svg/chat-confi
2
  import { IModalManagerChildrenProps } from '@/components/modal-manager';
3
  import { Divider, Flex, Form, Modal, Segmented } from 'antd';
4
  import { SegmentedValue } from 'antd/es/segmented';
5
- import { useState } from 'react';
 
6
  import AssistantSetting from './assistant-setting';
7
  import ModelSetting from './model-setting';
8
  import PromptEngine from './prompt-engine';
9
 
 
 
10
  import styles from './index.less';
11
 
12
  enum ConfigurationSegmented {
13
  AssistantSetting = 'Assistant Setting',
14
- ModelSetting = 'Model Setting',
15
  PromptEngine = 'Prompt Engine',
 
16
  }
17
 
18
  const segmentedMap = {
@@ -45,10 +48,24 @@ const ChatConfigurationModal = ({
45
  const [value, setValue] = useState<ConfigurationSegmented>(
46
  ConfigurationSegmented.AssistantSetting,
47
  );
 
 
 
48
 
49
  const handleOk = async () => {
50
- const x = await form.validateFields();
51
- console.info(x);
 
 
 
 
 
 
 
 
 
 
 
52
  };
53
 
54
  const handleCancel = () => {
@@ -97,7 +114,14 @@ const ChatConfigurationModal = ({
97
  colon={false}
98
  >
99
  {Object.entries(segmentedMap).map(([key, Element]) => (
100
- <Element key={key} show={key === value}></Element>
 
 
 
 
 
 
 
101
  ))}
102
  </Form>
103
  </Modal>
 
2
  import { IModalManagerChildrenProps } from '@/components/modal-manager';
3
  import { Divider, Flex, Form, Modal, Segmented } from 'antd';
4
  import { SegmentedValue } from 'antd/es/segmented';
5
+ import omit from 'lodash/omit';
6
+ import { useRef, useState } from 'react';
7
  import AssistantSetting from './assistant-setting';
8
  import ModelSetting from './model-setting';
9
  import PromptEngine from './prompt-engine';
10
 
11
+ import { useSetDialog } from '../hooks';
12
+ import { variableEnabledFieldMap } from './constants';
13
  import styles from './index.less';
14
 
15
  enum ConfigurationSegmented {
16
  AssistantSetting = 'Assistant Setting',
 
17
  PromptEngine = 'Prompt Engine',
18
+ ModelSetting = 'Model Setting',
19
  }
20
 
21
  const segmentedMap = {
 
48
  const [value, setValue] = useState<ConfigurationSegmented>(
49
  ConfigurationSegmented.AssistantSetting,
50
  );
51
+ const promptEngineRef = useRef(null);
52
+
53
+ const setDialog = useSetDialog();
54
 
55
  const handleOk = async () => {
56
+ const values = await form.validateFields();
57
+ const nextValues: any = omit(values, Object.keys(variableEnabledFieldMap));
58
+ const finalValues = {
59
+ ...nextValues,
60
+ prompt_config: {
61
+ ...nextValues.prompt_config,
62
+ parameters: promptEngineRef.current,
63
+ },
64
+ };
65
+ console.info(promptEngineRef.current);
66
+ console.info(nextValues);
67
+ console.info(finalValues);
68
+ setDialog(finalValues);
69
  };
70
 
71
  const handleCancel = () => {
 
114
  colon={false}
115
  >
116
  {Object.entries(segmentedMap).map(([key, Element]) => (
117
+ <Element
118
+ key={key}
119
+ show={key === value}
120
+ form={form}
121
+ {...(key === ConfigurationSegmented.PromptEngine
122
+ ? { ref: promptEngineRef }
123
+ : {})}
124
+ ></Element>
125
  ))}
126
  </Form>
127
  </Modal>
web/src/pages/chat/chat-configuration-modal/interface.ts CHANGED
@@ -1,3 +1,14 @@
 
 
1
  export interface ISegmentedContentProps {
2
  show: boolean;
 
 
 
 
 
 
 
 
 
3
  }
 
1
+ import { FormInstance } from 'antd';
2
+
3
  export interface ISegmentedContentProps {
4
  show: boolean;
5
+ form: FormInstance;
6
+ }
7
+
8
+ export interface IVariable {
9
+ temperature: number;
10
+ top_p: number;
11
+ frequency_penalty: number;
12
+ presence_penalty: number;
13
+ max_tokens: number;
14
  }
web/src/pages/chat/chat-configuration-modal/model-setting.tsx CHANGED
@@ -1,12 +1,48 @@
1
- import { Divider, Flex, Form, InputNumber, Select, Slider } from 'antd';
 
 
 
 
 
2
  import classNames from 'classnames';
 
3
  import { ISegmentedContentProps } from './interface';
4
 
 
 
5
  import styles from './index.less';
6
 
7
- const { Option } = Select;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- const ModelSetting = ({ show }: ISegmentedContentProps) => {
10
  return (
11
  <section
12
  className={classNames({
@@ -15,135 +51,170 @@ const ModelSetting = ({ show }: ISegmentedContentProps) => {
15
  >
16
  <Form.Item
17
  label="Model"
18
- name="model"
19
- // rules={[{ required: true, message: 'Please input!' }]}
20
  >
21
- <Select />
22
  </Form.Item>
23
  <Divider></Divider>
24
  <Form.Item
25
  label="Parameters"
26
  name="parameters"
 
27
  // rules={[{ required: true, message: 'Please input!' }]}
28
  >
29
- <Select />
 
 
 
30
  </Form.Item>
31
- <Form.Item label="Temperature">
32
- <Flex gap={20}>
 
 
 
 
 
 
 
33
  <Flex flex={1}>
34
  <Form.Item
35
- name={['address', 'province']}
36
  noStyle
37
  rules={[{ required: true, message: 'Province is required' }]}
38
  >
39
- <Slider style={{ display: 'inline-block', width: '100%' }} />
40
  </Form.Item>
41
  </Flex>
42
  <Form.Item
43
- name={['address', 'street']}
44
  noStyle
45
  rules={[{ required: true, message: 'Street is required' }]}
46
  >
47
  <InputNumber
48
- style={{
49
- width: 50,
50
- }}
 
51
  />
52
  </Form.Item>
53
  </Flex>
54
  </Form.Item>
55
- <Form.Item label="Top P">
56
- <Flex gap={20}>
 
 
 
57
  <Flex flex={1}>
58
  <Form.Item
59
- name={['address', 'province']}
60
  noStyle
61
  rules={[{ required: true, message: 'Province is required' }]}
62
  >
63
- <Slider style={{ display: 'inline-block', width: '100%' }} />
64
  </Form.Item>
65
  </Flex>
66
  <Form.Item
67
- name={['address', 'street']}
68
  noStyle
69
  rules={[{ required: true, message: 'Street is required' }]}
70
  >
71
  <InputNumber
72
- style={{
73
- width: 50,
74
- }}
 
75
  />
76
  </Form.Item>
77
  </Flex>
78
  </Form.Item>
79
- <Form.Item label="Presence Penalty">
80
- <Flex gap={20}>
 
 
 
 
 
 
 
81
  <Flex flex={1}>
82
  <Form.Item
83
- name={['address', 'province']}
84
  noStyle
85
  rules={[{ required: true, message: 'Province is required' }]}
86
  >
87
- <Slider style={{ display: 'inline-block', width: '100%' }} />
88
  </Form.Item>
89
  </Flex>
90
  <Form.Item
91
- name={['address', 'street']}
92
  noStyle
93
  rules={[{ required: true, message: 'Street is required' }]}
94
  >
95
  <InputNumber
96
- style={{
97
- width: 50,
98
- }}
 
99
  />
100
  </Form.Item>
101
  </Flex>
102
  </Form.Item>
103
- <Form.Item label="Frequency Penalty">
104
- <Flex gap={20}>
 
 
 
 
 
 
 
105
  <Flex flex={1}>
106
  <Form.Item
107
- name={['address', 'province']}
108
  noStyle
109
  rules={[{ required: true, message: 'Province is required' }]}
110
  >
111
- <Slider style={{ display: 'inline-block', width: '100%' }} />
112
  </Form.Item>
113
  </Flex>
114
  <Form.Item
115
- name={['address', 'street']}
116
  noStyle
117
  rules={[{ required: true, message: 'Street is required' }]}
118
  >
119
  <InputNumber
120
- style={{
121
- width: 50,
122
- }}
 
123
  />
124
  </Form.Item>
125
  </Flex>
126
  </Form.Item>
127
- <Form.Item label="Max Tokens">
128
- <Flex gap={20}>
 
 
 
129
  <Flex flex={1}>
130
  <Form.Item
131
- name={['address', 'province']}
132
  noStyle
133
  rules={[{ required: true, message: 'Province is required' }]}
134
  >
135
- <Slider style={{ display: 'inline-block', width: '100%' }} />
136
  </Form.Item>
137
  </Flex>
138
  <Form.Item
139
- name={['address', 'street']}
140
  noStyle
141
  rules={[{ required: true, message: 'Street is required' }]}
142
  >
143
  <InputNumber
144
- style={{
145
- width: 50,
146
- }}
147
  />
148
  </Form.Item>
149
  </Flex>
 
1
+ import {
2
+ LlmModelType,
3
+ ModelVariableType,
4
+ settledModelVariableMap,
5
+ } from '@/constants/knowledge';
6
+ import { Divider, Flex, Form, InputNumber, Select, Slider, Switch } from 'antd';
7
  import classNames from 'classnames';
8
+ import { useEffect } from 'react';
9
  import { ISegmentedContentProps } from './interface';
10
 
11
+ import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
12
+ import { variableEnabledFieldMap } from './constants';
13
  import styles from './index.less';
14
 
15
+ const ModelSetting = ({ show, form }: ISegmentedContentProps) => {
16
+ const parameterOptions = Object.values(ModelVariableType).map((x) => ({
17
+ label: x,
18
+ value: x,
19
+ }));
20
+
21
+ const parameters: ModelVariableType = Form.useWatch('parameters', form);
22
+
23
+ const modelOptions = useSelectLlmOptions();
24
+
25
+ const handleParametersChange = (value: ModelVariableType) => {
26
+ console.info(value);
27
+ };
28
+
29
+ useEffect(() => {
30
+ const variable = settledModelVariableMap[parameters];
31
+ form.setFieldsValue({ llm_setting: variable });
32
+ }, [parameters, form]);
33
+
34
+ useEffect(() => {
35
+ const values = Object.keys(variableEnabledFieldMap).reduce<
36
+ Record<string, boolean>
37
+ >((pre, field) => {
38
+ pre[field] = true;
39
+ return pre;
40
+ }, {});
41
+ form.setFieldsValue(values);
42
+ }, [form]);
43
+
44
+ useFetchLlmList(LlmModelType.Chat);
45
 
 
46
  return (
47
  <section
48
  className={classNames({
 
51
  >
52
  <Form.Item
53
  label="Model"
54
+ name="llm_id"
55
+ rules={[{ required: true, message: 'Please select!' }]}
56
  >
57
+ <Select options={modelOptions} />
58
  </Form.Item>
59
  <Divider></Divider>
60
  <Form.Item
61
  label="Parameters"
62
  name="parameters"
63
+ initialValue={ModelVariableType.Precise}
64
  // rules={[{ required: true, message: 'Please input!' }]}
65
  >
66
+ <Select<ModelVariableType>
67
+ options={parameterOptions}
68
+ onChange={handleParametersChange}
69
+ />
70
  </Form.Item>
71
+ <Form.Item label="Temperature" tooltip={'xx'}>
72
+ <Flex gap={20} align="center">
73
+ <Form.Item
74
+ name={'temperatureEnabled'}
75
+ valuePropName="checked"
76
+ noStyle
77
+ >
78
+ <Switch size="small" />
79
+ </Form.Item>
80
  <Flex flex={1}>
81
  <Form.Item
82
+ name={['llm_setting', 'temperature']}
83
  noStyle
84
  rules={[{ required: true, message: 'Province is required' }]}
85
  >
86
+ <Slider className={styles.variableSlider} max={1} step={0.01} />
87
  </Form.Item>
88
  </Flex>
89
  <Form.Item
90
+ name={['llm_setting', 'temperature']}
91
  noStyle
92
  rules={[{ required: true, message: 'Street is required' }]}
93
  >
94
  <InputNumber
95
+ className={styles.sliderInputNumber}
96
+ max={1}
97
+ min={0}
98
+ step={0.01}
99
  />
100
  </Form.Item>
101
  </Flex>
102
  </Form.Item>
103
+ <Form.Item label="Top P" tooltip={'xx'}>
104
+ <Flex gap={20} align="center">
105
+ <Form.Item name={'topPEnabled'} valuePropName="checked" noStyle>
106
+ <Switch size="small" />
107
+ </Form.Item>
108
  <Flex flex={1}>
109
  <Form.Item
110
+ name={['llm_setting', 'top_p']}
111
  noStyle
112
  rules={[{ required: true, message: 'Province is required' }]}
113
  >
114
+ <Slider className={styles.variableSlider} max={1} step={0.01} />
115
  </Form.Item>
116
  </Flex>
117
  <Form.Item
118
+ name={['llm_setting', 'top_p']}
119
  noStyle
120
  rules={[{ required: true, message: 'Street is required' }]}
121
  >
122
  <InputNumber
123
+ className={styles.sliderInputNumber}
124
+ max={1}
125
+ min={0}
126
+ step={0.01}
127
  />
128
  </Form.Item>
129
  </Flex>
130
  </Form.Item>
131
+ <Form.Item label="Presence Penalty" tooltip={'xx'}>
132
+ <Flex gap={20} align="center">
133
+ <Form.Item
134
+ name={'presencePenaltyEnabled'}
135
+ valuePropName="checked"
136
+ noStyle
137
+ >
138
+ <Switch size="small" />
139
+ </Form.Item>
140
  <Flex flex={1}>
141
  <Form.Item
142
+ name={['llm_setting', 'presence_penalty']}
143
  noStyle
144
  rules={[{ required: true, message: 'Province is required' }]}
145
  >
146
+ <Slider className={styles.variableSlider} max={1} step={0.01} />
147
  </Form.Item>
148
  </Flex>
149
  <Form.Item
150
+ name={['llm_setting', 'presence_penalty']}
151
  noStyle
152
  rules={[{ required: true, message: 'Street is required' }]}
153
  >
154
  <InputNumber
155
+ className={styles.sliderInputNumber}
156
+ max={1}
157
+ min={0}
158
+ step={0.01}
159
  />
160
  </Form.Item>
161
  </Flex>
162
  </Form.Item>
163
+ <Form.Item label="Frequency Penalty" tooltip={'xx'}>
164
+ <Flex gap={20} align="center">
165
+ <Form.Item
166
+ name={'frequencyPenaltyEnabled'}
167
+ valuePropName="checked"
168
+ noStyle
169
+ >
170
+ <Switch size="small" />
171
+ </Form.Item>
172
  <Flex flex={1}>
173
  <Form.Item
174
+ name={['llm_setting', 'frequency_penalty']}
175
  noStyle
176
  rules={[{ required: true, message: 'Province is required' }]}
177
  >
178
+ <Slider className={styles.variableSlider} max={1} step={0.01} />
179
  </Form.Item>
180
  </Flex>
181
  <Form.Item
182
+ name={['llm_setting', 'frequency_penalty']}
183
  noStyle
184
  rules={[{ required: true, message: 'Street is required' }]}
185
  >
186
  <InputNumber
187
+ className={styles.sliderInputNumber}
188
+ max={1}
189
+ min={0}
190
+ step={0.01}
191
  />
192
  </Form.Item>
193
  </Flex>
194
  </Form.Item>
195
+ <Form.Item label="Max Tokens" tooltip={'xx'}>
196
+ <Flex gap={20} align="center">
197
+ <Form.Item name={'maxTokensEnabled'} valuePropName="checked" noStyle>
198
+ <Switch size="small" />
199
+ </Form.Item>
200
  <Flex flex={1}>
201
  <Form.Item
202
+ name={['llm_setting', 'max_tokens']}
203
  noStyle
204
  rules={[{ required: true, message: 'Province is required' }]}
205
  >
206
+ <Slider className={styles.variableSlider} max={2048} />
207
  </Form.Item>
208
  </Flex>
209
  <Form.Item
210
+ name={['llm_setting', 'max_tokens']}
211
  noStyle
212
  rules={[{ required: true, message: 'Street is required' }]}
213
  >
214
  <InputNumber
215
+ className={styles.sliderInputNumber}
216
+ max={2048}
217
+ min={0}
218
  />
219
  </Form.Item>
220
  </Flex>
web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx CHANGED
@@ -1,3 +1,4 @@
 
1
  import { DeleteOutlined } from '@ant-design/icons';
2
  import {
3
  Button,
@@ -6,13 +7,19 @@ import {
6
  Form,
7
  Input,
8
  Row,
9
- Select,
10
  Switch,
11
  Table,
12
  TableProps,
13
  } from 'antd';
14
  import classNames from 'classnames';
15
- import { useState } from 'react';
 
 
 
 
 
 
16
  import { v4 as uuid } from 'uuid';
17
  import { EditableCell, EditableRow } from './editable-cell';
18
  import { ISegmentedContentProps } from './interface';
@@ -21,12 +28,20 @@ import styles from './index.less';
21
 
22
  interface DataType {
23
  key: string;
 
24
  optional: boolean;
25
  }
26
 
27
- const { Option } = Select;
 
 
 
 
28
 
29
- const PromptEngine = ({ show }: ISegmentedContentProps) => {
 
 
 
30
  const [dataSource, setDataSource] = useState<DataType[]>([]);
31
 
32
  const components = {
@@ -52,6 +67,44 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
52
  setDataSource(newData);
53
  };
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  const columns: TableProps<DataType>['columns'] = [
56
  {
57
  title: 'key',
@@ -71,8 +124,14 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
71
  key: 'optional',
72
  width: 40,
73
  align: 'center',
74
- render() {
75
- return <Switch size="small" />;
 
 
 
 
 
 
76
  },
77
  },
78
  {
@@ -87,17 +146,6 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
87
  },
88
  ];
89
 
90
- const handleAdd = () => {
91
- setDataSource((state) => [
92
- ...state,
93
- {
94
- key: uuid(),
95
- variable: '',
96
- optional: true,
97
- },
98
- ]);
99
- };
100
-
101
  return (
102
  <section
103
  className={classNames({
@@ -106,12 +154,20 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
106
  >
107
  <Form.Item
108
  label="Orchestrate"
109
- name="orchestrate"
110
  rules={[{ required: true, message: 'Please input!' }]}
 
 
 
 
 
111
  >
112
  <Input.TextArea autoSize={{ maxRows: 5, minRows: 5 }} />
113
  </Form.Item>
114
  <Divider></Divider>
 
 
 
 
115
  <section className={classNames(styles.variableContainer)}>
116
  <Row align={'middle'} justify="end">
117
  <Col span={6} className={styles.variableAlign}>
@@ -139,25 +195,8 @@ const PromptEngine = ({ show }: ISegmentedContentProps) => {
139
  </Row>
140
  )}
141
  </section>
142
- <Form.Item
143
- label="Select one context"
144
- name="context"
145
- rules={[
146
- {
147
- required: true,
148
- message: 'Please select your favourite colors!',
149
- type: 'array',
150
- },
151
- ]}
152
- >
153
- <Select mode="multiple" placeholder="Please select favourite colors">
154
- <Option value="red">Red</Option>
155
- <Option value="green">Green</Option>
156
- <Option value="blue">Blue</Option>
157
- </Select>
158
- </Form.Item>
159
  </section>
160
  );
161
  };
162
 
163
- export default PromptEngine;
 
1
+ import SimilaritySlider from '@/components/similarity-slider';
2
  import { DeleteOutlined } from '@ant-design/icons';
3
  import {
4
  Button,
 
7
  Form,
8
  Input,
9
  Row,
10
+ Slider,
11
  Switch,
12
  Table,
13
  TableProps,
14
  } from 'antd';
15
  import classNames from 'classnames';
16
+ import {
17
+ ForwardedRef,
18
+ forwardRef,
19
+ useEffect,
20
+ useImperativeHandle,
21
+ useState,
22
+ } from 'react';
23
  import { v4 as uuid } from 'uuid';
24
  import { EditableCell, EditableRow } from './editable-cell';
25
  import { ISegmentedContentProps } from './interface';
 
28
 
29
  interface DataType {
30
  key: string;
31
+ variable: string;
32
  optional: boolean;
33
  }
34
 
35
+ type FieldType = {
36
+ similarity_threshold?: number;
37
+ vector_similarity_weight?: number;
38
+ top_n?: number;
39
+ };
40
 
41
+ const PromptEngine = (
42
+ { show, form }: ISegmentedContentProps,
43
+ ref: ForwardedRef<Array<Omit<DataType, 'variable'>>>,
44
+ ) => {
45
  const [dataSource, setDataSource] = useState<DataType[]>([]);
46
 
47
  const components = {
 
67
  setDataSource(newData);
68
  };
69
 
70
+ const handleAdd = () => {
71
+ setDataSource((state) => [
72
+ ...state,
73
+ {
74
+ key: uuid(),
75
+ variable: '',
76
+ optional: true,
77
+ },
78
+ ]);
79
+ };
80
+
81
+ const handleOptionalChange = (row: DataType) => (checked: boolean) => {
82
+ const newData = [...dataSource];
83
+ const index = newData.findIndex((item) => row.key === item.key);
84
+ const item = newData[index];
85
+ newData.splice(index, 1, {
86
+ ...item,
87
+ optional: checked,
88
+ });
89
+ setDataSource(newData);
90
+ };
91
+
92
+ useImperativeHandle(
93
+ ref,
94
+ () => {
95
+ return dataSource
96
+ .filter((x) => x.variable.trim() !== '')
97
+ .map((x) => ({ key: x.variable, optional: x.optional }));
98
+ },
99
+ [dataSource],
100
+ );
101
+
102
+ useEffect(() => {
103
+ form.setFieldValue(['prompt_config', 'parameters'], dataSource);
104
+ const x = form.getFieldValue(['prompt_config', 'parameters']);
105
+ console.info(x);
106
+ }, [dataSource, form]);
107
+
108
  const columns: TableProps<DataType>['columns'] = [
109
  {
110
  title: 'key',
 
124
  key: 'optional',
125
  width: 40,
126
  align: 'center',
127
+ render(text, record) {
128
+ return (
129
+ <Switch
130
+ size="small"
131
+ checked={text}
132
+ onChange={handleOptionalChange(record)}
133
+ />
134
+ );
135
  },
136
  },
137
  {
 
146
  },
147
  ];
148
 
 
 
 
 
 
 
 
 
 
 
 
149
  return (
150
  <section
151
  className={classNames({
 
154
  >
155
  <Form.Item
156
  label="Orchestrate"
 
157
  rules={[{ required: true, message: 'Please input!' }]}
158
+ name={['prompt_config', 'system']}
159
+ initialValue={`你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。回答需要考虑聊天历史。
160
+ 以下是知识库:
161
+ {knowledge}
162
+ 以上是知识库。`}
163
  >
164
  <Input.TextArea autoSize={{ maxRows: 5, minRows: 5 }} />
165
  </Form.Item>
166
  <Divider></Divider>
167
+ <SimilaritySlider></SimilaritySlider>
168
+ <Form.Item<FieldType> label="Top n" name={'top_n'} initialValue={0}>
169
+ <Slider max={30} />
170
+ </Form.Item>
171
  <section className={classNames(styles.variableContainer)}>
172
  <Row align={'middle'} justify="end">
173
  <Col span={6} className={styles.variableAlign}>
 
195
  </Row>
196
  )}
197
  </section>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  </section>
199
  );
200
  };
201
 
202
+ export default forwardRef(PromptEngine);
web/src/pages/chat/hooks.ts ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { IDialog } from '@/interfaces/database/chat';
2
+ import { useCallback, useEffect } from 'react';
3
+ import { useDispatch, useSelector } from 'umi';
4
+
5
+ export const useFetchDialogList = () => {
6
+ const dispatch = useDispatch();
7
+ const dialogList: IDialog[] = useSelector(
8
+ (state: any) => state.chatModel.dialogList,
9
+ );
10
+
11
+ useEffect(() => {
12
+ dispatch({ type: 'chatModel/listDialog' });
13
+ }, [dispatch]);
14
+
15
+ return dialogList;
16
+ };
17
+
18
+ export const useSetDialog = () => {
19
+ const dispatch = useDispatch();
20
+
21
+ const setDialog = useCallback(
22
+ (payload: IDialog) => {
23
+ dispatch({ type: 'chatModel/setDialog', payload });
24
+ },
25
+ [dispatch],
26
+ );
27
+
28
+ return setDialog;
29
+ };
web/src/pages/chat/index.less CHANGED
@@ -4,6 +4,17 @@
4
  .chatAppWrapper {
5
  width: 288px;
6
  padding: 26px;
 
 
 
 
 
 
 
 
 
 
 
7
  }
8
  .chatTitleWrapper {
9
  width: 220px;
 
4
  .chatAppWrapper {
5
  width: 288px;
6
  padding: 26px;
7
+
8
+ .chatAppCard {
9
+ :global(.ant-card-body) {
10
+ padding: 10px;
11
+ }
12
+ .cubeIcon {
13
+ &:hover {
14
+ cursor: pointer;
15
+ }
16
+ }
17
+ }
18
  }
19
  .chatTitleWrapper {
20
  width: 220px;
web/src/pages/chat/index.tsx CHANGED
@@ -1,14 +1,73 @@
1
- import { FormOutlined } from '@ant-design/icons';
2
- import { Button, Card, Divider, Flex, Space, Tag } from 'antd';
3
- import { useSelector } from 'umi';
 
 
 
 
 
 
 
 
4
  import ChatContainer from './chat-container';
5
 
 
6
  import ModalManager from '@/components/modal-manager';
 
7
  import ChatConfigurationModal from './chat-configuration-modal';
 
 
 
8
  import styles from './index.less';
9
 
10
  const Chat = () => {
11
- const { name } = useSelector((state: any) => state.chatModel);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  return (
14
  <Flex className={styles.chatWrapper}>
@@ -32,9 +91,33 @@ const Chat = () => {
32
  </ModalManager>
33
 
34
  <Divider></Divider>
35
- <Card>
36
- <p>Card content</p>
37
- </Card>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  </Flex>
39
  </Flex>
40
  <Divider type={'vertical'} className={styles.divider}></Divider>
@@ -49,7 +132,9 @@ const Chat = () => {
49
  <b>Chat</b>
50
  <Tag>25</Tag>
51
  </Space>
52
- <FormOutlined />
 
 
53
  </Flex>
54
  <Divider></Divider>
55
  <section className={styles.chatTitleContent}>today</section>
 
1
+ import { DeleteOutlined, EditOutlined, FormOutlined } from '@ant-design/icons';
2
+ import {
3
+ Button,
4
+ Card,
5
+ Divider,
6
+ Dropdown,
7
+ Flex,
8
+ MenuProps,
9
+ Space,
10
+ Tag,
11
+ } from 'antd';
12
  import ChatContainer from './chat-container';
13
 
14
+ import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
15
  import ModalManager from '@/components/modal-manager';
16
+ import classNames from 'classnames';
17
  import ChatConfigurationModal from './chat-configuration-modal';
18
+ import { useFetchDialogList } from './hooks';
19
+
20
+ import { useState } from 'react';
21
  import styles from './index.less';
22
 
23
  const Chat = () => {
24
+ const dialogList = useFetchDialogList();
25
+ const [activated, setActivated] = useState<string>('');
26
+
27
+ const handleAppCardEnter = (id: string) => () => {
28
+ setActivated(id);
29
+ };
30
+
31
+ const handleAppCardLeave = () => {
32
+ setActivated('');
33
+ };
34
+
35
+ const items: MenuProps['items'] = [
36
+ {
37
+ key: '1',
38
+ label: (
39
+ <a
40
+ target="_blank"
41
+ rel="noopener noreferrer"
42
+ href="https://www.antgroup.com"
43
+ >
44
+ 1st menu item
45
+ </a>
46
+ ),
47
+ },
48
+ ];
49
+
50
+ const appItems: MenuProps['items'] = [
51
+ {
52
+ key: '1',
53
+ label: (
54
+ <Space>
55
+ <EditOutlined />
56
+ Edit
57
+ </Space>
58
+ ),
59
+ },
60
+ { type: 'divider' },
61
+ {
62
+ key: '2',
63
+ label: (
64
+ <Space>
65
+ <DeleteOutlined />
66
+ Delete chat
67
+ </Space>
68
+ ),
69
+ },
70
+ ];
71
 
72
  return (
73
  <Flex className={styles.chatWrapper}>
 
91
  </ModalManager>
92
 
93
  <Divider></Divider>
94
+ <Space direction={'vertical'} size={'middle'}>
95
+ {dialogList.map((x) => (
96
+ <Card
97
+ key={x.id}
98
+ className={classNames(styles.chatAppCard)}
99
+ onMouseEnter={handleAppCardEnter(x.id)}
100
+ onMouseLeave={handleAppCardLeave}
101
+ >
102
+ <Flex justify="space-between" align="center">
103
+ <Space>
104
+ {x.icon}
105
+ <section>
106
+ <b>{x.name}</b>
107
+ <div>{x.description}</div>
108
+ </section>
109
+ </Space>
110
+ {activated === x.id && (
111
+ <section>
112
+ <Dropdown menu={{ items: appItems }}>
113
+ <ChatAppCube className={styles.cubeIcon}></ChatAppCube>
114
+ </Dropdown>
115
+ </section>
116
+ )}
117
+ </Flex>
118
+ </Card>
119
+ ))}
120
+ </Space>
121
  </Flex>
122
  </Flex>
123
  <Divider type={'vertical'} className={styles.divider}></Divider>
 
132
  <b>Chat</b>
133
  <Tag>25</Tag>
134
  </Space>
135
+ <Dropdown menu={{ items }}>
136
+ <FormOutlined />
137
+ </Dropdown>
138
  </Flex>
139
  <Divider></Divider>
140
  <section className={styles.chatTitleContent}>today</section>
web/src/pages/chat/model.ts CHANGED
@@ -1,13 +1,18 @@
 
 
 
1
  import { DvaModel } from 'umi';
2
 
3
  export interface ChatModelState {
4
  name: string;
 
5
  }
6
 
7
  const model: DvaModel<ChatModelState> = {
8
  namespace: 'chatModel',
9
  state: {
10
  name: 'kate',
 
11
  },
12
  reducers: {
13
  save(state, action) {
@@ -16,16 +21,41 @@ const model: DvaModel<ChatModelState> = {
16
  ...action.payload,
17
  };
18
  },
19
- },
20
- subscriptions: {
21
- setup({ dispatch, history }) {
22
- return history.listen((query) => {
23
- console.log(query);
24
- });
25
  },
26
  },
 
27
  effects: {
28
- *query({ payload }, { call, put }) {},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  },
30
  };
31
 
 
1
+ import { IDialog } from '@/interfaces/database/chat';
2
+ import chatService from '@/services/chatService';
3
+ import { message } from 'antd';
4
  import { DvaModel } from 'umi';
5
 
6
  export interface ChatModelState {
7
  name: string;
8
+ dialogList: IDialog[];
9
  }
10
 
11
  const model: DvaModel<ChatModelState> = {
12
  namespace: 'chatModel',
13
  state: {
14
  name: 'kate',
15
+ dialogList: [],
16
  },
17
  reducers: {
18
  save(state, action) {
 
21
  ...action.payload,
22
  };
23
  },
24
+ setDialogList(state, { payload }) {
25
+ return {
26
+ ...state,
27
+ dialogList: payload,
28
+ };
 
29
  },
30
  },
31
+
32
  effects: {
33
+ *getDialog({ payload }, { call, put }) {
34
+ const { data } = yield call(chatService.getDialog, payload);
35
+ },
36
+ *setDialog({ payload }, { call, put }) {
37
+ const { data } = yield call(chatService.setDialog, payload);
38
+ if (data.retcode === 0) {
39
+ yield put({ type: 'listDialog' });
40
+ message.success('Created successfully !');
41
+ }
42
+ },
43
+ *listDialog({ payload }, { call, put }) {
44
+ const { data } = yield call(chatService.listDialog, payload);
45
+ yield put({ type: 'setDialogList', payload: data.data });
46
+ },
47
+ *listConversation({ payload }, { call, put }) {
48
+ const { data } = yield call(chatService.listConversation, payload);
49
+ },
50
+ *getConversation({ payload }, { call, put }) {
51
+ const { data } = yield call(chatService.getConversation, payload);
52
+ },
53
+ *setConversation({ payload }, { call, put }) {
54
+ const { data } = yield call(chatService.setConversation, payload);
55
+ },
56
+ *completeConversation({ payload }, { call, put }) {
57
+ const { data } = yield call(chatService.completeConversation, payload);
58
+ },
59
  },
60
  };
61
 
web/src/pages/knowledge/index.tsx CHANGED
@@ -2,33 +2,14 @@ import { ReactComponent as FilterIcon } from '@/assets/filter.svg';
2
  import ModalManager from '@/components/modal-manager';
3
  import { PlusOutlined } from '@ant-design/icons';
4
  import { Button, Flex, Space } from 'antd';
5
- import { useCallback, useEffect } from 'react';
6
- import { useDispatch, useNavigate, useSelector } from 'umi';
7
  import KnowledgeCard from './knowledge-card';
8
  import KnowledgeCreatingModal from './knowledge-creating-modal';
9
 
 
10
  import styles from './index.less';
11
 
12
  const Knowledge = () => {
13
- const dispatch = useDispatch();
14
- const knowledgeModel = useSelector((state: any) => state.knowledgeModel);
15
- const navigate = useNavigate();
16
- const { data = [] } = knowledgeModel;
17
-
18
- const fetchList = useCallback(() => {
19
- dispatch({
20
- type: 'knowledgeModel/getList',
21
- payload: {},
22
- });
23
- }, []);
24
-
25
- // const handleAddKnowledge = () => {
26
- // navigate(`/knowledge/${KnowledgeRouteKey.Configuration}`);
27
- // };
28
-
29
- useEffect(() => {
30
- fetchList();
31
- }, [fetchList]);
32
 
33
  return (
34
  <div className={styles.knowledge}>
 
2
  import ModalManager from '@/components/modal-manager';
3
  import { PlusOutlined } from '@ant-design/icons';
4
  import { Button, Flex, Space } from 'antd';
 
 
5
  import KnowledgeCard from './knowledge-card';
6
  import KnowledgeCreatingModal from './knowledge-creating-modal';
7
 
8
+ import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
9
  import styles from './index.less';
10
 
11
  const Knowledge = () => {
12
+ const data = useFetchKnowledgeList();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  return (
15
  <div className={styles.knowledge}>
web/src/pages/setting/List.tsx CHANGED
@@ -37,7 +37,7 @@ const SettingList = () => {
37
  type: 'settingModel/my_llm',
38
  payload: {},
39
  });
40
- }, []);
41
 
42
  return (
43
  <div
 
37
  type: 'settingModel/my_llm',
38
  payload: {},
39
  });
40
+ }, [dispatch]);
41
 
42
  return (
43
  <div
web/src/services/chatService.ts ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import api from '@/utils/api';
2
+ import registerServer from '@/utils/registerServer';
3
+ import request from '@/utils/request';
4
+
5
+ const {
6
+ getDialog,
7
+ setDialog,
8
+ listDialog,
9
+ getConversation,
10
+ setConversation,
11
+ completeConversation,
12
+ listConversation,
13
+ } = api;
14
+
15
+ const methods = {
16
+ getDialog: {
17
+ url: getDialog,
18
+ method: 'get',
19
+ },
20
+ setDialog: {
21
+ url: setDialog,
22
+ method: 'post',
23
+ },
24
+ listDialog: {
25
+ url: listDialog,
26
+ method: 'get',
27
+ },
28
+ listConversation: {
29
+ url: listConversation,
30
+ method: 'get',
31
+ },
32
+ getConversation: {
33
+ url: getConversation,
34
+ method: 'get',
35
+ },
36
+ setConversation: {
37
+ url: setConversation,
38
+ method: 'post',
39
+ },
40
+ completeConversation: {
41
+ url: completeConversation,
42
+ method: 'post',
43
+ },
44
+ } as const;
45
+
46
+ const chatService = registerServer<keyof typeof methods>(methods, request);
47
+
48
+ export default chatService;
web/src/utils/api.ts CHANGED
@@ -42,4 +42,14 @@ export default {
42
  document_create: `${api_host}/document/create`,
43
  document_run: `${api_host}/document/run`,
44
  document_change_parser: `${api_host}/document/change_parser`,
 
 
 
 
 
 
 
 
 
 
45
  };
 
42
  document_create: `${api_host}/document/create`,
43
  document_run: `${api_host}/document/run`,
44
  document_change_parser: `${api_host}/document/change_parser`,
45
+
46
+ setDialog: `${api_host}/dialog/set`,
47
+ getDialog: `${api_host}/dialog/get`,
48
+ listDialog: `${api_host}/dialog/list`,
49
+
50
+ setConversation: `${api_host}/conversation/set`,
51
+ getConversation: `${api_host}/conversation/get`,
52
+ listConversation: `${api_host}/conversation/list`,
53
+ removeConversation: `${api_host}/conversation/rm`,
54
+ completeConversation: `${api_host}/conversation/completion`,
55
  };
web/src/utils/index.ts CHANGED
@@ -5,22 +5,23 @@
5
  */
6
  // import numeral from 'numeral';
7
 
8
- import JSEncrypt from 'jsencrypt';
9
  import { Base64 } from 'js-base64';
 
10
 
11
  export const getWidth = () => {
12
  return { width: window.innerWidth };
13
  };
14
  export const rsaPsw = (password: string) => {
15
- const pub = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB-----END PUBLIC KEY-----"
16
- const encryptor = new JSEncrypt()
 
17
 
18
- encryptor.setPublicKey(pub)
19
 
20
- return encryptor.encrypt(Base64.encode(password))
21
- }
22
 
23
  export default {
24
  getWidth,
25
- rsaPsw
26
  };
 
5
  */
6
  // import numeral from 'numeral';
7
 
 
8
  import { Base64 } from 'js-base64';
9
+ import JSEncrypt from 'jsencrypt';
10
 
11
  export const getWidth = () => {
12
  return { width: window.innerWidth };
13
  };
14
  export const rsaPsw = (password: string) => {
15
+ const pub =
16
+ '-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB-----END PUBLIC KEY-----';
17
+ const encryptor = new JSEncrypt();
18
 
19
+ encryptor.setPublicKey(pub);
20
 
21
+ return encryptor.encrypt(Base64.encode(password));
22
+ };
23
 
24
  export default {
25
  getWidth,
26
+ rsaPsw,
27
  };