mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-09-01 15:34:05 +08:00
feat(timeline): enhance lane selection logic for Codex provider and add related tests
This commit is contained in:
@@ -268,6 +268,7 @@ export function laneHasWindow(lane: TimelineLane): boolean {
|
||||
|
||||
/** Shape the lane builder reads. Deliberately structural — see the note below. */
|
||||
interface WindowLike {
|
||||
id?: string;
|
||||
label?: string;
|
||||
usedPercent?: number | null;
|
||||
resetAtMs?: number | null;
|
||||
@@ -352,7 +353,25 @@ export function buildTimelineLane(input: TimelineLaneInput): TimelineLane {
|
||||
const windows = ((quota as { windows?: WindowLike[] }).windows ?? []).filter(
|
||||
(window) => typeof window.resetAtMs === 'number'
|
||||
);
|
||||
const chosen = pickLaneWindow(windows, maxPeriodHours);
|
||||
const preferredCodexId =
|
||||
maxPeriodHours !== undefined && maxPeriodHours <= SESSION_PERIOD_HOURS
|
||||
? 'five-hour'
|
||||
: 'weekly';
|
||||
// Codex can report model-scoped windows with the same period as the account
|
||||
// window (for example GPT-5.3-Codex-Spark weekly). A reset-time tie-break
|
||||
// would make the lane silently switch to that model's quota. Keep the lane
|
||||
// anchored to the standard account window whenever it fits this view.
|
||||
const preferredCodexWindow =
|
||||
provider === 'codex'
|
||||
? windows.find(
|
||||
(window) =>
|
||||
window.id === preferredCodexId &&
|
||||
typeof window.periodHours === 'number' &&
|
||||
window.periodHours > 0 &&
|
||||
(maxPeriodHours === undefined || window.periodHours <= maxPeriodHours)
|
||||
)
|
||||
: undefined;
|
||||
const chosen = preferredCodexWindow ?? pickLaneWindow(windows, maxPeriodHours);
|
||||
if (!chosen) return empty;
|
||||
|
||||
const resetCredits =
|
||||
|
||||
@@ -281,6 +281,39 @@ describe('buildTimelineLane', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('codex: keeps the weekly lane on the account quota instead of Spark quota', () => {
|
||||
const accountReset = at(2026, 7, 1, 20);
|
||||
const sparkReset = at(2026, 6, 29, 20);
|
||||
const lane = buildTimelineLane({
|
||||
...base,
|
||||
provider: 'codex',
|
||||
quota: {
|
||||
status: 'success',
|
||||
windows: [
|
||||
{
|
||||
id: 'weekly',
|
||||
label: 'Weekly limit',
|
||||
usedPercent: 70,
|
||||
resetAtMs: accountReset,
|
||||
periodHours: 168,
|
||||
},
|
||||
{
|
||||
id: 'gpt-5-3-codex-spark-weekly-0',
|
||||
label: 'GPT-5.3-Codex-Spark weekly limit',
|
||||
usedPercent: 2,
|
||||
resetAtMs: sparkReset,
|
||||
periodHours: 168,
|
||||
},
|
||||
],
|
||||
},
|
||||
maxPeriodHours: 14 * 24,
|
||||
});
|
||||
|
||||
expect(lane.anchorMs).toBe(accountReset);
|
||||
expect(lane.periodHours).toBe(168);
|
||||
expect(lane.remaining).toBe(30);
|
||||
});
|
||||
|
||||
test('codex: includes available reset credits with parseable expiry dates', () => {
|
||||
const expiresAt = '2026-08-02T12:00:00Z';
|
||||
const lane = buildTimelineLane({
|
||||
|
||||
Reference in New Issue
Block a user