npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tokenizer-core

v0.1.1

Published

Framework-agnostic LLM token counter — GLM/GPT/Qwen/DeepSeek exact, Claude/Gemini approx

Readme

tokenizer-core

Framework-agnostic LLM token counter — GLM/GPT/Qwen/DeepSeek exact, Claude/Gemini approx. No Obsidian or MCP coupling; usable from any Node or bundler context.

npm version License: MIT TypeScript

Count LLM tokens for text under a specific model's real tokenizer — so you can check whether content fits a context budget before sending it to a model. GLM, GPT, Qwen, and DeepSeek are counted exactly (HuggingFace tokenizer.json via @huggingface/tokenizers, or js-tiktoken); Claude and Gemini have no published offline tokenizer, so they use o200k_base × 1.15 and are returned with source: "approx" — never silently passed off as exact.

This is the engine behind llm-token-count (an Obsidian status-bar plugin) and a vault_count_tokens MCP tool. It has no Obsidian or MCP imports — use it from a CLI, a script, another plugin, or any bundler.

Install

npm install tokenizer-core

Quick start

import { countTokens, isApproxModel, listSupportedModels } from "tokenizer-core";

const result = await countTokens("Hello, world!", "glm-5.2", "/path/to/cache");
// => { tokens: 4, model: "glm-5.2", source: "exact", bytes: 13, chars: 13 }

isApproxModel("claude");   // true
isApproxModel("glm-5.2");  // false
listSupportedModels();
// => ["glm-5.2", "glm-5", "glm-4.6v-flash", "gpt-5", "gpt-4o", "gpt-4", "gpt-3.5", "qwen", "deepseek-v3.1", "claude", "gemini"]

API

countTokens(text, model, cacheDir) → Promise<CountResult>

  • text: string — the text to count.
  • model: string — one of listSupportedModels().
  • cacheDir: string — writable directory for caching HuggingFace tokenizer.json files (created if missing). The caller chooses the location (e.g. a plugin's .obsidian/plugins/<id>/tokenizers/ dir).

Returns { tokens, model, source, bytes, chars }. source is "exact" for HF/tiktoken models and "approx" for Claude/Gemini. Throws TokenizerLoadError on fetch failure — the caller should surface the error or fall back (the Obsidian plugins fall back to an approx count in the status bar).

isApproxModel(model) → boolean

true for claude and gemini; false for all exact-tokenizer models.

listSupportedModels() → string[]

The 11 supported model keys.

loadTokenizer(model, cacheDir) → Promise<Tokenizer | null>

Lower-level access to the loaded tokenizer (returns null for tiktoken/approx models, which don't use a Tokenizer instance). Useful if you need encode/decode directly.

Supported models

| Model | Source | Method | |---|---|---| | glm-5.2, glm-5, glm-4.6v-flash | exact | HuggingFace tokenizer.json (pinned SHA) | | gpt-5, gpt-4o | exact | js-tiktoken o200k_base | | gpt-4, gpt-3.5 | exact | js-tiktoken cl100k_base | | qwen, deepseek-v3.1 | exact | HuggingFace tokenizer.json (pinned SHA) | | claude, gemini | approx | o200k_base × 1.15 (labeled approx) |

GLM/Qwen/DeepSeek tokenizer SHAs are pinned to a specific HuggingFace commit (see src/registry.ts) to guarantee reproducible counts across machines and over time. A version bump is a registry edit + a new release of this package.

Why

GPT-based token counts are a poor proxy for GLM models: o200k_base over-counts pure Chinese by ~15–30% vs real GLM tokens, which is bad enough to break "does this fit in context?" decisions. This package loads each model's real tokenizer (HuggingFace for GLM/Qwen/DeepSeek, js-tiktoken for GPT) so the count is exact. For models with no published offline tokenizer (Claude, Gemini), it returns an honest labeled approximation rather than a silent guess.

Implementation notes

  • Pure TypeScript, no Obsidian or MCP imports — the cache path is a parameter, never hardcoded.
  • @huggingface/tokenizers (pure JS port of the Rust tokenizers crate) + js-tiktoken are dependencies; the published dist/ keeps them external so the consumer's bundler resolves them.
  • Build: npm run build (esbuild → dist/index.js, tsc --emitDeclarationOnly → dist/*.d.ts). Test: npm test (Node's built-in test runner, no test framework dep).

License

MIT