1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
16#[serde(rename_all = "snake_case")]
17#[non_exhaustive]
18#[repr(u8)]
19pub enum DistanceMetric {
20 #[default]
22 Cosine = 1,
23 Dot = 2,
25 L2 = 3,
27}
28
29impl DistanceMetric {
30 #[inline]
32 pub const fn as_byte(self) -> u8 {
33 self as u8
34 }
35
36 #[inline]
38 pub const fn from_byte(b: u8) -> Option<Self> {
39 match b {
40 1 => Some(Self::Cosine),
41 2 => Some(Self::Dot),
42 3 => Some(Self::L2),
43 _ => None,
44 }
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
54#[serde(rename_all = "snake_case")]
55#[non_exhaustive]
56#[repr(u8)]
57pub enum VectorDType {
58 #[default]
60 F32 = 1,
61 F16 = 2,
63 I8 = 3,
65}
66
67impl VectorDType {
68 #[inline]
70 pub const fn as_byte(self) -> u8 {
71 self as u8
72 }
73
74 #[inline]
76 pub const fn size_bytes(self) -> usize {
77 match self {
78 Self::F32 => 4,
79 Self::F16 => 2,
80 Self::I8 => 1,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
91#[serde(rename_all = "snake_case")]
92#[non_exhaustive]
93#[repr(u8)]
94pub enum VectorNorm {
95 #[default]
97 None = 0,
98 Unit = 1,
100}
101
102impl VectorNorm {
103 #[inline]
105 pub const fn as_byte(self) -> u8 {
106 self as u8
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
119pub struct EmbeddingKey {
120 pub model: Box<str>,
122 pub revision: Box<str>,
124 pub dims: u32,
126 pub metric: DistanceMetric,
128 pub dtype: VectorDType,
130 pub norm: VectorNorm,
132}
133
134impl EmbeddingKey {
135 pub fn new(
137 model: impl Into<Box<str>>,
138 revision: impl Into<Box<str>>,
139 dims: u32,
140 metric: DistanceMetric,
141 dtype: VectorDType,
142 norm: VectorNorm,
143 ) -> Self {
144 Self {
145 model: model.into(),
146 revision: revision.into(),
147 dims,
148 metric,
149 dtype,
150 norm,
151 }
152 }
153
154 pub fn canonical_bytes(&self) -> Vec<u8> {
157 let mut buf = Vec::new();
158
159 let model_bytes = self.model.as_bytes();
160 buf.extend_from_slice(&(model_bytes.len() as u32).to_be_bytes());
161 buf.extend_from_slice(model_bytes);
162
163 let rev_bytes = self.revision.as_bytes();
164 buf.extend_from_slice(&(rev_bytes.len() as u32).to_be_bytes());
165 buf.extend_from_slice(rev_bytes);
166
167 buf.extend_from_slice(&self.dims.to_be_bytes());
168 buf.push(self.metric.as_byte());
169 buf.push(self.dtype.as_byte());
170 buf.push(self.norm.as_byte());
171
172 buf
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_distance_metric_byte_roundtrip() {
182 for (metric, byte) in [
183 (DistanceMetric::Cosine, 1u8),
184 (DistanceMetric::Dot, 2u8),
185 (DistanceMetric::L2, 3u8),
186 ] {
187 assert_eq!(metric.as_byte(), byte);
188 assert_eq!(DistanceMetric::from_byte(byte), Some(metric));
189 }
190 assert_eq!(DistanceMetric::from_byte(99), None);
191 }
192
193 #[test]
194 fn test_vector_dtype_size_bytes() {
195 assert_eq!(VectorDType::F32.size_bytes(), 4);
196 assert_eq!(VectorDType::F16.size_bytes(), 2);
197 assert_eq!(VectorDType::I8.size_bytes(), 1);
198 }
199
200 #[test]
201 fn test_vector_norm_defaults() {
202 assert_eq!(VectorNorm::default(), VectorNorm::None);
203 }
204
205 #[test]
206 fn test_embedding_key_canonical_bytes_deterministic() {
207 let k1 = EmbeddingKey::new(
208 "bge-small-en-v1.5",
209 "v1.5",
210 384,
211 DistanceMetric::Cosine,
212 VectorDType::F32,
213 VectorNorm::Unit,
214 );
215 let k2 = EmbeddingKey::new(
216 "bge-small-en-v1.5",
217 "v1.5",
218 384,
219 DistanceMetric::Cosine,
220 VectorDType::F32,
221 VectorNorm::Unit,
222 );
223 assert_eq!(k1.canonical_bytes(), k2.canonical_bytes());
224 }
225
226 #[test]
227 fn test_embedding_key_canonical_bytes_differs_by_field() {
228 let k1 = EmbeddingKey::new(
229 "model-a",
230 "v1",
231 384,
232 DistanceMetric::Cosine,
233 VectorDType::F32,
234 VectorNorm::Unit,
235 );
236 let k2 = EmbeddingKey::new(
237 "model-b",
238 "v1",
239 384,
240 DistanceMetric::Cosine,
241 VectorDType::F32,
242 VectorNorm::Unit,
243 );
244 assert_ne!(k1.canonical_bytes(), k2.canonical_bytes());
245
246 let k3 = EmbeddingKey::new(
247 "model-a",
248 "v1",
249 768,
250 DistanceMetric::Cosine,
251 VectorDType::F32,
252 VectorNorm::Unit,
253 );
254 assert_ne!(k1.canonical_bytes(), k3.canonical_bytes());
255 }
256}