acquivela 0.2.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 (40) hide show
  1. acquivela-0.2.0/LICENSE +21 -0
  2. acquivela-0.2.0/PKG-INFO +215 -0
  3. acquivela-0.2.0/README.md +188 -0
  4. acquivela-0.2.0/acquivela/__init__.py +28 -0
  5. acquivela-0.2.0/acquivela/capabilities.py +19 -0
  6. acquivela-0.2.0/acquivela/cdm.py +106 -0
  7. acquivela-0.2.0/acquivela/execution_context.py +27 -0
  8. acquivela-0.2.0/acquivela/facade.py +47 -0
  9. acquivela-0.2.0/acquivela/models.py +49 -0
  10. acquivela-0.2.0/acquivela/pipeline.py +317 -0
  11. acquivela-0.2.0/acquivela/plugin.py +68 -0
  12. acquivela-0.2.0/acquivela/plugins/__init__.py +14 -0
  13. acquivela-0.2.0/acquivela/plugins/docling.py +213 -0
  14. acquivela-0.2.0/acquivela/plugins/mineru.py +281 -0
  15. acquivela-0.2.0/acquivela/public_result.py +114 -0
  16. acquivela-0.2.0/acquivela/registry.py +200 -0
  17. acquivela-0.2.0/acquivela/result.py +43 -0
  18. acquivela-0.2.0/acquivela.egg-info/PKG-INFO +215 -0
  19. acquivela-0.2.0/acquivela.egg-info/SOURCES.txt +38 -0
  20. acquivela-0.2.0/acquivela.egg-info/dependency_links.txt +1 -0
  21. acquivela-0.2.0/acquivela.egg-info/requires.txt +14 -0
  22. acquivela-0.2.0/acquivela.egg-info/top_level.txt +1 -0
  23. acquivela-0.2.0/pyproject.toml +48 -0
  24. acquivela-0.2.0/setup.cfg +4 -0
  25. acquivela-0.2.0/tests/test_capabilities.py +37 -0
  26. acquivela-0.2.0/tests/test_capability_selection.py +383 -0
  27. acquivela-0.2.0/tests/test_cdm_v02.py +400 -0
  28. acquivela-0.2.0/tests/test_contracts.py +129 -0
  29. acquivela-0.2.0/tests/test_contracts_extended.py +363 -0
  30. acquivela-0.2.0/tests/test_diagnostics.py +485 -0
  31. acquivela-0.2.0/tests/test_facade.py +188 -0
  32. acquivela-0.2.0/tests/test_fallback.py +719 -0
  33. acquivela-0.2.0/tests/test_import_linter.py +56 -0
  34. acquivela-0.2.0/tests/test_import_linter_extended.py +124 -0
  35. acquivela-0.2.0/tests/test_integration.py +488 -0
  36. acquivela-0.2.0/tests/test_pipeline.py +530 -0
  37. acquivela-0.2.0/tests/test_plugin_docling.py +275 -0
  38. acquivela-0.2.0/tests/test_plugin_health.py +494 -0
  39. acquivela-0.2.0/tests/test_plugin_mineru.py +244 -0
  40. acquivela-0.2.0/tests/test_registry.py +466 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AcquiVela Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,215 @@
1
+ Metadata-Version: 2.4
2
+ Name: acquivela
3
+ Version: 0.2.0
4
+ Summary: AcquiVela — Document Acquisition Framework
5
+ License: MIT
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Provides-Extra: docling
17
+ Requires-Dist: docling>=2.119.0; extra == "docling"
18
+ Provides-Extra: mineru
19
+ Requires-Dist: mineru>=3.4.4; extra == "mineru"
20
+ Provides-Extra: all
21
+ Requires-Dist: acquivela[docling,mineru]; extra == "all"
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8.0; extra == "dev"
24
+ Requires-Dist: importlinter>=0.16; extra == "dev"
25
+ Requires-Dist: acquivela[all]; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # AcquiVela
29
+
30
+ Document Acquisition Framework — Frozen Architecture v0.1
31
+
32
+ > **Note:** AcquiVela v0.1.0 is a pre-1.0 release. Some internal orchestration APIs (`Pipeline`, `Registry`) are exposed for use but are **not frozen public contracts** and may change. See [Governance](#governance) below.
33
+
34
+ ## What is AcquiVela?
35
+
36
+ AcquiVela is a capability-oriented document acquisition framework that orchestrates specialized acquisition engines (Docling, MinerU) through frozen public contracts and a versioned Canonical Document Model (CDM).
37
+
38
+ **Scope:** Document acquisition only (PDF, DOCX, scanned images, and more).
39
+ **Non-goals:** Embeddings, retrieval, vector DB, chunking, LLM reasoning, knowledge extraction.
40
+
41
+ ## Governed Public Contracts
42
+
43
+ The following are frozen per ADR-0001 through ADR-0006 and carry semantic-versioning compatibility guarantees:
44
+
45
+ | Contract | Module | Description |
46
+ |----------|--------|-------------|
47
+ | `AcquireRequest` | `models.py` | Input request (file path, engine hint, capability) |
48
+ | `AcquireResult` | `public_result.py` | Public result wrapper (status, canonical document, error) |
49
+ | `CanonicalDocument` | `cdm.py` | CDM root — pages + title |
50
+ | `Page` | `cdm.py` | A document page — text blocks + page number |
51
+ | `TextBlock` | `cdm.py` | A text block — text + type classification |
52
+ | `Plugin` | `plugin.py` | Plugin SPI (ABC) — `metadata()`, `acquire()`, `capabilities()` |
53
+ | `PluginHealth` | `plugin.py` | Plugin health status |
54
+ | `Capabilities` | `capabilities.py` | Canonical capability vocabulary |
55
+ | `Diagnostics` | `public_result.py` | Per-execution diagnostic metadata |
56
+
57
+ ## Internal Orchestration Infrastructure
58
+
59
+ The following components are **internal** and not frozen public contracts:
60
+
61
+ | Component | Module | Status |
62
+ |-----------|--------|--------|
63
+ | `Pipeline` | `pipeline.py` | Internal — may change before stable API decision |
64
+ | `Registry` | `registry.py` | Internal — may change before stable API decision |
65
+ | `PluginResult` | `result.py` | Internal SPI — not exported at package root |
66
+ | `ExecutionContext` | `execution_context.py` | Internal |
67
+
68
+ Plugins may NOT import `Pipeline`, `Registry`, or `public_result` — enforced via import-linter (ADR-0004 §3.2, ADR-0006 §3.3).
69
+
70
+ ## Installation
71
+
72
+ ```bash
73
+ # Base installation (no acquisition engines)
74
+ pip install acquivela
75
+
76
+ # With Docling support (PDF, DOCX, images, and more)
77
+ pip install acquivela[docling]
78
+
79
+ # With MinerU support (high-accuracy PDF)
80
+ pip install acquivela[mineru]
81
+
82
+ # With both engines
83
+ pip install acquivela[docling,mineru]
84
+ ```
85
+
86
+ ### System Dependencies
87
+
88
+ Some formats require additional system-level tools:
89
+
90
+ - **Legacy Office formats** (`.doc`, `.ppt`, `.xls`): Requires LibreOffice installed and on PATH
91
+ - **OpenDocument formats** (`.odt`, `.ods`, `.odp`): Requires `odfdo` Python package (included with `docling` extras)
92
+
93
+ ## Quick Start
94
+ ```python
95
+ # Recommended: use the M15 stable facade
96
+ from acquivela import AcquireRequest, acquire
97
+
98
+ result = acquire(
99
+ AcquireRequest(file_path="document.pdf")
100
+ )
101
+
102
+ if result.status == "success":
103
+ doc = result.canonical_document
104
+ for page in doc.pages:
105
+ for block in page.text_blocks:
106
+ print(block.text)
107
+ ```
108
+
109
+ ### Using the M15 facade (recommended normal path)
110
+ ```python
111
+ from acquivela import AcquireRequest, acquire
112
+
113
+ result = acquire(
114
+ AcquireRequest(file_path="document.pdf")
115
+ )
116
+
117
+ if result.status == "success":
118
+ doc = result.canonical_document
119
+ for page in doc.pages:
120
+ for block in page.text_blocks:
121
+ print(block.text)
122
+ ```
123
+
124
+ ### Using Engine Hints
125
+
126
+ ```python
127
+ # Force a specific engine
128
+ request = AcquireRequest(
129
+ file_path="document.pdf",
130
+ engine_hint="docling"
131
+ )
132
+ ```
133
+
134
+ ### Using Capability Filtering
135
+
136
+ ```python
137
+ from acquivela.capabilities import Capabilities
138
+
139
+ # Require text extraction capability
140
+ request = AcquireRequest(
141
+ file_path="document.pdf",
142
+ required_capability=Capabilities.DOCUMENT_TEXT
143
+ )
144
+ ```
145
+
146
+ ## Known Limitations
147
+
148
+ ### MinerU
149
+
150
+ MinerU has a known upstream issue (`PPDocLayoutV2Config` missing `reading_order_config`) that causes acquisition failures. AcquiVela correctly contains this failure — the pipeline returns `status="failure"` without exceptions escaping. The MinerU plugin reports `healthy` at the health-check level because it is importable, but actual acquisition will fail until the upstream issue is resolved.
151
+
152
+ ### Legacy Office Formats
153
+
154
+ `.doc`, `.ppt`, and `.xls` files require LibreOffice to be installed and available on PATH. Without LibreOffice, Docling cannot convert these legacy formats to modern equivalents.
155
+
156
+ ### OpenDocument Formats
157
+
158
+ `.odt`, `.ods`, and `.odp` files require the `odfdo` Python package. This is included when installing with `pip install acquivela[docling]`.
159
+
160
+ ### XML and JSON
161
+
162
+ Generic XML files are not supported. Docling supports specific XML dialects only (USPTO, JATS, XBRL, DOCLANG). Similarly, only `json_docling` format is supported for JSON input.
163
+
164
+ ### CDM v0.2 Exclusions
165
+
166
+ The Canonical Document Model (CDM) v0.2 captures text content only. Tables, images, equations, bounding boxes, and rich provenance are intentionally excluded from the current model.
167
+
168
+ ## Architecture
169
+
170
+ ### Package Structure
171
+
172
+ ```
173
+ acquivela/
174
+ ├── __init__.py # Public exports + internal orchestration (see Governance)
175
+ ├── cdm.py # CanonicalDocument, Page, TextBlock (PUBLIC/STABLE)
176
+ ├── models.py # AcquireRequest (PUBLIC/STABLE)
177
+ ├── plugin.py # Plugin SPI, PluginHealth (PUBLIC/STABLE)
178
+ ├── result.py # PluginResult (INTERNAL - not exported at root)
179
+ ├── public_result.py # AcquireResult, Diagnostics (PUBLIC/STABLE)
180
+ ├── capabilities.py # Capabilities (PUBLIC/STABLE)
181
+ ├── registry.py # Registry (INTERNAL - may change)
182
+ ├── pipeline.py # Pipeline (INTERNAL - may change)
183
+ ├── execution_context.py # ExecutionContext (INTERNAL)
184
+ └── plugins/ # Plugin implementations (one class per module)
185
+ ```
186
+
187
+ ### Status
188
+
189
+ - Charter: FROZEN
190
+ - ADR-0001 Project Boundaries: FROZEN
191
+ - ADR-0002 Execution Model: FROZEN
192
+ - ADR-0003 Core Data Contracts: FROZEN
193
+ - ADR-0004 Plugin & Execution Strategy: FROZEN
194
+ - ADR-0005 Observability & Diagnostics: FROZEN
195
+ - ADR-0006 Registry & Plugin Discovery: FROZEN
196
+
197
+ ## Development
198
+
199
+ ```bash
200
+ # Install with dev dependencies
201
+ pip install -e .[dev]
202
+
203
+ # Run contract tests
204
+ pytest tests/test_contracts.py
205
+
206
+ # Run import-linter validation
207
+ pytest tests/test_import_linter.py
208
+
209
+ # Or run import-linter directly
210
+ importlinter lint
211
+ ```
212
+
213
+ ## License
214
+
215
+ MIT License. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,188 @@
1
+ # AcquiVela
2
+
3
+ Document Acquisition Framework — Frozen Architecture v0.1
4
+
5
+ > **Note:** AcquiVela v0.1.0 is a pre-1.0 release. Some internal orchestration APIs (`Pipeline`, `Registry`) are exposed for use but are **not frozen public contracts** and may change. See [Governance](#governance) below.
6
+
7
+ ## What is AcquiVela?
8
+
9
+ AcquiVela is a capability-oriented document acquisition framework that orchestrates specialized acquisition engines (Docling, MinerU) through frozen public contracts and a versioned Canonical Document Model (CDM).
10
+
11
+ **Scope:** Document acquisition only (PDF, DOCX, scanned images, and more).
12
+ **Non-goals:** Embeddings, retrieval, vector DB, chunking, LLM reasoning, knowledge extraction.
13
+
14
+ ## Governed Public Contracts
15
+
16
+ The following are frozen per ADR-0001 through ADR-0006 and carry semantic-versioning compatibility guarantees:
17
+
18
+ | Contract | Module | Description |
19
+ |----------|--------|-------------|
20
+ | `AcquireRequest` | `models.py` | Input request (file path, engine hint, capability) |
21
+ | `AcquireResult` | `public_result.py` | Public result wrapper (status, canonical document, error) |
22
+ | `CanonicalDocument` | `cdm.py` | CDM root — pages + title |
23
+ | `Page` | `cdm.py` | A document page — text blocks + page number |
24
+ | `TextBlock` | `cdm.py` | A text block — text + type classification |
25
+ | `Plugin` | `plugin.py` | Plugin SPI (ABC) — `metadata()`, `acquire()`, `capabilities()` |
26
+ | `PluginHealth` | `plugin.py` | Plugin health status |
27
+ | `Capabilities` | `capabilities.py` | Canonical capability vocabulary |
28
+ | `Diagnostics` | `public_result.py` | Per-execution diagnostic metadata |
29
+
30
+ ## Internal Orchestration Infrastructure
31
+
32
+ The following components are **internal** and not frozen public contracts:
33
+
34
+ | Component | Module | Status |
35
+ |-----------|--------|--------|
36
+ | `Pipeline` | `pipeline.py` | Internal — may change before stable API decision |
37
+ | `Registry` | `registry.py` | Internal — may change before stable API decision |
38
+ | `PluginResult` | `result.py` | Internal SPI — not exported at package root |
39
+ | `ExecutionContext` | `execution_context.py` | Internal |
40
+
41
+ Plugins may NOT import `Pipeline`, `Registry`, or `public_result` — enforced via import-linter (ADR-0004 §3.2, ADR-0006 §3.3).
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ # Base installation (no acquisition engines)
47
+ pip install acquivela
48
+
49
+ # With Docling support (PDF, DOCX, images, and more)
50
+ pip install acquivela[docling]
51
+
52
+ # With MinerU support (high-accuracy PDF)
53
+ pip install acquivela[mineru]
54
+
55
+ # With both engines
56
+ pip install acquivela[docling,mineru]
57
+ ```
58
+
59
+ ### System Dependencies
60
+
61
+ Some formats require additional system-level tools:
62
+
63
+ - **Legacy Office formats** (`.doc`, `.ppt`, `.xls`): Requires LibreOffice installed and on PATH
64
+ - **OpenDocument formats** (`.odt`, `.ods`, `.odp`): Requires `odfdo` Python package (included with `docling` extras)
65
+
66
+ ## Quick Start
67
+ ```python
68
+ # Recommended: use the M15 stable facade
69
+ from acquivela import AcquireRequest, acquire
70
+
71
+ result = acquire(
72
+ AcquireRequest(file_path="document.pdf")
73
+ )
74
+
75
+ if result.status == "success":
76
+ doc = result.canonical_document
77
+ for page in doc.pages:
78
+ for block in page.text_blocks:
79
+ print(block.text)
80
+ ```
81
+
82
+ ### Using the M15 facade (recommended normal path)
83
+ ```python
84
+ from acquivela import AcquireRequest, acquire
85
+
86
+ result = acquire(
87
+ AcquireRequest(file_path="document.pdf")
88
+ )
89
+
90
+ if result.status == "success":
91
+ doc = result.canonical_document
92
+ for page in doc.pages:
93
+ for block in page.text_blocks:
94
+ print(block.text)
95
+ ```
96
+
97
+ ### Using Engine Hints
98
+
99
+ ```python
100
+ # Force a specific engine
101
+ request = AcquireRequest(
102
+ file_path="document.pdf",
103
+ engine_hint="docling"
104
+ )
105
+ ```
106
+
107
+ ### Using Capability Filtering
108
+
109
+ ```python
110
+ from acquivela.capabilities import Capabilities
111
+
112
+ # Require text extraction capability
113
+ request = AcquireRequest(
114
+ file_path="document.pdf",
115
+ required_capability=Capabilities.DOCUMENT_TEXT
116
+ )
117
+ ```
118
+
119
+ ## Known Limitations
120
+
121
+ ### MinerU
122
+
123
+ MinerU has a known upstream issue (`PPDocLayoutV2Config` missing `reading_order_config`) that causes acquisition failures. AcquiVela correctly contains this failure — the pipeline returns `status="failure"` without exceptions escaping. The MinerU plugin reports `healthy` at the health-check level because it is importable, but actual acquisition will fail until the upstream issue is resolved.
124
+
125
+ ### Legacy Office Formats
126
+
127
+ `.doc`, `.ppt`, and `.xls` files require LibreOffice to be installed and available on PATH. Without LibreOffice, Docling cannot convert these legacy formats to modern equivalents.
128
+
129
+ ### OpenDocument Formats
130
+
131
+ `.odt`, `.ods`, and `.odp` files require the `odfdo` Python package. This is included when installing with `pip install acquivela[docling]`.
132
+
133
+ ### XML and JSON
134
+
135
+ Generic XML files are not supported. Docling supports specific XML dialects only (USPTO, JATS, XBRL, DOCLANG). Similarly, only `json_docling` format is supported for JSON input.
136
+
137
+ ### CDM v0.2 Exclusions
138
+
139
+ The Canonical Document Model (CDM) v0.2 captures text content only. Tables, images, equations, bounding boxes, and rich provenance are intentionally excluded from the current model.
140
+
141
+ ## Architecture
142
+
143
+ ### Package Structure
144
+
145
+ ```
146
+ acquivela/
147
+ ├── __init__.py # Public exports + internal orchestration (see Governance)
148
+ ├── cdm.py # CanonicalDocument, Page, TextBlock (PUBLIC/STABLE)
149
+ ├── models.py # AcquireRequest (PUBLIC/STABLE)
150
+ ├── plugin.py # Plugin SPI, PluginHealth (PUBLIC/STABLE)
151
+ ├── result.py # PluginResult (INTERNAL - not exported at root)
152
+ ├── public_result.py # AcquireResult, Diagnostics (PUBLIC/STABLE)
153
+ ├── capabilities.py # Capabilities (PUBLIC/STABLE)
154
+ ├── registry.py # Registry (INTERNAL - may change)
155
+ ├── pipeline.py # Pipeline (INTERNAL - may change)
156
+ ├── execution_context.py # ExecutionContext (INTERNAL)
157
+ └── plugins/ # Plugin implementations (one class per module)
158
+ ```
159
+
160
+ ### Status
161
+
162
+ - Charter: FROZEN
163
+ - ADR-0001 Project Boundaries: FROZEN
164
+ - ADR-0002 Execution Model: FROZEN
165
+ - ADR-0003 Core Data Contracts: FROZEN
166
+ - ADR-0004 Plugin & Execution Strategy: FROZEN
167
+ - ADR-0005 Observability & Diagnostics: FROZEN
168
+ - ADR-0006 Registry & Plugin Discovery: FROZEN
169
+
170
+ ## Development
171
+
172
+ ```bash
173
+ # Install with dev dependencies
174
+ pip install -e .[dev]
175
+
176
+ # Run contract tests
177
+ pytest tests/test_contracts.py
178
+
179
+ # Run import-linter validation
180
+ pytest tests/test_import_linter.py
181
+
182
+ # Or run import-linter directly
183
+ importlinter lint
184
+ ```
185
+
186
+ ## License
187
+
188
+ MIT License. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,28 @@
1
+ """AcquiVela — Document Acquisition Framework.
2
+
3
+ Public exports per frozen architecture (ADR-0001 through ADR-0006).
4
+ """
5
+
6
+ from acquivela.cdm import CanonicalDocument, Page, TextBlock
7
+ from acquivela.capabilities import Capabilities
8
+ from acquivela.models import AcquireRequest
9
+ from acquivela.plugin import Plugin
10
+ from acquivela.public_result import AcquireResult, Diagnostics
11
+ from acquivela.facade import acquire
12
+ from acquivela.pipeline import Pipeline
13
+ from acquivela.registry import Registry
14
+
15
+ __version__ = "0.2.0"
16
+ __all__ = [
17
+ "CanonicalDocument",
18
+ "Page",
19
+ "TextBlock",
20
+ "Capabilities",
21
+ "AcquireRequest",
22
+ "Plugin",
23
+ "AcquireResult",
24
+ "Diagnostics",
25
+ "Pipeline",
26
+ "Registry",
27
+ "acquire",
28
+ ]
@@ -0,0 +1,19 @@
1
+ """Capability Vocabulary — Canonical capability constants for plugin selection.
2
+
3
+ Per ADR-0014: Plugins declare capabilities; consumers request them via
4
+ AcquireRequest.required_capability. Vocabulary is validated against this
5
+ module's constants before plugin lookup.
6
+ """
7
+
8
+
9
+ class Capabilities:
10
+ """Canonical capability constants.
11
+
12
+ Plugins declare capabilities as strings matching these constants.
13
+ Consumers request capabilities via AcquireRequest.required_capability.
14
+
15
+ Unknown plugin capabilities are tolerated at registration but inert
16
+ for selection — they cannot satisfy a required_capability request.
17
+ """
18
+
19
+ DOCUMENT_TEXT = "document.text"
@@ -0,0 +1,106 @@
1
+ """Canonical Document Model (CDM) v0.2 — Frozen per ADR-0003, extended per ADR-0011."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import List
6
+
7
+
8
+ class CanonicalDocument:
9
+ """CanonicalDocument — frozen public contract.
10
+
11
+ Attributes:
12
+ cdm_version: Version string, e.g., "0.2"
13
+ pages: List of Page objects (at least one required)
14
+ title: Document title, or None if unavailable (ADR-0011)
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ pages: List["Page"],
20
+ cdm_version: str = "0.2",
21
+ title: str | None = None,
22
+ ):
23
+ if not isinstance(pages, list):
24
+ raise TypeError(f"pages must be a list, got {type(pages).__name__}")
25
+ if len(pages) < 1:
26
+ raise ValueError("CanonicalDocument must contain at least one Page")
27
+ for i, page in enumerate(pages):
28
+ if not isinstance(page, Page):
29
+ raise TypeError(f"pages[{i}] must be a Page, got {type(page).__name__}")
30
+ self.cdm_version = cdm_version
31
+ self.pages = pages
32
+ self.title = title
33
+
34
+ def __repr__(self) -> str:
35
+ return (
36
+ f"CanonicalDocument(cdm_version={self.cdm_version!r}, "
37
+ f"pages={len(self.pages)}, title={self.title!r})"
38
+ )
39
+
40
+ def __eq__(self, other: object) -> bool:
41
+ if not isinstance(other, CanonicalDocument):
42
+ return NotImplemented
43
+ return (
44
+ self.cdm_version == other.cdm_version
45
+ and self.pages == other.pages
46
+ and self.title == other.title
47
+ )
48
+
49
+
50
+ class Page:
51
+ """Page within CanonicalDocument — frozen public contract.
52
+
53
+ Attributes:
54
+ text_blocks: List of TextBlock objects (at least one required)
55
+ page_number: 1-based physical page number, or None if unknown (ADR-0011)
56
+ """
57
+
58
+ def __init__(
59
+ self,
60
+ text_blocks: List["TextBlock"],
61
+ page_number: int | None = None,
62
+ ):
63
+ if not isinstance(text_blocks, list):
64
+ raise TypeError(f"text_blocks must be a list, got {type(text_blocks).__name__}")
65
+ if len(text_blocks) < 1:
66
+ raise ValueError("Page must contain at least one TextBlock")
67
+ for i, block in enumerate(text_blocks):
68
+ if not isinstance(block, TextBlock):
69
+ raise TypeError(f"text_blocks[{i}] must be a TextBlock, got {type(block).__name__}")
70
+ self.text_blocks = text_blocks
71
+ self.page_number = page_number
72
+
73
+ def __repr__(self) -> str:
74
+ return f"Page(text_blocks={len(self.text_blocks)}, page_number={self.page_number!r})"
75
+
76
+ def __eq__(self, other: object) -> bool:
77
+ if not isinstance(other, Page):
78
+ return NotImplemented
79
+ return (
80
+ self.text_blocks == other.text_blocks
81
+ and self.page_number == other.page_number
82
+ )
83
+
84
+
85
+ class TextBlock:
86
+ """TextBlock within Page — frozen public contract.
87
+
88
+ Attributes:
89
+ text: Text content string
90
+ text_type: Structural text classification, or None if unknown (ADR-0011).
91
+ Allowed values: "title", "paragraph", "list_item", "code", "caption"
92
+ """
93
+
94
+ def __init__(self, text: str, text_type: str | None = None):
95
+ if not isinstance(text, str):
96
+ raise TypeError(f"text must be a string, got {type(text).__name__}")
97
+ self.text = text
98
+ self.text_type = text_type
99
+
100
+ def __repr__(self) -> str:
101
+ return f"TextBlock(text={self.text!r}, text_type={self.text_type!r})"
102
+
103
+ def __eq__(self, other: object) -> bool:
104
+ if not isinstance(other, TextBlock):
105
+ return NotImplemented
106
+ return self.text == other.text and self.text_type == other.text_type
@@ -0,0 +1,27 @@
1
+ """ExecutionContext — Internal component per ADR-0005."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class ExecutionContext:
7
+ """Internal execution context — NOT a frozen contract.
8
+
9
+ This is an internal mechanism for carrying execution metadata
10
+ that does not belong in the frozen public contracts.
11
+ It is internal to the pipeline and carries no compatibility guarantee.
12
+
13
+ Per ADR-0005: Pipeline propagates ExecutionContext.selection_reason
14
+ into Diagnostics.selection_reason.
15
+ """
16
+
17
+ def __init__(self):
18
+ self.selection_reason: str | None = None
19
+ self.selected_plugin_id: str | None = None
20
+ self.available_plugins: list[str] = []
21
+ self.selection_method: str | None = None
22
+
23
+ def __repr__(self) -> str:
24
+ return (
25
+ f"ExecutionContext(reason={self.selection_reason!r}, "
26
+ f"plugin={self.selected_plugin_id!r}, method={self.selection_method!r})"
27
+ )
@@ -0,0 +1,47 @@
1
+ """AcquiVela high-level acquisition facade.
2
+
3
+ Provides a governed public execution entry point that allows consumers to perform
4
+ document acquisition using AcquiVela's stable public contracts without depending
5
+ directly on INTERNAL Pipeline or Registry APIs.
6
+
7
+ The facade delegates to existing internal orchestration (Pipeline/Registry/plugin
8
+ selection/health/fallback) while hiding those components from the consumer.
9
+
10
+ Do NOT promote Pipeline or Registry to PUBLIC/STABLE contracts.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Any
16
+
17
+ from acquivela import AcquireRequest
18
+ from acquivela.public_result import AcquireResult
19
+ from acquivela.pipeline import Pipeline
20
+ from acquivela.registry import Registry
21
+ from acquivela.plugins.docling import DoclingPlugin
22
+ from acquivela.plugins.mineru import MinerUPlugin
23
+
24
+
25
+ def acquire(request: AcquireRequest) -> AcquireResult:
26
+ """Acquire a document using stable public contracts.
27
+
28
+ Consumers do not need to import Pipeline or Registry.
29
+ Concrete plugin instantiation is hidden behind the facade.
30
+
31
+ Args:
32
+ request: Acquisition request specifying file path, engine hint,
33
+ and/or required capability.
34
+
35
+ Returns:
36
+ AcquireResult with status, canonical document, diagnostics, and error.
37
+
38
+ Normal consumer usage requires no direct Pipeline, Registry, or plugin
39
+ class construction.
40
+ """
41
+ # Internal bootstrap: create Registry, auto-register built-in plugins,
42
+ # then delegate to existing Pipeline orchestration.
43
+ registry = Registry()
44
+ registry.register(DoclingPlugin())
45
+ registry.register(MinerUPlugin())
46
+ pipeline = Pipeline(registry=registry)
47
+ return pipeline.execute_with_registry(request)