use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
#[derive(Debug, Clone, Serialize)]
pub struct EmbeddingResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub object: Option<ResponseObjectKind>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Vec<EmbeddingData>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<EmbeddingUsage>,
}
#[derive(Deserialize)]
struct EmbeddingResponseWire {
model: Option<String>,
object: Option<ResponseObjectKind>,
data: Option<Vec<EmbeddingData>>,
usage: Option<EmbeddingUsage>,
}
impl<'de> Deserialize<'de> for EmbeddingResponse {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let wire = EmbeddingResponseWire::deserialize(deserializer)?;
if wire.model.is_none()
&& wire.object.is_none()
&& wire.data.is_none()
&& wire.usage.is_none()
{
return Err(D::Error::custom(
"embedding response contained no documented fields",
));
}
Ok(Self {
model: wire.model,
object: wire.object,
data: wire.data,
usage: wire.usage,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ResponseObjectKind {
List,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingData {
#[serde(skip_serializing_if = "Option::is_none")]
pub index: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub object: Option<EmbeddingObjectKind>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding: Option<Vec<f32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EmbeddingObjectKind {
Embedding,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingUsage {
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_tokens: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_tokens: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_tokens: Option<u64>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn response_requires_one_documented_non_null_field() {
assert!(serde_json::from_str::<EmbeddingResponse>("{}").is_err());
assert!(serde_json::from_str::<EmbeddingResponse>(r#"{"data":null}"#).is_err());
assert!(serde_json::from_str::<EmbeddingResponse>(r#"{"data":[]}"#).is_ok());
}
#[test]
fn nested_properties_follow_their_optional_schema() {
let item: EmbeddingData = serde_json::from_str("{}").unwrap();
assert!(item.index.is_none());
assert!(item.object.is_none());
assert!(item.embedding.is_none());
let usage: EmbeddingUsage = serde_json::from_str("{}").unwrap();
assert!(usage.prompt_tokens.is_none());
}
}