feat: models filter by state

This commit is contained in:
jialin
2025-12-21 21:37:05 +08:00
parent 42f8e6a6d9
commit db45c861e4
9 changed files with 132 additions and 33 deletions
+2 -2
View File
@@ -216,8 +216,8 @@ export default {
'models.form.gpusAllocationType.custom.tips':
'You can specify the exact number of GPUs per replica.',
'models.mymodels.status.inactive': 'Stopped',
'models.mymodels.status.degrade': 'Abnormal',
'models.mymodels.status.active': 'Active',
'models.mymodels.status.degrade': 'Not Ready',
'models.mymodels.status.active': 'Ready',
'models.form.kvCache.tips':
'Extended KV cache and speculative decoding are only available with built-in backends (vLLM / SGLang), Please switch the backend in the <span class="bold-text">Advanced</span> settings to enable them.',
'models.form.kvCache.tips2':
+4 -4
View File
@@ -216,8 +216,8 @@ export default {
'models.form.gpusAllocationType.custom.tips':
'You can specify the exact number of GPUs per replica.',
'models.mymodels.status.inactive': 'Stopped',
'models.mymodels.status.degrade': 'Abnormal',
'models.mymodels.status.active': 'Active',
'models.mymodels.status.degrade': 'Not Ready',
'models.mymodels.status.active': 'Ready',
'models.form.kvCache.tips':
'Extended KV cache and speculative decoding are only available with built-in backends (vLLM / SGLang), Please switch the backend in the <span class="bold-text">Advanced</span> settings to enable them.',
'models.form.kvCache.tips2':
@@ -313,8 +313,8 @@ export default {
// 37. 'models.form.gpusAllocationType.auto.tips': 'The system automatically calculates the GPU count per replica, using powers of two by default and capped by the selected GPUs.',
// 38. 'models.form.gpusAllocationType.custom.tips': 'You can specify the exact number of GPUs per replica.',
// 39. 'models.mymodels.status.inactive': 'Stopped',
// 41. 'models.mymodels.status.degrade': 'Abnormal',
// 42. 'models.mymodels.status.active': 'Active'
// 41. 'models.mymodels.status.degrade': 'Not Ready',
// 42. 'models.mymodels.status.active': 'Ready',
// 43. 'models.form.remoteURL.tips': 'Refer to the <a href="https://docs.lmcache.ai/api_reference/configurations.html" target="_blank">configuration documentation</a> for details.',
// 44. 'models.form.kvCache.tips': 'Extended KV cache and speculative decoding are only available with built-in backends (vLLM / SGLang), Please switch the backend in the <span class="bold-text">Advanced</span> settings to enable them.',
// 45. 'models.form.kvCache.tips2': 'Only supported when using built-in inference backends (vLLM or SGLang).',
+2 -2
View File
@@ -220,8 +220,8 @@ export default {
'models.form.gpusAllocationType.custom.tips':
'Вы можете указать точное количество GPU на реплику.',
'models.mymodels.status.inactive': 'Остановлен',
'models.mymodels.status.degrade': 'Аномальный',
'models.mymodels.status.active': 'Активен',
'models.mymodels.status.degrade': 'Не готов',
'models.mymodels.status.active': 'Готов',
'models.form.kvCache.tips':
'Расширенный KV-кэш и спекулятивное декодирование доступны только со встроенными бэкендами (vLLM / SGLang). Переключите бэкенд в <span class="bold-text">Расширенных</span> настройках, чтобы включить их.',
'models.form.kvCache.tips2':
+1 -16
View File
@@ -161,21 +161,6 @@ const ModelItem: React.FC<{
return _.round(max_tokens / 1024);
}, [model]);
const status = useMemo(() => {
if (!model.replicas && !model.ready_replicas) {
return MyModelsStatusValueMap.Inactive;
}
if (model.replicas > 0 && !model.ready_replicas) {
return MyModelsStatusValueMap.Degrade;
}
if (model.replicas === model.ready_replicas && model.replicas > 0) {
return MyModelsStatusValueMap.Active;
}
return MyModelsStatusValueMap.Degrade;
}, [model]);
return (
<CardWrapper>
<Card
@@ -198,7 +183,7 @@ const ModelItem: React.FC<{
statusValue={{
status: MyModelsStatusMap[model.status],
text: intl.formatMessage({
id: MyModelsStatusLabelMap[model.status]
id: MyModelsStatusLabelMap[model.status] || ''
}),
message: model.state_message
}}
@@ -56,6 +56,7 @@ import {
ModelInstanceListItem,
SourceType
} from '../config/types';
import useFilterStatus from '../hooks/use-filter-status';
import useFormInitialValues from '../hooks/use-form-initial-values';
import useModelsColumns from '../hooks/use-models-columns';
import AccessControlModal from './access-control-modal';
@@ -78,6 +79,7 @@ interface ModelsProps {
onStop?: (ids: number[]) => void;
onStart?: () => void;
onTableSort?: (order: TableOrder | Array<TableOrder>) => void;
onStatusChange: (value?: any) => void;
sortOrder: string[];
queryParams: {
page: number;
@@ -119,6 +121,7 @@ const Models: React.FC<ModelsProps> = ({
onStop,
onStart,
onTableSort,
onStatusChange,
sortOrder,
deleteIds,
dataSource,
@@ -154,6 +157,10 @@ const Models: React.FC<ModelsProps> = ({
removeExpandedRowKey,
expandedRowKeys
} = useExpandedRowKeys(expandAtom);
const { labelRender, optionRender, handleStatusChange, statusOptions } =
useFilterStatus({
onStatusChange: onStatusChange
});
const [apiAccessInfo, setAPIAccessInfo] = useState<any>({
show: false,
@@ -663,6 +670,18 @@ const Models: React.FC<ModelsProps> = ({
onChange={handleClusterChange}
options={clusterList}
></BaseSelect>
<BaseSelect
allowClear
showSearch={false}
placeholder={intl.formatMessage({ id: 'common.filter.status' })}
style={{ width: 180 }}
size="large"
maxTagCount={1}
optionRender={optionRender}
labelRender={labelRender}
options={statusOptions}
onChange={handleStatusChange}
></BaseSelect>
<Button
type="text"
style={{ color: 'var(--ant-color-text-tertiary)' }}
+3 -3
View File
@@ -196,9 +196,9 @@ export const status: any = {
};
export const MyModelsStatusValueMap = {
Inactive: 'Inactive',
Degrade: 'Degrade',
Active: 'Active'
Inactive: 'stopped',
Degrade: 'not_ready',
Active: 'ready'
};
export const MyModelsStatusMap = {
@@ -0,0 +1,79 @@
import { useIntl } from '@umijs/max';
import { MyModelsStatusValueMap } from '../config';
const Dot = ({ color }: { color: string }) => {
return (
<span
style={{
backgroundColor: color,
borderRadius: '50%',
height: 8,
width: 8,
display: 'flex'
}}
></span>
);
};
const useFilterStatus = (options: {
onStatusChange: (value?: any) => void;
}) => {
const { onStatusChange } = options;
const intl = useIntl();
const statusOptions = [
{
value: MyModelsStatusValueMap.Active,
color: 'var(--ant-color-success)',
label: intl.formatMessage({
id: 'models.mymodels.status.active'
})
},
{
value: MyModelsStatusValueMap.Inactive,
color: 'var(--ant-color-fill)',
label: intl.formatMessage({
id: 'models.mymodels.status.inactive'
})
},
{
value: MyModelsStatusValueMap.Degrade,
color: 'var(--ant-color-error)',
label: intl.formatMessage({
id: 'models.mymodels.status.degrade'
})
}
];
const labelRender = (item: any) => {
const current = statusOptions.find((option) => option.value === item.value);
return (
<span className="flex-center gap-8">
<Dot color={current!.color}></Dot>
{item.label}
</span>
);
};
const optionRender = (item: any) => {
return (
<span className="flex-center gap-8">
<Dot color={item.data?.color}></Dot>
{item.label}
</span>
);
};
const handleStatusChange = (value: string | undefined) => {
onStatusChange(value);
};
return {
statusOptions,
labelRender,
optionRender,
handleStatusChange
};
};
export default useFilterStatus;
+16
View File
@@ -47,6 +47,7 @@ const Models: React.FC = () => {
search: '',
cluster_id: 0,
categories: [],
state: '',
sort_by: '-created_at'
});
@@ -197,6 +198,7 @@ const Models: React.FC = () => {
search: string;
categories: any[];
cluster_id: number;
state?: string;
}) => {
const search = params?.search || queryParams.search;
const categories = params?.categories || queryParams.categories;
@@ -287,6 +289,19 @@ const Models: React.FC = () => {
});
};
const handleOnStatusChange = async (value: string | undefined) => {
handleQueryChange({
page: 1,
state: value
});
createModelsChunkRequest({
search: queryParams.search,
categories: queryParams.categories,
cluster_id: queryParams.cluster_id || 0,
state: value
});
};
const handleClusterChange = async (value: number) => {
handleQueryChange({
page: 1,
@@ -408,6 +423,7 @@ const Models: React.FC = () => {
>
<TableList
dataSource={dataSource.dataList}
onStatusChange={handleOnStatusChange}
handleNameChange={handleNameChange}
handleCategoryChange={handleCategoryChange}
handleClusterChange={handleClusterChange}
+6 -6
View File
@@ -62,7 +62,6 @@ const UserModels: React.FC = () => {
}
});
const intl = useIntl();
const [status, setStatus] = React.useState<string | undefined>(undefined);
const statusOptions = useMemo(() => {
return [
@@ -131,7 +130,9 @@ const UserModels: React.FC = () => {
});
const handleStatusChange = (value: string) => {
setStatus(value);
handleQueryChange({
state: value
});
};
const labelRender = (item: any) => {
@@ -166,9 +167,8 @@ const UserModels: React.FC = () => {
status: getStatus(item)
};
});
if (!status) return result;
return result.filter((item) => item.status === status);
}, [dataSource.dataList, status]);
return result;
}, [dataSource.dataList]);
return (
<PageBox>
@@ -242,7 +242,7 @@ const UserModels: React.FC = () => {
loadend={dataSource.loadend}
dataSource={dataList}
image={<IconFont type="icon-models" />}
filters={{ ...queryParams, status: status }}
filters={{ ...queryParams }}
noFoundText={intl.formatMessage({
id: 'noresult.mymodels.nofound'
})}