fix(usage): expose pending reasoning and exact reset expiry

This commit is contained in:
MMEXA
2026-07-13 22:03:44 +08:00
parent 2fc604e047
commit b09d1f1c33
5 changed files with 58 additions and 13 deletions
+22 -2
View File
@@ -2029,6 +2029,22 @@ fn build_runtime_request_metadata_seed(
provider_request_body_ref.as_deref(),
plan.body.body_bytes_b64.as_deref(),
);
let provider_request_body = plan.body.json_body.as_ref().or_else(|| {
context_value_ref(context, "provider_request_body").filter(|value| !value.is_null())
});
let provider_api_format = context_string(context, "provider_api_format")
.or_else(|| non_empty_str(Some(plan.provider_api_format.as_str())));
let provider_model = context_string(context, "mapped_model")
.or_else(|| non_empty_str(plan.model_name.as_deref()));
let source_model =
context_string(context, "model").or_else(|| non_empty_str(plan.model_name.as_deref()));
metadata = attach_provider_request_body_metadata(
metadata,
provider_api_format.as_deref(),
provider_model.as_deref(),
source_model.as_deref(),
provider_request_body,
);
if let Some(proxy) = plan.proxy.as_ref() {
if let Some(node_id) = proxy
.node_id
@@ -3625,7 +3641,8 @@ mod tests {
content_encoding: None,
body: RequestBody::from_json(json!({
"model": "gpt-5.4",
"messages": [{"role": "user", "content": "hello"}]
"messages": [{"role": "user", "content": "hello"}],
"reasoning": {"effort": "max"}
})),
stream: false,
client_api_format: "claude:messages".to_string(),
@@ -3684,7 +3701,10 @@ mod tests {
.as_ref()
.and_then(Value::as_object)
.expect("pending usage should only keep lightweight request metadata");
assert_eq!(metadata.len(), 1);
assert_eq!(
metadata.get("provider_reasoning_effort"),
Some(&json!("max"))
);
let body_size = metadata
.get("body_size")
.and_then(Value::as_object)
@@ -310,7 +310,7 @@
:title="item.title"
class="tabular-nums"
>
{{ item.displayKey }} {{ formatCodexResetCreditDays(item.remainingSeconds) }}
{{ item.displayKey }} {{ formatCodexResetCreditExpiresAt(item.expiresAt) }}
</span>
<span
v-if="itemIndex < getVisibleCodexResetCreditItems(key).length - 1"
@@ -1019,7 +1019,7 @@ import {
import { getGeminiCliAccountCreditsText } from '@/utils/providerKeyQuota'
import {
formatCodexResetCreditCount as formatCodexResetCreditCountLabel,
formatCodexResetCreditDays,
formatCodexResetCreditExpiresAt,
getCodexResetCreditAvailableCount as getCodexResetCreditAvailableCountFromSnapshot,
getVisibleCodexResetCreditItems as getVisibleCodexResetCreditItemsFromSnapshot,
} from './codex-reset-credit-display'
@@ -1702,7 +1702,7 @@ async function handleConsumeCodexResetCredit(key: EndpointAPIKey) {
const earliest = getVisibleCodexResetCreditItems(key)[0]
const detailMessage = earliest
? `\n当前最早过期项:${earliest.displayKey}${formatCodexResetCreditDays(earliest.remainingSeconds)} 过期。`
? `\n当前最早过期项:${earliest.displayKey}${formatCodexResetCreditExpiresAt(earliest.expiresAt)} 过期。`
: ''
const confirmed = await confirm({
title: legacyT('确认使用 Codex 重置机会'),
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'
import {
formatCodexResetCreditCount,
formatCodexResetCreditDays,
formatCodexResetCreditExpiresAt,
getCodexResetCreditAvailableCount,
getVisibleCodexResetCreditItems,
} from '@/features/providers/components/codex-reset-credit-display'
@@ -74,8 +74,20 @@ describe('codex reset credit display helpers', () => {
])
})
it('formats reset credit remaining days with a one-day minimum', () => {
expect(formatCodexResetCreditDays(1)).toBe('1天')
expect(formatCodexResetCreditDays(86_401)).toBe('2天')
it('formats reset credit expiry as a precise local timestamp', () => {
const expiresAt = new Date(2026, 6, 12, 22, 4, 41).getTime() / 1000
expect(formatCodexResetCreditExpiresAt(expiresAt)).toBe('07-12 22:04:41')
expect(formatCodexResetCreditExpiresAt(null)).toBe('-')
})
it('derives a stable expiry timestamp from remaining seconds', () => {
const snapshot: QuotaResetCreditsSnapshot = {
available_count: 1,
updated_at: 1_700_000_000,
credits: [{ status: 'available', remaining_seconds: 600 }],
}
expect(getVisibleCodexResetCreditItems(snapshot, 1_700_000_300)[0]?.expiresAt)
.toBe(1_700_000_600)
})
})
@@ -66,7 +66,7 @@ export function getVisibleCodexResetCreditItems(
if (remainingSeconds === null || remainingSeconds <= 0) return null
return {
id: item.id,
expiresAt: item.expires_at,
expiresAt: nowUnixSecs + remainingSeconds,
remainingSeconds,
} satisfies CodexResetCreditDisplayCandidate
})
@@ -83,7 +83,11 @@ export function getVisibleCodexResetCreditItems(
})
}
export function formatCodexResetCreditDays(remainingSeconds: number): string {
const days = Math.max(1, Math.ceil(remainingSeconds / 86_400))
return `${days}`
export function formatCodexResetCreditExpiresAt(expiresAt: number | null | undefined): string {
if (typeof expiresAt !== 'number' || !Number.isFinite(expiresAt)) return '-'
const date = new Date(expiresAt * 1000)
if (Number.isNaN(date.getTime())) return '-'
const pad = (value: number) => String(value).padStart(2, '0')
return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
@@ -288,6 +288,15 @@ describe('UsageRecordsTable', () => {
expect(root.textContent).toContain('xhigh')
})
it('shows request reasoning effort while the record is pending', () => {
const root = mountUsageRecordsTable([buildRecord({
status: 'pending',
reasoning_effort: 'max',
})])
expect(root.textContent).toContain('max')
})
it('shows fast badge for priority service tier', () => {
const root = mountUsageRecordsTable([buildRecord({ service_tier: 'priority' })])