deepdoctection 1.2.9__tar.gz → 1.2.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/PKG-INFO +1 -1
  2. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/pyproject.toml +1 -1
  3. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/__init__.py +1 -1
  4. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/anngen.py +157 -45
  5. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/order.py +1 -1
  6. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection.egg-info/PKG-INFO +1 -1
  7. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/README.md +0 -0
  8. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/setup.cfg +0 -0
  9. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/analyzer/__init__.py +0 -0
  10. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/analyzer/config.py +0 -0
  11. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/analyzer/dd.py +0 -0
  12. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/analyzer/factory.py +0 -0
  13. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/configs/__init__.py +0 -0
  14. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/configs/conf_dd_one.yaml +0 -0
  15. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/configs/conf_tesseract.yaml +0 -0
  16. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/configs/profiles.jsonl +0 -0
  17. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/eval/__init__.py +0 -0
  18. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/eval/accmetric.py +0 -0
  19. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/eval/base.py +0 -0
  20. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/eval/cocometric.py +0 -0
  21. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/eval/eval.py +0 -0
  22. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/eval/registry.py +0 -0
  23. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/eval/tedsmetric.py +0 -0
  24. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/__init__.py +0 -0
  25. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/base.py +0 -0
  26. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/d2detect.py +0 -0
  27. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/deskew.py +0 -0
  28. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/doctrocr.py +0 -0
  29. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/hfdetr.py +0 -0
  30. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/hflayoutlm.py +0 -0
  31. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/hflm.py +0 -0
  32. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/model.py +0 -0
  33. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/pdftext.py +0 -0
  34. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/tessocr.py +0 -0
  35. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/extern/texocr.py +0 -0
  36. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/__init__.py +0 -0
  37. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/base.py +0 -0
  38. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/common.py +0 -0
  39. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/concurrency.py +0 -0
  40. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/doctectionpipe.py +0 -0
  41. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/language.py +0 -0
  42. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/layout.py +0 -0
  43. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/lm.py +0 -0
  44. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/refine.py +0 -0
  45. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/registry.py +0 -0
  46. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/segment.py +0 -0
  47. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/sub_layout.py +0 -0
  48. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/text.py +0 -0
  49. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/pipe/transform.py +0 -0
  50. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/py.typed +0 -0
  51. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/train/__init__.py +0 -0
  52. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/train/d2_frcnn_train.py +0 -0
  53. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/train/hf_detr_train.py +0 -0
  54. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection/train/hf_layoutlm_train.py +0 -0
  55. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection.egg-info/SOURCES.txt +0 -0
  56. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection.egg-info/dependency_links.txt +0 -0
  57. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection.egg-info/requires.txt +0 -0
  58. {deepdoctection-1.2.9 → deepdoctection-1.2.11}/src/deepdoctection.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepdoctection
3
- Version: 1.2.9
3
+ Version: 1.2.11
4
4
  Summary: Repository for Document AI - server/inference core package
5
5
  Author: Dr. Janis Meyer
6
6
  License: Apache License 2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepdoctection"
7
- version = "1.2.9"
7
+ version = "1.2.11"
8
8
  authors = [
9
9
  {name = "Dr. Janis Meyer"}
10
10
  ]
@@ -12,7 +12,7 @@ from dd_core.utils.env_info import collect_env_info
12
12
  from dd_core.utils.file_utils import _LazyModule
13
13
  from dd_core.utils.logger import LoggingRecord, logger
14
14
 
15
- __version__ = "1.2.9"
15
+ __version__ = "1.2.11"
16
16
  _IMPORT_STRUCTURE = {
17
17
  "analyzer": ["config_sanity_checks", "get_dd_analyzer", "ServiceFactory", "update_cfg_from_defaults"],
18
18
  "eval": [
@@ -50,6 +50,9 @@ class DataPointCacheStore(ABC):
50
50
  datapoints.
51
51
  """
52
52
 
53
+ def __init__(self, max_pages: int) -> None:
54
+ self.max_pages = max_pages
55
+
53
56
  @abstractmethod
54
57
  def put_datapoint(self, document_id: str, page_number: int, image: Image, job_id: str | None = None) -> None:
55
58
  """
@@ -127,8 +130,9 @@ class LocalDataPointCacheStore(DataPointCacheStore):
127
130
  Args:
128
131
  max_pages (int): Maximum number of pages to keep per document.
129
132
  """
130
- self.max_pages = max_pages
133
+
131
134
  self._pages: dict[str, dict[int, dict[str, Any]]] = {}
135
+ super().__init__(max_pages)
132
136
 
133
137
  def _get_cache_key(self, document_id: str, job_id: str | None) -> str:
134
138
  """
@@ -211,6 +215,17 @@ class DatapointManager:
211
215
  - `image.clear_image()`
212
216
  - `image.remove_image_from_lower_hierarchy(pixel_values_only=True)`
213
217
 
218
+ Async concurrency:
219
+ When `enable_async_handle=True`, the manager maintains a per-job-id registry of active datapoints
220
+ (`_datapoint_handle`, `_cache_anns_handle`). This makes it safe to use a single component instance
221
+ across concurrent async calls, each identified by a unique `job_id`. The assumption is that a given
222
+ `job_id` maps to exactly one document and its pages are processed sequentially within a job.
223
+
224
+ Use `set_datapoint(dp, job_id)` / `get_datapoint(job_id)` / `release_datapoint(job_id)` for the
225
+ async path. Pass `job_id` to all annotation methods that accept it to route to the correct slot.
226
+
227
+ When `enable_async_handle=False` (default), all behavior is identical to the original implementation.
228
+
214
229
  The manager is part of each `PipelineComponent`.
215
230
  """
216
231
 
@@ -220,21 +235,83 @@ class DatapointManager:
220
235
  model_id: Optional[str] = None,
221
236
  num_cached_datapoints: int = 0,
222
237
  remove_pixel_values_from_cache: bool = True,
223
- cache_store: LocalDataPointCacheStore | None = None,
238
+ cache_store: DataPointCacheStore | None = None,
239
+ enable_async_handle: bool = False,
224
240
  ) -> None:
225
241
  self._datapoint: Optional[Image] = None
226
242
  self._cache_anns: dict[str, ImageAnnotation] = {}
227
243
  self.datapoint_is_passed: bool = False
228
244
  self.service_id = service_id
229
245
  self.model_id = model_id
246
+ self.enable_async_handle = enable_async_handle
230
247
 
231
248
  if num_cached_datapoints < 0:
232
249
  raise ValueError("num_cached_datapoints must be >= 0")
233
250
  self.remove_pixel_values_from_cache = remove_pixel_values_from_cache
234
251
 
235
- self._cache_store = cache_store or LocalDataPointCacheStore(max_pages=num_cached_datapoints)
252
+ self._cache_store: DataPointCacheStore = cache_store or LocalDataPointCacheStore(
253
+ max_pages=num_cached_datapoints
254
+ )
236
255
  self.num_cached_datapoints = self._cache_store.max_pages
237
256
 
257
+ self._datapoint_handle: dict[str, Image] = {}
258
+ self._cache_anns_handle: dict[str, dict[str, ImageAnnotation]] = {}
259
+
260
+ def set_datapoint(self, dp: Image, job_id: str) -> None:
261
+ """
262
+ Register a datapoint for the given job_id in the async-safe slot.
263
+
264
+ Only used when `enable_async_handle=True`. Must be called before any annotation
265
+ methods are invoked with this job_id.
266
+
267
+ Args:
268
+ dp: The image datapoint to register.
269
+ job_id: Unique identifier for the current processing job.
270
+ """
271
+ self._datapoint_handle[job_id] = dp
272
+ self._cache_anns_handle[job_id] = {ann.annotation_id: ann for ann in dp.get_annotation()}
273
+
274
+ def get_datapoint(self, job_id: str) -> Image:
275
+ """
276
+ Retrieve the datapoint registered for the given job_id.
277
+
278
+ Args:
279
+ job_id: Unique identifier for the current processing job.
280
+
281
+ Returns:
282
+ The registered image datapoint.
283
+
284
+ Raises:
285
+ ValueError: If no datapoint has been registered for this job_id.
286
+ """
287
+ try:
288
+ return self._datapoint_handle[job_id]
289
+ except KeyError as exc:
290
+ raise ValueError(f"No datapoint passed for job_id={job_id}") from exc
291
+
292
+ def release_datapoint(self, job_id: str) -> None:
293
+ """
294
+ Remove the datapoint slot for the given job_id.
295
+
296
+ Should be called once the datapoint leaves the component (after `maybe_cache_datapoint`)
297
+ to free memory and avoid stale state.
298
+
299
+ Args:
300
+ job_id: Unique identifier for the current processing job.
301
+ """
302
+ self._datapoint_handle.pop(job_id, None)
303
+ self._cache_anns_handle.pop(job_id, None)
304
+
305
+ def _resolve_datapoint(self, job_id: str | None) -> Image:
306
+ if self.enable_async_handle and job_id is not None:
307
+ return self.get_datapoint(job_id)
308
+ return self.datapoint
309
+
310
+ def _resolve_cache_anns(self, job_id: str | None) -> dict[str, ImageAnnotation]:
311
+ if self.enable_async_handle and job_id is not None:
312
+ return self._cache_anns_handle[job_id]
313
+ return self._cache_anns
314
+
238
315
  def maybe_cache_datapoint(self, image: Optional[Image], job_id: str | None = None) -> None:
239
316
  """
240
317
  Cache the given datapoint if caching is enabled.
@@ -250,10 +327,8 @@ class DatapointManager:
250
327
  return
251
328
  if self.num_cached_datapoints <= 0:
252
329
  return
253
-
254
330
  if self.remove_pixel_values_from_cache:
255
331
  image.clear_image()
256
-
257
332
  self._cache_store.put_datapoint(
258
333
  document_id=image.document_id,
259
334
  page_number=image.page_number,
@@ -292,14 +367,23 @@ class DatapointManager:
292
367
  """Re-sets the model_id."""
293
368
  self.model_id = model_id
294
369
 
295
- def assert_datapoint_passed(self) -> None:
370
+ def assert_datapoint_passed(self, job_id: str | None = None) -> None:
296
371
  """
297
372
  Asserts that a datapoint is passed.
298
373
 
374
+ When `enable_async_handle=True` and `job_id` is provided, checks the per-job slot.
375
+ Otherwise falls back to the legacy flag check.
376
+
377
+ Args:
378
+ job_id: Optional job identifier used for async routing.
379
+
299
380
  Raises:
300
381
  AssertionError: If a datapoint has not been passed to `DatapointManager` before creating annotations.
301
382
  """
302
- assert self.datapoint_is_passed, "Pass datapoint to DatapointManager before creating anns"
383
+ if self.enable_async_handle and job_id is not None:
384
+ assert job_id in self._datapoint_handle, "Pass datapoint to DatapointManager before creating anns"
385
+ else:
386
+ assert self.datapoint_is_passed, "Pass datapoint to DatapointManager before creating anns"
303
387
 
304
388
  def set_image_annotation(
305
389
  self,
@@ -309,6 +393,7 @@ class DatapointManager:
309
393
  crop_image: bool = False,
310
394
  detect_result_max_width: Optional[float] = None,
311
395
  detect_result_max_height: Optional[float] = None,
396
+ job_id: str | None = None,
312
397
  ) -> Optional[str]:
313
398
  """
314
399
  Creates an image annotation from a raw `DetectionResult` dataclass.
@@ -332,6 +417,7 @@ class DatapointManager:
332
417
  pass the max width possible so coordinates can be rescaled.
333
418
  detect_result_max_height: If the detect result has a different scaling scheme from the image it refers to,
334
419
  pass the max height possible so coordinates can be rescaled.
420
+ job_id: Optional job identifier for async routing.
335
421
 
336
422
  Returns:
337
423
  The `annotation_id` of the generated image annotation, or `None` if there was a context error.
@@ -340,13 +426,15 @@ class DatapointManager:
340
426
  TypeError: If `detect_result.box` is not of type list or `np.ndarray`.
341
427
  ValueError: If the parent annotation's image is None or if the annotation's image is None.
342
428
  """
343
- self.assert_datapoint_passed()
429
+ self.assert_datapoint_passed(job_id)
344
430
  if not isinstance(detect_result.box, (list, np.ndarray)):
345
431
  raise TypeError(
346
432
  f"detect_result.box must be of type list or np.ndarray, but is of type {(type(detect_result.box))}"
347
433
  )
434
+ dp = self._resolve_datapoint(job_id)
435
+ cache_anns = self._resolve_cache_anns(job_id)
348
436
  with MappingContextManager(
349
- dp_name=self.datapoint.file_name, filter_level="annotation", detect_result=asdict(detect_result)
437
+ dp_name=dp.file_name, filter_level="annotation", detect_result=asdict(detect_result)
350
438
  ) as annotation_context:
351
439
  box = BoundingBox(
352
440
  ulx=detect_result.box[0],
@@ -360,8 +448,8 @@ class DatapointManager:
360
448
  box,
361
449
  detect_result_max_width,
362
450
  detect_result_max_height,
363
- self.datapoint.width,
364
- self.datapoint.height,
451
+ dp.width,
452
+ dp.height,
365
453
  )
366
454
  ann = ImageAnnotation(
367
455
  category_name=detect_result.class_name,
@@ -372,7 +460,7 @@ class DatapointManager:
372
460
  model_id=self.model_id,
373
461
  )
374
462
  if to_annotation_id is not None:
375
- parent_ann = self._cache_anns[to_annotation_id]
463
+ parent_ann = cache_anns[to_annotation_id]
376
464
  if parent_ann.image is None:
377
465
  raise ValueError("image cannot be None")
378
466
  parent_ann.image.dump(ann)
@@ -381,8 +469,8 @@ class DatapointManager:
381
469
  ann.bounding_box.transform( # type:ignore # pylint: disable=E1101
382
470
  image_width=parent_ann.image.width, image_height=parent_ann.image.height, absolute_coords=True
383
471
  ),
384
- parent_ann.get_bounding_box(self.datapoint.image_id).transform(
385
- image_width=self.datapoint.width, image_height=self.datapoint.height, absolute_coords=True
472
+ parent_ann.get_bounding_box(dp.image_id).transform(
473
+ image_width=dp.width, image_height=dp.height, absolute_coords=True
386
474
  ),
387
475
  )
388
476
  if ann.image is None:
@@ -394,16 +482,16 @@ class DatapointManager:
394
482
  ),
395
483
  )
396
484
  ann.image.set_embedding( # pylint: disable=E1101
397
- self.datapoint.image_id,
398
- ann_global_box.transform(image_width=self.datapoint.width, image_height=self.datapoint.height),
485
+ dp.image_id,
486
+ ann_global_box.transform(image_width=dp.width, image_height=dp.height),
399
487
  )
400
488
  parent_ann.dump_relationship(RelationshipKey.CHILD, ann.annotation_id)
401
489
 
402
- self.datapoint.dump(ann)
403
- self._cache_anns[ann.annotation_id] = ann
490
+ dp.dump(ann)
491
+ cache_anns[ann.annotation_id] = ann
404
492
 
405
493
  if to_image and to_annotation_id is None:
406
- self.datapoint.image_ann_to_image(annotation_id=ann.annotation_id, crop_image=crop_image)
494
+ dp.image_ann_to_image(annotation_id=ann.annotation_id, crop_image=crop_image)
407
495
 
408
496
  if annotation_context.context_error:
409
497
  return None
@@ -416,6 +504,7 @@ class DatapointManager:
416
504
  sub_cat_key: ObjectTypes,
417
505
  annotation_id: str,
418
506
  score: Optional[float] = None,
507
+ job_id: str | None = None,
419
508
  ) -> Optional[str]:
420
509
  """
421
510
  Creates a category annotation and dumps it as a subcategory to an already created annotation.
@@ -426,13 +515,16 @@ class DatapointManager:
426
515
  sub_cat_key: The key to dump the created annotation to.
427
516
  annotation_id: The id of the parent annotation. Currently, this can only be an image annotation.
428
517
  score: The score to add.
518
+ job_id: Optional job identifier for async routing.
429
519
 
430
520
  Returns:
431
521
  The `annotation_id` of the generated category annotation, or `None` if there was a context error.
432
522
  """
433
- self.assert_datapoint_passed()
523
+ self.assert_datapoint_passed(job_id)
524
+ dp = self._resolve_datapoint(job_id)
525
+ cache_anns = self._resolve_cache_anns(job_id)
434
526
  with MappingContextManager(
435
- dp_name=self.datapoint.file_name,
527
+ dp_name=dp.file_name,
436
528
  filter_level="annotation",
437
529
  category_annotation={
438
530
  "category_name": category_name.value,
@@ -447,7 +539,7 @@ class DatapointManager:
447
539
  service_id=self.service_id,
448
540
  model_id=self.model_id,
449
541
  )
450
- self._cache_anns[annotation_id].dump_sub_category(sub_cat_key, cat_ann)
542
+ cache_anns[annotation_id].dump_sub_category(sub_cat_key, cat_ann)
451
543
  if annotation_context.context_error:
452
544
  return None
453
545
  return cat_ann.annotation_id
@@ -460,6 +552,7 @@ class DatapointManager:
460
552
  annotation_id: str,
461
553
  value: Union[str, list[str], ReferencePayload],
462
554
  score: Optional[float] = None,
555
+ job_id: str | None = None,
463
556
  ) -> Optional[str]:
464
557
  """
465
558
  Creates a container annotation and dumps it as a subcategory to an already created annotation.
@@ -471,13 +564,16 @@ class DatapointManager:
471
564
  annotation_id: The id of the parent annotation. Currently, this can only be an image annotation.
472
565
  value: The value to store.
473
566
  score: The score to add.
567
+ job_id: Optional job identifier for async routing.
474
568
 
475
569
  Returns:
476
570
  The `annotation_id` of the generated container annotation, or None if there was a context error.
477
571
  """
478
- self.assert_datapoint_passed()
572
+ self.assert_datapoint_passed(job_id)
573
+ dp = self._resolve_datapoint(job_id)
574
+ cache_anns = self._resolve_cache_anns(job_id)
479
575
  with MappingContextManager(
480
- dp_name=self.datapoint.file_name,
576
+ dp_name=dp.file_name,
481
577
  filter_level="annotation",
482
578
  container_annotation={
483
579
  "category_name": category_name.value,
@@ -494,13 +590,17 @@ class DatapointManager:
494
590
  service_id=self.service_id,
495
591
  model_id=self.model_id,
496
592
  )
497
- self._cache_anns[annotation_id].dump_sub_category(sub_cat_key, cont_ann)
593
+ cache_anns[annotation_id].dump_sub_category(sub_cat_key, cont_ann)
498
594
  if annotation_context.context_error:
499
595
  return None
500
596
  return cont_ann.annotation_id
501
597
 
502
598
  def set_relationship_annotation(
503
- self, relationship_name: ObjectTypes, target_annotation_id: str, annotation_id: str
599
+ self,
600
+ relationship_name: ObjectTypes,
601
+ target_annotation_id: str,
602
+ annotation_id: str,
603
+ job_id: str | None = None,
504
604
  ) -> Optional[str]:
505
605
  """
506
606
  Creates a relationship annotation and dumps it to the target annotation.
@@ -509,14 +609,17 @@ class DatapointManager:
509
609
  relationship_name: The relationship key.
510
610
  target_annotation_id: The `annotation_id` of the parent `ImageAnnotation`.
511
611
  annotation_id: The `annotation_id` to dump the relationship to.
612
+ job_id: Optional job identifier for async routing.
512
613
 
513
614
  Returns:
514
615
  The `annotation_id` of the parent `ImageAnnotation` for reference if the dump has been successful, or `None`
515
616
  if there was a context error.
516
617
  """
517
- self.assert_datapoint_passed()
618
+ self.assert_datapoint_passed(job_id)
619
+ dp = self._resolve_datapoint(job_id)
620
+ cache_anns = self._resolve_cache_anns(job_id)
518
621
  with MappingContextManager(
519
- dp_name=self.datapoint.file_name,
622
+ dp_name=dp.file_name,
520
623
  filter_level="annotation",
521
624
  relationship_annotation={
522
625
  "relationship_name": relationship_name.value,
@@ -524,7 +627,7 @@ class DatapointManager:
524
627
  "annotation_id": annotation_id,
525
628
  },
526
629
  ) as annotation_context:
527
- self._cache_anns[target_annotation_id].dump_relationship(relationship_name, annotation_id)
630
+ cache_anns[target_annotation_id].dump_relationship(relationship_name, annotation_id)
528
631
  if annotation_context.context_error:
529
632
  return None
530
633
  return target_annotation_id
@@ -537,6 +640,7 @@ class DatapointManager:
537
640
  summary_value: Optional[Union[str, int, float, list[str], dict[str, Any]]] = None,
538
641
  summary_score: Optional[float] = None,
539
642
  annotation_id: Optional[str] = None,
643
+ job_id: str | None = None,
540
644
  ) -> Optional[str]:
541
645
  """
542
646
  Creates a subcategory of a summary annotation.
@@ -550,15 +654,18 @@ class DatapointManager:
550
654
  summary_value: Creates a `ContainerAnnotation` and stores the corresponding value.
551
655
  summary_score: Stores the score.
552
656
  annotation_id: The id of the parent annotation. Note that the parent annotation must have `image` not None.
657
+ job_id: Optional job identifier for async routing.
553
658
 
554
659
  Returns:
555
660
  The `annotation_id` of the generated category annotation, or None if there was a context error.
556
661
  """
557
- self.assert_datapoint_passed()
662
+ self.assert_datapoint_passed(job_id)
663
+ cache_anns = self._resolve_cache_anns(job_id)
664
+ dp = self._resolve_datapoint(job_id)
558
665
  if annotation_id is not None:
559
- image = self._cache_anns[annotation_id].image
666
+ image = cache_anns[annotation_id].image
560
667
  else:
561
- image = self.datapoint
668
+ image = dp
562
669
  assert image is not None, image
563
670
 
564
671
  ann: Union[CategoryAnnotation, ContainerAnnotation]
@@ -595,47 +702,53 @@ class DatapointManager:
595
702
  return None
596
703
  return ann.annotation_id
597
704
 
598
- def remove_annotations(self, annotation_ids: Sequence[str]) -> None:
705
+ def remove_annotations(self, annotation_ids: Sequence[str], job_id: str | None = None) -> None:
599
706
  """
600
707
  Removes the annotation by the given `annotation_id`.
601
708
 
602
709
  Args:
603
710
  annotation_ids: The `annotation_id` to remove.
711
+ job_id: Optional job identifier for async routing.
604
712
  """
605
- self.assert_datapoint_passed()
606
- self.datapoint.remove(annotation_ids)
713
+ self.assert_datapoint_passed(job_id)
714
+ dp = self._resolve_datapoint(job_id)
715
+ cache_anns = self._resolve_cache_anns(job_id)
716
+ dp.remove(annotation_ids)
607
717
  for ann_id in annotation_ids:
608
- if ann_id in self._cache_anns:
609
- self._cache_anns.pop(ann_id)
718
+ if ann_id in cache_anns:
719
+ cache_anns.pop(ann_id)
610
720
 
611
- def deactivate_annotation(self, annotation_id: str) -> None:
721
+ def deactivate_annotation(self, annotation_id: str, job_id: str | None = None) -> None:
612
722
  """
613
723
  Deactivates the annotation by the given `annotation_id`.
614
724
 
615
725
  Args:
616
726
  annotation_id: The `annotation_id` to deactivate.
727
+ job_id: Optional job identifier for async routing.
617
728
  """
618
- ann = self._cache_anns[annotation_id]
729
+ ann = self._resolve_cache_anns(job_id)[annotation_id]
619
730
  ann.deactivate()
620
731
 
621
- def get_annotation(self, annotation_id: str) -> ImageAnnotation:
732
+ def get_annotation(self, annotation_id: str, job_id: str | None = None) -> ImageAnnotation:
622
733
  """
623
734
  Gets a single `ImageAnnotation`.
624
735
 
625
736
  Args:
626
737
  annotation_id: The `annotation_id` of the annotation to retrieve.
738
+ job_id: Optional job identifier for async routing.
627
739
 
628
740
  Returns:
629
741
  The `ImageAnnotation` corresponding to the given `annotation_id`.
630
742
  """
631
- return self._cache_anns[annotation_id]
743
+ return self._resolve_cache_anns(job_id)[annotation_id]
632
744
 
633
- def get_cached_datapoints(self, last_k: int) -> tuple[Image, ...]:
745
+ def get_cached_datapoints(self, last_k: int, job_id: str | None = None) -> tuple[Image, ...]:
634
746
  """
635
747
  Returns a snapshot of the last `last_k` cached datapoints without removing them.
636
748
 
637
749
  Args:
638
750
  last_k: Number of most recently cached datapoints to return. Must be >= 0.
751
+ job_id: Optional job identifier to retrieve from the correct cache slot.
639
752
 
640
753
  Returns:
641
754
  A tuple containing up to the last `last_k` cached `Image` objects, ordered from older -> newer.
@@ -645,6 +758,5 @@ class DatapointManager:
645
758
  raise ValueError("last_k must be >= 0")
646
759
  if last_k == 0:
647
760
  return tuple()
648
-
649
- doc_id = self.datapoint.document_id
650
- return self._cache_store.get_datapoints(document_id=doc_id, last_d=last_k)
761
+ doc_id = self._resolve_datapoint(job_id).document_id
762
+ return self._cache_store.get_datapoints(document_id=doc_id, last_d=last_k, job_id=job_id)
@@ -373,7 +373,7 @@ class OrderGenerator:
373
373
  @staticmethod
374
374
  def _make_column_detect_results(columns: Sequence[BoundingBox]) -> Sequence[DetectionResult]:
375
375
  column_detect_result_list = []
376
- if os.environ.get("LOG_LEVEL", "INFO") == "DEBUG":
376
+ if os.environ.get("SHOW_WORD_ORDER_COLUMN", "False") == "True":
377
377
  for box in columns:
378
378
  column_detect_result_list.append(
379
379
  DetectionResult(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepdoctection
3
- Version: 1.2.9
3
+ Version: 1.2.11
4
4
  Summary: Repository for Document AI - server/inference core package
5
5
  Author: Dr. Janis Meyer
6
6
  License: Apache License 2.0