@xberg-io/opencode-xberg 1.3.8 → 1.4.0
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.
- package/.opencode/ai-rulez-bundle.json +17 -16
- package/.opencode/ai-rulez-content.js +3 -3
- package/.opencode/commands/xberg-extract.md +3 -3
- package/.opencode/commands/xberg-ocr.md +3 -3
- package/.opencode/plugins/xberg.js +1 -1
- package/.opencode/skills/xberg/SKILL.md +20 -25
- package/.opencode/skills/xberg/index.md +10 -0
- package/.opencode/skills/xberg/references/advanced-features.md +1 -1
- package/.opencode/skills/xberg/references/cli-reference.md +6 -4
- package/.opencode/skills/xberg/references/configuration.md +6 -6
- package/.opencode/skills/xberg/references/nodejs-api.md +1 -1
- package/.opencode/skills/xberg/references/other-bindings.md +1 -1
- package/.opencode/skills/xberg/references/python-api.md +6 -6
- package/.opencode/skills/xberg/references/rust-api.md +5 -5
- package/.opencode/skills/xberg/references/supported-formats.md +1 -1
- package/.opencode/skills/{batch-extraction → xberg-batch-extraction}/SKILL.md +11 -9
- package/.opencode/skills/xberg-batch-extraction/index.md +10 -0
- package/.opencode/skills/xberg-batch-extraction/references/advanced-features.md +628 -0
- package/.opencode/skills/xberg-batch-extraction/references/cli-reference.md +464 -0
- package/.opencode/skills/xberg-batch-extraction/references/configuration.md +587 -0
- package/.opencode/skills/xberg-batch-extraction/references/nodejs-api.md +485 -0
- package/.opencode/skills/xberg-batch-extraction/references/other-bindings.md +345 -0
- package/.opencode/skills/xberg-batch-extraction/references/python-api.md +657 -0
- package/.opencode/skills/xberg-batch-extraction/references/rust-api.md +412 -0
- package/.opencode/skills/xberg-batch-extraction/references/supported-formats.md +252 -0
- package/{.pi/skills/chunking → .opencode/skills/xberg-chunking}/SKILL.md +5 -5
- package/.opencode/skills/xberg-chunking/index.md +10 -0
- package/.opencode/skills/xberg-chunking/references/advanced-features.md +628 -0
- package/.opencode/skills/xberg-chunking/references/cli-reference.md +464 -0
- package/.opencode/skills/xberg-chunking/references/configuration.md +587 -0
- package/.opencode/skills/xberg-chunking/references/nodejs-api.md +485 -0
- package/.opencode/skills/xberg-chunking/references/other-bindings.md +345 -0
- package/.opencode/skills/xberg-chunking/references/python-api.md +657 -0
- package/.opencode/skills/xberg-chunking/references/rust-api.md +412 -0
- package/.opencode/skills/xberg-chunking/references/supported-formats.md +252 -0
- package/.opencode/skills/{extracting-keywords → xberg-extracting-keywords}/SKILL.md +7 -8
- package/.opencode/skills/xberg-extracting-keywords/index.md +10 -0
- package/.opencode/skills/xberg-extracting-keywords/references/advanced-features.md +628 -0
- package/.opencode/skills/xberg-extracting-keywords/references/cli-reference.md +464 -0
- package/.opencode/skills/xberg-extracting-keywords/references/configuration.md +587 -0
- package/.opencode/skills/xberg-extracting-keywords/references/nodejs-api.md +485 -0
- package/.opencode/skills/xberg-extracting-keywords/references/other-bindings.md +345 -0
- package/.opencode/skills/xberg-extracting-keywords/references/python-api.md +657 -0
- package/.opencode/skills/xberg-extracting-keywords/references/rust-api.md +412 -0
- package/.opencode/skills/xberg-extracting-keywords/references/supported-formats.md +252 -0
- package/{.pi/skills/extracting-tables → .opencode/skills/xberg-extracting-tables}/SKILL.md +4 -5
- package/.opencode/skills/xberg-extracting-tables/index.md +10 -0
- package/.opencode/skills/xberg-extracting-tables/references/advanced-features.md +628 -0
- package/.opencode/skills/xberg-extracting-tables/references/cli-reference.md +464 -0
- package/.opencode/skills/xberg-extracting-tables/references/configuration.md +587 -0
- package/.opencode/skills/xberg-extracting-tables/references/nodejs-api.md +485 -0
- package/.opencode/skills/xberg-extracting-tables/references/other-bindings.md +345 -0
- package/.opencode/skills/xberg-extracting-tables/references/python-api.md +657 -0
- package/.opencode/skills/xberg-extracting-tables/references/rust-api.md +412 -0
- package/.opencode/skills/xberg-extracting-tables/references/supported-formats.md +252 -0
- package/{.pi/skills/extracting-with-ocr → .opencode/skills/xberg-extracting-with-ocr}/SKILL.md +5 -6
- package/.opencode/skills/xberg-extracting-with-ocr/index.md +10 -0
- package/.opencode/skills/xberg-extracting-with-ocr/references/advanced-features.md +628 -0
- package/.opencode/skills/xberg-extracting-with-ocr/references/cli-reference.md +464 -0
- package/.opencode/skills/xberg-extracting-with-ocr/references/configuration.md +587 -0
- package/.opencode/skills/xberg-extracting-with-ocr/references/nodejs-api.md +485 -0
- package/.opencode/skills/xberg-extracting-with-ocr/references/other-bindings.md +345 -0
- package/.opencode/skills/xberg-extracting-with-ocr/references/python-api.md +657 -0
- package/.opencode/skills/xberg-extracting-with-ocr/references/rust-api.md +412 -0
- package/.opencode/skills/xberg-extracting-with-ocr/references/supported-formats.md +252 -0
- package/.opencode/skills/{picking-a-format → xberg-picking-a-format}/SKILL.md +4 -4
- package/.opencode/skills/xberg-picking-a-format/index.md +10 -0
- package/.opencode/skills/xberg-picking-a-format/references/advanced-features.md +628 -0
- package/.opencode/skills/xberg-picking-a-format/references/cli-reference.md +464 -0
- package/.opencode/skills/xberg-picking-a-format/references/configuration.md +587 -0
- package/.opencode/skills/xberg-picking-a-format/references/nodejs-api.md +485 -0
- package/.opencode/skills/xberg-picking-a-format/references/other-bindings.md +345 -0
- package/.opencode/skills/xberg-picking-a-format/references/python-api.md +657 -0
- package/.opencode/skills/xberg-picking-a-format/references/rust-api.md +412 -0
- package/.opencode/skills/xberg-picking-a-format/references/supported-formats.md +252 -0
- package/.pi/prompts/xberg-extract.md +1 -1
- package/.pi/prompts/xberg-ocr.md +1 -1
- package/.pi/skills/xberg/SKILL.md +20 -25
- package/.pi/skills/xberg/index.md +10 -0
- package/.pi/skills/xberg/references/advanced-features.md +1 -1
- package/.pi/skills/xberg/references/cli-reference.md +6 -4
- package/.pi/skills/xberg/references/configuration.md +6 -6
- package/.pi/skills/xberg/references/nodejs-api.md +1 -1
- package/.pi/skills/xberg/references/other-bindings.md +1 -1
- package/.pi/skills/xberg/references/python-api.md +6 -6
- package/.pi/skills/xberg/references/rust-api.md +5 -5
- package/.pi/skills/xberg/references/supported-formats.md +1 -1
- package/.pi/skills/{batch-extraction → xberg-batch-extraction}/SKILL.md +11 -9
- package/.pi/skills/xberg-batch-extraction/index.md +10 -0
- package/.pi/skills/xberg-batch-extraction/references/advanced-features.md +628 -0
- package/.pi/skills/xberg-batch-extraction/references/cli-reference.md +464 -0
- package/.pi/skills/xberg-batch-extraction/references/configuration.md +587 -0
- package/.pi/skills/xberg-batch-extraction/references/nodejs-api.md +485 -0
- package/.pi/skills/xberg-batch-extraction/references/other-bindings.md +345 -0
- package/.pi/skills/xberg-batch-extraction/references/python-api.md +657 -0
- package/.pi/skills/xberg-batch-extraction/references/rust-api.md +412 -0
- package/.pi/skills/xberg-batch-extraction/references/supported-formats.md +252 -0
- package/{.opencode/skills/chunking → .pi/skills/xberg-chunking}/SKILL.md +5 -5
- package/.pi/skills/xberg-chunking/index.md +10 -0
- package/.pi/skills/xberg-chunking/references/advanced-features.md +628 -0
- package/.pi/skills/xberg-chunking/references/cli-reference.md +464 -0
- package/.pi/skills/xberg-chunking/references/configuration.md +587 -0
- package/.pi/skills/xberg-chunking/references/nodejs-api.md +485 -0
- package/.pi/skills/xberg-chunking/references/other-bindings.md +345 -0
- package/.pi/skills/xberg-chunking/references/python-api.md +657 -0
- package/.pi/skills/xberg-chunking/references/rust-api.md +412 -0
- package/.pi/skills/xberg-chunking/references/supported-formats.md +252 -0
- package/.pi/skills/{extracting-keywords → xberg-extracting-keywords}/SKILL.md +7 -8
- package/.pi/skills/xberg-extracting-keywords/index.md +10 -0
- package/.pi/skills/xberg-extracting-keywords/references/advanced-features.md +628 -0
- package/.pi/skills/xberg-extracting-keywords/references/cli-reference.md +464 -0
- package/.pi/skills/xberg-extracting-keywords/references/configuration.md +587 -0
- package/.pi/skills/xberg-extracting-keywords/references/nodejs-api.md +485 -0
- package/.pi/skills/xberg-extracting-keywords/references/other-bindings.md +345 -0
- package/.pi/skills/xberg-extracting-keywords/references/python-api.md +657 -0
- package/.pi/skills/xberg-extracting-keywords/references/rust-api.md +412 -0
- package/.pi/skills/xberg-extracting-keywords/references/supported-formats.md +252 -0
- package/{.opencode/skills/extracting-tables → .pi/skills/xberg-extracting-tables}/SKILL.md +4 -5
- package/.pi/skills/xberg-extracting-tables/index.md +10 -0
- package/.pi/skills/xberg-extracting-tables/references/advanced-features.md +628 -0
- package/.pi/skills/xberg-extracting-tables/references/cli-reference.md +464 -0
- package/.pi/skills/xberg-extracting-tables/references/configuration.md +587 -0
- package/.pi/skills/xberg-extracting-tables/references/nodejs-api.md +485 -0
- package/.pi/skills/xberg-extracting-tables/references/other-bindings.md +345 -0
- package/.pi/skills/xberg-extracting-tables/references/python-api.md +657 -0
- package/.pi/skills/xberg-extracting-tables/references/rust-api.md +412 -0
- package/.pi/skills/xberg-extracting-tables/references/supported-formats.md +252 -0
- package/{.opencode/skills/extracting-with-ocr → .pi/skills/xberg-extracting-with-ocr}/SKILL.md +5 -6
- package/.pi/skills/xberg-extracting-with-ocr/index.md +10 -0
- package/.pi/skills/xberg-extracting-with-ocr/references/advanced-features.md +628 -0
- package/.pi/skills/xberg-extracting-with-ocr/references/cli-reference.md +464 -0
- package/.pi/skills/xberg-extracting-with-ocr/references/configuration.md +587 -0
- package/.pi/skills/xberg-extracting-with-ocr/references/nodejs-api.md +485 -0
- package/.pi/skills/xberg-extracting-with-ocr/references/other-bindings.md +345 -0
- package/.pi/skills/xberg-extracting-with-ocr/references/python-api.md +657 -0
- package/.pi/skills/xberg-extracting-with-ocr/references/rust-api.md +412 -0
- package/.pi/skills/xberg-extracting-with-ocr/references/supported-formats.md +252 -0
- package/.pi/skills/{picking-a-format → xberg-picking-a-format}/SKILL.md +4 -4
- package/.pi/skills/xberg-picking-a-format/index.md +10 -0
- package/.pi/skills/xberg-picking-a-format/references/advanced-features.md +628 -0
- package/.pi/skills/xberg-picking-a-format/references/cli-reference.md +464 -0
- package/.pi/skills/xberg-picking-a-format/references/configuration.md +587 -0
- package/.pi/skills/xberg-picking-a-format/references/nodejs-api.md +485 -0
- package/.pi/skills/xberg-picking-a-format/references/other-bindings.md +345 -0
- package/.pi/skills/xberg-picking-a-format/references/python-api.md +657 -0
- package/.pi/skills/xberg-picking-a-format/references/rust-api.md +412 -0
- package/.pi/skills/xberg-picking-a-format/references/supported-formats.md +252 -0
- package/README.md +75 -9
- package/assets/icon.svg +16 -9
- package/assets/logo.png +0 -0
- package/package.json +136 -21
- package/scripts/mcp-launch.sh +6 -5
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: picking-a-format
|
|
3
2
|
description: Use when choosing an output format for extracted documents — plain text, markdown, djot, HTML, JSON, or DocTags. Maps consumer (LLM, parser, archive) to the right `--format` / `--content-format` pair.
|
|
3
|
+
name: xberg-picking-a-format
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<!--
|
|
7
7
|
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
8
|
-
Content-Hash: blake3:
|
|
9
|
-
Source-Hash: blake3:
|
|
8
|
+
Content-Hash: blake3:a2f204c34c2eb303812f0b7b0a9acf29c879edd1985b8cc3312555b347378e1e
|
|
9
|
+
Source-Hash: blake3:ab2bcd453f9c2ded67752f6a9800186d42b6921f4be16f6ab3a773254a9fba00
|
|
10
10
|
Schema-Version: v1
|
|
11
11
|
-->
|
|
12
12
|
|
|
@@ -62,7 +62,7 @@ Index a corpus into a RAG store with tables and headings preserved:
|
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
xberg batch docs/*.pdf --format json --content-format markdown \
|
|
65
|
-
| jq -c '.
|
|
65
|
+
| jq -c '.result | {content: .content, tables: .tables}'
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
Strip a file to bare text for a token-tight summarizer:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
3
|
+
Content-Hash: blake3:9068c82b2c04e3797f748f8d411beb9c7f4757910501844a8a8b40bac4331750
|
|
4
|
+
Source-Hash: blake3:ab2bcd453f9c2ded67752f6a9800186d42b6921f4be16f6ab3a773254a9fba00
|
|
5
|
+
Schema-Version: v1
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Concepts
|
|
9
|
+
|
|
10
|
+
- [Picking A Format](SKILL.md) - Use when choosing an output format for extracted documents — plain text, markdown, djot, HTML, JSON, or DocTags. Maps consumer (LLM, parser, archive) to the right `--format` / `--content-format` pair.
|
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
3
|
+
Content-Hash: blake3:353a7a05cbe84a2af1d053c8173d2154f418972a2bbfc2846dbb2c17a8a46197
|
|
4
|
+
Source-Hash: blake3:ab2bcd453f9c2ded67752f6a9800186d42b6921f4be16f6ab3a773254a9fba00
|
|
5
|
+
Schema-Version: v1
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Advanced Features Reference
|
|
9
|
+
|
|
10
|
+
Xberg provides advanced features for customization, semantic processing, and integration with external systems. All library examples use the 1.0 unified API: `extract(input, config)` returns an `ExtractionResult` envelope, and per-document data lives in `result.results[i]`.
|
|
11
|
+
|
|
12
|
+
## Plugin System
|
|
13
|
+
|
|
14
|
+
The plugin system extends Xberg's extraction pipeline with custom post-processors, validators, and OCR backends. Plugin callbacks receive a single `ExtractedDocument` (not the envelope), with `.content`, `.mime_type`/`.mimeType`, and `.metadata`.
|
|
15
|
+
|
|
16
|
+
### Custom Post-Processors
|
|
17
|
+
|
|
18
|
+
Post-processors enrich extraction results after document parsing. They run non-destructively — if a post-processor fails, the extraction still succeeds (errors are logged).
|
|
19
|
+
|
|
20
|
+
=== "Python"
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
import asyncio
|
|
24
|
+
from xberg import register_post_processor, ExtractInput, extract, ExtractionConfig, ExtractedDocument
|
|
25
|
+
|
|
26
|
+
class MetadataEnricher:
|
|
27
|
+
def name(self) -> str:
|
|
28
|
+
return "metadata_enricher"
|
|
29
|
+
|
|
30
|
+
def version(self) -> str:
|
|
31
|
+
return "1.1.0"
|
|
32
|
+
|
|
33
|
+
def process(self, result: ExtractedDocument, config: ExtractionConfig) -> ExtractedDocument:
|
|
34
|
+
result.metadata.additional["processed_by"] = "metadata_enricher"
|
|
35
|
+
result.metadata.additional["char_count"] = str(len(result.content))
|
|
36
|
+
return result
|
|
37
|
+
|
|
38
|
+
def processing_stage(self) -> str:
|
|
39
|
+
return "middle" # "early", "middle", or "late"
|
|
40
|
+
|
|
41
|
+
register_post_processor(MetadataEnricher())
|
|
42
|
+
|
|
43
|
+
async def main() -> None:
|
|
44
|
+
result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig())
|
|
45
|
+
print(len(result.results[0].content))
|
|
46
|
+
|
|
47
|
+
asyncio.run(main())
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
=== "TypeScript"
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { registerPostProcessor, extract } from "@xberg-io/xberg";
|
|
54
|
+
|
|
55
|
+
const enricher = {
|
|
56
|
+
name() {
|
|
57
|
+
return "metadata_enricher";
|
|
58
|
+
},
|
|
59
|
+
async process(result) {
|
|
60
|
+
result.metadata.processed_by = "metadata_enricher";
|
|
61
|
+
result.metadata.char_count = result.content.length;
|
|
62
|
+
return result;
|
|
63
|
+
},
|
|
64
|
+
processingStage() {
|
|
65
|
+
return "middle"; // "early" | "middle" | "late"
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
registerPostProcessor(enricher);
|
|
70
|
+
|
|
71
|
+
const output = await extract({ kind: "uri", uri: "document.pdf" });
|
|
72
|
+
console.log(output.results[0].content.length);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Custom Validators
|
|
76
|
+
|
|
77
|
+
Validators perform quality checks. Unlike post-processors, a validator that raises causes the whole extraction to fail.
|
|
78
|
+
|
|
79
|
+
=== "Python"
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from xberg import register_validator, ExtractedDocument, ExtractionConfig, ValidationError
|
|
83
|
+
|
|
84
|
+
class MinimumContentValidator:
|
|
85
|
+
def name(self) -> str:
|
|
86
|
+
return "min_content_validator"
|
|
87
|
+
|
|
88
|
+
def version(self) -> str:
|
|
89
|
+
return "1.1.0"
|
|
90
|
+
|
|
91
|
+
def validate(self, result: ExtractedDocument, config: ExtractionConfig) -> None:
|
|
92
|
+
if len(result.content) < 100:
|
|
93
|
+
raise ValidationError("Extracted content too short (< 100 chars)")
|
|
94
|
+
|
|
95
|
+
def priority(self) -> int:
|
|
96
|
+
return 100 # higher runs first
|
|
97
|
+
|
|
98
|
+
def should_validate(self, result: ExtractedDocument, config: ExtractionConfig) -> bool:
|
|
99
|
+
return "pdf" in result.mime_type.lower()
|
|
100
|
+
|
|
101
|
+
register_validator(MinimumContentValidator())
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
=== "TypeScript"
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { registerValidator } from "@xberg-io/xberg";
|
|
108
|
+
|
|
109
|
+
const validator = {
|
|
110
|
+
name() {
|
|
111
|
+
return "min_content_validator";
|
|
112
|
+
},
|
|
113
|
+
validate(result) {
|
|
114
|
+
if (result.content.length < 100) {
|
|
115
|
+
throw new Error("Extracted content too short (< 100 chars)");
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
priority() {
|
|
119
|
+
return 100;
|
|
120
|
+
},
|
|
121
|
+
shouldValidate(result) {
|
|
122
|
+
return result.mimeType.toLowerCase().includes("pdf");
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
registerValidator(validator);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Custom OCR Backends
|
|
130
|
+
|
|
131
|
+
Register a custom OCR engine to integrate proprietary or specialized OCR.
|
|
132
|
+
|
|
133
|
+
=== "Python"
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from xberg import ExtractedDocument, OcrBackendType, OcrConfig, register_ocr_backend
|
|
137
|
+
|
|
138
|
+
class CustomOcrBackend:
|
|
139
|
+
def name(self) -> str:
|
|
140
|
+
return "custom_ocr"
|
|
141
|
+
|
|
142
|
+
def supports_language(self, language: str) -> bool:
|
|
143
|
+
return language in {"eng", "deu", "fra", "spa"}
|
|
144
|
+
|
|
145
|
+
def backend_type(self) -> OcrBackendType:
|
|
146
|
+
return OcrBackendType.CUSTOM
|
|
147
|
+
|
|
148
|
+
# The second argument is the resolved OcrConfig, not a language string.
|
|
149
|
+
def process_image(self, image_bytes: bytes, config: OcrConfig) -> ExtractedDocument:
|
|
150
|
+
language = config.language[0] if config.language else "eng"
|
|
151
|
+
# text = my_ocr_engine.recognize(image_bytes, language)
|
|
152
|
+
return ExtractedDocument(content="Extracted text from image", mime_type="text/plain")
|
|
153
|
+
|
|
154
|
+
register_ocr_backend(CustomOcrBackend())
|
|
155
|
+
|
|
156
|
+
config = OcrConfig(backend="custom_ocr", language=["eng"])
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
=== "TypeScript"
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { registerOcrBackend, extract } from "@xberg-io/xberg";
|
|
163
|
+
|
|
164
|
+
const supportedLangs = ["eng", "deu", "fra", "spa"];
|
|
165
|
+
|
|
166
|
+
const backend = {
|
|
167
|
+
name() {
|
|
168
|
+
return "custom_ocr";
|
|
169
|
+
},
|
|
170
|
+
supportedLanguages() {
|
|
171
|
+
return supportedLangs;
|
|
172
|
+
},
|
|
173
|
+
supportsLanguage(lang) {
|
|
174
|
+
return supportedLangs.includes(lang);
|
|
175
|
+
},
|
|
176
|
+
backendType() {
|
|
177
|
+
return "Custom";
|
|
178
|
+
},
|
|
179
|
+
// The second argument is the resolved OcrConfig, not a language string.
|
|
180
|
+
async processImage(imageBytes, config) {
|
|
181
|
+
const language = config?.language?.[0] ?? "eng";
|
|
182
|
+
// const text = await myOcrEngine.recognize(imageBytes, language);
|
|
183
|
+
return {
|
|
184
|
+
content: "Extracted text from image",
|
|
185
|
+
mime_type: "text/plain",
|
|
186
|
+
tables: [],
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
registerOcrBackend(backend);
|
|
192
|
+
|
|
193
|
+
const config = { ocr: { backend: "custom_ocr", language: ["eng"] } };
|
|
194
|
+
const output = await extract({ kind: "uri", uri: "scanned.pdf" }, config);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Per-Input Configuration in Batch Operations
|
|
198
|
+
|
|
199
|
+
Override extraction settings for individual inputs by setting `config` on each `ExtractInput` to a `FileExtractionConfig`. Inputs without an override use the batch-level `ExtractionConfig`. This suits mixed-format batches where different documents need different OCR, output, or processing.
|
|
200
|
+
|
|
201
|
+
=== "Python"
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
import asyncio
|
|
205
|
+
from xberg import ExtractInput, extract_batch, ExtractionConfig, FileExtractionConfig, OcrConfig
|
|
206
|
+
|
|
207
|
+
async def main() -> None:
|
|
208
|
+
report = ExtractInput(uri="report.pdf")
|
|
209
|
+
scan = ExtractInput(
|
|
210
|
+
kind="uri",
|
|
211
|
+
uri="scan.tiff",
|
|
212
|
+
config=FileExtractionConfig(
|
|
213
|
+
force_ocr=True,
|
|
214
|
+
ocr=OcrConfig(backend="tesseract", language=["deu"]),
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
output = await extract_batch([report, scan], ExtractionConfig(output_format="markdown"))
|
|
219
|
+
for doc in output.results:
|
|
220
|
+
print(len(doc.content))
|
|
221
|
+
|
|
222
|
+
asyncio.run(main())
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
=== "TypeScript"
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { extractBatch } from "@xberg-io/xberg";
|
|
229
|
+
|
|
230
|
+
const output = await extractBatch(
|
|
231
|
+
[
|
|
232
|
+
{ kind: "uri", uri: "report.pdf" },
|
|
233
|
+
{
|
|
234
|
+
kind: "uri",
|
|
235
|
+
uri: "scan.tiff",
|
|
236
|
+
config: { forceOcr: true, ocr: { backend: "tesseract", language: ["deu"] } },
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
{ outputFormat: "markdown" },
|
|
240
|
+
);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
All `ExtractionConfig` fields except batch-level concerns (`max_concurrent_extractions`, `use_cache`, `acceleration`, `security_limits`) can be overridden per input. Unset (`None`/omitted) fields inherit the batch default.
|
|
244
|
+
|
|
245
|
+
## Embeddings
|
|
246
|
+
|
|
247
|
+
Generate vector embeddings for text chunks using ONNX models. Embeddings enable semantic search, clustering, and similarity over extracted content. Available presets: `fast` (384 dims), `balanced` (768 dims), `quality` (1024 dims), `multilingual` (768 dims).
|
|
248
|
+
|
|
249
|
+
=== "Python"
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from xberg import (
|
|
253
|
+
ExtractInput, extract, ExtractionConfig,
|
|
254
|
+
ChunkingConfig, EmbeddingConfig, EmbeddingModelType,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Preset model (recommended)
|
|
258
|
+
config = ExtractionConfig(
|
|
259
|
+
chunking=ChunkingConfig(
|
|
260
|
+
max_characters=512,
|
|
261
|
+
overlap=100,
|
|
262
|
+
embedding=EmbeddingConfig(
|
|
263
|
+
model=EmbeddingModelType.preset("balanced"),
|
|
264
|
+
normalize=True,
|
|
265
|
+
batch_size=32,
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Custom ONNX model from HuggingFace
|
|
271
|
+
config = ExtractionConfig(
|
|
272
|
+
chunking=ChunkingConfig(
|
|
273
|
+
embedding=EmbeddingConfig(
|
|
274
|
+
model=EmbeddingModelType.custom(
|
|
275
|
+
model_id="sentence-transformers/all-MiniLM-L6-v2",
|
|
276
|
+
dimensions=384,
|
|
277
|
+
),
|
|
278
|
+
)
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
result = await extract(ExtractInput(uri="document.pdf"), config)
|
|
283
|
+
for chunk in result.results[0].chunks or []:
|
|
284
|
+
print(f"{chunk.content[:50]}... -> {len(chunk.embedding) if chunk.embedding else 0} dims")
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
=== "TypeScript"
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
import { extract } from "@xberg-io/xberg";
|
|
291
|
+
|
|
292
|
+
const config = {
|
|
293
|
+
chunking: {
|
|
294
|
+
maxCharacters: 512,
|
|
295
|
+
overlap: 100,
|
|
296
|
+
embedding: { model: { type: "preset", name: "balanced" } },
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const output = await extract({ kind: "uri", uri: "document.pdf" }, config);
|
|
301
|
+
for (const chunk of output.results[0].chunks ?? []) {
|
|
302
|
+
console.log(`Embedding dims: ${chunk.embedding?.length ?? 0}`);
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Keyword Extraction
|
|
307
|
+
|
|
308
|
+
Extract important keywords using YAKE or RAKE.
|
|
309
|
+
|
|
310
|
+
=== "Python"
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from xberg import (
|
|
314
|
+
ExtractInput, extract, ExtractionConfig,
|
|
315
|
+
KeywordConfig, KeywordAlgorithm, YakeParams, RakeParams,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
# YAKE
|
|
319
|
+
config = ExtractionConfig(
|
|
320
|
+
keywords=KeywordConfig(
|
|
321
|
+
algorithm=KeywordAlgorithm.YAKE,
|
|
322
|
+
max_keywords=15,
|
|
323
|
+
min_score=0.1,
|
|
324
|
+
language="en",
|
|
325
|
+
yake_params=YakeParams(window_size=1),
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
# RAKE
|
|
330
|
+
config = ExtractionConfig(
|
|
331
|
+
keywords=KeywordConfig(
|
|
332
|
+
algorithm=KeywordAlgorithm.RAKE,
|
|
333
|
+
max_keywords=10,
|
|
334
|
+
min_score=5.0,
|
|
335
|
+
language="en",
|
|
336
|
+
rake_params=RakeParams(min_word_length=1, max_words_per_phrase=3),
|
|
337
|
+
)
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
result = await extract(ExtractInput(uri="document.pdf"), config)
|
|
341
|
+
for keyword in result.results[0].extracted_keywords or []:
|
|
342
|
+
print(f"{keyword.text}: {keyword.score} ({keyword.algorithm})")
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
=== "TypeScript"
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { extract } from "@xberg-io/xberg";
|
|
349
|
+
|
|
350
|
+
const config = {
|
|
351
|
+
keywords: {
|
|
352
|
+
algorithm: "yake",
|
|
353
|
+
maxKeywords: 15,
|
|
354
|
+
minScore: 0.1,
|
|
355
|
+
language: "en",
|
|
356
|
+
yakeParams: { windowSize: 1 },
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const output = await extract({ kind: "uri", uri: "document.pdf" }, config);
|
|
361
|
+
for (const keyword of output.results[0].extractedKeywords ?? []) {
|
|
362
|
+
console.log(`${keyword.text}: ${keyword.score} (${keyword.algorithm})`);
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Language Detection
|
|
367
|
+
|
|
368
|
+
Detect document language(s) using ISO 639-1 codes.
|
|
369
|
+
|
|
370
|
+
=== "Python"
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
from xberg import ExtractInput, extract, ExtractionConfig, LanguageDetectionConfig
|
|
374
|
+
|
|
375
|
+
config = ExtractionConfig(
|
|
376
|
+
language_detection=LanguageDetectionConfig(
|
|
377
|
+
enabled=True,
|
|
378
|
+
min_confidence=0.8,
|
|
379
|
+
detect_multiple=False,
|
|
380
|
+
)
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
result = await extract(ExtractInput(uri="multilingual.pdf"), config)
|
|
384
|
+
for lang in result.results[0].detected_languages or []:
|
|
385
|
+
print(f"Detected language: {lang}") # e.g. "en", "de", "fr"
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
=== "TypeScript"
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import { extract } from "@xberg-io/xberg";
|
|
392
|
+
|
|
393
|
+
const config = {
|
|
394
|
+
languageDetection: { enabled: true, minConfidence: 0.8, detectMultiple: false },
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const output = await extract({ kind: "uri", uri: "multilingual.pdf" }, config);
|
|
398
|
+
for (const lang of output.results[0].detectedLanguages ?? []) {
|
|
399
|
+
console.log(`Detected language: ${lang}`);
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Token Reduction
|
|
404
|
+
|
|
405
|
+
Reduce token count in extracted content to lower LLM costs. Higher modes are more aggressive but may discard more information.
|
|
406
|
+
|
|
407
|
+
=== "Python"
|
|
408
|
+
|
|
409
|
+
```python
|
|
410
|
+
from xberg import ExtractInput, extract, ExtractionConfig, TokenReductionOptions
|
|
411
|
+
|
|
412
|
+
config = ExtractionConfig(
|
|
413
|
+
token_reduction=TokenReductionOptions(
|
|
414
|
+
mode="moderate", # "off", "light", "moderate", "aggressive", "maximum"
|
|
415
|
+
preserve_important_words=True,
|
|
416
|
+
)
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
result = await extract(ExtractInput(uri="document.pdf"), config)
|
|
420
|
+
print(f"Reduced content length: {len(result.results[0].content)}")
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
=== "TypeScript"
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { extract } from "@xberg-io/xberg";
|
|
427
|
+
|
|
428
|
+
const config = { tokenReduction: { mode: "moderate", preserveImportantWords: true } };
|
|
429
|
+
const output = await extract({ kind: "uri", uri: "document.pdf" }, config);
|
|
430
|
+
console.log(`Reduced content length: ${output.results[0].content.length}`);
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Modes:** `off` (default), `light`, `moderate`, `aggressive`, `maximum`.
|
|
434
|
+
|
|
435
|
+
## Page Extraction
|
|
436
|
+
|
|
437
|
+
Track per-page content separately.
|
|
438
|
+
|
|
439
|
+
=== "Python"
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
from xberg import ExtractInput, extract, ExtractionConfig, PageConfig
|
|
443
|
+
|
|
444
|
+
config = ExtractionConfig(
|
|
445
|
+
pages=PageConfig(
|
|
446
|
+
extract_pages=True,
|
|
447
|
+
insert_page_markers=True,
|
|
448
|
+
marker_format="\n\n<!-- PAGE {page_num} -->\n\n",
|
|
449
|
+
)
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
result = await extract(ExtractInput(uri="multi_page.pdf"), config)
|
|
453
|
+
for page in result.results[0].pages or []:
|
|
454
|
+
print(f"Page {page.page_number}: {len(page.content)} chars, {len(page.tables)} tables")
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
=== "TypeScript"
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
import { extract } from "@xberg-io/xberg";
|
|
461
|
+
|
|
462
|
+
const config = {
|
|
463
|
+
pages: {
|
|
464
|
+
extractPages: true,
|
|
465
|
+
insertPageMarkers: true,
|
|
466
|
+
markerFormat: "\n\n<!-- PAGE {page_num} -->\n\n",
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const output = await extract({ kind: "uri", uri: "multi_page.pdf" }, config);
|
|
471
|
+
for (const page of output.results[0].pages ?? []) {
|
|
472
|
+
console.log(`Page ${page.pageNumber}: ${page.content.length} chars`);
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Element-Based Output
|
|
477
|
+
|
|
478
|
+
Extract semantic elements (Unstructured-compatible) instead of unified content.
|
|
479
|
+
|
|
480
|
+
=== "Python"
|
|
481
|
+
|
|
482
|
+
```python
|
|
483
|
+
from xberg import ExtractInput, extract, ExtractionConfig
|
|
484
|
+
|
|
485
|
+
config = ExtractionConfig(result_format="element_based")
|
|
486
|
+
result = await extract(ExtractInput(uri="document.pdf"), config)
|
|
487
|
+
|
|
488
|
+
for element in result.results[0].elements or []:
|
|
489
|
+
print(f"Type: {element.element_type}") # title, heading, narrative_text, ...
|
|
490
|
+
print(f"Text: {element.text}")
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
=== "TypeScript"
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
import { extract } from "@xberg-io/xberg";
|
|
497
|
+
|
|
498
|
+
const config = { resultFormat: "element_based" };
|
|
499
|
+
const output = await extract({ kind: "uri", uri: "document.pdf" }, config);
|
|
500
|
+
|
|
501
|
+
for (const element of output.results[0].elements ?? []) {
|
|
502
|
+
console.log(`Type: ${element.elementType}`);
|
|
503
|
+
console.log(`Text: ${element.text}`);
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
**Element types:** `title`, `heading`, `narrative_text`, `list_item`, `table`, `image`, `page_break`, `code_block`, `block_quote`, `footer`, `header`.
|
|
508
|
+
|
|
509
|
+
## Djot Content
|
|
510
|
+
|
|
511
|
+
Output extracted content in Djot markup.
|
|
512
|
+
|
|
513
|
+
=== "Python"
|
|
514
|
+
|
|
515
|
+
```python
|
|
516
|
+
from xberg import ExtractInput, extract, ExtractionConfig
|
|
517
|
+
|
|
518
|
+
config = ExtractionConfig(output_format="djot")
|
|
519
|
+
result = await extract(ExtractInput(uri="document.pdf"), config)
|
|
520
|
+
print(result.results[0].content) # Djot-formatted content
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
=== "TypeScript"
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
import { extract } from "@xberg-io/xberg";
|
|
527
|
+
|
|
528
|
+
const config = { outputFormat: "djot" };
|
|
529
|
+
const output = await extract({ kind: "uri", uri: "document.pdf" }, config);
|
|
530
|
+
console.log(output.results[0].content);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## Security Limits
|
|
534
|
+
|
|
535
|
+
Cap resource consumption for untrusted input (especially archives).
|
|
536
|
+
|
|
537
|
+
=== "Python"
|
|
538
|
+
|
|
539
|
+
```python
|
|
540
|
+
from xberg import ExtractInput, extract, ExtractionConfig, SecurityLimits
|
|
541
|
+
|
|
542
|
+
config = ExtractionConfig(
|
|
543
|
+
security_limits=SecurityLimits(
|
|
544
|
+
max_archive_size=524_288_000, # 500 MB uncompressed
|
|
545
|
+
max_compression_ratio=100, # flag potential zip bombs
|
|
546
|
+
max_files_in_archive=10_000,
|
|
547
|
+
max_nesting_depth=1024,
|
|
548
|
+
max_content_size=104_857_600, # 100 MB
|
|
549
|
+
max_table_cells=100_000,
|
|
550
|
+
)
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
result = await extract(ExtractInput(uri="archive.zip"), config)
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
=== "TypeScript"
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
import { extract } from "@xberg-io/xberg";
|
|
560
|
+
|
|
561
|
+
const config = {
|
|
562
|
+
securityLimits: {
|
|
563
|
+
maxArchiveSize: 524_288_000,
|
|
564
|
+
maxCompressionRatio: 100,
|
|
565
|
+
maxFilesInArchive: 10_000,
|
|
566
|
+
maxNestingDepth: 1024,
|
|
567
|
+
maxContentSize: 104_857_600,
|
|
568
|
+
maxTableCells: 100_000,
|
|
569
|
+
},
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
const output = await extract({ kind: "uri", uri: "archive.zip" }, config);
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## Caching
|
|
576
|
+
|
|
577
|
+
Extraction results are cached by default (content-hash keyed) to speed up repeated extractions of identical documents.
|
|
578
|
+
|
|
579
|
+
=== "Python"
|
|
580
|
+
|
|
581
|
+
```python
|
|
582
|
+
from xberg import ExtractInput, extract, ExtractionConfig
|
|
583
|
+
|
|
584
|
+
# Caching on (default)
|
|
585
|
+
result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig(use_cache=True))
|
|
586
|
+
|
|
587
|
+
# Disable caching
|
|
588
|
+
result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig(use_cache=False))
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
=== "TypeScript"
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
import { extract } from "@xberg-io/xberg";
|
|
595
|
+
|
|
596
|
+
const output = await extract({ kind: "uri", uri: "document.pdf" }, { useCache: false });
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**CLI cache management:**
|
|
600
|
+
|
|
601
|
+
xberg cache stats
|
|
602
|
+
xberg cache clear
|
|
603
|
+
|
|
604
|
+
## API Server
|
|
605
|
+
|
|
606
|
+
Run Xberg as an HTTP API server.
|
|
607
|
+
|
|
608
|
+
# Default port 8000
|
|
609
|
+
xberg serve
|
|
610
|
+
|
|
611
|
+
# Custom host and port
|
|
612
|
+
xberg serve --host 0.0.0.0 --port 9000
|
|
613
|
+
|
|
614
|
+
**Example:**
|
|
615
|
+
|
|
616
|
+
curl -X POST -F "files=@document.pdf" http://localhost:8000/extract
|
|
617
|
+
|
|
618
|
+
## MCP Server
|
|
619
|
+
|
|
620
|
+
Run Xberg as a Model Context Protocol server for AI integrations.
|
|
621
|
+
|
|
622
|
+
# stdio transport
|
|
623
|
+
xberg mcp
|
|
624
|
+
|
|
625
|
+
# HTTP transport
|
|
626
|
+
xberg mcp --transport http --host 127.0.0.1 --port 8001
|
|
627
|
+
|
|
628
|
+
The MCP server exposes extraction tools (extract, extract_batch, detect_mime_type, cache management, and more) to AI models.
|