import { logger } from '@librechat/data-schemas'; import { Tokenizer as AiTokenizer } from 'ai-tokenizer'; export type EncodingName = 'o200k_base' | 'claude'; type EncodingData = ConstructorParameters[0]; class Tokenizer { private tokenizersCache: Partial> = {}; private loadingPromises: Partial>> = {}; /** Pre-loads an encoding so that subsequent getTokenCount calls are accurate. */ async initEncoding(encoding: EncodingName): Promise { if (this.tokenizersCache[encoding]) { return; } if (this.loadingPromises[encoding]) { return this.loadingPromises[encoding]; } this.loadingPromises[encoding] = (async () => { const data: EncodingData = encoding === 'claude' ? await import('ai-tokenizer/encoding/claude') : await import('ai-tokenizer/encoding/o200k_base'); this.tokenizersCache[encoding] = new AiTokenizer(data); })(); return this.loadingPromises[encoding]; } getTokenCount(text: string, encoding: EncodingName = 'o200k_base'): number { const tokenizer = this.tokenizersCache[encoding]; if (!tokenizer) { this.initEncoding(encoding); return Math.ceil(text.length / 4); } try { return tokenizer.count(text); } catch (error) { logger.error('[Tokenizer] Error getting token count:', error); delete this.tokenizersCache[encoding]; delete this.loadingPromises[encoding]; this.initEncoding(encoding); return Math.ceil(text.length / 4); } } } const TokenizerSingleton = new Tokenizer(); /** * Counts the number of tokens in a given text using ai-tokenizer with o200k_base encoding. * @param text - The text to count tokens in. Defaults to an empty string. * @returns The number of tokens in the provided text. */ export async function countTokens(text = ''): Promise { await TokenizerSingleton.initEncoding('o200k_base'); return TokenizerSingleton.getTokenCount(text, 'o200k_base'); } export default TokenizerSingleton;