Skip to main content
Glama
token-count.tsβ€’3.24 kB
/** * Lazy-loaded tiktoken types to avoid bundling WASM at module load time */ type TiktokenEncoding = 'cl100k_base' | 'p50k_base' | 'r50k_base' | 'gpt2'; type TiktokenModel = string; const DEFAULT_MODEL = 'claude-sonnet-4-5'; const DEFAULT_ENCODING: TiktokenEncoding = 'cl100k_base'; const MODEL_TO_ENCODING: Record<string, TiktokenEncoding> = { 'claude-sonnet-4-5': DEFAULT_ENCODING, 'claude-3-7-sonnet-20250219': DEFAULT_ENCODING, 'claude-3-5-sonnet-20240620': DEFAULT_ENCODING, 'claude-3-opus-20240229': DEFAULT_ENCODING, 'claude-3-sonnet-20240229': DEFAULT_ENCODING, 'claude-3-haiku-20240307': DEFAULT_ENCODING, 'gpt-4o': DEFAULT_ENCODING, 'gpt-4o-mini': DEFAULT_ENCODING, 'gpt-4-turbo': DEFAULT_ENCODING, 'gpt-3.5-turbo': DEFAULT_ENCODING, }; /** * Cache for loaded tiktoken library (lazy loaded) */ let tiktokenLib: typeof import('@dqbd/tiktoken') | null | 'unavailable' = null; /** * Encoder cache for when tiktoken is available */ const encoderCache = new Map<string, any>(); /** * Fallback token estimation (industry standard: ~4 chars per token) */ function estimateTokens(text: string): number { return Math.ceil(text.length / 4); } /** * Lazy load tiktoken library (handles WASM bundling issues gracefully) */ async function loadTiktoken(): Promise<typeof import('@dqbd/tiktoken') | null> { if (tiktokenLib === 'unavailable') { return null; } if (tiktokenLib !== null) { return tiktokenLib; } try { tiktokenLib = await import('@dqbd/tiktoken'); return tiktokenLib; } catch (error) { // Mark as unavailable to avoid repeated import attempts tiktokenLib = 'unavailable'; // Silent fallback - estimation will be used return null; } } function resolveEncoding(model: string): TiktokenEncoding { return MODEL_TO_ENCODING[model] ?? DEFAULT_ENCODING; } function getModelName(model?: string): string { return model?.trim() || process.env.COUNT_MODEL_DEFAULT || DEFAULT_MODEL; } async function getEncoder(model?: string) { const tiktoken = await loadTiktoken(); if (!tiktoken) { return null; // Fallback to estimation } const modelName = getModelName(model); const resolvedEncoding = resolveEncoding(modelName); if (encoderCache.has(resolvedEncoding)) { return encoderCache.get(resolvedEncoding)!; } const encoder = (() => { try { return tiktoken.encoding_for_model(modelName as any); } catch { return tiktoken.get_encoding(resolvedEncoding); } })(); encoderCache.set(resolvedEncoding, encoder); return encoder; } export async function countTokens( text: string, model?: string ): Promise<number> { const encoder = await getEncoder(model); if (!encoder) { return estimateTokens(text); } return encoder.encode(text).length; } export async function countJsonTokens( value: unknown, model?: string ): Promise<number> { const serialized = JSON.stringify(value); return countTokens(serialized, model); } export async function countStrings( tokens: string[], model?: string ): Promise<number> { if (!tokens.length) { return 0; } return countTokens(tokens.join('\n'), model); } export function getCountModel(): string { return getModelName(); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server