balibabu commited on
Commit
ebf2bde
·
1 Parent(s): 7cad30e

feat: delete the added model #503 and display an error message when the requested file fails to parse #684 (#708)

Browse files

### What problem does this PR solve?

feat: delete the added model #503
feat: display an error message when the requested file fails to parse
#684

### Type of change

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

web/.umirc.ts CHANGED
@@ -27,7 +27,7 @@ export default defineConfig({
27
  devtool: 'source-map',
28
  proxy: {
29
  '/v1': {
30
- target: 'http://123.60.95.134:9380/',
31
  changeOrigin: true,
32
  // pathRewrite: { '^/v1': '/v1' },
33
  },
 
27
  devtool: 'source-map',
28
  proxy: {
29
  '/v1': {
30
+ target: '',
31
  changeOrigin: true,
32
  // pathRewrite: { '^/v1': '/v1' },
33
  },
web/package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
web/package.json CHANGED
@@ -23,10 +23,10 @@
23
  "js-base64": "^3.7.5",
24
  "jsencrypt": "^3.3.2",
25
  "lodash": "^4.17.21",
 
26
  "rc-tween-one": "^3.0.6",
27
  "react-chat-elements": "^12.0.13",
28
  "react-copy-to-clipboard": "^5.1.0",
29
- "react-file-viewer": "^1.2.1",
30
  "react-i18next": "^14.0.0",
31
  "react-infinite-scroll-component": "^6.1.0",
32
  "react-markdown": "^9.0.1",
 
23
  "js-base64": "^3.7.5",
24
  "jsencrypt": "^3.3.2",
25
  "lodash": "^4.17.21",
26
+ "mammoth": "^1.7.2",
27
  "rc-tween-one": "^3.0.6",
28
  "react-chat-elements": "^12.0.13",
29
  "react-copy-to-clipboard": "^5.1.0",
 
30
  "react-i18next": "^14.0.0",
31
  "react-infinite-scroll-component": "^6.1.0",
32
  "react-markdown": "^9.0.1",
web/src/components/pdf-previewer/index.tsx CHANGED
@@ -34,7 +34,7 @@ const HighlightPopup = ({
34
  ) : null;
35
 
36
  const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
37
- const url = useGetDocumentUrl(documentId);
38
  const { highlights: state, setWidthAndHeight } = useGetChunkHighlights(chunk);
39
  const ref = useRef<(highlight: IHighlight) => void>(() => {});
40
  const [loaded, setLoaded] = useState(false);
@@ -55,7 +55,7 @@ const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
55
  return (
56
  <div className={styles.documentContainer}>
57
  <PdfLoader
58
- url={url}
59
  beforeLoad={<Skeleton active />}
60
  workerSrc="/pdfjs-dist/pdf.worker.min.js"
61
  >
 
34
  ) : null;
35
 
36
  const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
37
+ const getDocumentUrl = useGetDocumentUrl(documentId);
38
  const { highlights: state, setWidthAndHeight } = useGetChunkHighlights(chunk);
39
  const ref = useRef<(highlight: IHighlight) => void>(() => {});
40
  const [loaded, setLoaded] = useState(false);
 
55
  return (
56
  <div className={styles.documentContainer}>
57
  <PdfLoader
58
+ url={getDocumentUrl()}
59
  beforeLoad={<Skeleton active />}
60
  workerSrc="/pdfjs-dist/pdf.worker.min.js"
61
  >
web/src/constants/common.ts CHANGED
@@ -69,6 +69,8 @@ export const FileMimeTypeMap = {
69
  mp4: 'video/mp4',
70
  };
71
 
 
 
72
  //#region file preview
73
  export const Images = [
74
  'jpg',
@@ -84,7 +86,7 @@ export const Images = [
84
  ];
85
 
86
  // Without FileViewer
87
- export const ExceptiveType = ['xlsx', 'xls', 'pdf', ...Images];
88
 
89
- export const SupportedPreviewDocumentTypes = ['docx', 'csv', ...ExceptiveType];
90
  //#endregion
 
69
  mp4: 'video/mp4',
70
  };
71
 
72
+ export const Domain = 'demo.ragflow.io';
73
+
74
  //#region file preview
75
  export const Images = [
76
  'jpg',
 
86
  ];
87
 
88
  // Without FileViewer
89
+ export const ExceptiveType = ['xlsx', 'xls', 'pdf', 'docx', ...Images];
90
 
91
+ export const SupportedPreviewDocumentTypes = [...ExceptiveType];
92
  //#endregion
web/src/hooks/llmHooks.ts CHANGED
@@ -4,7 +4,10 @@ import {
4
  IMyLlmValue,
5
  IThirdOAIModelCollection,
6
  } from '@/interfaces/database/llm';
7
- import { IAddLlmRequestBody } from '@/interfaces/request/llm';
 
 
 
8
  import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/commonUtil';
9
  import { useCallback, useEffect, useMemo } from 'react';
10
  import { useDispatch, useSelector } from 'umi';
@@ -211,7 +214,7 @@ export const useSaveTenantInfo = () => {
211
  export const useAddLlm = () => {
212
  const dispatch = useDispatch();
213
 
214
- const saveTenantInfo = useCallback(
215
  (requestBody: IAddLlmRequestBody) => {
216
  return dispatch<any>({
217
  type: 'settingModel/add_llm',
@@ -221,5 +224,21 @@ export const useAddLlm = () => {
221
  [dispatch],
222
  );
223
 
224
- return saveTenantInfo;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  };
 
4
  IMyLlmValue,
5
  IThirdOAIModelCollection,
6
  } from '@/interfaces/database/llm';
7
+ import {
8
+ IAddLlmRequestBody,
9
+ IDeleteLlmRequestBody,
10
+ } from '@/interfaces/request/llm';
11
  import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/commonUtil';
12
  import { useCallback, useEffect, useMemo } from 'react';
13
  import { useDispatch, useSelector } from 'umi';
 
214
  export const useAddLlm = () => {
215
  const dispatch = useDispatch();
216
 
217
+ const addLlm = useCallback(
218
  (requestBody: IAddLlmRequestBody) => {
219
  return dispatch<any>({
220
  type: 'settingModel/add_llm',
 
224
  [dispatch],
225
  );
226
 
227
+ return addLlm;
228
+ };
229
+
230
+ export const useDeleteLlm = () => {
231
+ const dispatch = useDispatch();
232
+
233
+ const deleteLlm = useCallback(
234
+ (requestBody: IDeleteLlmRequestBody) => {
235
+ return dispatch<any>({
236
+ type: 'settingModel/delete_llm',
237
+ payload: requestBody,
238
+ });
239
+ },
240
+ [dispatch],
241
+ );
242
+
243
+ return deleteLlm;
244
  };
web/src/interfaces/request/llm.ts CHANGED
@@ -4,3 +4,8 @@ export interface IAddLlmRequestBody {
4
  model_type: string;
5
  api_base?: string; // chat|embedding|speech2text|image2text
6
  }
 
 
 
 
 
 
4
  model_type: string;
5
  api_base?: string; // chat|embedding|speech2text|image2text
6
  }
7
+
8
+ export interface IDeleteLlmRequestBody {
9
+ llm_factory: string; // Ollama
10
+ llm_name: string;
11
+ }
web/src/locales/en.ts CHANGED
@@ -506,6 +506,7 @@ export default {
506
  local: 'Local uploads',
507
  s3: 'S3 uploads',
508
  preview: 'Preview',
 
509
  },
510
  footer: {
511
  profile: 'All rights reserved @ React',
 
506
  local: 'Local uploads',
507
  s3: 'S3 uploads',
508
  preview: 'Preview',
509
+ fileError: 'File error',
510
  },
511
  footer: {
512
  profile: 'All rights reserved @ React',
web/src/locales/zh-traditional.ts CHANGED
@@ -469,6 +469,7 @@ export default {
469
  local: '本地上傳',
470
  s3: 'S3 上傳',
471
  preview: '預覽',
 
472
  },
473
  footer: {
474
  profile: '“保留所有權利 @ react”',
 
469
  local: '本地上傳',
470
  s3: 'S3 上傳',
471
  preview: '預覽',
472
+ fileError: '文件錯誤',
473
  },
474
  footer: {
475
  profile: '“保留所有權利 @ react”',
web/src/locales/zh.ts CHANGED
@@ -487,6 +487,7 @@ export default {
487
  local: '本地上传',
488
  s3: 'S3 上传',
489
  preview: '预览',
 
490
  },
491
  footer: {
492
  profile: 'All rights reserved @ React',
 
487
  local: '本地上传',
488
  s3: 'S3 上传',
489
  preview: '预览',
490
+ fileError: '文件错误',
491
  },
492
  footer: {
493
  profile: 'All rights reserved @ React',
web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx CHANGED
@@ -55,7 +55,7 @@ const PopoverContent = ({ record }: IProps) => {
55
  {
56
  key: 'process_duation',
57
  label: t('processDuration'),
58
- children: record.process_duation,
59
  },
60
  {
61
  key: 'progress_msg',
 
55
  {
56
  key: 'process_duation',
57
  label: t('processDuration'),
58
+ children: `${record.process_duation} s`,
59
  },
60
  {
61
  key: 'progress_msg',
web/src/pages/chat/chat-overview-modal/index.tsx CHANGED
@@ -1,4 +1,5 @@
1
  import LineChart from '@/components/line-chart';
 
2
  import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
3
  import { IModalProps } from '@/interfaces/common';
4
  import { IDialog, IStats } from '@/interfaces/database/chat';
@@ -80,7 +81,9 @@ const ChatOverviewModal = ({
80
  <Flex gap={8} vertical>
81
  {t('serviceApiEndpoint')}
82
  <Paragraph copyable className={styles.linkText}>
83
- https://demo.ragflow.io/v1/api/
 
 
84
  </Paragraph>
85
  </Flex>
86
  <Space size={'middle'}>
 
1
  import LineChart from '@/components/line-chart';
2
+ import { Domain } from '@/constants/common';
3
  import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
4
  import { IModalProps } from '@/interfaces/common';
5
  import { IDialog, IStats } from '@/interfaces/database/chat';
 
81
  <Flex gap={8} vertical>
82
  {t('serviceApiEndpoint')}
83
  <Paragraph copyable className={styles.linkText}>
84
+ https://
85
+ {location.hostname === Domain ? Domain : '<YOUR_MACHINE_IP>'}
86
+ /v1/api/
87
  </Paragraph>
88
  </Flex>
89
  <Space size={'middle'}>
web/src/pages/chat/embed-modal/index.tsx CHANGED
@@ -1,5 +1,6 @@
1
  import CopyToClipboard from '@/components/copy-to-clipboard';
2
  import HightLightMarkdown from '@/components/highlight-markdown';
 
3
  import { useTranslate } from '@/hooks/commonHooks';
4
  import { IModalProps } from '@/interfaces/common';
5
  import { Card, Modal, Tabs, TabsProps } from 'antd';
@@ -15,7 +16,7 @@ const EmbedModal = ({
15
  const text = `
16
  ~~~ html
17
  <iframe
18
- src="https://demo.ragflow.io/chat/share?shared_id=${token}"
19
  style="width: 100%; height: 100%; min-height: 600px"
20
  frameborder="0"
21
  >
 
1
  import CopyToClipboard from '@/components/copy-to-clipboard';
2
  import HightLightMarkdown from '@/components/highlight-markdown';
3
+ import { Domain } from '@/constants/common';
4
  import { useTranslate } from '@/hooks/commonHooks';
5
  import { IModalProps } from '@/interfaces/common';
6
  import { Card, Modal, Tabs, TabsProps } from 'antd';
 
16
  const text = `
17
  ~~~ html
18
  <iframe
19
+ src="https://${Domain}/chat/share?shared_id=${token}"
20
  style="width: 100%; height: 100%; min-height: 600px"
21
  frameborder="0"
22
  >
web/src/pages/document-viewer/docx/index.less ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) 2017 PlanGrid, Inc.
2
+
3
+ .docxViewerWrapper {
4
+ overflow-y: scroll;
5
+ height: 100%;
6
+ width: 100%;
7
+
8
+ .box {
9
+ width: 100%;
10
+ height: 100%;
11
+ }
12
+
13
+ :global(.document-container) {
14
+ padding: 30px;
15
+ width: 700px;
16
+ background: white;
17
+ margin: auto;
18
+ }
19
+
20
+ html,
21
+ bodyaddress,
22
+ blockquote,
23
+ body,
24
+ dd,
25
+ div,
26
+ dl,
27
+ dt,
28
+ fieldset,
29
+ form,
30
+ frame,
31
+ frameset,
32
+ h1,
33
+ h2,
34
+ h3,
35
+ h4,
36
+ h5,
37
+ h6,
38
+ noframes,
39
+ ol,
40
+ p,
41
+ ul,
42
+ center,
43
+ dir,
44
+ hr,
45
+ menu,
46
+ pre {
47
+ display: block;
48
+ unicode-bidi: embed;
49
+ }
50
+ li {
51
+ display: list-item;
52
+ list-style-type: disc;
53
+ }
54
+ head {
55
+ display: none;
56
+ }
57
+ table {
58
+ display: table;
59
+ }
60
+ img {
61
+ width: 100%;
62
+ }
63
+ tr {
64
+ display: table-row;
65
+ }
66
+ thead {
67
+ display: table-header-group;
68
+ }
69
+ tbody {
70
+ display: table-row-group;
71
+ }
72
+ tfoot {
73
+ display: table-footer-group;
74
+ }
75
+ col {
76
+ display: table-column;
77
+ }
78
+ colgroup {
79
+ display: table-column-group;
80
+ }
81
+ th {
82
+ display: table-cell;
83
+ }
84
+ td {
85
+ display: table-cell;
86
+ border-bottom: 1px solid #ccc;
87
+ border-right: 1px solid #ccc;
88
+ padding: 0.2em 0.5em;
89
+ }
90
+ caption {
91
+ display: table-caption;
92
+ }
93
+ th {
94
+ font-weight: bolder;
95
+ text-align: center;
96
+ }
97
+ caption {
98
+ text-align: center;
99
+ }
100
+ body {
101
+ margin: 8px;
102
+ }
103
+ h1 {
104
+ font-size: 2em;
105
+ margin: 0.67em 0;
106
+ }
107
+ h2 {
108
+ font-size: 1.5em;
109
+ margin: 0.75em 0;
110
+ }
111
+ h3 {
112
+ font-size: 1.17em;
113
+ margin: 0.83em 0;
114
+ }
115
+ h4,
116
+ p,
117
+ blockquote,
118
+ ul,
119
+ fieldset,
120
+ form,
121
+ ol,
122
+ dl,
123
+ dir,
124
+ menu {
125
+ margin: 1.12em 0;
126
+ }
127
+ h5 {
128
+ font-size: 0.83em;
129
+ margin: 1.5em 0;
130
+ }
131
+ h6 {
132
+ font-size: 0.75em;
133
+ margin: 1.67em 0;
134
+ }
135
+ h1,
136
+ h2,
137
+ h3,
138
+ h4,
139
+ h5,
140
+ h6,
141
+ b,
142
+ strong {
143
+ font-weight: bolder;
144
+ }
145
+ blockquote {
146
+ margin-left: 40px;
147
+ margin-right: 40px;
148
+ }
149
+ i,
150
+ cite,
151
+ em,
152
+ var,
153
+ address {
154
+ font-style: italic;
155
+ }
156
+ pre,
157
+ tt,
158
+ code,
159
+ kbd,
160
+ samp {
161
+ font-family: monospace;
162
+ }
163
+ pre {
164
+ white-space: pre;
165
+ }
166
+ button,
167
+ textarea,
168
+ input,
169
+ select {
170
+ display: inline-block;
171
+ }
172
+ big {
173
+ font-size: 1.17em;
174
+ }
175
+ small,
176
+ sub,
177
+ sup {
178
+ font-size: 0.83em;
179
+ }
180
+ sub {
181
+ vertical-align: sub;
182
+ }
183
+ sup {
184
+ vertical-align: super;
185
+ }
186
+ table {
187
+ border-spacing: 2px;
188
+ }
189
+ thead,
190
+ tbody,
191
+ tfoot {
192
+ vertical-align: middle;
193
+ }
194
+ td,
195
+ th,
196
+ tr {
197
+ vertical-align: inherit;
198
+ }
199
+ s,
200
+ strike,
201
+ del {
202
+ text-decoration: line-through;
203
+ }
204
+ hr {
205
+ border: 1px inset;
206
+ }
207
+ ol,
208
+ ul,
209
+ dir,
210
+ menu,
211
+ dd {
212
+ margin-left: 40px;
213
+ }
214
+ ol {
215
+ list-style-type: decimal;
216
+ }
217
+ ol ul,
218
+ ol ul,
219
+ ul ol,
220
+ ul ol,
221
+ ul ul,
222
+ ul ul,
223
+ ol ol,
224
+ ol ol {
225
+ margin-top: 0;
226
+ margin-bottom: 0;
227
+ }
228
+ u,
229
+ ins {
230
+ text-decoration: underline;
231
+ }
232
+ br:before {
233
+ content: '\A';
234
+ white-space: pre-line;
235
+ }
236
+ center {
237
+ text-align: center;
238
+ }
239
+ :link,
240
+ :visited {
241
+ text-decoration: underline;
242
+ }
243
+ :focus {
244
+ outline: thin dotted invert;
245
+ }
246
+ /* Begin bidirectionality settings (do not change) */
247
+ BDO[DIR='ltr'] {
248
+ direction: ltr;
249
+ unicode-bidi: bidi-override;
250
+ }
251
+ BDO[DIR='rtl'] {
252
+ direction: rtl;
253
+ unicode-bidi: bidi-override;
254
+ }
255
+ *[DIR='ltr'] {
256
+ direction: ltr;
257
+ unicode-bidi: embed;
258
+ }
259
+ *[DIR='rtl'] {
260
+ direction: rtl;
261
+ unicode-bidi: embed;
262
+ }
263
+ @media print {
264
+ h1 {
265
+ page-break-before: always;
266
+ }
267
+ h1,
268
+ h2,
269
+ h3,
270
+ h4,
271
+ h5,
272
+ h6 {
273
+ page-break-after: avoid;
274
+ }
275
+ ul,
276
+ ol,
277
+ dl {
278
+ page-break-before: avoid;
279
+ }
280
+ }
281
+ }
web/src/pages/document-viewer/docx/index.tsx ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Spin } from 'antd';
2
+ import FileError from '../file-error';
3
+
4
+ import { useFetchDocx } from '../hooks';
5
+ import styles from './index.less';
6
+
7
+ const Docx = ({ filePath }: { filePath: string }) => {
8
+ const { succeed, containerRef } = useFetchDocx(filePath);
9
+
10
+ return (
11
+ <>
12
+ {succeed ? (
13
+ <section className={styles.docxViewerWrapper}>
14
+ <div id="docx" ref={containerRef} className={styles.box}>
15
+ <Spin />
16
+ </div>
17
+ </section>
18
+ ) : (
19
+ <FileError></FileError>
20
+ )}
21
+ </>
22
+ );
23
+ };
24
+
25
+ export default Docx;
web/src/pages/document-viewer/excel/index.tsx CHANGED
@@ -1,35 +1,19 @@
1
- import jsPreviewExcel from '@js-preview/excel';
2
  import '@js-preview/excel/lib/index.css';
3
- import { useEffect } from 'react';
 
4
 
5
  const Excel = ({ filePath }: { filePath: string }) => {
6
- const fetchDocument = async () => {
7
- const myExcelPreviewer = jsPreviewExcel.init(
8
- document.getElementById('excel'),
9
- );
10
- const jsonFile = new XMLHttpRequest();
11
- jsonFile.open('GET', filePath, true);
12
- jsonFile.send();
13
- jsonFile.responseType = 'arraybuffer';
14
- jsonFile.onreadystatechange = () => {
15
- if (jsonFile.readyState === 4 && jsonFile.status === 200) {
16
- myExcelPreviewer
17
- .preview(jsonFile.response)
18
- .then((res: any) => {
19
- console.log('succeed');
20
- })
21
- .catch((e) => {
22
- console.log('failed', e);
23
- });
24
- }
25
- };
26
- };
27
 
28
- useEffect(() => {
29
- fetchDocument();
30
- }, []);
31
-
32
- return <div id="excel" style={{ height: '100%' }}></div>;
 
 
 
 
33
  };
34
 
35
  export default Excel;
 
 
1
  import '@js-preview/excel/lib/index.css';
2
+ import FileError from '../file-error';
3
+ import { useFetchExcel } from '../hooks';
4
 
5
  const Excel = ({ filePath }: { filePath: string }) => {
6
+ const { status, containerRef } = useFetchExcel(filePath);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ return (
9
+ <div
10
+ id="excel"
11
+ ref={containerRef}
12
+ style={{ height: '100%', width: '100%' }}
13
+ >
14
+ {status || <FileError></FileError>}
15
+ </div>
16
+ );
17
  };
18
 
19
  export default Excel;
web/src/pages/document-viewer/file-error/index.less ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .errorWrapper {
2
+ width: 100%;
3
+ height: 100%;
4
+ }
web/src/pages/document-viewer/file-error/index.tsx ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Alert, Flex } from 'antd';
2
+
3
+ import { useTranslate } from '@/hooks/commonHooks';
4
+ import styles from './index.less';
5
+
6
+ const FileError = () => {
7
+ const { t } = useTranslate('fileManager');
8
+ return (
9
+ <Flex align="center" justify="center" className={styles.errorWrapper}>
10
+ <Alert type="error" message={<h1>{t('fileError')}</h1>}></Alert>
11
+ </Flex>
12
+ );
13
+ };
14
+
15
+ export default FileError;
web/src/pages/document-viewer/hooks.ts ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import jsPreviewExcel from '@js-preview/excel';
2
+ import axios from 'axios';
3
+ import mammoth from 'mammoth';
4
+ import { useCallback, useEffect, useRef, useState } from 'react';
5
+
6
+ const useFetchDocument = () => {
7
+ const fetchDocument = useCallback((api: string) => {
8
+ return axios.get(api, { responseType: 'arraybuffer' });
9
+ }, []);
10
+
11
+ return fetchDocument;
12
+ };
13
+
14
+ export const useFetchExcel = (filePath: string) => {
15
+ const [status, setStatus] = useState(true);
16
+ const fetchDocument = useFetchDocument();
17
+ const containerRef = useRef<HTMLDivElement>(null);
18
+
19
+ const fetchDocumentAsync = useCallback(async () => {
20
+ let myExcelPreviewer;
21
+ if (containerRef.current) {
22
+ myExcelPreviewer = jsPreviewExcel.init(containerRef.current);
23
+ }
24
+ const jsonFile = await fetchDocument(filePath);
25
+ myExcelPreviewer
26
+ ?.preview(jsonFile.data)
27
+ .then(() => {
28
+ console.log('succeed');
29
+ setStatus(true);
30
+ })
31
+ .catch((e) => {
32
+ console.warn('failed', e);
33
+ myExcelPreviewer.destroy();
34
+ setStatus(false);
35
+ });
36
+ }, [filePath, fetchDocument]);
37
+
38
+ useEffect(() => {
39
+ fetchDocumentAsync();
40
+ }, [fetchDocumentAsync]);
41
+
42
+ return { status, containerRef };
43
+ };
44
+
45
+ export const useFetchDocx = (filePath: string) => {
46
+ const [succeed, setSucceed] = useState(true);
47
+ const fetchDocument = useFetchDocument();
48
+ const containerRef = useRef<HTMLDivElement>(null);
49
+
50
+ const fetchDocumentAsync = useCallback(async () => {
51
+ const jsonFile = await fetchDocument(filePath);
52
+ mammoth
53
+ .convertToHtml(
54
+ { arrayBuffer: jsonFile.data },
55
+ { includeDefaultStyleMap: true },
56
+ )
57
+ .then((result) => {
58
+ setSucceed(true);
59
+ const docEl = document.createElement('div');
60
+ docEl.className = 'document-container';
61
+ docEl.innerHTML = result.value;
62
+ const container = containerRef.current;
63
+ if (container) {
64
+ container.innerHTML = docEl.outerHTML;
65
+ }
66
+ })
67
+ .catch((a) => {
68
+ setSucceed(false);
69
+ console.warn('alexei: something went wrong', a);
70
+ });
71
+ }, [filePath, fetchDocument]);
72
+
73
+ useEffect(() => {
74
+ fetchDocumentAsync();
75
+ }, [fetchDocumentAsync]);
76
+
77
+ return { succeed, containerRef };
78
+ };
web/src/pages/document-viewer/index.tsx CHANGED
@@ -1,8 +1,8 @@
1
- import { ExceptiveType, Images } from '@/constants/common';
2
  import { api_host } from '@/utils/api';
3
  import { Flex, Image } from 'antd';
4
- import FileViewer from 'react-file-viewer';
5
  import { useParams, useSearchParams } from 'umi';
 
6
  import Excel from './excel';
7
  import Pdf from './pdf';
8
 
@@ -10,18 +10,12 @@ import styles from './index.less';
10
 
11
  // TODO: The interface returns an incorrect content-type for the SVG.
12
 
13
- const isNotExceptiveType = (ext: string) => ExceptiveType.indexOf(ext) === -1;
14
-
15
  const DocumentViewer = () => {
16
  const { id: documentId } = useParams();
17
  const api = `${api_host}/file/get/${documentId}`;
18
  const [currentQueryParameters] = useSearchParams();
19
  const ext = currentQueryParameters.get('ext');
20
 
21
- const onError = (e: any) => {
22
- console.error(e, 'error in file-viewer');
23
- };
24
-
25
  return (
26
  <section className={styles.viewerWrapper}>
27
  {Images.includes(ext!) && (
@@ -31,9 +25,8 @@ const DocumentViewer = () => {
31
  )}
32
  {ext === 'pdf' && <Pdf url={api}></Pdf>}
33
  {(ext === 'xlsx' || ext === 'xls') && <Excel filePath={api}></Excel>}
34
- {isNotExceptiveType(ext!) && (
35
- <FileViewer fileType={ext} filePath={api} onError={onError} />
36
- )}
37
  </section>
38
  );
39
  };
 
1
+ import { Images } from '@/constants/common';
2
  import { api_host } from '@/utils/api';
3
  import { Flex, Image } from 'antd';
 
4
  import { useParams, useSearchParams } from 'umi';
5
+ import Docx from './docx';
6
  import Excel from './excel';
7
  import Pdf from './pdf';
8
 
 
10
 
11
  // TODO: The interface returns an incorrect content-type for the SVG.
12
 
 
 
13
  const DocumentViewer = () => {
14
  const { id: documentId } = useParams();
15
  const api = `${api_host}/file/get/${documentId}`;
16
  const [currentQueryParameters] = useSearchParams();
17
  const ext = currentQueryParameters.get('ext');
18
 
 
 
 
 
19
  return (
20
  <section className={styles.viewerWrapper}>
21
  {Images.includes(ext!) && (
 
25
  )}
26
  {ext === 'pdf' && <Pdf url={api}></Pdf>}
27
  {(ext === 'xlsx' || ext === 'xls') && <Excel filePath={api}></Excel>}
28
+
29
+ {ext === 'docx' && <Docx filePath={api}></Docx>}
 
30
  </section>
31
  );
32
  };
web/src/pages/document-viewer/pdf/index.tsx CHANGED
@@ -1,5 +1,6 @@
1
  import { Skeleton } from 'antd';
2
  import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter';
 
3
 
4
  interface IProps {
5
  url: string;
@@ -9,11 +10,15 @@ const DocumentPreviewer = ({ url }: IProps) => {
9
  const resetHash = () => {};
10
 
11
  return (
12
- <div style={{ width: '100%' }}>
13
  <PdfLoader
14
  url={url}
15
  beforeLoad={<Skeleton active />}
16
  workerSrc="/pdfjs-dist/pdf.worker.min.js"
 
 
 
 
17
  >
18
  {(pdfDocument) => {
19
  return (
 
1
  import { Skeleton } from 'antd';
2
  import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter';
3
+ import FileError from '../file-error';
4
 
5
  interface IProps {
6
  url: string;
 
10
  const resetHash = () => {};
11
 
12
  return (
13
+ <div style={{ width: '100%', height: '100%' }}>
14
  <PdfLoader
15
  url={url}
16
  beforeLoad={<Skeleton active />}
17
  workerSrc="/pdfjs-dist/pdf.worker.min.js"
18
+ errorMessage={<FileError></FileError>}
19
+ onError={(e) => {
20
+ console.warn(e);
21
+ }}
22
  >
23
  {(pdfDocument) => {
24
  return (
web/src/pages/login/index.tsx CHANGED
@@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next';
7
  import { Icon, useNavigate } from 'umi';
8
  import RightPanel from './right-panel';
9
 
 
10
  import styles from './index.less';
11
 
12
  const Login = () => {
@@ -167,7 +168,7 @@ const Login = () => {
167
  Sign in with Google
168
  </div>
169
  </Button> */}
170
- {location.host === 'demo.ragflow.io' && (
171
  <Button
172
  block
173
  size="large"
 
7
  import { Icon, useNavigate } from 'umi';
8
  import RightPanel from './right-panel';
9
 
10
+ import { Domain } from '@/constants/common';
11
  import styles from './index.less';
12
 
13
  const Login = () => {
 
168
  Sign in with Google
169
  </div>
170
  </Button> */}
171
+ {location.host === Domain && (
172
  <Button
173
  block
174
  size="large"
web/src/pages/user-setting/model.ts CHANGED
@@ -167,6 +167,17 @@ const model: DvaModel<SettingModelState> = {
167
  }
168
  return retcode;
169
  },
 
 
 
 
 
 
 
 
 
 
 
170
  },
171
  };
172
  export default model;
 
167
  }
168
  return retcode;
169
  },
170
+ *delete_llm({ payload = {} }, { call, put }) {
171
+ const { data } = yield call(userService.delete_llm, payload);
172
+ const { retcode } = data;
173
+ if (retcode === 0) {
174
+ message.success(i18n.t('message.deleted'));
175
+
176
+ yield put({ type: 'my_llm' });
177
+ yield put({ type: 'factories_list' });
178
+ }
179
+ return retcode;
180
+ },
181
  },
182
  };
183
  export default model;
web/src/pages/user-setting/setting-model/hooks.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { useSetModalState } from '@/hooks/commonHooks';
2
  import {
3
  IApiKeySavingParams,
4
  ISystemModelSettingSavingParams,
5
  useAddLlm,
 
6
  useFetchLlmList,
7
  useSaveApiKey,
8
  useSaveTenantInfo,
@@ -164,3 +165,18 @@ export const useSubmitOllama = () => {
164
  selectedLlmFactory,
165
  };
166
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
2
  import {
3
  IApiKeySavingParams,
4
  ISystemModelSettingSavingParams,
5
  useAddLlm,
6
+ useDeleteLlm,
7
  useFetchLlmList,
8
  useSaveApiKey,
9
  useSaveTenantInfo,
 
165
  selectedLlmFactory,
166
  };
167
  };
168
+
169
+ export const useHandleDeleteLlm = (llmFactory: string) => {
170
+ const deleteLlm = useDeleteLlm();
171
+ const showDeleteConfirm = useShowDeleteConfirm();
172
+
173
+ const handleDeleteLlm = (name: string) => () => {
174
+ showDeleteConfirm({
175
+ onOk: async () => {
176
+ deleteLlm({ llm_factory: llmFactory, llm_name: name });
177
+ },
178
+ });
179
+ };
180
+
181
+ return { handleDeleteLlm };
182
+ };
web/src/pages/user-setting/setting-model/index.tsx CHANGED
@@ -6,7 +6,11 @@ import {
6
  useFetchLlmFactoryListOnMount,
7
  useFetchMyLlmListOnMount,
8
  } from '@/hooks/llmHooks';
9
- import { SettingOutlined, UserOutlined } from '@ant-design/icons';
 
 
 
 
10
  import {
11
  Avatar,
12
  Button,
@@ -21,6 +25,7 @@ import {
21
  Space,
22
  Spin,
23
  Tag,
 
24
  Typography,
25
  } from 'antd';
26
  import { useCallback } from 'react';
@@ -28,6 +33,7 @@ import SettingTitle from '../components/setting-title';
28
  import { isLocalLlmFactory } from '../utils';
29
  import ApiKeyModal from './api-key-modal';
30
  import {
 
31
  useSelectModelProvidersLoading,
32
  useSubmitApiKey,
33
  useSubmitOllama,
@@ -67,6 +73,7 @@ interface IModelCardProps {
67
  const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
68
  const { visible, switchVisible } = useSetModalState();
69
  const { t } = useTranslate('setting');
 
70
 
71
  const handleApiKeyClick = () => {
72
  clickApiKey(item.name);
@@ -113,6 +120,11 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
113
  <List.Item>
114
  <Space>
115
  {item.name} <Tag color="#b8b8b8">{item.type}</Tag>
 
 
 
 
 
116
  </Space>
117
  </List.Item>
118
  )}
 
6
  useFetchLlmFactoryListOnMount,
7
  useFetchMyLlmListOnMount,
8
  } from '@/hooks/llmHooks';
9
+ import {
10
+ CloseCircleOutlined,
11
+ SettingOutlined,
12
+ UserOutlined,
13
+ } from '@ant-design/icons';
14
  import {
15
  Avatar,
16
  Button,
 
25
  Space,
26
  Spin,
27
  Tag,
28
+ Tooltip,
29
  Typography,
30
  } from 'antd';
31
  import { useCallback } from 'react';
 
33
  import { isLocalLlmFactory } from '../utils';
34
  import ApiKeyModal from './api-key-modal';
35
  import {
36
+ useHandleDeleteLlm,
37
  useSelectModelProvidersLoading,
38
  useSubmitApiKey,
39
  useSubmitOllama,
 
73
  const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
74
  const { visible, switchVisible } = useSetModalState();
75
  const { t } = useTranslate('setting');
76
+ const { handleDeleteLlm } = useHandleDeleteLlm(item.name);
77
 
78
  const handleApiKeyClick = () => {
79
  clickApiKey(item.name);
 
120
  <List.Item>
121
  <Space>
122
  {item.name} <Tag color="#b8b8b8">{item.type}</Tag>
123
+ <Tooltip title={t('delete', { keyPrefix: 'common' })}>
124
+ <Button type={'text'} onClick={handleDeleteLlm(item.name)}>
125
+ <CloseCircleOutlined style={{ color: '#D92D20' }} />
126
+ </Button>
127
+ </Tooltip>
128
  </Space>
129
  </List.Item>
130
  )}
web/src/services/userService.ts CHANGED
@@ -15,6 +15,7 @@ const {
15
  set_api_key,
16
  set_tenant_info,
17
  add_llm,
 
18
  } = api;
19
 
20
  const methods = {
@@ -66,6 +67,10 @@ const methods = {
66
  url: add_llm,
67
  method: 'post',
68
  },
 
 
 
 
69
  } as const;
70
 
71
  const userService = registerServer<keyof typeof methods>(methods, request);
 
15
  set_api_key,
16
  set_tenant_info,
17
  add_llm,
18
+ delete_llm,
19
  } = api;
20
 
21
  const methods = {
 
67
  url: add_llm,
68
  method: 'post',
69
  },
70
+ delete_llm: {
71
+ url: delete_llm,
72
+ method: 'post',
73
+ },
74
  } as const;
75
 
76
  const userService = registerServer<keyof typeof methods>(methods, request);
web/src/utils/api.ts CHANGED
@@ -18,6 +18,7 @@ export default {
18
  my_llm: `${api_host}/llm/my_llms`,
19
  set_api_key: `${api_host}/llm/set_api_key`,
20
  add_llm: `${api_host}/llm/add_llm`,
 
21
 
22
  // knowledge base
23
  kb_list: `${api_host}/kb/list`,
 
18
  my_llm: `${api_host}/llm/my_llms`,
19
  set_api_key: `${api_host}/llm/set_api_key`,
20
  add_llm: `${api_host}/llm/add_llm`,
21
+ delete_llm: `${api_host}/llm/delete_llm`,
22
 
23
  // knowledge base
24
  kb_list: `${api_host}/kb/list`,
web/typings.d.ts CHANGED
@@ -10,7 +10,6 @@ import { LoginModelState } from '@/pages/login/model';
10
  import { SettingModelState } from '@/pages/user-setting/model';
11
 
12
  declare module 'lodash';
13
- declare module 'react-file-viewer';
14
 
15
  function useSelector<TState = RootState, TSelected = unknown>(
16
  selector: (state: TState) => TSelected,
 
10
  import { SettingModelState } from '@/pages/user-setting/model';
11
 
12
  declare module 'lodash';
 
13
 
14
  function useSelector<TState = RootState, TSelected = unknown>(
15
  selector: (state: TState) => TSelected,