Skip to main content

openai_interface/files/
delete.rs

1//! Delete a file by file ID.
2
3pub mod request {
4    use std::collections::HashMap;
5
6    use url::Url;
7
8    use crate::{
9        errors::OapiError,
10        rest::delete::{Delete, DeleteNoStream},
11    };
12
13    #[derive(Debug, Default)]
14    pub struct DeleteRequest<'a> {
15        pub file_id: &'a str,
16        pub extra_query: HashMap<&'a str, &'a str>,
17    }
18
19    impl Delete for DeleteRequest<'_> {
20        /// base_url should look like <https://api.openai.com/v1>
21        fn build_url(&self, base_url: &str) -> Result<String, crate::errors::OapiError> {
22            let mut url =
23                Url::parse(base_url.trim_end_matches('/')).map_err(OapiError::UrlError)?;
24            url.path_segments_mut()
25                .map_err(|_| OapiError::UrlCannotBeBase(base_url.to_string()))?
26                .push("files")
27                .push(self.file_id);
28
29            for (key, value) in &self.extra_query {
30                url.query_pairs_mut().append_pair(key, value);
31            }
32
33            Ok(url.to_string())
34        }
35    }
36
37    impl<'a> DeleteNoStream for DeleteRequest<'a> {
38        type Response = super::response::FileDeleted;
39    }
40}
41
42pub mod response {
43    use serde::Deserialize;
44
45    #[derive(Debug, Deserialize, Clone)]
46    pub struct FileDeleted {
47        pub id: String,
48        pub deleted: bool,
49        pub object: FileDeletedObject,
50    }
51
52    #[derive(Debug, Deserialize, Clone)]
53    #[serde(rename_all = "snake_case")]
54    pub enum FileDeletedObject {
55        File,
56    }
57
58    crate::impl_from_str!(FileDeleted);
59}
60
61#[cfg(test)]
62mod tests {
63    use anyhow::{Context, anyhow};
64
65    use crate::{
66        files::{delete::request::DeleteRequest, list::request::ListFilesRequest},
67        rest::{default_client, delete::DeleteNoStream, get::GetNoStream},
68    };
69
70    const MODELSTUDIO_BASE_URL: &str = "https://dashscope.aliyuncs.com/compatible-mode/v1/";
71
72    fn modelstudio_key() -> Option<String> {
73        std::env::var("QWEN_API_KEY")
74            .ok()
75            .map(|key| key.trim().to_string())
76            .filter(|key| !key.is_empty())
77    }
78
79    #[tokio::test]
80    async fn test_modelstudio_delete_files() -> anyhow::Result<()> {
81        let Some(api_key) = modelstudio_key() else {
82            println!("Skipping: set QWEN_API_KEY to run this test");
83            return Ok(());
84        };
85
86        let client = default_client();
87
88        let list_request = ListFilesRequest::default();
89        let list_response = list_request
90            .get_response(&client, MODELSTUDIO_BASE_URL, &api_key)
91            .await
92            .with_context(|| anyhow!("Failed to list files."))?;
93
94        let files = list_response
95            .data
96            .iter()
97            .filter_map(|file_object| {
98                let name = file_object.filename.as_str();
99                let file_id = file_object.id.as_str();
100                if name.starts_with("test") {
101                    Some(file_id)
102                } else {
103                    None
104                }
105            })
106            .collect::<Vec<&str>>();
107
108        let futures = files
109            .iter()
110            .map(|file| {
111                let client = client.clone();
112                let api_key = api_key.clone();
113                async move {
114                    let delete_request = DeleteRequest {
115                        file_id: file,
116                        ..Default::default()
117                    };
118                    delete_request
119                        .get_response(&client, MODELSTUDIO_BASE_URL, &api_key)
120                        .await
121                        .with_context(|| anyhow!("Failed to delete file {}", file))
122                }
123            })
124            .collect::<Vec<_>>();
125
126        let results = futures_util::future::join_all(futures).await;
127
128        for result in results {
129            result?;
130        }
131
132        Ok(())
133    }
134}