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.
- datalab_python_sdk-0.7.0/.github/workflows/ci.yml +31 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/.pre-commit-config.yaml +1 -1
- datalab_python_sdk-0.7.0/CHANGELOG.md +58 -0
- datalab_python_sdk-0.7.0/PKG-INFO +159 -0
- datalab_python_sdk-0.7.0/README.md +138 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/__init__.py +29 -1
- datalab_python_sdk-0.7.0/datalab_sdk/auth.py +130 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/cli.py +566 -71
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/client.py +920 -323
- datalab_python_sdk-0.7.0/datalab_sdk/exceptions.py +110 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/models.py +309 -38
- datalab_python_sdk-0.7.0/datalab_sdk/settings.py +26 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/pyproject.toml +16 -2
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/README.md +2 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/end_to_end_workflow.py +27 -36
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/1_get_step_types.py +6 -5
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/2_get_workflows.py +1 -1
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/4_execute_workflow.py +8 -16
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/5_poll_workflow.py +17 -21
- datalab_python_sdk-0.7.0/tests/test_api_consistency.py +378 -0
- datalab_python_sdk-0.7.0/tests/test_cli_options.py +144 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/test_client_methods.py +169 -88
- datalab_python_sdk-0.7.0/tests/test_device_login.py +143 -0
- datalab_python_sdk-0.7.0/tests/test_errors.py +143 -0
- datalab_python_sdk-0.7.0/tests/test_new_endpoints.py +267 -0
- datalab_python_sdk-0.7.0/tests/test_results.py +153 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/test_workflows.py +42 -33
- datalab_python_sdk-0.7.0/uv.lock +1382 -0
- datalab_python_sdk-0.5.0/.github/workflows/ci.yml +0 -19
- datalab_python_sdk-0.5.0/.github/workflows/publish.yml +0 -38
- datalab_python_sdk-0.5.0/PKG-INFO +0 -80
- datalab_python_sdk-0.5.0/README.md +0 -62
- datalab_python_sdk-0.5.0/datalab_sdk/exceptions.py +0 -38
- datalab_python_sdk-0.5.0/datalab_sdk/settings.py +0 -16
- datalab_python_sdk-0.5.0/poetry.lock +0 -1201
- datalab_python_sdk-0.5.0/uv.lock +0 -1372
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/.gitignore +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/.python-version +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/LICENSE +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/08-Lambda-Calculus.pptx +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/adversarial.pdf +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/bid_evaluation.docx +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/book_review.ppt +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/book_store.xls +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/chi_hind.png +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/how_to_read.doc +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/normandy.epub +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/sample-1-sheet.xlsx +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/thinkpython.pdf +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/data/vibe.html +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/datalab_sdk/mimetypes.py +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/integration/README.md +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/integration/__init__.py +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/integration/test_live_api.py +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/integration/test_readme_examples.py +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/pytest.ini +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/3_create_workflow.py +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_api_tutorial/README.md +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/README.md +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/eval_segmentation.json +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/parse_segment.json +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/segment_parallel_extract.json +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/recipes/workflows/workflow_definitions/slack_alert.json +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/__init__.py +0 -0
- {datalab_python_sdk-0.5.0 → datalab_python_sdk-0.7.0}/tests/conftest.py +0 -0
- {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/
|
|
@@ -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
|
|
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.")
|