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, Serialize};
44
45 #[derive(Debug, Deserialize, Serialize, Clone)]
46 pub struct FileDeleted {
47 pub id: String,
48 pub deleted: bool,
49 pub object: FileDeletedObject,
50 }
51
52 #[derive(Debug, Deserialize, Serialize, 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::{RequestOptions, 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(
91 &client,
92 MODELSTUDIO_BASE_URL,
93 &RequestOptions::bearer(&api_key),
94 )
95 .await
96 .with_context(|| anyhow!("Failed to list files."))?;
97
98 let files = list_response
99 .data
100 .iter()
101 .filter_map(|file_object| {
102 let name = file_object.filename.as_str();
103 let file_id = file_object.id.as_str();
104 if name.starts_with("test") {
105 Some(file_id)
106 } else {
107 None
108 }
109 })
110 .collect::<Vec<&str>>();
111
112 let futures = files
113 .iter()
114 .map(|file| {
115 let client = client.clone();
116 let api_key = api_key.clone();
117 async move {
118 let delete_request = DeleteRequest {
119 file_id: file,
120 ..Default::default()
121 };
122 delete_request
123 .get_response(
124 &client,
125 MODELSTUDIO_BASE_URL,
126 &RequestOptions::bearer(&api_key),
127 )
128 .await
129 .with_context(|| anyhow!("Failed to delete file {}", file))
130 }
131 })
132 .collect::<Vec<_>>();
133
134 let results = futures_util::future::join_all(futures).await;
135
136 for result in results {
137 result?;
138 }
139
140 Ok(())
141 }
142}