chore: display instance resource claim
This commit is contained in:
@@ -6,6 +6,10 @@
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.m-b-6 {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.m-b-5 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
|
||||
// import './iconfont/iconfont.js';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_y6wf3nguxjl.js'
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_d7ffxju7lc6.js'
|
||||
});
|
||||
|
||||
export default IconFont;
|
||||
|
||||
@@ -915,48 +915,42 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = forwardRef(
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="tools">
|
||||
{maskUpload?.length ? (
|
||||
<span className="flex-center upload-mask">
|
||||
<span className="font-size-12">
|
||||
{intl.formatMessage({ id: 'playground.image.mask.uploaded' })}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
{imageStatus.isOriginal && (
|
||||
<>
|
||||
{imageStatus.isOriginal && (
|
||||
<>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
<Tooltip
|
||||
title={
|
||||
<span style={{ whiteSpace: 'pre-wrap' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'playground.image.negativeMask.tips'
|
||||
})}
|
||||
>
|
||||
<Checkbox
|
||||
onChange={handleOnChangeMask}
|
||||
className="flex-center"
|
||||
checked={invertMask}
|
||||
disabled={isDisabled || !!maskUpload?.length}
|
||||
>
|
||||
<span className="font-size-12">
|
||||
{intl.formatMessage({
|
||||
id: 'playground.image.negativeMask'
|
||||
})}
|
||||
</span>
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'playground.image.saveMask'
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Checkbox
|
||||
onChange={handleOnChangeMask}
|
||||
className="flex-center"
|
||||
checked={invertMask}
|
||||
disabled={isDisabled || !!maskUpload?.length}
|
||||
>
|
||||
<span className="font-size-12">
|
||||
{intl.formatMessage({
|
||||
id: 'playground.image.negativeMask'
|
||||
})}
|
||||
>
|
||||
<Button onClick={downloadMask} size="middle" type="text">
|
||||
<IconFont
|
||||
className="font-size-14"
|
||||
type="icon-save2"
|
||||
></IconFont>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'playground.image.saveMask'
|
||||
})}
|
||||
>
|
||||
<Button onClick={downloadMask} size="middle" type="text">
|
||||
<IconFont
|
||||
className="font-size-14"
|
||||
type="icon-save2"
|
||||
></IconFont>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
{!imageStatus.isOriginal && (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import useOverlayScroller from '@/hooks/use-overlay-scroller';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import SimpleBar from 'simplebar-react';
|
||||
import 'simplebar-react/dist/simplebar.min.css';
|
||||
import TableHeader from './header';
|
||||
import './index.less';
|
||||
@@ -21,8 +21,14 @@ interface SimpleTableProps {
|
||||
}
|
||||
const SimpleTabel: React.FC<SimpleTableProps> = (props) => {
|
||||
const { columns, dataSource, rowKey, bordered = true } = props;
|
||||
const scroller = React.useRef<any>(null);
|
||||
const { initialize } = useOverlayScroller();
|
||||
|
||||
React.useEffect(() => {
|
||||
initialize(scroller.current);
|
||||
}, [initialize]);
|
||||
return (
|
||||
<SimpleBar style={{ maxHeight: 200 }}>
|
||||
<div style={{ maxHeight: 200 }} ref={scroller}>
|
||||
<table
|
||||
className={classNames('simple-table', {
|
||||
'simple-table-bordered': bordered
|
||||
@@ -43,7 +49,7 @@ const SimpleTabel: React.FC<SimpleTableProps> = (props) => {
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</SimpleBar>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
import React from 'react';
|
||||
|
||||
import { convertFileSize } from '@/utils';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Divider } from 'antd';
|
||||
|
||||
interface InfoColumnProps {
|
||||
fieldList: { label: string; key: string }[];
|
||||
fieldList: {
|
||||
label: string;
|
||||
key: string;
|
||||
locale?: boolean;
|
||||
render?: (val: any, data: any) => any;
|
||||
}[];
|
||||
style?: React.CSSProperties;
|
||||
data: Record<string, any>;
|
||||
}
|
||||
const InfoColumn: React.FC<InfoColumnProps> = (props) => {
|
||||
const { data, fieldList } = props;
|
||||
const { data, fieldList, style } = props;
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<span className="flex">
|
||||
<span className="flex" style={style}>
|
||||
{fieldList.map((item, index) => {
|
||||
return (
|
||||
<>
|
||||
<span className="flex-column flex-center">
|
||||
<span> {intl.formatMessage({ id: item.label })}</span>
|
||||
<span> {convertFileSize(data[item.key], 0)}</span>
|
||||
<span>
|
||||
{' '}
|
||||
{item.locale
|
||||
? intl.formatMessage({ id: item.label })
|
||||
: item.label}
|
||||
</span>
|
||||
<span style={{ color: 'var(--color-white-quaternary)' }}>
|
||||
{' '}
|
||||
{item.render?.(data[item.key], data) ?? data[item.key]}
|
||||
</span>
|
||||
</span>
|
||||
{index < fieldList.length - 1 ? (
|
||||
<Divider
|
||||
|
||||
@@ -53,6 +53,8 @@ html {
|
||||
--color-white-light-2: rgba(255, 255, 255, 25%);
|
||||
--color-white-light-3: rgba(255, 255, 255, 35%);
|
||||
--color-white-light-4: rgba(255, 255, 255, 45%);
|
||||
--color-gray-fill-4: rgba(92, 92, 92, 45%);
|
||||
--color-gray-fill-3: rgba(92, 92, 92, 35%);
|
||||
--color-text-1: var(--ant-color-text);
|
||||
--color-text-3: rgba(0, 0, 0, 45%);
|
||||
--color-text-2: rgba(0, 0, 0, 65%);
|
||||
|
||||
@@ -148,5 +148,5 @@ export default {
|
||||
'playground.image.origin': 'Original',
|
||||
'playground.image.mask': 'Mask',
|
||||
'playground.image.negativeMask.tips':
|
||||
'After selection, no further masking can be drawn; therefore, you should draw the mask first and then check the option.'
|
||||
'1. After selection, no further masking can be drawn; therefore, you should draw the mask first and then check the option.\n 2. Once a mask image is uploaded, no further masks can be generated.'
|
||||
};
|
||||
|
||||
@@ -142,5 +142,5 @@ export default {
|
||||
'playground.image.origin': '原图',
|
||||
'playground.image.mask': '遮罩',
|
||||
'playground.image.negativeMask.tips':
|
||||
'选择后,将不可再绘制遮罩;因此,你应该先绘制遮罩然后再勾选'
|
||||
'1. 选择后,将不可再绘制遮罩;因此,你应该先绘制遮罩然后再勾选.\n 2. 当上传了遮罩图片,将不可再生成遮罩'
|
||||
};
|
||||
|
||||
@@ -7,12 +7,12 @@ import StatusTag from '@/components/status-tag';
|
||||
import { HandlerOptions } from '@/hooks/use-chunk-fetch';
|
||||
import useDownloadStream from '@/hooks/use-download-stream';
|
||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||
import { convertFileSize } from '@/utils';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownloadOutlined,
|
||||
HddFilled,
|
||||
InfoCircleOutlined,
|
||||
ThunderboltFilled
|
||||
InfoCircleOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import {
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import React, { useCallback, 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';
|
||||
@@ -87,10 +88,20 @@ const distributeCols = [
|
||||
return row.port ? `${row.worker_ip}:${row.port}` : row.worker_ip;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'models.form.backend',
|
||||
locale: true,
|
||||
key: 'backend'
|
||||
},
|
||||
{
|
||||
title: 'models.table.gpuindex',
|
||||
locale: true,
|
||||
key: 'gpu_index'
|
||||
},
|
||||
{
|
||||
title: 'resources.table.memory',
|
||||
locale: true,
|
||||
key: 'ram'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -109,6 +120,40 @@ const renderMessage = (title: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
const InfoItem = (props: { label: string; value: any; width?: number }) => {
|
||||
const { label, value, width } = props;
|
||||
const Wrapper = styled.div`
|
||||
.info-item {
|
||||
width: ${width ? `${width}px` : '100%'};
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 4px 8px;
|
||||
background-color: var(--color-gray-fill-3);
|
||||
border-radius: 4px;
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
color: var(--color-white-quaternary);
|
||||
.index {
|
||||
color: var(--ant-color-text-light-solid);
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className="info-item">
|
||||
<span className="label">{label}</span>
|
||||
<span className="value">{value}</span>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
instanceData,
|
||||
workerList,
|
||||
@@ -160,6 +205,23 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
[]
|
||||
);
|
||||
|
||||
const displayGPUs = (vrams: Record<string, number>) => {
|
||||
return (
|
||||
<span className="flex-column">
|
||||
{Object.keys(vrams)
|
||||
?.sort?.()
|
||||
.map((index) => {
|
||||
return (
|
||||
<span key={index}>
|
||||
[{index}] {''}
|
||||
{convertFileSize(vrams?.[index], 0)}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const renderWorkerInfo = useMemo(() => {
|
||||
let workerIp = '-';
|
||||
if (instanceData.worker_ip) {
|
||||
@@ -167,23 +229,50 @@ 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>{instanceData.worker_name}</div>
|
||||
<div className="flex-center">
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<HddFilled className="m-r-5" />
|
||||
{workerIp}
|
||||
{instanceData.worker_name}
|
||||
</div>
|
||||
<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 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>
|
||||
<div className="flex-center">
|
||||
<ThunderboltFilled className="m-r-5" />
|
||||
{intl.formatMessage({ id: 'models.form.backend' })}:{' '}
|
||||
{modelData?.backend || ''}
|
||||
{modelData.backend_version ? `(${modelData.backend_version})` : ''}
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
@@ -197,24 +286,23 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
worker_name: data?.name,
|
||||
worker_ip: data?.ip,
|
||||
port: '',
|
||||
gpu_index: item.gpu_index
|
||||
ram: convertFileSize(item.computed_resource_claim?.ram, 0),
|
||||
gpu_index: displayGPUs(item.computed_resource_claim?.vram || {})
|
||||
};
|
||||
});
|
||||
|
||||
const mainWorker = [
|
||||
{
|
||||
worker_name: `${instanceData.worker_name}`,
|
||||
worker_name: `${instanceData.worker_name} (main)`,
|
||||
worker_ip: `${instanceData.worker_ip}`,
|
||||
port: '',
|
||||
gpu_index: `${instanceData.gpu_indexes?.sort?.()} (main)`
|
||||
ram: convertFileSize(instanceData.computed_resource_claim?.ram, 0),
|
||||
gpu_index: displayGPUs(instanceData.computed_resource_claim?.vram || {})
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 style={{ margin: 0 }}>
|
||||
{intl.formatMessage({ id: 'models.table.backend' })}
|
||||
</h3>
|
||||
<SimpleTabel
|
||||
columns={distributeCols}
|
||||
dataSource={[...mainWorker, ...list]}
|
||||
@@ -255,7 +343,13 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
<AutoTooltip title={instanceData.name} ghost>
|
||||
<span className="m-r-5">{instanceData.name}</span>
|
||||
</AutoTooltip>
|
||||
<Tooltip title={renderWorkerInfo}>
|
||||
<Tooltip
|
||||
title={renderWorkerInfo}
|
||||
overlayInnerStyle={{
|
||||
width: 'max-content',
|
||||
maxWidth: '400px'
|
||||
}}
|
||||
>
|
||||
<span className="server-info">
|
||||
<InfoCircleOutlined />
|
||||
</span>
|
||||
@@ -327,7 +421,9 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
{instanceData?.distributed_servers?.rpc_servers?.length && (
|
||||
<Tooltip
|
||||
overlayInnerStyle={{
|
||||
width: '400px'
|
||||
width: 'max-content',
|
||||
maxWidth: '500px',
|
||||
minWidth: '400px'
|
||||
}}
|
||||
title={renderDistributionInfo}
|
||||
>
|
||||
|
||||
@@ -60,6 +60,12 @@ export interface FormData {
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface ComputedResourceClaim {
|
||||
offload_layers: number;
|
||||
total_layers: number;
|
||||
ram: number;
|
||||
vram: Record<string, number>;
|
||||
}
|
||||
export interface ModelInstanceListItem {
|
||||
backend?: string;
|
||||
backend_version?: string;
|
||||
@@ -68,12 +74,14 @@ export interface ModelInstanceListItem {
|
||||
huggingface_filename: string;
|
||||
ollama_library_model_name: string;
|
||||
distributed_servers?: {
|
||||
rpc_servers: any[];
|
||||
};
|
||||
computed_resource_claim?: {
|
||||
offload_layers: number;
|
||||
total_layers: number;
|
||||
rpc_servers: {
|
||||
pid: number;
|
||||
port: number;
|
||||
worker_id: string;
|
||||
computed_resource_claim: ComputedResourceClaim;
|
||||
}[];
|
||||
};
|
||||
computed_resource_claim?: ComputedResourceClaim;
|
||||
s3_address: string;
|
||||
worker_id: number;
|
||||
gpu_indexes?: number[];
|
||||
|
||||
@@ -239,23 +239,23 @@ export const ImageEidtParamsConfig: ParamsSchema[] = [
|
||||
];
|
||||
|
||||
export const ImageconstExtraConfig: ParamsSchema[] = [
|
||||
{
|
||||
type: 'Select',
|
||||
name: 'quality',
|
||||
options: [
|
||||
{ label: 'playground.params.standard', value: 'standard', locale: true },
|
||||
{ label: 'playground.params.hd', value: 'hd', locale: true }
|
||||
],
|
||||
label: {
|
||||
text: 'playground.params.quality',
|
||||
isLocalized: true
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: false
|
||||
}
|
||||
]
|
||||
},
|
||||
// {
|
||||
// type: 'Select',
|
||||
// name: 'quality',
|
||||
// options: [
|
||||
// { label: 'playground.params.standard', value: 'standard', locale: true },
|
||||
// { label: 'playground.params.hd', value: 'hd', locale: true }
|
||||
// ],
|
||||
// label: {
|
||||
// text: 'playground.params.quality',
|
||||
// isLocalized: true
|
||||
// },
|
||||
// rules: [
|
||||
// {
|
||||
// required: false
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
type: 'Select',
|
||||
name: 'style',
|
||||
|
||||
@@ -24,7 +24,8 @@ export const IMG_METAKEYS = [
|
||||
'cfg_scale',
|
||||
'guidance',
|
||||
'negative_prompt',
|
||||
'seed'
|
||||
'seed',
|
||||
'strength'
|
||||
];
|
||||
|
||||
export const llmInitialValues = {
|
||||
@@ -44,12 +45,13 @@ export const advancedFieldsDefaultValus = {
|
||||
guidance: 3.5,
|
||||
sampling_steps: 10,
|
||||
negative_prompt: null,
|
||||
strength: null,
|
||||
schedule_method: 'discrete',
|
||||
preview: 'preview_faster'
|
||||
};
|
||||
|
||||
export const openaiCompatibleFieldsDefaultValus = {
|
||||
quality: 'standard',
|
||||
// quality: 'standard',
|
||||
style: null
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import StatusTag from '@/components/status-tag';
|
||||
import Hotkeys from '@/config/hotkeys';
|
||||
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||
import useTableSort from '@/hooks/use-table-sort';
|
||||
import { handleBatchRequest } from '@/utils';
|
||||
import { convertFileSize, handleBatchRequest } from '@/utils';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
@@ -41,15 +41,27 @@ const { Column } = Table;
|
||||
const fieldList = [
|
||||
{
|
||||
label: 'resources.table.total',
|
||||
key: 'total'
|
||||
key: 'total',
|
||||
locale: true,
|
||||
render: (val: any) => {
|
||||
return convertFileSize(val, 0);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'resources.table.used',
|
||||
key: 'used'
|
||||
key: 'used',
|
||||
locale: true,
|
||||
render: (val: any) => {
|
||||
return convertFileSize(val, 0);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'resources.table.allocated',
|
||||
key: 'allocated'
|
||||
key: 'allocated',
|
||||
locale: true,
|
||||
render: (val: any) => {
|
||||
return convertFileSize(val, 0);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user