fix(accounts): align quota ordering and summaries

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Zyy
2026-08-11 17:21:23 +08:00
co-authored by Codex
parent a31dad992c
commit f370d355b6
5 changed files with 116 additions and 18 deletions
@@ -334,7 +334,11 @@ pub(crate) fn parse_account_quota_snapshot(
&mut windows,
)?;
}
windows.sort_by(|left, right| left.key.cmp(&right.key));
windows.sort_by(|left, right| {
quota_source_order(left.source())
.cmp(&quota_source_order(right.source()))
.then_with(|| left.key.cmp(&right.key))
});
Ok(CodexAccountQuotaSnapshot {
account_id,
credential_revision,
@@ -345,6 +349,14 @@ pub(crate) fn parse_account_quota_snapshot(
})
}
fn quota_source_order(source: &str) -> u8 {
match source {
"codex" => 0,
"code_review" => 1,
_ => 2,
}
}
/// 账号状态只能由顶层 canonical Codex 额度桶的明确拒绝来改变。
///
/// `fact.exhausted()` 和窗口 `limit_reached` 都会聚合或继承 secondary、附加桶等事实,
@@ -364,6 +364,58 @@ async fn persisted_codex_alias_keeps_the_top_level_rate_limit_canonical() {
);
}
#[tokio::test]
async fn persisted_quota_orders_core_window_before_additional_limit() {
let store = Arc::new(MemoryAccountStore::default());
create_account(&store, "acct_quota_order").await;
let account = store.account("acct_quota_order").expect("account");
let raw = json!({
"rate_limit": {
"secondary_window": {
"used_percent": 42,
"reset_at": 1_900_604_800,
"limit_window_seconds": 604_800
}
},
"additional_rate_limits": [{
"limit_name": "GPT-5.3-Codex-Spark",
"metered_feature": "gpt-5.3-codex-spark",
"rate_limit": {
"primary_window": {
"used_percent": 17,
"reset_at": 1_900_604_800,
"limit_window_seconds": 604_800
}
}
}]
});
store
.compare_and_swap_quota(QuotaObservation {
account_id: account.id().clone(),
expected_revision: account.revision(),
quota: Some(OpaqueProviderData::new(
raw.as_object().expect("quota object").clone(),
)),
observed_at: Some(SystemTime::now()),
limit_reached: None,
})
.await
.expect("persist quota");
let snapshot = quota_service(&store)
.read_account(account.id())
.await
.expect("read quota")
.expect("quota snapshot");
let sources = snapshot
.windows()
.iter()
.map(|window| window.source())
.collect::<Vec<_>>();
assert_eq!(sources, ["codex", "gpt-5.3-codex-spark"]);
}
#[tokio::test]
async fn code_review_limit_projects_as_independent_window_with_limit_name() {
let store = Arc::new(MemoryAccountStore::default());
@@ -13,7 +13,7 @@ const quotaWindows = computed(() => props.account.quota.windows)
const visibleQuotaWindows = computed(() => visibleSummaryQuotaWindows(quotaWindows.value))
const summaryClass = computed(
() =>
`grid w-full max-w-40 min-w-0 gap-2 whitespace-normal py-0.5 ${
`grid w-full min-w-0 gap-1.5 py-0.5 ${
visibleQuotaWindows.value.length <= 1 ? 'min-h-13 content-center' : ''
}`,
)
@@ -66,25 +66,29 @@ function variantValue(metric: string, compact: string, detail: string) {
const rootClass = computed(() =>
variantValue(
'grid min-w-0 grid-rows-[11px_14px] gap-1',
'grid min-w-0 grid-rows-[14px_14px] gap-1',
'flex min-w-0 flex-col',
'rounded-lg bg-(--cp-bg-subtle) p-2',
),
)
const headerClass = computed(() =>
variantValue(
'flex items-center justify-between gap-2 text-[11px] leading-none font-bold',
'mb-1 flex items-center justify-between gap-2 text-[11px] leading-none font-bold',
'flex items-center justify-between gap-2 text-[11px] leading-[14px] font-bold',
'mb-1 flex items-baseline justify-between gap-2 text-[11px] leading-[14px] font-bold',
'flex items-center justify-between gap-3 text-[12px] font-bold',
),
)
const labelClass = computed(() =>
isCompact.value || isMetric.value ? 'text-(--cp-text-muted)' : 'text-(--cp-text-secondary)',
)
const labelClass = computed(() => {
if (isMetric.value)
return 'truncate text-(--cp-text-muted)'
if (isCompact.value)
return 'truncate text-(--cp-text-muted)'
return 'text-(--cp-text-secondary)'
})
const valueClass = computed(() =>
variantValue(
'text-[11px] leading-none font-bold',
'text-[10px] leading-none font-heavy',
'text-[10px] leading-[14px] font-heavy',
'text-[12px] font-heavy',
),
)
@@ -94,7 +98,7 @@ const trackOffsetClass = computed(() =>
const trackShapeClass = computed(() =>
variantValue(
'h-1.5 w-full overflow-hidden rounded-full',
'h-1 w-full overflow-hidden rounded-full',
'h-[3px] w-full overflow-hidden rounded-full',
'h-2 overflow-hidden rounded-full',
),
)
@@ -104,7 +108,7 @@ const barStyle = computed(() => {
return undefined
return quotaWindowBarStyle(
props.window,
variantValue('6px', '4px', '8px'),
variantValue('6px', '3px', '8px'),
)
})
const barClass = computed(() => props.window ? quotaWindowBarClass(props.window) : undefined)
@@ -167,10 +171,38 @@ const requestBars = computed(() => {
<div :class="rootClass">
<template v-if="mode === 'quota' && window">
<div :class="headerClass">
<span class="min-w-0" :class="labelClass">
<span
class="min-w-0"
:class="labelClass"
:title="isCompact || isMetric ? window.labelDisplay : undefined"
>
{{ window.labelDisplay }}
</span>
<span class="flex shrink-0 items-baseline justify-end gap-1.5 font-mono tabular-nums">
<span
v-if="isCompact"
class="flex shrink-0 items-baseline justify-end gap-1.5 font-mono tabular-nums"
>
<span
v-if="quotaLocalUsageVisible"
class="text-right text-(--cp-text-muted)"
:class="valueClass"
:title="`本地 Token 用量:${quotaLocalUsageDisplay}`"
>
{{ quotaLocalUsageDisplay }}
</span>
<span
v-if="quotaValueVisible"
class="text-right"
:class="[valueClass, percentTextClass]"
:title="`额度已使用:${window.usedPercentDisplay}`"
>
{{ window.usedPercentDisplay }}
</span>
</span>
<span
v-else
class="flex shrink-0 items-baseline justify-end gap-1.5 font-mono tabular-nums"
>
<span
v-if="quotaLocalUsageVisible"
class="text-(--cp-text-muted)"
+7 -5
View File
@@ -25,7 +25,7 @@ const quotaGroupOrder = new Map([
['other', 2],
])
const relaxedCellClass = 'py-3 align-middle'
const relaxedCellClass = 'py-2 align-middle'
export const accountColumns = [
{
@@ -76,8 +76,8 @@ export const accountColumns = [
key: 'planType',
label: '套餐',
sortable: true,
flex: 0.8,
minWidth: '112px',
flex: 0.5,
minWidth: '90px',
cellClass: relaxedCellClass,
},
{
@@ -96,7 +96,8 @@ export const accountColumns = [
minWidth: '160px',
format: (_value: unknown, row: AccountRow) => optionalAccountCell(row.usage.lastUsedAtDisplay),
emptyText: '',
cellClass: `${relaxedCellClass} text-(--cp-text-secondary)`,
headerClass: '!pl-8',
cellClass: `${relaxedCellClass} !pl-8 text-(--cp-text-secondary)`,
},
{
key: 'accessTokenExpiresAtDisplay',
@@ -212,7 +213,8 @@ function compareQuotaWindows(left: AccountQuotaWindow, right: AccountQuotaWindow
if (secondsDifference !== 0)
return secondsDifference
return left.key.localeCompare(right.key)
// 同组同周期保留 Provider 的投影顺序,避免内部 key 把 additional 排到 core 前面。
return 0
}
function groupOrder(window: AccountQuotaWindow) {