88from functools import cache
99from io import BytesIO
1010from pathlib import Path
11- from typing import Annotated , Any , NoReturn , Self
11+ from typing import Any , NoReturn , Self
1212
1313from docling .datamodel .accelerator_options import AcceleratorDevice
1414from icij_common .pydantic_utils import (
1515 icij_config ,
1616 merge_configs ,
1717 no_enum_values_config ,
18- safe_copy ,
1918)
20- from pydantic import AfterValidator , Field , TypeAdapter
2119from pydantic import BaseModel as _BaseModel
20+ from pydantic import Field , TypeAdapter
2221
2322logger = logging .getLogger (__name__ )
2423base_config = merge_configs (icij_config (), no_enum_values_config ())
@@ -120,8 +119,8 @@ def to_marker(self) -> str:
120119
121120class Status (StrEnum ):
122121 FAILURE = "failure"
123- SUCCESS = "success"
124122 PARTIAL_SUCCESS = "partial_success"
123+ SUCCESS = "success"
125124
126125 @classmethod
127126 def from_docling (cls , v : Any ) -> Self :
@@ -139,6 +138,26 @@ def from_docling(cls, v: Any) -> Self:
139138 def allows_conversion (self ) -> bool :
140139 return self is Status .SUCCESS or self is Status .PARTIAL_SUCCESS
141140
141+ def __add__ (self , other : "Status" ) -> "Status" :
142+ if not isinstance (other , Status ):
143+ msg = (
144+ f"can't add { other } of type { other .__class__ .__name__ } "
145+ f"to { self .__class__ .__name__ } "
146+ )
147+ raise TypeError (msg )
148+ statuses = sorted ((self , other ), key = lambda x : x .value )
149+ match statuses :
150+ case (Status .FAILURE , Status .FAILURE ):
151+ return Status .FAILURE
152+ case (Status .FAILURE , Status .SUCCESS ):
153+ return Status .PARTIAL_SUCCESS
154+ case (_, Status .PARTIAL_SUCCESS ) | (Status .PARTIAL_SUCCESS , _):
155+ return Status .PARTIAL_SUCCESS
156+ case (Status .SUCCESS , Status .SUCCESS ):
157+ return Status .SUCCESS
158+ case _:
159+ raise ValueError (f"unexpected value { statuses } " )
160+
142161
143162class Error (BaseModel ):
144163 id : str
@@ -179,30 +198,24 @@ def _id_title(title: str) -> str:
179198class InputDoc (BaseModel ):
180199 ext : SupportedExt
181200 path : Path
182- content : bytes | None = None
201+ n_pages : int
183202
184203 @classmethod
185- def from_path (cls , path : str | Path ) -> Self :
204+ def from_path (cls , path : str | Path , n_pages : int ) -> Self :
186205 if isinstance (path , str ):
187206 path = Path (path )
188207 ext = SupportedExt (path .suffix )
189- return cls (path = path , ext = ext )
208+ return cls (path = path , ext = ext , n_pages = n_pages )
190209
191210 def to_docling (self ): # noqa: ANN201
192211 from docling_core .types .io import DocumentStream # noqa: PLC0415
193212
194- if self .content is not None :
195- return DocumentStream (name = str (self .path ), stream = BytesIO (self .content ))
196-
197213 if not self .path .suffix :
198214 return DocumentStream (
199215 name = str (self .path ), stream = BytesIO (self .path .read_bytes ())
200216 )
201217 return self .path
202218
203- def without_content (self ) -> Self :
204- return safe_copy (self , update = {"content" : None })
205-
206219
207220Ranges = list [tuple [int , int ]]
208221
@@ -223,6 +236,7 @@ def from_pages_bytes_sizes(cls, sizes: Sequence[int]) -> Self:
223236class ConversionOutput (BaseModel ):
224237 path : Path
225238 pages : Pages = Field (default_factory = Pages )
239+ confidence : float | None
226240
227241
228242class MarkdownDoc (ConversionOutput ):
@@ -235,20 +249,14 @@ def _valid_conversion_statuses(cls) -> set:
235249 return {ConversionStatus .SUCCESS , ConversionStatus .PARTIAL_SUCCESS }
236250
237251
238- def _input_should_not_have_content (value : InputDoc ) -> InputDoc :
239- if value .content is not None :
240- raise ValueError (f"response input can't have content, but got { value } " )
241- return value
242-
243-
244252class _BaseResult (BaseModel , ABC ):
245253 input : InputDoc
246254 status : Status
247255 errors : list [Error ] = []
248256
249257
250258class ResponseResult (_BaseResult ):
251- input : Annotated [ InputDoc , AfterValidator ( func = _input_should_not_have_content )]
259+ input : InputDoc
252260 output_path : Path
253261
254262
@@ -258,7 +266,7 @@ class Result(_BaseResult):
258266
259267 def to_response (self ) -> ResponseResult :
260268 return ResponseResult (
261- input = self .input . without_content () ,
269+ input = self .input ,
262270 status = self .status ,
263271 errors = self .errors ,
264272 output_path = self .output .path ,
0 commit comments