Skip to main content

openai_interface/uploads/
mod.rs

1//! The Uploads API: upload large files in multiple parts via `/uploads`.
2//!
3//! > ![warn] This module is untested!
4//! > If you encounter any issues, please report them on the repository.
5//!
6//! The Uploads API lets you upload large files (up to 8 GB per part
7//! upload) in multiple parts, which is more resilient to network
8//! interruptions than a single multipart request through
9//! [`files::create`](crate::files::create). The workflow is:
10//!
11//! 1. `create` — start an upload, declaring the total byte size.
12//! 2. `parts` — upload each chunk (up to 64 MB per part, except the
13//!    last).
14//! 3. `complete` — finalize the upload with the ordered part IDs,
15//!    yielding a [`FileObject`](crate::files::FileObject).
16//!
17//! A started-but-incomplete upload can be discarded via [`cancel`].
18//!
19//! See [the OpenAI Uploads API reference](https://platform.openai.com/docs/api-reference/uploads).
20
21pub mod cancel;
22pub mod complete;
23pub mod parts;
24
25use serde::{Deserialize, Serialize};
26use url::Url;
27
28use crate::{
29    errors::OapiError,
30    rest::post::{Post, PostNoStream},
31};
32
33/// The status of an upload.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub enum UploadStatus {
37    /// The upload is in progress; parts can still be added.
38    Pending,
39    /// The upload was completed and its file is ready.
40    Completed,
41    /// The upload was cancelled.
42    Cancelled,
43}
44
45/// A single uploaded part of a multi-part upload.
46#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
47pub struct UploadPart {
48    /// The part ID, e.g. `part_...`.
49    pub id: String,
50    /// Unix timestamp (seconds) of when the part was created.
51    #[serde(default)]
52    pub created_at: Option<u64>,
53    /// The ID of the upload this part belongs to.
54    #[serde(default)]
55    pub upload_id: Option<String>,
56    /// The object type, always `upload.part`.
57    #[serde(default)]
58    pub object: Option<String>,
59}
60
61crate::impl_from_str!(UploadPart);
62
63/// An upload session.
64#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
65pub struct Upload {
66    /// The upload ID, e.g. `upload_...`.
67    pub id: String,
68    /// Unix timestamp (seconds) of when the upload was created.
69    #[serde(default)]
70    pub created_at: Option<u64>,
71    /// The name of the file being uploaded.
72    pub filename: String,
73    /// The intended purpose of the uploaded file, e.g. `batch`.
74    pub purpose: String,
75    /// The size of the file being uploaded, in bytes.
76    pub bytes: u64,
77    /// The current status.
78    pub status: UploadStatus,
79    /// The ready [`FileObject`](crate::files::FileObject) ID once the
80    /// upload is completed.
81    #[serde(default)]
82    pub file_id: Option<String>,
83    /// Unix timestamp (seconds) of when the upload expires.
84    #[serde(default)]
85    pub expires_at: Option<u64>,
86    /// The object type, always `upload`.
87    #[serde(default)]
88    pub object: Option<String>,
89}
90
91crate::impl_from_str!(Upload);
92
93/// Starts an upload session via `POST /uploads`.
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct CreateUploadRequest {
96    /// The name of the file to upload.
97    pub filename: String,
98    /// The intended purpose of the uploaded file. One of `assistants`,
99    /// `batch`, `fine-tune`, `vision`, or `user_data`.
100    pub purpose: String,
101    /// The number of bytes in the file to be uploaded.
102    pub bytes: u64,
103    /// The MIME type of the file to be uploaded.
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub mime_type: Option<String>,
106    /// Additional JSON properties flattened into the request body, for
107    /// fields not covered by the typed struct.
108    #[serde(flatten, default, skip_serializing_if = "Option::is_none")]
109    pub extra_body_map: Option<serde_json::Map<String, serde_json::Value>>,
110}
111
112impl Post for CreateUploadRequest {
113    fn is_streaming(&self) -> bool {
114        false
115    }
116
117    /// Builds the URL for the request.
118    ///
119    /// `base_url` should be like <https://api.openai.com/v1>
120    fn build_url(&self, base_url: &str) -> Result<String, OapiError> {
121        let mut url = Url::parse(base_url.trim_end_matches('/')).map_err(OapiError::UrlError)?;
122        url.path_segments_mut()
123            .map_err(|_| OapiError::UrlCannotBeBase(base_url.to_string()))?
124            .push("uploads");
125        Ok(url.to_string())
126    }
127}
128
129impl PostNoStream for CreateUploadRequest {
130    type Response = Upload;
131}