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::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::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::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::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)]
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, skip_serializing_if = "Option::is_none")]
109 pub extra_body: 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}