linger-openai-sdk 0.1.1

Rust-native async SDK for OpenAI APIs with typed requests, streaming, uploads, retries, and pluggable transports.
Documentation
use crate::error::LingerError;
use crate::RequestId;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;

/// EN: Request body for `POST /v1/embeddings`.
/// 中文:`POST /v1/embeddings` 的请求体。
#[derive(Clone, Debug, Serialize, PartialEq)]
#[non_exhaustive]
pub struct CreateEmbeddingRequest {
    /// EN: Model id used to create embeddings.
    /// 中文:用于创建 embeddings 的模型 ID。
    pub model: String,
    /// EN: Input text or text array.
    /// 中文:输入文本或文本数组。
    pub input: EmbeddingInput,
    /// EN: Optional embedding encoding format.
    /// 中文:可选的 embedding 编码格式。
    #[serde(skip_serializing_if = "Option::is_none")]
    pub encoding_format: Option<EmbeddingEncodingFormat>,
    /// EN: Optional output vector dimension count for supported models.
    /// 中文:支持的模型可选输出向量维度数。
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dimensions: Option<u32>,
    /// EN: Optional end-user identifier.
    /// 中文:可选的终端用户标识。
    #[serde(skip_serializing_if = "Option::is_none")]
    pub user: Option<String>,
    /// EN: Forward-compatible optional fields not yet covered by handwritten types.
    /// 中文:手写类型尚未覆盖的前向兼容可选字段。
    #[serde(flatten)]
    pub extra: BTreeMap<String, Value>,
}

impl CreateEmbeddingRequest {
    /// EN: Starts building an embeddings request.
    /// 中文:开始构建 embeddings 请求。
    pub fn builder() -> CreateEmbeddingRequestBuilder {
        CreateEmbeddingRequestBuilder::default()
    }
}

/// EN: Builder for create-embedding requests.
/// 中文:创建 embedding 请求的构建器。
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct CreateEmbeddingRequestBuilder {
    model: Option<String>,
    input: Option<EmbeddingInput>,
    encoding_format: Option<EmbeddingEncodingFormat>,
    dimensions: Option<u32>,
    user: Option<String>,
    extra: BTreeMap<String, Value>,
}

impl CreateEmbeddingRequestBuilder {
    /// EN: Sets the embedding model id.
    /// 中文:设置 embedding 模型 ID。
    pub fn model(mut self, model: impl Into<String>) -> Self {
        self.model = Some(model.into());
        self
    }

    /// EN: Sets the embedding input.
    /// 中文:设置 embedding 输入。
    pub fn input(mut self, input: impl Into<EmbeddingInput>) -> Self {
        self.input = Some(input.into());
        self
    }

    /// EN: Sets the embedding encoding format.
    /// 中文:设置 embedding 编码格式。
    pub fn encoding_format(mut self, encoding_format: EmbeddingEncodingFormat) -> Self {
        self.encoding_format = Some(encoding_format);
        self
    }

    /// EN: Sets the output vector dimensions.
    /// 中文:设置输出向量维度。
    pub fn dimensions(mut self, dimensions: u32) -> Self {
        self.dimensions = Some(dimensions);
        self
    }

    /// EN: Sets the optional end-user identifier.
    /// 中文:设置可选的终端用户标识。
    pub fn user(mut self, user: impl Into<String>) -> Self {
        self.user = Some(user.into());
        self
    }

    /// EN: Adds a forward-compatible JSON field.
    /// 中文:添加前向兼容的 JSON 字段。
    pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
        self.extra.insert(name.into(), value);
        self
    }

    /// EN: Builds and validates the request.
    /// 中文:构建并校验请求。
    pub fn build(self) -> Result<CreateEmbeddingRequest, LingerError> {
        let model = self
            .model
            .filter(|value| !value.trim().is_empty())
            .ok_or_else(|| LingerError::invalid_config("model is required"))?;
        let input = self
            .input
            .ok_or_else(|| LingerError::invalid_config("input is required"))?;
        if input.is_empty() {
            return Err(LingerError::invalid_config("input must not be empty"));
        }
        Ok(CreateEmbeddingRequest {
            model,
            input,
            encoding_format: self.encoding_format,
            dimensions: self.dimensions,
            user: self.user,
            extra: self.extra,
        })
    }
}

/// EN: Embeddings API input value.
/// 中文:Embeddings API 输入值。
#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
#[serde(untagged)]
#[non_exhaustive]
pub enum EmbeddingInput {
    /// EN: Single text input.
    /// 中文:单条文本输入。
    Text(String),
    /// EN: Multiple text inputs.
    /// 中文:多条文本输入。
    Texts(Vec<String>),
}

impl EmbeddingInput {
    fn is_empty(&self) -> bool {
        match self {
            Self::Text(value) => value.is_empty(),
            Self::Texts(values) => values.is_empty() || values.iter().any(String::is_empty),
        }
    }
}

impl From<&str> for EmbeddingInput {
    fn from(value: &str) -> Self {
        Self::Text(value.to_string())
    }
}

impl From<String> for EmbeddingInput {
    fn from(value: String) -> Self {
        Self::Text(value)
    }
}

impl From<Vec<String>> for EmbeddingInput {
    fn from(value: Vec<String>) -> Self {
        Self::Texts(value)
    }
}

/// EN: Embedding encoding format.
/// 中文:Embedding 编码格式。
#[derive(Clone, Copy, Debug, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum EmbeddingEncodingFormat {
    /// EN: Floating-point vector output.
    /// 中文:浮点向量输出。
    Float,
    /// EN: Base64-encoded vector output.
    /// 中文:Base64 编码的向量输出。
    Base64,
}

/// EN: Response object returned by the Embeddings API.
/// 中文:Embeddings API 返回的响应对象。
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[non_exhaustive]
pub struct EmbeddingResponse {
    /// EN: API list object type.
    /// 中文:API 列表对象类型。
    pub object: String,
    /// EN: Embedding items.
    /// 中文:Embedding 项。
    #[serde(default)]
    pub data: Vec<Embedding>,
    /// EN: Model used to create the embeddings.
    /// 中文:用于创建 embeddings 的模型。
    pub model: String,
    /// EN: Token usage.
    /// 中文:Token 用量。
    pub usage: EmbeddingUsage,
    /// EN: OpenAI request id from response headers.
    /// 中文:响应头中的 OpenAI 请求 ID。
    #[serde(skip)]
    request_id: Option<RequestId>,
}

impl EmbeddingResponse {
    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
        self.request_id = request_id;
        self
    }

    /// EN: Returns the OpenAI request id, when present.
    /// 中文:返回 OpenAI 请求 ID,如存在。
    pub fn request_id(&self) -> Option<&RequestId> {
        self.request_id.as_ref()
    }
}

/// EN: Single embedding item.
/// 中文:单个 embedding 项。
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[non_exhaustive]
pub struct Embedding {
    /// EN: API object type.
    /// 中文:API 对象类型。
    pub object: String,
    /// EN: Embedding vector.
    /// 中文:Embedding 向量。
    pub embedding: Vec<f32>,
    /// EN: Index of this embedding in the response.
    /// 中文:该 embedding 在响应中的索引。
    pub index: u32,
}

/// EN: Token usage for an embeddings request.
/// 中文:Embeddings 请求的 token 用量。
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct EmbeddingUsage {
    /// EN: Prompt token count.
    /// 中文:Prompt token 数量。
    pub prompt_tokens: u64,
    /// EN: Total token count.
    /// 中文:总 token 数量。
    pub total_tokens: u64,
}