use std::fmt;
pub(crate) const RAISE_SENTINEL: &str = "\u{0}HFCT_RAISE\u{1}";
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
TemplateRaised {
message: String,
},
Compile(minijinja::Error),
Render(minijinja::Error),
Config(String),
#[cfg(feature = "hub")]
Hub(String),
#[cfg(feature = "tokenizers")]
Tokenize(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::TemplateRaised { message } => {
write!(f, "template rejected the input: {message}")
}
Error::Compile(e) => write!(f, "chat template failed to compile: {e}"),
Error::Render(e) => write!(f, "chat template failed to render: {e}"),
Error::Config(m) => write!(f, "invalid chat-template config: {m}"),
#[cfg(feature = "hub")]
Error::Hub(m) => write!(f, "failed to fetch from the Hugging Face Hub: {m}"),
#[cfg(feature = "tokenizers")]
Error::Tokenize(m) => write!(f, "failed to tokenize the rendered prompt: {m}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Compile(e) | Error::Render(e) => Some(e),
_ => None,
}
}
}
impl Error {
pub(crate) fn from_render(e: minijinja::Error) -> Self {
if let Some(msg) = find_raised_message(&e) {
return Error::TemplateRaised { message: msg };
}
Error::Render(e)
}
}
fn find_raised_message(e: &minijinja::Error) -> Option<String> {
let mut current: Option<&(dyn std::error::Error + 'static)> = Some(e);
while let Some(err) = current {
let s = err.to_string();
if let Some(idx) = s.find(RAISE_SENTINEL) {
let after = &s[idx + RAISE_SENTINEL.len()..];
let msg = match after.find(RAISE_SENTINEL) {
Some(end) => &after[..end],
None => after,
};
return Some(msg.to_string());
}
current = err.source();
}
None
}