dynamo-tokenizers
Efficient, versatile tokenization for LLM inference. Wraps Hugging Face, TikToken, fastokens, and Baseten Tokenizer backends behind a small encode/decode/sequence API designed for streaming detokenization.
Features
- Multiple backends. Hugging Face
tokenizers, OpenAItiktoken,fastokens, andbasetenkenizerbehind one trait. - Streaming-friendly.
Sequencetracks incremental token-id appends and emits text deltas without re-decoding the full prefix. - Prefix caching.
CachedTokenizerrecords prefix tokenizations at special-token boundaries; repeated prompts that share a system prefix re-encode only the trailing suffix, turning O(N) work into O(suffix_len). - Hash verification. Detect tokenizer drift across model versions.
Segmented prompts
BasetenTokenizer supports segmented encoding for renderers such as Kimi K3's
XTML renderer, where trusted control tokens and untrusted message content must
remain distinct:
Many Kimi model repositories ship tiktoken assets rather than a directly
loadable tokenizer.json. Baseten publishes compatible tokenizer.json
artifacts for these models; for Kimi K3, use
baseten/kimi-k3-tokenizer.
Download the file and pass its path to BasetenTokenizer::from_file.
use ;
let tokenizer = from_file?;
let segments = ;
let encoding = tokenizer.encode_segments?;
Segmented encoding preserves legacy tiktoken chunk boundaries for long-input token-ID parity, as required by Kimi K3.
Quick start
use HuggingFaceTokenizer;
use ;
// tokenizer.json downloaded from any HuggingFace model repo
let tokenizer = from_file
.expect;
let encoding = tokenizer.encode
.expect;
println!;
let decoded = tokenizer.decode
.expect;
assert_eq!;
Streaming detokenization with Sequence
use ;
use Arc;
let tokenizer = from;
let mut sequence = new;
sequence.append_text
.expect;
// As each new token id is produced by the engine, append it
// and get back just the incremental text delta:
let delta = sequence.append_token_id
.expect;
Prefix caching with CachedTokenizer
Multi-turn chat workloads re-tokenize a large shared prefix (system prompt +
prior turns) on every request. CachedTokenizer wraps a compatible tokenizer and
caches prefix tokenizations at special-token boundaries (e.g. <|im_start|>,
<|im_end|>, <s>, </s>). On a hit it merges the cached prefix tokens with a
fresh encode of the trailing suffix only.
Boundaries are taken only immediately after a registered special token —
those are atomic in BPE, so the merge is exact:
encode(prefix) + encode(suffix) == encode(prefix + suffix). There is no
whitespace/punctuation fallback; the cache prefers a miss over a corrupt split.
use ;
use ;
use Arc;
let hf = from_file
.expect;
let inner: = new;
// The atomic special tokens the model uses as turn delimiters.
// An empty list disables caching: encode/encode_batch pass straight through.
let specials = vec!;
let cached = new
.expect; // 256 MiB budget
let encoding = cached.encode
.expect;
let stats = cached.cache_stats;
println!;
TikToken models expose the exact special-token strings registered with their BPE, so the same cache can be constructed without duplicating model metadata:
use ;
use Tokenizer;
use Arc;
let tiktoken = from_file_auto
.expect;
let specials = tiktoken.special_tokens.to_vec;
let inner: = new;
let cached = new
.expect;
Entries are evicted by approximate LRU once max_memory_bytes is exceeded. The
cache lives as long as the CachedTokenizer instance. Use .with_observer(...)
to push request-level hit/miss events into your metrics. Use
.with_token_observer(...) to receive exact cached and uncached token counts
after each successful encode while L1 is active. Partial hits report both
categories, so consumers can increment cached_tokens_total and
uncached_tokens_total counters and derive a token-level reuse ratio.