mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-11-01 10:19:34 +00:00
feat: delete the added model #503 and display an error message when the requested file fails to parse #684 (#708)
### 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)
This commit is contained in:
parent
bef1bbdf3e
commit
d65ba3e4d7
@ -27,7 +27,7 @@ export default defineConfig({
|
||||
devtool: 'source-map',
|
||||
proxy: {
|
||||
'/v1': {
|
||||
target: 'http://123.60.95.134:9380/',
|
||||
target: '',
|
||||
changeOrigin: true,
|
||||
// pathRewrite: { '^/v1': '/v1' },
|
||||
},
|
||||
|
||||
1883
web/package-lock.json
generated
1883
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -23,10 +23,10 @@
|
||||
"js-base64": "^3.7.5",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mammoth": "^1.7.2",
|
||||
"rc-tween-one": "^3.0.6",
|
||||
"react-chat-elements": "^12.0.13",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-file-viewer": "^1.2.1",
|
||||
"react-i18next": "^14.0.0",
|
||||
"react-infinite-scroll-component": "^6.1.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
|
||||
@ -34,7 +34,7 @@ const HighlightPopup = ({
|
||||
) : null;
|
||||
|
||||
const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
|
||||
const url = useGetDocumentUrl(documentId);
|
||||
const getDocumentUrl = useGetDocumentUrl(documentId);
|
||||
const { highlights: state, setWidthAndHeight } = useGetChunkHighlights(chunk);
|
||||
const ref = useRef<(highlight: IHighlight) => void>(() => {});
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
@ -55,7 +55,7 @@ const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
|
||||
return (
|
||||
<div className={styles.documentContainer}>
|
||||
<PdfLoader
|
||||
url={url}
|
||||
url={getDocumentUrl()}
|
||||
beforeLoad={<Skeleton active />}
|
||||
workerSrc="/pdfjs-dist/pdf.worker.min.js"
|
||||
>
|
||||
|
||||
@ -69,6 +69,8 @@ export const FileMimeTypeMap = {
|
||||
mp4: 'video/mp4',
|
||||
};
|
||||
|
||||
export const Domain = 'demo.ragflow.io';
|
||||
|
||||
//#region file preview
|
||||
export const Images = [
|
||||
'jpg',
|
||||
@ -84,7 +86,7 @@ export const Images = [
|
||||
];
|
||||
|
||||
// Without FileViewer
|
||||
export const ExceptiveType = ['xlsx', 'xls', 'pdf', ...Images];
|
||||
export const ExceptiveType = ['xlsx', 'xls', 'pdf', 'docx', ...Images];
|
||||
|
||||
export const SupportedPreviewDocumentTypes = ['docx', 'csv', ...ExceptiveType];
|
||||
export const SupportedPreviewDocumentTypes = [...ExceptiveType];
|
||||
//#endregion
|
||||
|
||||
@ -4,7 +4,10 @@ import {
|
||||
IMyLlmValue,
|
||||
IThirdOAIModelCollection,
|
||||
} from '@/interfaces/database/llm';
|
||||
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
|
||||
import {
|
||||
IAddLlmRequestBody,
|
||||
IDeleteLlmRequestBody,
|
||||
} from '@/interfaces/request/llm';
|
||||
import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/commonUtil';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
@ -211,7 +214,7 @@ export const useSaveTenantInfo = () => {
|
||||
export const useAddLlm = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const saveTenantInfo = useCallback(
|
||||
const addLlm = useCallback(
|
||||
(requestBody: IAddLlmRequestBody) => {
|
||||
return dispatch<any>({
|
||||
type: 'settingModel/add_llm',
|
||||
@ -221,5 +224,21 @@ export const useAddLlm = () => {
|
||||
[dispatch],
|
||||
);
|
||||
|
||||
return saveTenantInfo;
|
||||
return addLlm;
|
||||
};
|
||||
|
||||
export const useDeleteLlm = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const deleteLlm = useCallback(
|
||||
(requestBody: IDeleteLlmRequestBody) => {
|
||||
return dispatch<any>({
|
||||
type: 'settingModel/delete_llm',
|
||||
payload: requestBody,
|
||||
});
|
||||
},
|
||||
[dispatch],
|
||||
);
|
||||
|
||||
return deleteLlm;
|
||||
};
|
||||
|
||||
@ -4,3 +4,8 @@ export interface IAddLlmRequestBody {
|
||||
model_type: string;
|
||||
api_base?: string; // chat|embedding|speech2text|image2text
|
||||
}
|
||||
|
||||
export interface IDeleteLlmRequestBody {
|
||||
llm_factory: string; // Ollama
|
||||
llm_name: string;
|
||||
}
|
||||
|
||||
@ -506,6 +506,7 @@ export default {
|
||||
local: 'Local uploads',
|
||||
s3: 'S3 uploads',
|
||||
preview: 'Preview',
|
||||
fileError: 'File error',
|
||||
},
|
||||
footer: {
|
||||
profile: 'All rights reserved @ React',
|
||||
|
||||
@ -469,6 +469,7 @@ export default {
|
||||
local: '本地上傳',
|
||||
s3: 'S3 上傳',
|
||||
preview: '預覽',
|
||||
fileError: '文件錯誤',
|
||||
},
|
||||
footer: {
|
||||
profile: '“保留所有權利 @ react”',
|
||||
|
||||
@ -487,6 +487,7 @@ export default {
|
||||
local: '本地上传',
|
||||
s3: 'S3 上传',
|
||||
preview: '预览',
|
||||
fileError: '文件错误',
|
||||
},
|
||||
footer: {
|
||||
profile: 'All rights reserved @ React',
|
||||
|
||||
@ -55,7 +55,7 @@ const PopoverContent = ({ record }: IProps) => {
|
||||
{
|
||||
key: 'process_duation',
|
||||
label: t('processDuration'),
|
||||
children: record.process_duation,
|
||||
children: `${record.process_duation} s`,
|
||||
},
|
||||
{
|
||||
key: 'progress_msg',
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import LineChart from '@/components/line-chart';
|
||||
import { Domain } from '@/constants/common';
|
||||
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IDialog, IStats } from '@/interfaces/database/chat';
|
||||
@ -80,7 +81,9 @@ const ChatOverviewModal = ({
|
||||
<Flex gap={8} vertical>
|
||||
{t('serviceApiEndpoint')}
|
||||
<Paragraph copyable className={styles.linkText}>
|
||||
https://demo.ragflow.io/v1/api/
|
||||
https://
|
||||
{location.hostname === Domain ? Domain : '<YOUR_MACHINE_IP>'}
|
||||
/v1/api/
|
||||
</Paragraph>
|
||||
</Flex>
|
||||
<Space size={'middle'}>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import CopyToClipboard from '@/components/copy-to-clipboard';
|
||||
import HightLightMarkdown from '@/components/highlight-markdown';
|
||||
import { Domain } from '@/constants/common';
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { Card, Modal, Tabs, TabsProps } from 'antd';
|
||||
@ -15,7 +16,7 @@ const EmbedModal = ({
|
||||
const text = `
|
||||
~~~ html
|
||||
<iframe
|
||||
src="https://demo.ragflow.io/chat/share?shared_id=${token}"
|
||||
src="https://${Domain}/chat/share?shared_id=${token}"
|
||||
style="width: 100%; height: 100%; min-height: 600px"
|
||||
frameborder="0"
|
||||
>
|
||||
|
||||
281
web/src/pages/document-viewer/docx/index.less
Normal file
281
web/src/pages/document-viewer/docx/index.less
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright (c) 2017 PlanGrid, Inc.
|
||||
|
||||
.docxViewerWrapper {
|
||||
overflow-y: scroll;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:global(.document-container) {
|
||||
padding: 30px;
|
||||
width: 700px;
|
||||
background: white;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
html,
|
||||
bodyaddress,
|
||||
blockquote,
|
||||
body,
|
||||
dd,
|
||||
div,
|
||||
dl,
|
||||
dt,
|
||||
fieldset,
|
||||
form,
|
||||
frame,
|
||||
frameset,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
noframes,
|
||||
ol,
|
||||
p,
|
||||
ul,
|
||||
center,
|
||||
dir,
|
||||
hr,
|
||||
menu,
|
||||
pre {
|
||||
display: block;
|
||||
unicode-bidi: embed;
|
||||
}
|
||||
li {
|
||||
display: list-item;
|
||||
list-style-type: disc;
|
||||
}
|
||||
head {
|
||||
display: none;
|
||||
}
|
||||
table {
|
||||
display: table;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
tr {
|
||||
display: table-row;
|
||||
}
|
||||
thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
tbody {
|
||||
display: table-row-group;
|
||||
}
|
||||
tfoot {
|
||||
display: table-footer-group;
|
||||
}
|
||||
col {
|
||||
display: table-column;
|
||||
}
|
||||
colgroup {
|
||||
display: table-column-group;
|
||||
}
|
||||
th {
|
||||
display: table-cell;
|
||||
}
|
||||
td {
|
||||
display: table-cell;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
caption {
|
||||
display: table-caption;
|
||||
}
|
||||
th {
|
||||
font-weight: bolder;
|
||||
text-align: center;
|
||||
}
|
||||
caption {
|
||||
text-align: center;
|
||||
}
|
||||
body {
|
||||
margin: 8px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.17em;
|
||||
margin: 0.83em 0;
|
||||
}
|
||||
h4,
|
||||
p,
|
||||
blockquote,
|
||||
ul,
|
||||
fieldset,
|
||||
form,
|
||||
ol,
|
||||
dl,
|
||||
dir,
|
||||
menu {
|
||||
margin: 1.12em 0;
|
||||
}
|
||||
h5 {
|
||||
font-size: 0.83em;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
h6 {
|
||||
font-size: 0.75em;
|
||||
margin: 1.67em 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
blockquote {
|
||||
margin-left: 40px;
|
||||
margin-right: 40px;
|
||||
}
|
||||
i,
|
||||
cite,
|
||||
em,
|
||||
var,
|
||||
address {
|
||||
font-style: italic;
|
||||
}
|
||||
pre,
|
||||
tt,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace;
|
||||
}
|
||||
pre {
|
||||
white-space: pre;
|
||||
}
|
||||
button,
|
||||
textarea,
|
||||
input,
|
||||
select {
|
||||
display: inline-block;
|
||||
}
|
||||
big {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
small,
|
||||
sub,
|
||||
sup {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
sub {
|
||||
vertical-align: sub;
|
||||
}
|
||||
sup {
|
||||
vertical-align: super;
|
||||
}
|
||||
table {
|
||||
border-spacing: 2px;
|
||||
}
|
||||
thead,
|
||||
tbody,
|
||||
tfoot {
|
||||
vertical-align: middle;
|
||||
}
|
||||
td,
|
||||
th,
|
||||
tr {
|
||||
vertical-align: inherit;
|
||||
}
|
||||
s,
|
||||
strike,
|
||||
del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
hr {
|
||||
border: 1px inset;
|
||||
}
|
||||
ol,
|
||||
ul,
|
||||
dir,
|
||||
menu,
|
||||
dd {
|
||||
margin-left: 40px;
|
||||
}
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
ol ul,
|
||||
ol ul,
|
||||
ul ol,
|
||||
ul ol,
|
||||
ul ul,
|
||||
ul ul,
|
||||
ol ol,
|
||||
ol ol {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
u,
|
||||
ins {
|
||||
text-decoration: underline;
|
||||
}
|
||||
br:before {
|
||||
content: '\A';
|
||||
white-space: pre-line;
|
||||
}
|
||||
center {
|
||||
text-align: center;
|
||||
}
|
||||
:link,
|
||||
:visited {
|
||||
text-decoration: underline;
|
||||
}
|
||||
:focus {
|
||||
outline: thin dotted invert;
|
||||
}
|
||||
/* Begin bidirectionality settings (do not change) */
|
||||
BDO[DIR='ltr'] {
|
||||
direction: ltr;
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
BDO[DIR='rtl'] {
|
||||
direction: rtl;
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
*[DIR='ltr'] {
|
||||
direction: ltr;
|
||||
unicode-bidi: embed;
|
||||
}
|
||||
*[DIR='rtl'] {
|
||||
direction: rtl;
|
||||
unicode-bidi: embed;
|
||||
}
|
||||
@media print {
|
||||
h1 {
|
||||
page-break-before: always;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
ul,
|
||||
ol,
|
||||
dl {
|
||||
page-break-before: avoid;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
web/src/pages/document-viewer/docx/index.tsx
Normal file
25
web/src/pages/document-viewer/docx/index.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Spin } from 'antd';
|
||||
import FileError from '../file-error';
|
||||
|
||||
import { useFetchDocx } from '../hooks';
|
||||
import styles from './index.less';
|
||||
|
||||
const Docx = ({ filePath }: { filePath: string }) => {
|
||||
const { succeed, containerRef } = useFetchDocx(filePath);
|
||||
|
||||
return (
|
||||
<>
|
||||
{succeed ? (
|
||||
<section className={styles.docxViewerWrapper}>
|
||||
<div id="docx" ref={containerRef} className={styles.box}>
|
||||
<Spin />
|
||||
</div>
|
||||
</section>
|
||||
) : (
|
||||
<FileError></FileError>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Docx;
|
||||
@ -1,35 +1,19 @@
|
||||
import jsPreviewExcel from '@js-preview/excel';
|
||||
import '@js-preview/excel/lib/index.css';
|
||||
import { useEffect } from 'react';
|
||||
import FileError from '../file-error';
|
||||
import { useFetchExcel } from '../hooks';
|
||||
|
||||
const Excel = ({ filePath }: { filePath: string }) => {
|
||||
const fetchDocument = async () => {
|
||||
const myExcelPreviewer = jsPreviewExcel.init(
|
||||
document.getElementById('excel'),
|
||||
);
|
||||
const jsonFile = new XMLHttpRequest();
|
||||
jsonFile.open('GET', filePath, true);
|
||||
jsonFile.send();
|
||||
jsonFile.responseType = 'arraybuffer';
|
||||
jsonFile.onreadystatechange = () => {
|
||||
if (jsonFile.readyState === 4 && jsonFile.status === 200) {
|
||||
myExcelPreviewer
|
||||
.preview(jsonFile.response)
|
||||
.then((res: any) => {
|
||||
console.log('succeed');
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log('failed', e);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
const { status, containerRef } = useFetchExcel(filePath);
|
||||
|
||||
useEffect(() => {
|
||||
fetchDocument();
|
||||
}, []);
|
||||
|
||||
return <div id="excel" style={{ height: '100%' }}></div>;
|
||||
return (
|
||||
<div
|
||||
id="excel"
|
||||
ref={containerRef}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
>
|
||||
{status || <FileError></FileError>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Excel;
|
||||
|
||||
4
web/src/pages/document-viewer/file-error/index.less
Normal file
4
web/src/pages/document-viewer/file-error/index.less
Normal file
@ -0,0 +1,4 @@
|
||||
.errorWrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
15
web/src/pages/document-viewer/file-error/index.tsx
Normal file
15
web/src/pages/document-viewer/file-error/index.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { Alert, Flex } from 'antd';
|
||||
|
||||
import { useTranslate } from '@/hooks/commonHooks';
|
||||
import styles from './index.less';
|
||||
|
||||
const FileError = () => {
|
||||
const { t } = useTranslate('fileManager');
|
||||
return (
|
||||
<Flex align="center" justify="center" className={styles.errorWrapper}>
|
||||
<Alert type="error" message={<h1>{t('fileError')}</h1>}></Alert>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileError;
|
||||
78
web/src/pages/document-viewer/hooks.ts
Normal file
78
web/src/pages/document-viewer/hooks.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import jsPreviewExcel from '@js-preview/excel';
|
||||
import axios from 'axios';
|
||||
import mammoth from 'mammoth';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
const useFetchDocument = () => {
|
||||
const fetchDocument = useCallback((api: string) => {
|
||||
return axios.get(api, { responseType: 'arraybuffer' });
|
||||
}, []);
|
||||
|
||||
return fetchDocument;
|
||||
};
|
||||
|
||||
export const useFetchExcel = (filePath: string) => {
|
||||
const [status, setStatus] = useState(true);
|
||||
const fetchDocument = useFetchDocument();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const fetchDocumentAsync = useCallback(async () => {
|
||||
let myExcelPreviewer;
|
||||
if (containerRef.current) {
|
||||
myExcelPreviewer = jsPreviewExcel.init(containerRef.current);
|
||||
}
|
||||
const jsonFile = await fetchDocument(filePath);
|
||||
myExcelPreviewer
|
||||
?.preview(jsonFile.data)
|
||||
.then(() => {
|
||||
console.log('succeed');
|
||||
setStatus(true);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.warn('failed', e);
|
||||
myExcelPreviewer.destroy();
|
||||
setStatus(false);
|
||||
});
|
||||
}, [filePath, fetchDocument]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchDocumentAsync();
|
||||
}, [fetchDocumentAsync]);
|
||||
|
||||
return { status, containerRef };
|
||||
};
|
||||
|
||||
export const useFetchDocx = (filePath: string) => {
|
||||
const [succeed, setSucceed] = useState(true);
|
||||
const fetchDocument = useFetchDocument();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const fetchDocumentAsync = useCallback(async () => {
|
||||
const jsonFile = await fetchDocument(filePath);
|
||||
mammoth
|
||||
.convertToHtml(
|
||||
{ arrayBuffer: jsonFile.data },
|
||||
{ includeDefaultStyleMap: true },
|
||||
)
|
||||
.then((result) => {
|
||||
setSucceed(true);
|
||||
const docEl = document.createElement('div');
|
||||
docEl.className = 'document-container';
|
||||
docEl.innerHTML = result.value;
|
||||
const container = containerRef.current;
|
||||
if (container) {
|
||||
container.innerHTML = docEl.outerHTML;
|
||||
}
|
||||
})
|
||||
.catch((a) => {
|
||||
setSucceed(false);
|
||||
console.warn('alexei: something went wrong', a);
|
||||
});
|
||||
}, [filePath, fetchDocument]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchDocumentAsync();
|
||||
}, [fetchDocumentAsync]);
|
||||
|
||||
return { succeed, containerRef };
|
||||
};
|
||||
@ -1,8 +1,8 @@
|
||||
import { ExceptiveType, Images } from '@/constants/common';
|
||||
import { Images } from '@/constants/common';
|
||||
import { api_host } from '@/utils/api';
|
||||
import { Flex, Image } from 'antd';
|
||||
import FileViewer from 'react-file-viewer';
|
||||
import { useParams, useSearchParams } from 'umi';
|
||||
import Docx from './docx';
|
||||
import Excel from './excel';
|
||||
import Pdf from './pdf';
|
||||
|
||||
@ -10,18 +10,12 @@ import styles from './index.less';
|
||||
|
||||
// TODO: The interface returns an incorrect content-type for the SVG.
|
||||
|
||||
const isNotExceptiveType = (ext: string) => ExceptiveType.indexOf(ext) === -1;
|
||||
|
||||
const DocumentViewer = () => {
|
||||
const { id: documentId } = useParams();
|
||||
const api = `${api_host}/file/get/${documentId}`;
|
||||
const [currentQueryParameters] = useSearchParams();
|
||||
const ext = currentQueryParameters.get('ext');
|
||||
|
||||
const onError = (e: any) => {
|
||||
console.error(e, 'error in file-viewer');
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={styles.viewerWrapper}>
|
||||
{Images.includes(ext!) && (
|
||||
@ -31,9 +25,8 @@ const DocumentViewer = () => {
|
||||
)}
|
||||
{ext === 'pdf' && <Pdf url={api}></Pdf>}
|
||||
{(ext === 'xlsx' || ext === 'xls') && <Excel filePath={api}></Excel>}
|
||||
{isNotExceptiveType(ext!) && (
|
||||
<FileViewer fileType={ext} filePath={api} onError={onError} />
|
||||
)}
|
||||
|
||||
{ext === 'docx' && <Docx filePath={api}></Docx>}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Skeleton } from 'antd';
|
||||
import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter';
|
||||
import FileError from '../file-error';
|
||||
|
||||
interface IProps {
|
||||
url: string;
|
||||
@ -9,11 +10,15 @@ const DocumentPreviewer = ({ url }: IProps) => {
|
||||
const resetHash = () => {};
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%' }}>
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<PdfLoader
|
||||
url={url}
|
||||
beforeLoad={<Skeleton active />}
|
||||
workerSrc="/pdfjs-dist/pdf.worker.min.js"
|
||||
errorMessage={<FileError></FileError>}
|
||||
onError={(e) => {
|
||||
console.warn(e);
|
||||
}}
|
||||
>
|
||||
{(pdfDocument) => {
|
||||
return (
|
||||
|
||||
@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Icon, useNavigate } from 'umi';
|
||||
import RightPanel from './right-panel';
|
||||
|
||||
import { Domain } from '@/constants/common';
|
||||
import styles from './index.less';
|
||||
|
||||
const Login = () => {
|
||||
@ -167,7 +168,7 @@ const Login = () => {
|
||||
Sign in with Google
|
||||
</div>
|
||||
</Button> */}
|
||||
{location.host === 'demo.ragflow.io' && (
|
||||
{location.host === Domain && (
|
||||
<Button
|
||||
block
|
||||
size="large"
|
||||
|
||||
@ -167,6 +167,17 @@ const model: DvaModel<SettingModelState> = {
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
*delete_llm({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(userService.delete_llm, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success(i18n.t('message.deleted'));
|
||||
|
||||
yield put({ type: 'my_llm' });
|
||||
yield put({ type: 'factories_list' });
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
},
|
||||
};
|
||||
export default model;
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { useSetModalState } from '@/hooks/commonHooks';
|
||||
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
|
||||
import {
|
||||
IApiKeySavingParams,
|
||||
ISystemModelSettingSavingParams,
|
||||
useAddLlm,
|
||||
useDeleteLlm,
|
||||
useFetchLlmList,
|
||||
useSaveApiKey,
|
||||
useSaveTenantInfo,
|
||||
@ -164,3 +165,18 @@ export const useSubmitOllama = () => {
|
||||
selectedLlmFactory,
|
||||
};
|
||||
};
|
||||
|
||||
export const useHandleDeleteLlm = (llmFactory: string) => {
|
||||
const deleteLlm = useDeleteLlm();
|
||||
const showDeleteConfirm = useShowDeleteConfirm();
|
||||
|
||||
const handleDeleteLlm = (name: string) => () => {
|
||||
showDeleteConfirm({
|
||||
onOk: async () => {
|
||||
deleteLlm({ llm_factory: llmFactory, llm_name: name });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return { handleDeleteLlm };
|
||||
};
|
||||
|
||||
@ -6,7 +6,11 @@ import {
|
||||
useFetchLlmFactoryListOnMount,
|
||||
useFetchMyLlmListOnMount,
|
||||
} from '@/hooks/llmHooks';
|
||||
import { SettingOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
CloseCircleOutlined,
|
||||
SettingOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
@ -21,6 +25,7 @@ import {
|
||||
Space,
|
||||
Spin,
|
||||
Tag,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { useCallback } from 'react';
|
||||
@ -28,6 +33,7 @@ import SettingTitle from '../components/setting-title';
|
||||
import { isLocalLlmFactory } from '../utils';
|
||||
import ApiKeyModal from './api-key-modal';
|
||||
import {
|
||||
useHandleDeleteLlm,
|
||||
useSelectModelProvidersLoading,
|
||||
useSubmitApiKey,
|
||||
useSubmitOllama,
|
||||
@ -67,6 +73,7 @@ interface IModelCardProps {
|
||||
const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
|
||||
const { visible, switchVisible } = useSetModalState();
|
||||
const { t } = useTranslate('setting');
|
||||
const { handleDeleteLlm } = useHandleDeleteLlm(item.name);
|
||||
|
||||
const handleApiKeyClick = () => {
|
||||
clickApiKey(item.name);
|
||||
@ -113,6 +120,11 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
|
||||
<List.Item>
|
||||
<Space>
|
||||
{item.name} <Tag color="#b8b8b8">{item.type}</Tag>
|
||||
<Tooltip title={t('delete', { keyPrefix: 'common' })}>
|
||||
<Button type={'text'} onClick={handleDeleteLlm(item.name)}>
|
||||
<CloseCircleOutlined style={{ color: '#D92D20' }} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
@ -15,6 +15,7 @@ const {
|
||||
set_api_key,
|
||||
set_tenant_info,
|
||||
add_llm,
|
||||
delete_llm,
|
||||
} = api;
|
||||
|
||||
const methods = {
|
||||
@ -66,6 +67,10 @@ const methods = {
|
||||
url: add_llm,
|
||||
method: 'post',
|
||||
},
|
||||
delete_llm: {
|
||||
url: delete_llm,
|
||||
method: 'post',
|
||||
},
|
||||
} as const;
|
||||
|
||||
const userService = registerServer<keyof typeof methods>(methods, request);
|
||||
|
||||
@ -18,6 +18,7 @@ export default {
|
||||
my_llm: `${api_host}/llm/my_llms`,
|
||||
set_api_key: `${api_host}/llm/set_api_key`,
|
||||
add_llm: `${api_host}/llm/add_llm`,
|
||||
delete_llm: `${api_host}/llm/delete_llm`,
|
||||
|
||||
// knowledge base
|
||||
kb_list: `${api_host}/kb/list`,
|
||||
|
||||
1
web/typings.d.ts
vendored
1
web/typings.d.ts
vendored
@ -10,7 +10,6 @@ import { LoginModelState } from '@/pages/login/model';
|
||||
import { SettingModelState } from '@/pages/user-setting/model';
|
||||
|
||||
declare module 'lodash';
|
||||
declare module 'react-file-viewer';
|
||||
|
||||
function useSelector<TState = RootState, TSelected = unknown>(
|
||||
selector: (state: TState) => TSelected,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user