byteit 1.2.0__tar.gz → 1.3.0__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 (52) hide show
  1. {byteit-1.2.0 → byteit-1.3.0}/CHANGELOG.md +10 -0
  2. {byteit-1.2.0/byteit.egg-info → byteit-1.3.0}/PKG-INFO +3 -3
  3. {byteit-1.2.0 → byteit-1.3.0}/README.md +2 -2
  4. {byteit-1.2.0 → byteit-1.3.0}/byteit/ByteITClient.py +2 -2
  5. {byteit-1.2.0 → byteit-1.3.0}/byteit/__init__.py +3 -3
  6. byteit-1.3.0/byteit/models/ParseType.py +11 -0
  7. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/ProcessingOptions.py +18 -18
  8. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/__init__.py +2 -3
  9. {byteit-1.2.0 → byteit-1.3.0}/byteit/validations.py +1 -1
  10. {byteit-1.2.0 → byteit-1.3.0/byteit.egg-info}/PKG-INFO +3 -3
  11. {byteit-1.2.0 → byteit-1.3.0}/byteit.egg-info/SOURCES.txt +9 -9
  12. {byteit-1.2.0 → byteit-1.3.0}/pyproject.toml +1 -1
  13. {byteit-1.2.0/tests → byteit-1.3.0/tests/unit}/test_client.py +32 -54
  14. {byteit-1.2.0/tests → byteit-1.3.0/tests/unit}/test_models.py +18 -12
  15. byteit-1.2.0/byteit/models/ExtractionType.py +0 -10
  16. {byteit-1.2.0 → byteit-1.3.0}/LICENSE +0 -0
  17. {byteit-1.2.0 → byteit-1.3.0}/MANIFEST.in +0 -0
  18. {byteit-1.2.0 → byteit-1.3.0}/byteit/_http.py +0 -0
  19. {byteit-1.2.0 → byteit-1.3.0}/byteit/_polling.py +0 -0
  20. {byteit-1.2.0 → byteit-1.3.0}/byteit/_rate_limit.py +0 -0
  21. {byteit-1.2.0 → byteit-1.3.0}/byteit/connectors/LocalFileInputConnector.py +0 -0
  22. {byteit-1.2.0 → byteit-1.3.0}/byteit/connectors/LocalFileOutputConnector.py +0 -0
  23. {byteit-1.2.0 → byteit-1.3.0}/byteit/connectors/S3InputConnector.py +0 -0
  24. {byteit-1.2.0 → byteit-1.3.0}/byteit/connectors/S3OutputConnector.py +0 -0
  25. {byteit-1.2.0 → byteit-1.3.0}/byteit/connectors/__init__.py +0 -0
  26. {byteit-1.2.0 → byteit-1.3.0}/byteit/connectors/base.py +0 -0
  27. {byteit-1.2.0 → byteit-1.3.0}/byteit/exceptions.py +0 -0
  28. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/CustomJob.py +0 -0
  29. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/CustomJobList.py +0 -0
  30. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/DocumentMetadata.py +0 -0
  31. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/DocumentType.py +0 -0
  32. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/ExtractJob.py +0 -0
  33. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/ExtractJobList.py +0 -0
  34. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/ExtractionSchema.py +0 -0
  35. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/JobList.py +0 -0
  36. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/JobStatus.py +0 -0
  37. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/OutputFormat.py +0 -0
  38. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/ParseJob.py +0 -0
  39. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/SavedSchema.py +0 -0
  40. {byteit-1.2.0 → byteit-1.3.0}/byteit/models/SavedSchemaList.py +0 -0
  41. {byteit-1.2.0 → byteit-1.3.0}/byteit/progress.py +0 -0
  42. {byteit-1.2.0 → byteit-1.3.0}/byteit.egg-info/dependency_links.txt +0 -0
  43. {byteit-1.2.0 → byteit-1.3.0}/byteit.egg-info/requires.txt +0 -0
  44. {byteit-1.2.0 → byteit-1.3.0}/byteit.egg-info/top_level.txt +0 -0
  45. {byteit-1.2.0 → byteit-1.3.0}/setup.cfg +0 -0
  46. {byteit-1.2.0 → byteit-1.3.0}/setup.py +0 -0
  47. {byteit-1.2.0/tests → byteit-1.3.0/tests/unit}/__init__.py +0 -0
  48. {byteit-1.2.0/tests → byteit-1.3.0/tests/unit}/test_connectors.py +0 -0
  49. {byteit-1.2.0/tests → byteit-1.3.0/tests/unit}/test_exceptions.py +0 -0
  50. {byteit-1.2.0/tests → byteit-1.3.0/tests/unit}/test_integration.py +0 -0
  51. {byteit-1.2.0/tests → byteit-1.3.0/tests/unit}/test_package_init.py +0 -0
  52. {byteit-1.2.0/tests → byteit-1.3.0/tests/unit}/test_progress.py +0 -0
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.0] - 2026-07-29
11
+
12
+ ### Added
13
+ - `ParseType.HANDWRITTEN` (`"handwritten"`) parse mode for handwritten documents.
14
+
15
+ ### Changed
16
+ - Renamed `ExtractionType` to `ParseType`.
17
+ - Renamed `ProcessingOptions.extraction_type` to `ProcessingOptions.parse_type`
18
+ (API payload key is now `parse_type`).
19
+
10
20
  ## [1.2.0] - 2026-07-28
11
21
 
12
22
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: byteit
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: AI-powered document intelligence platform - Turn your data into structured data with a single line of code.
5
5
  Author-email: ByteIT GmbH <support@byteit.ai>
6
6
  License: Apache-2.0
@@ -302,7 +302,7 @@ result = client.parse(
302
302
  processing_options={
303
303
  "languages": ["de", "en"],
304
304
  "page_range": "1-5",
305
- "extraction_type": "complex",
305
+ "parse_type": "complex",
306
306
  },
307
307
  )
308
308
  ```
@@ -313,7 +313,7 @@ Supported fields:
313
313
  |---|---|---|---|
314
314
  | `languages` | `list[str]` | `["en"]` | Language codes for OCR/parsing |
315
315
  | `page_range` | `str` | `""` (all pages) | Pages to process, e.g. `"1-5"` or `"1,3,5"` |
316
- | `extraction_type` | `str` | `"auto"` | One of `"auto"`, `"complex"`, `"ocr"`, etc. |
316
+ | `parse_type` | `str` | `"auto"` | One of `"auto"`, `"complex"`, `"handwritten"` |
317
317
  | `image_annotations` | `bool` | `False` | Enable image annotation extraction |
318
318
  | `force_image_annotations` | `bool` | `False` | Force annotation extraction even when the image is detected as useless |
319
319
  | `table_enrichment` | `bool` | `False` | Enable table enrichment |
@@ -264,7 +264,7 @@ result = client.parse(
264
264
  processing_options={
265
265
  "languages": ["de", "en"],
266
266
  "page_range": "1-5",
267
- "extraction_type": "complex",
267
+ "parse_type": "complex",
268
268
  },
269
269
  )
270
270
  ```
@@ -275,7 +275,7 @@ Supported fields:
275
275
  |---|---|---|---|
276
276
  | `languages` | `list[str]` | `["en"]` | Language codes for OCR/parsing |
277
277
  | `page_range` | `str` | `""` (all pages) | Pages to process, e.g. `"1-5"` or `"1,3,5"` |
278
- | `extraction_type` | `str` | `"auto"` | One of `"auto"`, `"complex"`, `"ocr"`, etc. |
278
+ | `parse_type` | `str` | `"auto"` | One of `"auto"`, `"complex"`, `"handwritten"` |
279
279
  | `image_annotations` | `bool` | `False` | Enable image annotation extraction |
280
280
  | `force_image_annotations` | `bool` | `False` | Force annotation extraction even when the image is detected as useless |
281
281
  | `table_enrichment` | `bool` | `False` | Enable table enrichment |
@@ -160,7 +160,7 @@ class ByteITClient:
160
160
  output: Optional file path to save the result to disk.
161
161
  processing_options: ProcessingOptions or dict with keys:
162
162
  ``languages`` (list[str]), ``page_range`` (str), and
163
- ``extraction_type`` (str or ExtractionType).
163
+ ``parse_type`` (str or ParseType).
164
164
  ``image_annotations`` (bool), ``force_image_annotations`` (bool),
165
165
  ``table_enrichment`` (bool)
166
166
  output_format: Optional output format override. When omitted, the
@@ -230,7 +230,7 @@ class ByteITClient:
230
230
  paths.
231
231
  processing_options: ProcessingOptions or dict with keys:
232
232
  ``languages`` (list[str]), ``page_range`` (str), and
233
- ``extraction_type`` (str or ExtractionType).
233
+ ``parse_type`` (str or ParseType).
234
234
  queue_for_batch: When ``True``, the job is queued for batch
235
235
  processing at a reduced credit cost. Processing is not
236
236
  immediate.
@@ -28,13 +28,13 @@ from .models.CustomJob import CustomJob
28
28
  from .models.CustomJobList import CustomJobList
29
29
  from .models.DocumentMetadata import DocumentMetadata
30
30
  from .models.DocumentType import DocumentType
31
- from .models.ExtractionType import ExtractionType
32
31
  from .models.ExtractJob import ExtractJob
33
32
  from .models.ExtractJobList import ExtractJobList
34
33
  from .models.JobList import JobList
35
34
  from .models.JobStatus import JobStatus
36
35
  from .models.OutputFormat import OutputFormat
37
36
  from .models.ParseJob import ParseJob
37
+ from .models.ParseType import ParseType
38
38
  from .models.ProcessingOptions import ProcessingOptions
39
39
  from .models.SavedSchema import SavedSchema
40
40
  from .models.SavedSchemaList import SavedSchemaList
@@ -49,7 +49,7 @@ except ImportError:
49
49
  try:
50
50
  __version__ = version("byteit")
51
51
  except PackageNotFoundError:
52
- __version__ = "1.2.0" # fallback, keep in sync with pyproject.toml
52
+ __version__ = "1.3.0" # fallback, keep in sync with pyproject.toml
53
53
 
54
54
  try:
55
55
  from .models.ExtractionSchema import ExtractionSchema # noqa: F401
@@ -104,7 +104,7 @@ __all__ = [
104
104
  "DocumentMetadata",
105
105
  "DocumentType",
106
106
  "ProcessingOptions",
107
- "ExtractionType",
107
+ "ParseType",
108
108
  "ExtractJob",
109
109
  "ExtractJobList",
110
110
  "OutputFormat",
@@ -0,0 +1,11 @@
1
+ """Document parse type value object."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class ParseType(Enum):
7
+ """Document parse type enumeration."""
8
+
9
+ AUTO = "auto"
10
+ COMPLEX = "complex"
11
+ HANDWRITTEN = "handwritten"
@@ -3,7 +3,7 @@
3
3
  from dataclasses import dataclass, field
4
4
  from typing import Any
5
5
 
6
- from byteit.models.ExtractionType import ExtractionType
6
+ from byteit.models.ParseType import ParseType
7
7
 
8
8
 
9
9
  def _default_list() -> list[str]:
@@ -24,7 +24,7 @@ class ProcessingOptions:
24
24
  force_image_annotations: Force image annotation extraction even when
25
25
  image is detected as useless
26
26
  table_enrichment: Enable table enrichment
27
- extraction_type: Extraction mode used by the backend parser
27
+ parse_type: Parse mode used by the backend parser
28
28
  """
29
29
 
30
30
  languages: list[str] = field(default_factory=_default_list)
@@ -32,26 +32,26 @@ class ProcessingOptions:
32
32
  image_annotations: bool = field(default=False)
33
33
  force_image_annotations: bool = field(default=False)
34
34
  table_enrichment: bool = field(default=False)
35
- extraction_type: ExtractionType | str = field(default=ExtractionType.AUTO)
35
+ parse_type: ParseType | str = field(default=ParseType.AUTO)
36
36
 
37
37
  @staticmethod
38
- def _parse_extraction_type(
39
- extraction_type: ExtractionType | str,
40
- ) -> ExtractionType:
41
- """Parse extraction type into an enum value."""
42
- if isinstance(extraction_type, ExtractionType):
43
- return extraction_type
44
-
45
- normalized_extraction_type = extraction_type.lower()
46
- for value in ExtractionType:
47
- if value.value == normalized_extraction_type:
38
+ def _parse_parse_type(
39
+ parse_type: ParseType | str,
40
+ ) -> ParseType:
41
+ """Parse parse type into an enum value."""
42
+ if isinstance(parse_type, ParseType):
43
+ return parse_type
44
+
45
+ normalized_parse_type = parse_type.lower()
46
+ for value in ParseType:
47
+ if value.value == normalized_parse_type:
48
48
  return value
49
49
 
50
- raise ValueError(f"Invalid extraction type: {extraction_type}")
50
+ raise ValueError(f"Invalid parse type: {parse_type}")
51
51
 
52
52
  def __post_init__(self) -> None:
53
53
  """Normalize processing option values after initialization."""
54
- self.extraction_type = self._parse_extraction_type(self.extraction_type)
54
+ self.parse_type = self._parse_parse_type(self.parse_type)
55
55
 
56
56
  if self.force_image_annotations:
57
57
  self.image_annotations = True
@@ -79,7 +79,7 @@ class ProcessingOptions:
79
79
  if self.table_enrichment:
80
80
  result["table_enrichment"] = self.table_enrichment
81
81
 
82
- result["extraction_type"] = self.extraction_type.value
82
+ result["parse_type"] = self.parse_type.value
83
83
 
84
84
  return result
85
85
 
@@ -98,7 +98,7 @@ class ProcessingOptions:
98
98
  image_annotations = data.get("image_annotations", False)
99
99
  force_image_annotations = data.get("force_image_annotations", False)
100
100
  table_enrichment = data.get("table_enrichment", False)
101
- extraction_type = data.get("extraction_type", ExtractionType.AUTO)
101
+ parse_type = data.get("parse_type", ParseType.AUTO)
102
102
 
103
103
  return cls(
104
104
  languages=languages,
@@ -106,5 +106,5 @@ class ProcessingOptions:
106
106
  image_annotations=image_annotations,
107
107
  force_image_annotations=force_image_annotations,
108
108
  table_enrichment=table_enrichment,
109
- extraction_type=extraction_type,
109
+ parse_type=parse_type,
110
110
  )
@@ -4,13 +4,13 @@ from .CustomJob import CustomJob
4
4
  from .CustomJobList import CustomJobList
5
5
  from .DocumentMetadata import DocumentMetadata
6
6
  from .DocumentType import DocumentType
7
- from .ExtractionType import ExtractionType
8
7
  from .ExtractJob import ExtractJob
9
8
  from .ExtractJobList import ExtractJobList
10
9
  from .JobList import JobList
11
10
  from .JobStatus import JobStatus
12
11
  from .OutputFormat import OutputFormat
13
12
  from .ParseJob import ParseJob
13
+ from .ParseType import ParseType
14
14
  from .ProcessingOptions import ProcessingOptions
15
15
  from .SavedSchema import SavedSchema
16
16
  from .SavedSchemaList import SavedSchemaList
@@ -22,12 +22,11 @@ __all__ = [
22
22
  "DocumentType",
23
23
  "ExtractJob",
24
24
  "ExtractJobList",
25
- "ExtractionType",
26
- "ExtractionType",
27
25
  "JobList",
28
26
  "JobStatus",
29
27
  "OutputFormat",
30
28
  "ParseJob",
29
+ "ParseType",
31
30
  "ProcessingOptions",
32
31
  "SavedSchema",
33
32
  "SavedSchemaList",
@@ -11,7 +11,7 @@ VALID_PROCESSING_OPTIONS: set[str] = {
11
11
  "image_annotations",
12
12
  "force_image_annotations",
13
13
  "table_enrichment",
14
- "extraction_type",
14
+ "parse_type",
15
15
  }
16
16
 
17
17
  # Multi-file upload limits. Mirror the backend
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: byteit
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: AI-powered document intelligence platform - Turn your data into structured data with a single line of code.
5
5
  Author-email: ByteIT GmbH <support@byteit.ai>
6
6
  License: Apache-2.0
@@ -302,7 +302,7 @@ result = client.parse(
302
302
  processing_options={
303
303
  "languages": ["de", "en"],
304
304
  "page_range": "1-5",
305
- "extraction_type": "complex",
305
+ "parse_type": "complex",
306
306
  },
307
307
  )
308
308
  ```
@@ -313,7 +313,7 @@ Supported fields:
313
313
  |---|---|---|---|
314
314
  | `languages` | `list[str]` | `["en"]` | Language codes for OCR/parsing |
315
315
  | `page_range` | `str` | `""` (all pages) | Pages to process, e.g. `"1-5"` or `"1,3,5"` |
316
- | `extraction_type` | `str` | `"auto"` | One of `"auto"`, `"complex"`, `"ocr"`, etc. |
316
+ | `parse_type` | `str` | `"auto"` | One of `"auto"`, `"complex"`, `"handwritten"` |
317
317
  | `image_annotations` | `bool` | `False` | Enable image annotation extraction |
318
318
  | `force_image_annotations` | `bool` | `False` | Force annotation extraction even when the image is detected as useless |
319
319
  | `table_enrichment` | `bool` | `False` | Enable table enrichment |
@@ -30,20 +30,20 @@ byteit/models/DocumentType.py
30
30
  byteit/models/ExtractJob.py
31
31
  byteit/models/ExtractJobList.py
32
32
  byteit/models/ExtractionSchema.py
33
- byteit/models/ExtractionType.py
34
33
  byteit/models/JobList.py
35
34
  byteit/models/JobStatus.py
36
35
  byteit/models/OutputFormat.py
37
36
  byteit/models/ParseJob.py
37
+ byteit/models/ParseType.py
38
38
  byteit/models/ProcessingOptions.py
39
39
  byteit/models/SavedSchema.py
40
40
  byteit/models/SavedSchemaList.py
41
41
  byteit/models/__init__.py
42
- tests/__init__.py
43
- tests/test_client.py
44
- tests/test_connectors.py
45
- tests/test_exceptions.py
46
- tests/test_integration.py
47
- tests/test_models.py
48
- tests/test_package_init.py
49
- tests/test_progress.py
42
+ tests/unit/__init__.py
43
+ tests/unit/test_client.py
44
+ tests/unit/test_connectors.py
45
+ tests/unit/test_exceptions.py
46
+ tests/unit/test_integration.py
47
+ tests/unit/test_models.py
48
+ tests/unit/test_package_init.py
49
+ tests/unit/test_progress.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "byteit"
7
- version = "1.2.0"
7
+ version = "1.3.0"
8
8
  description = "AI-powered document intelligence platform - Turn your data into structured data with a single line of code."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -9,6 +9,7 @@ import requests
9
9
  from pydantic import Field
10
10
 
11
11
  from byteit import ByteITClient
12
+ from byteit._rate_limit import RateLimitedSubmitter
12
13
  from byteit.connectors import LocalFileInputConnector
13
14
  from byteit.exceptions import (
14
15
  APIKeyError,
@@ -20,10 +21,10 @@ from byteit.exceptions import (
20
21
  ValidationError,
21
22
  )
22
23
  from byteit.models.ExtractionSchema import ExtractionSchema
23
- from byteit.models.ExtractionType import ExtractionType
24
24
  from byteit.models.ExtractJob import ExtractJob
25
25
  from byteit.models.OutputFormat import OutputFormat
26
26
  from byteit.models.ParseJob import ParseJob
27
+ from byteit.models.ParseType import ParseType
27
28
  from byteit.models.ProcessingOptions import ProcessingOptions
28
29
  from byteit.models.SavedSchema import SavedSchema
29
30
  from byteit.models.SavedSchemaList import SavedSchemaList
@@ -289,7 +290,7 @@ class TestDownloadParseResult:
289
290
  class TestCreateJob:
290
291
  """Test _create_job method."""
291
292
 
292
- @patch.object(ByteITClient, "_request")
293
+ @patch.object(RateLimitedSubmitter, "_request")
293
294
  @patch.object(ByteITClient, "_get_parse_job_details")
294
295
  def test_create_job_with_local_file(self, mock_get_status, mock_request):
295
296
  """Create job with local file uploads correctly."""
@@ -305,13 +306,13 @@ class TestCreateJob:
305
306
  output_connector = Mock()
306
307
  output_connector.to_dict.return_value = {"type": "localfile"}
307
308
 
308
- result = client._create_job(connector, output_connector, OutputFormat.TXT)
309
+ result = client._create_job(connector, output_connector)
309
310
 
310
311
  assert result == mock_job
311
312
  mock_request.assert_called_once()
312
313
  mock_get_status.assert_called_once_with("job_123")
313
314
 
314
- @patch.object(ByteITClient, "_request")
315
+ @patch.object(RateLimitedSubmitter, "_request")
315
316
  def test_create_job_with_s3(self, mock_request):
316
317
  """Create job with S3 connector."""
317
318
  client = ByteITClient("test_key")
@@ -336,44 +337,24 @@ class TestCreateJob:
336
337
  output_connector = Mock()
337
338
  output_connector.to_dict.return_value = {"type": "localfile"}
338
339
 
339
- result = client._create_job(connector, output_connector, OutputFormat.JSON)
340
+ result = client._create_job(connector, output_connector)
340
341
 
341
342
  assert isinstance(result, ParseJob)
342
343
  assert result.id == "job_123"
343
344
 
344
- @patch.object(ByteITClient, "_request")
345
- def test_create_job_serializes_excel_format_as_zip(self, mock_request):
346
- """Serialize Excel output format as zip in the job creation payload."""
345
+ def test_create_job_serializes_excel_format_as_zip(self):
346
+ """Serialize Excel output format as zip in the job payload."""
347
347
  client = ByteITClient("test_key")
348
- mock_request.return_value = {
349
- "job": {
350
- "id": "job_excel",
351
- "created_at": "2024-01-01T00:00:00Z",
352
- "updated_at": "2024-01-01T00:00:00Z",
353
- "processing_status": "pending",
354
- "result_format": "zip",
355
- }
356
- }
357
-
358
- file_obj = Mock()
359
- file_obj.closed = False
360
-
361
- connector = Mock()
362
- connector.to_dict.return_value = {"type": "localfile"}
363
- connector.get_file_data.return_value = ("test.pdf", file_obj)
364
348
 
365
- output_connector = Mock()
366
- output_connector.to_dict.return_value = {"type": "localfile"}
367
-
368
- result = client._create_job(connector, output_connector, OutputFormat.EXCEL)
349
+ data = client._build_localfile_job_data(
350
+ processing_options=None,
351
+ result_format=OutputFormat.EXCEL,
352
+ queue_for_batch=False,
353
+ )
369
354
 
370
- assert isinstance(result, ParseJob)
371
- assert result.result_format == "zip"
372
- request_kwargs = mock_request.call_args.kwargs
373
- assert request_kwargs["data"]["output_format"] == "zip"
374
- file_obj.close.assert_called_once()
355
+ assert data["output_format"] == "zip"
375
356
 
376
- @patch.object(ByteITClient, "_request")
357
+ @patch.object(RateLimitedSubmitter, "_request")
377
358
  @patch.object(ByteITClient, "_get_parse_job_details")
378
359
  def test_create_job_uses_parse_jobs_collection_path(
379
360
  self, mock_get_status, mock_request
@@ -390,14 +371,14 @@ class TestCreateJob:
390
371
  output_connector = Mock()
391
372
  output_connector.to_dict.return_value = {"type": "localfile"}
392
373
 
393
- client._create_job(connector, output_connector, OutputFormat.MD)
374
+ client._create_job(connector, output_connector)
394
375
 
395
376
  assert mock_request.call_args.args[:2] == (
396
377
  "POST",
397
378
  "/v1/jobs/parse-jobs/",
398
379
  )
399
380
 
400
- @patch.object(ByteITClient, "_request")
381
+ @patch.object(RateLimitedSubmitter, "_request")
401
382
  @patch.object(ByteITClient, "_get_parse_job_details")
402
383
  def test_create_job_sends_queue_for_batch_when_true(
403
384
  self, mock_get_status, mock_request
@@ -417,14 +398,14 @@ class TestCreateJob:
417
398
  client._create_job(
418
399
  connector,
419
400
  output_connector,
420
- OutputFormat.JSON,
401
+ None,
421
402
  queue_for_batch=True,
422
403
  )
423
404
 
424
405
  request_kwargs = mock_request.call_args.kwargs
425
406
  assert request_kwargs["data"]["queue_for_batch"] == "true"
426
407
 
427
- @patch.object(ByteITClient, "_request")
408
+ @patch.object(RateLimitedSubmitter, "_request")
428
409
  @patch.object(ByteITClient, "_get_parse_job_details")
429
410
  def test_create_job_omits_queue_for_batch_by_default(
430
411
  self, mock_get_status, mock_request
@@ -441,7 +422,7 @@ class TestCreateJob:
441
422
  output_connector = Mock()
442
423
  output_connector.to_dict.return_value = {"type": "localfile"}
443
424
 
444
- client._create_job(connector, output_connector, OutputFormat.JSON)
425
+ client._create_job(connector, output_connector)
445
426
 
446
427
  request_kwargs = mock_request.call_args.kwargs
447
428
  assert "queue_for_batch" not in request_kwargs["data"]
@@ -451,7 +432,7 @@ class TestCreateJobRateLimitHandling:
451
432
  """Test adaptive rate-limit handling during parse job submission."""
452
433
 
453
434
  @patch("time.sleep")
454
- @patch.object(ByteITClient, "_request")
435
+ @patch.object(RateLimitedSubmitter, "_request")
455
436
  @patch.object(ByteITClient, "_get_parse_job_details")
456
437
  def test_create_job_retries_after_rate_limit(
457
438
  self, mock_get_status, mock_request, mock_sleep
@@ -471,14 +452,14 @@ class TestCreateJobRateLimitHandling:
471
452
  output_connector = Mock()
472
453
  output_connector.to_dict.return_value = {"type": "localfile"}
473
454
 
474
- client._create_job(connector, output_connector, OutputFormat.JSON)
455
+ client._create_job(connector, output_connector)
475
456
 
476
457
  assert mock_request.call_count == 2
477
458
  mock_sleep.assert_called_once_with(2.0)
478
- assert client._submission_delay == 1.0
459
+ assert client._rate_limiter._submission_delay == 1.0
479
460
 
480
461
  @patch("time.sleep")
481
- @patch.object(ByteITClient, "_request")
462
+ @patch.object(RateLimitedSubmitter, "_request")
482
463
  @patch.object(ByteITClient, "_get_parse_job_details")
483
464
  def test_create_job_spaces_subsequent_submissions_after_rate_limit(
484
465
  self, mock_get_status, mock_request, mock_sleep
@@ -495,16 +476,16 @@ class TestCreateJobRateLimitHandling:
495
476
  output_connector = Mock()
496
477
  output_connector.to_dict.return_value = {"type": "localfile"}
497
478
 
498
- client._submission_delay = 2.0
499
- client._last_submission_at = 100.0
479
+ client._rate_limiter._submission_delay = 2.0
480
+ client._rate_limiter._last_submission_at = 100.0
500
481
 
501
482
  with patch("time.monotonic", side_effect=[101.0, 102.5]):
502
- client._create_job(connector, output_connector, OutputFormat.JSON)
483
+ client._create_job(connector, output_connector)
503
484
 
504
485
  mock_sleep.assert_called_once_with(1.0)
505
486
 
506
487
  @patch("time.sleep")
507
- @patch.object(ByteITClient, "_request")
488
+ @patch.object(RateLimitedSubmitter, "_request")
508
489
  def test_create_job_raises_after_max_rate_limit_retries(
509
490
  self, mock_request, mock_sleep
510
491
  ):
@@ -522,7 +503,7 @@ class TestCreateJobRateLimitHandling:
522
503
  output_connector.to_dict.return_value = {"type": "localfile"}
523
504
 
524
505
  with pytest.raises(RateLimitError, match="Too many requests"):
525
- client._create_job(connector, output_connector, OutputFormat.JSON)
506
+ client._create_job(connector, output_connector)
526
507
 
527
508
  assert mock_request.call_count == 3
528
509
  assert mock_sleep.call_count == 2
@@ -691,7 +672,7 @@ class TestParse:
691
672
  mock_wait.assert_called_once_with(
692
673
  "job_123", input_connector=mock_connector, job=mock_job
693
674
  )
694
- mock_download.assert_called_once_with("job_123")
675
+ mock_download.assert_called_once_with("job_123", result_format=None)
695
676
 
696
677
  @patch.object(ByteITClient, "_download_parse_result")
697
678
  @patch.object(ByteITClient, "_wait_for_completion")
@@ -854,7 +835,6 @@ class TestSubmitJob:
854
835
  input_connector=mock_input_conn,
855
836
  output_connector=mock_output_conn,
856
837
  processing_options=None,
857
- result_format=OutputFormat.JSON,
858
838
  queue_for_batch=False,
859
839
  )
860
840
 
@@ -879,14 +859,12 @@ class TestSubmitJob:
879
859
 
880
860
  client._submit_job(
881
861
  "test.pdf",
882
- {"extraction_type": "complex"},
862
+ {"parse_type": "complex"},
883
863
  OutputFormat.JSON,
884
864
  )
885
865
 
886
866
  call_kwargs = mock_create.call_args[1]
887
- assert call_kwargs["processing_options"].extraction_type is (
888
- ExtractionType.COMPLEX
889
- )
867
+ assert call_kwargs["processing_options"].parse_type is (ParseType.COMPLEX)
890
868
 
891
869
  @patch.object(ByteITClient, "_create_job")
892
870
  @patch.object(ByteITClient, "_to_output_connector")
@@ -3,10 +3,10 @@
3
3
  import pytest
4
4
 
5
5
  from byteit.models.DocumentMetadata import DocumentMetadata
6
- from byteit.models.ExtractionType import ExtractionType
7
6
  from byteit.models.JobList import JobList
8
7
  from byteit.models.JobStatus import JobStatus
9
8
  from byteit.models.ParseJob import ParseJob
9
+ from byteit.models.ParseType import ParseType
10
10
  from byteit.models.ProcessingOptions import ProcessingOptions
11
11
  from byteit.models.SavedSchema import SavedSchema
12
12
  from byteit.models.SavedSchemaList import SavedSchemaList
@@ -164,27 +164,27 @@ class TestProcessingOptions:
164
164
 
165
165
  assert options.languages == ["en"]
166
166
  assert options.page_range == ""
167
- assert options.extraction_type is ExtractionType.AUTO
167
+ assert options.parse_type is ParseType.AUTO
168
168
 
169
169
  def test_to_dict(self):
170
170
  """to_dict serializes options."""
171
171
  options = ProcessingOptions(
172
172
  languages=["en", "es"],
173
173
  page_range="1-5",
174
- extraction_type=ExtractionType.COMPLEX,
174
+ parse_type=ParseType.COMPLEX,
175
175
  )
176
176
 
177
177
  result = options.to_dict()
178
178
 
179
179
  assert result["languages"] == ["en", "es"]
180
180
  assert result["page_range"] == "1-5"
181
- assert result["extraction_type"] == "complex"
181
+ assert result["parse_type"] == "complex"
182
182
 
183
- def test_from_dict_parses_extraction_type(self):
184
- """from_dict converts extraction_type strings into enums."""
185
- options = ProcessingOptions.from_dict({"extraction_type": "complex"})
183
+ def test_from_dict_parses_parse_type(self):
184
+ """from_dict converts parse_type strings into enums."""
185
+ options = ProcessingOptions.from_dict({"parse_type": "complex"})
186
186
 
187
- assert options.extraction_type is ExtractionType.COMPLEX
187
+ assert options.parse_type is ParseType.COMPLEX
188
188
 
189
189
  def test_force_image_annotations_enables_image_annotations(self):
190
190
  """force_image_annotations implies image_annotations."""
@@ -195,10 +195,16 @@ class TestProcessingOptions:
195
195
  assert options.to_dict()["force_image_annotations"] is True
196
196
  assert options.to_dict()["image_annotations"] is True
197
197
 
198
- def test_invalid_extraction_type_raises_error(self):
199
- """Invalid extraction type values are rejected."""
200
- with pytest.raises(ValueError, match="Invalid extraction type"):
201
- ProcessingOptions(extraction_type="invalid")
198
+ def test_accepts_handwritten_parse_type(self):
199
+ """HANDWRITTEN is a valid parse type."""
200
+ options = ProcessingOptions(parse_type="handwritten")
201
+
202
+ assert options.parse_type is ParseType.HANDWRITTEN
203
+
204
+ def test_invalid_parse_type_raises_error(self):
205
+ """Invalid parse type values are rejected."""
206
+ with pytest.raises(ValueError, match="Invalid parse type"):
207
+ ProcessingOptions(parse_type="invalid")
202
208
 
203
209
 
204
210
  class TestSavedSchema:
@@ -1,10 +0,0 @@
1
- """Document extraction type value object."""
2
-
3
- from enum import Enum
4
-
5
-
6
- class ExtractionType(Enum):
7
- """Document extraction type enumeration."""
8
-
9
- AUTO = "auto"
10
- COMPLEX = "complex"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes