Skip to main content

fx_mistral/
files.rs

1use reqwest::multipart;
2use serde::{Deserialize, Serialize};
3
4use crate::{MistralClient, MistralError, MistralApiError};
5
6#[derive(Serialize, Deserialize, Debug)]
7pub struct FileData {
8    pub id: String,
9    object: String,
10    bytes: u64,
11    created_at: u64,
12    filename: String,
13    purpose: String,
14    sample_type: String,
15    num_lines: Option<u64>,
16    source: String,
17}
18
19#[derive(Serialize, Deserialize, Debug)]
20pub struct SignedUrl {
21    pub url: String,
22}
23
24pub struct FileClient<'a> {
25    mistral_client: &'a MistralClient,
26    files_path: String,
27}
28
29impl<'a> FileClient<'a> {
30    pub fn new(mistral_client: &'a MistralClient) -> Self {
31        FileClient {
32            mistral_client,
33            files_path: "files".to_string(),
34        }
35    }
36
37    pub async fn upload_file(&self, file_data: Vec<u8>) -> Result<FileData, MistralError> {
38        let part = multipart::Part::bytes(file_data).file_name("uploaded_file.pdf");
39        let form = multipart::Form::new()
40            .text("purpose", "ocr")
41            .part("file", part);
42
43        let response = self
44            .mistral_client
45            .client
46            .post(&format!("{}/{}", self.mistral_client.base_url, self.files_path))
47            .bearer_auth(&self.mistral_client.api_key)
48            .multipart(form)
49            .send()
50            .await
51            .map_err(MistralError::Network)?;
52
53        let status = response.status();
54        let response_text = response.text().await.map_err(MistralError::Network)?;
55
56        if !status.is_success() {
57            return match serde_json::from_str::<MistralApiError>(&response_text) {
58                Ok(mut err) => {
59                    err.description = crate::error_description(err.code).to_string();
60                    Err(MistralError::Api(err))
61                },
62                Err(_) => Err(MistralError::Http(status)),
63            };
64        }
65
66        serde_json::from_str(&response_text).map_err(MistralError::Parse)
67    }
68
69    pub async fn get_signed_url(&self, file_id: &str) -> Result<SignedUrl, MistralError> {
70        let call_url = format!(
71            "{}/{}/{}/url?expiry=24",
72            self.mistral_client.base_url, self.files_path, file_id
73        );
74
75        let response = self
76            .mistral_client
77            .client
78            .get(call_url)
79            .bearer_auth(&self.mistral_client.api_key)
80            .send()
81            .await
82            .map_err(MistralError::Network)?;
83
84        let status = response.status();
85        let response_text = response.text().await.map_err(MistralError::Network)?;
86
87        if !status.is_success() {
88            return match serde_json::from_str::<MistralApiError>(&response_text) {
89                Ok(mut err) => {
90                    err.description = crate::error_description(err.code).to_string();
91                    Err(MistralError::Api(err))
92                },
93                Err(_) => Err(MistralError::Http(status)),
94            };
95        }
96
97        serde_json::from_str(&response_text).map_err(MistralError::Parse)
98    }
99}