Files
OpenOPC/opc/plugins/office_ui/frontend_src/lib/contextUsage.ts
T
2026-07-01 17:56:31 +08:00

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 {}
}