import { useTranslation } from "react-i18next"; import { I18nKey } from "#/i18n/declaration"; import { cn } from "#/utils/utils"; import { getContextWindowUsagePercentage } from "#/utils/format-token-count"; /** Context fill percentage above which the meter warns and the compact CTA becomes prominent. */ export const CONTEXT_FILL_WARNING_PERCENT = 70; /** Context fill percentage above which the meter signals danger. */ export const CONTEXT_FILL_DANGER_PERCENT = 90; export type ContextFillTone = "neutral" | "warning" | "danger"; export function getContextFillTone(usagePercentage: number): ContextFillTone { if (usagePercentage > CONTEXT_FILL_DANGER_PERCENT) { return "danger"; } if (usagePercentage > CONTEXT_FILL_WARNING_PERCENT) { return "warning"; } return "neutral"; } interface ContextMeterProps { /** Tokens currently held in the agent's context (`per_turn_token`). */ perTurnToken: number; /** Model context window size in tokens. */ contextWindow: number; } /** * Context-fill progress bar: neutral below * {@link CONTEXT_FILL_WARNING_PERCENT}, amber above it, red above * {@link CONTEXT_FILL_DANGER_PERCENT}. Shows the raw token numbers underneath. */ export function ContextMeter({ perTurnToken, contextWindow, }: ContextMeterProps) { const { t } = useTranslation("openhands"); // Some models (e.g. via OpenRouter) report no context window size; show the // raw token count without a misleading "x / 0" or "0.0% used" readout. const isWindowUnknown = contextWindow <= 0; // Capped at 100 by getContextWindowUsagePercentage, so the bar width and // the label never read above a full window. const usagePercentage = getContextWindowUsagePercentage( perTurnToken, contextWindow, ); const tone = getContextFillTone(usagePercentage); const isWarning = tone === "warning"; const isDanger = tone === "danger"; const roundedPercentage = Math.round(usagePercentage); const remainingPercentage = Math.max(0, 100 - roundedPercentage); const usagePercentLabel = isWindowUnknown ? t(I18nKey.CONVERSATION$CONTEXT_WINDOW_UNKNOWN) : `${roundedPercentage}% ${t(I18nKey.CONVERSATION$USED)} (${remainingPercentage}% ${t(I18nKey.CONVERSATION$LEFT)})`; return (
{t(I18nKey.CONVERSATION$CONTEXT_WINDOW)} {usagePercentLabel}
{isWindowUnknown ? perTurnToken.toLocaleString() : `${perTurnToken.toLocaleString()} / ${contextWindow.toLocaleString()}`}
); }