@@ -1545,9 +1545,10 @@ function quotaSpeedMultiplier(model, serviceTier = null) {
15451545 const value = String ( model || "" ) . toLowerCase ( ) ;
15461546 const tier = String ( serviceTier || "" ) . toLowerCase ( ) ;
15471547 const isFast =
1548- / ( ^ | [ - _ \s ] ) f a s t ( $ | [ - _ \s ] ) | h i g h [ - _ \s ] ? s p e e d | s p e e d y / . test ( value ) ||
1548+ / ( ^ | [ - _ \s ] ) f a s t ( $ | [ - _ \s ] ) | h i g h [ - _ \s ] ? s p e e d | s p e e d y | t u r b o | a c c e l e r a t e d / . test ( value ) ||
15491549 tier === "fast" ||
1550- tier === "priority" ;
1550+ tier === "priority" ||
1551+ tier === "turbo" ;
15511552 return isFast ? codexRateCard ( model ) . fastMultiplier : 1 ;
15521553}
15531554
@@ -1923,6 +1924,7 @@ async function readLatestSqliteRateLimitQuota(options = {}) {
19231924 const effectiveSinceSeconds = Number . isFinite ( sinceMs )
19241925 ? Math . floor ( sinceMs / 1000 )
19251926 : Math . floor ( Date . now ( ) / 1000 ) - 14 * 24 * 60 * 60 ;
1927+ const accountFilter = normalizeAccountFilter ( options . accountIdentity ) ;
19261928 if ( ! ( await pathExists ( logsDbPath ( ) ) ) ) return null ;
19271929 let DatabaseSync ;
19281930 try {
@@ -1943,7 +1945,7 @@ async function readLatestSqliteRateLimitQuota(options = {}) {
19431945 limit 1000`
19441946 )
19451947 . all ( effectiveSinceSeconds ) ;
1946- for ( const row of rows ) {
1948+ for ( const row of filterRowsByAccount ( rows , accountFilter ) ) {
19471949 const quota = quotaFromCodexRateLimitsMessage ( extractCodexLogMessage ( row . feedback_log_body ) , row . ts ) ;
19481950 if ( quota ) return quota ;
19491951 }
@@ -1964,6 +1966,7 @@ async function readLatestUsageLimitQuota(options = {}) {
19641966 const effectiveSinceSeconds = Number . isFinite ( sinceMs )
19651967 ? Math . floor ( sinceMs / 1000 )
19661968 : Math . floor ( Date . now ( ) / 1000 ) - 14 * 24 * 60 * 60 ;
1969+ const accountFilter = normalizeAccountFilter ( options . accountIdentity ) ;
19671970 if ( ! ( await pathExists ( logsDbPath ( ) ) ) ) return null ;
19681971 let DatabaseSync ;
19691972 try {
@@ -1984,7 +1987,7 @@ async function readLatestUsageLimitQuota(options = {}) {
19841987 limit 1000`
19851988 )
19861989 . all ( effectiveSinceSeconds ) ;
1987- for ( const row of rows ) {
1990+ for ( const row of filterRowsByAccount ( rows , accountFilter ) ) {
19881991 const quota = quotaFromUsageLimitMessage ( extractCodexLogMessage ( row . feedback_log_body ) , row . ts ) ;
19891992 if ( quota ) return quota ;
19901993 }
@@ -2144,6 +2147,10 @@ async function parseLatestRateLimitFile(file, sinceMs) {
21442147 }
21452148 if ( entry . type !== "event_msg" || entry . payload ?. type !== "token_count" ) continue ;
21462149 if ( ! entry . payload ?. rate_limits ) continue ;
2150+ serviceTier =
2151+ entry . payload ?. service_tier ?? entry . payload ?. serviceTier ??
2152+ entry . payload ?. rate_limits ?. service_tier ?? entry . payload ?. rate_limits ?. serviceTier ??
2153+ serviceTier ;
21472154 const timestamp = entry . timestamp ?? new Date ( file . mtimeMs ) . toISOString ( ) ;
21482155 const eventMs = new Date ( timestamp ) . getTime ( ) ;
21492156 const info = entry . payload ?. info ?? { } ;
@@ -2221,14 +2228,18 @@ async function parseQuotaEventFile(file) {
22212228 const ms = dateMs ( timestamp ) ;
22222229 if ( ! Number . isFinite ( ms ) ) continue ;
22232230 const info = entry . payload ?. info ?? { } ;
2231+ const eventServiceTier =
2232+ entry . payload ?. service_tier ?? entry . payload ?. serviceTier ??
2233+ entry . payload ?. rate_limits ?. service_tier ?? entry . payload ?. rate_limits ?. serviceTier ??
2234+ serviceTier ;
22242235 events . push ( {
22252236 filePath : file . path ,
22262237 sessionId : sessionId ?? file . path ,
22272238 startedAtMs,
22282239 timestamp,
22292240 ms,
22302241 model,
2231- serviceTier,
2242+ serviceTier : eventServiceTier ,
22322243 tokenUsage : normalizeTokenUsage ( info . total_token_usage ?? info . totalTokenUsage ) ,
22332244 rateLimits : entry . payload ?. rate_limits ?? null ,
22342245 } ) ;
@@ -2266,6 +2277,45 @@ function numberField(fields, key) {
22662277 return Number . isFinite ( value ) ? value : 0 ;
22672278}
22682279
2280+ function normalizeAccountFilter ( identity ) {
2281+ const ids = new Set ( ) ;
2282+ const emails = new Set ( ) ;
2283+ for ( const value of [ identity ?. userId , identity ?. accountUserId , identity ?. chatgptUserId , identity ?. subject ] ) {
2284+ const text = typeof value === "string" ? value . trim ( ) . toLowerCase ( ) : "" ;
2285+ if ( text ) ids . add ( text ) ;
2286+ }
2287+ const email = typeof identity ?. email === "string" ? identity . email . trim ( ) . toLowerCase ( ) : "" ;
2288+ if ( email ) emails . add ( email ) ;
2289+ return ids . size || emails . size ? { ids, emails } : null ;
2290+ }
2291+
2292+ function responseEventAccountMatch ( event , accountFilter ) {
2293+ if ( ! accountFilter ) return "match" ;
2294+ const accountId = typeof event ?. accountId === "string" ? event . accountId . trim ( ) . toLowerCase ( ) : "" ;
2295+ const email = typeof event ?. email === "string" ? event . email . trim ( ) . toLowerCase ( ) : "" ;
2296+ if ( ! accountId && ! email ) return "unknown" ;
2297+ if ( accountId && accountFilter . ids . has ( accountId ) ) return "match" ;
2298+ if ( email && accountFilter . emails . has ( email ) ) return "match" ;
2299+ return "mismatch" ;
2300+ }
2301+
2302+ function filterRowsByAccount ( rows , accountFilter ) {
2303+ if ( ! accountFilter ) return rows ;
2304+ const parsed = rows . map ( ( row ) => {
2305+ const fields = parseLogKeyValues ( row ?. feedback_log_body ) ;
2306+ return {
2307+ row,
2308+ accountId : fields [ "user.account_id" ] || null ,
2309+ email : fields [ "user.email" ] || null ,
2310+ } ;
2311+ } ) ;
2312+ const hasTaggedRows = parsed . some ( ( entry ) => entry . accountId || entry . email ) ;
2313+ if ( ! hasTaggedRows ) return rows ;
2314+ return parsed
2315+ . filter ( ( entry ) => responseEventAccountMatch ( entry , accountFilter ) === "match" )
2316+ . map ( ( entry ) => entry . row ) ;
2317+ }
2318+
22692319function responseCompletedEventFromLogRow ( row ) {
22702320 const message = extractCodexLogMessage ( row ?. feedback_log_body ) ;
22712321 if ( message ?. type === "response.completed" && message . response ?. usage ) {
@@ -2296,6 +2346,8 @@ function responseCompletedEventFromLogRow(row) {
22962346 serviceTier,
22972347 tokenUsage,
22982348 signature,
2349+ accountId : null ,
2350+ email : null ,
22992351 } ;
23002352 }
23012353
@@ -2312,8 +2364,10 @@ function responseCompletedEventFromLogRow(row) {
23122364 const outputTokens = numberField ( fields , "output_token_count" ) ;
23132365 const cachedInputTokens = numberField ( fields , "cached_token_count" ) ;
23142366 const reasoningOutputTokens = numberField ( fields , "reasoning_token_count" ) ;
2367+ const toolTokenCount = numberField ( fields , "tool_token_count" ) ;
23152368 const totalTokens = Math . max ( 0 , inputTokens + outputTokens ) ;
2316- if ( totalTokens <= 0 ) return null ;
2369+ const eventTotalTokens = totalTokens > 0 ? totalTokens : toolTokenCount ;
2370+ if ( eventTotalTokens <= 0 ) return null ;
23172371
23182372 const conversationId = fields [ "conversation.id" ] || row ?. thread_id || `sqlite-row-${ row ?. id ?? ms } ` ;
23192373 const model = fields . slug || fields . model || null ;
@@ -2330,7 +2384,7 @@ function responseCompletedEventFromLogRow(row) {
23302384 cachedInputTokens,
23312385 outputTokens,
23322386 reasoningOutputTokens,
2333- totalTokens,
2387+ totalTokens : eventTotalTokens ,
23342388 } ,
23352389 signature : [
23362390 Math . floor ( ms / 1000 ) ,
@@ -2340,6 +2394,8 @@ function responseCompletedEventFromLogRow(row) {
23402394 cachedInputTokens ,
23412395 reasoningOutputTokens ,
23422396 ] . join ( "|" ) ,
2397+ accountId : fields [ "user.account_id" ] || null ,
2398+ email : fields [ "user.email" ] || null ,
23432399 } ;
23442400}
23452401
@@ -2354,11 +2410,16 @@ async function readSqliteResponseCompletedEvents(options = {}) {
23542410
23552411 const sinceMs = Number . isFinite ( options . sinceMs ) ? options . sinceMs : Date . now ( ) - 6 * 60 * 60 * 1000 ;
23562412 const sinceSeconds = Math . max ( 0 , Math . floor ( ( sinceMs - 5 * 60 * 1000 ) / 1000 ) ) ;
2413+ const accountFilter = normalizeAccountFilter ( options . accountIdentity ) ;
2414+ const accountCacheKey = accountFilter
2415+ ? `${ Array . from ( accountFilter . ids ) . join ( "," ) } |${ Array . from ( accountFilter . emails ) . join ( "," ) } `
2416+ : "all" ;
23572417 const cacheKey = [
23582418 await fileCachePart ( logsDbPath ( ) ) ,
23592419 await fileCachePart ( logsDbWalPath ( ) ) ,
23602420 await fileCachePart ( logsDbShmPath ( ) ) ,
23612421 sinceSeconds ,
2422+ accountCacheKey ,
23622423 ] . join ( ":" ) ;
23632424 if ( sqliteResponseEventCache . has ( cacheKey ) ) return sqliteResponseEventCache . get ( cacheKey ) ;
23642425
@@ -2382,11 +2443,24 @@ async function readSqliteResponseCompletedEvents(options = {}) {
23822443 )
23832444 . all ( sinceSeconds ) ;
23842445
2385- const seen = new Set ( ) ;
2386- const rawEvents = [ ] ;
2446+ const parsedEvents = [ ] ;
2447+ let hasAccountTaggedEvents = false ;
23872448 for ( const row of rows ) {
23882449 const event = responseCompletedEventFromLogRow ( row ) ;
2389- if ( ! event || seen . has ( event . signature ) ) continue ;
2450+ if ( ! event ) continue ;
2451+ if ( event . accountId || event . email ) hasAccountTaggedEvents = true ;
2452+ parsedEvents . push ( event ) ;
2453+ }
2454+
2455+ const filteredEvents =
2456+ accountFilter && hasAccountTaggedEvents
2457+ ? parsedEvents . filter ( ( event ) => responseEventAccountMatch ( event , accountFilter ) === "match" )
2458+ : parsedEvents ;
2459+
2460+ const seen = new Set ( ) ;
2461+ const rawEvents = [ ] ;
2462+ for ( const event of filteredEvents ) {
2463+ if ( seen . has ( event . signature ) ) continue ;
23902464 seen . add ( event . signature ) ;
23912465 rawEvents . push ( event ) ;
23922466 }
@@ -2778,7 +2852,10 @@ async function readQuotaEstimate(options = {}) {
27782852
27792853 const sqliteSinceMs =
27802854 effectiveSinceMs && Number . isFinite ( effectiveSinceMs ) ? Math . min ( effectiveSinceMs , earliestBaseMs ) : earliestBaseMs ;
2781- const sqliteEvents = await readSqliteResponseCompletedEvents ( { sinceMs : sqliteSinceMs } ) ;
2855+ const sqliteEvents = await readSqliteResponseCompletedEvents ( {
2856+ sinceMs : sqliteSinceMs ,
2857+ accountIdentity : options . accountIdentity ,
2858+ } ) ;
27822859 const events = [ ...sessionEvents , ...sqliteEvents ] ;
27832860 if ( ! events . length ) return quotaEstimateUnavailable ( "\u672a\u627e\u5230\u672c\u5730 token \u8bb0\u5f55" ) ;
27842861 events . sort ( ( a , b ) => a . ms - b . ms ) ;
@@ -2855,10 +2932,13 @@ async function readQuotaEstimate(options = {}) {
28552932 sessionDelta ?. weightedTokens >= 100
28562933 ? buildWindowEstimate ( baseQuota . session , sessionEstimateCoefficient , sessionDelta . weightedTokens , calibration . sessionSamples )
28572934 : null ;
2858- // Weekly quota moves slowly and local Codex logs normally include the latest
2859- // weekly rate_limit snapshot. Estimating between snapshots tends to be more
2860- // confusing than useful, so keep weekly display pinned to the observed value.
2861- const weeklyEstimate = null ;
2935+ const weeklyEstimateCoefficient = seededWeeklyDelta
2936+ ? Math . min ( tunedWeeklyCoefficient . coefficient , quotaCoefficientBounds ( baseQuota . planType , "weekly" ) . max )
2937+ : tunedWeeklyCoefficient . coefficient ;
2938+ const weeklyEstimate =
2939+ weeklyDelta ?. weightedTokens >= 500
2940+ ? buildWindowEstimate ( baseQuota . weekly , weeklyEstimateCoefficient , weeklyDelta . weightedTokens , calibration . weeklySamples )
2941+ : null ;
28622942
28632943 if ( ! sessionEstimate && ! weeklyEstimate ) return quotaEstimateUnavailable ( "\u7b49\u5f85\u5386\u53f2\u6821\u51c6\u6837\u672c" ) ;
28642944 return {
@@ -3268,7 +3348,7 @@ function updateLearningWindow(existing, sample) {
32683348 Number . isFinite ( actualDelta ) &&
32693349 actualDelta > 0 &&
32703350 ( predictedDelta > actualDelta * 2 || actualDelta > predictedDelta * 1.5 ) ;
3271- const sampleWeight = needsFastCorrection ? 0.55 : 0.2 ;
3351+ const sampleWeight = needsFastCorrection ? 0.7 : 0.35 ;
32723352 const coefficient = Number . isFinite ( previous ) && previous > 0
32733353 ? previous * ( 1 - sampleWeight ) + sample . coefficient * sampleWeight
32743354 : sample . coefficient ;
@@ -3357,9 +3437,9 @@ async function readBestLocalQuota(scope, files) {
33573437 return newerQuota (
33583438 newerQuota (
33593439 await readLatestLocalQuota ( { since : scope . since , files } ) ,
3360- await readLatestSqliteRateLimitQuota ( { since : scope . since } )
3440+ await readLatestSqliteRateLimitQuota ( { since : scope . since , accountIdentity : scope . account } )
33613441 ) ,
3362- await readLatestUsageLimitQuota ( { since : scope . since } )
3442+ await readLatestUsageLimitQuota ( { since : scope . since , accountIdentity : scope . account } )
33633443 ) ;
33643444}
33653445
@@ -3381,6 +3461,7 @@ async function resolveQuotaWithMode(scope, files, usage = null) {
33813461 files,
33823462 baseQuota,
33833463 calibration : scope . accountQuotaCalibration ,
3464+ accountIdentity : scope . account ,
33843465 } ) ;
33853466 const fallbackQuota = attachQuotaEstimate ( resolveQuota ( scope , baseQuota ) , quotaEstimate ) ;
33863467 return {
@@ -3397,6 +3478,7 @@ async function resolveQuotaWithMode(scope, files, usage = null) {
33973478 files,
33983479 baseQuota,
33993480 calibration : scope . accountQuotaCalibration ,
3481+ accountIdentity : scope . account ,
34003482 } ) ;
34013483 const resolvedQuota = attachQuotaEstimate ( resolveQuota ( scope , baseQuota ) , quotaEstimate ) ;
34023484 if ( latestQuota && scope . accountId ) {
0 commit comments