mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-11-11 03:24:30 +00:00
feat: support external Hugging Face tokenizer JSON files (#1973)
This commit is contained in:
1 parent
42d6c0ab92
commit
4964abdfc5
58 files changed
+97390
-180
No files matched your search
@@ -0,0 +1,97 @@
|
||||
# JSON tokenizers
|
||||
|
||||
Use a Hugging Face `tokenizer.json` to supply the tokenizer vocabulary, merges,
|
||||
added tokens, and processing stages. Without this option, sd.cpp keeps its
|
||||
embedded tokenizer for the selected model.
|
||||
|
||||
```shell
|
||||
sd-cli --diffusion-model model.gguf --llm text_encoder.gguf \
|
||||
--tokenizer tokenizer_gemma2.json --vae vae.safetensors -p "a cat"
|
||||
```
|
||||
|
||||
Choose the JSON belonging to the text encoder checkpoint. Checking that IDs fit
|
||||
the embedding table does not establish that two vocabularies have the same
|
||||
meaning. The JSON file is loaded when the text encoder is created; its embedded
|
||||
vocabulary is not loaded in this case.
|
||||
|
||||
| Option | Encoder |
|
||||
| --- | --- |
|
||||
| `--tokenizer FILE` | Main LLM/BPE encoder: Gemma 2, Gemma 3, Qwen 2/3, Mistral, GPT-OSS; also Anima and HiDream-O1 |
|
||||
| `--tokenizer FILE` | Shared CLIP tokenizer in SD1/SD2/SDXL, or CLIP-L in Flux |
|
||||
| `--tokenizer clip-l=FILE` | Separate CLIP-L in SD3 or Flux |
|
||||
| `--tokenizer clip-g=FILE` | Separate CLIP-G in SD3 |
|
||||
|
||||
Use comma-separated assignments to configure multiple slots, for example
|
||||
`--tokenizer main=main.json,clip-l=clip_l.json,clip-g=clip_g.json`.
|
||||
A plain file path is equivalent to `main=FILE`. You may also repeat `--tokenizer`
|
||||
with explicit assignments, such as `--tokenizer main=main.json --tokenizer clip-l=clip.json`.
|
||||
Empty assignment paths, unknown keys, malformed assignments and
|
||||
duplicate slots are rejected. Commas separate entries in the assignment form;
|
||||
quote the complete argument when paths contain spaces.
|
||||
|
||||
SD3 overrides must name the `clip-l` or `clip-g` slot. SDXL uses one shared
|
||||
tokenizer for both CLIP encoders. Do not supply both `main` and `clip-l` for Flux.
|
||||
A slot targeting an absent or unsupported encoder fails initialization.
|
||||
T5/SentencePiece Unigram tokenizers are outside this implementation's scope.
|
||||
|
||||
For example, SD3 can load the same CLIP JSON into both slots:
|
||||
|
||||
```shell
|
||||
sd-cli --diffusion-model sd3.gguf --clip_l clip_l.safetensors \
|
||||
--clip_g clip_g.safetensors --t5xxl t5xxl.gguf --vae vae.safetensors \
|
||||
--tokenizer clip-l=tokenizer_clip.json,clip-g=tokenizer_clip.json \
|
||||
-p "a cat"
|
||||
```
|
||||
|
||||
The C API accepts the same string in `sd_ctx_params_t::tokenizer`. A null or
|
||||
empty value keeps the embedded tokenizers. The CLI passes the string through;
|
||||
`TokenizerConfig` parses and validates it when text encoders are initialized.
|
||||
|
||||
```c
|
||||
sd_ctx_params_t params;
|
||||
sd_ctx_params_init(¶ms);
|
||||
params.tokenizer = "clip-l=tokenizer_clip.json,clip-g=tokenizer_clip.json";
|
||||
```
|
||||
|
||||
Rebuild applications against the updated public header when using the updated
|
||||
library.
|
||||
|
||||
## Supported components
|
||||
|
||||
| Stage | Supported configurations |
|
||||
| --- | --- |
|
||||
| Normalizer | `Sequence`, `NFC`, `Lowercase`, `Replace` with String/Regex patterns |
|
||||
| PreTokenizer | `Sequence`, `Split` with String/Regex patterns, all five delimiter behaviors and `invert`; `ByteLevel` with `add_prefix_space` and `use_regex` |
|
||||
| Model | Deterministic `BPE`, string or array-pair merges, `unk_token`, `fuse_unk`, `byte_fallback`, `ignore_merges`, `end_of_word_suffix` |
|
||||
| PostProcessor | Single-sequence `TemplateProcessing` with at most one prefix and one suffix token, `RobertaProcessing`, `ByteLevel` |
|
||||
| Decoder | `Sequence`, `Replace`, `ByteLevel`, `ByteFallback`, `Fuse` |
|
||||
| AddedToken | Special and ordinary added tokens, original IDs, raw or normalized matching, leftmost-longest matching |
|
||||
|
||||
`ByteLevel.use_regex` defaults to true when omitted. ByteLevel postprocessing
|
||||
changes offsets only and adds no tokens. Added tokens with `single_word`,
|
||||
`lstrip`, or `rstrip` enabled, nonzero BPE dropout, nonempty
|
||||
`continuing_subword_prefix`, and unsupported component types fail loading.
|
||||
New added-token IDs must follow the model vocabulary consecutively; configurations
|
||||
whose IDs Hugging Face would reassign are rejected.
|
||||
JSON `padding` and `truncation` must be null. This API returns IDs, not offsets,
|
||||
type IDs, or paired-input encodings; the pair template is not used.
|
||||
|
||||
The pipeline covers the CLIP, Gemma 2, Gemma 3, GPT-OSS, Mistral 3, Qwen 2 and
|
||||
Qwen 3 JSON configurations used by the differential test. It does not imply
|
||||
support for every tokenizer published under those model names.
|
||||
|
||||
## Prompt integration
|
||||
|
||||
Prompt attention parsing and model-specific chat/image templates remain in the
|
||||
conditioner. Raw `encode()` does not add BOS/EOS. The conditioner concatenates
|
||||
weighted prompt fragments, then the existing padding/chunking step applies the
|
||||
JSON single-sequence template once per sequence or CLIP chunk. Padding ID,
|
||||
direction, length limits and attention masks remain text encoder policies.
|
||||
CLIP requires both BOS and EOS because its chunking reserves those positions.
|
||||
|
||||
The internal `encode()`, `tokenize()`, and `decode()` interfaces return a success
|
||||
flag and write to an output parameter. A successful result may be empty; a failed
|
||||
call clears its output. JSON tokenizer input, normalization, and regex failures
|
||||
return `false` with diagnostic information instead of throwing. Invalid
|
||||
JSON, unsupported stages, conflicting IDs and IDs outside the encoder embedding
|
||||
table fail initialization instead of falling back to the embedded tokenizer.
|
||||
Reference in new issue
Block a user