pub mod output;
pub mod param;
use crate::{Client, error::DashScopeError};
const FILE_PATH: &str = "files";
pub enum FilePurpose {
FineTune,
FileExtract,
Batch,
}
impl FilePurpose {
pub fn as_str(&self) -> &'static str {
match self {
FilePurpose::FineTune => "fine-tune",
FilePurpose::FileExtract => "file-extract",
FilePurpose::Batch => "batch",
}
}
}
pub struct File<'a> {
client: &'a Client,
}
impl<'a> File<'a> {
pub fn new(client: &'a Client) -> Self {
Self { client }
}
pub async fn create(
&self,
files: Vec<&str>,
purpose: FilePurpose,
descriptions: Option<Vec<&str>>
) -> Result<crate::operation::file::output::FileUploadOutput, DashScopeError> {
use reqwest::multipart;
use std::path::Path;
let purpose_str = purpose.as_str().to_string();
let file_paths: Vec<String> = {
let mut result = Vec::with_capacity(files.len());
for p in files {
let path = Path::new(p);
if !path.exists() {
return Err(DashScopeError::UploadError(format!("File not found: {}", p)));
}
result.push(p.to_string());
}
result
};
let descriptions: Option<Vec<String>> = descriptions.map(|descs| descs.iter().map(|s| s.to_string()).collect());
self.client.post_multipart(FILE_PATH, move || {
let mut form = multipart::Form::new()
.text("purpose", purpose_str.clone());
if let Some(descs) = &descriptions {
for desc in descs {
form = form.text("descriptions", desc.clone());
}
};
let mut form_with_files = form;
for file_path in &file_paths {
let path = Path::new(file_path);
let file_name = path.file_name()
.and_then(|name| name.to_str())
.unwrap_or(&format!("file_{}", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()))
.to_string();
let file_data = std::fs::read(file_path)
.unwrap_or_else(|e| {
std::panic::resume_unwind(Box::new(DashScopeError::UploadError(
format!("Failed to read file {}: {}", file_path, e)
)));
});
let part = multipart::Part::bytes(file_data)
.file_name(file_name);
form_with_files = form_with_files.part("files", part);
}
form_with_files
}).await
}
pub async fn retrieve(
&self,
file_id: &str,
) -> Result<crate::operation::file::output::FileRetrieveOutput, DashScopeError> {
let path = format!("files/{}", file_id);
self.client.get_with_params(&path, &()).await
}
pub async fn list(
&self,
page_no: Option<u64>,
page_size: Option<u64>,
) -> Result<crate::operation::file::output::FileListOutput, DashScopeError> {
use serde_json::json;
let validated_page_no = page_no.unwrap_or(1);
let validated_page_no = if validated_page_no < 1 {
1
} else {
validated_page_no
};
let validated_page_size = page_size.unwrap_or(10);
let validated_page_size = validated_page_size.clamp(1, 100);
let params = json!({
"page_no": validated_page_no,
"page_size": validated_page_size,
});
self.client.get_with_params("files", ¶ms).await
}
pub async fn delete(
&self,
file_id: &str,
) -> Result<crate::operation::file::output::FileDeleteOutput, DashScopeError> {
let path = format!("files/{}", file_id);
self.client.delete(&path).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::ConfigBuilder;
#[tokio::test]
async fn test_file_operations() {
let _ = dotenvy::dotenv(); let api_key = std::env::var("DASHSCOPE_API_KEY").expect("DASHSCOPE_API_KEY must be set");
let config = ConfigBuilder::default().api_key(api_key).build().unwrap();
let client = Client::with_config(config);
let file = File::new(&client);
let result = file.list(Some(1), Some(10)).await;
match result {
Ok(list_output) => {
println!("Retrieved {} files", list_output.data.files.len());
}
Err(e) => {
eprintln!("Error listing files: {:?}", e);
}
}
}
#[tokio::test]
async fn test_file_retrieve() {
let _ = dotenvy::dotenv();
let api_key = std::env::var("DASHSCOPE_API_KEY").expect("DASHSCOPE_API_KEY must be set");
let config = ConfigBuilder::default().api_key(api_key).build().unwrap();
let client = Client::with_config(config);
let file = File::new(&client);
let list_result = file.list(Some(1), Some(1)).await;
match list_result {
Ok(list_output) => {
if let Some(first_file) = list_output.data.files.first() {
let result = file.retrieve(&first_file.file_id).await;
match result {
Ok(file_info) => {
println!("Retrieved file: {}", file_info.data.name);
}
Err(e) => {
eprintln!("Error retrieving file: {:?}", e);
}
}
} else {
println!("No files found to retrieve");
}
}
Err(e) => {
eprintln!("Error listing files for retrieve test: {:?}", e);
}
}
}
}