Skip to main content

hf_chat_template/
lib.rs

1//! Render Hugging Face `chat_template` (Jinja2) strings the way Python
2//! `transformers.apply_chat_template` does.
3//!
4//! This crate emits a **prompt string**. Turning it into token IDs is the caller's job
5//! (`tokenizers`, `tiktoken-rs`, …) — keeping that boundary is deliberate.
6//!
7//! ```
8//! use hf_chat_template::ChatTemplate;
9//! use minijinja::context;
10//!
11//! let tmpl = ChatTemplate::from_str(
12//!     "{% for m in messages %}<|{{ m.role }}|>{{ m.content }}\n{% endfor %}\
13//!      {% if add_generation_prompt %}<|assistant|>{% endif %}",
14//! ).unwrap();
15//!
16//! let out = tmpl.render_value(context! {
17//!     messages => vec![
18//!         context!{ role => "user", content => "hi" },
19//!     ],
20//!     add_generation_prompt => true,
21//! }).unwrap();
22//! assert_eq!(out, "<|user|>hi\n<|assistant|>");
23//! ```
24//!
25//! ## Special tokens & BOS doubling
26//! Templates that emit `{{ bos_token }}` expect you to pass `bos_token` in the context, and
27//! to set `add_special_tokens = false` at encode time so the tokenizer does not add BOS a
28//! second time. This crate renders exactly what the template says and never strips silently.
29
30#![forbid(unsafe_code)]
31#![deny(missing_docs)]
32
33mod clock;
34mod config;
35mod engine;
36mod error;
37#[cfg(feature = "hub")]
38mod hub;
39mod json;
40mod model;
41mod template;
42
43#[cfg(feature = "strftime")]
44pub use clock::LocalClock;
45pub use clock::{Clock, FixedClock, SystemClock};
46pub use config::{ChatTemplateField, NamedTemplate, TokenField, TokenizerConfig};
47pub use error::Error;
48pub use model::{Content, Message, RenderInput};
49pub use template::{ChatTemplate, ChatTemplateBuilder};
50
51/// Re-export of the `minijinja` we build against, so downstreams can construct context
52/// [`Value`](minijinja::Value)s without guessing a version. Note: `render_value` ties our
53/// public API to this `minijinja` major version.
54pub use minijinja;