feat(admin): 默认过滤实时负载禁用项
实时负载默认仅展示平台和模型均启用的来源,并增加全部状态、仅看禁用筛选及重置行为测试。
This commit is contained in:
@@ -37,6 +37,7 @@ describe('filterAndSortRealtimeLoad', () => {
|
|||||||
const result = filterAndSortRealtimeLoad(statuses, platforms, {
|
const result = filterAndSortRealtimeLoad(statuses, platforms, {
|
||||||
query: '平台',
|
query: '平台',
|
||||||
platformId: '',
|
platformId: '',
|
||||||
|
availability: 'enabled',
|
||||||
modelType: '',
|
modelType: '',
|
||||||
runtimeState: '',
|
runtimeState: '',
|
||||||
sortField: 'load',
|
sortField: 'load',
|
||||||
@@ -50,6 +51,7 @@ describe('filterAndSortRealtimeLoad', () => {
|
|||||||
const queued = filterAndSortRealtimeLoad(statuses, platforms, {
|
const queued = filterAndSortRealtimeLoad(statuses, platforms, {
|
||||||
query: '',
|
query: '',
|
||||||
platformId: '',
|
platformId: '',
|
||||||
|
availability: 'enabled',
|
||||||
modelType: 'image_generate',
|
modelType: 'image_generate',
|
||||||
runtimeState: 'queued',
|
runtimeState: 'queued',
|
||||||
sortField: 'queued',
|
sortField: 'queued',
|
||||||
@@ -59,6 +61,7 @@ describe('filterAndSortRealtimeLoad', () => {
|
|||||||
const attention = filterAndSortRealtimeLoad(statuses, platforms, {
|
const attention = filterAndSortRealtimeLoad(statuses, platforms, {
|
||||||
query: '',
|
query: '',
|
||||||
platformId: '',
|
platformId: '',
|
||||||
|
availability: 'all',
|
||||||
modelType: '',
|
modelType: '',
|
||||||
runtimeState: 'attention',
|
runtimeState: 'attention',
|
||||||
sortField: 'load',
|
sortField: 'load',
|
||||||
@@ -69,4 +72,19 @@ describe('filterAndSortRealtimeLoad', () => {
|
|||||||
expect(attention.map((item) => item.platformModelId)).toEqual(['model-b']);
|
expect(attention.map((item) => item.platformModelId)).toEqual(['model-b']);
|
||||||
expect(statuses.map((item) => item.platformModelId)).toEqual(['model-a', 'model-b']);
|
expect(statuses.map((item) => item.platformModelId)).toEqual(['model-a', 'model-b']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('hides disabled entries by default and supports all or disabled-only filters', () => {
|
||||||
|
const base = {
|
||||||
|
query: '',
|
||||||
|
platformId: '',
|
||||||
|
modelType: '',
|
||||||
|
runtimeState: '',
|
||||||
|
sortField: 'load' as const,
|
||||||
|
sortDirection: 'desc' as const,
|
||||||
|
now: Date.now(),
|
||||||
|
};
|
||||||
|
expect(filterAndSortRealtimeLoad(statuses, platforms, { ...base, availability: 'enabled' }).map((item) => item.platformModelId)).toEqual(['model-a']);
|
||||||
|
expect(filterAndSortRealtimeLoad(statuses, platforms, { ...base, availability: 'all' }).map((item) => item.platformModelId)).toEqual(['model-b', 'model-a']);
|
||||||
|
expect(filterAndSortRealtimeLoad(statuses, platforms, { ...base, availability: 'disabled' }).map((item) => item.platformModelId)).toEqual(['model-b']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export function RealtimeLoadPanel(props: {
|
|||||||
const [restoreSavingId, setRestoreSavingId] = useState<string | null>(null);
|
const [restoreSavingId, setRestoreSavingId] = useState<string | null>(null);
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [platformId, setPlatformId] = useState('');
|
const [platformId, setPlatformId] = useState('');
|
||||||
|
const [availability, setAvailability] = useState<RealtimeLoadAvailability>('enabled');
|
||||||
const [modelType, setModelType] = useState('');
|
const [modelType, setModelType] = useState('');
|
||||||
const [runtimeState, setRuntimeState] = useState('');
|
const [runtimeState, setRuntimeState] = useState('');
|
||||||
const [sortField, setSortField] = useState<RealtimeLoadSortField>('load');
|
const [sortField, setSortField] = useState<RealtimeLoadSortField>('load');
|
||||||
@@ -33,21 +34,22 @@ export function RealtimeLoadPanel(props: {
|
|||||||
() => filterAndSortRealtimeLoad(props.modelRateLimits, platformMap, {
|
() => filterAndSortRealtimeLoad(props.modelRateLimits, platformMap, {
|
||||||
query,
|
query,
|
||||||
platformId,
|
platformId,
|
||||||
|
availability,
|
||||||
modelType,
|
modelType,
|
||||||
runtimeState,
|
runtimeState,
|
||||||
sortField,
|
sortField,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
now,
|
now,
|
||||||
}),
|
}),
|
||||||
[modelType, now, platformId, platformMap, props.modelRateLimits, query, runtimeState, sortDirection, sortField],
|
[availability, modelType, now, platformId, platformMap, props.modelRateLimits, query, runtimeState, sortDirection, sortField],
|
||||||
);
|
);
|
||||||
const hasActiveFilters = Boolean(query || platformId || modelType || runtimeState);
|
const hasActiveFilters = Boolean(query || platformId || modelType || runtimeState || availability !== 'all');
|
||||||
const advancedFilterCount = [
|
const advancedFilterCount = [
|
||||||
modelType,
|
modelType,
|
||||||
runtimeState,
|
runtimeState,
|
||||||
sortDirection === 'desc' ? '' : sortDirection,
|
sortDirection === 'desc' ? '' : sortDirection,
|
||||||
].filter(Boolean).length;
|
].filter(Boolean).length;
|
||||||
const hasActiveSettings = Boolean(query || platformId || advancedFilterCount || sortField !== 'load');
|
const hasActiveSettings = Boolean(query || platformId || availability !== 'enabled' || advancedFilterCount || sortField !== 'load');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = window.setInterval(() => setNow(Date.now()), 1000);
|
const timer = window.setInterval(() => setNow(Date.now()), 1000);
|
||||||
@@ -112,6 +114,7 @@ export function RealtimeLoadPanel(props: {
|
|||||||
function resetFilters() {
|
function resetFilters() {
|
||||||
setQuery('');
|
setQuery('');
|
||||||
setPlatformId('');
|
setPlatformId('');
|
||||||
|
setAvailability('enabled');
|
||||||
setModelType('');
|
setModelType('');
|
||||||
setRuntimeState('');
|
setRuntimeState('');
|
||||||
setSortField('load');
|
setSortField('load');
|
||||||
@@ -153,6 +156,15 @@ export function RealtimeLoadPanel(props: {
|
|||||||
<option value="rpm">RPM</option>
|
<option value="rpm">RPM</option>
|
||||||
<option value="priority">平台优先级</option>
|
<option value="priority">平台优先级</option>
|
||||||
</Select>
|
</Select>
|
||||||
|
<Select
|
||||||
|
aria-label="启用状态筛选"
|
||||||
|
value={availability}
|
||||||
|
onChange={(event) => setAvailability(event.target.value as RealtimeLoadAvailability)}
|
||||||
|
>
|
||||||
|
<option value="enabled">仅看启用</option>
|
||||||
|
<option value="all">全部状态</option>
|
||||||
|
<option value="disabled">仅看禁用</option>
|
||||||
|
</Select>
|
||||||
<div className="compactAdminToolbarActions">
|
<div className="compactAdminToolbarActions">
|
||||||
<Button type="button" variant="outline" size="sm" onClick={() => setFilterDialogOpen(true)}>
|
<Button type="button" variant="outline" size="sm" onClick={() => setFilterDialogOpen(true)}>
|
||||||
<SlidersHorizontal size={14} />
|
<SlidersHorizontal size={14} />
|
||||||
@@ -422,6 +434,7 @@ export function filterAndSortRealtimeLoad(
|
|||||||
options: {
|
options: {
|
||||||
query: string;
|
query: string;
|
||||||
platformId: string;
|
platformId: string;
|
||||||
|
availability: RealtimeLoadAvailability;
|
||||||
modelType: string;
|
modelType: string;
|
||||||
runtimeState: string;
|
runtimeState: string;
|
||||||
sortField: RealtimeLoadSortField;
|
sortField: RealtimeLoadSortField;
|
||||||
@@ -447,6 +460,8 @@ export function filterAndSortRealtimeLoad(
|
|||||||
const cooling = cooldownRemainingMs(status.modelCooldownUntil, options.now) > 0 ||
|
const cooling = cooldownRemainingMs(status.modelCooldownUntil, options.now) > 0 ||
|
||||||
cooldownRemainingMs(status.platformCooldownUntil, options.now) > 0;
|
cooldownRemainingMs(status.platformCooldownUntil, options.now) > 0;
|
||||||
const disabled = !status.enabled || status.platformStatus !== 'enabled';
|
const disabled = !status.enabled || status.platformStatus !== 'enabled';
|
||||||
|
const matchesAvailability = options.availability === 'all' ||
|
||||||
|
(options.availability === 'disabled' ? disabled : !disabled);
|
||||||
const queuedTasks = admissionQueueDepth(status);
|
const queuedTasks = admissionQueueDepth(status);
|
||||||
const matchesRuntime = !options.runtimeState ||
|
const matchesRuntime = !options.runtimeState ||
|
||||||
(options.runtimeState === 'high' && status.loadRatio >= 0.8) ||
|
(options.runtimeState === 'high' && status.loadRatio >= 0.8) ||
|
||||||
@@ -456,6 +471,7 @@ export function filterAndSortRealtimeLoad(
|
|||||||
return (!keyword || text.includes(keyword)) &&
|
return (!keyword || text.includes(keyword)) &&
|
||||||
(!options.platformId || status.platformId === options.platformId) &&
|
(!options.platformId || status.platformId === options.platformId) &&
|
||||||
(!options.modelType || status.modelType.includes(options.modelType)) &&
|
(!options.modelType || status.modelType.includes(options.modelType)) &&
|
||||||
|
matchesAvailability &&
|
||||||
matchesRuntime;
|
matchesRuntime;
|
||||||
});
|
});
|
||||||
const direction = options.sortDirection === 'asc' ? 1 : -1;
|
const direction = options.sortDirection === 'asc' ? 1 : -1;
|
||||||
@@ -466,6 +482,8 @@ export function filterAndSortRealtimeLoad(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RealtimeLoadAvailability = 'enabled' | 'disabled' | 'all';
|
||||||
|
|
||||||
function realtimeLoadSortValue(status: ModelRateLimitStatus, field: RealtimeLoadSortField) {
|
function realtimeLoadSortValue(status: ModelRateLimitStatus, field: RealtimeLoadSortField) {
|
||||||
if (field === 'queued') return admissionQueueDepth(status);
|
if (field === 'queued') return admissionQueueDepth(status);
|
||||||
if (field === 'concurrent') return status.concurrent.currentValue;
|
if (field === 'concurrent') return status.concurrent.currentValue;
|
||||||
|
|||||||
Reference in New Issue
Block a user