slm_inference 0.1.1

Backend-agnostic trait layer for running Small Language Model (SLM) inference in Rust.
Documentation
mod gemma4;
mod llama3;
mod mistal;
mod phi4;
mod qwen25;

pub use gemma4::{Gemma4Flavor, GemmaFormatter};
pub use llama3::Llama3Formatter;
pub use mistal::{MistralFlavor, MistralFormatter};
pub use phi4::Phi4Formatter;
pub use qwen25::Qwen25Formatter;

use crate::errors::ModelFormatterError;
use crate::formatter::SlmToolStyle;
use crate::{SlmFormatter, SlmRole};

pub enum SlmDynamicFormatter {
    Gemma(GemmaFormatter),
    Llama3(Llama3Formatter),
    Mistral(MistralFormatter),
    Qwen25(Qwen25Formatter),
    Phi4(Phi4Formatter),
}

impl TryFrom<&str> for SlmDynamicFormatter {
    type Error = ModelFormatterError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value.to_lowercase().as_str() {
            "llama3" => Ok(Self::Llama3(Llama3Formatter)),
            "gemma4" => Ok(Self::Gemma(GemmaFormatter::new(
                Gemma4Flavor::Vanilla,
                true,
            ))),
            "gemma4-google" => Ok(Self::Gemma(GemmaFormatter::new(
                Gemma4Flavor::GoogleOfficial,
                true,
            ))),
            "gemma4-unsloth" => Ok(Self::Gemma(GemmaFormatter::new(
                Gemma4Flavor::UnslothFixed,
                true,
            ))),
            "mistral" => Ok(Self::Mistral(MistralFormatter::new(
                MistralFlavor::V3Tekken,
                true,
            ))),
            "mistral-legacy" => Ok(Self::Mistral(MistralFormatter::new(
                MistralFlavor::Legacy,
                true,
            ))),
            "qwen25" => Ok(Self::Qwen25(Qwen25Formatter::new(true))),
            "phi4" => Ok(Self::Phi4(Phi4Formatter::new(true))),
            _ => Err(ModelFormatterError::UnknownModelFormatter(
                value.to_string(),
            )),
        }
    }
}

impl SlmDynamicFormatter {
    fn deref(&self) -> &dyn SlmFormatter {
        match self {
            Self::Llama3(f) => f,
            Self::Gemma(f) => f,
            Self::Mistral(f) => f,
            Self::Qwen25(f) => f,
            Self::Phi4(f) => f,
        }
    }
}

impl SlmFormatter for SlmDynamicFormatter {
    fn bos(&self) -> Option<&str> {
        self.deref().bos()
    }

    fn turn_start(&self, role: &SlmRole) -> String {
        self.deref().turn_start(role)
    }

    fn turn_end(&self, role: &SlmRole) -> String {
        self.deref().turn_end(role)
    }

    fn reasoning_bounds(&self) -> Option<(&str, &str)> {
        self.deref().reasoning_bounds()
    }

    fn wrap_reasoning(&self, content: &str) -> String {
        self.deref().wrap_reasoning(content)
    }

    fn reasoning_trigger(&self) -> Option<&str> {
        self.deref().reasoning_trigger()
    }

    fn tool_style(&self) -> SlmToolStyle {
        self.deref().tool_style()
    }

    fn format_tool_call(&self, name: &str, arguments_json: &str) -> String {
        self.deref().format_tool_call(name, arguments_json)
    }

    fn format_tool_response(&self, tool_name: &str, response_content: &str) -> String {
        self.deref()
            .format_tool_response(tool_name, response_content)
    }

    fn clean(&self, text: &str) -> String {
        self.deref().clean(text)
    }

    fn strip_tags(&self, text: &str) -> String {
        self.deref().strip_tags(text)
    }
}