style: model list buttons

main
jialin 1 year ago
parent a7cfa05504
commit 5edbc1a7b5

@ -89,7 +89,7 @@ export default [
name: 'apikeys',
path: '/api-keys',
key: 'apikeys',
icon: 'LockOutlined',
icon: 'KeyOutlined',
component: './api-keys'
},
{

@ -10,6 +10,11 @@ interface DropdownButtonsProps {
items: MenuProps['items'];
size?: 'small' | 'middle' | 'large';
trigger?: Trigger[];
showText?: boolean;
disabled?: boolean;
variant?: 'filled' | 'outlined';
color?: string;
extra?: React.ReactNode;
onSelect: (val: any, item?: any) => void;
}
@ -17,9 +22,16 @@ const DropdownButtons: React.FC<DropdownButtonsProps> = ({
items,
size = 'middle',
trigger = ['hover'],
showText,
disabled,
variant,
color,
extra,
onSelect
}) => {
const headItem = _.head(items);
const intl = useIntl();
const handleMenuClick = (item: any) => {
const selectItem = _.find(items, { key: item.key });
onSelect(item.key, selectItem);
@ -37,19 +49,20 @@ const DropdownButtons: React.FC<DropdownButtonsProps> = ({
return (
<>
{items?.length === 1 ? (
<Tooltip title={intl.formatMessage({ id: _.head(items)?.label })}>
<Tooltip title={intl.formatMessage({ id: headItem?.label })}>
<Button
className={classNames('dropdown-button', size)}
{..._.head(items)}
icon={_.get(items, '0.icon')}
icon={headItem?.icon}
size={size}
{..._.get(items, '0.props')}
{...headItem?.props}
onClick={handleButtonClick}
></Button>
</Tooltip>
) : (
<Dropdown.Button
disabled={disabled}
trigger={trigger}
type="primary"
dropdownRender={(menus: any) => {
return (
<div
@ -67,9 +80,10 @@ const DropdownButtons: React.FC<DropdownButtonsProps> = ({
<Button
{...item.props}
type="text"
size="middle"
size={size}
icon={item.icon}
key={item.key}
disabled={item.disabled}
onClick={() => handleMenuClick(item)}
style={{ width: '100%', justifyContent: 'flex-start' }}
>
@ -81,21 +95,45 @@ const DropdownButtons: React.FC<DropdownButtonsProps> = ({
);
}}
buttonsRender={([leftButton, rightButton]) => [
<Tooltip
title={intl.formatMessage({ id: _.head(items)?.label })}
key="leftButton"
>
<Button
className={classNames('dropdown-button', size)}
onClick={handleButtonClick}
size={size}
icon={_.head(items)?.icon}
></Button>
</Tooltip>,
<>
{showText ? (
<Button
{...headItem?.props}
disabled={headItem?.disabled || disabled}
className={classNames('dropdown-button', size)}
onClick={handleButtonClick}
size={size}
icon={headItem?.icon}
variant={variant}
color={color}
>
{intl.formatMessage({
id: headItem?.label
})}
{extra}
</Button>
) : (
<Tooltip
title={intl.formatMessage({ id: headItem?.label })}
key="leftButton"
>
<Button
{...headItem?.props}
className={classNames('dropdown-button', size)}
onClick={handleButtonClick}
size={size}
icon={headItem?.icon}
disabled={headItem?.disabled}
></Button>
</Tooltip>
)}
</>,
<Button
icon={<MoreOutlined />}
size={size}
key="menu"
variant={variant}
color="default"
className={classNames('dropdown-button', size)}
></Button>
]}

@ -3,15 +3,20 @@ import React from 'react';
import styles from './styles/wrapper.less';
const Wrapper: React.FC<{
label?: string;
label?: React.ReactNode;
description?: React.ReactNode;
labelExtra?: React.ReactNode;
children: React.ReactNode;
}> = ({ children, label, description, ...rest }) => {
}> = ({ children, label, description, labelExtra, ...rest }) => {
return (
<div className={styles['wrapper']}>
{label && (
<span className="label">
<LabelInfo label={label} description={description}></LabelInfo>
<LabelInfo
label={label}
description={description}
labelExtra={labelExtra}
></LabelInfo>
</span>
)}
{React.isValidElement(children)

@ -8,17 +8,26 @@ import ListItem from './list-item';
interface ListInputProps {
dataList: string[];
label: string;
label: React.ReactNode;
description?: React.ReactNode;
btnText?: string;
options?: Global.HintOptions[];
placeholder?: string;
labelExtra?: React.ReactNode;
onChange: (data: string[]) => void;
}
const ListInput: React.FC<ListInputProps> = (props) => {
const intl = useIntl();
const { dataList, label, description, onChange, btnText, options } = props;
const {
dataList,
label,
description,
onChange,
btnText,
options,
labelExtra
} = props;
const [list, setList] = React.useState<{ value: string; uid: number }[]>([]);
const countRef = React.useRef(0);
const buttonRef = React.useRef<HTMLButtonElement>(null);
@ -71,7 +80,7 @@ const ListInput: React.FC<ListInputProps> = (props) => {
}, [dataList]);
return (
<Wrapper label={label} description={description}>
<Wrapper label={label} description={description} labelExtra={labelExtra}>
<>
{_.map(list, (item: any, index: number) => {
return (

@ -7,9 +7,10 @@ interface NoteInfoProps {
required?: boolean;
label: React.ReactNode;
description?: React.ReactNode;
labelExtra?: React.ReactNode;
}
const NoteInfo: React.FC<NoteInfoProps> = (props) => {
const { required, description, label } = props || {};
const { required, description, label, labelExtra } = props || {};
if (!label) return null;
return (
@ -43,6 +44,7 @@ const NoteInfo: React.FC<NoteInfoProps> = (props) => {
</span>
</>
)}
{labelExtra}
</span>
);
};

@ -17,6 +17,7 @@ interface WrapperProps {
variant?: string;
hasPrefix?: boolean;
classList?: string;
labelExtra?: React.ReactNode;
onClick?: () => void;
}
@ -35,6 +36,7 @@ const Wrapper: React.FC<WrapperProps> = ({
hasPrefix,
noWrapperStyle,
classList,
labelExtra,
onClick
}) => {
return (
@ -74,6 +76,7 @@ const Wrapper: React.FC<WrapperProps> = ({
label={label}
required={required}
description={description}
labelExtra={labelExtra}
></LabelInfo>
</label>
{extra && <div className={wrapperStyle.extra}>{extra}</div>}

@ -20,6 +20,7 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
checkStatus,
trim = true,
loading,
labelExtra,
...rest
} = props;
const [isFocus, setIsFocus] = useState(false);
@ -74,6 +75,7 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
<Wrapper
status={checkStatus || status}
label={label}
labelExtra={labelExtra}
isFocus={isFocus}
required={required}
description={description}

@ -9,6 +9,7 @@ export interface SealFormItemProps {
addAfter?: React.ReactNode;
allowNull?: boolean;
loading?: React.ReactNode;
labelExtra?: React.ReactNode;
trim?: boolean;
checkStatus?: 'success' | 'error' | 'warning' | '';
}

@ -53,12 +53,12 @@ const TableRow: React.FC<
childrenDataRef.current = childrenData;
const axiosToken = useRef<any>(null);
const [updateChild, setUpdateChild] = useState(true);
const [currentExpand, setCurrentExpand] = useState(false);
const { updateChunkedList, cacheDataListRef } = useUpdateChunkedList({
dataList: childrenData,
limit: 100,
setDataList: setChildrenData
// callback: (list) => renderChildren?.(list)
});
useEffect(() => {
@ -92,7 +92,10 @@ const TableRow: React.FC<
></Empty>
);
}
return renderChildren?.(childrenData, record);
return renderChildren?.(childrenData, {
parent: record,
currentExpanded: currentExpand
});
};
const handlePolling = async () => {
@ -163,6 +166,7 @@ const TableRow: React.FC<
const handleRowExpand = async () => {
onExpand?.(!expanded, record, record[rowKey]);
setCurrentExpand(!expanded);
if (pollTimer.current) {
clearInterval(pollTimer.current);

@ -2,8 +2,7 @@
position: relative;
display: flex;
align-items: center;
min-height: 54px;
padding-block: 5px;
height: 54px;
border-radius: var(--border-radius-mdium);
transition: all 0.2s ease;

@ -59,7 +59,10 @@ export interface SealTableProps {
onSort?: (dataIndex: string, order: 'ascend' | 'descend') => void;
onExpand?: (expanded: boolean, record: any, rowKey: any) => void;
onExpandAll?: (expanded: boolean) => void;
renderChildren?: (data: any, parent?: any) => React.ReactNode;
renderChildren?: (
data: any,
options: { parent?: any; [key: string]: any }
) => React.ReactNode;
loadChildren?: (record: any, options?: any) => Promise<any[]>;
loadChildrenAPI?: (record: any) => string;
contentRendered?: () => void;

@ -14,7 +14,7 @@ const TableHeader = ({ columns }: TableHeaderProps) => {
{columns.map((column: any, index: number) => {
return (
<th key={index}>
<span className="cell-span">
<span className="cell-span cell-header">
{column.locale
? intl.formatMessage({ id: column.title })
: column.title}

@ -16,7 +16,7 @@
}
tr:last-child td {
border-bottom: none;
// border-bottom: none;
}
}
@ -25,9 +25,17 @@
font-weight: 600;
}
td {
color: var(--color-white-quaternary);
}
.cell-span {
display: flex;
padding: 4px 6px;
min-height: 32px;
}
.cell-header {
font-weight: var(--font-weight-bold);
}
}

@ -6,11 +6,31 @@ import TableHeader from './header';
import './index.less';
import TableRow from './row';
interface ColumnProps {
export interface ColumnProps {
title: string;
key: string;
render?: (data: { dataIndex: string; row: any }) => any;
render?: (data: {
dataIndex: string;
dataList?: any[];
row: any;
rowIndex?: number;
colIndex?: number;
}) => any;
locale?: boolean;
colSpan?: (params: {
row: any;
rowIndex: number;
colIndex: number;
dataIndex: string;
dataList: any[];
}) => number;
rowSpan?: (params: {
row: any;
rowIndex: number;
colIndex: number;
dataIndex: string;
dataList: any[];
}) => number;
}
interface SimpleTableProps {
@ -42,7 +62,9 @@ const SimpleTabel: React.FC<SimpleTableProps> = (props) => {
return (
<TableRow
row={item}
rowIndex={index}
columns={columns}
dataList={dataSource}
key={rowKey ? item[rowKey] : index}
></TableRow>
);

@ -1,25 +1,88 @@
import React from 'react';
import React, { useMemo } from 'react';
interface TableRowProps {
row: any;
columns: any;
rowIndex: number;
dataList: any[];
}
const TableRow = ({ row, columns }: TableRowProps) => {
interface TableCellProps {
row: any;
column: any;
rowIndex: number;
colIndex: number;
dataList: any[];
}
const TableCell: React.FC<TableCellProps> = (props: TableCellProps) => {
const { row, column, rowIndex, colIndex, dataList } = props;
const renderContent = useMemo(() => {
return column.render
? column.render({
dataIndex: column.key,
dataList: dataList,
row: row,
rowIndex,
colIndex: colIndex
})
: row[column.key];
}, [column, row, rowIndex, colIndex, dataList]);
if (renderContent === null && (column.colSpan || column.rowSpan)) {
return null;
}
return (
<td
key={colIndex}
rowSpan={column.rowSpan?.({
row,
rowIndex,
colIndex: colIndex,
dataIndex: column.key,
dataList: dataList
})}
colSpan={column.colSpan?.({
row,
rowIndex,
colIndex: colIndex,
dataIndex: column.key,
dataList: dataList
})}
>
<span className="cell-span">
{column.render
? column.render({
dataIndex: column.key,
dataList: dataList,
row: row,
rowIndex,
colIndex: colIndex
})
: row[column.key]}
</span>
</td>
);
};
const TableRow = ({ row, columns, rowIndex, dataList }: TableRowProps) => {
return (
<tr>
{columns.map((column: any, index: number) => {
return (
<td key={index}>
<span className="cell-span">
{column.render
? column.render({ dataIndex: column.key, row: row })
: row[column.key]}
</span>
</td>
<TableCell
key={index}
rowIndex={rowIndex}
colIndex={index}
dataList={dataList}
row={row}
column={column}
></TableCell>
);
})}
</tr>
);
};
export default React.memo(TableRow);
export default TableRow;

@ -3,13 +3,16 @@ import { useCallback, useState } from 'react';
export default function useExpandedRowKeys(defaultKeys: React.Key[] = []) {
const [expandedRowKeys, setExpandedRowKeys] =
useState<React.Key[]>(defaultKeys);
const [currentExpand, setCurrentExpand] = useState<React.Key | null>(null);
const handleExpandChange = useCallback(
(expanded: boolean, record: any, rowKey: any) => {
if (expanded) {
setExpandedRowKeys((keys) => [...keys, rowKey]);
setCurrentExpand(rowKey);
} else {
setExpandedRowKeys((keys) => keys.filter((key) => key !== rowKey));
setCurrentExpand(null);
}
},
[]
@ -19,6 +22,7 @@ export default function useExpandedRowKeys(defaultKeys: React.Key[] = []) {
(expanded: boolean, keys: React.Key[] = []) => {
if (!expanded) {
setExpandedRowKeys([]);
setCurrentExpand(null);
}
if (expanded) {
setExpandedRowKeys((prevKeys) => [...new Set([...prevKeys, ...keys])]);
@ -41,10 +45,13 @@ export default function useExpandedRowKeys(defaultKeys: React.Key[] = []) {
const clearExpandedRowKeys = () => {
setExpandedRowKeys([]);
setCurrentExpand(null);
};
return {
expandedRowKeys,
currentExpand,
setCurrentExpand,
clearExpandedRowKeys,
updateExpandedRowKeys,
removeExpandedRowKey,

@ -108,5 +108,7 @@ export default {
'models.table.list.getStart':
'<span style="margin-right: 5px;font-size: 13px;">Get started with</span> <span style="font-size: 14px;font-weight: 700">DeepSeek-R1-Distill-Qwen-1.5B</span>',
'models.table.llamaAcrossworker': 'Llama-box Across Workers',
'models.table.vllmAcrossworker': 'vLLM Across Workers'
'models.table.vllmAcrossworker': 'vLLM Across Workers',
'models.form.releases': 'Releases',
'models.form.moreparameters': 'More Parameters'
};

@ -109,5 +109,8 @@ export default {
'models.table.llamaAcrossworker':
'TODO: Translate key "models.table.llamaAcrossworker"',
'models.table.vllmAcrossworker':
'TODO: Translate key "models.table.vllmAcrossworker"'
'TODO: Translate key "models.table.vllmAcrossworker"',
'models.form.releases': 'TODO: Translate key "models.form.releases"',
'models.form.moreparameters':
'TODO: Translate key "models.form.moreparameters"'
};

@ -104,5 +104,7 @@ export default {
'models.table.list.getStart':
'<span style="margin-right: 5px;font-size: 13px;">一键部署</span><span style="font-size: 14px;font-weight: 700">DeepSeek-R1-Distill-Qwen-1.5B</span><span style="margin-left: 5px;font-size: 13px;">立即使用!</span>',
'models.table.llamaAcrossworker': 'Llama-box 跨节点',
'models.table.vllmAcrossworker': 'vLLM 跨节点'
'models.table.vllmAcrossworker': 'vLLM 跨节点',
'models.form.releases': '版本',
'models.form.moreparameters': '更多参数'
};

@ -101,12 +101,14 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
if (backend === backendOptionsMap.llamaBox) {
return {
backend: 'llama-box',
releases: 'https://github.com/gpustack/llama-box/releases',
link: 'https://github.com/gpustack/llama-box?tab=readme-ov-file#usage'
};
}
if (backend === backendOptionsMap.vllm) {
return {
backend: 'vLLM',
releases: 'https://github.com/vllm-project/vllm/releases',
link: 'https://docs.vllm.ai/en/stable/serving/openai_compatible_server.html#command-line-arguments-for-the-server'
};
}
@ -327,6 +329,25 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
description={intl.formatMessage({
id: 'models.form.backendVersion.tips'
})}
labelExtra={
backendParamsTips?.releases && (
<span
style={{
marginLeft: 5
}}
>
(
<Typography.Link
style={{ lineHeight: 1 }}
href={backendParamsTips?.releases}
target="_blank"
>
{intl.formatMessage({ id: 'models.form.releases' })}
</Typography.Link>
)
</span>
)
}
></SealInput.Input>
</Form.Item>
@ -346,19 +367,20 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
dataList={form.getFieldValue('backend_parameters') || []}
onChange={handleBackendParametersChange}
options={paramsConfig}
description={
backendParamsTips && (
<span>
{intl.formatMessage(
{ id: 'models.form.backend_parameters.vllm.tips' },
{ backend: backendParamsTips.backend || '' }
)}{' '}
labelExtra={
backendParamsTips?.link && (
<span style={{ marginLeft: 2 }}>
(
<Typography.Link
href={backendParamsTips.link}
style={{ lineHeight: 1 }}
href={backendParamsTips?.link}
target="_blank"
>
{intl.formatMessage({ id: 'common.text.here' })}
{intl.formatMessage({
id: 'models.form.moreparameters'
})}
</Typography.Link>
)
</span>
)
}

@ -2,7 +2,7 @@ import AutoTooltip from '@/components/auto-tooltip';
import DropdownButtons from '@/components/drop-down-buttons';
import IconFont from '@/components/icon-font';
import RowChildren from '@/components/seal-table/components/row-children';
import SimpleTabel from '@/components/simple-table';
import SimpleTabel, { ColumnProps } from '@/components/simple-table';
import StatusTag from '@/components/status-tag';
import { HandlerOptions } from '@/hooks/use-chunk-fetch';
import useDownloadStream from '@/hooks/use-download-stream';
@ -12,7 +12,8 @@ import {
DeleteOutlined,
DownloadOutlined,
HddFilled,
InfoCircleOutlined
InfoCircleOutlined,
ThunderboltFilled
} from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import {
@ -27,17 +28,48 @@ import {
} from 'antd';
import dayjs from 'dayjs';
import _ from 'lodash';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';
import styled from 'styled-components';
import { MODEL_INSTANCE_API } from '../apis';
import { InstanceStatusMap, InstanceStatusMapValue, status } from '../config';
import { ModelInstanceListItem } from '../config/types';
import '../style/instance-item.less';
const WorkerInfo = (props: {
title: React.ReactNode;
defaultOpen: boolean;
}) => {
const [open, setOpen] = React.useState(props.defaultOpen);
useEffect(() => {
if (props.defaultOpen) {
setTimeout(() => {
setOpen(false);
}, 1000);
}
}, [props.defaultOpen]);
return (
<span className="server-info-wrapper">
<Tooltip
open={open}
onOpenChange={setOpen}
title={props.title}
overlayInnerStyle={{
width: 'max-content',
maxWidth: '400px'
}}
>
<span className="server-info">
<InfoCircleOutlined />
</span>
</Tooltip>
</span>
);
};
interface InstanceItemProps {
instanceData: ModelInstanceListItem;
workerList: WorkerListItem[];
modelData?: any;
defaultOpenId: string;
handleChildSelect: (val: string, item: ModelInstanceListItem) => void;
}
@ -76,7 +108,7 @@ const childActionList = [
}
];
const distributeCols = [
const distributeCols: ColumnProps[] = [
{
title: 'Worker',
key: 'worker_name'
@ -84,7 +116,7 @@ const distributeCols = [
{
title: 'IP',
key: 'worker_ip',
render: ({ row }: { row: ModelInstanceListItem }) => {
render: ({ row }) => {
return row.port ? `${row.worker_ip}:${row.port}` : row.worker_ip;
}
},
@ -94,9 +126,21 @@ const distributeCols = [
key: 'gpu_index'
},
{
title: 'resources.table.memory',
title: 'resources.table.vram',
locale: true,
key: 'ram'
key: 'vram',
rowSpan: ({ row, rowIndex, colIndex, dataIndex, dataList }) => {
return rowIndex === 0 ? dataList.length : 0;
},
render: ({ rowIndex, dataList }) => {
if (rowIndex === 0) {
return convertFileSize(
_.sumBy(dataList, (item: any) => item.vram),
2
);
}
return null;
}
}
];
@ -153,6 +197,7 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
instanceData,
workerList,
modelData,
defaultOpenId,
handleChildSelect
}) => {
const [api, contextHolder] = notification.useNotification({
@ -224,55 +269,32 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
? `${instanceData.worker_ip}:${instanceData.port}`
: instanceData.worker_ip;
}
let backend = modelData?.backend || '';
if (modelData.backend_version) {
backend += ` (${modelData.backend_version})`;
}
const vrams = instanceData.computed_resource_claim?.vram || {};
return (
<div>
<div style={{ marginBottom: 5 }}>
<div>{instanceData.worker_name}</div>
<div className="flex-center">
<HddFilled className="m-r-5" />
{instanceData.worker_name}
{workerIp}
</div>
<div className="flex m-b-6 gap-6">
<InfoItem label="IP" value={workerIp} width={180}></InfoItem>
<InfoItem
width={90}
label={intl.formatMessage({ id: 'models.form.backend' })}
value={backend}
></InfoItem>
<div className="flex-center">
<IconFont type="icon-filled-gpu" className="m-r-5" />
{intl.formatMessage({ id: 'models.table.gpuindex' })}: [
{_.join(instanceData.gpu_indexes?.sort?.(), ',')}]
</div>
<div className="flex gap-6">
<InfoItem
width={180}
label={intl.formatMessage({ id: 'models.table.gpuindex' })}
value={Object.keys(vrams)
?.sort?.()
.map((index) => {
return (
<span className="flex-1" key={index}>
<span className="index">
[{index}] {''}
</span>
{convertFileSize(vrams?.[index], 0)}
</span>
);
})}
></InfoItem>
<InfoItem
width={90}
label={intl.formatMessage({ id: 'resources.table.memory' })}
value={convertFileSize(
instanceData.computed_resource_claim?.ram,
0
)}
></InfoItem>
<div className="flex-center">
<ThunderboltFilled className="m-r-5" />
{intl.formatMessage({ id: 'models.form.backend' })}:{' '}
{modelData?.backend || ''}
{modelData.backend_version ? `(${modelData.backend_version})` : ''}
</div>
</div>
);
}, [modelData, instanceData, intl]);
const calcTotalVram = (vram: Record<string, number>) => {
return _.sum(_.values(vram));
};
const renderDistributedServer = useCallback(
(severList: any[]) => {
const list = _.map(severList, (item: any) => {
@ -281,20 +303,35 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
worker_name: data?.name,
worker_ip: data?.ip,
port: '',
ram: convertFileSize(item.computed_resource_claim?.ram, 0),
gpu_index: displayGPUs(item.computed_resource_claim?.vram || {})
vram: calcTotalVram(item.computed_resource_claim?.vram || {}),
gpu_index: _.keys(item.computed_resource_claim?.vram).join(',')
};
});
// const list = [
// {
// worker_name: 'worker1',
// worker_ip: '192.168.50.23',
// port: '',
// vram: 21555525632,
// gpu_index: '0,1'
// },
// {
// worker_name: 'worker2',
// worker_ip: '192.168.50.25',
// port: '',
// vram: 21555525632,
// gpu_index: '2,3'
// }
// ];
const mainWorker = [
{
worker_name: `${instanceData.worker_name} (main)`,
worker_name: `${instanceData.worker_name}`,
worker_ip: `${instanceData.worker_ip}`,
port: '',
ram: convertFileSize(instanceData.computed_resource_claim?.ram, 0),
gpu_index: displayGPUs(
instanceData.computed_resource_claim?.vram || {}
)
vram: calcTotalVram(instanceData.computed_resource_claim?.vram || {}),
gpu_index: `${instanceData.gpu_indexes?.join?.(',')}(main)`
}
];
@ -443,18 +480,12 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
<AutoTooltip title={instanceData.name} ghost>
<span className="m-r-5">{instanceData.name}</span>
</AutoTooltip>
<Tooltip
title={renderWorkerInfo}
overlayInnerStyle={{
width: 'max-content',
maxWidth: '400px'
}}
>
<span className="server-info">
<InfoCircleOutlined className="m-r-2" />{' '}
{intl.formatMessage({ id: 'common.button.moreInfo' })}
</span>
</Tooltip>
{!!instanceData.worker_id && (
<WorkerInfo
title={renderWorkerInfo}
defaultOpen={defaultOpenId === instanceData.name}
></WorkerInfo>
)}
</span>
</Col>
<Col span={6}>
@ -468,10 +499,9 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
>
{renderOffloadInfo}
{renderDistributionInfo(
instanceData.distributed_servers?.rpc_servers || []
)}
{renderDistributionInfo(
instanceData.distributed_servers?.ray_actors || []
instanceData.distributed_servers?.rpc_servers ||
instanceData.distributed_servers?.ray_actors ||
[]
)}
</span>
</Col>

@ -1,7 +1,7 @@
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
import { Space } from 'antd';
import _ from 'lodash';
import React from 'react';
import React, { useEffect, useMemo } from 'react';
import { ModelInstanceListItem } from '../config/types';
import '../style/instance-item.less';
import InstanceItem from './instance-item';
@ -10,6 +10,7 @@ interface InstanceItemProps {
list: ModelInstanceListItem[];
workerList: WorkerListItem[];
modelData?: any;
currentExpanded?: string;
handleChildSelect: (val: string, item: ModelInstanceListItem) => void;
}
@ -17,8 +18,26 @@ const Instances: React.FC<InstanceItemProps> = ({
list,
workerList,
modelData,
currentExpanded,
handleChildSelect
}) => {
const [firstLoad, setFirstLoad] = React.useState(true);
const defaultOpenId = useMemo(() => {
if (!currentExpanded) {
return '';
}
const current = _.find(
list,
(item: ModelInstanceListItem) => item.worker_id
);
return current ? current.name : '';
}, [currentExpanded, list]);
useEffect(() => {
setFirstLoad(false);
}, []);
return (
<Space size={16} direction="vertical" style={{ width: '100%' }}>
{_.map(list, (item: ModelInstanceListItem, index: number) => {
@ -28,6 +47,7 @@ const Instances: React.FC<InstanceItemProps> = ({
modelData={modelData}
workerList={workerList}
instanceData={item}
defaultOpenId={firstLoad ? defaultOpenId : ''}
handleChildSelect={handleChildSelect}
></InstanceItem>
);

@ -32,7 +32,7 @@ import {
SyncOutlined
} from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
import { useAccess, useIntl, useNavigate } from '@umijs/max';
import {
Button,
Dropdown,
@ -68,7 +68,6 @@ import {
} from '../config';
import { FormData, ListItem, ModelInstanceListItem } from '../config/types';
import { useGenerateFormEditInitialValues } from '../hooks';
import DeployDropdown from './deploy-dropdown';
import DeployModal from './deploy-modal';
import Instances from './instances';
import ModelTag from './model-tag';
@ -120,7 +119,7 @@ const ActionList = [
{
label: 'common.button.start',
key: 'start',
icon: <IconFont type="icon-playcircle"></IconFont>
icon: <IconFont type="icon-outline-play"></IconFont>
},
{
label: 'common.button.delete',
@ -132,6 +131,27 @@ const ActionList = [
}
];
const ButtonList = [
{
label: 'common.button.start',
key: 'start',
icon: <IconFont type="icon-outline-play"></IconFont>
},
{
label: 'common.button.stop',
key: 'stop',
icon: <IconFont type="icon-stop1"></IconFont>
},
{
label: 'common.button.delete',
key: 'delete',
icon: <DeleteOutlined />,
props: {
danger: true
}
}
];
const generateSource = (record: ListItem) => {
if (record.source === modelSourceMap.modelscope_value) {
return `${modelSourceMap.modelScope}/${record.model_scope_model_id}`;
@ -327,6 +347,12 @@ const Models: React.FC<ModelsProps> = ({
}, []);
const sourceOptions = [
{
label: intl.formatMessage({ id: 'menu.models.modelCatalog' }),
value: 'catalog',
key: 'catalog',
icon: <IconFont type="icon-catalog"></IconFont>
},
{
label: 'Hugging Face',
value: modelSourceMap.huggingface_value,
@ -345,12 +371,6 @@ const Models: React.FC<ModelsProps> = ({
key: 'modelscope',
icon: <IconFont type="icon-tu2"></IconFont>
},
{
label: intl.formatMessage({ id: 'menu.models.modelCatalog' }),
value: 'catalog',
key: 'catalog',
icon: <IconFont type="icon-catalog"></IconFont>
},
{
label: intl.formatMessage({ id: 'models.form.localPath' }),
value: modelSourceMap.local_path_value,
@ -659,11 +679,12 @@ const Models: React.FC<ModelsProps> = ({
);
const renderChildren = useCallback(
(list: any, parent?: any) => {
(list: any, options: { parent?: any; [key: string]: any }) => {
return (
<Instances
list={list}
modelData={parent}
currentExpanded={options.currentExpanded}
modelData={options.parent}
workerList={workerList}
handleChildSelect={handleChildSelect}
></Instances>
@ -739,6 +760,18 @@ const Models: React.FC<ModelsProps> = ({
});
};
const handleActionSelect = (val: any) => {
if (val === 'delete') {
handleDeleteBatch();
}
if (val === 'start') {
handleStartBatch();
}
if (val === 'stop') {
handleStopBatch();
}
};
const columns: SealColumnProps[] = useMemo(() => {
return [
{
@ -939,12 +972,6 @@ const Models: React.FC<ModelsProps> = ({
onClick: handleClickDropdown
}}
trigger={['hover']}
dropdownRender={() => (
<DeployDropdown
items={sourceOptions}
onSelect={handleClickDropdown}
></DeployDropdown>
)}
placement="bottomRight"
>
<Button
@ -955,7 +982,19 @@ const Models: React.FC<ModelsProps> = ({
{intl?.formatMessage?.({ id: 'models.button.deploy' })}
</Button>
</Dropdown>
<Button
<DropdownButtons
items={ButtonList}
extra={
rowSelection.selectedRowKeys.length > 0 && (
<span>({rowSelection.selectedRowKeys.length})</span>
)
}
size="large"
showText={true}
disabled={!rowSelection.selectedRowKeys.length}
onSelect={handleActionSelect}
/>
{/* <Button
icon={<IconFont type="icon-outline-play"></IconFont>}
onClick={handleStartBatch}
disabled={!rowSelection.selectedRows.length}
@ -993,7 +1032,7 @@ const Models: React.FC<ModelsProps> = ({
)}
</span>
</Button>
</Access>
</Access> */}
</Space>
}
></PageTools>

@ -1,6 +1,7 @@
import AutoTooltip from '@/components/auto-tooltip';
import PageTools from '@/components/page-tools';
import ProgressBar from '@/components/progress-bar';
import InfoColumn from '@/components/simple-table/info-column';
import useTableSort from '@/hooks/use-table-sort';
import { convertFileSize } from '@/utils';
import { SyncOutlined } from '@ant-design/icons';
@ -12,6 +13,33 @@ import { queryGpuDevicesList } from '../apis';
import { GPUDeviceItem } from '../config/types';
const { Column } = Table;
const fieldList = [
{
label: 'resources.table.total',
key: 'total',
locale: true,
render: (val: any) => {
return convertFileSize(val, 0);
}
},
{
label: 'resources.table.used',
key: 'used',
locale: true,
render: (val: any) => {
return convertFileSize(val, 0);
}
},
{
label: 'resources.table.allocated',
key: 'allocated',
locale: true,
render: (val: any) => {
return convertFileSize(val, 0);
}
}
];
const GPUList: React.FC = () => {
console.log('GPUList======');
const intl = useIntl();
@ -228,19 +256,10 @@ const GPUList: React.FC = () => {
) * 100
}
label={
<span className="flex-column">
<span>
{intl.formatMessage({ id: 'resources.table.total' })}:{' '}
{convertFileSize(record.memory?.total, 0)}
</span>
<span>
{intl.formatMessage({ id: 'resources.table.used' })}:{' '}
{convertFileSize(
record.memory?.used || record.memory?.allocated,
0
)}
</span>
</span>
<InfoColumn
fieldList={fieldList}
data={record.memory}
></InfoColumn>
}
></ProgressBar>
);

Loading…
Cancel
Save