82 lines
2.6 KiB
TypeScript
82 lines
2.6 KiB
TypeScript
export interface ContextUsageLike {
|
|
contextTokens?: number | null
|
|
contextWindow?: number | null
|
|
contextRemainingPct?: number | null
|
|
}
|
|
|
|
export interface ContextUsageMetrics {
|
|
usedPct?: number
|
|
remainingPct?: number
|
|
usedTokens?: number
|
|
windowTokens?: number
|
|
}
|
|
|
|
function asFiniteNumber(value: number | null | undefined): number | undefined {
|
|
if (typeof value !== 'number' || !Number.isFinite(value)) return undefined
|
|
return value
|
|
}
|
|
|
|
function clampPct(value: number): number {
|
|
return Math.max(0, Math.min(Math.round(value), 100))
|
|
}
|
|
|
|
export function getContextUsageMetrics(
|
|
value: ContextUsageLike | null | undefined,
|
|
): ContextUsageMetrics {
|
|
const contextTokens = asFiniteNumber(value?.contextTokens)
|
|
const contextWindow = asFiniteNumber(value?.contextWindow)
|
|
const contextRemainingPct = asFiniteNumber(value?.contextRemainingPct)
|
|
|
|
const normalizedTokens = typeof contextTokens === 'number' ? Math.max(0, Math.round(contextTokens)) : undefined
|
|
const normalizedWindow = typeof contextWindow === 'number' && contextWindow > 0
|
|
? Math.max(1, Math.round(contextWindow))
|
|
: undefined
|
|
const normalizedRemainingPct = typeof contextRemainingPct === 'number'
|
|
? clampPct(contextRemainingPct)
|
|
: undefined
|
|
|
|
if (typeof normalizedTokens === 'number' && typeof normalizedWindow === 'number') {
|
|
const usedTokens = Math.min(normalizedTokens, normalizedWindow)
|
|
const usedPct = clampPct((usedTokens / normalizedWindow) * 100)
|
|
return {
|
|
usedPct,
|
|
remainingPct: 100 - usedPct,
|
|
usedTokens,
|
|
windowTokens: normalizedWindow,
|
|
}
|
|
}
|
|
|
|
if (typeof normalizedRemainingPct === 'number' && typeof normalizedWindow === 'number') {
|
|
const usedPct = 100 - normalizedRemainingPct
|
|
return {
|
|
usedPct,
|
|
remainingPct: normalizedRemainingPct,
|
|
usedTokens: Math.round((usedPct / 100) * normalizedWindow),
|
|
windowTokens: normalizedWindow,
|
|
}
|
|
}
|
|
|
|
if (typeof normalizedWindow === 'number') {
|
|
return {
|
|
usedPct: 0,
|
|
remainingPct: 100,
|
|
usedTokens: 0,
|
|
windowTokens: normalizedWindow,
|
|
}
|
|
}
|
|
|
|
if (typeof normalizedRemainingPct === 'number') {
|
|
// No usable window: the used/max ratio is undefined. Upstream reports an
|
|
// unknown window as remaining_pct=0, so deriving usedPct here would render
|
|
// a misleading 100% (or 0% at turn start). Surface remaining for any text
|
|
// display but do not drive the ring — the ring hides without a usedPct.
|
|
return { remainingPct: normalizedRemainingPct }
|
|
}
|
|
|
|
if (typeof normalizedTokens === 'number') {
|
|
return { usedTokens: normalizedTokens }
|
|
}
|
|
|
|
return {}
|
|
}
|