Skip to main content

liter_llm/types/
embedding.rs

1use serde::{Deserialize, Serialize};
2
3use super::common::Usage;
4use crate::cost;
5
6// ─── Encoding format ──────────────────────────────────────────────────────────
7
8/// The format in which the embedding vectors are returned.
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum EmbeddingFormat {
12    /// 32-bit floating-point numbers (default).
13    Float,
14    /// Base64-encoded string representation of the floats.
15    Base64,
16}
17
18// ─── Request ──────────────────────────────────────────────────────────────────
19
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21#[serde(deny_unknown_fields)]
22pub struct EmbeddingRequest {
23    pub model: String,
24    pub input: EmbeddingInput,
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub encoding_format: Option<EmbeddingFormat>,
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub dimensions: Option<u32>,
29    #[serde(default, skip_serializing_if = "Option::is_none")]
30    pub user: Option<String>,
31}
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum EmbeddingInput {
36    Single(String),
37    Multiple(Vec<String>),
38}
39
40impl Default for EmbeddingInput {
41    fn default() -> Self {
42        Self::Single(String::new())
43    }
44}
45
46// ─── Response ─────────────────────────────────────────────────────────────────
47
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49pub struct EmbeddingResponse {
50    /// Always `"list"` from OpenAI-compatible APIs.  Stored as a plain
51    /// `String` so non-standard provider values do not break deserialization.
52    pub object: String,
53    pub data: Vec<EmbeddingObject>,
54    pub model: String,
55    #[serde(default, skip_serializing_if = "Option::is_none")]
56    pub usage: Option<Usage>,
57}
58
59impl EmbeddingResponse {
60    /// Estimate the cost of this embedding request based on embedded pricing data.
61    ///
62    /// Returns `None` if:
63    /// - the `model` field is not present in the embedded pricing registry, or
64    /// - the `usage` field is absent from the response.
65    ///
66    /// Embedding models only charge for input tokens; output cost is zero.
67    ///
68    /// # Example
69    ///
70    /// ```rust,ignore
71    /// let cost = response.estimated_cost();
72    /// if let Some(usd) = cost {
73    ///     println!("Embedding cost: ${usd:.8}");
74    /// }
75    /// ```
76    #[must_use]
77    pub fn estimated_cost(&self) -> Option<f64> {
78        let usage = self.usage.as_ref()?;
79        cost::completion_cost(&self.model, usage.prompt_tokens, usage.completion_tokens)
80    }
81}
82
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84pub struct EmbeddingObject {
85    /// Always `"embedding"` from OpenAI-compatible APIs.  Stored as a plain
86    /// `String` so non-standard provider values do not break deserialization.
87    pub object: String,
88    pub embedding: Vec<f64>,
89    pub index: u32,
90}