chore: add model api
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { request } from '@umijs/max';
|
||||
import { GPUDeviceItem, ListItem } from '../config/types';
|
||||
import { GPUDeviceItem, ListItem, ModelFile } from '../config/types';
|
||||
|
||||
export const WORKERS_API = '/workers';
|
||||
export const GPU_DEVICES_API = '/gpu-devices';
|
||||
export const MODEL_FILES_API = '/model-files';
|
||||
|
||||
export async function queryWorkersList(params: Global.SearchParams) {
|
||||
return request<Global.PageResponse<ListItem>>(`${WORKERS_API}`, {
|
||||
@@ -36,3 +37,39 @@ export async function updateWorker(id: string | number, data: any) {
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export async function queryModelFilesList(params: Global.SearchParams) {
|
||||
return request<Global.PageResponse<ModelFile>>(MODEL_FILES_API, {
|
||||
method: 'GET',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteModelFile(id: string | number) {
|
||||
return request<Global.PageResponse<ModelFile>>(`${MODEL_FILES_API}/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateModelFile(id: string | number, data: any) {
|
||||
return request<Global.PageResponse<ModelFile>>(`${MODEL_FILES_API}/${id}`, {
|
||||
method: 'PUT',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export async function downloadModelFile(data: any) {
|
||||
return request<Global.PageResponse<ModelFile>>(MODEL_FILES_API, {
|
||||
method: 'POST',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export async function retryDownloadModelFile(id: string | number) {
|
||||
return request<Global.PageResponse<ModelFile>>(
|
||||
`${MODEL_FILES_API}/${id}/reset`,
|
||||
{
|
||||
method: 'POST'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import AutoTooltip from '@/components/auto-tooltip';
|
||||
import CopyButton from '@/components/copy-button';
|
||||
import DeleteModal from '@/components/delete-modal';
|
||||
import DropDownActions from '@/components/drop-down-actions';
|
||||
import DropdownButtons from '@/components/drop-down-buttons';
|
||||
import PageTools from '@/components/page-tools';
|
||||
import StatusTag from '@/components/status-tag';
|
||||
import useAppUtils from '@/hooks/use-app-utils';
|
||||
import useTableFetch from '@/hooks/use-table-fetch';
|
||||
import { modelSourceMap } from '@/pages/llmodels/config';
|
||||
import {
|
||||
@@ -13,14 +15,66 @@ import {
|
||||
onLineSourceOptions
|
||||
} from '@/pages/llmodels/config/button-actions';
|
||||
import DownloadModal from '@/pages/llmodels/download';
|
||||
import { convertFileSize } from '@/utils';
|
||||
import { DeleteOutlined, DownOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, ConfigProvider, Empty, Input, Space, Table } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useState } from 'react';
|
||||
import { deleteWorker, queryWorkersList } from '../apis';
|
||||
import { WorkerStatusMapValue, status } from '../config';
|
||||
import { ModelFile as ListItem } from '../config/types';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
MODEL_FILES_API,
|
||||
deleteModelFile,
|
||||
downloadModelFile,
|
||||
queryModelFilesList,
|
||||
queryWorkersList,
|
||||
retryDownloadModelFile
|
||||
} from '../apis';
|
||||
import {
|
||||
ModelfileState,
|
||||
ModelfileStateMap,
|
||||
ModelfileStateMapValue,
|
||||
WorkerStatusMap
|
||||
} from '../config';
|
||||
import {
|
||||
ModelFile as ListItem,
|
||||
ListItem as WorkerListItem
|
||||
} from '../config/types';
|
||||
|
||||
const getWorkerName = (
|
||||
id: number,
|
||||
workersList: Global.BaseOption<number>[]
|
||||
) => {
|
||||
const worker = workersList.find((item) => item.value === id);
|
||||
return worker?.label || '';
|
||||
};
|
||||
const InstanceStatusTag = (props: { data: ListItem }) => {
|
||||
const { data } = props;
|
||||
if (!data.state) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<StatusTag
|
||||
download={
|
||||
data.state === ModelfileStateMap.Downloading
|
||||
? { percent: data.download_progress }
|
||||
: undefined
|
||||
}
|
||||
statusValue={{
|
||||
status:
|
||||
data.state === ModelfileStateMap.Downloading &&
|
||||
data.download_progress === 100
|
||||
? ModelfileState[ModelfileStateMap.Ready]
|
||||
: ModelfileState[data.state],
|
||||
text: ModelfileStateMapValue[data.state],
|
||||
message:
|
||||
data.state === ModelfileStateMap.Downloading &&
|
||||
data.download_progress === 100
|
||||
? ''
|
||||
: data.state_message
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ModelFiles = () => {
|
||||
const {
|
||||
@@ -35,12 +89,18 @@ const ModelFiles = () => {
|
||||
handleSearch,
|
||||
handleNameChange
|
||||
} = useTableFetch<ListItem>({
|
||||
fetchAPI: queryWorkersList,
|
||||
deleteAPI: deleteWorker,
|
||||
contentForDelete: 'worker'
|
||||
fetchAPI: queryModelFilesList,
|
||||
deleteAPI: deleteModelFile,
|
||||
API: MODEL_FILES_API,
|
||||
watch: true,
|
||||
contentForDelete: 'resources.modelfiles.modelfile'
|
||||
});
|
||||
|
||||
const intl = useIntl();
|
||||
const { showSuccess } = useAppUtils();
|
||||
const [workersList, setWorkersList] = useState<Global.BaseOption<number>[]>(
|
||||
[]
|
||||
);
|
||||
const [downloadModalStatus, setDownlaodMoalStatus] = useState<{
|
||||
show: boolean;
|
||||
width: number | string;
|
||||
@@ -53,12 +113,46 @@ const ModelFiles = () => {
|
||||
gpuOptions: []
|
||||
});
|
||||
|
||||
const handleSelect = (val: any, record: ListItem) => {
|
||||
if (val === 'delete') {
|
||||
handleDelete({
|
||||
...record,
|
||||
name: record.local_path
|
||||
});
|
||||
useEffect(() => {
|
||||
const fetchWorkerList = async () => {
|
||||
try {
|
||||
const res = await queryWorkersList({
|
||||
page: 1,
|
||||
perPage: 100
|
||||
});
|
||||
|
||||
const list = res.items
|
||||
?.map((item: WorkerListItem) => {
|
||||
return {
|
||||
...item,
|
||||
value: item.id,
|
||||
label: item.name
|
||||
};
|
||||
})
|
||||
.filter(
|
||||
(item: WorkerListItem) => item.state === WorkerStatusMap.ready
|
||||
);
|
||||
setWorkersList(list);
|
||||
} catch (error) {
|
||||
// console.log('error', error);
|
||||
}
|
||||
};
|
||||
fetchWorkerList();
|
||||
}, []);
|
||||
|
||||
const handleSelect = async (val: any, record: ListItem) => {
|
||||
try {
|
||||
if (val === 'delete') {
|
||||
handleDelete({
|
||||
...record,
|
||||
name: record.local_path
|
||||
});
|
||||
} else if (val === 'retry') {
|
||||
await retryDownloadModelFile(record.id);
|
||||
showSuccess();
|
||||
}
|
||||
} catch (error) {
|
||||
// console.log('error', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,30 +182,47 @@ const ModelFiles = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleDownload = (data: any) => {
|
||||
console.log('download:', data);
|
||||
const handleDownload = async (data: any) => {
|
||||
try {
|
||||
await downloadModelFile(data);
|
||||
setDownlaodMoalStatus({
|
||||
...downloadModalStatus,
|
||||
show: false
|
||||
});
|
||||
showSuccess();
|
||||
} catch (error) {
|
||||
// console.log('error', error);
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Path',
|
||||
dataIndex: 'local_path',
|
||||
title: intl.formatMessage({ id: 'resources.modelfiles.form.path' }),
|
||||
dataIndex: 'resolved_paths',
|
||||
width: 240,
|
||||
render: (text: string, record: ListItem) => {
|
||||
return (
|
||||
<AutoTooltip ghost maxWidth={240}>
|
||||
<span>{record.local_path}</span>
|
||||
</AutoTooltip>
|
||||
record.resolved_paths?.length > 0 && (
|
||||
<span className="flex-center">
|
||||
<AutoTooltip ghost maxWidth={240}>
|
||||
<span>{record.resolved_paths?.[0]}</span>
|
||||
</AutoTooltip>
|
||||
<CopyButton
|
||||
text={record.resolved_paths?.[0]}
|
||||
type="link"
|
||||
></CopyButton>
|
||||
</span>
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Size',
|
||||
title: intl.formatMessage({ id: 'resources.modelfiles.size' }),
|
||||
dataIndex: 'size',
|
||||
render: (text: string, record: ListItem) => {
|
||||
return (
|
||||
<AutoTooltip ghost maxWidth={100}>
|
||||
<span>{record.size}</span>
|
||||
<span>{convertFileSize(record.size, 1)}</span>
|
||||
</AutoTooltip>
|
||||
);
|
||||
}
|
||||
@@ -122,7 +233,7 @@ const ModelFiles = () => {
|
||||
render: (text: string, record: ListItem) => {
|
||||
return (
|
||||
<AutoTooltip ghost maxWidth={240}>
|
||||
<span>{record.worker_name}</span>
|
||||
<span>{getWorkerName(record.worker_id, workersList)}</span>
|
||||
</AutoTooltip>
|
||||
);
|
||||
}
|
||||
@@ -140,15 +251,7 @@ const ModelFiles = () => {
|
||||
title: intl.formatMessage({ id: 'common.table.status' }),
|
||||
dataIndex: 'state',
|
||||
render: (text: string, record: ListItem) => {
|
||||
return (
|
||||
<StatusTag
|
||||
statusValue={{
|
||||
status: status[record.state] as any,
|
||||
text: WorkerStatusMapValue[record.state],
|
||||
message: record.state_message
|
||||
}}
|
||||
></StatusTag>
|
||||
);
|
||||
return <InstanceStatusTag data={record} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -209,7 +312,7 @@ const ModelFiles = () => {
|
||||
type="primary"
|
||||
iconPosition="end"
|
||||
>
|
||||
Download Model
|
||||
{intl.formatMessage({ id: 'resources.modelfiles.download' })}
|
||||
</Button>
|
||||
</DropDownActions>
|
||||
<Button
|
||||
@@ -250,11 +353,12 @@ const ModelFiles = () => {
|
||||
<DeleteModal ref={modalRef}></DeleteModal>
|
||||
<DownloadModal
|
||||
open={downloadModalStatus.show}
|
||||
title={intl.formatMessage({ id: 'models.button.deploy' })}
|
||||
title={intl.formatMessage({ id: 'resources.modelfiles.download' })}
|
||||
source={downloadModalStatus.source}
|
||||
width={downloadModalStatus.width}
|
||||
onCancel={handleDownloadCancel}
|
||||
onOk={handleDownload}
|
||||
workersList={workersList}
|
||||
></DownloadModal>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -114,3 +114,21 @@ export const containerInstallOptions = [
|
||||
{ label: 'Hygon DCU', value: 'dcu' },
|
||||
{ label: 'CPU', value: 'cpu' }
|
||||
];
|
||||
|
||||
export const ModelfileStateMap = {
|
||||
Error: 'error',
|
||||
Downloading: 'downloading',
|
||||
Ready: 'ready'
|
||||
};
|
||||
|
||||
export const ModelfileStateMapValue = {
|
||||
[ModelfileStateMap.Error]: 'Error',
|
||||
[ModelfileStateMap.Downloading]: 'Downloading',
|
||||
[ModelfileStateMap.Ready]: 'Ready'
|
||||
};
|
||||
|
||||
export const ModelfileState: any = {
|
||||
[ModelfileStateMap.Ready]: StatusMaps.success,
|
||||
[ModelfileStateMap.Error]: StatusMaps.error,
|
||||
[ModelfileStateMap.Downloading]: StatusMaps.transitioning
|
||||
};
|
||||
|
||||
@@ -99,17 +99,31 @@ export interface ListItem {
|
||||
|
||||
export interface ModelFile {
|
||||
source: string;
|
||||
size: number;
|
||||
id: number;
|
||||
created_at: string;
|
||||
worker_name: string;
|
||||
huggingface_repo_id: string;
|
||||
huggingface_filename: string;
|
||||
ollama_library_model_name: string;
|
||||
model_scope_model_id: string;
|
||||
model_scope_file_path: string;
|
||||
local_path: string;
|
||||
local_dir: string;
|
||||
worker_id: number;
|
||||
size: number;
|
||||
download_progress: number;
|
||||
resolved_paths: string[];
|
||||
state: string;
|
||||
state_message: string;
|
||||
id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface ModelFileFormData {
|
||||
source: string;
|
||||
huggingface_repo_id: string;
|
||||
huggingface_filename: string;
|
||||
ollama_library_model_name: string;
|
||||
model_scope_model_id: string;
|
||||
model_scope_file_path: string;
|
||||
local_path: string;
|
||||
local_dir: string;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { TabsProps } from 'antd';
|
||||
import { useCallback, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import GPUs from './components/gpus';
|
||||
import ModelFiles from './components/model-files';
|
||||
import Workers from './components/workers';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
@@ -12,28 +13,29 @@ const Wrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const items: TabsProps['items'] = [
|
||||
{
|
||||
key: 'workers',
|
||||
label: 'Workers',
|
||||
children: <Workers />
|
||||
},
|
||||
{
|
||||
key: 'gpus',
|
||||
label: 'GPUs',
|
||||
children: <GPUs />
|
||||
}
|
||||
// {
|
||||
// key: 'model-files',
|
||||
// label: 'Model Files',
|
||||
// children: <ModelFiles />
|
||||
// }
|
||||
];
|
||||
const Resources = () => {
|
||||
const [activeKey, setActiveKey] = useState('workers');
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
const items: TabsProps['items'] = [
|
||||
{
|
||||
key: 'workers',
|
||||
label: 'Workers',
|
||||
children: <Workers />
|
||||
},
|
||||
{
|
||||
key: 'gpus',
|
||||
label: 'GPUs',
|
||||
children: <GPUs />
|
||||
},
|
||||
{
|
||||
key: 'model-files',
|
||||
label: intl.formatMessage({ id: 'resources.modelfiles.modelfile' }),
|
||||
children: <ModelFiles />
|
||||
}
|
||||
];
|
||||
|
||||
const handleChangeTab = useCallback((key: string) => {
|
||||
setActiveKey(key);
|
||||
}, []);
|
||||
|
||||
Reference in New Issue
Block a user