Skip to content

Commit 6621c82

Browse files
authored
Merge pull request #205 from shaoohh/agent/fix-quota-unavailable-plan
fix: report unavailable quota models
2 parents f48a4e7 + 5c14912 commit 6621c82

2 files changed

Lines changed: 83 additions & 20 deletions

File tree

src/output/quota-table.ts

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ interface Labels {
3030
resetsIn: string;
3131
noData: string;
3232
now: string;
33+
notInPlan: string;
3334
}
3435

35-
const LABELS_EN: Labels = { dashboard: 'TokenPlan Quota', week: 'Week', current: 'Left', weekly: 'Wk left', resetsIn: 'Reset', noData: 'No quota data available.', now: 'now' };
36-
const LABELS_CN: Labels = { dashboard: 'TokenPlan 配额面板', week: '周期', current: '剩余', weekly: '周剩余', resetsIn: '重置', noData: '暂无配额数据', now: '即将' };
36+
const LABELS_EN: Labels = { dashboard: 'TokenPlan Quota', week: 'Week', current: 'Left', weekly: 'Wk left', resetsIn: 'Reset', noData: 'No quota data available.', now: 'now', notInPlan: 'not in plan' };
37+
const LABELS_CN: Labels = { dashboard: 'TokenPlan 配额面板', week: '周期', current: '剩余', weekly: '周剩余', resetsIn: '重置', noData: '暂无配额数据', now: '即将', notInPlan: '不在当前套餐中' };
3738

3839
const MODEL_NAME_CN: Record<string, string> = {
3940
'general': '通用',
@@ -84,6 +85,16 @@ function isUnweekly(status: number | undefined | null): boolean {
8485
return status === 3;
8586
}
8687

88+
function isUnavailablePlan(model: QuotaModelRemain): boolean {
89+
// Weekly status 3 is normally unlimited. When both windows are status 3 and
90+
// both totals are zero, however, the API uses the same status for a model
91+
// that has no quota bucket in the current plan (see issue #173).
92+
return model.current_interval_total_count === 0
93+
&& model.current_weekly_total_count === 0
94+
&& model.current_interval_status === 3
95+
&& model.current_weekly_status === 3;
96+
}
97+
8798
function clampPct(value: number): number {
8899
return Math.max(0, Math.min(MAX_DISPLAY_PCT, Math.round(value)));
89100
}
@@ -154,6 +165,14 @@ function renderMetric(
154165
return `${label} ${bar}`;
155166
}
156167

168+
function renderUnavailableMetric(label: string, unavailableLabel: string, color: boolean): string {
169+
if (color) {
170+
const bar = `${BG_EMPTY}${' '.repeat(COMPACT_BAR_WIDTH)}${R}`;
171+
return `${D}${label}${R} ${bar} ${FG_RED}${B}${unavailableLabel}${R}`;
172+
}
173+
return `${label} [${'.'.repeat(COMPACT_BAR_WIDTH)}] ${unavailableLabel}`;
174+
}
175+
157176
function boxLine(w: number, l: string, f: string, r: string, c: boolean): string {
158177
return c ? `${D}${l}${f.repeat(w)}${r}${R}` : `+${'-'.repeat(w)}+`;
159178
}
@@ -169,24 +188,31 @@ export function renderQuotaTable(models: QuotaModelRemain[], config: Config): vo
169188

170189
const rows = models.map((m) => {
171190
const displayName = displayModelName(m.model_name, config.region);
172-
const current = renderMetric(
173-
L.current,
174-
m.current_interval_usage_count,
175-
m.current_interval_total_count,
176-
m.current_interval_remaining_percent,
177-
useColor,
178-
);
179-
const weekly = renderMetric(
180-
L.weekly,
181-
m.current_weekly_usage_count,
182-
m.current_weekly_total_count,
183-
m.current_weekly_remaining_percent,
184-
useColor,
185-
m.weekly_boost_permille,
186-
isUnweekly(m.current_weekly_status),
187-
config.region === 'cn' ? UNLIMITED_LABEL_CN : UNLIMITED_LABEL_EN,
188-
);
189-
const reset = `${L.resetsIn} ${formatDuration(m.remains_time, L.now)}`;
191+
const unavailable = isUnavailablePlan(m);
192+
const current = unavailable
193+
? renderUnavailableMetric(L.current, L.notInPlan, useColor)
194+
: renderMetric(
195+
L.current,
196+
m.current_interval_usage_count,
197+
m.current_interval_total_count,
198+
m.current_interval_remaining_percent,
199+
useColor,
200+
);
201+
const weekly = unavailable
202+
? renderUnavailableMetric(L.weekly, L.notInPlan, useColor)
203+
: renderMetric(
204+
L.weekly,
205+
m.current_weekly_usage_count,
206+
m.current_weekly_total_count,
207+
m.current_weekly_remaining_percent,
208+
useColor,
209+
m.weekly_boost_permille,
210+
isUnweekly(m.current_weekly_status),
211+
config.region === 'cn' ? UNLIMITED_LABEL_CN : UNLIMITED_LABEL_EN,
212+
);
213+
const reset = unavailable
214+
? `${L.resetsIn} —`
215+
: `${L.resetsIn} ${formatDuration(m.remains_time, L.now)}`;
190216
return { displayName, current, weekly, reset };
191217
});
192218

test/output/quota-table.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,41 @@ describe('renderQuotaTable', () => {
253253
expect(output).toContain('unlimited');
254254
expect(output).not.toContain('100%');
255255
});
256+
257+
it('renders a zero-allocation model as unavailable instead of unlimited', () => {
258+
const lines: string[] = [];
259+
const originalLog = console.log;
260+
261+
console.log = (message?: unknown) => {
262+
lines.push(String(message ?? ''));
263+
};
264+
265+
try {
266+
renderQuotaTable(
267+
[
268+
{
269+
...createModel(),
270+
model_name: 'video',
271+
current_interval_total_count: 0,
272+
current_interval_usage_count: 0,
273+
current_interval_remaining_percent: 100,
274+
current_interval_status: 3,
275+
current_weekly_total_count: 0,
276+
current_weekly_usage_count: 0,
277+
current_weekly_remaining_percent: 100,
278+
current_weekly_status: 3,
279+
},
280+
],
281+
{ ...createConfig(), noColor: true },
282+
);
283+
} finally {
284+
console.log = originalLog;
285+
}
286+
287+
const output = lines.join('\n');
288+
expect(output).toContain('not in plan');
289+
expect(output).toContain('Reset —');
290+
expect(output).not.toContain('unlimited');
291+
expect(output).not.toContain('100%');
292+
});
256293
});

0 commit comments

Comments
 (0)