cloudiful_docling_convert/
conversion.rs1use crate::api::{DoclingClient, DoclingConfig};
2use crate::document::{
3 ConvertOptions, GenericFileConvertOptions, InputDocument, InputKind, PdfConvertOptions,
4 TextConvertOptions,
5};
6use crate::error::{PdfConvertError, Result};
7use crate::pdf::PdfInfo;
8use crate::processor::build_pdf_chunk_plan;
9
10#[derive(Debug, Clone)]
11pub struct DoclingRuntimeConfig {
12 pub docling_base_url: String,
13 pub openai_base_url: String,
14 pub vlm_pipeline_model: String,
15 pub picture_description_model: String,
16 pub code_formula_model: String,
17 pub api_key: Option<String>,
18}
19
20impl DoclingRuntimeConfig {
21 pub fn without_vlm(docling_base_url: impl Into<String>) -> Self {
22 Self {
23 docling_base_url: docling_base_url.into(),
24 openai_base_url: String::new(),
25 vlm_pipeline_model: String::new(),
26 picture_description_model: String::new(),
27 code_formula_model: String::new(),
28 api_key: None,
29 }
30 }
31
32 pub fn into_docling_config(self) -> DoclingConfig {
33 DoclingConfig {
34 base_url: self.docling_base_url,
35 openai_base_url: self.openai_base_url,
36 vlm_pipeline_model: self.vlm_pipeline_model,
37 picture_description_model: self.picture_description_model,
38 code_formula_model: self.code_formula_model,
39 api_key: self.api_key,
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
45pub struct ConversionBehavior {
46 pub pages_per_file: u32,
47 pub split_input: bool,
48 pub split_by_bookmark: bool,
49 pub chunking: bool,
50 pub batch_size: usize,
51}
52
53impl Default for ConversionBehavior {
54 fn default() -> Self {
55 Self {
56 pages_per_file: 5,
57 split_input: true,
58 split_by_bookmark: false,
59 chunking: false,
60 batch_size: 2,
61 }
62 }
63}
64
65pub fn build_docling_client(config: DoclingRuntimeConfig) -> Result<DoclingClient> {
66 DoclingClient::new(config.into_docling_config())
67}
68
69pub fn build_convert_options(
70 input_kind: InputKind,
71 behavior: &ConversionBehavior,
72) -> Result<ConvertOptions> {
73 match input_kind {
74 InputKind::Pdf => Ok(ConvertOptions::Pdf(PdfConvertOptions {
75 pages_per_file: behavior.pages_per_file,
76 split_input: behavior.split_input,
77 split_by_bookmark: behavior.split_by_bookmark,
78 chunking: behavior.chunking,
79 batch_size: behavior.batch_size,
80 })),
81 InputKind::Docx | InputKind::Markdown => {
82 reject_pdf_only_options(input_kind, behavior)?;
83 Ok(ConvertOptions::Generic(GenericFileConvertOptions {
84 chunking: behavior.chunking,
85 }))
86 }
87 InputKind::Text => {
88 reject_pdf_only_options(input_kind, behavior)?;
89 Ok(ConvertOptions::Text(TextConvertOptions::default()))
90 }
91 }
92}
93
94pub fn build_pdf_options(behavior: &ConversionBehavior) -> Result<PdfConvertOptions> {
95 let options = PdfConvertOptions {
96 pages_per_file: behavior.pages_per_file,
97 split_input: behavior.split_input,
98 split_by_bookmark: behavior.split_by_bookmark,
99 chunking: behavior.chunking,
100 batch_size: behavior.batch_size,
101 };
102 options.validate()?;
103 Ok(options)
104}
105
106pub fn count_input_chunks(input: &InputDocument, behavior: &ConversionBehavior) -> Result<usize> {
107 match input.kind()? {
108 InputKind::Pdf => {
109 let pdf_info = PdfInfo::load_from_bytes(input.bytes.as_ref())?;
110 let total = build_pdf_chunk_plan(&build_pdf_options(behavior)?, &pdf_info)
111 .len()
112 .max(1);
113 Ok(total)
114 }
115 _ => Ok(1),
116 }
117}
118
119fn reject_pdf_only_options(input_kind: InputKind, behavior: &ConversionBehavior) -> Result<()> {
120 if behavior.split_by_bookmark {
121 return Err(PdfConvertError::validation_error(
122 "split_by_bookmark",
123 format!(
124 "bookmark splitting is only available for PDF inputs, got {:?}",
125 input_kind
126 ),
127 ));
128 }
129
130 Ok(())
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn generic_input_rejects_bookmark_splitting() {
139 let err = build_convert_options(
140 InputKind::Docx,
141 &ConversionBehavior {
142 split_by_bookmark: true,
143 ..ConversionBehavior::default()
144 },
145 )
146 .unwrap_err();
147
148 assert!(err.to_string().contains("bookmark splitting"));
149 }
150}