Skip to main content

zai_rs/tool/file_parser_create/
data.rs

1//! # File Parser Creation API
2//!
3//! This module provides the file parser creation client for creating file
4//! parsing tasks.
5
6use std::{path::Path, sync::Arc};
7
8use tracing::{debug, trace, warn};
9
10use super::{request::*, response::*};
11use crate::{
12    ZaiResult,
13    client::{
14        endpoints::{ApiBase, EndpointConfig, paths},
15        error::codes,
16        http::{HttpClientConfig, parse_typed_response, send_multipart_request},
17    },
18};
19
20/// File parser creation client.
21///
22/// This client provides functionality to create file parsing tasks,
23/// supporting multiple file formats and parsing tools.
24///
25/// ## Examples
26///
27/// ```rust,ignore
28/// use zai_rs::tool::file_parser_create::{FileParserCreateRequest, ToolType, FileType};
29/// use std::path::Path;
30///
31/// let api_key = "your-api-key".to_string();
32/// let file_path = Path::new("document.pdf");
33///
34/// let request = FileParserCreateRequest::new(
35///     api_key,
36///     file_path,
37///     ToolType::Lite,
38///     FileType::PDF,
39/// )?;
40/// ```
41pub struct FileParserCreateRequest {
42    /// API key for authentication
43    pub key: String,
44    url: String,
45    endpoint_config: EndpointConfig,
46    api_base: ApiBase,
47    http_config: Arc<HttpClientConfig>,
48    /// Path to the file to parse
49    pub file_path: std::path::PathBuf,
50    /// Parsing tool type to use
51    pub tool_type: ToolType,
52    /// File type to parse
53    pub file_type: FileType,
54}
55
56impl FileParserCreateRequest {
57    /// Creates a new file parser creation request.
58    ///
59    /// ## Arguments
60    ///
61    /// * `key` - API key for authentication
62    /// * `file_path` - Path to the file to parse
63    /// * `tool_type` - Type of parsing tool to use
64    /// * `file_type` - Type of file to parse
65    ///
66    /// ## Returns
67    ///
68    /// A new `FileParserCreateRequest` instance or an error if validation
69    /// fails.
70    pub fn new(
71        key: String,
72        file_path: &Path,
73        tool_type: ToolType,
74        file_type: FileType,
75    ) -> crate::ZaiResult<Self> {
76        // Validate that file exists
77        if !file_path.exists() {
78            return Err(crate::client::error::ZaiError::FileError {
79                code: codes::SDK_FILE_NOT_FOUND,
80                message: format!("File does not exist: {}", file_path.display()),
81            });
82        }
83
84        // Validate that file type is supported by tool
85        if !file_type.is_supported_by(&tool_type) {
86            return Err(crate::client::error::ZaiError::ApiError {
87                code: 1200,
88                message: format!(
89                    "File type {:?} is not supported by tool type {:?}",
90                    file_type, tool_type
91                ),
92            });
93        }
94
95        let endpoint_config = EndpointConfig::default();
96        let api_base = ApiBase::PaasV4;
97        let url = endpoint_config.url(&api_base, paths::FILE_PARSER_CREATE);
98
99        Ok(Self {
100            key,
101            url,
102            endpoint_config,
103            api_base,
104            http_config: Arc::new(HttpClientConfig::default()),
105            file_path: file_path.to_path_buf(),
106            tool_type,
107            file_type,
108        })
109    }
110
111    /// Creates a new file parser creation request with automatic file type
112    /// detection.
113    ///
114    /// ## Arguments
115    ///
116    /// * `key` - API key for authentication
117    /// * `file_path` - Path to the file to parse
118    /// * `tool_type` - Type of parsing tool to use
119    ///
120    /// ## Returns
121    ///
122    /// A new `FileParserCreateRequest` instance or an error if validation
123    /// fails.
124    pub fn new_with_auto_type(
125        key: String,
126        file_path: &Path,
127        tool_type: ToolType,
128    ) -> crate::ZaiResult<Self> {
129        let file_type = FileType::from_path(file_path).ok_or_else(|| {
130            crate::client::error::ZaiError::FileError {
131                code: codes::SDK_FILE_TYPE_UNSUPPORTED,
132                message: format!(
133                    "Could not determine file type from path: {}",
134                    file_path.display()
135                ),
136            }
137        })?;
138
139        Self::new(key, file_path, tool_type, file_type)
140    }
141
142    pub fn with_base_url(mut self, base_url: impl Into<String>) -> Self {
143        self.api_base = ApiBase::Custom(base_url.into());
144        self.url = self
145            .endpoint_config
146            .url(&self.api_base, paths::FILE_PARSER_CREATE);
147        self
148    }
149
150    pub fn with_endpoint_config(mut self, endpoint_config: EndpointConfig) -> Self {
151        self.endpoint_config = endpoint_config;
152        self.url = self
153            .endpoint_config
154            .url(&self.api_base, paths::FILE_PARSER_CREATE);
155        self
156    }
157
158    pub fn with_http_config(mut self, config: HttpClientConfig) -> Self {
159        self.http_config = Arc::new(config);
160        self
161    }
162
163    /// Sends the file parser task creation request.
164    ///
165    /// ## Returns
166    ///
167    /// A `FileParserCreateResponse` containing the task ID and status.
168    pub async fn send(&self) -> ZaiResult<FileParserCreateResponse> {
169        debug!(file = %self.file_path.display(), "Creating file parser task");
170
171        let file_bytes = tokio::fs::read(&self.file_path).await?;
172        let file_name = self
173            .file_path
174            .file_name()
175            .unwrap_or_default()
176            .to_string_lossy()
177            .to_string();
178
179        trace!(bytes = file_bytes.len(), file_name = %file_name, "Prepared parser upload");
180
181        let url = self.url.clone();
182        let key = self.key.clone();
183        let config = self.http_config.clone();
184        let tool_type = self.tool_type.clone();
185        let file_type = self.file_type.clone();
186        let response = send_multipart_request(reqwest::Method::POST, url, key, config, move || {
187            let file_part = reqwest::multipart::Part::bytes(file_bytes.clone())
188                .file_name(file_name.clone())
189                .mime_str("application/octet-stream")?;
190            Ok(reqwest::multipart::Form::new()
191                .part("file", file_part)
192                .text("tool_type", format!("{:?}", tool_type).to_lowercase())
193                .text("file_type", format!("{:?}", file_type)))
194        })
195        .await
196        .map_err(|e| e.context("file parser create"))?;
197
198        let create_response =
199            parse_typed_response::<FileParserCreateResponse>(response)
200                .await
201                .map_err(|e| e.context("file parser create"))?;
202
203        debug!(task_id = %create_response.task_id, "File parser task created");
204
205        if !create_response.is_success() {
206            warn!(
207                message = %create_response.message,
208                "File parser task creation rejected by server"
209            );
210            return Err(crate::client::error::ZaiError::ApiError {
211                code: codes::SDK_EXTERNAL_TOOL,
212                message: format!("Task creation failed: {}", create_response.message),
213            });
214        }
215
216        Ok(create_response)
217    }
218}