mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-16 15:38:10 +01:00
✨ feat: Add token usage indicator to chat input
Add TokenUsageIndicator component with circular progress ring Create useTokenUsage hook with Jotai atom for state Add model context window lookups to data-provider Consolidate token utilities (output limits, TOKEN_DEFAULTS) Display input/output tokens and percentage of context used
This commit is contained in:
parent
dc489e7b25
commit
af2958fcba
11 changed files with 710 additions and 32 deletions
87
client/src/components/Chat/Input/TokenUsageIndicator.tsx
Normal file
87
client/src/components/Chat/Input/TokenUsageIndicator.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import { memo } from 'react';
|
||||
import { TooltipAnchor } from '@librechat/client';
|
||||
import { useTokenUsage } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
function formatTokens(n: number): string {
|
||||
if (n >= 1000000) {
|
||||
return `${(n / 1000000).toFixed(1)}M`;
|
||||
}
|
||||
if (n >= 1000) {
|
||||
return `${(n / 1000).toFixed(1)}K`;
|
||||
}
|
||||
return n.toString();
|
||||
}
|
||||
|
||||
const TokenUsageIndicator = memo(function TokenUsageIndicator() {
|
||||
const { inputTokens, outputTokens, maxContext } = useTokenUsage();
|
||||
|
||||
const totalUsed = inputTokens + outputTokens;
|
||||
const hasMaxContext = maxContext !== null && maxContext > 0;
|
||||
const percentage = hasMaxContext ? Math.min((totalUsed / maxContext) * 100, 100) : 0;
|
||||
|
||||
// Ring calculations
|
||||
const size = 28;
|
||||
const strokeWidth = 2.5;
|
||||
const radius = (size - strokeWidth) / 2;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
const offset = circumference - (percentage / 100) * circumference;
|
||||
|
||||
const tooltipText = hasMaxContext
|
||||
? `Input: ${formatTokens(inputTokens)} | Output: ${formatTokens(outputTokens)} | Max: ${formatTokens(maxContext)}`
|
||||
: `Input: ${formatTokens(inputTokens)} | Output: ${formatTokens(outputTokens)} | Max: N/A`;
|
||||
|
||||
// Color based on percentage
|
||||
const getProgressColor = () => {
|
||||
if (!hasMaxContext) {
|
||||
return 'stroke-text-secondary';
|
||||
}
|
||||
if (percentage > 90) {
|
||||
return 'stroke-red-500';
|
||||
}
|
||||
if (percentage > 75) {
|
||||
return 'stroke-yellow-500';
|
||||
}
|
||||
return 'stroke-green-500';
|
||||
};
|
||||
|
||||
return (
|
||||
<TooltipAnchor
|
||||
description={tooltipText}
|
||||
render={
|
||||
<div className="flex size-9 items-center justify-center rounded-full p-1 transition-colors hover:bg-surface-hover">
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox={`0 0 ${size} ${size}`}
|
||||
className="rotate-[-90deg]"
|
||||
>
|
||||
{/* Background ring */}
|
||||
<circle
|
||||
cx={size / 2}
|
||||
cy={size / 2}
|
||||
r={radius}
|
||||
fill="transparent"
|
||||
strokeWidth={strokeWidth}
|
||||
className="stroke-border-medium"
|
||||
/>
|
||||
{/* Progress ring */}
|
||||
<circle
|
||||
cx={size / 2}
|
||||
cy={size / 2}
|
||||
r={radius}
|
||||
fill="transparent"
|
||||
strokeWidth={strokeWidth}
|
||||
strokeDasharray={circumference}
|
||||
strokeDashoffset={hasMaxContext ? offset : circumference}
|
||||
strokeLinecap="round"
|
||||
className={cn('transition-all duration-300', getProgressColor())}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default TokenUsageIndicator;
|
||||
Loading…
Add table
Add a link
Reference in a new issue