alith-client 0.4.3

The Easiest Rust Interface for Local LLMs, and an Interface for Deterministic Signals from Probabilistic LLM Vibes
Documentation
use thiserror::Error;
pub mod basic_url;
pub mod boolean;
pub mod custom;
pub mod exact_string;
pub mod faux_url;
pub mod integer;
pub mod none;
pub mod text;

pub use basic_url::BasicUrlGrammar;
pub use boolean::BooleanGrammar;
pub use custom::CustomGrammar;
pub use exact_string::ExactStringGrammar;
pub use faux_url::FauxUrlGrammar;
pub use integer::IntegerGrammar;
pub use none::NoneGrammar;
pub use text::sentences::SentencesGrammar;
pub use text::text_grammar::TextGrammar;
pub use text::text_list::TextListGrammar;
pub use text::words::WordsGrammar;

#[derive(Clone, PartialEq)]
pub enum Grammar {
    Boolean(BooleanGrammar),
    Integer(IntegerGrammar),
    Text(TextGrammar),
    Sentences(SentencesGrammar),
    Words(WordsGrammar),
    TextList(TextListGrammar),
    BasicUrl(BasicUrlGrammar),
    ExactString(ExactStringGrammar),
    FauxUrl(FauxUrlGrammar),
    NoneGrammar(NoneGrammar),
    Custom(CustomGrammar),
}

macro_rules! grammar_default {
    ($enum_name:ident {
        $($variant:ident => $fn_name:ident: $inner_type:ident),* $(,)?
    }) => {
        impl $enum_name {
            $(
                pub fn $fn_name() -> $inner_type {
                    $inner_type::default()
                }
            )*

            pub fn grammar_string(&self) -> String {
                match self {
                    $(
                        $enum_name::$variant(grammar) => grammar.grammar_string(),
                    )*
                }
            }

            pub fn validate_clean(&self, content: &str) -> Result<String, GrammarError> {
                match self {
                    $(
                        $enum_name::$variant(grammar) => grammar.validate_clean(content),
                    )*
                }
            }

            pub fn set_stop_word_done<T: AsRef<str>>(&mut self, stop_word: T) -> &mut Self {
                match self {
                    $(
                        $enum_name::$variant(grammar) => {
                           if grammar.stop_word_done.as_deref() != Some(stop_word.as_ref()) {
                               grammar.stop_word_done = Some(stop_word.as_ref().to_owned());
                            }
                            self
                        }
                    )*
                }
            }

            pub fn set_stop_word_no_result<T: AsRef<str>>(&mut self, stop_word: T) -> &mut Self {
                match self {
                    $(
                        $enum_name::$variant(grammar) => {
                           if grammar.stop_word_no_result.as_deref() != Some(stop_word.as_ref()) {
                               grammar.stop_word_no_result = Some(stop_word.as_ref().to_owned());
                            }
                            self
                        }
                    )*
                }
            }
        }
    };
}

grammar_default! {
    Grammar {
        Boolean => boolean: BooleanGrammar,
        Integer => integer: IntegerGrammar,
        Text => text: TextGrammar,
        Sentences => sentences: SentencesGrammar,
        Words => words: WordsGrammar,
        TextList => text_list: TextListGrammar,
        BasicUrl => basic_url: BasicUrlGrammar,
        ExactString => exact_string: ExactStringGrammar,
        FauxUrl => faux_url: FauxUrlGrammar,
        NoneGrammar => none: NoneGrammar,
        Custom => custom: CustomGrammar,
    }
}

impl Default for Grammar {
    fn default() -> Self {
        Grammar::Text(TextGrammar::default())
    }
}

pub trait GrammarSetterTrait {
    fn stop_word_done_mut(&mut self) -> &mut Option<String>;

    fn stop_word_no_result_mut(&mut self) -> &mut Option<String>;

    fn set_stop_word_done<T: AsRef<str>>(&mut self, stop_word: T) -> &mut Self
    where
        Self: Sized,
    {
        if self.stop_word_done_mut().as_deref() != Some(stop_word.as_ref()) {
            *self.stop_word_done_mut() = Some(stop_word.as_ref().to_owned());
        }
        self
    }

    fn set_stop_word_no_result<T: AsRef<str>>(&mut self, stop_word: T) -> &mut Self
    where
        Self: Sized,
    {
        if self.stop_word_no_result_mut().as_deref() != Some(stop_word.as_ref()) {
            *self.stop_word_no_result_mut() = Some(stop_word.as_ref().to_owned());
        }
        self
    }
}

#[derive(Error, Debug, PartialEq)]
pub enum GrammarError {
    #[error("grammar not set")]
    GrammarNotSet,
    #[error("response ({response}) lacks the correct prefix for given grammar ({correct_prefix})")]
    PrefixIncorrect {
        correct_prefix: String,
        response: String,
    },
    #[error("failed to parse response_content ({content}) as type ({parse_type})")]
    ParseValueError { content: String, parse_type: String },
    #[error("incorrect destructuring function ({function}) for grammar type ({grammar_type})")]
    DestructuringIncorrect {
        function: String,
        grammar_type: String,
    },
}