use serde::{Deserialize, Serialize};
use serde_json::{Map, Value as Json};
use crate::error::Error;
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct TokenizerConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chat_template: Option<ChatTemplateField>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bos_token: Option<TokenField>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub eos_token: Option<TokenField>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pad_token: Option<TokenField>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unk_token: Option<TokenField>,
#[serde(default, flatten)]
pub extra: Map<String, Json>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ChatTemplateField {
Single(String),
Named(Vec<NamedTemplate>),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NamedTemplate {
pub name: String,
pub template: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TokenField {
Str(String),
Obj(Map<String, Json>),
}
impl TokenField {
pub fn as_str(&self) -> Option<&str> {
match self {
TokenField::Str(s) => Some(s),
TokenField::Obj(m) => m.get("content").and_then(Json::as_str),
}
}
}
impl TokenizerConfig {
pub(crate) fn special_tokens(&self) -> Vec<(String, String)> {
let mut out = Vec::new();
for (name, field) in [
("bos_token", &self.bos_token),
("eos_token", &self.eos_token),
("pad_token", &self.pad_token),
("unk_token", &self.unk_token),
] {
if let Some(s) = field.as_ref().and_then(TokenField::as_str) {
out.push((name.to_string(), s.to_string()));
}
}
out
}
}
pub(crate) fn resolve_template<'a>(
field: &'a ChatTemplateField,
name: Option<&str>,
) -> Result<&'a str, Error> {
match field {
ChatTemplateField::Single(s) => Ok(s),
ChatTemplateField::Named(list) => {
if list.is_empty() {
return Err(Error::Config("chat_template list is empty".into()));
}
if let Some(want) = name {
list.iter()
.find(|t| t.name == want)
.map(|t| t.template.as_str())
.ok_or_else(|| {
Error::Config(format!(
"no chat_template named '{want}'; available: {}",
available_names(list)
))
})
} else if let Some(t) = list.iter().find(|t| t.name == "default") {
Ok(&t.template)
} else if list.len() == 1 {
Ok(&list[0].template)
} else {
Err(Error::Config(format!(
"ambiguous chat_template: specify a name via builder. available: {}",
available_names(list)
)))
}
}
}
}
fn available_names(list: &[NamedTemplate]) -> String {
list.iter()
.map(|t| t.name.as_str())
.collect::<Vec<_>>()
.join(", ")
}