chore: add cancel query model
This commit is contained in:
@@ -3,13 +3,16 @@ import SimpleBar from 'simplebar-react';
|
||||
import 'simplebar-react/dist/simplebar.min.css';
|
||||
import '../style/column-wrapper.less';
|
||||
|
||||
const ColumnWrapper: React.FC<any> = ({ children, footer }) => {
|
||||
const ColumnWrapper: React.FC<any> = ({ children, footer, height }) => {
|
||||
if (footer) {
|
||||
return (
|
||||
<div className="column-wrapper-footer">
|
||||
<div className="column-wrapper">
|
||||
<SimpleBar
|
||||
style={{ height: 'calc(100vh - 89px)', paddingBottom: '50px' }}
|
||||
style={{
|
||||
height: height || 'calc(100vh - 89px)',
|
||||
paddingBottom: '50px'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SimpleBar>
|
||||
@@ -20,7 +23,9 @@ const ColumnWrapper: React.FC<any> = ({ children, footer }) => {
|
||||
}
|
||||
return (
|
||||
<div className="column-wrapper">
|
||||
<SimpleBar style={{ height: 'calc(100vh - 89px)' }}>{children}</SimpleBar>
|
||||
<SimpleBar style={{ height: height || 'calc(100vh - 89px)' }}>
|
||||
{children}
|
||||
</SimpleBar>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { PageActionType } from '@/config/types';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Drawer } from 'antd';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { modelSourceMap } from '../config';
|
||||
import { FormData, ListItem } from '../config/types';
|
||||
import ColumnWrapper from './column-wrapper';
|
||||
@@ -38,6 +38,8 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
const form = useRef<any>({});
|
||||
const intl = useIntl();
|
||||
const [huggingfaceRepoId, setHuggingfaceRepoId] = useState<string>('');
|
||||
const [collapsed, setCollapsed] = useState<boolean>(false);
|
||||
const [loadingModel, setLoadingModel] = useState<boolean>(false);
|
||||
|
||||
const handleSelectModelFile = useCallback((item: any) => {
|
||||
form.current?.setFieldValue?.('huggingface_filename', item.path);
|
||||
@@ -100,15 +102,22 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
<SearchModel
|
||||
modelSource={props.source}
|
||||
onSelectModel={handleOnSelectModel}
|
||||
setLoadingModel={setLoadingModel}
|
||||
></SearchModel>
|
||||
</ColumnWrapper>
|
||||
)}
|
||||
{props.source === modelSourceMap.huggingface_value && (
|
||||
<ColumnWrapper>
|
||||
<ModelCard repo={huggingfaceRepoId}></ModelCard>
|
||||
<ModelCard
|
||||
repo={huggingfaceRepoId}
|
||||
onCollapse={setCollapsed}
|
||||
collapsed={collapsed}
|
||||
></ModelCard>
|
||||
<HFModelFile
|
||||
repo={huggingfaceRepoId}
|
||||
onSelectFile={handleSelectModelFile}
|
||||
collapsed={collapsed}
|
||||
loadingModel={loadingModel}
|
||||
></HFModelFile>
|
||||
</ColumnWrapper>
|
||||
)}
|
||||
@@ -126,9 +135,11 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
}
|
||||
>
|
||||
<>
|
||||
<TitleWrapper>
|
||||
{intl.formatMessage({ id: 'models.form.configurations' })}
|
||||
</TitleWrapper>
|
||||
{source === modelSourceMap.huggingface_value && (
|
||||
<TitleWrapper>
|
||||
{intl.formatMessage({ id: 'models.form.configurations' })}
|
||||
</TitleWrapper>
|
||||
)}
|
||||
<DataForm
|
||||
source={source}
|
||||
action={action}
|
||||
@@ -143,4 +154,4 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AddModal;
|
||||
export default memo(AddModal);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { convertFileSize } from '@/utils';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Col, Empty, Row, Space, Spin, Tag } from 'antd';
|
||||
import { Col, Empty, Row, Select, Space, Spin, Tag } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import SimpleBar from 'simplebar-react';
|
||||
import 'simplebar-react/dist/simplebar.min.css';
|
||||
import { queryHuggingfaceModelFiles } from '../apis';
|
||||
import FileType from '../config/file-type';
|
||||
import '../style/hf-model-file.less';
|
||||
@@ -12,16 +14,31 @@ import TitleWrapper from './title-wrapper';
|
||||
|
||||
interface HFModelFileProps {
|
||||
repo: string;
|
||||
collapsed?: boolean;
|
||||
loadingModel?: boolean;
|
||||
onSelectFile?: (file: any) => void;
|
||||
}
|
||||
|
||||
const HFModelFile: React.FC<HFModelFileProps> = (props) => {
|
||||
const { collapsed, loadingModel } = props;
|
||||
const intl = useIntl();
|
||||
const [dataSource, setDataSource] = useState<any>({
|
||||
fileList: [],
|
||||
loading: false
|
||||
});
|
||||
|
||||
const [sortType, setSortType] = useState<string>('size');
|
||||
const [current, setCurrent] = useState<string>('');
|
||||
const modelFilesSortOptions = useRef<any[]>([
|
||||
{
|
||||
label: intl.formatMessage({ id: 'models.sort.size' }),
|
||||
value: 'size'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'models.sort.name' }),
|
||||
value: 'name'
|
||||
}
|
||||
]);
|
||||
const axiosTokenRef = useRef<any>(null);
|
||||
|
||||
const handleSelectModelFile = (item: any) => {
|
||||
props.onSelectFile?.(item);
|
||||
@@ -34,14 +51,23 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
|
||||
handleSelectModelFile({});
|
||||
return;
|
||||
}
|
||||
axiosTokenRef.current?.abort?.();
|
||||
axiosTokenRef.current = new AbortController();
|
||||
setDataSource({ ...dataSource, loading: true });
|
||||
setCurrent('');
|
||||
try {
|
||||
const res = await queryHuggingfaceModelFiles({ repo: props.repo });
|
||||
const res = await queryHuggingfaceModelFiles(
|
||||
{ repo: props.repo },
|
||||
{
|
||||
signal: axiosTokenRef.current.signal
|
||||
}
|
||||
);
|
||||
const list = _.filter(res, (file: any) => {
|
||||
return _.endsWith(file.path, '.gguf') || _.includes(file.path, '.gguf');
|
||||
});
|
||||
const sortList = _.sortBy(list, (item: any) => item.size);
|
||||
const sortList = _.sortBy(list, (item: any) => {
|
||||
return sortType === 'size' ? item.size : item.path;
|
||||
});
|
||||
setDataSource({ fileList: sortList, loading: false });
|
||||
handleSelectModelFile(list[0]);
|
||||
} catch (error) {
|
||||
@@ -50,6 +76,14 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSortChange = (value: string) => {
|
||||
const list = _.sortBy(dataSource.fileList, (item: any) => {
|
||||
return value === 'size' ? item.size : item.path;
|
||||
});
|
||||
setSortType(value);
|
||||
setDataSource({ ...dataSource, fileList: list });
|
||||
};
|
||||
|
||||
const getModelQuantizationType = (item: any) => {
|
||||
const name = _.split(item.path, '.').slice(0, -1).join('.');
|
||||
let quanType = _.toUpper(name.split('-').slice(-1)[0]);
|
||||
@@ -77,76 +111,93 @@ const HFModelFile: React.FC<HFModelFileProps> = (props) => {
|
||||
useEffect(() => {
|
||||
handleFetchModelFiles();
|
||||
}, [props.repo]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
axiosTokenRef.current?.abort?.();
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<div>
|
||||
<TitleWrapper>
|
||||
<span>Available Files ({dataSource.fileList.length || 0})</span>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'models.available.files' })} (
|
||||
{dataSource.fileList.length || 0})
|
||||
</span>
|
||||
<Select
|
||||
value={sortType}
|
||||
onChange={handleSortChange}
|
||||
labelRender={({ label }) => {
|
||||
return (
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'model.deploy.sort' })}: {label}
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
options={modelFilesSortOptions.current}
|
||||
size="middle"
|
||||
style={{ width: '120px' }}
|
||||
></Select>
|
||||
</TitleWrapper>
|
||||
<div style={{ padding: '16px 24px' }}>
|
||||
<Spin spinning={dataSource.loading} style={{ minHeight: 100 }}>
|
||||
{dataSource.fileList.length ? (
|
||||
<Row gutter={[16, 24]}>
|
||||
{_.map(dataSource.fileList, (item: any) => {
|
||||
return (
|
||||
<Col span={24} key={item.path}>
|
||||
<div
|
||||
className={classNames('hf-model-file', {
|
||||
active: item.path === current
|
||||
})}
|
||||
tabIndex={0}
|
||||
onClick={() => handleSelectModelFile(item)}
|
||||
onKeyDown={(e) => handleOnEnter(e, item)}
|
||||
>
|
||||
<div className="title">{item.path}</div>
|
||||
<Space className="tags">
|
||||
{/* <span className="tag-item">
|
||||
{convertFileSize(item.size)}
|
||||
</span> */}
|
||||
<Tag
|
||||
className="tag-item"
|
||||
color="green"
|
||||
style={{
|
||||
marginRight: 0
|
||||
}}
|
||||
>
|
||||
<span style={{ opacity: 0.65 }}>
|
||||
{convertFileSize(item.size)}
|
||||
</span>
|
||||
</Tag>
|
||||
{getModelQuantizationType(item)}
|
||||
</Space>
|
||||
<div className="btn">
|
||||
{/* <Button size="middle">
|
||||
{item.path === current
|
||||
? intl.formatMessage({
|
||||
id: 'common.button.selected'
|
||||
})
|
||||
: intl.formatMessage({
|
||||
id: 'common.button.select'
|
||||
})}
|
||||
</Button> */}
|
||||
<SimpleBar
|
||||
style={{ maxHeight: collapsed ? 'max-content' : 'calc(100vh - 330px)' }}
|
||||
>
|
||||
<div style={{ padding: '16px 24px' }}>
|
||||
<Spin
|
||||
spinning={dataSource.loading || loadingModel}
|
||||
style={{ minHeight: 100 }}
|
||||
>
|
||||
{dataSource.fileList.length ? (
|
||||
<Row gutter={[16, 24]}>
|
||||
{_.map(dataSource.fileList, (item: any) => {
|
||||
return (
|
||||
<Col span={24} key={item.path}>
|
||||
<div
|
||||
className={classNames('hf-model-file', {
|
||||
active: item.path === current
|
||||
})}
|
||||
tabIndex={0}
|
||||
onClick={() => handleSelectModelFile(item)}
|
||||
onKeyDown={(e) => handleOnEnter(e, item)}
|
||||
>
|
||||
<div className="title">{item.path}</div>
|
||||
<Space className="tags">
|
||||
<Tag
|
||||
className="tag-item"
|
||||
color="green"
|
||||
style={{
|
||||
marginRight: 0
|
||||
}}
|
||||
>
|
||||
<span style={{ opacity: 0.65 }}>
|
||||
{convertFileSize(item.size)}
|
||||
</span>
|
||||
</Tag>
|
||||
{getModelQuantizationType(item)}
|
||||
</Space>
|
||||
<div className="btn"></div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
) : (
|
||||
!dataSource.loading && (
|
||||
<Empty
|
||||
imageStyle={{ height: 'auto', marginTop: '20px' }}
|
||||
image={
|
||||
<SearchOutlined
|
||||
className="font-size-16"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
></SearchOutlined>
|
||||
}
|
||||
description="No files found"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Spin>
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
) : (
|
||||
!dataSource.loading && (
|
||||
<Empty
|
||||
imageStyle={{ height: 'auto', marginTop: '20px' }}
|
||||
image={
|
||||
<SearchOutlined
|
||||
className="font-size-16"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
></SearchOutlined>
|
||||
}
|
||||
description="No files found"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Spin>
|
||||
</div>
|
||||
</SimpleBar>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ const HFModelItem: React.FC<HFModelItemProps> = (props) => {
|
||||
<div className="info">
|
||||
{props.source === modelSourceMap.huggingface_value ? (
|
||||
<Space size={16}>
|
||||
{props.task && (
|
||||
{/* {props.task && (
|
||||
<Tag
|
||||
className="tag-item"
|
||||
color="gold"
|
||||
@@ -50,7 +50,7 @@ const HFModelItem: React.FC<HFModelItemProps> = (props) => {
|
||||
>
|
||||
<span style={{ opacity: 0.65 }}>{props.task}</span>
|
||||
</Tag>
|
||||
)}
|
||||
)} */}
|
||||
<span>
|
||||
{dayjs().to(
|
||||
dayjs(dayjs(props.updatedAt).format('YYYY-MM-DD HH:mm:ss'))
|
||||
|
||||
@@ -1,20 +1,51 @@
|
||||
import HighlightCode from '@/components/highlight-code';
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { downloadFile } from '@huggingface/hub';
|
||||
import { Button, Empty, Tag } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { queryHuggingfaceModelDetail } from '../apis';
|
||||
import useRequestToken from '@/hooks/use-request-token';
|
||||
import {
|
||||
DownOutlined,
|
||||
FileTextOutlined,
|
||||
RightOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Empty, Tag, Tooltip } from 'antd';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import SimpleBar from 'simplebar-react';
|
||||
import 'simplebar-react/dist/simplebar.min.css';
|
||||
import { downloadModelFile, queryHuggingfaceModelDetail } from '../apis';
|
||||
import '../style/model-card.less';
|
||||
import TitleWrapper from './title-wrapper';
|
||||
|
||||
const ModelCard: React.FC<{ repo: string }> = (props) => {
|
||||
const { repo } = props;
|
||||
const ModelCard: React.FC<{
|
||||
repo: string;
|
||||
onCollapse: (flag: boolean) => void;
|
||||
collapsed: boolean;
|
||||
}> = (props) => {
|
||||
const { repo, onCollapse, collapsed } = props;
|
||||
const intl = useIntl();
|
||||
const requestSource = useRequestToken();
|
||||
const [modelData, setModelData] = useState<any>({});
|
||||
const [readmeText, setReadmeText] = useState<string | null>(null);
|
||||
const requestToken = useRef<any>(null);
|
||||
const axiosTokenRef = useRef<any>(null);
|
||||
|
||||
const loadFile = async (repo: string, sha: string) => {
|
||||
const res = await (
|
||||
await downloadFile({ repo, revision: sha, path: 'README.md' })
|
||||
)?.text();
|
||||
return res;
|
||||
try {
|
||||
axiosTokenRef.current?.abort?.();
|
||||
axiosTokenRef.current = new AbortController();
|
||||
const res = await downloadModelFile(
|
||||
{
|
||||
repo,
|
||||
revision: sha,
|
||||
path: 'README.md'
|
||||
},
|
||||
{
|
||||
signal: axiosTokenRef.current.signal
|
||||
}
|
||||
);
|
||||
return res || '';
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const getModelCardData = async () => {
|
||||
@@ -22,42 +53,110 @@ const ModelCard: React.FC<{ repo: string }> = (props) => {
|
||||
setModelData(null);
|
||||
return;
|
||||
}
|
||||
requestToken.current?.cancel?.();
|
||||
requestToken.current = requestSource();
|
||||
try {
|
||||
const res = await queryHuggingfaceModelDetail({ repo });
|
||||
const [modelcard, readme] = await Promise.all([
|
||||
queryHuggingfaceModelDetail(
|
||||
{ repo },
|
||||
{
|
||||
token: requestToken.current.token
|
||||
}
|
||||
),
|
||||
loadFile(repo, 'main')
|
||||
]);
|
||||
|
||||
setModelData(res);
|
||||
setModelData(modelcard);
|
||||
setReadmeText(readme);
|
||||
} catch (error) {
|
||||
setModelData({});
|
||||
}
|
||||
};
|
||||
|
||||
const handleCollapse = useCallback(() => {
|
||||
onCollapse(!collapsed);
|
||||
}, [collapsed]);
|
||||
|
||||
useEffect(() => {
|
||||
getModelCardData();
|
||||
}, [repo]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!readmeText) {
|
||||
onCollapse(false);
|
||||
}
|
||||
}, [readmeText]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
requestToken.current?.cancel?.();
|
||||
axiosTokenRef.current?.abort?.();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TitleWrapper>
|
||||
<span>Model Card</span>
|
||||
<span>{intl.formatMessage({ id: 'models.data.card' })}</span>
|
||||
</TitleWrapper>
|
||||
<div className="wrapper">
|
||||
{modelData ? (
|
||||
<div className="model-card-wrap">
|
||||
<div className="title">{modelData.id}</div>
|
||||
<div className="flex-between flex-center">
|
||||
<Tag className="tag-item">
|
||||
<span className="m-r-5">Architecture:</span>
|
||||
{modelData.config?.model_type}
|
||||
</Tag>
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href={`https://huggingface.co/${modelData.id}`}
|
||||
>
|
||||
View in Hugging Face
|
||||
<IconFont type="icon-external-link"></IconFont>
|
||||
</Button>
|
||||
<div className="title">
|
||||
{modelData.id}{' '}
|
||||
<Tooltip title={intl.formatMessage({ id: 'models.viewin.hf' })}>
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
target="_blank"
|
||||
href={`https://huggingface.co/${modelData.id}`}
|
||||
>
|
||||
<IconFont type="icon-external-link"></IconFont>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex-between flex-center">
|
||||
{modelData.config?.model_type && (
|
||||
<Tag className="tag-item" color="gold">
|
||||
<span style={{ opacity: 0.65 }}>
|
||||
<span className="m-r-5">
|
||||
{intl.formatMessage({ id: 'models.architecture' })}:
|
||||
</span>
|
||||
{modelData.config?.model_type}
|
||||
</span>
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
{readmeText && (
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
backgroundColor: '#282c34',
|
||||
marginTop: 16,
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<span className="mkd-title" onClick={handleCollapse}>
|
||||
<span>
|
||||
<FileTextOutlined className="m-r-5" /> README.md
|
||||
</span>
|
||||
<span>
|
||||
{collapsed ? <DownOutlined /> : <RightOutlined />}
|
||||
</span>
|
||||
</span>
|
||||
<SimpleBar
|
||||
style={{
|
||||
maxHeight: collapsed ? 300 : 0
|
||||
}}
|
||||
>
|
||||
<HighlightCode
|
||||
code={readmeText}
|
||||
lang="markdown"
|
||||
copyable={false}
|
||||
></HighlightCode>
|
||||
</SimpleBar>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>
|
||||
|
||||
@@ -5,17 +5,14 @@ import { Button, Input, Select } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { queryHuggingfaceModels } from '../apis';
|
||||
import {
|
||||
modelFilesSortOptions,
|
||||
modelSourceMap,
|
||||
ollamaModelOptions
|
||||
} from '../config';
|
||||
import { modelSourceMap, ollamaModelOptions } from '../config';
|
||||
import SearchStyle from '../style/search-result.less';
|
||||
import SearchInput from './search-input';
|
||||
import SearchResult from './search-result';
|
||||
|
||||
interface SearchInputProps {
|
||||
modelSource: string;
|
||||
setLoadingModel?: (flag: boolean) => void;
|
||||
onSourceChange?: (source: string) => void;
|
||||
onSelectModel: (model: any) => void;
|
||||
}
|
||||
@@ -38,7 +35,7 @@ const sourceList = [
|
||||
const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
console.log('SearchModel======');
|
||||
const intl = useIntl();
|
||||
const { modelSource, onSourceChange, onSelectModel } = props;
|
||||
const { modelSource, setLoadingModel, onSourceChange, onSelectModel } = props;
|
||||
const [dataSource, setDataSource] = useState<{
|
||||
repoOptions: any[];
|
||||
loading: boolean;
|
||||
@@ -51,6 +48,17 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
const cacheRepoOptions = useRef<any[]>([]);
|
||||
const axiosTokenRef = useRef<any>(null);
|
||||
const customOllamaModelRef = useRef<any>(null);
|
||||
const modelFilesSortOptions = useRef<any[]>([
|
||||
{ label: intl.formatMessage({ id: 'models.sort.likes' }), value: 'likes' },
|
||||
{
|
||||
label: intl.formatMessage({ id: 'models.sort.downloads' }),
|
||||
value: 'downloads'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'models.sort.updated' }),
|
||||
value: 'updatedAt'
|
||||
}
|
||||
]);
|
||||
|
||||
const handleOnSelectModel = useCallback((item: any) => {
|
||||
onSelectModel(item);
|
||||
@@ -67,6 +75,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
pre.loading = true;
|
||||
return { ...pre };
|
||||
});
|
||||
setLoadingModel?.(true);
|
||||
cacheRepoOptions.current = [];
|
||||
const params = {
|
||||
search: {
|
||||
@@ -93,12 +102,15 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
repoOptions: sortedList,
|
||||
loading: false
|
||||
});
|
||||
setLoadingModel?.(false);
|
||||
handleOnSelectModel(sortedList[0]);
|
||||
} catch (error) {
|
||||
console.log('queryHuggingfaceModels error===', error);
|
||||
setDataSource({
|
||||
repoOptions: [],
|
||||
loading: false
|
||||
});
|
||||
setLoadingModel?.(false);
|
||||
handleOnSelectModel({});
|
||||
cacheRepoOptions.current = [];
|
||||
}
|
||||
@@ -184,15 +196,18 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
loading: false
|
||||
});
|
||||
};
|
||||
|
||||
const renderHFSearch = () => {
|
||||
return (
|
||||
<>
|
||||
<SearchInput onSearch={handlerSearchModels}></SearchInput>
|
||||
<div className={SearchStyle.filter}>
|
||||
<span>
|
||||
<span className="value">{dataSource.repoOptions.length}</span>
|
||||
results
|
||||
<span className="value">
|
||||
{intl.formatMessage(
|
||||
{ id: 'models.search.result' },
|
||||
{ count: dataSource.repoOptions.length }
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
<Select
|
||||
value={sortType}
|
||||
@@ -204,7 +219,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
options={modelFilesSortOptions}
|
||||
options={modelFilesSortOptions.current}
|
||||
size="middle"
|
||||
style={{ width: '150px' }}
|
||||
></Select>
|
||||
@@ -233,10 +248,13 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
useEffect(() => {
|
||||
handleOnOpen();
|
||||
console.log('SearchModel useEffect', modelSource);
|
||||
}, [modelSource]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
axiosTokenRef.current?.abort?.();
|
||||
};
|
||||
}, [modelSource]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ flex: 1 }}>
|
||||
|
||||
@@ -5,11 +5,12 @@ import PageTools from '@/components/page-tools';
|
||||
import SealTable from '@/components/seal-table';
|
||||
import SealColumn from '@/components/seal-table/components/seal-column';
|
||||
import { PageAction } from '@/config';
|
||||
import HotKeys from '@/config/hotkeys';
|
||||
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
|
||||
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||
import useTableSort from '@/hooks/use-table-sort';
|
||||
import ViewCodeModal from '@/pages/playground/components/view-code-modal';
|
||||
import { handleBatchRequest } from '@/utils';
|
||||
import { handleBatchRequest, platformCall } from '@/utils';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
@@ -23,6 +24,7 @@ import { Button, Dropdown, Input, Space, Tag, message } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { memo, useCallback, useRef, useState } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import {
|
||||
MODELS_API,
|
||||
MODEL_INSTANCE_API,
|
||||
@@ -64,6 +66,7 @@ const Models: React.FC<ModelsProps> = ({
|
||||
total
|
||||
}) => {
|
||||
console.log('model list====2');
|
||||
const platform = platformCall();
|
||||
const access = useAccess();
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigate();
|
||||
@@ -77,7 +80,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
params: {},
|
||||
show: false
|
||||
});
|
||||
const [openViewCodeModal, setOpenViewCodeModal] = useState(false);
|
||||
const [openLogModal, setOpenLogModal] = useState(false);
|
||||
const [openAddModal, setOpenAddModal] = useState(false);
|
||||
const [openDeployModal, setOpenDeployModal] = useState<any>({
|
||||
@@ -92,9 +94,46 @@ const Models: React.FC<ModelsProps> = ({
|
||||
const [currentInstanceUrl, setCurrentInstanceUrl] = useState<string>('');
|
||||
const modalRef = useRef<any>(null);
|
||||
|
||||
useHotkeys(
|
||||
HotKeys.NEW1.join(','),
|
||||
() => {
|
||||
setOpenDeployModal({
|
||||
show: true,
|
||||
width: 'calc(100vw - 220px)',
|
||||
source: modelSourceMap.huggingface_value
|
||||
});
|
||||
},
|
||||
{ preventDefault: true }
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
HotKeys.NEW2.join(','),
|
||||
() => {
|
||||
setOpenDeployModal({
|
||||
show: true,
|
||||
width: 600,
|
||||
source: modelSourceMap.ollama_library_value
|
||||
});
|
||||
},
|
||||
{ preventDefault: true }
|
||||
);
|
||||
|
||||
const sourceOptions = [
|
||||
{
|
||||
label: 'Hugging Face',
|
||||
label: (
|
||||
<span className="flex-center flex-between">
|
||||
<span>Hugging Face</span>
|
||||
<Tag style={{ marginRight: 0 }} className="m-l-10">
|
||||
{platform.isMac ? (
|
||||
<>
|
||||
<IconFont type="icon-command"></IconFont> + 1
|
||||
</>
|
||||
) : (
|
||||
<>CTRL + 1</>
|
||||
)}
|
||||
</Tag>
|
||||
</span>
|
||||
),
|
||||
value: modelSourceMap.huggingface_value,
|
||||
key: 'huggingface',
|
||||
icon: <IconFont type="icon-huggingface"></IconFont>,
|
||||
@@ -107,7 +146,20 @@ const Models: React.FC<ModelsProps> = ({
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Ollama Library',
|
||||
label: (
|
||||
<span className="flex-center flex-between">
|
||||
<span>Ollama Library</span>
|
||||
<Tag style={{ marginRight: 0 }} className="m-l-10">
|
||||
{platform.isMac ? (
|
||||
<>
|
||||
<IconFont type="icon-command"></IconFont> + 2
|
||||
</>
|
||||
) : (
|
||||
<>CTRL + 2</>
|
||||
)}
|
||||
</Tag>
|
||||
</span>
|
||||
),
|
||||
value: modelSourceMap.ollama_library_value,
|
||||
key: 'ollama_library',
|
||||
icon: <IconFont type="icon-ollama"></IconFont>,
|
||||
|
||||
@@ -2,11 +2,7 @@ import React from 'react';
|
||||
import '../style/title-wrapper.less';
|
||||
|
||||
const TitleWrapper: React.FC<any> = ({ children }) => {
|
||||
return (
|
||||
<h3 className="h3">
|
||||
<span>{children}</span>
|
||||
</h3>
|
||||
);
|
||||
return <h3 className="h3">{children}</h3>;
|
||||
};
|
||||
|
||||
export default TitleWrapper;
|
||||
|
||||
Reference in New Issue
Block a user