chore: worker downloading tooltip
This commit is contained in:
@@ -31,11 +31,23 @@
|
|||||||
|
|
||||||
.cell-span {
|
.cell-span {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 4px 6px;
|
padding: 6px;
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell-header {
|
.cell-header {
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
th {
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
border-bottom: 1px solid var(--ant-color-split);
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
border-bottom: 1px solid var(--ant-color-split);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import TableRow from './row';
|
|||||||
export interface ColumnProps {
|
export interface ColumnProps {
|
||||||
title: string;
|
title: string;
|
||||||
key: string;
|
key: string;
|
||||||
|
width?: string | number;
|
||||||
render?: (data: {
|
render?: (data: {
|
||||||
dataIndex: string;
|
dataIndex: string;
|
||||||
dataList?: any[];
|
dataList?: any[];
|
||||||
@@ -34,13 +35,20 @@ export interface ColumnProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SimpleTableProps {
|
interface SimpleTableProps {
|
||||||
|
theme?: 'dark' | 'light';
|
||||||
columns: ColumnProps[];
|
columns: ColumnProps[];
|
||||||
dataSource: any[];
|
dataSource: any[];
|
||||||
bordered?: boolean;
|
bordered?: boolean;
|
||||||
rowKey?: string;
|
rowKey?: string;
|
||||||
}
|
}
|
||||||
const SimpleTabel: React.FC<SimpleTableProps> = (props) => {
|
const SimpleTabel: React.FC<SimpleTableProps> = (props) => {
|
||||||
const { columns, dataSource, rowKey, bordered = true } = props;
|
const {
|
||||||
|
columns,
|
||||||
|
dataSource,
|
||||||
|
rowKey,
|
||||||
|
bordered = true,
|
||||||
|
theme = 'dark'
|
||||||
|
} = props;
|
||||||
const scroller = React.useRef<any>(null);
|
const scroller = React.useRef<any>(null);
|
||||||
const { initialize } = useOverlayScroller();
|
const { initialize } = useOverlayScroller();
|
||||||
|
|
||||||
@@ -50,7 +58,7 @@ const SimpleTabel: React.FC<SimpleTableProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div style={{ maxHeight: 200 }} ref={scroller}>
|
<div style={{ maxHeight: 200 }} ref={scroller}>
|
||||||
<table
|
<table
|
||||||
className={classNames('simple-table', {
|
className={classNames('simple-table', theme, {
|
||||||
'simple-table-bordered': bordered
|
'simple-table-bordered': bordered
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ const TableCell: React.FC<TableCellProps> = (props: TableCellProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<td
|
<td
|
||||||
|
width={column.width}
|
||||||
key={colIndex}
|
key={colIndex}
|
||||||
rowSpan={column.rowSpan?.({
|
rowSpan={column.rowSpan?.({
|
||||||
row,
|
row,
|
||||||
|
|||||||
@@ -875,6 +875,12 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.light-downloading-tooltip {
|
||||||
|
&.ant-tooltip .ant-tooltip-arrow::before {
|
||||||
|
background: var(--color-white-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tooltip-wrapper {
|
.tooltip-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function useTableFetch<ListItem>(options: {
|
|||||||
loadend: false,
|
loadend: false,
|
||||||
total: 0
|
total: 0
|
||||||
});
|
});
|
||||||
const [queryParams, setQueryParams] = useState({
|
const [queryParams, setQueryParams] = useState<any>({
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
search: ''
|
search: ''
|
||||||
@@ -191,6 +191,7 @@ export default function useTableFetch<ListItem>(options: {
|
|||||||
sortOrder,
|
sortOrder,
|
||||||
queryParams,
|
queryParams,
|
||||||
modalRef,
|
modalRef,
|
||||||
|
setQueryParams,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleDeleteBatch,
|
handleDeleteBatch,
|
||||||
fetchData,
|
fetchData,
|
||||||
|
|||||||
@@ -119,5 +119,6 @@ export default {
|
|||||||
'models.form.backend.warning.llamabox':
|
'models.form.backend.warning.llamabox':
|
||||||
'To use the llama-box backend, specify the full path to the model file (e.g.,<span style="font-weight: 700">/data/models/model.gguf</span>). For sharded models, provide the path to the first shard (e.g.,<span style="font-weight: 700">/data/models/model-00001-of-00004.gguf</span>).',
|
'To use the llama-box backend, specify the full path to the model file (e.g.,<span style="font-weight: 700">/data/models/model.gguf</span>). For sharded models, provide the path to the first shard (e.g.,<span style="font-weight: 700">/data/models/model-00001-of-00004.gguf</span>).',
|
||||||
'models.form.keyvalue.paste':
|
'models.form.keyvalue.paste':
|
||||||
'Paste multiple lines of text, with each line containing a key-value pair. The key and value are separated by an = sign, and different key-value pairs are separated by newline characters.'
|
'Paste multiple lines of text, with each line containing a key-value pair. The key and value are separated by an = sign, and different key-value pairs are separated by newline characters.',
|
||||||
|
'models.form.files': 'files'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -66,5 +66,7 @@ export default {
|
|||||||
'The default storage directory is /var/lib/gpustack/cache.',
|
'The default storage directory is /var/lib/gpustack/cache.',
|
||||||
'resources.modelfiles.retry.download': 'Retry Download',
|
'resources.modelfiles.retry.download': 'Retry Download',
|
||||||
'resources.modelfiles.storagePath.holder':
|
'resources.modelfiles.storagePath.holder':
|
||||||
'Waiting for download to complete...'
|
'Waiting for download to complete...',
|
||||||
|
'resources.filter.worker': 'Filter by Worker',
|
||||||
|
'resources.filter.file': 'Filter by File'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -119,9 +119,11 @@ export default {
|
|||||||
'models.form.backend.warning.llamabox':
|
'models.form.backend.warning.llamabox':
|
||||||
'Чтобы использовать бэкенд llama-box , укажите полный путь к файлу модели (например,<span style="font-weight: 700">/data/models/model.gguf</span>). Для шардированных моделей укажите путь к первому шарду (например,<span style="font-weight: 700">/data/models/model-00001-of-00004.gguf</span>).',
|
'Чтобы использовать бэкенд llama-box , укажите полный путь к файлу модели (например,<span style="font-weight: 700">/data/models/model.gguf</span>). Для шардированных моделей укажите путь к первому шарду (например,<span style="font-weight: 700">/data/models/model-00001-of-00004.gguf</span>).',
|
||||||
'models.form.keyvalue.paste':
|
'models.form.keyvalue.paste':
|
||||||
'Paste multiple lines of text, with each line containing a key-value pair. The key and value are separated by an = sign, and different key-value pairs are separated by newline characters.'
|
'Paste multiple lines of text, with each line containing a key-value pair. The key and value are separated by an = sign, and different key-value pairs are separated by newline characters.',
|
||||||
|
'models.form.files': 'files'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
//1. 'models.form.keyvalue.paste'
|
//1. 'models.form.keyvalue.paste'
|
||||||
|
//2. 'models.form.files'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ export default {
|
|||||||
'The default storage directory is /var/lib/gpustack/cache.',
|
'The default storage directory is /var/lib/gpustack/cache.',
|
||||||
'resources.modelfiles.retry.download': 'Retry Download',
|
'resources.modelfiles.retry.download': 'Retry Download',
|
||||||
'resources.modelfiles.storagePath.holder':
|
'resources.modelfiles.storagePath.holder':
|
||||||
'Waiting for download to complete...'
|
'Waiting for download to complete...',
|
||||||
|
'resources.filter.worker': 'Filter by Worker',
|
||||||
|
'resources.filter.file': 'Filter by File'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -79,4 +81,6 @@ export default {
|
|||||||
//7. 'resources.modelfiles.retry.download',
|
//7. 'resources.modelfiles.retry.download',
|
||||||
//8. 'resources.modelfiles.form.localdir.tips',
|
//8. 'resources.modelfiles.form.localdir.tips',
|
||||||
//9. 'resources.modelfiles.storagePath.holder',
|
//9. 'resources.modelfiles.storagePath.holder',
|
||||||
|
//10. 'resources.filter.worker',
|
||||||
|
//11. 'resources.filter.file',
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -112,5 +112,6 @@ export default {
|
|||||||
'models.form.backend.warning.llamabox':
|
'models.form.backend.warning.llamabox':
|
||||||
'要使用 llama-box 后端,请指定模型文件的完整路径(例如:<span style="font-weight: 700">/data/models/model.gguf</span>)。对于分片模型,请提供第一个分片的路径(例如:<span style="font-weight: 700">/data/models/model-00001-of-00004.gguf</span>)。',
|
'要使用 llama-box 后端,请指定模型文件的完整路径(例如:<span style="font-weight: 700">/data/models/model.gguf</span>)。对于分片模型,请提供第一个分片的路径(例如:<span style="font-weight: 700">/data/models/model-00001-of-00004.gguf</span>)。',
|
||||||
'models.form.keyvalue.paste':
|
'models.form.keyvalue.paste':
|
||||||
'粘贴多行文本,每行包含一个键值对,键和值之间用 = 号分隔,不同的键值对之间用换行符分隔。'
|
'粘贴多行文本,每行包含一个键值对,键和值之间用 = 号分隔,不同的键值对之间用换行符分隔。',
|
||||||
|
'models.form.files': '文件'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,5 +64,7 @@ export default {
|
|||||||
'resources.modelfiles.form.localdir.tips':
|
'resources.modelfiles.form.localdir.tips':
|
||||||
'默认存储目录为 /var/lib/gpustack/cache',
|
'默认存储目录为 /var/lib/gpustack/cache',
|
||||||
'resources.modelfiles.retry.download': '重新下载',
|
'resources.modelfiles.retry.download': '重新下载',
|
||||||
'resources.modelfiles.storagePath.holder': '等待下载完成...'
|
'resources.modelfiles.storagePath.holder': '等待下载完成...',
|
||||||
|
'resources.filter.worker': '按 worker 筛选',
|
||||||
|
'resources.filter.file': '按文件筛选'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,34 @@ const fieldList = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const downloadList: ColumnProps[] = [
|
||||||
|
{
|
||||||
|
title: 'Worker',
|
||||||
|
key: 'worker_name',
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Status',
|
||||||
|
key: 'download_progress',
|
||||||
|
render: ({ row }) => {
|
||||||
|
return (
|
||||||
|
<StatusTag
|
||||||
|
download={{
|
||||||
|
percent: row.download_progress
|
||||||
|
}}
|
||||||
|
statusValue={{
|
||||||
|
status: row.download_progress
|
||||||
|
? status[InstanceStatusMap.Running]
|
||||||
|
: status[InstanceStatusMap.Initializing],
|
||||||
|
text: row.download_progress,
|
||||||
|
message: ''
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const WorkerInfo = (props: {
|
const WorkerInfo = (props: {
|
||||||
title: React.ReactNode;
|
title: React.ReactNode;
|
||||||
defaultOpen: boolean;
|
defaultOpen: boolean;
|
||||||
@@ -70,6 +98,82 @@ const WorkerInfo = (props: {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const RenderRayactorDownloading = (props: {
|
||||||
|
severList: any[];
|
||||||
|
instanceData: any;
|
||||||
|
workerList: WorkerListItem[];
|
||||||
|
}) => {
|
||||||
|
const { severList, instanceData, workerList } = props;
|
||||||
|
if (!severList.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const list = _.map(severList, (item: any) => {
|
||||||
|
const data = _.find(workerList, { id: item.worker_id });
|
||||||
|
return {
|
||||||
|
worker_name: data?.name,
|
||||||
|
worker_ip: data?.ip,
|
||||||
|
download_progress: _.round(item.download_progress, 2)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const mainWorker = [
|
||||||
|
{
|
||||||
|
worker_name: `${instanceData.worker_name}`,
|
||||||
|
worker_ip: `${instanceData.worker_ip}`,
|
||||||
|
download_progress: _.round(instanceData.download_progress, 2)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SimpleTabel
|
||||||
|
columns={downloadList}
|
||||||
|
dataSource={[...mainWorker, ...list]}
|
||||||
|
theme="light"
|
||||||
|
></SimpleTabel>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RenderWorkerDownloading = (props: {
|
||||||
|
rayActors: any[];
|
||||||
|
workerList: WorkerListItem[];
|
||||||
|
instanceData: ModelInstanceListItem;
|
||||||
|
}) => {
|
||||||
|
const { rayActors, workerList, instanceData } = props;
|
||||||
|
if (instanceData.state === InstanceStatusMap.Error || !rayActors.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
arrow={true}
|
||||||
|
overlayInnerStyle={{
|
||||||
|
width: 300,
|
||||||
|
backgroundColor: 'var(--color-white-1)'
|
||||||
|
}}
|
||||||
|
overlayClassName="light-downloading-tooltip"
|
||||||
|
title={
|
||||||
|
<RenderRayactorDownloading
|
||||||
|
severList={rayActors}
|
||||||
|
workerList={workerList}
|
||||||
|
instanceData={instanceData}
|
||||||
|
></RenderRayactorDownloading>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Progress
|
||||||
|
showInfo={false}
|
||||||
|
type="circle"
|
||||||
|
size={20}
|
||||||
|
strokeColor="var(--color-progress-green)"
|
||||||
|
percent={
|
||||||
|
_.find(rayActors, (item: any) => item.download_progress < 100)
|
||||||
|
?.download_progress || 100
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const InstanceStatusTag = (
|
const InstanceStatusTag = (
|
||||||
props: Pick<InstanceItemProps, 'instanceData' | 'handleChildSelect'>
|
props: Pick<InstanceItemProps, 'instanceData' | 'handleChildSelect'>
|
||||||
) => {
|
) => {
|
||||||
@@ -79,39 +183,41 @@ const InstanceStatusTag = (
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<StatusTag
|
<>
|
||||||
download={
|
<StatusTag
|
||||||
instanceData.state === InstanceStatusMap.Downloading
|
download={
|
||||||
? { percent: instanceData.download_progress }
|
instanceData.state === InstanceStatusMap.Downloading
|
||||||
: undefined
|
? { percent: instanceData.download_progress }
|
||||||
}
|
: undefined
|
||||||
extra={
|
}
|
||||||
instanceData.state === InstanceStatusMap.Error &&
|
extra={
|
||||||
instanceData.worker_id ? (
|
instanceData.state === InstanceStatusMap.Error &&
|
||||||
<Button
|
instanceData.worker_id ? (
|
||||||
type="link"
|
<Button
|
||||||
size="small"
|
type="link"
|
||||||
style={{ paddingLeft: 0 }}
|
size="small"
|
||||||
onClick={() => handleChildSelect('viewlog', instanceData)}
|
style={{ paddingLeft: 0 }}
|
||||||
>
|
onClick={() => handleChildSelect('viewlog', instanceData)}
|
||||||
{intl.formatMessage({ id: 'models.list.more.logs' })}
|
>
|
||||||
</Button>
|
{intl.formatMessage({ id: 'models.list.more.logs' })}
|
||||||
) : null
|
</Button>
|
||||||
}
|
) : null
|
||||||
statusValue={{
|
}
|
||||||
status:
|
statusValue={{
|
||||||
instanceData.state === InstanceStatusMap.Downloading &&
|
status:
|
||||||
instanceData.download_progress === 100
|
instanceData.state === InstanceStatusMap.Downloading &&
|
||||||
? status[InstanceStatusMap.Running]
|
instanceData.download_progress === 100
|
||||||
: status[instanceData.state],
|
? status[InstanceStatusMap.Running]
|
||||||
text: InstanceStatusMapValue[instanceData.state],
|
: status[instanceData.state],
|
||||||
message:
|
text: InstanceStatusMapValue[instanceData.state],
|
||||||
instanceData.state === InstanceStatusMap.Downloading &&
|
message:
|
||||||
instanceData.download_progress === 100
|
instanceData.state === InstanceStatusMap.Downloading &&
|
||||||
? ''
|
instanceData.download_progress === 100
|
||||||
: instanceData.state_message
|
? ''
|
||||||
}}
|
: instanceData.state_message
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -341,6 +447,7 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
|||||||
<Tag
|
<Tag
|
||||||
color="processing"
|
color="processing"
|
||||||
style={{
|
style={{
|
||||||
|
marginRight: 0,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
@@ -471,13 +578,18 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
|||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col span={4}>
|
||||||
<span
|
<span
|
||||||
style={{ paddingLeft: '62px' }}
|
style={{ paddingLeft: '62px', gap: 4 }}
|
||||||
className="flex justify-center"
|
className="flex-center justify-center"
|
||||||
>
|
>
|
||||||
<InstanceStatusTag
|
<InstanceStatusTag
|
||||||
instanceData={instanceData}
|
instanceData={instanceData}
|
||||||
handleChildSelect={handleChildSelect}
|
handleChildSelect={handleChildSelect}
|
||||||
/>
|
/>
|
||||||
|
<RenderWorkerDownloading
|
||||||
|
rayActors={instanceData.distributed_servers?.ray_actors || []}
|
||||||
|
workerList={workerList}
|
||||||
|
instanceData={instanceData}
|
||||||
|
></RenderWorkerDownloading>
|
||||||
</span>
|
</span>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={5}>
|
<Col span={5}>
|
||||||
|
|||||||
@@ -6,6 +6,64 @@ import { ModelInstanceListItem } from '../config/types';
|
|||||||
import '../style/instance-item.less';
|
import '../style/instance-item.less';
|
||||||
import InstanceItem from './instance-item';
|
import InstanceItem from './instance-item';
|
||||||
|
|
||||||
|
const testInstanceData = {
|
||||||
|
source: 'huggingface',
|
||||||
|
huggingface_repo_id: 'Qwen/Qwen2.5-3B-Instruct',
|
||||||
|
huggingface_filename: null,
|
||||||
|
ollama_library_model_name: null,
|
||||||
|
model_scope_model_id: null,
|
||||||
|
model_scope_file_path: null,
|
||||||
|
local_path: null,
|
||||||
|
name: 'qwen2.5-XVCXa',
|
||||||
|
worker_id: 1,
|
||||||
|
worker_name: 'sealgpuhost4080',
|
||||||
|
worker_ip: '192.168.50.12',
|
||||||
|
pid: 208852,
|
||||||
|
port: 40063,
|
||||||
|
download_progress: 72.11,
|
||||||
|
resolved_path: null,
|
||||||
|
state: 'downloading',
|
||||||
|
state_message: '',
|
||||||
|
computed_resource_claim: {
|
||||||
|
is_unified_memory: false,
|
||||||
|
offload_layers: null,
|
||||||
|
total_layers: null,
|
||||||
|
ram: null,
|
||||||
|
vram: {
|
||||||
|
'0': 1717148058
|
||||||
|
},
|
||||||
|
tensor_split: null
|
||||||
|
},
|
||||||
|
gpu_indexes: [0],
|
||||||
|
model_id: 1,
|
||||||
|
model_name: 'qwen2.5',
|
||||||
|
distributed_servers: {
|
||||||
|
rpc_servers: null,
|
||||||
|
ray_actors: [
|
||||||
|
{
|
||||||
|
worker_id: 2,
|
||||||
|
worker_ip: '192.168.50.13',
|
||||||
|
total_gpus: 1,
|
||||||
|
gpu_indexes: [0],
|
||||||
|
computed_resource_claim: {
|
||||||
|
is_unified_memory: false,
|
||||||
|
offload_layers: null,
|
||||||
|
total_layers: null,
|
||||||
|
ram: null,
|
||||||
|
vram: {
|
||||||
|
'0': 23181498777
|
||||||
|
},
|
||||||
|
tensor_split: null
|
||||||
|
},
|
||||||
|
download_progress: 27.49
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
id: 1,
|
||||||
|
created_at: '2025-03-24T12:06:30Z',
|
||||||
|
updated_at: '2025-03-24T12:09:01Z'
|
||||||
|
};
|
||||||
|
|
||||||
interface InstanceItemProps {
|
interface InstanceItemProps {
|
||||||
list: ModelInstanceListItem[];
|
list: ModelInstanceListItem[];
|
||||||
workerList: WorkerListItem[];
|
workerList: WorkerListItem[];
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import useBodyScroll from '@/hooks/use-body-scroll';
|
|||||||
import useTableFetch from '@/hooks/use-table-fetch';
|
import useTableFetch from '@/hooks/use-table-fetch';
|
||||||
import { createModel } from '@/pages/llmodels/apis';
|
import { createModel } from '@/pages/llmodels/apis';
|
||||||
import DeployModal from '@/pages/llmodels/components/deploy-modal';
|
import DeployModal from '@/pages/llmodels/components/deploy-modal';
|
||||||
import FileParts from '@/pages/llmodels/components/file-parts';
|
|
||||||
import {
|
import {
|
||||||
backendOptionsMap,
|
backendOptionsMap,
|
||||||
getSourceRepoConfigValue,
|
getSourceRepoConfigValue,
|
||||||
@@ -27,22 +26,17 @@ import {
|
|||||||
} from '@/pages/llmodels/config/button-actions';
|
} from '@/pages/llmodels/config/button-actions';
|
||||||
import DownloadModal from '@/pages/llmodels/download';
|
import DownloadModal from '@/pages/llmodels/download';
|
||||||
import { convertFileSize } from '@/utils';
|
import { convertFileSize } from '@/utils';
|
||||||
import {
|
import { DeleteOutlined, DownOutlined, SyncOutlined } from '@ant-design/icons';
|
||||||
DeleteOutlined,
|
|
||||||
DownOutlined,
|
|
||||||
InfoCircleOutlined,
|
|
||||||
SyncOutlined
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
ConfigProvider,
|
ConfigProvider,
|
||||||
Empty,
|
Empty,
|
||||||
Input,
|
Input,
|
||||||
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
Tooltip,
|
|
||||||
message
|
message
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -112,7 +106,7 @@ const InstanceStatusTag = (props: { data: ListItem }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ModelFiles = () => {
|
const ModelFiles = () => {
|
||||||
const { getGPUList, generateFormValues } = useGenerateFormEditInitialValues();
|
const { getGPUList } = useGenerateFormEditInitialValues();
|
||||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||||
const [modelsExpandKeys, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom);
|
const [modelsExpandKeys, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -127,7 +121,8 @@ const ModelFiles = () => {
|
|||||||
handlePageChange,
|
handlePageChange,
|
||||||
handleTableChange,
|
handleTableChange,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
handleNameChange
|
handleNameChange,
|
||||||
|
setQueryParams
|
||||||
} = useTableFetch<ListItem>({
|
} = useTableFetch<ListItem>({
|
||||||
fetchAPI: queryModelFilesList,
|
fetchAPI: queryModelFilesList,
|
||||||
deleteAPI: deleteModelFile,
|
deleteAPI: deleteModelFile,
|
||||||
@@ -202,6 +197,20 @@ const ModelFiles = () => {
|
|||||||
return name.replace(filterPattern, '$1');
|
return name.replace(filterPattern, '$1');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleWorkerChange = (value: number) => {
|
||||||
|
setQueryParams({
|
||||||
|
...queryParams,
|
||||||
|
page: 1,
|
||||||
|
worker_id: value
|
||||||
|
});
|
||||||
|
fetchData({
|
||||||
|
query: {
|
||||||
|
...queryParams,
|
||||||
|
page: 1,
|
||||||
|
worker_id: value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
const generateInitialValues = (record: ListItem) => {
|
const generateInitialValues = (record: ListItem) => {
|
||||||
const isGGUF = _.includes(record.resolved_paths?.[0], 'gguf');
|
const isGGUF = _.includes(record.resolved_paths?.[0], 'gguf');
|
||||||
const isOllama = !!record.ollama_library_model_name;
|
const isOllama = !!record.ollama_library_model_name;
|
||||||
@@ -251,28 +260,20 @@ const ModelFiles = () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tag
|
||||||
overlayInnerStyle={{
|
className="flex-center"
|
||||||
width: 120,
|
color="purple"
|
||||||
padding: 0
|
style={{
|
||||||
|
marginRight: 0,
|
||||||
|
height: 22,
|
||||||
|
borderRadius: 'var(--border-radius-base)'
|
||||||
}}
|
}}
|
||||||
title={<FileParts fileList={partsList} showSize={false}></FileParts>}
|
|
||||||
>
|
>
|
||||||
<Tag
|
<span style={{ opacity: 1 }}>
|
||||||
className="tag-item"
|
{record.resolved_paths?.length}{' '}
|
||||||
color="purple"
|
{intl.formatMessage({ id: 'models.form.files' })}
|
||||||
style={{
|
</span>
|
||||||
marginRight: 0,
|
</Tag>
|
||||||
height: 22,
|
|
||||||
borderRadius: 'var(--border-radius-base)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span style={{ opacity: 1 }}>
|
|
||||||
<InfoCircleOutlined className="m-r-5" />
|
|
||||||
{partsList.length} parts
|
|
||||||
</span>
|
|
||||||
</Tag>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -492,12 +493,23 @@ const ModelFiles = () => {
|
|||||||
<Space>
|
<Space>
|
||||||
<Input
|
<Input
|
||||||
placeholder={intl.formatMessage({
|
placeholder={intl.formatMessage({
|
||||||
id: 'common.filter.name'
|
id: 'resources.filter.file'
|
||||||
})}
|
})}
|
||||||
style={{ width: 300 }}
|
style={{ width: 230 }}
|
||||||
allowClear
|
allowClear
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
></Input>
|
></Input>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
showSearch={false}
|
||||||
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'resources.filter.worker'
|
||||||
|
})}
|
||||||
|
style={{ width: 230 }}
|
||||||
|
size="large"
|
||||||
|
onChange={handleWorkerChange}
|
||||||
|
options={workersList}
|
||||||
|
></Select>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||||
|
|||||||
Reference in New Issue
Block a user