use std::ffi::NulError;
use std::num::NonZeroI32;
use std::os::raw::c_int;
use std::path::PathBuf;
use std::string::FromUtf8Error;
use crate::batch_add_error::BatchAddError;
use crate::mtmd::MtmdEvalError;
use crate::mtmd::mtmd_input_chunk_type::MtmdInputChunkTypeError;
pub type Result<TValue> = std::result::Result<TValue, LlamaCppError>;
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LlamaCppError {
#[error("BackendAlreadyInitialized")]
BackendAlreadyInitialized,
#[error("{0}")]
ChatTemplateError(#[from] ChatTemplateError),
#[error("{0}")]
DecodeError(#[from] DecodeError),
#[error("{0}")]
EncodeError(#[from] EncodeError),
#[error("{0}")]
LlamaModelLoadError(#[from] LlamaModelLoadError),
#[error("{0}")]
LlamaContextLoadError(#[from] LlamaContextLoadError),
#[error["{0}"]]
BatchAddError(#[from] BatchAddError),
#[error(transparent)]
EmbeddingError(#[from] EmbeddingsError),
#[error("Backend device {0} not found")]
BackendDeviceNotFound(usize),
#[error("Max devices exceeded. Max devices is {0}")]
MaxDevicesExceeded(usize),
#[error("JsonSchemaToGrammarError: {0}")]
JsonSchemaToGrammarError(String),
#[error(transparent)]
FitError(#[from] FitError),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum ChatTemplateError {
#[error("chat template not found - returned null pointer")]
MissingTemplate,
#[error("null byte in string {0}")]
NullError(#[from] NulError),
#[error(transparent)]
Utf8Error(#[from] std::str::Utf8Error),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum MetaValError {
#[error("null byte in string {0}")]
NullError(#[from] NulError),
#[error("FromUtf8Error {0}")]
FromUtf8Error(#[from] FromUtf8Error),
#[error("Negative return value. Likely due to a missing index or key. Got return value: {0}")]
NegativeReturn(i32),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LlamaContextLoadError {
#[error("null reference from llama.cpp")]
NullReturn,
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum DecodeError {
#[error("Decode Error 1: NoKvCacheSlot")]
NoKvCacheSlot,
#[error("Decode Error 2: Aborted")]
Aborted,
#[error("Decode Error -1: n_tokens == 0")]
NTokensZero,
#[error("Decode Error {0}: unknown")]
Unknown(c_int),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum EncodeError {
#[error("Encode Error 1: NoKvCacheSlot")]
NoKvCacheSlot,
#[error("Encode Error -1: n_tokens == 0")]
NTokensZero,
#[error("Encode Error {0}: unknown")]
Unknown(c_int),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum EmbeddingsError {
#[error("Embeddings weren't enabled in the context options")]
NotEnabled,
#[error("Logits were not enabled for the given token")]
LogitsNotEnabled,
#[error("Can't use sequence embeddings with a model supporting only LLAMA_POOLING_TYPE_NONE")]
NonePoolType,
#[error("Invalid embedding dimension: {0}")]
InvalidEmbeddingDimension(#[source] std::num::TryFromIntError),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LogitsError {
#[error("logits data pointer is null")]
NullLogits,
#[error("logit for token index {0} is not initialized")]
TokenNotInitialized(i32),
#[error("token index {token_index} exceeds context size {context_size}")]
TokenIndexExceedsContext {
token_index: u32,
context_size: u32,
},
#[error("n_vocab does not fit into usize: {0}")]
VocabSizeOverflow(#[source] std::num::TryFromIntError),
#[error("token_index does not fit into u32: {0}")]
TokenIndexOverflow(#[source] std::num::TryFromIntError),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum GrammarError {
#[error("Grammar root not found in grammar string")]
RootNotFound,
#[error("Trigger word contains null bytes: {0}")]
TriggerWordNullBytes(NulError),
#[error("Grammar string or root contains null bytes: {0}")]
GrammarNullBytes(NulError),
#[error("String contains null bytes: {0}")]
NulError(#[from] NulError),
#[error("Grammar initialization failed: {0}")]
NullGrammar(String),
#[error("Integer overflow: {0}")]
IntegerOverflow(String),
#[error("llguidance error: {0}")]
LlguidanceError(String),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum SamplingError {
#[error("Integer overflow: {0}")]
IntegerOverflow(String),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum SampleError {
#[error("C++ exception during sampling: {0}")]
CppException(String),
#[error("Invalid argument passed to sampler")]
InvalidArgument,
}
impl From<NonZeroI32> for DecodeError {
fn from(value: NonZeroI32) -> Self {
match value.get() {
1 => Self::NoKvCacheSlot,
2 => Self::Aborted,
-1 => Self::NTokensZero,
error_code => Self::Unknown(error_code),
}
}
}
impl From<NonZeroI32> for EncodeError {
fn from(value: NonZeroI32) -> Self {
match value.get() {
1 => Self::NoKvCacheSlot,
-1 => Self::NTokensZero,
error_code => Self::Unknown(error_code),
}
}
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LlamaModelLoadError {
#[error("null byte in string {0}")]
NullError(#[from] NulError),
#[error("null result from llama cpp")]
NullResult,
#[error("failed to convert path {0} to str")]
PathToStrError(PathBuf),
#[error("model file not found: {0}")]
FileNotFound(PathBuf),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LlamaLoraAdapterInitError {
#[error("null byte in string {0}")]
NullError(#[from] NulError),
#[error("null result from llama cpp")]
NullResult,
#[error("failed to convert path {0} to str")]
PathToStrError(PathBuf),
#[error("adapter file not found: {0}")]
FileNotFound(PathBuf),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LlamaLoraAdapterSetError {
#[error("error code from llama cpp")]
ErrorResult(i32),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LlamaLoraAdapterRemoveError {
#[error("error code from llama cpp")]
ErrorResult(i32),
}
#[derive(Debug, thiserror::Error, Clone)]
#[non_exhaustive]
pub enum TokenToStringError {
#[error("Unknown Token Type")]
UnknownTokenType,
#[error("Insufficient Buffer Space {0}")]
InsufficientBufferSpace(c_int),
#[error("FromUtf8Error {0}")]
FromUtf8Error(#[from] FromUtf8Error),
#[error("Integer conversion error: {0}")]
IntConversionError(#[from] std::num::TryFromIntError),
}
#[derive(Debug, thiserror::Error)]
pub enum StringToTokenError {
#[error("{0}")]
NulError(#[from] NulError),
#[error("{0}")]
CIntConversionError(#[from] std::num::TryFromIntError),
}
#[derive(Debug, thiserror::Error)]
pub enum NewLlamaChatMessageError {
#[error("{0}")]
NulError(#[from] NulError),
}
#[derive(Debug, thiserror::Error)]
pub enum ApplyChatTemplateError {
#[error("{0}")]
FromUtf8Error(#[from] FromUtf8Error),
#[error("Integer conversion error: {0}")]
IntConversionError(#[from] std::num::TryFromIntError),
}
#[derive(Debug, thiserror::Error)]
pub enum MarkerDetectionError {
#[error("ffi error {0}")]
FfiError(i32),
#[error("c++ exception during template analysis: {0}")]
AnalyzeException(String),
#[error("ffi returned non-utf8 marker bytes: {0}")]
MarkerUtf8Error(#[from] FromUtf8Error),
}
#[derive(Debug, thiserror::Error)]
pub enum ParseChatMessageError {
#[error("ffi error {0}")]
FfiError(i32),
#[error("c++ exception during chat parse: {0}")]
ParseException(String),
#[error("ffi returned non-utf8 string: {0}")]
StringUtf8Error(#[from] FromUtf8Error),
#[error("tools_json is not valid JSON: {0}")]
ToolsJsonInvalid(#[source] serde_json::Error),
#[error("tools_json must be a JSON array")]
ToolsJsonNotArray,
#[error("could not serialize tools to JSON: {0}")]
ToolsSerialization(String),
#[error("model has no chat template")]
NoChatTemplate,
#[error("template-override fallback parser failed: {0}")]
TemplateOverrideFailed(#[from] ToolCallFormatFailure),
}
#[derive(Debug, thiserror::Error)]
pub enum ToolCallFormatFailure {
#[error("bracketed-args fallback parser: {0}")]
BracketedArgs(#[from] BracketedArgsFailure),
#[error("json-object fallback parser: {0}")]
JsonObject(#[from] JsonObjectFailure),
#[error("key-value-xml-tags fallback parser: {0}")]
KeyValueXmlTags(#[from] KeyValueXmlTagsFailure),
#[error("paired-quote fallback parser: {0}")]
PairedQuote(#[from] PairedQuoteFailure),
#[error("xml-function-tags fallback parser: {0}")]
XmlFunctionTags(#[from] XmlFunctionTagsFailure),
}
#[derive(Debug, thiserror::Error)]
pub enum JsonObjectFailure {
#[error("tool call body has malformed JSON: {message}")]
InvalidJson { message: String },
}
#[derive(Debug, thiserror::Error)]
pub enum BracketedArgsFailure {
#[error("tool call '{tool_name}' arguments are not valid JSON: {message}")]
InvalidJsonArguments { tool_name: String, message: String },
#[error("tool call '{tool_name}' arguments truncated before JSON value completed")]
UnterminatedArguments { tool_name: String },
}
#[derive(Debug, thiserror::Error)]
pub enum PairedQuoteFailure {
#[error("empty key in tool call '{tool_name}' arguments")]
EmptyKey { tool_name: String },
#[error("tool call '{tool_name}' translated arguments are not valid JSON: {message}")]
InvalidJsonArguments { tool_name: String, message: String },
#[error("tool call '{tool_name}' has unclosed quoted value for key '{key}'")]
UnclosedQuotedValue { tool_name: String, key: String },
#[error("tool call '{tool_name}' arguments ended without close marker (state: {state})")]
UnclosedArgumentBlock {
tool_name: String,
state: &'static str,
},
#[error(
"tool call '{tool_name}' has unexpected character '{character}' after value for key '{key}'"
)]
UnexpectedCharAfterValue {
tool_name: String,
key: String,
character: char,
},
}
#[derive(Debug, thiserror::Error)]
pub enum KeyValueXmlTagsFailure {
#[error("tool call function tag has empty name")]
EmptyFunctionName,
#[error("tool call function block is missing close tag '{expected_close}'")]
UnclosedFunctionBlock { expected_close: String },
#[error("tool call function '{function_name}' has key tag with empty content")]
EmptyKey { function_name: String },
#[error("tool call function '{function_name}' is missing key close tag '{expected_close}'")]
UnclosedKeyTag {
function_name: String,
expected_close: String,
},
#[error(
"tool call function '{function_name}' key '{key}' is missing value open tag '{expected_open}'"
)]
MissingValueTag {
function_name: String,
key: String,
expected_open: String,
},
#[error(
"tool call function '{function_name}' key '{key}' is missing value close tag '{expected_close}'"
)]
UnclosedValueTag {
function_name: String,
key: String,
expected_close: String,
},
}
#[derive(Debug, thiserror::Error)]
pub enum XmlFunctionTagsFailure {
#[error("tool call function tag has empty name")]
EmptyFunctionName,
#[error("tool call function '{function_name}' is missing close tag '{expected_close}'")]
UnclosedFunctionBlock {
function_name: String,
expected_close: String,
},
#[error("tool call function '{function_name}' has parameter with empty name")]
EmptyParameterName { function_name: String },
#[error(
"tool call function '{function_name}' parameter '{parameter_name}' is missing close tag '{expected_close}'"
)]
UnclosedParameterBlock {
function_name: String,
parameter_name: String,
expected_close: String,
},
}
#[derive(Debug, thiserror::Error)]
pub enum EvalMultimodalChunksError {
#[error("{0}")]
EvalFailed(#[from] MtmdEvalError),
#[error("{0}")]
UnknownChunkType(#[from] MtmdInputChunkTypeError),
#[error("chunk index {0} out of bounds during post-eval walk")]
ChunkOutOfBounds(usize),
}
#[derive(Debug, thiserror::Error)]
pub enum SamplerAcceptError {
#[error("C++ exception during sampler accept: {0}")]
CppException(String),
#[error("Invalid argument passed to sampler accept")]
InvalidArgument,
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum ModelParamsError {
#[error("No available slot in override vector")]
NoAvailableSlot,
#[error("Override slot is not empty")]
SlotNotEmpty,
#[error("Invalid character in key: byte {byte}, {reason}")]
InvalidCharacterInKey {
byte: u8,
reason: String,
},
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum TokenSamplingError {
#[error("No token was selected by the sampler")]
NoTokenSelected,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, thiserror::Error)]
pub enum FitError {
#[error("could not find allocations that fit available memory")]
Failure,
#[error("hard error during parameter fitting")]
Error,
}
#[cfg(test)]
mod tests {
use std::num::NonZeroI32;
use super::{DecodeError, EncodeError};
#[test]
fn decode_error_no_kv_cache_slot() {
let error = DecodeError::from(NonZeroI32::new(1).expect("1 is non-zero"));
assert_eq!(error, DecodeError::NoKvCacheSlot);
assert_eq!(error.to_string(), "Decode Error 1: NoKvCacheSlot");
}
#[test]
fn decode_error_n_tokens_zero() {
let error = DecodeError::from(NonZeroI32::new(-1).expect("-1 is non-zero"));
assert_eq!(error, DecodeError::NTokensZero);
assert_eq!(error.to_string(), "Decode Error -1: n_tokens == 0");
}
#[test]
fn decode_error_aborted() {
let error = DecodeError::from(NonZeroI32::new(2).expect("2 is non-zero"));
assert_eq!(error, DecodeError::Aborted);
assert_eq!(error.to_string(), "Decode Error 2: Aborted");
}
#[test]
fn decode_error_unknown() {
let error = DecodeError::from(NonZeroI32::new(42).expect("42 is non-zero"));
assert_eq!(error, DecodeError::Unknown(42));
assert_eq!(error.to_string(), "Decode Error 42: unknown");
}
#[test]
fn encode_error_no_kv_cache_slot() {
let error = EncodeError::from(NonZeroI32::new(1).expect("1 is non-zero"));
assert_eq!(error, EncodeError::NoKvCacheSlot);
assert_eq!(error.to_string(), "Encode Error 1: NoKvCacheSlot");
}
#[test]
fn encode_error_n_tokens_zero() {
let error = EncodeError::from(NonZeroI32::new(-1).expect("-1 is non-zero"));
assert_eq!(error, EncodeError::NTokensZero);
assert_eq!(error.to_string(), "Encode Error -1: n_tokens == 0");
}
#[test]
fn encode_error_unknown() {
let error = EncodeError::from(NonZeroI32::new(99).expect("99 is non-zero"));
assert_eq!(error, EncodeError::Unknown(99));
assert_eq!(error.to_string(), "Encode Error 99: unknown");
}
}