44 detectOutputFormat ,
55 type Client ,
66} from "bailian-cli-core" ;
7- import { ansi , emitResult } from "bailian-cli-runtime" ;
8- import { displayWidth , padEnd } from "bailian-cli-runtime" ;
7+ import { ansi , emitResult , renderBoxTable } from "bailian-cli-runtime" ;
98
109const MODEL_LIST_API = "zeldaHttp.dashscopeModel./zelda/api/v1/modelCenter/listFoundationModels" ;
1110const MONITOR_API = "zeldaEasy.bailian-telemetry.monitor.getMonitorData" ;
@@ -49,24 +48,6 @@ function calculateTPM(item: QpmInfoItem | undefined, fallbackPeriod?: number): n
4948 return Math . floor ( ( item . usage_limit * 60 ) / period ) ;
5049}
5150
52- function formatNumber ( num : number ) : string {
53- return num . toLocaleString ( "en-US" ) ;
54- }
55-
56- function formatRatio ( usage : number , limit : number ) : string {
57- if ( limit <= 0 ) return "-" ;
58- const pct = Math . round ( ( usage / limit ) * 100 ) ;
59- return `${ formatNumber ( usage ) } /${ formatNumber ( limit ) } (${ pct } %)` ;
60- }
61-
62- function getStatus ( usage : number , limit : number ) : string {
63- if ( limit <= 0 ) return "-" ;
64- const pct = ( usage / limit ) * 100 ;
65- if ( pct >= 100 ) return "Rate Limited" ;
66- if ( pct >= 80 ) return "Near limit" ;
67- return "Normal" ;
68- }
69-
7051function getNestedRecord (
7152 obj : Record < string , unknown > ,
7253 key : string ,
@@ -157,7 +138,11 @@ async function fetchMonitorData(
157138 }
158139
159140 return { rpm, tpm } ;
160- } catch {
141+ } catch ( error ) {
142+ // Re-throw console authentication errors, but catch other errors
143+ if ( error instanceof Error && error . message ?. includes ( "Console session" ) ) {
144+ throw error ;
145+ }
161146 return { rpm : - 1 , tpm : - 1 } ;
162147 }
163148}
@@ -168,56 +153,59 @@ interface CheckRow {
168153 rpmLimit : number ;
169154 tpmUsage : number ;
170155 tpmLimit : number ;
156+ rpmQuotaLeft : number | null ;
157+ tpmQuotaLeft : number | null ;
158+ rpmQuotaLabel : string | null ;
159+ tpmQuotaLabel : string | null ;
171160}
172161
173162function printTable ( rows : CheckRow [ ] ) : void {
174163 const color = ansi ( process . stdout ) ;
164+ const headers = [ "Model" , "RPM Used/Limit" , "TPM Used/Limit" , "Status" , "RPM Left" , "TPM Left" ] ;
175165
176- const headers = [ "Model" , "RPM Usage/Limit" , "TPM Usage/Limit" , "Status" ] ;
166+ const rpmPercents = rows . map ( ( r ) => r . rpmQuotaLeft ) ;
167+ const rpmLabels = rows . map ( ( r ) => r . rpmQuotaLabel ) ;
168+ const tpmPercents = rows . map ( ( r ) => r . tpmQuotaLeft ) ;
169+ const tpmLabels = rows . map ( ( r ) => r . tpmQuotaLabel ) ;
177170
178171 const tableRows = rows . map ( ( r ) => {
179- const rpmStr = r . rpmUsage < 0 ? "-" : formatRatio ( r . rpmUsage , r . rpmLimit ) ;
180- const tpmStr = r . tpmUsage < 0 ? "-" : formatRatio ( r . tpmUsage , r . tpmLimit ) ;
172+ const rpmStr = r . rpmUsage < 0 ? "-" : ` ${ r . rpmUsage } / ${ r . rpmLimit } ` ;
173+ const tpmStr = r . tpmUsage < 0 ? "-" : ` ${ r . tpmUsage } / ${ r . tpmLimit } ` ;
181174 const maxPct = Math . max (
182175 r . rpmLimit > 0 ? ( r . rpmUsage / r . rpmLimit ) * 100 : 0 ,
183176 r . tpmLimit > 0 ? ( r . tpmUsage / r . tpmLimit ) * 100 : 0 ,
184177 ) ;
185178 const status =
186179 r . rpmUsage < 0
187180 ? "-"
188- : getStatus ( Math . max ( r . rpmUsage , r . tpmUsage ) , Math . max ( r . rpmLimit , r . tpmLimit ) ) ;
189- return { cells : [ r . model , rpmStr , tpmStr , status ] , maxPct } ;
181+ : maxPct >= 100
182+ ? "Rate Limited"
183+ : maxPct >= 80
184+ ? "Near limit"
185+ : "Normal" ;
186+ return [ r . model , rpmStr , tpmStr , status , "" , "" ] ;
190187 } ) ;
191188
192- if ( tableRows . length === 0 ) {
193- process . stdout . write ( "No models found.\n" ) ;
194- return ;
195- }
196-
197- const widths = headers . map ( ( label , col ) =>
198- Math . max ( displayWidth ( label ) , ...tableRows . map ( ( r ) => displayWidth ( r . cells [ col ] ) ) ) ,
199- ) ;
200-
201- const headerLine = headers . map ( ( label , col ) => color . bold ( padEnd ( label , widths [ col ] ) ) ) . join ( " " ) ;
202- const separator = widths . map ( ( w ) => color . dim ( "─" . repeat ( w ) ) ) . join ( "──" ) ;
203-
204- process . stdout . write ( headerLine + "\n" ) ;
205- process . stdout . write ( separator + "\n" ) ;
206-
207- const statusCol = 3 ;
208- for ( const r of tableRows ) {
209- const cells = r . cells . map ( ( cell , col ) => {
210- if ( col === statusCol ) {
211- if ( cell === "Rate Limited" ) return color . red ( padEnd ( cell , widths [ col ] ) ) ;
212- if ( cell === "Near limit" ) return color . yellow ( padEnd ( cell , widths [ col ] ) ) ;
213- if ( cell === "Normal" ) return color . green ( padEnd ( cell , widths [ col ] ) ) ;
189+ const lines = renderBoxTable ( {
190+ headers,
191+ rows : tableRows ,
192+ align : [ "left" , "right" , "right" , "left" , "left" , "left" ] ,
193+ barColumns : [
194+ { index : 4 , percents : rpmPercents , labels : rpmLabels , width : 15 } ,
195+ { index : 5 , percents : tpmPercents , labels : tpmLabels , width : 15 } ,
196+ ] ,
197+ cellColor : ( rowIndex , colIndex , value ) => {
198+ if ( colIndex === 3 ) {
199+ // Status 列着色
200+ if ( value === "Rate Limited" ) return color . red ( value ) ;
201+ if ( value === "Near limit" ) return color . yellow ( value ) ;
202+ if ( value === "Normal" ) return color . green ( value ) ;
214203 }
215- return padEnd ( cell , widths [ col ] ) ;
216- } ) ;
217- process . stdout . write ( cells . join ( " " ) + "\n" ) ;
218- }
204+ return undefined ;
205+ } ,
206+ } ) ;
219207
220- process . stdout . write ( color . dim ( `\nTotal: ${ rows . length } models` ) + "\n" ) ;
208+ for ( const line of lines ) process . stdout . write ( line + "\n" ) ;
221209}
222210
223211export default defineCommand ( {
@@ -295,12 +283,35 @@ export default defineCommand({
295283 const tpmLimit =
296284 calculateTPM ( userSpec , modelDefault ?. usage_limit_period ) || calculateTPM ( modelDefault ) ;
297285
286+ const rpmUsage = monitorResults [ idx ] . rpm ;
287+ const tpmUsage = monitorResults [ idx ] . tpm ;
288+
289+ // RPM Quota Left = 1 - (rpmUsage / rpmLimit) in percentage
290+ let rpmQuotaPercent : number | null = null ;
291+ let rpmQuotaLabel : string | null = null ;
292+ if ( rpmUsage >= 0 && rpmLimit > 0 ) {
293+ rpmQuotaPercent = Math . max ( 0 , 100 - ( rpmUsage / rpmLimit ) * 100 ) ;
294+ rpmQuotaLabel = rpmQuotaPercent . toFixed ( 1 ) + "%" ;
295+ }
296+
297+ // TPM Quota Left = 1 - (tpmUsage / tpmLimit) in percentage
298+ let tpmQuotaPercent : number | null = null ;
299+ let tpmQuotaLabel : string | null = null ;
300+ if ( tpmUsage >= 0 && tpmLimit > 0 ) {
301+ tpmQuotaPercent = Math . max ( 0 , 100 - ( tpmUsage / tpmLimit ) * 100 ) ;
302+ tpmQuotaLabel = tpmQuotaPercent . toFixed ( 1 ) + "%" ;
303+ }
304+
298305 return {
299306 model : m . model ,
300- rpmUsage : monitorResults [ idx ] . rpm ,
307+ rpmUsage,
301308 rpmLimit,
302- tpmUsage : monitorResults [ idx ] . tpm ,
309+ tpmUsage,
303310 tpmLimit,
311+ rpmQuotaLeft : rpmQuotaPercent ,
312+ tpmQuotaLeft : tpmQuotaPercent ,
313+ rpmQuotaLabel,
314+ tpmQuotaLabel,
304315 } ;
305316 } ) ;
306317
0 commit comments