openai_interface/files/
delete.rs1pub 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 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}