refactor(pool): 移除调度多维评分详情,简化为 reasons 摘要

移除 scheduling_score、candidate_eligible、scheduling_dimensions 等字段,
前后端统一使用 scheduling_reasons 展示调度状态,精简号池列表信息密度。
This commit is contained in:
fawney19
2026-03-04 22:51:02 +08:00
parent b2dcf82ca8
commit 1d04c41ae7
7 changed files with 5 additions and 407 deletions
-17
View File
@@ -142,11 +142,6 @@ export interface PoolKeyDetail {
| string | string
scheduling_label?: string scheduling_label?: string
scheduling_reasons?: PoolSchedulingReason[] scheduling_reasons?: PoolSchedulingReason[]
scheduling_score?: number
candidate_eligible?: boolean
scheduling_blocked_count?: number
scheduling_degraded_count?: number
scheduling_dimensions?: PoolSchedulingDimension[]
} }
export interface PoolSchedulingReason { export interface PoolSchedulingReason {
@@ -158,18 +153,6 @@ export interface PoolSchedulingReason {
detail?: string | null detail?: string | null
} }
export interface PoolSchedulingDimension {
code: string
label: string
status: 'ok' | 'degraded' | 'blocked' | string
blocking: boolean
source: 'manual' | 'pool' | 'health' | 'policy' | string
weight: number
score: number
ttl_seconds?: number | null
detail?: string | null
}
export interface PoolKeysPageResponse { export interface PoolKeysPageResponse {
total: number total: number
page: number page: number
+1 -289
View File
@@ -291,9 +291,6 @@
> >
配额 配额
</TableHead> </TableHead>
<TableHead class="w-[180px] font-semibold whitespace-nowrap">
调度
</TableHead>
<TableHead class="w-24 font-semibold whitespace-nowrap"> <TableHead class="w-24 font-semibold whitespace-nowrap">
状态 状态
</TableHead> </TableHead>
@@ -439,95 +436,6 @@
class="text-xs text-muted-foreground" class="text-xs text-muted-foreground"
>-</span> >-</span>
</TableCell> </TableCell>
<TableCell class="py-3">
<div
class="grid grid-rows-[16px_16px] gap-1 w-20"
:title="getSchedulingDimensionTitle(key)"
>
<div class="h-4 flex items-center justify-between gap-1">
<span
class="text-[11px] font-normal leading-none tabular-nums"
:class="isCandidateEligible(key) ? 'text-foreground' : 'text-destructive'"
>
{{ getSchedulingScore(key).toFixed(1) }}
</span>
<Popover
:open="schedulingDetailDesktopPopoverOpenKeyId === key.key_id"
@update:open="(v: boolean) => handleSchedulingDetailDesktopPopoverToggle(key.key_id, v)"
>
<PopoverTrigger as-child>
<button
type="button"
class="inline-flex h-4 w-4 items-center justify-center text-muted-foreground hover:text-foreground"
title="查看计算详情"
@click.stop
>
<CircleHelp class="w-3 h-3" />
</button>
</PopoverTrigger>
<PopoverContent
class="w-72 p-3"
side="bottom"
align="start"
>
<div class="space-y-2">
<div class="text-xs font-medium">
调度分计算详情
</div>
<div class="max-h-56 overflow-y-auto space-y-2 pr-1">
<div
v-for="item in getSchedulingRuleEntries(key)"
:key="`${key.key_id}-score-detail-${item.code}`"
class="rounded-md border border-border/60 p-2"
>
<div class="flex items-center justify-between gap-2">
<span class="text-[11px] font-medium">
{{ item.label }}
</span>
<Badge
:variant="getSchedulingDimensionStatusVariant(item.status)"
class="text-[10px]"
>
{{ getSchedulingDimensionStatusLabel(item.status) }}
</Badge>
</div>
<div
v-if="item.weight != null || item.score != null"
class="text-[10px] text-muted-foreground mt-1"
>
<span v-if="item.weight != null">权重 {{ item.weight }}</span>
<span v-if="item.weight != null && item.score != null"> · </span>
<span v-if="item.score != null">分值 {{ Math.round(item.score * 100) }}</span>
</div>
<div
v-if="item.detail"
class="text-[10px] text-muted-foreground mt-1 break-all"
>
{{ item.detail }}
</div>
<div
v-if="item.ttl_seconds && item.ttl_seconds > 0"
class="text-[10px] text-muted-foreground mt-1"
>
剩余 {{ formatTTL(item.ttl_seconds) }}
</div>
</div>
</div>
</div>
</PopoverContent>
</Popover>
</div>
<div class="h-4 flex items-center">
<div class="w-full h-1.5 bg-border rounded-full overflow-hidden">
<div
class="h-full transition-all duration-300"
:class="getSchedulingScoreBarColor(getSchedulingScore(key))"
:style="{ width: `${Math.max(Math.min(getSchedulingScore(key), 100), 0)}%` }"
/>
</div>
</div>
</div>
</TableCell>
<TableCell class="py-3"> <TableCell class="py-3">
<Badge <Badge
:variant="getSchedulingBadgeVariant(key)" :variant="getSchedulingBadgeVariant(key)"
@@ -896,94 +804,8 @@
</div> </div>
<div <div
class="mt-2.5 grid gap-2" class="mt-2.5 grid gap-2"
:class="showAccountQuotaColumn ? 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5' : 'grid-cols-2 sm:grid-cols-4'" :class="showAccountQuotaColumn ? 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4' : 'grid-cols-2 sm:grid-cols-3'"
> >
<div class="p-2 bg-muted/50 rounded-lg text-xs">
<div class="text-muted-foreground mb-0.5">
调度
</div>
<div class="w-24 flex items-center justify-between gap-1">
<div
class="font-normal tabular-nums text-[10px] leading-none"
:class="isCandidateEligible(key) ? 'text-foreground' : 'text-destructive'"
:title="getSchedulingDimensionTitle(key)"
>
{{ getSchedulingScore(key).toFixed(1) }}
</div>
<Popover
:open="schedulingDetailMobilePopoverOpenKeyId === key.key_id"
@update:open="(v: boolean) => handleSchedulingDetailMobilePopoverToggle(key.key_id, v)"
>
<PopoverTrigger as-child>
<button
type="button"
class="inline-flex h-4 w-4 items-center justify-center text-muted-foreground hover:text-foreground"
title="查看计算详情"
@click.stop
>
<CircleHelp class="w-3 h-3" />
</button>
</PopoverTrigger>
<PopoverContent
class="w-72 p-3"
side="bottom"
align="start"
>
<div class="space-y-2">
<div class="text-xs font-medium">
调度分计算详情
</div>
<div class="max-h-56 overflow-y-auto space-y-2 pr-1">
<div
v-for="item in getSchedulingRuleEntries(key)"
:key="`${key.key_id}-mobile-score-detail-${item.code}`"
class="rounded-md border border-border/60 p-2"
>
<div class="flex items-center justify-between gap-2">
<span class="text-[11px] font-medium">
{{ item.label }}
</span>
<Badge
:variant="getSchedulingDimensionStatusVariant(item.status)"
class="text-[10px]"
>
{{ getSchedulingDimensionStatusLabel(item.status) }}
</Badge>
</div>
<div
v-if="item.weight != null || item.score != null"
class="text-[10px] text-muted-foreground mt-1"
>
<span v-if="item.weight != null">权重 {{ item.weight }}</span>
<span v-if="item.weight != null && item.score != null"> · </span>
<span v-if="item.score != null">分值 {{ Math.round(item.score * 100) }}</span>
</div>
<div
v-if="item.detail"
class="text-[10px] text-muted-foreground mt-1 break-all"
>
{{ item.detail }}
</div>
<div
v-if="item.ttl_seconds && item.ttl_seconds > 0"
class="text-[10px] text-muted-foreground mt-1"
>
剩余 {{ formatTTL(item.ttl_seconds) }}
</div>
</div>
</div>
</div>
</PopoverContent>
</Popover>
</div>
<div class="w-24 h-1.5 bg-border rounded-full overflow-hidden mt-1">
<div
class="h-full transition-all duration-300"
:class="getSchedulingScoreBarColor(getSchedulingScore(key))"
:style="{ width: `${Math.max(Math.min(getSchedulingScore(key), 100), 0)}%` }"
/>
</div>
</div>
<div class="p-2 bg-muted/50 rounded-lg text-xs"> <div class="p-2 bg-muted/50 rounded-lg text-xs">
<div class="text-muted-foreground mb-0.5"> <div class="text-muted-foreground mb-0.5">
最后使用 最后使用
@@ -1162,7 +984,6 @@ import {
Globe, Globe,
SquarePen, SquarePen,
Trash2, Trash2,
CircleHelp,
Ban, Ban,
} from 'lucide-vue-next' } from 'lucide-vue-next'
@@ -1392,8 +1213,6 @@ async function selectProvider(id: string) {
oauthKeyEditDialogOpen.value = false oauthKeyEditDialogOpen.value = false
proxyDesktopPopoverOpenKeyId.value = null proxyDesktopPopoverOpenKeyId.value = null
proxyMobilePopoverOpenKeyId.value = null proxyMobilePopoverOpenKeyId.value = null
schedulingDetailDesktopPopoverOpenKeyId.value = null
schedulingDetailMobilePopoverOpenKeyId.value = null
suppressFiltersWatch = true suppressFiltersWatch = true
currentPage.value = 1 currentPage.value = 1
searchQuery.value = '' searchQuery.value = ''
@@ -1438,8 +1257,6 @@ const recoveringHealthKeyId = ref<string | null>(null)
const savingProxyKeyId = ref<string | null>(null) const savingProxyKeyId = ref<string | null>(null)
const proxyDesktopPopoverOpenKeyId = ref<string | null>(null) const proxyDesktopPopoverOpenKeyId = ref<string | null>(null)
const proxyMobilePopoverOpenKeyId = ref<string | null>(null) const proxyMobilePopoverOpenKeyId = ref<string | null>(null)
const schedulingDetailDesktopPopoverOpenKeyId = ref<string | null>(null)
const schedulingDetailMobilePopoverOpenKeyId = ref<string | null>(null)
const deletingKeyId = ref<string | null>(null) const deletingKeyId = ref<string | null>(null)
const togglingKeyId = ref<string | null>(null) const togglingKeyId = ref<string | null>(null)
@@ -1753,20 +1570,6 @@ function handleProxyMobilePopoverToggle(keyId: string, open: boolean) {
} }
} }
function handleSchedulingDetailDesktopPopoverToggle(keyId: string, open: boolean) {
schedulingDetailDesktopPopoverOpenKeyId.value = open ? keyId : null
if (open) {
schedulingDetailMobilePopoverOpenKeyId.value = null
}
}
function handleSchedulingDetailMobilePopoverToggle(keyId: string, open: boolean) {
schedulingDetailMobilePopoverOpenKeyId.value = open ? keyId : null
if (open) {
schedulingDetailDesktopPopoverOpenKeyId.value = null
}
}
async function setKeyProxy(key: PoolKeyDetail, nodeId: string) { async function setKeyProxy(key: PoolKeyDetail, nodeId: string) {
savingProxyKeyId.value = key.key_id savingProxyKeyId.value = key.key_id
try { try {
@@ -2050,14 +1853,6 @@ function getSchedulingTitle(key: PoolKeyDetail): string {
const accountAlertTitle = getAccountAlertTitle(key) const accountAlertTitle = getAccountAlertTitle(key)
if (accountAlertTitle) return accountAlertTitle if (accountAlertTitle) return accountAlertTitle
if (key.scheduling_dimensions && key.scheduling_dimensions.length > 0) {
return key.scheduling_dimensions.map((item) => {
const ttl = item.ttl_seconds && item.ttl_seconds > 0 ? ` (${formatTTL(item.ttl_seconds)})` : ''
const detail = item.detail ? ` - ${item.detail}` : ''
return `${item.label}: ${item.status}${ttl}${detail}`
}).join('\n')
}
const reasons = key.scheduling_reasons ?? [] const reasons = key.scheduling_reasons ?? []
if (reasons.length > 0) { if (reasons.length > 0) {
return reasons.map((item) => { return reasons.map((item) => {
@@ -2074,89 +1869,6 @@ function getSchedulingTitle(key: PoolKeyDetail): string {
return getSchedulingBadgeLabel(key) return getSchedulingBadgeLabel(key)
} }
function getSchedulingScore(key: PoolKeyDetail): number {
const raw = key.scheduling_score
if (typeof raw === 'number' && Number.isFinite(raw)) {
return Math.max(Math.min(raw, 100), 0)
}
const status = getSchedulingStatus(key)
if (status === 'blocked') return 35
if (status === 'degraded') return 68
return 100
}
function getSchedulingScoreBarColor(score: number): string {
if (score >= 80) return 'bg-green-500 dark:bg-green-400'
if (score >= 50) return 'bg-yellow-500 dark:bg-yellow-400'
return 'bg-red-500 dark:bg-red-400'
}
function isCandidateEligible(key: PoolKeyDetail): boolean {
if (typeof key.candidate_eligible === 'boolean') return key.candidate_eligible
return getSchedulingStatus(key) !== 'blocked'
}
function getSchedulingDimensionTitle(key: PoolKeyDetail): string {
const dimensions = key.scheduling_dimensions ?? []
if (!dimensions.length) return ''
return dimensions.map((item) => {
const ttl = item.ttl_seconds && item.ttl_seconds > 0 ? ` (${formatTTL(item.ttl_seconds)})` : ''
const detail = item.detail ? ` - ${item.detail}` : ''
return `${item.label}: ${item.status}${ttl}${detail}`
}).join('\n')
}
function getSchedulingRuleEntries(
key: PoolKeyDetail,
): NonNullable<PoolKeyDetail['scheduling_dimensions']> {
const dimensions = key.scheduling_dimensions ?? []
if (dimensions.length > 0) {
return dimensions
}
const reasons = key.scheduling_reasons ?? []
if (reasons.length > 0) {
return reasons.map((reason) => ({
code: reason.code,
label: reason.label,
status: reason.blocking ? 'blocked' : 'degraded',
blocking: reason.blocking,
source: reason.source,
weight: 0,
score: 0,
ttl_seconds: reason.ttl_seconds ?? null,
detail: reason.detail ?? null,
}))
}
const fallbackStatus = getSchedulingStatus(key)
return [
{
code: key.scheduling_reason ?? 'available',
label: getSchedulingBadgeLabel(key),
status: fallbackStatus === 'available' ? 'ok' : fallbackStatus,
blocking: fallbackStatus === 'blocked',
source: 'pool',
weight: 0,
score: 0,
ttl_seconds: null,
detail: null,
},
]
}
function getSchedulingDimensionStatusLabel(status: string): string {
if (status === 'blocked') return '阻塞'
if (status === 'degraded') return '降级'
return '可用'
}
function getSchedulingDimensionStatusVariant(status: string): PoolStatusVariant {
if (status === 'blocked') return 'destructive'
if (status === 'degraded') return 'warning'
return 'default'
}
function formatTTL(seconds: number): string { function formatTTL(seconds: number): string {
if (seconds <= 0) return '' if (seconds <= 0) return ''
const m = Math.floor(seconds / 60) const m = Math.floor(seconds / 60)
+4 -46
View File
@@ -47,7 +47,6 @@ from .schemas import (
PoolKeysPageResponse, PoolKeysPageResponse,
PoolOverviewItem, PoolOverviewItem,
PoolOverviewResponse, PoolOverviewResponse,
PoolSchedulingDimension,
PoolSchedulingReason, PoolSchedulingReason,
PresetDimensionMetaResponse, PresetDimensionMetaResponse,
PresetModeMetaResponse, PresetModeMetaResponse,
@@ -508,17 +507,7 @@ def _build_pool_scheduling_state(
cost_limit: int | None, cost_limit: int | None,
cost_soft_threshold_percent: int, cost_soft_threshold_percent: int,
health_score: float, health_score: float,
) -> tuple[ ) -> tuple[str, str, str, list[PoolSchedulingReason]]:
str,
str,
str,
list[PoolSchedulingReason],
float,
bool,
int,
int,
list[PoolSchedulingDimension],
]:
"""Build unified scheduling state for frontend display.""" """Build unified scheduling state for frontend display."""
snapshot = PoolSchedulingSnapshot( snapshot = PoolSchedulingSnapshot(
is_active=is_active, is_active=is_active,
@@ -537,28 +526,12 @@ def _build_pool_scheduling_state(
dimensions_raw = evaluate_pool_scheduling_dimensions(snapshot) dimensions_raw = evaluate_pool_scheduling_dimensions(snapshot)
summary = summarize_pool_scheduling_dimensions(dimensions_raw) summary = summarize_pool_scheduling_dimensions(dimensions_raw)
scheduling_dimensions: list[PoolSchedulingDimension] = []
scheduling_reasons: list[PoolSchedulingReason] = [] scheduling_reasons: list[PoolSchedulingReason] = []
for item in dimensions_raw: for item in dimensions_raw:
detail = item.detail
if item.code == "cooldown":
detail = _format_cooldown_detail(detail)
model = PoolSchedulingDimension(
code=item.code,
label=item.label,
status=item.status,
blocking=bool(item.blocking or item.status == "blocked"),
source=item.source,
weight=item.weight,
score=item.score,
ttl_seconds=item.ttl_seconds,
detail=detail,
)
scheduling_dimensions.append(model)
if item.status != "ok": if item.status != "ok":
detail = item.detail
if item.code == "cooldown":
detail = _format_cooldown_detail(detail)
scheduling_reasons.append( scheduling_reasons.append(
PoolSchedulingReason( PoolSchedulingReason(
code=item.code, code=item.code,
@@ -575,11 +548,6 @@ def _build_pool_scheduling_state(
summary.reason, summary.reason,
summary.label, summary.label,
scheduling_reasons, scheduling_reasons,
summary.score,
summary.candidate_eligible,
summary.blocked_count,
summary.degraded_count,
scheduling_dimensions,
) )
@@ -902,11 +870,6 @@ class AdminListPoolKeysAdapter(AdminApiAdapter):
scheduling_reason, scheduling_reason,
scheduling_label, scheduling_label,
scheduling_reasons, scheduling_reasons,
scheduling_score,
candidate_eligible,
scheduling_blocked_count,
scheduling_degraded_count,
scheduling_dimensions,
) = _build_pool_scheduling_state( ) = _build_pool_scheduling_state(
is_active=bool(k.is_active), is_active=bool(k.is_active),
account_blocked=account_state.blocked, account_blocked=account_state.blocked,
@@ -1035,11 +998,6 @@ class AdminListPoolKeysAdapter(AdminApiAdapter):
scheduling_reason=scheduling_reason, scheduling_reason=scheduling_reason,
scheduling_label=scheduling_label, scheduling_label=scheduling_label,
scheduling_reasons=scheduling_reasons, scheduling_reasons=scheduling_reasons,
scheduling_score=scheduling_score,
candidate_eligible=candidate_eligible,
scheduling_blocked_count=scheduling_blocked_count,
scheduling_degraded_count=scheduling_degraded_count,
scheduling_dimensions=scheduling_dimensions,
) )
) )
-18
View File
@@ -64,19 +64,6 @@ class PoolSchedulingReason(BaseModel):
detail: str | None = None detail: str | None = None
class PoolSchedulingDimension(BaseModel):
"""Detailed scheduling dimension status."""
code: str
label: str
status: str = "ok" # ok / degraded / blocked
blocking: bool = False
source: str = "pool"
weight: int = 1
score: float = 1.0 # normalized 0~1
ttl_seconds: int | None = None
detail: str | None = None
class PoolKeyDetail(BaseModel): class PoolKeyDetail(BaseModel):
"""Detailed status of a single pool key.""" """Detailed status of a single pool key."""
@@ -124,11 +111,6 @@ class PoolKeyDetail(BaseModel):
scheduling_reason: str = "available" scheduling_reason: str = "available"
scheduling_label: str = "可用" scheduling_label: str = "可用"
scheduling_reasons: list[PoolSchedulingReason] = Field(default_factory=list) scheduling_reasons: list[PoolSchedulingReason] = Field(default_factory=list)
scheduling_score: float = 100.0
candidate_eligible: bool = True
scheduling_blocked_count: int = 0
scheduling_degraded_count: int = 0
scheduling_dimensions: list[PoolSchedulingDimension] = Field(default_factory=list)
model_config = ConfigDict(from_attributes=True) model_config = ConfigDict(from_attributes=True)
@@ -53,7 +53,6 @@ class PoolSchedulingSummary:
status: str # available / degraded / blocked status: str # available / degraded / blocked
reason: str reason: str
label: str label: str
score: float
candidate_eligible: bool candidate_eligible: bool
blocked_count: int blocked_count: int
degraded_count: int degraded_count: int
@@ -408,7 +407,6 @@ def summarize_pool_scheduling_dimensions(
status="available", status="available",
reason="available", reason="available",
label="可用", label="可用",
score=100.0,
candidate_eligible=True, candidate_eligible=True,
blocked_count=0, blocked_count=0,
degraded_count=0, degraded_count=0,
@@ -417,18 +415,12 @@ def summarize_pool_scheduling_dimensions(
blocked = [item for item in dimensions if item.status == "blocked" or item.blocking] blocked = [item for item in dimensions if item.status == "blocked" or item.blocking]
degraded = [item for item in dimensions if item.status == "degraded"] degraded = [item for item in dimensions if item.status == "degraded"]
total_weight = sum(max(item.weight, 1) for item in dimensions)
weighted_score = sum(
max(item.weight, 1) * max(min(item.score, 1.0), 0.0) for item in dimensions
) / max(total_weight, 1)
if blocked: if blocked:
primary = blocked[0] primary = blocked[0]
return PoolSchedulingSummary( return PoolSchedulingSummary(
status="blocked", status="blocked",
reason=primary.code, reason=primary.code,
label=primary.label, label=primary.label,
score=round(weighted_score * 100, 1),
candidate_eligible=False, candidate_eligible=False,
blocked_count=len(blocked), blocked_count=len(blocked),
degraded_count=len(degraded), degraded_count=len(degraded),
@@ -440,7 +432,6 @@ def summarize_pool_scheduling_dimensions(
status="degraded", status="degraded",
reason=primary.code, reason=primary.code,
label=primary.label, label=primary.label,
score=round(weighted_score * 100, 1),
candidate_eligible=True, candidate_eligible=True,
blocked_count=0, blocked_count=0,
degraded_count=len(degraded), degraded_count=len(degraded),
@@ -450,7 +441,6 @@ def summarize_pool_scheduling_dimensions(
status="available", status="available",
reason="available", reason="available",
label="可用", label="可用",
score=round(weighted_score * 100, 1),
candidate_eligible=True, candidate_eligible=True,
blocked_count=0, blocked_count=0,
degraded_count=0, degraded_count=0,
@@ -40,7 +40,6 @@ def test_summary_available_when_all_dimensions_ok() -> None:
assert summary.candidate_eligible is True assert summary.candidate_eligible is True
assert summary.blocked_count == 0 assert summary.blocked_count == 0
assert summary.degraded_count == 0 assert summary.degraded_count == 0
assert summary.score == 100.0
def test_summary_blocked_when_manual_disabled() -> None: def test_summary_blocked_when_manual_disabled() -> None:
@@ -51,7 +50,6 @@ def test_summary_blocked_when_manual_disabled() -> None:
assert summary.reason == "manual_disabled" assert summary.reason == "manual_disabled"
assert summary.candidate_eligible is False assert summary.candidate_eligible is False
assert summary.blocked_count >= 1 assert summary.blocked_count >= 1
assert summary.score < 100.0
def test_summary_blocked_when_account_state_blocked() -> None: def test_summary_blocked_when_account_state_blocked() -> None:
@@ -114,7 +112,6 @@ def test_empty_summary_defaults_to_available() -> None:
summary = summarize_pool_scheduling_dimensions([]) summary = summarize_pool_scheduling_dimensions([])
assert summary.status == "available" assert summary.status == "available"
assert summary.reason == "available" assert summary.reason == "available"
assert summary.score == 100.0
def test_dimension_result_keeps_degraded_health_details() -> None: def test_dimension_result_keeps_degraded_health_details() -> None:
@@ -12,11 +12,6 @@ def test_pool_scheduling_state_manual_disabled_is_blocked() -> None:
reason, reason,
_label, _label,
reasons, reasons,
score,
candidate_eligible,
blocked_count,
_degraded_count,
dimensions,
) = _build_pool_scheduling_state( ) = _build_pool_scheduling_state(
is_active=False, is_active=False,
account_blocked=False, account_blocked=False,
@@ -34,11 +29,7 @@ def test_pool_scheduling_state_manual_disabled_is_blocked() -> None:
assert status == "blocked" assert status == "blocked"
assert reason == "manual_disabled" assert reason == "manual_disabled"
assert candidate_eligible is False
assert blocked_count >= 1
assert score < 100
assert any(item.code == "manual_disabled" for item in reasons) assert any(item.code == "manual_disabled" for item in reasons)
assert any(item.code == "manual_disabled" for item in dimensions)
def test_pool_scheduling_state_cooldown_detail_is_mapped() -> None: def test_pool_scheduling_state_cooldown_detail_is_mapped() -> None:
@@ -47,11 +38,6 @@ def test_pool_scheduling_state_cooldown_detail_is_mapped() -> None:
reason, reason,
_label, _label,
reasons, reasons,
_score,
_candidate_eligible,
_blocked_count,
_degraded_count,
dimensions,
) = _build_pool_scheduling_state( ) = _build_pool_scheduling_state(
is_active=True, is_active=True,
account_blocked=False, account_blocked=False,
@@ -69,9 +55,7 @@ def test_pool_scheduling_state_cooldown_detail_is_mapped() -> None:
assert reason == "cooldown" assert reason == "cooldown"
cooldown_reason = next(item for item in reasons if item.code == "cooldown") cooldown_reason = next(item for item in reasons if item.code == "cooldown")
cooldown_dimension = next(item for item in dimensions if item.code == "cooldown")
assert cooldown_reason.detail == "429 限流" assert cooldown_reason.detail == "429 限流"
assert cooldown_dimension.detail == "429 限流"
def test_pool_scheduling_state_cost_soft_is_degraded() -> None: def test_pool_scheduling_state_cost_soft_is_degraded() -> None:
@@ -80,11 +64,6 @@ def test_pool_scheduling_state_cost_soft_is_degraded() -> None:
reason, reason,
_label, _label,
_reasons, _reasons,
_score,
candidate_eligible,
blocked_count,
degraded_count,
_dimensions,
) = _build_pool_scheduling_state( ) = _build_pool_scheduling_state(
is_active=True, is_active=True,
account_blocked=False, account_blocked=False,
@@ -102,9 +81,6 @@ def test_pool_scheduling_state_cost_soft_is_degraded() -> None:
assert status == "degraded" assert status == "degraded"
assert reason == "cost_soft" assert reason == "cost_soft"
assert candidate_eligible is True
assert blocked_count == 0
assert degraded_count >= 1
def test_known_banned_reason_account_block_prefix() -> None: def test_known_banned_reason_account_block_prefix() -> None: