pub const EOT: &str = "<turn|>";
pub fn conversation_start(system: Option<&str>, think: bool) -> String {
let mut s = String::from("<bos>");
if think || system.is_some() {
s.push_str("<|turn>system\n");
if think {
s.push_str("<|think|>\n");
}
if let Some(sys) = system {
s.push_str(sys.trim());
}
s.push_str("<turn|>\n");
}
s
}
pub fn user_turn(content: &str) -> String {
format!("<|turn>user\n{}<turn|>\n<|turn>model\n", content.trim())
}
pub fn end_of_model_turn() -> String {
format!("{EOT}\n")
}
pub trait ChatFormat {
fn conversation_start(&self, system: Option<&str>, think: bool) -> String;
fn user_turn(&self, content: &str, think: bool) -> String;
fn end_of_model_turn(&self) -> String;
fn end_of_turn_token(&self) -> &'static str;
fn think_ids(&self) -> (u32, u32);
fn starts_in_thinking(&self, think: bool) -> bool;
}
pub struct GemmaChat;
impl ChatFormat for GemmaChat {
fn conversation_start(&self, system: Option<&str>, think: bool) -> String {
conversation_start(system, think)
}
fn user_turn(&self, content: &str, _think: bool) -> String {
user_turn(content)
}
fn end_of_model_turn(&self) -> String {
end_of_model_turn()
}
fn end_of_turn_token(&self) -> &'static str {
EOT
}
fn think_ids(&self) -> (u32, u32) {
(100, 101)
}
fn starts_in_thinking(&self, _think: bool) -> bool {
false
}
}
pub struct QwenChat;
const QWEN_REASONING_XHIGH: &str = "Reasoning effort is set to xhigh. Please think carefully through the task, validate key assumptions, consider plausible alternatives, and prioritize correctness, consistency, and clarity in the final answer.";
impl ChatFormat for QwenChat {
fn conversation_start(&self, system: Option<&str>, think: bool) -> String {
let mut s = String::new();
match (think, system) {
(true, Some(sys)) => s.push_str(&format!("<|im_start|>system\n{QWEN_REASONING_XHIGH}\n\n{}<|im_end|>\n", sys.trim())),
(true, None) => s.push_str(&format!("<|im_start|>system\n{QWEN_REASONING_XHIGH}<|im_end|>\n")),
(false, Some(sys)) => s.push_str(&format!("<|im_start|>system\n{}<|im_end|>\n", sys.trim())),
(false, None) => {}
}
s
}
fn user_turn(&self, content: &str, think: bool) -> String {
let tail = if think { "<think>\n" } else { "<think>\n\n</think>\n\n" };
format!("<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n{tail}", content.trim())
}
fn end_of_model_turn(&self) -> String {
"<|im_end|>\n".to_string()
}
fn end_of_turn_token(&self) -> &'static str {
"<|im_end|>"
}
fn think_ids(&self) -> (u32, u32) {
(248068, 248069)
}
fn starts_in_thinking(&self, think: bool) -> bool {
think
}
}
pub fn with_turn_end(fmt: &dyn ChatFormat, tok: &tokenizers::Tokenizer, mut eos: Vec<u32>) -> Vec<u32> {
if let Some(id) = tok.token_to_id(fmt.end_of_turn_token())
&& !eos.contains(&id)
{
eos.push(id);
}
eos
}