Skip to content

Commit 8b99617

Browse files
committed
feat: fix code review issue
1 parent d37f4c0 commit 8b99617

10 files changed

Lines changed: 63 additions & 143 deletions

File tree

packages/cli/tests/e2e/quota.e2e.test.ts

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@ describe("e2e: quota", () => {
66
const { stderr, exitCode } = await runCli(["quota", "list", "--help"]);
77
expect(exitCode, stderr).toBe(0);
88
expect(stderr).toContain("--model");
9-
expect(stderr).toContain("--all");
109
});
1110

1211
test("quota list --help 包含所有示例", async () => {
1312
const { stderr, exitCode } = await runCli(["quota", "list", "--help"]);
1413
expect(exitCode, stderr).toBe(0);
1514
expect(stderr).toContain("bl quota list");
1615
expect(stderr).toContain("bl quota list --model qwen3.6-plus");
17-
expect(stderr).toContain("bl quota list --all");
1816
});
1917

2018
test("quota request --help 正常退出", async () => {
@@ -57,30 +55,14 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
5755
]);
5856
expect(exitCode, stderr).toBe(0);
5957
const data = parseStdoutJson<{
60-
api?: string;
61-
data?: {
58+
apis?: (string | { api: string; note?: string })[];
59+
modelListInput?: {
6260
input?: { queryQpmInfo?: boolean; supports?: { selfServiceLimitIncrease?: boolean } };
6361
};
6462
}>(stdout);
65-
expect(data.api).toContain("listFoundationModels");
66-
expect(data.data?.input?.queryQpmInfo).toBe(true);
67-
expect(data.data?.input?.supports?.selfServiceLimitIncrease).toBe(true);
68-
});
69-
70-
test("quota list --dry-run --all 不传 supports 过滤", async () => {
71-
const { stdout, stderr, exitCode } = await runCli([
72-
"quota",
73-
"list",
74-
"--all",
75-
"--dry-run",
76-
"--output",
77-
"json",
78-
]);
79-
expect(exitCode, stderr).toBe(0);
80-
const data = parseStdoutJson<{
81-
data?: { input?: { supports?: unknown } };
82-
}>(stdout);
83-
expect(data.data?.input?.supports).toBeUndefined();
63+
expect(data.apis?.[0]).toContain("listFoundationModels");
64+
expect(data.modelListInput?.input?.queryQpmInfo).toBe(true);
65+
expect(data.modelListInput?.input?.supports?.selfServiceLimitIncrease).toBe(true);
8466
});
8567

8668
test("quota list 文本输出包含英文表头", async () => {
@@ -109,7 +91,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
10991
expect(result.stderr).toContain("no matching models found");
11092
});
11193

112-
test("quota list JSON 输出包含 model/rpm/tpm/maxTPM", async () => {
94+
test("quota list JSON 输出包含 model/rpm/tpm", async () => {
11395
const result = await runCli(["quota", "list", "--output", "json"]);
11496
if (isConsoleAuthFailure(result)) return;
11597
expect(result.exitCode, result.stderr).toBe(0);

packages/commands/src/commands/model/list.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
defineCommand,
3+
detectOutputFormat,
34
fetchModelDetail,
45
fetchModelGroups,
56
fetchPredictConfig,
@@ -303,20 +304,19 @@ export default defineCommand({
303304
],
304305
async run(ctx) {
305306
const { settings, flags } = ctx;
306-
const format = settings.outputExplicit ? settings.output : "json";
307+
const format = detectOutputFormat(settings.output);
307308
const modelKey = flags.model;
308-
const isDetailMode = Boolean(modelKey);
309309

310310
// ── Detail mode ──
311-
if (isDetailMode) {
311+
if (modelKey) {
312312
const shouldEnrich = Boolean(flags.enrich);
313313

314314
if (settings.dryRun) {
315315
emitResult({ action: "model.detail", model: modelKey, enrich: shouldEnrich }, format);
316316
return;
317317
}
318318

319-
const detail = await fetchModelDetail(ctx.client.console.bind(ctx.client), modelKey!);
319+
const detail = await fetchModelDetail(ctx.client.console.bind(ctx.client), modelKey);
320320

321321
if (!detail) {
322322
emitBare(`Model "${modelKey}" not found.`);

packages/commands/src/commands/quota/check.ts

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import {
22
defineCommand,
3+
BailianError,
4+
ExitCode,
35
effectiveConsoleGatewayConfig,
46
detectOutputFormat,
7+
unwrapResponse,
8+
MODEL_LIST_API,
59
type Client,
610
} from "bailian-cli-core";
711
import { ansi, emitResult, renderBoxTable } from "bailian-cli-runtime";
812

9-
const MODEL_LIST_API = "zeldaHttp.dashscopeModel./zelda/api/v1/modelCenter/listFoundationModels";
1013
const MONITOR_API = "zeldaEasy.bailian-telemetry.monitor.getMonitorData";
1114

1215
interface QpmInfoItem {
@@ -48,28 +51,6 @@ function calculateTPM(item: QpmInfoItem | undefined, fallbackPeriod?: number): n
4851
return Math.floor((item.usage_limit * 60) / period);
4952
}
5053

51-
function getNestedRecord(
52-
obj: Record<string, unknown>,
53-
key: string,
54-
): Record<string, unknown> | undefined {
55-
const val = obj[key];
56-
if (val && typeof val === "object" && !Array.isArray(val)) return val as Record<string, unknown>;
57-
return undefined;
58-
}
59-
60-
function extractResponseData(result: Record<string, unknown>): Record<string, unknown> {
61-
const data = getNestedRecord(result, "data");
62-
if (!data) return result;
63-
const dataV2 = getNestedRecord(data, "DataV2");
64-
if (dataV2) {
65-
const inner = getNestedRecord(dataV2, "data");
66-
const innerData = inner ? getNestedRecord(inner, "data") : undefined;
67-
return innerData ?? inner ?? dataV2;
68-
}
69-
const direct = getNestedRecord(data, "data");
70-
return direct ?? data;
71-
}
72-
7354
async function fetchAllModelsWithQpm(client: Client): Promise<ModelWithQpm[]> {
7455
const allModels: ModelWithQpm[] = [];
7556
let pageNo = 1;
@@ -86,7 +67,7 @@ async function fetchAllModelsWithQpm(client: Client): Promise<ModelWithQpm[]> {
8667
},
8768
});
8869

89-
const resp = extractResponseData(raw as Record<string, unknown>);
70+
const resp = unwrapResponse(raw as Record<string, unknown>);
9071
const list = (resp.list as ModelWithQpm[]) ?? [];
9172
const total = (resp.total as number) ?? 0;
9273

@@ -123,7 +104,7 @@ async function fetchMonitorData(
123104
},
124105
});
125106

126-
const resp = extractResponseData(raw as Record<string, unknown>);
107+
const resp = unwrapResponse(raw as Record<string, unknown>);
127108
const metrics = (resp.data ?? resp) as MonitorMetric[] | Record<string, unknown>;
128109
if (!Array.isArray(metrics)) return { rpm: 0, tpm: 0 };
129110

@@ -139,8 +120,9 @@ async function fetchMonitorData(
139120

140121
return { rpm, tpm };
141122
} catch (error) {
142-
// Re-throw console authentication errors, but catch other errors
143-
if (error instanceof Error && error.message?.includes("Console session")) {
123+
// Re-throw authentication errors (BailianError with ExitCode.AUTH);
124+
// other errors are treated as "no data" and show "-" in the table.
125+
if (error instanceof BailianError && error.exitCode === ExitCode.AUTH) {
144126
throw error;
145127
}
146128
return { rpm: -1, tpm: -1 };

packages/commands/src/commands/quota/list.ts

Lines changed: 26 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
import { defineCommand, BailianError, detectOutputFormat, type Client } from "bailian-cli-core";
1+
import {
2+
defineCommand,
3+
BailianError,
4+
ExitCode,
5+
detectOutputFormat,
6+
unwrapResponse,
7+
MODEL_LIST_API,
8+
type Client,
9+
} from "bailian-cli-core";
210
import { emitResult, renderBoxTable } from "bailian-cli-runtime";
311

4-
const MODEL_LIST_API = "zeldaHttp.dashscopeModel./zelda/api/v1/modelCenter/listFoundationModels";
512
const MONITOR_API = "zeldaEasy.bailian-telemetry.monitor.getMonitorData";
613

714
interface QpmInfoItem {
@@ -72,7 +79,7 @@ async function fetchMonitorData(
7279
},
7380
});
7481

75-
const resp = extractResponseData(raw as Record<string, unknown>);
82+
const resp = unwrapResponse(raw as Record<string, unknown>);
7683
const metrics = (resp.data ?? resp) as MonitorMetric[] | Record<string, unknown>;
7784
if (!Array.isArray(metrics)) {
7885
return { rpm: 0, tpm: 0 };
@@ -90,37 +97,15 @@ async function fetchMonitorData(
9097

9198
return { rpm, tpm };
9299
} catch (error) {
93-
// Re-throw console authentication errors, but catch other errors
94-
if (error instanceof Error && error.message?.includes("Console session")) {
100+
// Re-throw authentication errors (BailianError with ExitCode.AUTH);
101+
// other errors are treated as "no data" and show "-" in the table.
102+
if (error instanceof BailianError && error.exitCode === ExitCode.AUTH) {
95103
throw error;
96104
}
97-
// Return -1 to indicate no data (same as check.ts for consistency)
98105
return { rpm: -1, tpm: -1 };
99106
}
100107
}
101108

102-
function getNestedRecord(
103-
obj: Record<string, unknown>,
104-
key: string,
105-
): Record<string, unknown> | undefined {
106-
const val = obj[key];
107-
if (val && typeof val === "object" && !Array.isArray(val)) return val as Record<string, unknown>;
108-
return undefined;
109-
}
110-
111-
function extractResponseData(result: Record<string, unknown>): Record<string, unknown> {
112-
const data = getNestedRecord(result, "data");
113-
if (!data) return result;
114-
const dataV2 = getNestedRecord(data, "DataV2");
115-
if (dataV2) {
116-
const inner = getNestedRecord(dataV2, "data");
117-
const innerData = inner ? getNestedRecord(inner, "data") : undefined;
118-
return innerData ?? inner ?? dataV2;
119-
}
120-
const direct = getNestedRecord(data, "data");
121-
return direct ?? data;
122-
}
123-
124109
async function fetchAllModelsWithQpm(client: Client): Promise<ModelWithQpm[]> {
125110
const allModels: ModelWithQpm[] = [];
126111
let pageNo = 1;
@@ -137,7 +122,7 @@ async function fetchAllModelsWithQpm(client: Client): Promise<ModelWithQpm[]> {
137122

138123
const raw = await client.console(MODEL_LIST_API, { input });
139124

140-
const resp = extractResponseData(raw as Record<string, unknown>);
125+
const resp = unwrapResponse(raw as Record<string, unknown>);
141126
const list = (resp.list as ModelWithQpm[]) ?? [];
142127
const total = (resp.total as number) ?? 0;
143128

@@ -153,7 +138,6 @@ interface ListRow {
153138
model: string;
154139
rpm: string;
155140
tpm: string;
156-
maxTpm: string;
157141
rpmQuotaLeft: number | null;
158142
tpmQuotaLeft: number | null;
159143
rpmQuotaLabel: string | null;
@@ -193,22 +177,11 @@ export default defineCommand({
193177
valueHint: "<model>",
194178
description: "Model name(s), comma-separated",
195179
},
196-
all: {
197-
type: "switch",
198-
description: "Show all models, not just self-service ones",
199-
},
200180
},
201-
exampleArgs: [
202-
"",
203-
"--model qwen3.6-plus",
204-
"--model qwen3.6-plus,qwen-turbo",
205-
"--all",
206-
"--output json",
207-
],
181+
exampleArgs: ["", "--model qwen3.6-plus", "--model qwen3.6-plus,qwen-turbo", "--output json"],
208182
async run(ctx) {
209183
const { settings, flags } = ctx;
210184
const modelFlag = flags.model || undefined;
211-
const showAll = Boolean(flags.all);
212185
const format = detectOutputFormat(settings.output);
213186

214187
if (settings.dryRun) {
@@ -218,9 +191,18 @@ export default defineCommand({
218191
group: false,
219192
queryQpmInfo: true,
220193
ignoreWorkspaceServiceSite: true,
194+
supports: { selfServiceLimitIncrease: true },
221195
};
222-
if (!showAll) input.supports = { selfServiceLimitIncrease: true };
223-
emitResult({ api: MODEL_LIST_API, data: { input } }, format);
196+
emitResult(
197+
{
198+
apis: [
199+
MODEL_LIST_API,
200+
{ api: MONITOR_API, note: "called per-model for text output with gauges" },
201+
],
202+
modelListInput: { input },
203+
},
204+
format,
205+
);
224206
return;
225207
}
226208

@@ -249,13 +231,11 @@ export default defineCommand({
249231
const defaultTPM = calculateTPM(modelDefault);
250232
const currentRPM = calculateRPM(userSpec, modelDefault?.count_limit_period) || defaultRPM;
251233
const currentTPM = calculateTPM(userSpec, modelDefault?.usage_limit_period) || defaultTPM;
252-
const maxTPM = defaultTPM * 2;
253234

254235
return {
255236
model: m.model,
256237
rpm: currentRPM > 0 ? currentRPM : null,
257238
tpm: currentTPM > 0 ? currentTPM : null,
258-
maxTPM: maxTPM > 0 ? maxTPM : null,
259239
};
260240
});
261241
emitResult(items, format);
@@ -276,7 +256,6 @@ export default defineCommand({
276256
const defaultTPM = calculateTPM(modelDefault);
277257
const currentRPM = calculateRPM(userSpec, modelDefault?.count_limit_period) || defaultRPM;
278258
const currentTPM = calculateTPM(userSpec, modelDefault?.usage_limit_period) || defaultTPM;
279-
const maxTPM = defaultTPM * 2;
280259

281260
const rpmUsage = monitorResults[idx].rpm;
282261
const tpmUsage = monitorResults[idx].tpm;
@@ -301,7 +280,6 @@ export default defineCommand({
301280
model: m.model,
302281
rpm: currentRPM > 0 ? formatNumber(currentRPM) : "-",
303282
tpm: currentTPM > 0 ? formatNumber(currentTPM) : "-",
304-
maxTpm: maxTPM > 0 ? formatNumber(maxTPM) : "-",
305283
rpmQuotaLeft: rpmQuotaPercent,
306284
tpmQuotaLeft: tpmQuotaPercent,
307285
rpmQuotaLabel,

packages/commands/src/commands/usage/shared.ts

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
fetchModelList,
33
BailianError,
44
ExitCode,
5+
unwrapResponse,
56
type Client,
67
type Settings,
78
} from "bailian-cli-core";
@@ -33,32 +34,12 @@ export function requireWorkspaceId(settings: Settings, binName: string): string
3334
);
3435
}
3536

36-
// ---------------------------------------------------------------------------
37-
// Response unwrapping (shared DataV2 envelope handling)
38-
// ---------------------------------------------------------------------------
39-
40-
function getNestedRecord(
41-
obj: Record<string, unknown>,
42-
key: string,
43-
): Record<string, unknown> | undefined {
44-
const val = obj[key];
45-
if (val && typeof val === "object" && !Array.isArray(val)) return val as Record<string, unknown>;
46-
return undefined;
47-
}
48-
37+
/**
38+
* @deprecated Use `unwrapResponse` from bailian-cli-core instead.
39+
* Kept here only for internal backward-compat with callers that already import this.
40+
*/
4941
export function extractResponseData(result: Record<string, unknown>): Record<string, unknown> {
50-
const data = getNestedRecord(result, "data");
51-
if (!data) return result;
52-
53-
const dataV2 = getNestedRecord(data, "DataV2");
54-
if (dataV2) {
55-
const inner = getNestedRecord(dataV2, "data");
56-
const innerData = inner ? getNestedRecord(inner, "data") : undefined;
57-
return innerData ?? inner ?? dataV2;
58-
}
59-
60-
const direct = getNestedRecord(data, "data");
61-
return direct ?? data;
42+
return unwrapResponse(result);
6243
}
6344

6445
// ---------------------------------------------------------------------------
@@ -226,7 +207,7 @@ export function printFreeTierTable(options: FreeTierTableOptions): void {
226207
headers,
227208
rows,
228209
align: ["left", "left", "right", "right", "left", "left"],
229-
barColumn: { index: 4, percents, labels: barLabels },
210+
barColumns: [{ index: 4, percents, labels: barLabels }],
230211
cellColor: (_rowIndex, colIndex, value) => {
231212
if (colIndex !== autoStopCol) return undefined;
232213
if (value === "ON") return color.green(value);

packages/commands/src/commands/usage/summary.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
printUsageOverview,
1616
quotaRemainingPercent,
1717
type FreeTierQuota,
18+
type OverviewStatistic,
1819
} from "./shared.ts";
1920

2021
/** Free-tier rows shown in the summary before the "+N more" hint. */
@@ -88,7 +89,7 @@ export default defineCommand({
8889
const stopMap = new Map(stopStatuses.map((status) => [status.model, status.freeTierOnly]));
8990

9091
// --- Usage overview (requires workspace) ---
91-
let overview: ReturnType<typeof extractOverviewData>;
92+
let overview: OverviewStatistic | undefined;
9293
if (workspaceId) {
9394
const result = await pollTelemetryApi(ctx.client, OVERVIEW_API, {
9495
startTime,

0 commit comments

Comments
 (0)