chore: catalog api
This commit is contained in:
@@ -1,31 +1,49 @@
|
||||
import IMG from '@/assets/images/small-logo-200x200.png';
|
||||
import AutoTooltip from '@/components/auto-tooltip';
|
||||
import { Tag, Typography } from 'antd';
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { ClockCircleOutlined } from '@ant-design/icons';
|
||||
import { Divider, Tag, Typography } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { CatalogItem as CatalogItemType } from '../config/types';
|
||||
import '../style/catalog-item.less';
|
||||
|
||||
const COLORS = ['blue', 'purple', 'orange'];
|
||||
interface CatalogItemProps {
|
||||
activeId: number;
|
||||
itemId: number;
|
||||
data: any;
|
||||
onDeploy: () => void;
|
||||
data: CatalogItemType;
|
||||
onClick: (data: CatalogItemType) => void;
|
||||
}
|
||||
const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
||||
const { onDeploy, itemId, activeId, data } = props;
|
||||
const { onClick, activeId, data } = props;
|
||||
|
||||
const handleOnDeploy = () => {
|
||||
onDeploy();
|
||||
const handleOnClick = useCallback(() => {
|
||||
onClick(data);
|
||||
}, [data, onClick]);
|
||||
|
||||
const icon = useMemo(() => {
|
||||
const home = data.home?.replace(/\/$/, '');
|
||||
const icon = data.icon?.replace(/^\//, '');
|
||||
if (icon) {
|
||||
return `${home}/${icon}`;
|
||||
}
|
||||
return IMG;
|
||||
}, [data]);
|
||||
|
||||
const handleOnError = (e: any) => {
|
||||
e.target.src = IMG;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('catalog-item', { active: activeId === itemId })}
|
||||
onClick={handleOnClick}
|
||||
className={classNames('catalog-item', { active: activeId === data.id })}
|
||||
>
|
||||
<div className="content">
|
||||
<div className="title">
|
||||
<div className="img">
|
||||
<img src={IMG} alt="" />
|
||||
<img src={icon} alt="" onError={handleOnError} />
|
||||
</div>
|
||||
<AutoTooltip ghost style={{ flex: 1 }}>
|
||||
{data.name}
|
||||
@@ -36,34 +54,43 @@ const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
<div className="item-footer">
|
||||
<div className="update-time">
|
||||
<span className="flex-center">
|
||||
<ClockCircleOutlined
|
||||
className="m-r-5"
|
||||
style={{ color: 'var(--ant-color-text-secondary)' }}
|
||||
/>
|
||||
{data.release_date}
|
||||
</span>
|
||||
<span className="flex-center">
|
||||
<IconFont type="icon-justice" className="m-r-5"></IconFont>
|
||||
{_.map(data.licenses, (license: string, index: number) => {
|
||||
return (
|
||||
<>
|
||||
<span key={license}>{license}</span>
|
||||
{index !== data.licenses.length - 1 && (
|
||||
<Divider type="vertical" />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<div className="tags">
|
||||
{data.tags.map((sItem, i) => {
|
||||
{data.categories.map((sItem, i) => {
|
||||
return (
|
||||
<Tag key={sItem} className="tag-item" color={COLORS[i]}>
|
||||
{sItem}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
{/* <Tag color="blue" className="tag-item">
|
||||
Audio
|
||||
</Tag>
|
||||
<Tag color="purple" className="tag-item">
|
||||
GGUF
|
||||
</Tag>
|
||||
<Tag color="orange" className="tag-item">
|
||||
Qwen
|
||||
</Tag> */}
|
||||
{/* <span className="dot"></span>
|
||||
<Tag className="tag-item">Audio</Tag>
|
||||
<Tag className="tag-item">GGUF</Tag>
|
||||
<Tag className="tag-item">Qwen</Tag> */}
|
||||
{data.size?.length > 0 && (
|
||||
{data.sizes?.length > 0 && (
|
||||
<>
|
||||
<span className="dot"></span>
|
||||
{data.size.map((sItem, i) => {
|
||||
{data.sizes.map((sItem, i) => {
|
||||
return (
|
||||
<Tag key={sItem} className="tag-item">
|
||||
{sItem}
|
||||
{sItem}B
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -35,6 +35,11 @@ interface DataFormProps {
|
||||
action: PageActionType;
|
||||
selectedModel: any;
|
||||
isGGUF: boolean;
|
||||
sizeOptions?: Global.BaseOption<number>[];
|
||||
quantizationOptions?: Global.BaseOption<string>[];
|
||||
sourceDisable?: boolean;
|
||||
byBuiltIn?: boolean;
|
||||
backendOptions?: Global.BaseOption<string>[];
|
||||
onOk: (values: FormData) => void;
|
||||
onBackendChange?: (value: string) => void;
|
||||
}
|
||||
@@ -69,7 +74,14 @@ const sourceOptions = [
|
||||
];
|
||||
|
||||
const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
const { action, isGGUF, onOk } = props;
|
||||
const {
|
||||
action,
|
||||
isGGUF,
|
||||
sourceDisable = true,
|
||||
backendOptions,
|
||||
byBuiltIn,
|
||||
onOk
|
||||
} = props;
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
const [gpuOptions, setGpuOptions] = useState<
|
||||
@@ -259,34 +271,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
);
|
||||
};
|
||||
|
||||
const renderS3Fields = () => {
|
||||
return (
|
||||
<>
|
||||
<Form.Item<FormData>
|
||||
name="s3_address"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'common.form.rule.input'
|
||||
},
|
||||
{ name: intl.formatMessage({ id: 'models.form.s3address' }) }
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.s3address'
|
||||
})}
|
||||
required
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderOllamaModelFields = () => {
|
||||
return (
|
||||
<>
|
||||
@@ -364,6 +348,71 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleSizeChange = (val: any) => {
|
||||
form.setFieldsValue({
|
||||
quantization: ''
|
||||
});
|
||||
};
|
||||
|
||||
const renderFieldsFromCatalog = useMemo(() => {
|
||||
if (!byBuiltIn) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Form.Item<FormData>
|
||||
name="size"
|
||||
key="size"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'common.form.rule.select'
|
||||
},
|
||||
{ name: 'size' }
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealAutoComplete
|
||||
filterOption
|
||||
onChange={handleSizeChange}
|
||||
defaultActiveFirstOption
|
||||
disabled={false}
|
||||
options={props.sizeOptions}
|
||||
label="Size"
|
||||
required
|
||||
></SealAutoComplete>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData>
|
||||
name="quantization"
|
||||
key="quantization"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'common.form.rule.select'
|
||||
},
|
||||
{ name: 'quantization' }
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealAutoComplete
|
||||
filterOption
|
||||
defaultActiveFirstOption
|
||||
disabled={false}
|
||||
options={props.quantizationOptions}
|
||||
label="Quantization"
|
||||
required
|
||||
></SealAutoComplete>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
}, [props.sizeOptions, props.quantizationOptions, byBuiltIn]);
|
||||
|
||||
const renderFieldsBySource = useMemo(() => {
|
||||
if (SEARCH_SOURCE.includes(props.source)) {
|
||||
return renderHuggingfaceFields();
|
||||
@@ -373,9 +422,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
return renderOllamaModelFields();
|
||||
}
|
||||
|
||||
if (props.source === modelSourceMap.s3_value) {
|
||||
return renderS3Fields();
|
||||
}
|
||||
if (props.source === modelSourceMap.local_path_value) {
|
||||
return renderLocalPathFields();
|
||||
}
|
||||
@@ -477,7 +523,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
>
|
||||
{
|
||||
<SealSelect
|
||||
disabled={true}
|
||||
disabled={sourceDisable}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.source'
|
||||
})}
|
||||
@@ -531,35 +577,38 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
options={[
|
||||
{
|
||||
label: `llama-box`,
|
||||
value: backendOptionsMap.llamaBox,
|
||||
disabled:
|
||||
props.source === modelSourceMap.local_path_value
|
||||
? false
|
||||
: !isGGUF
|
||||
},
|
||||
{
|
||||
label: 'vLLM',
|
||||
value: backendOptionsMap.vllm,
|
||||
disabled:
|
||||
props.source === modelSourceMap.local_path_value
|
||||
? false
|
||||
: isGGUF
|
||||
},
|
||||
{
|
||||
label: 'vox-box',
|
||||
value: backendOptionsMap.voxBox,
|
||||
disabled: props.source === modelSourceMap.ollama_library_value
|
||||
}
|
||||
]}
|
||||
options={
|
||||
backendOptions ?? [
|
||||
{
|
||||
label: `llama-box`,
|
||||
value: backendOptionsMap.llamaBox,
|
||||
disabled:
|
||||
props.source === modelSourceMap.local_path_value
|
||||
? false
|
||||
: !isGGUF
|
||||
},
|
||||
{
|
||||
label: 'vLLM',
|
||||
value: backendOptionsMap.vllm,
|
||||
disabled:
|
||||
props.source === modelSourceMap.local_path_value
|
||||
? false
|
||||
: isGGUF
|
||||
},
|
||||
{
|
||||
label: 'vox-box',
|
||||
value: backendOptionsMap.voxBox,
|
||||
disabled: props.source === modelSourceMap.ollama_library_value
|
||||
}
|
||||
]
|
||||
}
|
||||
disabled={
|
||||
action === PageAction.EDIT &&
|
||||
props.source !== modelSourceMap.local_path_value
|
||||
}
|
||||
></SealSelect>
|
||||
</Form.Item>
|
||||
{renderFieldsFromCatalog}
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea
|
||||
label={intl.formatMessage({
|
||||
|
||||
@@ -9,11 +9,6 @@ import { backendOptionsMap, modelSourceMap } from '../config';
|
||||
import { FormData, ListItem } from '../config/types';
|
||||
import ColumnWrapper from './column-wrapper';
|
||||
import DataForm from './data-form';
|
||||
import HFModelFile from './hf-model-file';
|
||||
import ModelCard from './model-card';
|
||||
import SearchModel from './search-model';
|
||||
import Separator from './separator';
|
||||
import TitleWrapper from './title-wrapper';
|
||||
|
||||
type AddModalProps = {
|
||||
title: string;
|
||||
@@ -158,41 +153,6 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
footer={false}
|
||||
>
|
||||
<div style={{ display: 'flex', height: '100%' }}>
|
||||
{SEARCH_SOURCE.includes(props.source) && (
|
||||
<>
|
||||
<div style={{ display: 'flex', flex: 1 }}>
|
||||
<ColumnWrapper>
|
||||
<SearchModel
|
||||
modelSource={props.source}
|
||||
onSelectModel={handleOnSelectModel}
|
||||
setLoadingModel={setLoadingModel}
|
||||
></SearchModel>
|
||||
</ColumnWrapper>
|
||||
<Separator></Separator>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flex: 1 }}>
|
||||
<ColumnWrapper>
|
||||
<ModelCard
|
||||
selectedModel={selectedModel}
|
||||
onCollapse={setCollapsed}
|
||||
collapsed={collapsed}
|
||||
modelSource={props.source}
|
||||
setIsGGUF={handleSetIsGGUF}
|
||||
></ModelCard>
|
||||
{isGGUF && (
|
||||
<HFModelFile
|
||||
ref={modelFileRef}
|
||||
selectedModel={selectedModel}
|
||||
modelSource={props.source}
|
||||
onSelectFile={handleSelectModelFile}
|
||||
collapsed={collapsed}
|
||||
></HFModelFile>
|
||||
)}
|
||||
</ColumnWrapper>
|
||||
<Separator></Separator>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<ColumnWrapper
|
||||
footer={
|
||||
<ModalFooter
|
||||
@@ -207,12 +167,6 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
}
|
||||
>
|
||||
<>
|
||||
{SEARCH_SOURCE.includes(source) && (
|
||||
<TitleWrapper>
|
||||
{intl.formatMessage({ id: 'models.form.configurations' })}
|
||||
<span style={{ display: 'flex', height: 24 }}></span>
|
||||
</TitleWrapper>
|
||||
)}
|
||||
<DataForm
|
||||
source={source}
|
||||
action={action}
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
DownOutlined,
|
||||
EditOutlined,
|
||||
ExperimentOutlined,
|
||||
GoldOutlined,
|
||||
PictureOutlined,
|
||||
SyncOutlined,
|
||||
WechatWorkOutlined
|
||||
@@ -222,12 +221,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
}, [deleteIds]);
|
||||
|
||||
const sourceOptions = [
|
||||
{
|
||||
label: 'Model Catalog',
|
||||
value: 'model_catalog',
|
||||
key: 'model_catalog',
|
||||
icon: <GoldOutlined />
|
||||
},
|
||||
{
|
||||
label: 'Hugging Face',
|
||||
value: modelSourceMap.huggingface_value,
|
||||
@@ -688,9 +681,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
source: modelSourceMap.huggingface_value
|
||||
});
|
||||
}
|
||||
if (item.key === 'model_catalog') {
|
||||
navigate('/models/catalog');
|
||||
}
|
||||
|
||||
if (item.key === 'ollama_library') {
|
||||
setOpenDeployModal({
|
||||
@@ -722,7 +712,8 @@ const Models: React.FC<ModelsProps> = ({
|
||||
<PageContainer
|
||||
ghost
|
||||
header={{
|
||||
title: intl.formatMessage({ id: 'models.title' })
|
||||
title: intl.formatMessage({ id: 'models.title' }),
|
||||
breadcrumb: {}
|
||||
}}
|
||||
extra={[]}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user