Skip to content

qwen 3.5 support and generalized conversion script - #381

Closed
kylegrover wants to merge 3 commits into
zspacelabs:mainfrom
kylegrover:kylegrover/qwen-3.5-rebased
Closed

qwen 3.5 support and generalized conversion script#381
kylegrover wants to merge 3 commits into
zspacelabs:mainfrom
kylegrover:kylegrover/qwen-3.5-rebased

Conversation

@kylegrover

Copy link
Copy Markdown

I wanted to try this out with Qwen 3.5 in a custom setup and this is what I ended up with. Leaving this as a draft until I have more time to review, let me know if it looks interesting or like it's completely missing the plot of how pretrained should be added

…tools

Implements a high-performance Qwen3.5 tokenization path, achieving up to
~8x faster single-string encoding than tiktoken via a custom Logos lexer.

Core Changes:
- Added QWEN35_PATTERN and regex-automata variant to pretrained/openai/patterns.rs.
- Implemented Qwen35Token Logos DFA lexer in spanners/span_lexers/logos/qwen35.rs.
  - Handles Letter/Mark runs, single-char digits, and greedy newline absorption.
  - Delegates contraction splitting to Gpt2Family post-processing.
- Registered Qwen3.5 in regex_automata.rs KNOWN_TRANSFORMS for fallback support.
- Relaxed UnifiedTokenVocab subset checks to support pruned vocabularies like Qwen.
- Added grounding logic to span_vocab.rs to handle non-decomposable BPE tokens.

Python Bindings & Tooling:
- Added Tokenizer.from_tiktoken_file() for loading custom BPE vocabularies.
- Added SpecialFilter API to control special token recognition during encoding.
- Included hf_to_tiktoken.py utility for converting HF tokenizer.json to .tiktoken.
- Added comprehensive Qwen3.5 benchmarks and unit tests for SpecialFilter.
- Updated documentation and book with custom model loading guides.
This introduces a generic custom vocabulary path (converter and loader)
for non-OpenAI models, using Qwen 3.5 as the initial proof-of-concept.

Core additions & fixes:
- Implement custom `.tiktoken` loader and Python `from_tiktoken_file` API.
- Fix Python API bug where `options=None` failed to pass to the Rust binding.
- Add feature-gated Qwen 3.5 lexer and reference matchers.
- Add synthetic custom-loader tests to Python CI to validate the generic
  path without requiring giant 100MB+ model assets.
- Add Rust regression tests locking in the "pair vocab may be a subset of
  span vocab" behavior.
- Fix compilation error in `qwen35.rs` under the `testing` feature by
  using a `no_std`-safe string conversion.
- Fix Python lockfile resolution for `>=3.9` by removing a stray `black`
  dev dependency.
- Fix existing Rust doctest failures related to the `download` feature.
- Update `.gitignore` to exclude generated `.tiktoken`/`.json` assets and
  Windows `.pyd` build artifacts.
@codecov

codecov Bot commented Apr 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.05882% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...rdchipper/src/spanners/span_lexers/logos/qwen35.rs 97.10% 4 Missing ⚠️
crates/wordchipper/src/vocab/unified_vocab.rs 92.30% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want this functionality; but I want it at the rust level.

  • this could be a specialized json format loader.
  • this could be something that loads through the tokenizers library, and then converts the vocab in-memory.

I want to avoid python-only features; as this is a rust-first crate.

/// The Qwen3.5 pretrained vocabulary word pattern.
///
/// Shared by all Qwen3.5 model sizes (0.6B–72B).
pub const QWEN35_PATTERN: ConstRegexPattern = ConstRegexPattern::Fancy(join_patterns!(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be in the openai module.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be pulled into a stand-alone PR

Comment thread .gitignore
.pytest_cache

# Local custom-model conversion artifacts
bindings/python/qwen3.5.tiktoken

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should not be polluting the tree. If you have to generate intermediate artifacts; they should be in "target/"; or managed in tmp or a cache directory directly.

@crutcher

Copy link
Copy Markdown
Member

Being able to load a HF module via named path hf:${HFUSER}/${HFMODEL} is a goal. I had planned on having an optional feature dep on tokenizers at the rust crate level; and when provided, registering an additional lookup handler which would match by schema, use tokenizers to pull the model, and check to see if a fast bpe tokenizer was constructed; then convert it over to a wordchipper one.

@crutcher

Copy link
Copy Markdown
Member

See: #377

@kylegrover

Copy link
Copy Markdown
Author

#382 is looking pretty close, I'll just start a new branch from there to pull in any other ideas from this, will PR if it seems useful

@kylegrover kylegrover closed this May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants