openai_interface/files/
retrieve.rs

1pub mod request {
2    use url::Url;
3
4    use crate::{
5        errors::OapiError,
6        rest::get::{Get, GetNoStream},
7    };
8
9    pub struct RetrieveRequest<'a> {
10        pub file_id: &'a str,
11    }
12
13    impl<'a> Get for RetrieveRequest<'a> {
14        /// base_url should look like https://api.openai.com/v1/ (must ends with '/')
15        fn build_url(&self, base_url: &str) -> Result<String, OapiError> {
16            let url = Url::parse(base_url)
17                .map_err(|e| OapiError::UrlError(e))?
18                .join("files/")
19                .unwrap()
20                .join(self.file_id)
21                .map_err(|e| OapiError::UrlError(e))?;
22
23            Ok(url.to_string())
24        }
25    }
26
27    impl<'a> GetNoStream for RetrieveRequest<'a> {
28        type Response = crate::files::FileObject;
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use std::sync::LazyLock;
35
36    use super::*;
37    use crate::{
38        files::{list::request::ListFilesRequest, retrieve::request::RetrieveRequest},
39        rest::get::{Get, GetNoStream},
40    };
41    use anyhow::bail;
42    use futures_util::future::{self};
43
44    const MODELSCOPE_BASE_URL: &str = "https://dashscope.aliyuncs.com/compatible-mode/v1/";
45    const MODELSCOPE_KEY: LazyLock<&str> =
46        LazyLock::new(|| include_str!("../../keys/modelstudio_domestic_key").trim());
47
48    #[test]
49    fn test_build_url() {
50        let request = request::RetrieveRequest { file_id: "file_id" };
51        let url = request.build_url("https://api.openai.com/v1/").unwrap();
52        assert_eq!(url, "https://api.openai.com/v1/files/file_id");
53    }
54
55    #[tokio::test]
56    async fn test_retrieve_file() -> Result<(), anyhow::Error> {
57        // first get all files
58        let list_request = ListFilesRequest {
59            limit: Some(5), // avoid rate limit
60            ..Default::default()
61        };
62
63        let list_response = list_request
64            .get_response(MODELSCOPE_BASE_URL, &MODELSCOPE_KEY)
65            .await?;
66
67        let futures: Vec<_> = list_response
68            .data
69            .iter()
70            .map(|file_object| {
71                let file_id = file_object.id.clone();
72                let base_url = MODELSCOPE_BASE_URL.to_string();
73                let key = MODELSCOPE_KEY.to_string();
74                async move {
75                    let retrieve_request = RetrieveRequest { file_id: &file_id };
76                    retrieve_request.get_response(&base_url, &key).await
77                }
78            })
79            .collect();
80
81        let results = future::join_all(futures).await;
82
83        for (i, result) in results.iter().enumerate() {
84            match result {
85                Ok(file_object) => {
86                    assert_eq!(&list_response.data[i].id, &file_object.id);
87                    assert_eq!(&list_response.data[i].filename, &file_object.filename);
88                    assert_eq!(&list_response.data[i].purpose, &file_object.purpose);
89                    // assert_eq!(&list_response.data[i], file_object);
90                }
91                Err(e) => {
92                    bail!(
93                        "Failed to get response: {e:#}. The file is: index {i}, {:?}",
94                        list_response.data[i]
95                    )
96                }
97            }
98        }
99
100        Ok(())
101    }
102}