datalab-python-sdk 0.5.0__tar.gz → 0.7.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 (66) hide show
  1. datalab_python_sdk-0.7.0/.github/workflows/ci.yml +31 -0
  2. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/.pre-commit-config.yaml +1 -1
  3. datalab_python_sdk-0.7.0/CHANGELOG.md +58 -0
  4. datalab_python_sdk-0.7.0/PKG-INFO +159 -0
  5. datalab_python_sdk-0.7.0/README.md +138 -0
  6. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/__init__.py +29 -1
  7. datalab_python_sdk-0.7.0/datalab_sdk/auth.py +130 -0
  8. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/cli.py +566 -71
  9. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/client.py +920 -323
  10. datalab_python_sdk-0.7.0/datalab_sdk/exceptions.py +110 -0
  11. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/models.py +309 -38
  12. datalab_python_sdk-0.7.0/datalab_sdk/settings.py +26 -0
  13. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/pyproject.toml +16 -2
  14. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/README.md +2 -0
  15. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/end_to_end_workflow.py +27 -36
  16. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/1_get_step_types.py +6 -5
  17. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/2_get_workflows.py +1 -1
  18. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/4_execute_workflow.py +8 -16
  19. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/5_poll_workflow.py +17 -21
  20. datalab_python_sdk-0.7.0/tests/test_api_consistency.py +378 -0
  21. datalab_python_sdk-0.7.0/tests/test_cli_options.py +144 -0
  22. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/test_client_methods.py +169 -88
  23. datalab_python_sdk-0.7.0/tests/test_device_login.py +143 -0
  24. datalab_python_sdk-0.7.0/tests/test_errors.py +143 -0
  25. datalab_python_sdk-0.7.0/tests/test_new_endpoints.py +267 -0
  26. datalab_python_sdk-0.7.0/tests/test_results.py +153 -0
  27. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/test_workflows.py +42 -33
  28. datalab_python_sdk-0.7.0/uv.lock +1382 -0
  29. datalab_python_sdk-0.5.0/.github/workflows/ci.yml +0 -19
  30. datalab_python_sdk-0.5.0/.github/workflows/publish.yml +0 -38
  31. datalab_python_sdk-0.5.0/PKG-INFO +0 -80
  32. datalab_python_sdk-0.5.0/README.md +0 -62
  33. datalab_python_sdk-0.5.0/datalab_sdk/exceptions.py +0 -38
  34. datalab_python_sdk-0.5.0/datalab_sdk/settings.py +0 -16
  35. datalab_python_sdk-0.5.0/poetry.lock +0 -1201
  36. datalab_python_sdk-0.5.0/uv.lock +0 -1372
  37. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/.gitignore +0 -0
  38. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/.python-version +0 -0
  39. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/LICENSE +0 -0
  40. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/08-Lambda-Calculus.pptx +0 -0
  41. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/adversarial.pdf +0 -0
  42. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/bid_evaluation.docx +0 -0
  43. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/book_review.ppt +0 -0
  44. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/book_store.xls +0 -0
  45. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/chi_hind.png +0 -0
  46. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/how_to_read.doc +0 -0
  47. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/normandy.epub +0 -0
  48. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/sample-1-sheet.xlsx +0 -0
  49. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/thinkpython.pdf +0 -0
  50. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/vibe.html +0 -0
  51. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/mimetypes.py +0 -0
  52. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/integration/README.md +0 -0
  53. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/integration/__init__.py +0 -0
  54. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/integration/test_live_api.py +0 -0
  55. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/integration/test_readme_examples.py +0 -0
  56. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/pytest.ini +0 -0
  57. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/3_create_workflow.py +0 -0
  58. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/README.md +0 -0
  59. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/README.md +0 -0
  60. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/eval_segmentation.json +0 -0
  61. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/parse_segment.json +0 -0
  62. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/segment_parallel_extract.json +0 -0
  63. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/slack_alert.json +0 -0
  64. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/__init__.py +0 -0
  65. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/conftest.py +0 -0
  66. {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/test_cli_simple.py +0 -0
@@ -0,0 +1,31 @@
1
+ name: Unit tests
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]
17
+ steps:
18
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
19
+ with:
20
+ persist-credentials: false
21
+ - uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ - name: Install dependencies
25
+ run: uv sync --group dev --frozen
26
+ - name: Lint
27
+ run: |
28
+ uv run ruff check .
29
+ uv run ruff format --check .
30
+ - name: Run tests
31
+ run: uv run pytest tests/
@@ -1,7 +1,7 @@
1
1
  repos:
2
2
  - repo: https://github.com/astral-sh/ruff-pre-commit
3
3
  # Ruff version.
4
- rev: v0.12.3
4
+ rev: v0.17.1
5
5
  hooks:
6
6
  # Run the linter.
7
7
  - id: ruff-check
@@ -0,0 +1,58 @@
1
+ # Changelog
2
+
3
+ The newest release is at the top. Each export to this repository uses the
4
+ top section as its commit message.
5
+
6
+ ## 0.7.0
7
+
8
+ New:
9
+
10
+ - `datalab login`: sign in from a terminal or a coding agent with a device
11
+ code. Approve it in the browser, type the emailed 6-digit code, and the SDK
12
+ saves an API key to `~/.config/datalab/config.json` (mode 0600).
13
+ `datalab logout` deletes it.
14
+ - The clients use the saved key when no `api_key` is passed and
15
+ `DATALAB_API_KEY` is not set.
16
+
17
+ ## 0.6.0
18
+
19
+ Breaking changes:
20
+
21
+ - The SDK requires Python 3.11 or later. Python 3.10 reached end of life in
22
+ October 2026.
23
+ - `ExtractOptions.mode` now defaults to `None`. The SDK sends `mode` only when
24
+ you set it, so the API picks the parse mode from `extraction_mode`. To keep
25
+ the old behavior, set `mode="fast"`.
26
+ - App-only evaluation controls are no longer accepted by SDK calls.
27
+
28
+ New:
29
+
30
+ - **Open beta:** `run_agent()` and the `agent` CLI command run existing
31
+ published processors. Processor design is not included.
32
+ - `form_fields()`, `generate_extraction_schemas()` and `delete_result()`,
33
+ each with a sync version on `DatalabClient`.
34
+ - Convert options: `merge_cross_page`, `word_bboxes`, `disable_html_prettify`,
35
+ `exact_transcription`, `keep_pageheader_in_output` and
36
+ `keep_pagefooter_in_output`.
37
+ - `processing_location` on the extract, segment, custom processor, track
38
+ changes, OCR, fill, document and upload calls.
39
+ - `run_pipeline()` sends `checkpoint_id` and `processing_location`.
40
+ `create_pipeline()` accepts `reuse_draft`.
41
+ - CLI commands `fill` and `form-fields`, and CLI flags for the new
42
+ convert and extract options.
43
+
44
+ Fixes:
45
+
46
+ - API errors keep the message from the API and map HTTP errors to exception
47
+ classes.
48
+ - The SDK downloads signed result URLs and keeps result fields that it
49
+ dropped before.
50
+ - The User-Agent header and `datalab --version` show the installed version.
51
+ - The CLI `--skip_cache` option is a flag. `--format` accepts a
52
+ comma-separated list.
53
+
54
+ Deprecated:
55
+
56
+ - The workflow methods, because the API no longer serves workflows. Use
57
+ pipelines.
58
+ - `run_custom_processor()`. Use `run_agent()`.
@@ -0,0 +1,159 @@
1
+ Metadata-Version: 2.5
2
+ Name: datalab-python-sdk
3
+ Version: 0.7.0
4
+ Summary: SDK for the Datalab document intelligence API
5
+ Project-URL: Homepage, https://www.datalab.to
6
+ Project-URL: Documentation, https://documentation.datalab.to
7
+ Project-URL: Repository, https://github.com/datalab-to/sdk
8
+ Author-email: Datalab Team <hi@datalab.to>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: api,datalab,document-intelligence,sdk
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: aiohttp>=3.12.14
14
+ Requires-Dist: click>=8.2.1
15
+ Requires-Dist: ijson>=3.3.0
16
+ Requires-Dist: pydantic-settings>=2.10.1
17
+ Requires-Dist: pydantic>=2.11.7
18
+ Requires-Dist: tenacity>=8.2.3
19
+ Requires-Dist: tqdm>=4.66.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Datalab SDK
23
+
24
+ A Python SDK for the [Datalab API](https://www.datalab.to/?utm_source=gh-sdk) - it turns documents into verifiable knowledge your agents act on. Datalab is the team behind [Chandra](https://github.com/datalab-to/chandra), [marker](https://github.com/datalab-to/marker), [surya](https://github.com/datalab-to/surya), and [lift](https://github.com/datalab-to/lift).
25
+
26
+ See the full documentation at [https://documentation.datalab.to](https://documentation.datalab.to).
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install datalab-python-sdk
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### Authentication
37
+
38
+ [Sign up](https://www.datalab.to/?utm_source=gh-sdk), then get your API key from [https://www.datalab.to/app/keys](https://www.datalab.to/app/keys):
39
+
40
+ ```bash
41
+ export DATALAB_API_KEY="your_api_key_here"
42
+ ```
43
+
44
+ Or sign in from a terminal, which also works for coding agents:
45
+
46
+ ```bash
47
+ datalab login
48
+ ```
49
+
50
+ It shows a code and a link. Open the link, sign in or sign up, approve the code, and type the 6-digit code that we email you. The SDK saves the key to `~/.config/datalab/config.json`, readable only by your user, and uses it when `DATALAB_API_KEY` is not set. `datalab logout` deletes it.
51
+
52
+ ### Basic Usage
53
+
54
+ ```python
55
+ from datalab_sdk import DatalabClient
56
+
57
+ client = DatalabClient() # use env var from above, or pass api_key="your_api_key_here"
58
+
59
+ # Convert PDF to markdown
60
+ result = client.convert("document.pdf")
61
+ print(result.markdown)
62
+ ```
63
+
64
+ `AsyncDatalabClient` has the same methods as `DatalabClient`, as coroutines.
65
+
66
+ ## Conversion options
67
+
68
+ ```python
69
+ from datalab_sdk import ConvertOptions
70
+
71
+ options = ConvertOptions(
72
+ mode="balanced", # lite, fast, balanced, accurate (SDK default: fast)
73
+ output_format="markdown,html", # markdown, html, json, chunks (comma-separated)
74
+ page_range="0-4",
75
+ extras="chart_understanding,table_cell_bboxes",
76
+ merge_cross_page=True, # merge tables, paragraphs and lists split across pages
77
+ word_bboxes=True, # per-word bounding boxes in the HTML output
78
+ save_checkpoint=True, # reuse the parse in extract() or segment()
79
+ )
80
+ result = client.convert("document.pdf", options=options)
81
+ ```
82
+
83
+ Other options include `paginate`, `add_block_ids`, `disable_html_prettify`, `exact_transcription`, `token_efficient_markdown`, `include_markdown_in_chunks`, `disable_image_extraction`, `disable_image_captions`, `fence_synthetic_captions`, `keep_spreadsheet_formatting`, `keep_pageheader_in_output`, `keep_pagefooter_in_output`, `additional_config`, `webhook_url`, `skip_cache` and `processing_location`. The [API reference](https://documentation.datalab.to/api-reference/convert-document) describes each option.
84
+
85
+ ## Structured extraction
86
+
87
+ ```python
88
+ from datalab_sdk import ExtractOptions
89
+
90
+ result = client.extract(
91
+ "invoice.pdf",
92
+ options=ExtractOptions(
93
+ page_schema='{"properties": {"total": {"type": "number"}}}', # or schema_id="sch_..."
94
+ extraction_mode="balanced", # turbo, fast, balanced, accurate
95
+ ),
96
+ )
97
+ print(result.extraction_schema_json, result.extraction_score_average)
98
+ ```
99
+
100
+ When you do not set the parse `mode`, the API picks the parse mode that matches `extraction_mode`.
101
+
102
+ ## Other endpoints
103
+
104
+ | Method | Endpoint |
105
+ | --- | --- |
106
+ | `run_agent()` | **Beta:** run an existing published processor (`AgentOptions(processor="cp_...")`) |
107
+ | `segment()` | Split a document into sections (`SegmentOptions`) |
108
+ | `fill()` | Fill a PDF or image form (`FormFillingOptions`) |
109
+ | `form_fields()` | Find the fillable fields in a PDF or image |
110
+ | `track_changes()` | Read tracked changes and comments from DOCX or PDF |
111
+ | `create_document()` | Make a DOCX from markdown with track changes markup |
112
+ | `generate_extraction_schemas()` | Suggest extraction schemas for a saved checkpoint |
113
+ | `delete_result()` | Result deletion, by `result.request_id` |
114
+ | `upload_files()`, `list_files()`, ... | File storage. Pass the returned `datalab://file-...` reference as `file_url` |
115
+ | `create_extraction_schema()`, ... | Saved extraction schemas |
116
+ | `create_pipeline()`, `run_pipeline()`, ... | [Pipelines](https://documentation.datalab.to/docs/welcome/sdk/pipelines) |
117
+
118
+ `run_custom_processor()` and `ocr()` call deprecated API endpoints. Use `run_agent()` and `convert()` instead.
119
+
120
+ Published-agent execution is in open beta; the API may change. Processor design and raw-prompt execution are not included.
121
+
122
+ ## Inputs, polling and errors
123
+
124
+ - Send a local file with `file_path`, or a URL or a `datalab://file-...` reference with `file_url`. When you set `processing_location`, send the document as `file_url`.
125
+ - Processing methods poll until the result is ready. Set `max_polls` and `poll_interval` to change the limit (default: 300 polls, 1 second apart). Use `stream_response_to` to write a large result straight to disk.
126
+ - The SDK retries a submission that gets HTTP 429 or 503, and polls that get 408, 429, 5xx or a connection error. It waits for the `Retry-After` header when the API sends one.
127
+ - API errors raise `DatalabAPIError` (with `status_code` and `response_data`) or one of its subclasses: `DatalabAuthenticationError` (401, 403), `DatalabPaymentRequiredError` (402), `DatalabNotFoundError` (404), `DatalabRateLimitError` (429) and `DatalabServerError` (5xx). Invalid arguments raise `DatalabValidationError`, which is also a `ValueError`.
128
+
129
+ ## Workflows
130
+
131
+ The Datalab API removed the Workflows API. The workflow methods and CLI commands in this SDK are deprecated, and the API does not answer them. Use pipelines instead.
132
+
133
+ ## CLI Usage
134
+
135
+ The SDK includes a command-line interface:
136
+
137
+ ```bash
138
+ # Convert a document or a directory of documents
139
+ datalab convert document.pdf --mode balanced --format markdown,html
140
+
141
+ # Extract with a saved schema
142
+ datalab extract invoice.pdf --schema_id sch_XXXXX --extraction_mode fast
143
+
144
+ # Other commands
145
+ datalab agent --help
146
+ datalab segment --help
147
+ datalab fill --help
148
+ datalab form-fields --help
149
+ datalab track-changes --help
150
+ datalab create-document --help
151
+ ```
152
+
153
+ ## Contributing
154
+
155
+ Issues and bug reports are welcome here. We develop the SDK in a private repository and mirror each release to this one, so we do not merge pull requests here directly. Open an issue, or open a pull request to show the change, and we will port it and credit you.
156
+
157
+ ## License
158
+
159
+ MIT License
@@ -0,0 +1,138 @@
1
+ # Datalab SDK
2
+
3
+ A Python SDK for the [Datalab API](https://www.datalab.to/?utm_source=gh-sdk) - it turns documents into verifiable knowledge your agents act on. Datalab is the team behind [Chandra](https://github.com/datalab-to/chandra), [marker](https://github.com/datalab-to/marker), [surya](https://github.com/datalab-to/surya), and [lift](https://github.com/datalab-to/lift).
4
+
5
+ See the full documentation at [https://documentation.datalab.to](https://documentation.datalab.to).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install datalab-python-sdk
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### Authentication
16
+
17
+ [Sign up](https://www.datalab.to/?utm_source=gh-sdk), then get your API key from [https://www.datalab.to/app/keys](https://www.datalab.to/app/keys):
18
+
19
+ ```bash
20
+ export DATALAB_API_KEY="your_api_key_here"
21
+ ```
22
+
23
+ Or sign in from a terminal, which also works for coding agents:
24
+
25
+ ```bash
26
+ datalab login
27
+ ```
28
+
29
+ It shows a code and a link. Open the link, sign in or sign up, approve the code, and type the 6-digit code that we email you. The SDK saves the key to `~/.config/datalab/config.json`, readable only by your user, and uses it when `DATALAB_API_KEY` is not set. `datalab logout` deletes it.
30
+
31
+ ### Basic Usage
32
+
33
+ ```python
34
+ from datalab_sdk import DatalabClient
35
+
36
+ client = DatalabClient() # use env var from above, or pass api_key="your_api_key_here"
37
+
38
+ # Convert PDF to markdown
39
+ result = client.convert("document.pdf")
40
+ print(result.markdown)
41
+ ```
42
+
43
+ `AsyncDatalabClient` has the same methods as `DatalabClient`, as coroutines.
44
+
45
+ ## Conversion options
46
+
47
+ ```python
48
+ from datalab_sdk import ConvertOptions
49
+
50
+ options = ConvertOptions(
51
+ mode="balanced", # lite, fast, balanced, accurate (SDK default: fast)
52
+ output_format="markdown,html", # markdown, html, json, chunks (comma-separated)
53
+ page_range="0-4",
54
+ extras="chart_understanding,table_cell_bboxes",
55
+ merge_cross_page=True, # merge tables, paragraphs and lists split across pages
56
+ word_bboxes=True, # per-word bounding boxes in the HTML output
57
+ save_checkpoint=True, # reuse the parse in extract() or segment()
58
+ )
59
+ result = client.convert("document.pdf", options=options)
60
+ ```
61
+
62
+ Other options include `paginate`, `add_block_ids`, `disable_html_prettify`, `exact_transcription`, `token_efficient_markdown`, `include_markdown_in_chunks`, `disable_image_extraction`, `disable_image_captions`, `fence_synthetic_captions`, `keep_spreadsheet_formatting`, `keep_pageheader_in_output`, `keep_pagefooter_in_output`, `additional_config`, `webhook_url`, `skip_cache` and `processing_location`. The [API reference](https://documentation.datalab.to/api-reference/convert-document) describes each option.
63
+
64
+ ## Structured extraction
65
+
66
+ ```python
67
+ from datalab_sdk import ExtractOptions
68
+
69
+ result = client.extract(
70
+ "invoice.pdf",
71
+ options=ExtractOptions(
72
+ page_schema='{"properties": {"total": {"type": "number"}}}', # or schema_id="sch_..."
73
+ extraction_mode="balanced", # turbo, fast, balanced, accurate
74
+ ),
75
+ )
76
+ print(result.extraction_schema_json, result.extraction_score_average)
77
+ ```
78
+
79
+ When you do not set the parse `mode`, the API picks the parse mode that matches `extraction_mode`.
80
+
81
+ ## Other endpoints
82
+
83
+ | Method | Endpoint |
84
+ | --- | --- |
85
+ | `run_agent()` | **Beta:** run an existing published processor (`AgentOptions(processor="cp_...")`) |
86
+ | `segment()` | Split a document into sections (`SegmentOptions`) |
87
+ | `fill()` | Fill a PDF or image form (`FormFillingOptions`) |
88
+ | `form_fields()` | Find the fillable fields in a PDF or image |
89
+ | `track_changes()` | Read tracked changes and comments from DOCX or PDF |
90
+ | `create_document()` | Make a DOCX from markdown with track changes markup |
91
+ | `generate_extraction_schemas()` | Suggest extraction schemas for a saved checkpoint |
92
+ | `delete_result()` | Result deletion, by `result.request_id` |
93
+ | `upload_files()`, `list_files()`, ... | File storage. Pass the returned `datalab://file-...` reference as `file_url` |
94
+ | `create_extraction_schema()`, ... | Saved extraction schemas |
95
+ | `create_pipeline()`, `run_pipeline()`, ... | [Pipelines](https://documentation.datalab.to/docs/welcome/sdk/pipelines) |
96
+
97
+ `run_custom_processor()` and `ocr()` call deprecated API endpoints. Use `run_agent()` and `convert()` instead.
98
+
99
+ Published-agent execution is in open beta; the API may change. Processor design and raw-prompt execution are not included.
100
+
101
+ ## Inputs, polling and errors
102
+
103
+ - Send a local file with `file_path`, or a URL or a `datalab://file-...` reference with `file_url`. When you set `processing_location`, send the document as `file_url`.
104
+ - Processing methods poll until the result is ready. Set `max_polls` and `poll_interval` to change the limit (default: 300 polls, 1 second apart). Use `stream_response_to` to write a large result straight to disk.
105
+ - The SDK retries a submission that gets HTTP 429 or 503, and polls that get 408, 429, 5xx or a connection error. It waits for the `Retry-After` header when the API sends one.
106
+ - API errors raise `DatalabAPIError` (with `status_code` and `response_data`) or one of its subclasses: `DatalabAuthenticationError` (401, 403), `DatalabPaymentRequiredError` (402), `DatalabNotFoundError` (404), `DatalabRateLimitError` (429) and `DatalabServerError` (5xx). Invalid arguments raise `DatalabValidationError`, which is also a `ValueError`.
107
+
108
+ ## Workflows
109
+
110
+ The Datalab API removed the Workflows API. The workflow methods and CLI commands in this SDK are deprecated, and the API does not answer them. Use pipelines instead.
111
+
112
+ ## CLI Usage
113
+
114
+ The SDK includes a command-line interface:
115
+
116
+ ```bash
117
+ # Convert a document or a directory of documents
118
+ datalab convert document.pdf --mode balanced --format markdown,html
119
+
120
+ # Extract with a saved schema
121
+ datalab extract invoice.pdf --schema_id sch_XXXXX --extraction_mode fast
122
+
123
+ # Other commands
124
+ datalab agent --help
125
+ datalab segment --help
126
+ datalab fill --help
127
+ datalab form-fields --help
128
+ datalab track-changes --help
129
+ datalab create-document --help
130
+ ```
131
+
132
+ ## Contributing
133
+
134
+ Issues and bug reports are welcome here. We develop the SDK in a private repository and mirror each release to this one, so we do not merge pull requests here directly. Open an issue, or open a pull request to show the change, and we will port it and credit you.
135
+
136
+ ## License
137
+
138
+ MIT License
@@ -6,7 +6,18 @@ supporting document conversion, extraction, segmentation, and more.
6
6
  """
7
7
 
8
8
  from .client import DatalabClient, AsyncDatalabClient
9
- from .exceptions import DatalabError, DatalabAPIError, DatalabTimeoutError
9
+ from .exceptions import (
10
+ DatalabError,
11
+ DatalabAPIError,
12
+ DatalabAuthenticationError,
13
+ DatalabPaymentRequiredError,
14
+ DatalabNotFoundError,
15
+ DatalabRateLimitError,
16
+ DatalabServerError,
17
+ DatalabTimeoutError,
18
+ DatalabFileError,
19
+ DatalabValidationError,
20
+ )
10
21
  from .models import (
11
22
  ConversionResult,
12
23
  CreateDocumentResult,
@@ -21,6 +32,11 @@ from .models import (
21
32
  OCROptions,
22
33
  FormFillingOptions,
23
34
  FormFillingResult,
35
+ FormFieldsOptions,
36
+ FormFieldsResult,
37
+ AgentOptions,
38
+ AgentResult,
39
+ ExtractionSchemaSuggestions,
24
40
  Workflow,
25
41
  WorkflowStep,
26
42
  WorkflowExecution,
@@ -43,7 +59,14 @@ __all__ = [
43
59
  "AsyncDatalabClient",
44
60
  "DatalabError",
45
61
  "DatalabAPIError",
62
+ "DatalabAuthenticationError",
63
+ "DatalabPaymentRequiredError",
64
+ "DatalabNotFoundError",
65
+ "DatalabRateLimitError",
66
+ "DatalabServerError",
46
67
  "DatalabTimeoutError",
68
+ "DatalabFileError",
69
+ "DatalabValidationError",
47
70
  "ConversionResult",
48
71
  "CreateDocumentResult",
49
72
  "FileResult",
@@ -57,6 +80,11 @@ __all__ = [
57
80
  "OCROptions",
58
81
  "FormFillingOptions",
59
82
  "FormFillingResult",
83
+ "FormFieldsOptions",
84
+ "FormFieldsResult",
85
+ "AgentOptions",
86
+ "AgentResult",
87
+ "ExtractionSchemaSuggestions",
60
88
  "Workflow",
61
89
  "WorkflowStep",
62
90
  "WorkflowExecution",
@@ -0,0 +1,130 @@
1
+ """Sign in from a terminal with a device code (`datalab login`), and keep the API key on disk.
2
+
3
+ The flow (server side: /api/auth/device/code, /app/device, /api/auth/device/token):
4
+ 1. Ask the server for a code. Show the human the code and a URL, and open the browser.
5
+ 2. The human approves in the browser, where they are signed in as usual.
6
+ 3. Poll until the server returns a new API key, then save it to the config file.
7
+
8
+ The key is saved to $XDG_CONFIG_HOME/datalab/config.json (default ~/.config/datalab/config.json)
9
+ with mode 0600. DATALAB_API_KEY in the environment always wins over the saved key.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import os
16
+ import stat
17
+ import time
18
+ import urllib.error
19
+ import urllib.request
20
+ import webbrowser
21
+ from pathlib import Path
22
+ from typing import Callable, Optional
23
+
24
+ from datalab_sdk.exceptions import DatalabAPIError
25
+
26
+ KEYS_PAGE = "https://www.datalab.to/app/keys"
27
+
28
+
29
+ def config_path() -> Path:
30
+ base = os.environ.get("XDG_CONFIG_HOME") or os.path.join(Path.home(), ".config")
31
+ return Path(base) / "datalab" / "config.json"
32
+
33
+
34
+ def load_saved_api_key() -> Optional[str]:
35
+ try:
36
+ return json.loads(config_path().read_text()).get("api_key") or None
37
+ except (OSError, ValueError):
38
+ return None
39
+
40
+
41
+ def save_api_key(api_key: str, host: str, team_name: Optional[str] = None) -> Path:
42
+ path = config_path()
43
+ path.parent.mkdir(parents=True, exist_ok=True)
44
+ path.write_text(
45
+ json.dumps({"api_key": api_key, "host": host, "team_name": team_name}, indent=2)
46
+ )
47
+ os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
48
+ return path
49
+
50
+
51
+ def delete_saved_api_key() -> bool:
52
+ try:
53
+ config_path().unlink()
54
+ return True
55
+ except FileNotFoundError:
56
+ return False
57
+
58
+
59
+ def _post(url: str, payload: dict, headers: dict) -> tuple[int, dict]:
60
+ req = urllib.request.Request(
61
+ url,
62
+ data=json.dumps(payload).encode(),
63
+ headers={
64
+ "Content-Type": "application/json",
65
+ "Accept": "application/json",
66
+ **headers,
67
+ },
68
+ method="POST",
69
+ )
70
+ try:
71
+ with urllib.request.urlopen(req, timeout=30) as res:
72
+ return res.status, json.loads(res.read() or b"{}")
73
+ except urllib.error.HTTPError as e:
74
+ try:
75
+ return e.code, json.loads(e.read() or b"{}")
76
+ except ValueError:
77
+ return e.code, {}
78
+
79
+
80
+ def device_login(
81
+ host: str = "https://www.datalab.to",
82
+ client_name: str = "Datalab CLI",
83
+ open_browser: bool = True,
84
+ extra_headers: Optional[dict] = None,
85
+ on_code: Optional[Callable[[dict], None]] = None,
86
+ sleep: Callable[[float], None] = time.sleep,
87
+ ) -> dict:
88
+ """Run the device flow and return {"api_key", "team_name", "key_name"}. Raises DatalabAPIError."""
89
+ host = host.rstrip("/")
90
+ headers = extra_headers or {}
91
+ status, code = _post(
92
+ f"{host}/api/auth/device/code", {"client_name": client_name}, headers
93
+ )
94
+ if status != 200:
95
+ raise DatalabAPIError(
96
+ f"Could not start the sign-in ({status}): {code.get('detail', code)}"
97
+ )
98
+ if on_code:
99
+ on_code(code)
100
+ if open_browser:
101
+ try:
102
+ webbrowser.open(code["verification_uri_complete"])
103
+ except Exception:
104
+ pass
105
+
106
+ interval = float(code.get("interval", 5))
107
+ deadline = time.monotonic() + float(code.get("expires_in", 600))
108
+ while time.monotonic() < deadline:
109
+ sleep(interval)
110
+ status, body = _post(
111
+ f"{host}/api/auth/device/token",
112
+ {"device_code": code["device_code"]},
113
+ headers,
114
+ )
115
+ if status == 200 and body.get("api_key"):
116
+ return body
117
+ error = body.get("error")
118
+ if error == "authorization_pending":
119
+ continue
120
+ if error == "slow_down":
121
+ interval += 5
122
+ continue
123
+ if error == "access_denied":
124
+ raise DatalabAPIError("The sign-in was denied in the browser.")
125
+ if error == "expired_token":
126
+ raise DatalabAPIError(
127
+ "The sign-in code expired. Run `datalab login` again."
128
+ )
129
+ raise DatalabAPIError(f"Sign-in failed ({status}): {body}")
130
+ raise DatalabAPIError("The sign-in code expired. Run `datalab login` again.")