use crate::SlmRole;
use crate::formatter::{SlmFormatter, SlmToolStyle};
pub struct Qwen25Formatter {
thinking: bool,
}
impl Qwen25Formatter {
pub fn new(thinking: bool) -> Self {
Self { thinking }
}
}
impl SlmFormatter for Qwen25Formatter {
fn bos(&self) -> Option<&str> {
None
}
fn turn_start(&self, role: &SlmRole) -> String {
match role {
SlmRole::System => "<|im_start|>system\n".to_string(),
SlmRole::User => "<|im_start|>user\n".to_string(),
SlmRole::Assistant => "<|im_start|>assistant\n".to_string(),
SlmRole::Tool(_) => String::new(),
}
}
fn turn_end(&self, role: &SlmRole) -> String {
match role {
SlmRole::Tool(_) => String::new(),
_ => "<|im_end|>\n".to_string(),
}
}
fn reasoning_bounds(&self) -> Option<(&str, &str)> {
if self.thinking {
Some(("<think>\n", "</think>"))
} else {
None
}
}
fn wrap_reasoning(&self, content: &str) -> String {
if self.thinking {
format!("<think>\n{}</think>", content.trim())
} else {
content.to_string()
}
}
fn reasoning_trigger(&self) -> Option<&str> {
if self.thinking {
Some("<think>\n")
} else {
None
}
}
fn tool_style(&self) -> SlmToolStyle {
SlmToolStyle::Inline
}
fn format_tool_call(&self, name: &str, arguments: &str) -> String {
format!(
r#"{{"name": "{}", "arguments": {}}}"#,
name,
arguments.trim()
)
}
fn format_tool_response(&self, _name: &str, content: &str) -> String {
format!("<|im_start|>tool\n{}<|im_end|>\n", content.trim())
}
fn strip_tags(&self, text: &str) -> String {
let mut cleaned = text.to_string();
let qwen_structural_tags = [
"<|im_start|>",
"<|im_end|>",
"system\n",
"user\n",
"assistant\n",
"tool\n",
];
for tag in qwen_structural_tags {
cleaned = cleaned.replace(tag, "");
}
let qwen_channels = ["<think>", "</think>"];
for tag in qwen_channels {
cleaned = cleaned.replace(tag, "");
}
while let Some(start_idx) = cleaned.find("<|im_start|>tool") {
if let Some(end_idx) = cleaned[start_idx..].find("<|im_end|>") {
let absolute_end_idx = start_idx + end_idx + "<|im_end|>\n".len();
cleaned.drain(start_idx..absolute_end_idx);
} else {
cleaned.drain(start_idx..);
break;
}
}
cleaned.trim().to_string()
}
}