SIGN IN SIGN UP

🚨 Refactor a bit add_tokens logic: fix bytelevel decode of added tokens + less memory deserialization 🚨 (#1995)

* we don't need  the model....

* fmt

* nit

* better version?

* test + the only viable fix

* up

* fmt

* nit

* fix

* fmt + clippy

* update the added vocab bench to reflect real world usecases

* big update, squeeze even more perfs

* profile exampl.e

* fmt

* bench: reduce normalizers to 2 (none, nfkc) for faster runs

* bench: fix sample_size to 10 (criterion minimum)

* bench: reduce to 100k tokens only for practical CI runtime

* perf: remove daachorse, keep algorithmic improvements using aho-corasick

The daachorse library swap is now isolated in PR #1999. This restores
aho-corasick while keeping all the algorithmic improvements:
- refresh_added_tokens uses added_tokens_map_r directly (no token_to_id loop)
- MatchingSet is Option<(AhoCorasick, Vec<u32>)> for empty-trie fast-path
- AddedVocabulary no longer maintains redundant added_tokens/special_tokens Vecs

* mut not required

* just fmt

* nits

* fix: single-pass partition in refresh_added_tokens, fix add_tokens signature

Map to (content, id, normalized) in one pass then partition, dropping
the boolean by destructuring. Also fix truncation_benchmark add_tokens
call to pass owned values as required by the iterator bound.

* proper fix

* nits

* update

* fix: targeted property check for duplicate token skip, simplify trie errors

Skip token only when content AND (normalized, lstrip, rstrip, special) match
an existing entry — avoids full struct equality. Replace .expect() with
.unwrap() on trie build since duplicates/empties are structurally impossible.

* current changes

* fix serialzie

* fmt + fix test

* refactor: proper error propagation, remove double iteration, use PartialEq

- Replace .unwrap()/.expect() with ? in add_tokens, refresh_normalized_tokens,
  refresh_added_tokens, and with_normalizer (return Result instead of panicking)
- Remove seed_normalized_cache and double iteration during deserialization;
  let add_tokens compute normalization directly
- Use PartialEq for duplicate token comparison instead of manual field checks
  (also fixes missing single_word comparison)
- Update all callers in tests, benchmarks, examples, and Python/Node bindings

* refactor: use ToPyResult for cleaner error conversion in Python bindings

* up?

* patch

* order

* ci: pin macOS Python to 3.13 (3.14 breaks abi3 linking)

* ci: add -undefined dynamic_lookup for macOS abi3 cross-compilation linking

* fix: rustfmt

* fix: remove missing profile_added_vocab_deserialize example from Cargo.toml

* fix: rustfmt node bindings

* fix: ci_benchmark add_tokens signature compat

* fix: clippy unused Result warning

* fix: support TOKENIZERS_DATA_DIR env var in Python benchmarks

* fix: don't serialize normalized_cache — always recompute from normalizer

The normalized cache is a derived value from content + normalizer.
Serializing it is wrong because the normalizer may have changed since
the file was saved. The cache is always rebuilt during add_tokens()
using the active normalizer.

* Update tokenizers/Cargo.toml

* refactor: add_special_tokens takes impl IntoIterator<Item = AddedToken>

Consistent with add_tokens — consumes tokens instead of borrowing,
avoiding clones in the internal forwarding call.

* Apply suggestion from @ArthurZucker
A
Arthur committed
d863e6e514b1cb022d89e340da4499ae6a7407f5
Parent: 51a6e82
Committed by GitHub <noreply@github.com> on 4/10/2026, 2:52:34 PM