Skip to main content

lattice_embed/
error.rs

1//! Error types for embedding operations.
2
3use thiserror::Error;
4
5/// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
6///
7/// Errors that can occur during embedding operations.
8#[derive(Error, Debug)]
9#[non_exhaustive]
10pub enum EmbedError {
11    /// Model not loaded (needs initialization).
12    #[error("model not loaded: {0}")]
13    ModelNotLoaded(String),
14
15    /// Wrong model loaded (concurrent model switch in progress).
16    ///
17    /// This can happen when multiple tasks request different models concurrently.
18    /// The caller should retry with backoff.
19    #[error("wrong model loaded: expected {expected}, got {actual}")]
20    WrongModelLoaded {
21        /// Model that was expected.
22        expected: String,
23        /// Model that was actually loaded.
24        actual: String,
25    },
26
27    /// Model initialization failed.
28    #[error("model initialization failed: {0}")]
29    ModelInitialization(String),
30
31    /// Embedding inference failed.
32    #[error("embedding inference failed: {0}")]
33    InferenceFailed(String),
34
35    /// Blocking task failed (panic or cancellation).
36    ///
37    /// The model cache may be lost; next call will reinitialize.
38    #[error("task execution failed: {0}")]
39    TaskFailed(String),
40
41    /// Invalid input provided.
42    #[error("invalid input: {0}")]
43    InvalidInput(String),
44
45    /// Input text exceeds maximum allowed length.
46    #[error("text too long: {length} chars exceeds maximum {max} chars")]
47    TextTooLong {
48        /// Actual length in characters.
49        length: usize,
50        /// Maximum allowed length.
51        max: usize,
52    },
53
54    /// Dimension mismatch between expected and actual.
55    #[error("dimension mismatch: expected {expected}, got {actual}")]
56    DimensionMismatch {
57        /// Expected dimension.
58        expected: usize,
59        /// Actual dimension.
60        actual: usize,
61    },
62
63    /// Model not supported by this service.
64    #[error("model not supported: {0}")]
65    UnsupportedModel(String),
66
67    /// Internal logic error (count mismatch, unexpected state).
68    #[error("internal error: {0}")]
69    Internal(String),
70
71    /// A quantization tier did not match the tier required by the operation.
72    ///
73    /// Raised by the `simd::tier` prepared-dispatch functions (e.g.
74    /// `approximate_cosine_distance_prepared`) when a `PreparedQuery`'s tier does not
75    /// match the stored data's tier, or a batch dispatch function's presumed tier.
76    #[error("tier mismatch in {op}: expected {expected:?}, got {actual:?}")]
77    TierMismatch {
78        /// Name of the operation where the mismatch was detected.
79        op: &'static str,
80        /// Tier the operation required.
81        expected: crate::simd::QuantizationTier,
82        /// Tier actually supplied.
83        actual: crate::simd::QuantizationTier,
84    },
85}
86
87/// **Stable**: result type alias for embedding operations.
88pub type Result<T> = std::result::Result<T, EmbedError>;
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_error_display() {
96        let err = EmbedError::DimensionMismatch {
97            expected: 384,
98            actual: 768,
99        };
100        assert_eq!(err.to_string(), "dimension mismatch: expected 384, got 768");
101    }
102
103    #[test]
104    fn test_error_variants() {
105        let err = EmbedError::ModelNotLoaded("test".into());
106        assert_eq!(err.to_string(), "model not loaded: test");
107
108        let err = EmbedError::WrongModelLoaded {
109            expected: "small".into(),
110            actual: "large".into(),
111        };
112        assert!(err.to_string().contains("expected small"));
113
114        let err = EmbedError::ModelInitialization("failed".into());
115        assert!(err.to_string().contains("initialization"));
116
117        let err = EmbedError::InferenceFailed("oom".into());
118        assert!(err.to_string().contains("inference"));
119
120        let err = EmbedError::TaskFailed("panic".into());
121        assert!(err.to_string().contains("task"));
122
123        let err = EmbedError::InvalidInput("empty".into());
124        assert!(err.to_string().contains("invalid input"));
125
126        let err = EmbedError::UnsupportedModel("gpt4".into());
127        assert!(err.to_string().contains("not supported"));
128
129        let err = EmbedError::Internal("bug".into());
130        assert!(err.to_string().contains("internal"));
131
132        let err = EmbedError::TextTooLong {
133            length: 50000,
134            max: 32768,
135        };
136        assert!(err.to_string().contains("50000"));
137        assert!(err.to_string().contains("32768"));
138    }
139}