Skip to main content

ferrin_spec/error/
model.rs

1//! Model lookup, capability and reference errors.
2
3use serde::Deserialize;
4use serde::Serialize;
5
6use crate::shared::ModelId;
7use crate::shared::ProviderId;
8use crate::shared::ProviderReference;
9
10/// Kinds of models a provider can expose.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(rename_all = "kebab-case")]
13#[non_exhaustive]
14pub enum ModelKind {
15    /// Text generation.
16    Language,
17    /// Embeddings.
18    Embedding,
19    /// Image generation.
20    Image,
21    /// Speech-to-text.
22    Transcription,
23    /// Text-to-speech.
24    Speech,
25    /// Reranking.
26    Reranking,
27    /// Video generation.
28    Video,
29    /// Speech translation.
30    SpeechTranslation,
31    /// Realtime sessions.
32    Realtime,
33}
34
35impl ModelKind {
36    /// Returns a human-readable name (`language model`, `image model`, ...).
37    #[must_use]
38    pub fn as_str(self) -> &'static str {
39        match self {
40            Self::Language => "language model",
41            Self::Embedding => "embedding model",
42            Self::Image => "image model",
43            Self::Transcription => "transcription model",
44            Self::Speech => "speech model",
45            Self::Reranking => "reranking model",
46            Self::Video => "video model",
47            Self::SpeechTranslation => "speech translation model",
48            Self::Realtime => "realtime model",
49        }
50    }
51}
52
53impl std::fmt::Display for ModelKind {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        f.write_str(self.as_str())
56    }
57}
58
59/// The requested model does not exist or the provider has no models of that kind.
60#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
61#[error("{message}")]
62pub struct NoSuchModelError {
63    /// Provider that was asked, if known.
64    pub provider: Option<ProviderId>,
65    /// Requested model id.
66    pub model_id: String,
67    /// Requested model kind.
68    pub model_kind: ModelKind,
69    /// Explanation.
70    pub message: String,
71}
72
73impl NoSuchModelError {
74    /// Creates an error for an unknown `model_id` of `model_kind`.
75    #[must_use]
76    pub fn new(model_id: impl Into<String>, model_kind: ModelKind) -> Self {
77        let model_id = model_id.into();
78        Self {
79            provider: None,
80            message: format!("no such {model_kind}: {model_id}"),
81            model_id,
82            model_kind,
83        }
84    }
85
86    /// Attaches the provider that was asked.
87    #[must_use]
88    pub fn with_provider(mut self, provider: &ProviderId) -> Self {
89        self.provider = Some(provider.clone());
90        self.message = format!(
91            "no such {}: {} (provider {provider})",
92            self.model_kind, self.model_id
93        );
94        self
95    }
96
97    /// Creates an error stating that `provider` exposes no models of `model_kind`.
98    #[must_use]
99    pub fn unsupported_kind(provider: &ProviderId, model_id: &str, model_kind: ModelKind) -> Self {
100        Self {
101            provider: Some(provider.clone()),
102            model_id: model_id.to_owned(),
103            model_kind,
104            message: format!(
105                "provider {provider} does not expose {model_kind}s (requested {model_id})"
106            ),
107        }
108    }
109
110    /// Overrides the message.
111    #[must_use]
112    pub fn with_message(mut self, message: impl Into<String>) -> Self {
113        self.message = message.into();
114        self
115    }
116}
117
118/// A provider reference has no entry for the provider that received it.
119#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
120#[error("{message}")]
121pub struct NoSuchProviderReferenceError {
122    /// Provider key that was looked up.
123    pub provider: String,
124    /// The reference that was received.
125    pub reference: ProviderReference,
126    /// Explanation.
127    pub message: String,
128}
129
130impl NoSuchProviderReferenceError {
131    /// Creates an error for `provider` missing in `reference`.
132    #[must_use]
133    pub fn new(provider: impl Into<String>, reference: ProviderReference) -> Self {
134        let provider = provider.into();
135        let available: Vec<&str> = reference.keys().map(String::as_str).collect();
136        Self {
137            message: format!(
138                "no provider reference found for provider `{provider}`; available providers: {}",
139                available.join(", ")
140            ),
141            provider,
142            reference,
143        }
144    }
145}
146
147/// Too many values were passed to a single embedding call.
148#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
149#[error(
150    "too many values for a single embedding call: {provider} model `{model_id}` accepts up to \
151     {max_embeddings_per_call} values per call, got {value_count}"
152)]
153pub struct TooManyEmbeddingValuesForCallError {
154    /// Provider identifier.
155    pub provider: ProviderId,
156    /// Model identifier.
157    pub model_id: ModelId,
158    /// Maximum values per call.
159    pub max_embeddings_per_call: usize,
160    /// Number of values that were passed.
161    pub value_count: usize,
162}
163
164/// The provider or model does not support the requested functionality.
165#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
166#[error("{message}")]
167pub struct UnsupportedFunctionalityError {
168    /// Name of the functionality.
169    pub functionality: String,
170    /// Explanation.
171    pub message: String,
172}
173
174impl UnsupportedFunctionalityError {
175    /// Creates an error with the default message.
176    #[must_use]
177    pub fn new(functionality: impl Into<String>) -> Self {
178        let functionality = functionality.into();
179        Self {
180            message: format!("`{functionality}` functionality not supported"),
181            functionality,
182        }
183    }
184
185    /// Creates an error with a custom message.
186    #[must_use]
187    pub fn with_message(functionality: impl Into<String>, message: impl Into<String>) -> Self {
188        Self {
189            functionality: functionality.into(),
190            message: message.into(),
191        }
192    }
193}