use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum FurnaceError {
#[error(transparent)]
Model(#[from] ModelError),
#[error(transparent)]
Api(#[from] ApiError),
#[error(transparent)]
Nerve(#[from] crate::nerve::BuildError),
}
impl FurnaceError {
pub fn is_client_error(&self) -> bool {
matches!(
self,
Self::Model(
ModelError::InputValidation { .. }
| ModelError::EmptyBatch
| ModelError::BatchTooLarge { .. }
| ModelError::InvalidDataType(_)
| ModelError::InvalidInput(_)
| ModelError::InvalidArgument { .. }
)
)
}
}
#[derive(Debug, Error)]
pub enum ModelError {
#[error("Failed to load model from {path}: {source}")]
LoadFailed {
path: PathBuf,
#[source]
source: Box<dyn std::error::Error + Send + Sync>,
},
#[error("Invalid model format: {0}")]
InvalidFormat(String),
#[error("Model file not found: {0}")]
FileNotFound(PathBuf),
#[error("Inference failed: {0}")]
InferenceFailed(String),
#[error(
"expected {} values (batch {batch} × {item:?}), got {got}",
.batch * .item.iter().product::<usize>()
)]
InputValidation {
batch: usize,
item: Vec<usize>,
got: usize,
},
#[error("a request needs at least one item, got an empty batch")]
EmptyBatch,
#[error("batch size {size} exceeds the limit of {max} items per request")]
BatchTooLarge { size: usize, max: usize },
#[error("Invalid input data type: {0}")]
InvalidDataType(String),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Invalid argument '{arg}': {value} - {reason}")]
InvalidArgument {
arg: String,
value: String,
reason: String,
},
}
#[derive(Debug, Error)]
pub enum ApiError {
#[error("Server startup failed: {0}")]
ServerStartup(String),
}
pub type Result<T> = std::result::Result<T, FurnaceError>;
#[derive(Debug, Serialize, Deserialize)]
pub struct ErrorResponse {
pub error: String,
pub code: String,
pub details: Option<serde_json::Value>,
pub timestamp: String,
}
impl ErrorResponse {
pub fn new(error: String, code: String, details: Option<serde_json::Value>) -> Self {
Self {
error,
code,
details,
timestamp: chrono::Utc::now().to_rfc3339(),
}
}
}