use serde::Deserialize;
#[derive(Debug, Deserialize, Clone)]
pub struct CreateEmbeddingResponse {
pub data: Vec<Embedding>,
pub model: String,
pub object: String,
pub usage: Usage,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Embedding {
pub embedding: EmbeddingVector,
pub index: usize,
pub object: String,
}
#[derive(Debug, Deserialize, Clone, PartialEq)]
#[serde(untagged)]
pub enum EmbeddingVector {
Floats(Vec<f32>),
Base64(String),
}
#[derive(Debug, Deserialize, Clone)]
pub struct Usage {
pub prompt_tokens: usize,
pub total_tokens: usize,
}
crate::impl_from_str!(CreateEmbeddingResponse);
#[cfg(test)]
mod tests {
#[test]
fn parse_float_response() {
let content = r#"{
"data": [
{
"embedding": [0.033480461686849594,0.00393357127904892,0.008528660982847214,0.03501124680042267,0.053708694875240326,-0.013427173718610081,0.003826963249593973,-0.0037968941032886505,-0.059481941163539886,0.21693414449691772,0.023467724293470383,-0.011972927488386631,0.017680570483207703,0.006883066613227129,0.04200912266969681,-0.04117812216281891],
"index": 0,
"object": "embedding"
}
],
"object": "list",
"model": "text-embedding-v4",
"usage": {"prompt_tokens": 2, "total_tokens": 2},
"id": "7010976a-5a36-955a-b837-0eedda118144"
}"#;
let response: super::CreateEmbeddingResponse = content.parse().unwrap();
assert_eq!(response.object, "list");
assert_eq!(response.data.len(), 1);
assert_eq!(response.data[0].index, 0);
assert_eq!(response.data[0].object, "embedding");
assert_eq!(response.model, "text-embedding-v4");
assert_eq!(response.usage.prompt_tokens, 2);
assert_eq!(response.usage.total_tokens, 2);
let super::EmbeddingVector::Floats(vector) = &response.data[0].embedding else {
panic!("expected float embedding vector");
};
assert_eq!(vector.len(), 16);
#[allow(clippy::excessive_precision)]
{
assert!((vector[0] - 0.033480461686849594).abs() < 1e-6);
}
}
#[test]
fn parse_base64_response() {
let content = r#"{
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": "AACAPwAAAAAAAAAAAAAAAAAAAAA="
}
],
"model": "text-embedding-3-small",
"usage": {"prompt_tokens": 5, "total_tokens": 5}
}"#;
let response: super::CreateEmbeddingResponse = content.parse().unwrap();
assert_eq!(
response.data[0].embedding,
super::EmbeddingVector::Base64("AACAPwAAAAAAAAAAAAAAAAAAAAA=".to_string())
);
}
}