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, Deserializer, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;

/// EN: Request body for `POST /v1/batches`.
/// 中文:`POST /v1/batches` 的请求体。
#[derive(Clone, Debug, Serialize, PartialEq)]
#[non_exhaustive]
pub struct CreateBatchRequest {
    /// EN: Uploaded JSONL input file id.
    /// 中文:已上传的 JSONL 输入文件 ID。
    pub input_file_id: String,
    /// EN: Endpoint each batch line targets.
    /// 中文:batch 每一行所请求的端点。
    pub endpoint: String,
    /// EN: Batch completion window, currently such as `24h`.
    /// 中文:batch 完成窗口,当前例如 `24h`。
    pub completion_window: String,
    /// EN: Optional metadata attached to the batch.
    /// 中文:附加到 batch 的可选元数据。
    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
    pub metadata: BTreeMap<String, String>,
    /// EN: Forward-compatible optional fields not yet covered by handwritten types.
    /// 中文:手写类型尚未覆盖的前向兼容可选字段。
    #[serde(flatten)]
    pub extra: BTreeMap<String, Value>,
}

impl CreateBatchRequest {
    /// EN: Starts building a batch request.
    /// 中文:开始构建 batch 请求。
    pub fn builder() -> CreateBatchRequestBuilder {
        CreateBatchRequestBuilder::default()
    }
}

/// EN: Builder for create-batch requests.
/// 中文:创建 batch 请求的构建器。
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct CreateBatchRequestBuilder {
    input_file_id: Option<String>,
    endpoint: Option<String>,
    completion_window: Option<String>,
    metadata: BTreeMap<String, String>,
    extra: BTreeMap<String, Value>,
}

impl CreateBatchRequestBuilder {
    /// EN: Sets the uploaded input file id.
    /// 中文:设置已上传的输入文件 ID。
    pub fn input_file_id(mut self, input_file_id: impl Into<String>) -> Self {
        self.input_file_id = Some(input_file_id.into());
        self
    }

    /// EN: Sets the target endpoint.
    /// 中文:设置目标端点。
    pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
        self.endpoint = Some(endpoint.into());
        self
    }

    /// EN: Sets the completion window.
    /// 中文:设置完成窗口。
    pub fn completion_window(mut self, completion_window: impl Into<String>) -> Self {
        self.completion_window = Some(completion_window.into());
        self
    }

    /// EN: Adds a metadata entry.
    /// 中文:添加一项元数据。
    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
        self.metadata.insert(key.into(), value.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<CreateBatchRequest, LingerError> {
        let input_file_id = self
            .input_file_id
            .filter(|value| !value.trim().is_empty())
            .ok_or_else(|| LingerError::invalid_config("input_file_id is required"))?;
        let endpoint = self
            .endpoint
            .filter(|value| !value.trim().is_empty())
            .ok_or_else(|| LingerError::invalid_config("endpoint is required"))?;
        let completion_window = self
            .completion_window
            .filter(|value| !value.trim().is_empty())
            .ok_or_else(|| LingerError::invalid_config("completion_window is required"))?;
        if self
            .metadata
            .iter()
            .any(|(key, value)| key.trim().is_empty() || value.is_empty())
        {
            return Err(LingerError::invalid_config(
                "metadata keys and values must not be empty",
            ));
        }
        Ok(CreateBatchRequest {
            input_file_id,
            endpoint,
            completion_window,
            metadata: self.metadata,
            extra: self.extra,
        })
    }
}

/// EN: Batch object returned by the Batches API.
/// 中文:Batches API 返回的 batch 对象。
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[non_exhaustive]
pub struct Batch {
    /// EN: Batch id.
    /// 中文:batch ID。
    pub id: String,
    /// EN: API object type.
    /// 中文:API 对象类型。
    pub object: String,
    /// EN: Target endpoint for requests in this batch.
    /// 中文:此 batch 中请求的目标端点。
    pub endpoint: String,
    /// EN: Batch validation or processing errors, when returned.
    /// 中文:batch 校验或处理错误,如响应中存在。
    #[serde(default)]
    pub errors: Option<Value>,
    /// EN: Uploaded input file id.
    /// 中文:已上传的输入文件 ID。
    pub input_file_id: String,
    /// EN: Batch completion window.
    /// 中文:batch 完成窗口。
    pub completion_window: String,
    /// EN: Current batch status.
    /// 中文:当前 batch 状态。
    pub status: String,
    /// EN: Output file id, when available.
    /// 中文:输出文件 ID,如可用。
    #[serde(default)]
    pub output_file_id: Option<String>,
    /// EN: Error file id, when available.
    /// 中文:错误文件 ID,如可用。
    #[serde(default)]
    pub error_file_id: Option<String>,
    /// EN: Unix timestamp for creation.
    /// 中文:创建时间的 Unix 时间戳。
    pub created_at: u64,
    /// EN: Unix timestamp for in-progress transition.
    /// 中文:进入进行中状态的 Unix 时间戳。
    #[serde(default)]
    pub in_progress_at: Option<u64>,
    /// EN: Unix timestamp for expiration.
    /// 中文:过期时间的 Unix 时间戳。
    #[serde(default)]
    pub expires_at: Option<u64>,
    /// EN: Unix timestamp for finalizing transition.
    /// 中文:进入最终化状态的 Unix 时间戳。
    #[serde(default)]
    pub finalizing_at: Option<u64>,
    /// EN: Unix timestamp for completion.
    /// 中文:完成时间的 Unix 时间戳。
    #[serde(default)]
    pub completed_at: Option<u64>,
    /// EN: Unix timestamp for failure.
    /// 中文:失败时间的 Unix 时间戳。
    #[serde(default)]
    pub failed_at: Option<u64>,
    /// EN: Unix timestamp for expiration completion.
    /// 中文:过期完成时间的 Unix 时间戳。
    #[serde(default)]
    pub expired_at: Option<u64>,
    /// EN: Unix timestamp for cancelling transition.
    /// 中文:进入取消中状态的 Unix 时间戳。
    #[serde(default)]
    pub cancelling_at: Option<u64>,
    /// EN: Unix timestamp for cancellation.
    /// 中文:取消时间的 Unix 时间戳。
    #[serde(default)]
    pub cancelled_at: Option<u64>,
    /// EN: Request counters for this batch.
    /// 中文:此 batch 的请求计数。
    pub request_counts: BatchRequestCounts,
    /// EN: Metadata returned with this batch.
    /// 中文:此 batch 返回的元数据。
    #[serde(default, deserialize_with = "deserialize_null_default")]
    pub metadata: BTreeMap<String, String>,
    /// EN: Additional fields preserved for forward compatibility.
    /// 中文:为前向兼容保留的额外字段。
    #[serde(flatten)]
    pub extra: BTreeMap<String, Value>,
    /// EN: OpenAI request id from response headers.
    /// 中文:响应头中的 OpenAI 请求 ID。
    #[serde(skip)]
    request_id: Option<RequestId>,
}

impl Batch {
    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: Batch request counters.
/// 中文:batch 请求计数。
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct BatchRequestCounts {
    /// EN: Total request count.
    /// 中文:请求总数。
    pub total: u64,
    /// EN: Completed request count.
    /// 中文:已完成请求数。
    pub completed: u64,
    /// EN: Failed request count.
    /// 中文:失败请求数。
    pub failed: u64,
}

/// EN: Paginated batch list returned by the Batches API.
/// 中文:Batches API 返回的分页 batch 列表。
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[non_exhaustive]
pub struct BatchListPage {
    /// EN: API list object type.
    /// 中文:API 列表对象类型。
    pub object: String,
    /// EN: Batches on this page.
    /// 中文:本页 batch。
    #[serde(default)]
    pub data: Vec<Batch>,
    /// EN: First batch id on this page.
    /// 中文:本页第一个 batch ID。
    #[serde(default)]
    pub first_id: Option<String>,
    /// EN: Last batch id on this page.
    /// 中文:本页最后一个 batch ID。
    #[serde(default)]
    pub last_id: Option<String>,
    /// EN: Whether more batches are available.
    /// 中文:是否还有更多 batch。
    pub has_more: bool,
    /// EN: OpenAI request id from response headers.
    /// 中文:响应头中的 OpenAI 请求 ID。
    #[serde(skip)]
    request_id: Option<RequestId>,
}

impl BatchListPage {
    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()
    }
}

fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
    D: Deserializer<'de>,
    T: Deserialize<'de> + Default,
{
    Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
}