Repository navigation
Conversation
Change-Id: Id42ddd71e5349f481dbd64a6cd647a4680896be9 Signed-off-by: Thomas Kosiewski <tk@coder.com>
… HF tokenizers Change-Id: I904ed1182063c82c2e0a35419f80d63a8850847b Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I0f96165f7fcc5fd63e7ee6e135ebd7cc8ee1bb35 Signed-off-by: Thomas Kosiewski <tk@coder.com>
…e new encodings Change-Id: Ia00eca10b223fc1d603ef4a0cd9cdd8ed1753987 Signed-off-by: Thomas Kosiewski <tk@coder.com>
…ment script Change-Id: I6c13862010fb202b3669a5eb0609e1fd09b2df73 Signed-off-by: Thomas Kosiewski <tk@coder.com>
ThomasK33
marked this pull request as ready for review
November 3, 2026 12:07
Member
Author
|
@codex review |
…mma4 Change-Id: I90a3902ca658a509914567620012e240dd0fba86 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Member
Author
|
@codex review |
…assignment Change-Id: Ib3b7010e069520d5500b30f1168209dd098fd535 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Member
Author
|
@codex review |
Change-Id: I43365d721c3cd3a9288c89b6eebb70587a3e9178 Signed-off-by: Thomas Kosiewski <tk@coder.com>
…7F in llama4, type large literals gemma4 exports kind, name and a codec, the shape PR #10 uses, so Tokenizer keeps one constructor path and no early return. llama4's contraction group now folds U+017F like the reference. merges and special_tokens carry a type, so the d.ts files stay small. SentencePieceTokenizer decodes extended special tokens. Change-Id: I11cb526e749c7945070c28c0d4e80aa16a5601ff Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ie21ae7b9e87cf646d4e6a61079ec8f62fc07dfd6 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Member
Author
|
Review record for head
Generated with |
3 tasks
…codings Change-Id: I79215a2aa1dcac516445c804abb8019f41dd2e86 Signed-off-by: Thomas Kosiewski <tk@coder.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The operator's goal for this library: "this library wasn't updated in a long while. It needs to get the new tokenizer for opus and for a few other models ... Please take it from here on autopilot and get this landed."
This PR adds exact offline token counts for two providers whose counts are wrong today.
o200k_base× 1.08. Google's Gemma 4 report says Gemma 4 uses the Gemini tokenizer, and Google's owngoogle-genaiSDK countsgemini-3.1andgemini-3.5with Gemma 4.This PR adds encodings and the engine that runs them. It does not change
models.json. A later PR maps models to encodings, using the table below.What
ai-tokenizer/encoding/llama4(200,000 tokens). It runs on the existingTokenizer.ai-tokenizer/encoding/gemma4(262,144 tokens, 514,906 merges) and a new engine insrc/sentencepiece.ts. Gemma 4 has no regex pre-split, and it merges by merge-list index. The existing engine cannot run that. The new engine uses a heap and a linked symbol list, so it takes O(n log n) time on a long run with no space.Tokenizertakes theCodecshape from PR feat(encoding): add claude_v3 and claude_v4_7 encodings ported from ctok #10. The gemma4 module exportskind = "sentencepiece",nameand acodecthat builds the engine on first use.Tokenizerdelegatesencodeanddecodeto it, andinstanceof Tokenizerholds. The tokenizer.ts hunks equal PR feat(encoding): add claude_v3 and claude_v4_7 encodings ported from ctok #10's except one change:Codec.encodealso takesallowedSpecialanddisallowedSpecial, so gemma4 keepsTokenizer's special-token rules.gemma4andllama4are both insrc/encoding/index.ts, sonew Tokenizer(encoding[model.encoding])typechecks.scripts/generate-hf-encodings.tswrites both modules from atokenizer.jsonpinned to a Hugging Face revision and a SHA-256. The raw files stay out of git. The script asserts every property the runtime relies on.fixtures/andsrc/hf-encodings.test.tscompare both encodings with Hugging Facetokenizerson 1,680 samples.scripts/compare-hf.tsruns the same comparison on any files.scripts/measure-gateway.tsruns the online check.Revisions: Gemma 4 is
google/gemma-4-E4B-itatee0ef602(ungated). Llama 4 is theunsloth/Llama-4-Scout-17B-16E-Instructmirror atafd8e498. Thetokenizer.jsonfor Maverick has the same SHA-256.Evidence
Exactness against Hugging Face
tokenizers0.23.3, token id by token id, on this head (PR #8 is merged):/usr/share/doc(152M chars)_, space,ab, 日, 😀, base64, minified JSON (14.8M chars, up to 3M per file)X'+ every code point, with 7 left contexts and suffixes (11.1M strings), and 8,554 words +'ſThe committed test runs 1,680 samples: gemma4 is 1680/1680 exact and llama4 is 1668/1668 exact with 12 long runs skipped (issue #6, PR #15). I mutated the engine and the pattern to check that the test can fail. Dropping the space mark, the stale-entry check or the tie rule fails gemma4 samples. Using JS
\s, dropping/, or dropping the U+017F fold from the llama4 pattern fails llama4 samples.Online check through
https://ai-gateway.vercel.sh/v1/chat/completions,max_tokens16, 11 samples per model (English, Chinese, Japanese, Arabic, Hindi, Russian, German, TypeScript, Python, JSON, emoji). Content tokens areprompt_tokens(sample) - prompt_tokens("a") + 1. The ratio is content tokens divided by the offline count.o200k_basetotal ratioflash,pro,flash-lite) matches on all 14. Gemini 3 to 3.8 drops each\r, drops leading newlines, and counts a literal U+2581 as 3 byte tokens. Llama 4 Scout trims leading and trailing whitespace of message content. The README states this. The mapping PR can apply the cleanup per model before it counts.<unusedN>ids. My engine gives equal counts for both on all fixture samples and on 99 articles (2,588,778 tokens each). Onegemma4encoding coversgemini-2.5-*,gemini-3-*andgemini-3.1to3.8. The oldo200k_base× 1.08 is off by +6.1% on this set.gemini-4-argonis a different tokenizer. Keep it ono200k_basefor now."a"costs 1 token. Llama 4 Scout adds 10 tokens (a lone"a"costs 11).usage.costsummed to about $0.17 over all runs.Size
src/encoding/llama4.tssrc/encoding/gemma4.tssrc/encoding/o200k_base.ts(for scale)npm packon this head: tarball 27.0 MB, unpacked 69.2 MB, 107 files (main before PR #10 and this PR: 12.5 MB tarball).dist/index.d.tsis 68 KB anddist/sdk.d.tsis 70 KB.mergesandspecial_tokenscarry a type annotation, so tsdown does not copy their literals into the.d.tsfiles. An install of the tarball runs both encodings throughnew Tokenizer(...)under Node ESM and CJS, andtscaccepts the README patternnew Tokenizer(encoding[model.encoding]).Gates on the pushed head:
bun run format --check,bun run typecheck,bun test(618 pass, 0 fail, including the 429 Claude tests),bun run build --dts=falseandbun run build.Risks
Codecinterface. PR feat(encoding): add claude_v3 and claude_v4_7 encodings ported from ctok #10 is merged. This head mergesmain, andCodec.encodekeeps the two optional special-token parameters. The ctok codecs from PR feat(encoding): add claude_v3 and claude_v4_7 encodings ported from ctok #10 stay assignable to it.src/encoding/index.tsandpackage.jsonlist all of the Claude encodings plusgemma4andllama4.new Tokenizer(gemma4, { "<x>": id })throwsgemma4 does not support special tokens. The engine class itself decodes extended tokens.o200k_basehas the same fold gap. The reference regex(?i:'s)folds U+017F, and the repo's'[sS]does not.o200k_basegives 3 tokens where tiktoken gives 2 for" I'ſ"(also for" I'ſe"and" I'ſl").cl100k_baseshows no difference over the same scan. I did not change those encodings in this PR.tokenizer.jsonmatches Scout's byte for byte.<|begin_of_text|>hi<|end_of_text|>, wherecountgives 13 andencode(text, "all", [])gives 3). Gemini 3.5 does not read<bos>or<eos>as special tokens.counttreats such text as ordinary text, as PR fix: count special-token text as ordinary text #9 does. The README says how to match Scout.Generated with
xum• Model:anthropic:claude-sonnet-5-5• Thinking:xhigh