@xberg-io/opencode-xberg 1.3.6 → 1.3.8

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. package/.opencode/ai-rulez-bundle.json +51 -0
  2. package/.opencode/ai-rulez-content.js +167 -0
  3. package/.opencode/commands/xberg-extract.md +14 -0
  4. package/.opencode/commands/xberg-ocr.md +14 -0
  5. package/.opencode/plugins/xberg.js +75 -62
  6. package/.opencode/skills/batch-extraction/SKILL.md +168 -0
  7. package/.opencode/skills/chunking/SKILL.md +163 -0
  8. package/.opencode/skills/extracting-keywords/SKILL.md +171 -0
  9. package/.opencode/skills/extracting-tables/SKILL.md +156 -0
  10. package/.opencode/skills/extracting-with-ocr/SKILL.md +130 -0
  11. package/.opencode/skills/picking-a-format/SKILL.md +104 -0
  12. package/.opencode/skills/xberg/SKILL.md +427 -0
  13. package/.opencode/skills/xberg/references/advanced-features.md +628 -0
  14. package/.opencode/skills/xberg/references/cli-reference.md +462 -0
  15. package/.opencode/skills/xberg/references/configuration.md +587 -0
  16. package/.opencode/skills/xberg/references/nodejs-api.md +485 -0
  17. package/.opencode/skills/xberg/references/other-bindings.md +345 -0
  18. package/.opencode/skills/xberg/references/python-api.md +657 -0
  19. package/.opencode/skills/xberg/references/rust-api.md +412 -0
  20. package/.opencode/skills/xberg/references/supported-formats.md +252 -0
  21. package/.pi/prompts/xberg-extract.md +14 -0
  22. package/.pi/prompts/xberg-ocr.md +14 -0
  23. package/.pi/skills/batch-extraction/SKILL.md +168 -0
  24. package/.pi/skills/chunking/SKILL.md +163 -0
  25. package/.pi/skills/extracting-keywords/SKILL.md +171 -0
  26. package/.pi/skills/extracting-tables/SKILL.md +156 -0
  27. package/.pi/skills/extracting-with-ocr/SKILL.md +130 -0
  28. package/.pi/skills/picking-a-format/SKILL.md +104 -0
  29. package/.pi/skills/xberg/SKILL.md +427 -0
  30. package/.pi/skills/xberg/references/advanced-features.md +628 -0
  31. package/.pi/skills/xberg/references/cli-reference.md +462 -0
  32. package/.pi/skills/xberg/references/configuration.md +587 -0
  33. package/.pi/skills/xberg/references/nodejs-api.md +485 -0
  34. package/.pi/skills/xberg/references/other-bindings.md +345 -0
  35. package/.pi/skills/xberg/references/python-api.md +657 -0
  36. package/.pi/skills/xberg/references/rust-api.md +412 -0
  37. package/.pi/skills/xberg/references/supported-formats.md +252 -0
  38. package/README.md +40 -0
  39. package/package.json +69 -16
  40. package/scripts/mcp-launch.sh +188 -0
@@ -0,0 +1,657 @@
1
+ <!--
2
+ AI-RULEZ :: GENERATED FILE — DO NOT EDIT
3
+ Content-Hash: blake3:c551708aa62f1b25b1cea8041e8aab5b3101a1622317b7239d3de04d8a375754
4
+ Source-Hash: blake3:3fca38eb564d0ed9c9194d070fd8dec96de727edc92e82954cb4b9af52cddbaf
5
+ Schema-Version: v1
6
+ -->
7
+
8
+ # Xberg Python API Reference
9
+
10
+ Comprehensive documentation for the Xberg Python API. All extraction logic and heavy lifting is implemented in a high-performance Rust core; Python adds custom plugin support (post-processors, validators, OCR/embedding backends).
11
+
12
+ Install with `pip install xberg`.
13
+
14
+ The Python API is **async-only**. There are exactly two entry points — `extract` and `extract_batch` — and both are coroutines that must be awaited. There are no `extract_file` / `extract_bytes` / `extract_file_sync` / `batch_extract_files` functions.
15
+
16
+ ## Extraction Functions
17
+
18
+ ### `extract` (async)
19
+
20
+ ```python
21
+ async def extract(
22
+ input: ExtractInput | None = None,
23
+ config: ExtractionConfig | None = None,
24
+ ) -> ExtractionResult
25
+ ```
26
+
27
+ Extract content from a single input (a local path, `file://` URI, HTTP(S) URL, or raw bytes). Returns an `ExtractionResult` **envelope** — the extracted document(s) live in `result.results`.
28
+
29
+ **Parameters:**
30
+
31
+ - `input` (ExtractInput | None): The input to extract. Construct with `ExtractInput(uri=...)` or `ExtractInput(kind="bytes", ...)`.
32
+ - `config` (ExtractionConfig | None): Extraction configuration (uses defaults if None).
33
+
34
+ **Returns:** `ExtractionResult` with `results`, `errors`, and `summary`.
35
+
36
+ **Example:**
37
+
38
+ ```python
39
+ import asyncio
40
+ from xberg import ExtractInput, extract, ExtractionConfig
41
+
42
+ async def main() -> None:
43
+ result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig())
44
+ doc = result.results[0]
45
+ print(doc.content)
46
+ print(doc.metadata)
47
+ print(doc.tables)
48
+
49
+ asyncio.run(main())
50
+ ```
51
+
52
+ ### `extract_batch` (async)
53
+
54
+ ```python
55
+ async def extract_batch(
56
+ inputs: list[ExtractInput],
57
+ config: ExtractionConfig | None = None,
58
+ ) -> ExtractionResult
59
+ ```
60
+
61
+ Extract content from multiple inputs in parallel. Returns a single `ExtractionResult` envelope whose `results` list contains one `ExtractedDocument` per input (in input order), plus any per-input `errors`.
62
+
63
+ **Example:**
64
+
65
+ ```python
66
+ import asyncio
67
+ from xberg import ExtractInput, extract_batch
68
+
69
+ async def main() -> None:
70
+ inputs = [
71
+ ExtractInput(kind="uri", uri="document.pdf"),
72
+ ExtractInput(
73
+ kind="bytes",
74
+ bytes=b"Hello from memory",
75
+ mime_type="text/plain",
76
+ filename="note.txt",
77
+ ),
78
+ ]
79
+ output = await extract_batch(inputs)
80
+ for doc in output.results:
81
+ print(doc.content[:200])
82
+ print(f"Inputs: {output.summary.inputs}, results: {output.summary.results}, errors: {output.summary.errors}")
83
+
84
+ asyncio.run(main())
85
+ ```
86
+
87
+ ## Input Construction
88
+
89
+ ### ExtractInput
90
+
91
+ Unified input for both entry points.
92
+
93
+ **Fields:**
94
+
95
+ | Field | Type | Description |
96
+ | ----------- | ------------------------ | ---------------------------------------------------------------- |
97
+ | `kind` | `"uri"` \| `"bytes"` | Source kind. `"uri"` requires `uri`; `"bytes"` requires `bytes`. |
98
+ | `uri` | str \| None | Local path, `file://` URI, or HTTP(S) URL (for `kind="uri"`). |
99
+ | `bytes` | bytes \| None | Raw bytes (for `kind="bytes"`). |
100
+ | `mime_type` | str \| None | Optional MIME type hint. |
101
+ | `filename` | str \| None | Filename hint used for MIME detection and metadata. |
102
+ | `config` | FileExtractionConfig \| None | Per-input extraction overrides. |
103
+
104
+ **Construction:**
105
+
106
+ ```python
107
+ from xberg import ExtractInput
108
+
109
+ # From a path, file:// URI, or URL
110
+ ExtractInput(uri="document.pdf")
111
+ ExtractInput(uri="https://example.com/report.pdf")
112
+
113
+ # From raw bytes (mime_type or filename helps detection)
114
+ ExtractInput(kind="bytes", bytes=b"%PDF-1.4 ...", mime_type="application/pdf", filename="document.pdf")
115
+ ```
116
+
117
+ ## Configuration Classes
118
+
119
+ ### ExtractionConfig
120
+
121
+ Main extraction configuration. All fields are optional and use sensible defaults when omitted. Construct with keyword arguments.
122
+
123
+ **Common fields:**
124
+
125
+ | Field | Type | Default | Description |
126
+ | --------------------------- | --------------------------------- | ----------- | ------------------------------------------------------------------- |
127
+ | `use_cache` | bool | True | Cache extraction results to speed up repeated extractions. |
128
+ | `enable_quality_processing` | bool | True | Quality post-processing to clean and normalize extracted text. |
129
+ | `ocr` | OcrConfig \| None | None | OCR configuration. None = OCR disabled. |
130
+ | `force_ocr` | bool | False | Force OCR even for searchable PDFs that contain extractable text. |
131
+ | `chunking` | ChunkingConfig \| None | None | Text chunking configuration. None = disabled. |
132
+ | `images` | ImageExtractionConfig \| None | None | Extract images FROM documents. None = no image extraction. |
133
+ | `pdf_options` | PdfConfig \| None | None | PDF-specific options. |
134
+ | `token_reduction` | TokenReductionOptions \| None | None | Reduce token count in extracted content. None = no reduction. |
135
+ | `language_detection` | LanguageDetectionConfig \| None | None | Detect document language(s). |
136
+ | `keywords` | KeywordConfig \| None | None | Keyword extraction. |
137
+ | `pages` | PageConfig \| None | None | Per-page content extraction and tracking. |
138
+ | `postprocessor` | PostProcessorConfig \| None | None | Post-processor pipeline configuration. |
139
+ | `html_output` | HtmlOutputConfig \| None | None | Styled HTML output configuration (when `output_format="html"`). |
140
+ | `max_concurrent_extractions`| int \| None | None | Optional batch ceiling; when unset, concurrency derives from `concurrency.max_threads`. |
141
+ | `result_format` | `"unified"` \| `"element_based"` | "unified" | Result structure format. |
142
+ | `output_format` | OutputFormat \| str | "plain" | Content renderer: plain, markdown, djot, HTML, JSON, DocTags, or custom. |
143
+ | `security_limits` | SecurityLimits \| None | None | Security limits for archive extraction. |
144
+
145
+ **Example:**
146
+
147
+ ```python
148
+ from xberg import ExtractInput, extract, ExtractionConfig, OcrConfig, ChunkingConfig
149
+
150
+ config = ExtractionConfig(
151
+ use_cache=True,
152
+ enable_quality_processing=True,
153
+ output_format="markdown",
154
+ ocr=OcrConfig(backend="tesseract", language=["eng"]),
155
+ chunking=ChunkingConfig(max_characters=1000, overlap=200),
156
+ )
157
+ result = await extract(ExtractInput(uri="document.pdf"), config)
158
+ ```
159
+
160
+ ### FileExtractionConfig
161
+
162
+ Per-input extraction overrides, set on `ExtractInput.config`. All fields are `Optional` — `None` means "use the batch/default config value". Mirrors `ExtractionConfig` minus batch-level concerns (`max_concurrent_extractions`, `use_cache`, `acceleration`, `security_limits`).
163
+
164
+ ```python
165
+ from xberg import ExtractInput, FileExtractionConfig, OcrConfig
166
+
167
+ scanned = ExtractInput(
168
+ kind="uri",
169
+ uri="scanned.pdf",
170
+ config=FileExtractionConfig(
171
+ force_ocr=True,
172
+ ocr=OcrConfig(backend="tesseract", language=["deu"]),
173
+ ),
174
+ )
175
+ ```
176
+
177
+ ### OcrConfig
178
+
179
+ OCR configuration for extracting text from images and scanned pages.
180
+
181
+ | Field | Type | Default | Description |
182
+ | ------------------ | ----------------------- | ----------- | ------------------------------------------------------------------------------------------- |
183
+ | `enabled` | bool | True | Whether OCR is enabled. |
184
+ | `backend` | str | `""` | OCR backend: `"tesseract"`, `"paddleocr"`, `"paddle-ocr"`, or `"vlm"`. |
185
+ | `language` | list[str] | `["eng"]` | Language codes. Use multiple entries for multilingual OCR. |
186
+ | `tesseract_config` | TesseractConfig \| None | None | Tesseract-specific configuration. |
187
+
188
+ ```python
189
+ from xberg import OcrConfig, TesseractConfig
190
+
191
+ OcrConfig(backend="tesseract", language=["deu"])
192
+ OcrConfig(backend="tesseract", language=["eng", "fra"], tesseract_config=TesseractConfig(psm=6))
193
+ OcrConfig(backend="paddleocr", language=["chinese"])
194
+ ```
195
+
196
+ ### TesseractConfig
197
+
198
+ Detailed Tesseract OCR tuning. Common fields:
199
+
200
+ | Field | Type | Default | Description |
201
+ | ------------------------ | -------------------------------- | ------- | ----------------------------------------------------------------------------------------- |
202
+ | `psm` | int | 3 | Page Segmentation Mode: 0 (detection only), 3 (auto), 6 (uniform block), 11 (sparse text). |
203
+ | `oem` | int | 3 | OCR Engine Mode: 0 (legacy), 1 (LSTM), 2 (both), 3 (auto). |
204
+ | `min_confidence` | float | 0.0 | Minimum confidence threshold (0.0–1.0). |
205
+ | `preprocessing` | ImagePreprocessingConfig \| None | None | Image preprocessing before OCR. |
206
+ | `enable_table_detection` | bool | True | Detect and extract tables from OCR output. |
207
+ | `tessedit_char_whitelist`| str | `""` | Whitelist of characters to recognize (empty = all). |
208
+
209
+ ```python
210
+ from xberg import TesseractConfig, ImagePreprocessingConfig
211
+
212
+ TesseractConfig(psm=6, oem=1, min_confidence=0.8, enable_table_detection=True)
213
+ TesseractConfig(preprocessing=ImagePreprocessingConfig(target_dpi=300, denoise=True))
214
+ ```
215
+
216
+ ### ChunkingConfig
217
+
218
+ Text chunking for RAG, indexing, and length-limited systems.
219
+
220
+ | Field | Type | Default | Description |
221
+ | ------------------------- | ------------------------- | --------- | -------------------------------------------------------------------------- |
222
+ | `max_characters` | int | 1000 | Maximum size per chunk (in units determined by `sizing`). |
223
+ | `overlap` | int | 200 | Overlap between consecutive chunks. |
224
+ | `trim` | bool | True | Trim whitespace from chunk boundaries. |
225
+ | `chunker_type` | `"text"` \| `"markdown"` \| `"semantic"` | "text" | Chunking strategy. |
226
+ | `embedding` | EmbeddingConfig \| None | None | Generate an embedding per chunk. None = no embeddings. |
227
+ | `preset` | str \| None | None | Named preset (overrides individual settings if provided). |
228
+
229
+ > **IMPORTANT:** The fields are `max_characters` and `overlap` (NOT `max_chars`/`max_overlap` or `max_chars`/`max_chars`).
230
+
231
+ ```python
232
+ from xberg import ExtractionConfig, ChunkingConfig, EmbeddingConfig, EmbeddingModelType
233
+
234
+ # Defaults (1000 / 200)
235
+ ExtractionConfig(chunking=ChunkingConfig())
236
+
237
+ # Custom size and overlap
238
+ ExtractionConfig(chunking=ChunkingConfig(max_characters=512, overlap=100))
239
+
240
+ # Markdown chunker with embeddings
241
+ ExtractionConfig(
242
+ chunking=ChunkingConfig(
243
+ chunker_type="markdown",
244
+ max_characters=512,
245
+ overlap=50,
246
+ embedding=EmbeddingConfig(model=EmbeddingModelType.preset("balanced")),
247
+ )
248
+ )
249
+ ```
250
+
251
+ ### EmbeddingConfig
252
+
253
+ Embedding generation for chunks using ONNX models.
254
+
255
+ | Field | Type | Default | Description |
256
+ | ------------------------ | -------------------------- | ------------------ | ------------------------------------------------------- |
257
+ | `model` | EmbeddingModelType \| None | "balanced" preset | The embedding model to use. |
258
+ | `normalize` | bool | True | Normalize vectors to unit length (cosine similarity). |
259
+ | `batch_size` | int | 32 | Texts to embed per batch. |
260
+ | `show_download_progress` | bool | False | Show model download progress. |
261
+ | `cache_dir` | str \| None | None | Custom model cache directory. |
262
+
263
+ ### EmbeddingModelType
264
+
265
+ Embedding model selector. Static constructors:
266
+
267
+ ```python
268
+ @staticmethod
269
+ def preset(name: str) -> EmbeddingModelType # "fast", "balanced", "quality", "multilingual"
270
+
271
+ @staticmethod
272
+ def custom(model_id: str, dimensions: int) -> EmbeddingModelType # any ONNX model on HuggingFace
273
+
274
+ @staticmethod
275
+ def llm(llm) -> EmbeddingModelType # an LLM-backed embedding model
276
+
277
+ @staticmethod
278
+ def plugin(name: str) -> EmbeddingModelType # a registered embedding backend
279
+ ```
280
+
281
+ ```python
282
+ from xberg import EmbeddingConfig, EmbeddingModelType
283
+
284
+ EmbeddingConfig(model=EmbeddingModelType.preset("balanced"))
285
+ EmbeddingConfig(model=EmbeddingModelType.custom(model_id="BAAI/bge-small-en-v1.5", dimensions=384))
286
+ ```
287
+
288
+ Presets: `fast` (384 dims), `balanced` (768 dims), `quality` (1024 dims), `multilingual` (768 dims).
289
+
290
+ ### KeywordConfig
291
+
292
+ Keyword extraction (YAKE or RAKE).
293
+
294
+ | Field | Type | Default | Description |
295
+ | -------------- | --------------------------------- | ------- | ------------------------------------------ |
296
+ | `algorithm` | KeywordAlgorithm \| str | YAKE | `KeywordAlgorithm.YAKE` or `.RAKE` (or `"yake"`/`"rake"`). |
297
+ | `max_keywords` | int | 10 | Maximum number of keywords to extract. |
298
+ | `min_score` | float | 0.0 | Minimum score threshold. |
299
+ | `language` | str \| None | "en" | Language hint. |
300
+ | `yake_params` | YakeParams \| None | None | YAKE tuning (`window_size`). |
301
+ | `rake_params` | RakeParams \| None | None | RAKE tuning (`min_word_length`, `max_words_per_phrase`). |
302
+
303
+ ```python
304
+ from xberg import ExtractionConfig, KeywordConfig, KeywordAlgorithm, YakeParams
305
+
306
+ ExtractionConfig(
307
+ keywords=KeywordConfig(
308
+ algorithm=KeywordAlgorithm.YAKE,
309
+ max_keywords=10,
310
+ min_score=0.1,
311
+ language="en",
312
+ yake_params=YakeParams(window_size=1),
313
+ )
314
+ )
315
+ ```
316
+
317
+ ### PdfConfig
318
+
319
+ | Field | Type | Default | Description |
320
+ | ------------------ | ----------------------- | ------- | ------------------------------------------------- |
321
+ | `extract_images` | bool | False | Extract images from PDF documents. |
322
+ | `extract_tables` | bool | True | Extract tables from PDF. |
323
+ | `passwords` | list[str] \| None | None | Passwords to try when opening encrypted PDFs. |
324
+ | `extract_metadata` | bool | True | Extract PDF metadata. |
325
+ | `hierarchy` | HierarchyConfig \| None | None | Document hierarchy detection. |
326
+
327
+ ```python
328
+ from xberg import ExtractionConfig, PdfConfig, HierarchyConfig
329
+
330
+ ExtractionConfig(
331
+ pdf_options=PdfConfig(
332
+ extract_images=True,
333
+ extract_metadata=True,
334
+ passwords=["password1", "password2"],
335
+ hierarchy=HierarchyConfig(enabled=True, k_clusters=6),
336
+ )
337
+ )
338
+ ```
339
+
340
+ ### LanguageDetectionConfig
341
+
342
+ | Field | Type | Default | Description |
343
+ | ----------------- | ----- | ------- | ---------------------------------------------------- |
344
+ | `enabled` | bool | True | Enable language detection. |
345
+ | `min_confidence` | float | 0.8 | Minimum confidence threshold (0.0–1.0). |
346
+ | `detect_multiple` | bool | False | Detect multiple languages instead of only the top one. |
347
+
348
+ ### TokenReductionOptions
349
+
350
+ | Field | Type | Default | Description |
351
+ | -------------------------- | ---- | ------- | --------------------------------------------------------------------- |
352
+ | `mode` | str | `""` | `"off"`, `"light"`, `"moderate"`, `"aggressive"`, or `"maximum"`. |
353
+ | `preserve_important_words` | bool | True | Preserve capitalized words, technical terms, and proper nouns. |
354
+
355
+ ### PageConfig
356
+
357
+ | Field | Type | Default | Description |
358
+ | --------------------- | ---- | -------------------------------------- | -------------------------------------------- |
359
+ | `extract_pages` | bool | False | Extract pages into `ExtractedDocument.pages`. |
360
+ | `insert_page_markers` | bool | False | Insert page markers into content. |
361
+ | `marker_format` | str | `"\n\n<!-- PAGE {page_num} -->\n\n"` | Marker template containing `{page_num}`. |
362
+
363
+ ### PostProcessorConfig
364
+
365
+ | Field | Type | Default | Description |
366
+ | --------------------- | ----------------- | ------- | ----------------------------------------------------------- |
367
+ | `enabled` | bool | True | Enable post-processors in the extraction pipeline. |
368
+ | `enabled_processors` | list[str] \| None | None | Whitelist of processor names to run. None = run all enabled. |
369
+ | `disabled_processors` | list[str] \| None | None | Blacklist of processor names to skip. |
370
+
371
+ ### ImagePreprocessingConfig
372
+
373
+ Preprocessing applied to images **before OCR** (not for extracting images from documents).
374
+
375
+ | Field | Type | Default | Description |
376
+ | --------------------- | ---- | ------- | ------------------------------------------------- |
377
+ | `target_dpi` | int | 300 | Target DPI for normalization before OCR. |
378
+ | `auto_rotate` | bool | False | Detect and correct image rotation. |
379
+ | `deskew` | bool | True | Correct skewed images. |
380
+ | `denoise` | bool | False | Apply denoising filters. |
381
+ | `contrast_enhance` | bool | False | Enhance contrast. |
382
+ | `binarization_method` | str | "otsu" | Method for black-and-white conversion. |
383
+ | `invert_colors` | bool | False | Invert colors (white text on black). |
384
+
385
+ ### ImageExtractionConfig
386
+
387
+ Configuration for extracting images **from** documents.
388
+
389
+ | Field | Type | Default | Description |
390
+ | --------------------- | ---- | ------- | ------------------------------------------------- |
391
+ | `extract_images` | bool | True | Enable image extraction. |
392
+ | `target_dpi` | int | 300 | Target DPI for image normalization. |
393
+ | `max_image_dimension` | int | 4096 | Maximum width/height for extracted images. |
394
+ | `auto_adjust_dpi` | bool | True | Automatically adjust DPI by content quality. |
395
+ | `min_dpi` | int | 72 | Minimum DPI threshold. |
396
+ | `max_dpi` | int | 600 | Maximum DPI threshold. |
397
+
398
+ ## ExtractionResult (envelope) and ExtractedDocument
399
+
400
+ `extract` and `extract_batch` both return an **`ExtractionResult` envelope**. Per-document data lives on each `ExtractedDocument` in `result.results`.
401
+
402
+ ### ExtractionResult
403
+
404
+ | Field | Type | Description |
405
+ | --------- | --------------------------- | -------------------------------------------- |
406
+ | `results` | list[ExtractedDocument] | Extracted documents in discovery/input order. |
407
+ | `errors` | list[ExtractionErrorItem] | Non-fatal per-input errors. |
408
+ | `summary` | ExtractionSummary | Aggregate counts for the operation. |
409
+
410
+ `ExtractionSummary` fields: `inputs`, `results`, `errors`, `remote_urls`, `pages_crawled`, `documents_downloaded`.
411
+
412
+ ### ExtractedDocument
413
+
414
+ | Field | Type | Description |
415
+ | --------------------- | ------------------------------ | ----------------------------------------------------------------- |
416
+ | `content` | str | Main extracted text in the configured `output_format`. |
417
+ | `mime_type` | str | MIME type of the source document. |
418
+ | `metadata` | Metadata | Document metadata (flat — format-specific fields at the top level). |
419
+ | `tables` | list[Table] | Extracted tables (each with `cells` and `markdown`). |
420
+ | `detected_languages` | list[str] \| None | Detected ISO 639-1 codes (if language detection enabled). |
421
+ | `chunks` | list[Chunk] \| None | Text chunks (if chunking enabled). |
422
+ | `images` | list[ExtractedImage] \| None | Extracted images (if enabled). |
423
+ | `pages` | list[PageContent] \| None | Per-page content (if page extraction enabled). |
424
+ | `elements` | list[Element] \| None | Semantic elements (if `result_format="element_based"`). |
425
+ | `extracted_keywords` | list[Keyword] \| None | Extracted keywords (if enabled). |
426
+ | `quality_score` | float \| None | Overall quality score (0.0–1.0). |
427
+ | `processing_warnings` | list[ProcessingWarning] | Non-fatal warnings from the pipeline. |
428
+
429
+ **Example:**
430
+
431
+ ```python
432
+ import asyncio
433
+ from xberg import ExtractInput, extract, ExtractionConfig, ChunkingConfig
434
+
435
+ async def main() -> None:
436
+ config = ExtractionConfig(
437
+ chunking=ChunkingConfig(max_characters=512),
438
+ output_format="markdown",
439
+ language_detection=None,
440
+ )
441
+ result = await extract(ExtractInput(uri="document.pdf"), config)
442
+ doc = result.results[0]
443
+
444
+ print(f"Content preview: {doc.content[:200]}")
445
+ print(f"MIME type: {doc.mime_type}")
446
+ print(f"Tables: {len(doc.tables)}")
447
+
448
+ if doc.detected_languages:
449
+ print(f"Languages: {doc.detected_languages}")
450
+
451
+ for chunk in doc.chunks or []:
452
+ print(f"Chunk: {chunk.content[:80]}")
453
+ if chunk.embedding:
454
+ print(f" embedding dims: {len(chunk.embedding)}")
455
+
456
+ asyncio.run(main())
457
+ ```
458
+
459
+ ### Metadata access
460
+
461
+ Metadata exposes typed attributes — access fields directly. Format-specific metadata lives under `metadata.format`:
462
+
463
+ ```python
464
+ result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig())
465
+ metadata = result.results[0].metadata
466
+
467
+ if metadata.pages:
468
+ print(f"Pages: {metadata.pages.total_count}")
469
+ if metadata.title:
470
+ print(f"Title: {metadata.title}")
471
+ if metadata.authors:
472
+ print(f"Authors: {', '.join(metadata.authors)}")
473
+ ```
474
+
475
+ ### Tables
476
+
477
+ ```python
478
+ result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig())
479
+ for table in result.results[0].tables:
480
+ print(f"Table with {len(table.cells)} rows")
481
+ print(table.markdown)
482
+ for row in table.cells:
483
+ print(row)
484
+ ```
485
+
486
+ ## Error Classes
487
+
488
+ The errors raised by `extract()` and `extract_batch()` are plain `RuntimeError` — the typed exception classes below are **not** raised by these entry points, so catch `RuntimeError` (or `Exception`) around an `extract()`/`extract_batch()` call.
489
+
490
+ Xberg also defines a family of typed exception classes re-exported from `xberg`. The `XbergError`-rooted ones inherit from `XbergError` (base): `ParsingError`, `OcrError`, `ValidationError`, `MissingDependencyError`, `UnsupportedFormatError`, `CacheError`, `ImageProcessingError`, `SerializationError`, `PluginError`, `SecurityError`, `TranscriptionError`, `EmbeddingError`, `RerankingError`, `XbergTimeoutError`, `IoError`, and others. A few classes sit in separate hierarchies rooted at `Exception` rather than `XbergError` — e.g. `ConfigError`/`PdfAnalysisError` and `ParseError`/`SchemaValidationError`.
491
+
492
+ > Note: the OCR exception is `OcrError` (not `OCRError`).
493
+
494
+ ```python
495
+ import asyncio
496
+ from xberg import ExtractInput, extract, ExtractionConfig
497
+
498
+ async def main() -> None:
499
+ try:
500
+ result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig())
501
+ except RuntimeError as e:
502
+ print(f"Extraction failed: {e}")
503
+
504
+ asyncio.run(main())
505
+ ```
506
+
507
+ Per-input failures during `extract_batch` are also reported non-fatally in `result.errors` (a list of `ExtractionErrorItem`) rather than raised.
508
+
509
+ ## Plugin System
510
+
511
+ Plugins extend the Rust pipeline. Plugin callbacks receive a single `ExtractedDocument` (with `.content`, `.mime_type`, `.metadata`), not the envelope.
512
+
513
+ ### Registering Post-Processors
514
+
515
+ `register_post_processor(processor)` — runs after extraction to enrich each result.
516
+
517
+ Required methods: `process(result: ExtractedDocument) -> ExtractedDocument`, `processing_stage() -> str` (`"early"`, `"middle"`, `"late"`). Optional: `name() -> str`, `version() -> str`, `should_process(result) -> bool`, `initialize()`, `shutdown()`.
518
+
519
+ ```python
520
+ from xberg import register_post_processor, ExtractedDocument
521
+
522
+ class WordCountProcessor:
523
+ def name(self) -> str:
524
+ return "word_count"
525
+
526
+ def version(self) -> str:
527
+ return "1.1.0"
528
+
529
+ def processing_stage(self) -> str:
530
+ return "early"
531
+
532
+ def process(self, result: ExtractedDocument) -> ExtractedDocument:
533
+ result.metadata["word_count"] = len(result.content.split())
534
+ return result
535
+
536
+ def should_process(self, result: ExtractedDocument) -> bool:
537
+ return bool(result.content)
538
+
539
+ register_post_processor(WordCountProcessor())
540
+ ```
541
+
542
+ ### Registering Validators
543
+
544
+ `register_validator(validator)` — runs after extraction; raising fails the extraction.
545
+
546
+ Expected methods: `name() -> str`, `version() -> str`, `validate(result: ExtractedDocument) -> None`. Optional: `should_validate(result) -> bool`, `priority() -> int` (higher runs first), `initialize()`, `shutdown()`.
547
+
548
+ ```python
549
+ from xberg import register_validator, ExtractedDocument, ValidationError
550
+
551
+ class MinLengthValidator:
552
+ def __init__(self, min_length: int = 100):
553
+ self.min_length = min_length
554
+
555
+ def name(self) -> str:
556
+ return "min_length_validator"
557
+
558
+ def version(self) -> str:
559
+ return "1.1.0"
560
+
561
+ def priority(self) -> int:
562
+ return 100
563
+
564
+ def validate(self, result: ExtractedDocument) -> None:
565
+ if len(result.content) < self.min_length:
566
+ raise ValidationError(f"Content too short: {len(result.content)}")
567
+
568
+ register_validator(MinLengthValidator())
569
+ ```
570
+
571
+ ### Registering Document Extractors
572
+
573
+ `register_document_extractor(extractor)` — adds support for a custom format.
574
+
575
+ Expected methods: `name() -> str`, `version() -> str`, `supported_mime_types() -> list[str]`, `priority() -> int`, `extract(content: bytes, mime_type: str, config: dict) -> ExtractedDocument`. Optional: `initialize()`, `shutdown()`.
576
+
577
+ ```python
578
+ from xberg import register_document_extractor, ExtractedDocument
579
+ import json
580
+
581
+ class CustomJsonExtractor:
582
+ def name(self) -> str:
583
+ return "custom-json-extractor"
584
+
585
+ def version(self) -> str:
586
+ return "1.1.0"
587
+
588
+ def supported_mime_types(self) -> list[str]:
589
+ return ["application/json"]
590
+
591
+ def priority(self) -> int:
592
+ return 50
593
+
594
+ def extract(self, content: bytes, mime_type: str, config: dict) -> ExtractedDocument:
595
+ data = json.loads(content)
596
+ return {"content": json.dumps(data, indent=2), "mime_type": "application/json"}
597
+
598
+ register_document_extractor(CustomJsonExtractor())
599
+ ```
600
+
601
+ ### Registering OCR and Embedding Backends
602
+
603
+ `register_ocr_backend(backend)` and `register_embedding_backend(backend)` plug in custom OCR/embedding engines.
604
+
605
+ ```python
606
+ from xberg import register_embedding_backend
607
+
608
+ class MyEmbedder:
609
+ def name(self) -> str:
610
+ return "my-embedder"
611
+
612
+ def version(self) -> str:
613
+ return "1.1.0"
614
+
615
+ def dimensions(self) -> int:
616
+ return 768
617
+
618
+ def embed(self, texts: list[str]) -> list[list[float]]:
619
+ ... # return one vector per text
620
+
621
+ register_embedding_backend(MyEmbedder())
622
+ ```
623
+
624
+ Reference an embedding backend from config via `EmbeddingConfig(model={"type": "plugin", "name": "my-embedder"})`.
625
+
626
+ ### Plugin Management
627
+
628
+ ```python
629
+ from xberg import (
630
+ list_document_extractors, list_post_processors, list_validators, list_ocr_backends,
631
+ unregister_post_processor, unregister_validator, unregister_document_extractor, unregister_ocr_backend,
632
+ clear_post_processors, clear_validators, clear_document_extractors, clear_ocr_backends,
633
+ )
634
+
635
+ print(list_post_processors())
636
+ unregister_post_processor("word_count")
637
+ clear_validators()
638
+ ```
639
+
640
+ ## Format Enums
641
+
642
+ ### Content output format (`output_format`)
643
+
644
+ `"plain"` (default), `"markdown"`, `"djot"`, `"html"`, `"json"`, `"doctags"`, or a registered custom renderer name.
645
+
646
+ ### Result format (`result_format`)
647
+
648
+ `"unified"` (default — all content in `content`) or `"element_based"` (Unstructured-compatible semantic elements in `elements`).
649
+
650
+ ## Other Functions
651
+
652
+ ```python
653
+ from xberg import list_supported_formats, __version__
654
+
655
+ formats = list_supported_formats() # all supported document formats
656
+ print(__version__)
657
+ ```