Kaynağa Gözat

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)
tags/v0.6.0
balibabu 1 yıl önce
ebeveyn
işleme
d65ba3e4d7
No account linked to committer's email address

+ 1
- 1
web/.umirc.ts Dosyayı Görüntüle

devtool: 'source-map', devtool: 'source-map',
proxy: { proxy: {
'/v1': { '/v1': {
target: 'http://123.60.95.134:9380/',
target: '',
changeOrigin: true, changeOrigin: true,
// pathRewrite: { '^/v1': '/v1' }, // pathRewrite: { '^/v1': '/v1' },
}, },

+ 3002
- 4483
web/package-lock.json
Dosya farkı çok büyük olduğundan ihmal edildi
Dosyayı Görüntüle


+ 1
- 1
web/package.json Dosyayı Görüntüle

"js-base64": "^3.7.5", "js-base64": "^3.7.5",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mammoth": "^1.7.2",
"rc-tween-one": "^3.0.6", "rc-tween-one": "^3.0.6",
"react-chat-elements": "^12.0.13", "react-chat-elements": "^12.0.13",
"react-copy-to-clipboard": "^5.1.0", "react-copy-to-clipboard": "^5.1.0",
"react-file-viewer": "^1.2.1",
"react-i18next": "^14.0.0", "react-i18next": "^14.0.0",
"react-infinite-scroll-component": "^6.1.0", "react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",

+ 2
- 2
web/src/components/pdf-previewer/index.tsx Dosyayı Görüntüle

) : null; ) : null;


const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => { const DocumentPreviewer = ({ chunk, documentId, visible }: IProps) => {
const url = useGetDocumentUrl(documentId);
const getDocumentUrl = useGetDocumentUrl(documentId);
const { highlights: state, setWidthAndHeight } = useGetChunkHighlights(chunk); const { highlights: state, setWidthAndHeight } = useGetChunkHighlights(chunk);
const ref = useRef<(highlight: IHighlight) => void>(() => {}); const ref = useRef<(highlight: IHighlight) => void>(() => {});
const [loaded, setLoaded] = useState(false); const [loaded, setLoaded] = useState(false);
return ( return (
<div className={styles.documentContainer}> <div className={styles.documentContainer}>
<PdfLoader <PdfLoader
url={url}
url={getDocumentUrl()}
beforeLoad={<Skeleton active />} beforeLoad={<Skeleton active />}
workerSrc="/pdfjs-dist/pdf.worker.min.js" workerSrc="/pdfjs-dist/pdf.worker.min.js"
> >

+ 4
- 2
web/src/constants/common.ts Dosyayı Görüntüle

mp4: 'video/mp4', mp4: 'video/mp4',
}; };


export const Domain = 'demo.ragflow.io';

//#region file preview //#region file preview
export const Images = [ export const Images = [
'jpg', 'jpg',
]; ];


// Without FileViewer // 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 //#endregion

+ 22
- 3
web/src/hooks/llmHooks.ts Dosyayı Görüntüle

IMyLlmValue, IMyLlmValue,
IThirdOAIModelCollection, IThirdOAIModelCollection,
} from '@/interfaces/database/llm'; } from '@/interfaces/database/llm';
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import {
IAddLlmRequestBody,
IDeleteLlmRequestBody,
} from '@/interfaces/request/llm';
import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/commonUtil'; import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/commonUtil';
import { useCallback, useEffect, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'umi'; import { useDispatch, useSelector } from 'umi';
export const useAddLlm = () => { export const useAddLlm = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();


const saveTenantInfo = useCallback(
const addLlm = useCallback(
(requestBody: IAddLlmRequestBody) => { (requestBody: IAddLlmRequestBody) => {
return dispatch<any>({ return dispatch<any>({
type: 'settingModel/add_llm', type: 'settingModel/add_llm',
[dispatch], [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;
}; };

+ 5
- 0
web/src/interfaces/request/llm.ts Dosyayı Görüntüle

model_type: string; model_type: string;
api_base?: string; // chat|embedding|speech2text|image2text api_base?: string; // chat|embedding|speech2text|image2text
} }

export interface IDeleteLlmRequestBody {
llm_factory: string; // Ollama
llm_name: string;
}

+ 1
- 0
web/src/locales/en.ts Dosyayı Görüntüle

local: 'Local uploads', local: 'Local uploads',
s3: 'S3 uploads', s3: 'S3 uploads',
preview: 'Preview', preview: 'Preview',
fileError: 'File error',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

+ 1
- 0
web/src/locales/zh-traditional.ts Dosyayı Görüntüle

local: '本地上傳', local: '本地上傳',
s3: 'S3 上傳', s3: 'S3 上傳',
preview: '預覽', preview: '預覽',
fileError: '文件錯誤',
}, },
footer: { footer: {
profile: '“保留所有權利 @ react”', profile: '“保留所有權利 @ react”',

+ 1
- 0
web/src/locales/zh.ts Dosyayı Görüntüle

local: '本地上传', local: '本地上传',
s3: 'S3 上传', s3: 'S3 上传',
preview: '预览', preview: '预览',
fileError: '文件错误',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

+ 1
- 1
web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx Dosyayı Görüntüle

{ {
key: 'process_duation', key: 'process_duation',
label: t('processDuration'), label: t('processDuration'),
children: record.process_duation,
children: `${record.process_duation} s`,
}, },
{ {
key: 'progress_msg', key: 'progress_msg',

+ 4
- 1
web/src/pages/chat/chat-overview-modal/index.tsx Dosyayı Görüntüle

import LineChart from '@/components/line-chart'; import LineChart from '@/components/line-chart';
import { Domain } from '@/constants/common';
import { useSetModalState, useTranslate } from '@/hooks/commonHooks'; import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IDialog, IStats } from '@/interfaces/database/chat'; import { IDialog, IStats } from '@/interfaces/database/chat';
<Flex gap={8} vertical> <Flex gap={8} vertical>
{t('serviceApiEndpoint')} {t('serviceApiEndpoint')}
<Paragraph copyable className={styles.linkText}> <Paragraph copyable className={styles.linkText}>
https://demo.ragflow.io/v1/api/
https://
{location.hostname === Domain ? Domain : '<YOUR_MACHINE_IP>'}
/v1/api/
</Paragraph> </Paragraph>
</Flex> </Flex>
<Space size={'middle'}> <Space size={'middle'}>

+ 2
- 1
web/src/pages/chat/embed-modal/index.tsx Dosyayı Görüntüle

import CopyToClipboard from '@/components/copy-to-clipboard'; import CopyToClipboard from '@/components/copy-to-clipboard';
import HightLightMarkdown from '@/components/highlight-markdown'; import HightLightMarkdown from '@/components/highlight-markdown';
import { Domain } from '@/constants/common';
import { useTranslate } from '@/hooks/commonHooks'; import { useTranslate } from '@/hooks/commonHooks';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { Card, Modal, Tabs, TabsProps } from 'antd'; import { Card, Modal, Tabs, TabsProps } from 'antd';
const text = ` const text = `
~~~ html ~~~ html
<iframe <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" style="width: 100%; height: 100%; min-height: 600px"
frameborder="0" frameborder="0"
> >

+ 281
- 0
web/src/pages/document-viewer/docx/index.less Dosyayı Görüntüle

// 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
- 0
web/src/pages/document-viewer/docx/index.tsx Dosyayı Görüntüle

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;

+ 12
- 28
web/src/pages/document-viewer/excel/index.tsx Dosyayı Görüntüle

import jsPreviewExcel from '@js-preview/excel';
import '@js-preview/excel/lib/index.css'; 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 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; export default Excel;

+ 4
- 0
web/src/pages/document-viewer/file-error/index.less Dosyayı Görüntüle

.errorWrapper {
width: 100%;
height: 100%;
}

+ 15
- 0
web/src/pages/document-viewer/file-error/index.tsx Dosyayı Görüntüle

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
- 0
web/src/pages/document-viewer/hooks.ts Dosyayı Görüntüle

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 };
};

+ 4
- 11
web/src/pages/document-viewer/index.tsx Dosyayı Görüntüle

import { ExceptiveType, Images } from '@/constants/common';
import { Images } from '@/constants/common';
import { api_host } from '@/utils/api'; import { api_host } from '@/utils/api';
import { Flex, Image } from 'antd'; import { Flex, Image } from 'antd';
import FileViewer from 'react-file-viewer';
import { useParams, useSearchParams } from 'umi'; import { useParams, useSearchParams } from 'umi';
import Docx from './docx';
import Excel from './excel'; import Excel from './excel';
import Pdf from './pdf'; import Pdf from './pdf';




// TODO: The interface returns an incorrect content-type for the SVG. // TODO: The interface returns an incorrect content-type for the SVG.


const isNotExceptiveType = (ext: string) => ExceptiveType.indexOf(ext) === -1;

const DocumentViewer = () => { const DocumentViewer = () => {
const { id: documentId } = useParams(); const { id: documentId } = useParams();
const api = `${api_host}/file/get/${documentId}`; const api = `${api_host}/file/get/${documentId}`;
const [currentQueryParameters] = useSearchParams(); const [currentQueryParameters] = useSearchParams();
const ext = currentQueryParameters.get('ext'); const ext = currentQueryParameters.get('ext');


const onError = (e: any) => {
console.error(e, 'error in file-viewer');
};

return ( return (
<section className={styles.viewerWrapper}> <section className={styles.viewerWrapper}>
{Images.includes(ext!) && ( {Images.includes(ext!) && (
)} )}
{ext === 'pdf' && <Pdf url={api}></Pdf>} {ext === 'pdf' && <Pdf url={api}></Pdf>}
{(ext === 'xlsx' || ext === 'xls') && <Excel filePath={api}></Excel>} {(ext === 'xlsx' || ext === 'xls') && <Excel filePath={api}></Excel>}
{isNotExceptiveType(ext!) && (
<FileViewer fileType={ext} filePath={api} onError={onError} />
)}

{ext === 'docx' && <Docx filePath={api}></Docx>}
</section> </section>
); );
}; };

+ 6
- 1
web/src/pages/document-viewer/pdf/index.tsx Dosyayı Görüntüle

import { Skeleton } from 'antd'; import { Skeleton } from 'antd';
import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter'; import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter';
import FileError from '../file-error';


interface IProps { interface IProps {
url: string; url: string;
const resetHash = () => {}; const resetHash = () => {};


return ( return (
<div style={{ width: '100%' }}>
<div style={{ width: '100%', height: '100%' }}>
<PdfLoader <PdfLoader
url={url} url={url}
beforeLoad={<Skeleton active />} beforeLoad={<Skeleton active />}
workerSrc="/pdfjs-dist/pdf.worker.min.js" workerSrc="/pdfjs-dist/pdf.worker.min.js"
errorMessage={<FileError></FileError>}
onError={(e) => {
console.warn(e);
}}
> >
{(pdfDocument) => { {(pdfDocument) => {
return ( return (

+ 2
- 1
web/src/pages/login/index.tsx Dosyayı Görüntüle

import { Icon, useNavigate } from 'umi'; import { Icon, useNavigate } from 'umi';
import RightPanel from './right-panel'; import RightPanel from './right-panel';
import { Domain } from '@/constants/common';
import styles from './index.less'; import styles from './index.less';
const Login = () => { const Login = () => {
Sign in with Google Sign in with Google
</div> </div>
</Button> */} </Button> */}
{location.host === 'demo.ragflow.io' && (
{location.host === Domain && (
<Button <Button
block block
size="large" size="large"

+ 11
- 0
web/src/pages/user-setting/model.ts Dosyayı Görüntüle

} }
return retcode; 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; export default model;

+ 17
- 1
web/src/pages/user-setting/setting-model/hooks.ts Dosyayı Görüntüle

import { useSetModalState } from '@/hooks/commonHooks';
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
import { import {
IApiKeySavingParams, IApiKeySavingParams,
ISystemModelSettingSavingParams, ISystemModelSettingSavingParams,
useAddLlm, useAddLlm,
useDeleteLlm,
useFetchLlmList, useFetchLlmList,
useSaveApiKey, useSaveApiKey,
useSaveTenantInfo, useSaveTenantInfo,
selectedLlmFactory, 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 };
};

+ 13
- 1
web/src/pages/user-setting/setting-model/index.tsx Dosyayı Görüntüle

useFetchLlmFactoryListOnMount, useFetchLlmFactoryListOnMount,
useFetchMyLlmListOnMount, useFetchMyLlmListOnMount,
} from '@/hooks/llmHooks'; } from '@/hooks/llmHooks';
import { SettingOutlined, UserOutlined } from '@ant-design/icons';
import {
CloseCircleOutlined,
SettingOutlined,
UserOutlined,
} from '@ant-design/icons';
import { import {
Avatar, Avatar,
Button, Button,
Space, Space,
Spin, Spin,
Tag, Tag,
Tooltip,
Typography, Typography,
} from 'antd'; } from 'antd';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { isLocalLlmFactory } from '../utils'; import { isLocalLlmFactory } from '../utils';
import ApiKeyModal from './api-key-modal'; import ApiKeyModal from './api-key-modal';
import { import {
useHandleDeleteLlm,
useSelectModelProvidersLoading, useSelectModelProvidersLoading,
useSubmitApiKey, useSubmitApiKey,
useSubmitOllama, useSubmitOllama,
const ModelCard = ({ item, clickApiKey }: IModelCardProps) => { const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
const { visible, switchVisible } = useSetModalState(); const { visible, switchVisible } = useSetModalState();
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { handleDeleteLlm } = useHandleDeleteLlm(item.name);


const handleApiKeyClick = () => { const handleApiKeyClick = () => {
clickApiKey(item.name); clickApiKey(item.name);
<List.Item> <List.Item>
<Space> <Space>
{item.name} <Tag color="#b8b8b8">{item.type}</Tag> {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> </Space>
</List.Item> </List.Item>
)} )}

+ 5
- 0
web/src/services/userService.ts Dosyayı Görüntüle

set_api_key, set_api_key,
set_tenant_info, set_tenant_info,
add_llm, add_llm,
delete_llm,
} = api; } = api;
const methods = { const methods = {
url: add_llm, url: add_llm,
method: 'post', method: 'post',
}, },
delete_llm: {
url: delete_llm,
method: 'post',
},
} as const; } as const;
const userService = registerServer<keyof typeof methods>(methods, request); const userService = registerServer<keyof typeof methods>(methods, request);

+ 1
- 0
web/src/utils/api.ts Dosyayı Görüntüle

my_llm: `${api_host}/llm/my_llms`, my_llm: `${api_host}/llm/my_llms`,
set_api_key: `${api_host}/llm/set_api_key`, set_api_key: `${api_host}/llm/set_api_key`,
add_llm: `${api_host}/llm/add_llm`, add_llm: `${api_host}/llm/add_llm`,
delete_llm: `${api_host}/llm/delete_llm`,
// knowledge base // knowledge base
kb_list: `${api_host}/kb/list`, kb_list: `${api_host}/kb/list`,

+ 0
- 1
web/typings.d.ts Dosyayı Görüntüle

import { SettingModelState } from '@/pages/user-setting/model'; import { SettingModelState } from '@/pages/user-setting/model';
declare module 'lodash'; declare module 'lodash';
declare module 'react-file-viewer';
function useSelector<TState = RootState, TSelected = unknown>( function useSelector<TState = RootState, TSelected = unknown>(
selector: (state: TState) => TSelected, selector: (state: TState) => TSelected,

Loading…
İptal
Kaydet