pub fn prepend_bos<T: Copy + PartialEq>(tokens: &mut Vec<T>, bos: Option<T>)Expand description
Prepends the checkpoint’s BOS id to an already-encoded prompt, unless the prompt already starts with it.
§The rule, stated once
The chat template owns BOS when it prints one; the loader owns it otherwise. Which of the two happens is a property of the individual checkpoint, not of the family:
- Many upstream templates open with
{{ bos_token }}— gemma-2/3 (<bos>), Mistral-Instruct and TinyLlama (<s>), Llama-3 (<|begin_of_text|>). Rendering one of those already puts BOS in the text, and bothGgufBpeTokenizer::encodeandGgufSpmTokenizer::encodesplit on special-token text first, so it comes back as the BOS id in position 0. - Unsloth deliberately strips
{{ bos_token }}out of the templates it bakes into its GGUF exports, precisely so that a runtime which adds BOS itself does not double it. On those checkpoints the render carries no BOS and the loader must add it.
So neither “always add” nor “never add” is right, and a renderer
cannot be sniffed for which case it is. This function implements the
only rule that is correct for both: add the id, idempotently.
bos is already the gated value — pass None when
should_add_bos_token says this vocabulary does not take one
(BPE/qwen2 ship a bos_token_id they never prepend).
Note this is stricter than llama.cpp, whose add_special path
pushes BOS unconditionally and leaves the duplicate to a warning.
Ferrox has no user-visible “you asked for two BOS tokens” surface, so
it dedupes instead of warning.
Generic over the id width because the CLI and server carry prompts as
Vec<usize> and the tokenizers emit Vec<u32>.