@xberg-io/langchain-xberg 1.0.14 → 1.1.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/README.md +3 -3
- package/dist/index.cjs +2 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -6
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
A [LangChain.js](https://js.langchain.com) document loader for [Xberg](https://github.com/xberg-io/xberg).
|
|
13
13
|
Point it at a file, a directory, or raw bytes and it returns LangChain `Document`s with the extracted
|
|
14
|
-
text, tables, and rich metadata from
|
|
14
|
+
text, tables, and rich metadata from 107 document formats — with optional OCR for scans and images.
|
|
15
15
|
|
|
16
16
|
Extraction runs locally in-process through the `@xberg-io/xberg` native binding. No API key, no cloud
|
|
17
17
|
call, no data leaves your machine.
|
|
@@ -69,13 +69,13 @@ const pages = await loader.load(); // metadata.page is 0-indexed
|
|
|
69
69
|
|
|
70
70
|
## Supported formats
|
|
71
71
|
|
|
72
|
-
Xberg extracts from
|
|
72
|
+
Xberg extracts from 107 formats including PDF, DOCX, PPTX, XLSX, HTML, EPUB, images, and more. See the
|
|
73
73
|
[Xberg documentation](https://docs.xberg.io) for the full list and the extraction configuration
|
|
74
74
|
reference.
|
|
75
75
|
|
|
76
76
|
## Part of Xberg.io
|
|
77
77
|
|
|
78
|
-
- [Xberg](https://github.com/xberg-io/xberg) — document intelligence: text, tables, metadata from
|
|
78
|
+
- [Xberg](https://github.com/xberg-io/xberg) — document intelligence: text, tables, metadata from 107 formats with optional OCR.
|
|
79
79
|
- [Xberg Enterprise](https://github.com/xberg-io/xberg-enterprise) — managed extraction API with SDKs, dashboards, and observability.
|
|
80
80
|
|
|
81
81
|
## License
|
package/dist/index.cjs
CHANGED
|
@@ -162,8 +162,7 @@ function pagesToDocuments(document, source) {
|
|
|
162
162
|
const baseMetadata = buildMetadata(document, source);
|
|
163
163
|
const documents = [];
|
|
164
164
|
for (const page of document.pages ?? []) {
|
|
165
|
-
const metadata = { ...baseMetadata };
|
|
166
|
-
metadata.page = page.pageNumber - 1;
|
|
165
|
+
const metadata = { ...baseMetadata, page: page.pageNumber - 1 };
|
|
167
166
|
if (page.isBlank != null) {
|
|
168
167
|
metadata.is_blank = page.isBlank;
|
|
169
168
|
}
|
|
@@ -233,7 +232,7 @@ var XbergLoader = class extends import_base.BaseDocumentLoader {
|
|
|
233
232
|
result = batch ? await (0, import_xberg.extractBatch)(inputs, this.config ?? null) : await (0, import_xberg.extract)(inputs[0], this.config ?? null);
|
|
234
233
|
} catch (error) {
|
|
235
234
|
const source = sources[0] ?? "input";
|
|
236
|
-
throw new Error(`Failed to extract '${source}': ${errorMessage(error)}
|
|
235
|
+
throw new Error(`Failed to extract '${source}': ${errorMessage(error)}`, { cause: error });
|
|
237
236
|
}
|
|
238
237
|
return resultToDocuments(result, sources, {
|
|
239
238
|
chunking: isChunkingEnabled(this.config),
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/loader.ts","../src/mapping.ts"],"sourcesContent":["export { XbergLoader, type XbergLoaderOptions } from \"./loader\";\n","import { stat } from \"node:fs/promises\";\nimport { BaseDocumentLoader } from \"@langchain/core/document_loaders/base\";\nimport type { Document } from \"@langchain/core/documents\";\nimport { extract, extractBatch, ExtractInputKind } from \"@xberg-io/xberg\";\nimport type { ExtractInput, ExtractionConfig } from \"@xberg-io/xberg\";\nimport fastGlob from \"fast-glob\";\nimport { isChunkingEnabled, isPerPageEnabled, resultToDocuments, type ResultEnvelope } from \"./mapping\";\n\nconst DEFAULT_GLOB = \"**/*\";\n\nexport interface XbergLoaderOptions {\n /** File path, list of file paths, or directory path to load. */\n filePath?: string | string[];\n /** Raw bytes to extract from. Mutually exclusive with `filePath`. */\n data?: Uint8Array;\n /** MIME type hint. Required when using `data`, optional for `filePath`. */\n mimeType?: string;\n /** Glob pattern for directory mode. Defaults to matching all files. */\n glob?: string;\n /** Xberg extraction configuration controlling output format, OCR, pages, chunking, etc. */\n config?: ExtractionConfig;\n}\n\n/**\n * Load documents using Xberg, supporting 101 file formats with true async extraction.\n *\n * By default each source becomes one Document. Enable `chunking` on the\n * `ExtractionConfig` to emit one Document per chunk, or `pages` for one Document\n * per page. Multiple paths (a list or a directory glob) are extracted with a single\n * `extractBatch` call so concurrency happens Rust-side.\n */\nexport class XbergLoader extends BaseDocumentLoader {\n private readonly filePath?: string | string[];\n private readonly data?: Uint8Array;\n private readonly mimeType?: string;\n private readonly glob?: string;\n private readonly config?: ExtractionConfig;\n\n constructor(options: XbergLoaderOptions) {\n super();\n const { filePath, data, mimeType, glob, config } = options;\n if (filePath === undefined && data === undefined) {\n throw new Error(\"Either 'filePath' or 'data' must be provided.\");\n }\n if (filePath !== undefined && data !== undefined) {\n throw new Error(\"Cannot specify both 'filePath' and 'data'. Use one or the other.\");\n }\n if (data !== undefined && mimeType === undefined) {\n throw new Error(\"'mimeType' is required when using 'data'.\");\n }\n\n this.filePath = filePath;\n this.data = data;\n this.mimeType = mimeType;\n this.glob = glob;\n this.config = config;\n }\n\n async load(): Promise<Document[]> {\n const { inputs, sources, batch } = await this.buildInputs();\n if (inputs.length === 0) {\n return [];\n }\n\n let result;\n try {\n result = batch ? await extractBatch(inputs, this.config ?? null) : await extract(inputs[0], this.config ?? null);\n } catch (error) {\n const source = sources[0] ?? \"input\";\n throw new Error(`Failed to extract '${source}': ${errorMessage(error)}`);\n }\n\n return resultToDocuments(result as unknown as ResultEnvelope, sources, {\n chunking: isChunkingEnabled(this.config),\n perPage: isPerPageEnabled(this.config),\n });\n }\n\n private async buildInputs(): Promise<{ inputs: ExtractInput[]; sources: string[]; batch: boolean }> {\n if (this.data !== undefined) {\n const source = `bytes://${this.mimeType}`;\n const input: ExtractInput = { kind: ExtractInputKind.Bytes, bytes: this.data, mimeType: this.mimeType };\n return { inputs: [input], sources: [source], batch: false };\n }\n\n const { paths, batch } = await this.resolvePaths();\n const inputs: ExtractInput[] = paths.map((path) => ({\n kind: ExtractInputKind.Uri,\n uri: path,\n mimeType: this.mimeType,\n }));\n return { inputs, sources: paths, batch };\n }\n\n private async resolvePaths(): Promise<{ paths: string[]; batch: boolean }> {\n const filePath = this.filePath;\n if (Array.isArray(filePath)) {\n return { paths: filePath, batch: true };\n }\n if (typeof filePath === \"string\") {\n if (await isDirectory(filePath)) {\n const pattern = this.glob ?? DEFAULT_GLOB;\n const matches = await fastGlob(pattern, { cwd: filePath, onlyFiles: true, absolute: true });\n return { paths: matches.sort(), batch: true };\n }\n return { paths: [filePath], batch: false };\n }\n return { paths: [], batch: false };\n }\n}\n\nasync function isDirectory(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { Document } from \"@langchain/core/documents\";\nimport type { DocumentCounts, ExtractionConfig, Keyword, Metadata, ProcessingWarning, Table } from \"@xberg-io/xberg\";\n\n// Metadata fields (from Xberg Metadata) that carry JSON-friendly scalar/list values,\n// mapped from the binding's camelCase keys to the snake_case output keys used by the\n// Python adapter. Opaque nested fields (pages, format, imagePreprocessing, jsonSchema,\n// error) are skipped because they are native objects, not plain data. ~keep\nconst METADATA_FIELDS: ReadonlyArray<readonly [string, keyof Metadata]> = [\n [\"title\", \"title\"],\n [\"subject\", \"subject\"],\n [\"authors\", \"authors\"],\n [\"keywords\", \"keywords\"],\n [\"language\", \"language\"],\n [\"created_at\", \"createdAt\"],\n [\"modified_at\", \"modifiedAt\"],\n [\"created_by\", \"createdBy\"],\n [\"modified_by\", \"modifiedBy\"],\n [\"category\", \"category\"],\n [\"tags\", \"tags\"],\n [\"document_version\", \"documentVersion\"],\n [\"abstract_text\", \"abstractText\"],\n [\"output_format\", \"outputFormat\"],\n [\"ocr_used\", \"ocrUsed\"],\n [\"extraction_duration_ms\", \"extractionDurationMs\"],\n];\n\nconst CONTENT_SEPARATOR = \"\\n\\n\";\n\nexport interface ChunkMetadataShape {\n chunkIndex: number;\n totalChunks: number;\n headingPath?: string[];\n tokenCount?: number;\n firstPage?: number;\n lastPage?: number;\n}\n\nexport interface ChunkShape {\n content: string;\n chunkType: unknown;\n metadata: ChunkMetadataShape;\n}\n\nexport interface PageShape {\n pageNumber: number;\n content: string;\n tables?: Table[];\n isBlank?: boolean;\n}\n\n// Structural view of a binding ExtractedDocument. The binding types its own fields\n// through unexported `Js*` aliases; this interface uses the clean exported types plus\n// local chunk/page shapes so the mapper is fully typed and testable without the addon. ~keep\nexport interface ExtractedDoc {\n content?: string;\n mimeType?: string;\n metadata?: Metadata;\n tables?: Table[];\n counts?: DocumentCounts;\n detectedLanguages?: string[];\n chunks?: ChunkShape[];\n pages?: PageShape[];\n extractedKeywords?: Keyword[];\n qualityScore?: number;\n processingWarnings?: ProcessingWarning[];\n}\n\nexport interface ResultEnvelope {\n errors?: Array<{ source: string; message: string }>;\n results?: ExtractedDoc[];\n}\n\nexport interface SplitOptions {\n chunking: boolean;\n perPage: boolean;\n}\n\nexport function isChunkingEnabled(config?: ExtractionConfig | null): boolean {\n return config?.chunking != null;\n}\n\nexport function isPerPageEnabled(config?: ExtractionConfig | null): boolean {\n return Boolean(config?.pages?.extractPages);\n}\n\nexport function flattenMetadata(metadata?: Metadata): Record<string, unknown> {\n const flat: Record<string, unknown> = {};\n if (!metadata) {\n return flat;\n }\n\n for (const [snakeKey, camelKey] of METADATA_FIELDS) {\n const value = metadata[camelKey];\n if (value != null) {\n flat[snakeKey] = value;\n }\n }\n\n const additional = metadata.additional;\n if (additional && Object.keys(additional).length > 0) {\n flat.additional = { ...additional };\n }\n\n return flat;\n}\n\nexport function assembleContent(content: string | undefined, tables: Table[] | undefined): string {\n const text = content ?? \"\";\n if (!tables || tables.length === 0) {\n return text;\n }\n const parts = tables.map((table) => table.markdown ?? \"\").filter((markdown) => markdown.length > 0);\n if (parts.length === 0) {\n return text;\n }\n return [text, ...parts].join(CONTENT_SEPARATOR);\n}\n\nexport function buildMetadata(document: ExtractedDoc, source: string): Record<string, unknown> {\n const metadata = flattenMetadata(document.metadata);\n\n metadata.mime_type = document.mimeType;\n if (document.qualityScore != null) {\n metadata.quality_score = document.qualityScore;\n }\n if (document.detectedLanguages && document.detectedLanguages.length > 0) {\n metadata.detected_languages = document.detectedLanguages;\n }\n if (document.counts != null) {\n metadata.page_count = document.counts.pages;\n }\n\n if (document.extractedKeywords && document.extractedKeywords.length > 0) {\n metadata.extracted_keywords = document.extractedKeywords.map((keyword) => ({\n text: keyword.text,\n score: keyword.score,\n algorithm: String(keyword.algorithm),\n }));\n }\n\n const tables = document.tables ?? [];\n metadata.table_count = tables.length;\n if (tables.length > 0) {\n metadata.tables = tables.map((table) => ({\n cells: table.cells,\n markdown: table.markdown,\n page_number: table.pageNumber,\n }));\n }\n\n if (document.processingWarnings && document.processingWarnings.length > 0) {\n metadata.processing_warnings = document.processingWarnings.map((warning) => ({\n source: warning.source,\n message: warning.message,\n }));\n }\n\n metadata.source = source;\n return metadata;\n}\n\nfunction chunksToDocuments(document: ExtractedDoc, source: string): Document[] {\n const baseMetadata = buildMetadata(document, source);\n const documents: Document[] = [];\n\n for (const chunk of document.chunks ?? []) {\n const metadata: Record<string, unknown> = { ...baseMetadata };\n const chunkMetadata = chunk.metadata;\n metadata.chunk_index = chunkMetadata.chunkIndex;\n metadata.total_chunks = chunkMetadata.totalChunks;\n metadata.chunk_type = String(chunk.chunkType);\n if (chunkMetadata.headingPath && chunkMetadata.headingPath.length > 0) {\n metadata.heading_path = [...chunkMetadata.headingPath];\n }\n if (chunkMetadata.tokenCount != null) {\n metadata.token_count = chunkMetadata.tokenCount;\n }\n if (chunkMetadata.firstPage != null) {\n // Xberg uses 1-indexed pages; LangChain convention is 0-indexed. ~keep\n metadata.page = chunkMetadata.firstPage - 1;\n metadata.first_page = chunkMetadata.firstPage;\n }\n if (chunkMetadata.lastPage != null) {\n metadata.last_page = chunkMetadata.lastPage;\n }\n documents.push(new Document({ pageContent: chunk.content, metadata }));\n }\n\n return documents;\n}\n\nfunction pagesToDocuments(document: ExtractedDoc, source: string): Document[] {\n const baseMetadata = buildMetadata(document, source);\n const documents: Document[] = [];\n\n for (const page of document.pages ?? []) {\n const metadata: Record<string, unknown> = { ...baseMetadata };\n // Xberg uses 1-indexed pages; LangChain convention is 0-indexed. ~keep\n metadata.page = page.pageNumber - 1;\n if (page.isBlank != null) {\n metadata.is_blank = page.isBlank;\n }\n const pageContent = assembleContent(page.content, page.tables);\n documents.push(new Document({ pageContent, metadata }));\n }\n\n return documents;\n}\n\nexport function documentToDocuments(document: ExtractedDoc, source: string, options: SplitOptions): Document[] {\n if (options.chunking && document.chunks && document.chunks.length > 0) {\n return chunksToDocuments(document, source);\n }\n if (options.perPage && document.pages && document.pages.length > 0) {\n return pagesToDocuments(document, source);\n }\n const metadata = buildMetadata(document, source);\n const pageContent = assembleContent(document.content, document.tables);\n return [new Document({ pageContent, metadata })];\n}\n\nexport function resultToDocuments(result: ResultEnvelope, sources: string[], options: SplitOptions): Document[] {\n if (result.errors && result.errors.length > 0) {\n const error = result.errors[0];\n throw new Error(`Failed to extract '${error.source}': ${error.message}`);\n }\n\n const documents: Document[] = [];\n const results = result.results ?? [];\n results.forEach((document, index) => {\n const source = sources[index] ?? (sources.length > 0 ? sources[sources.length - 1] : \"\");\n documents.push(...documentToDocuments(document, source, options));\n });\n return documents;\n}\n"],"mappings":"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","names":["fastGlob"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/loader.ts","../src/mapping.ts"],"sourcesContent":["export { XbergLoader, type XbergLoaderOptions } from \"./loader\";\n","import { stat } from \"node:fs/promises\";\nimport { BaseDocumentLoader } from \"@langchain/core/document_loaders/base\";\nimport type { Document } from \"@langchain/core/documents\";\nimport { extract, extractBatch, ExtractInputKind } from \"@xberg-io/xberg\";\nimport type { ExtractInput, ExtractionConfig } from \"@xberg-io/xberg\";\nimport fastGlob from \"fast-glob\";\nimport { isChunkingEnabled, isPerPageEnabled, resultToDocuments, type ResultEnvelope } from \"./mapping\";\n\nconst DEFAULT_GLOB = \"**/*\";\n\nexport interface XbergLoaderOptions {\n /** File path, list of file paths, or directory path to load. */\n filePath?: string | string[];\n /** Raw bytes to extract from. Mutually exclusive with `filePath`. */\n data?: Uint8Array;\n /** MIME type hint. Required when using `data`, optional for `filePath`. */\n mimeType?: string;\n /** Glob pattern for directory mode. Defaults to matching all files. */\n glob?: string;\n /** Xberg extraction configuration controlling output format, OCR, pages, chunking, etc. */\n config?: ExtractionConfig;\n}\n\n/**\n * Load documents using Xberg, supporting 107 file formats with true async extraction.\n *\n * By default each source becomes one Document. Enable `chunking` on the\n * `ExtractionConfig` to emit one Document per chunk, or `pages` for one Document\n * per page. Multiple paths (a list or a directory glob) are extracted with a single\n * `extractBatch` call so concurrency happens Rust-side.\n */\nexport class XbergLoader extends BaseDocumentLoader {\n private readonly filePath?: string | string[];\n private readonly data?: Uint8Array;\n private readonly mimeType?: string;\n private readonly glob?: string;\n private readonly config?: ExtractionConfig;\n\n constructor(options: XbergLoaderOptions) {\n super();\n const { filePath, data, mimeType, glob, config } = options;\n if (filePath === undefined && data === undefined) {\n throw new Error(\"Either 'filePath' or 'data' must be provided.\");\n }\n if (filePath !== undefined && data !== undefined) {\n throw new Error(\"Cannot specify both 'filePath' and 'data'. Use one or the other.\");\n }\n if (data !== undefined && mimeType === undefined) {\n throw new Error(\"'mimeType' is required when using 'data'.\");\n }\n\n this.filePath = filePath;\n this.data = data;\n this.mimeType = mimeType;\n this.glob = glob;\n this.config = config;\n }\n\n async load(): Promise<Document[]> {\n const { inputs, sources, batch } = await this.buildInputs();\n if (inputs.length === 0) {\n return [];\n }\n\n let result;\n try {\n result = batch ? await extractBatch(inputs, this.config ?? null) : await extract(inputs[0], this.config ?? null);\n } catch (error) {\n const source = sources[0] ?? \"input\";\n throw new Error(`Failed to extract '${source}': ${errorMessage(error)}`, { cause: error });\n }\n\n return resultToDocuments(result as unknown as ResultEnvelope, sources, {\n chunking: isChunkingEnabled(this.config),\n perPage: isPerPageEnabled(this.config),\n });\n }\n\n private async buildInputs(): Promise<{ inputs: ExtractInput[]; sources: string[]; batch: boolean }> {\n if (this.data !== undefined) {\n const source = `bytes://${this.mimeType}`;\n const input: ExtractInput = { kind: ExtractInputKind.Bytes, bytes: this.data, mimeType: this.mimeType };\n return { inputs: [input], sources: [source], batch: false };\n }\n\n const { paths, batch } = await this.resolvePaths();\n const inputs: ExtractInput[] = paths.map((path) => ({\n kind: ExtractInputKind.Uri,\n uri: path,\n mimeType: this.mimeType,\n }));\n return { inputs, sources: paths, batch };\n }\n\n private async resolvePaths(): Promise<{ paths: string[]; batch: boolean }> {\n const filePath = this.filePath;\n if (Array.isArray(filePath)) {\n return { paths: filePath, batch: true };\n }\n if (typeof filePath === \"string\") {\n if (await isDirectory(filePath)) {\n const pattern = this.glob ?? DEFAULT_GLOB;\n const matches = await fastGlob(pattern, { cwd: filePath, onlyFiles: true, absolute: true });\n return { paths: matches.sort(), batch: true };\n }\n return { paths: [filePath], batch: false };\n }\n return { paths: [], batch: false };\n }\n}\n\nasync function isDirectory(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { Document } from \"@langchain/core/documents\";\nimport type { DocumentCounts, ExtractionConfig, Keyword, Metadata, ProcessingWarning, Table } from \"@xberg-io/xberg\";\n\n// Metadata fields (from Xberg Metadata) that carry JSON-friendly scalar/list values,\n// mapped from the binding's camelCase keys to the snake_case output keys used by the\n// Python adapter. Opaque nested fields (pages, format, imagePreprocessing, jsonSchema,\n// error) are skipped because they are native objects, not plain data. ~keep\nconst METADATA_FIELDS: ReadonlyArray<readonly [string, keyof Metadata]> = [\n [\"title\", \"title\"],\n [\"subject\", \"subject\"],\n [\"authors\", \"authors\"],\n [\"keywords\", \"keywords\"],\n [\"language\", \"language\"],\n [\"created_at\", \"createdAt\"],\n [\"modified_at\", \"modifiedAt\"],\n [\"created_by\", \"createdBy\"],\n [\"modified_by\", \"modifiedBy\"],\n [\"category\", \"category\"],\n [\"tags\", \"tags\"],\n [\"document_version\", \"documentVersion\"],\n [\"abstract_text\", \"abstractText\"],\n [\"output_format\", \"outputFormat\"],\n [\"ocr_used\", \"ocrUsed\"],\n [\"extraction_duration_ms\", \"extractionDurationMs\"],\n];\n\nconst CONTENT_SEPARATOR = \"\\n\\n\";\n\nexport interface ChunkMetadataShape {\n chunkIndex: number;\n totalChunks: number;\n headingPath?: string[];\n tokenCount?: number;\n firstPage?: number;\n lastPage?: number;\n}\n\nexport interface ChunkShape {\n content: string;\n chunkType: unknown;\n metadata: ChunkMetadataShape;\n}\n\nexport interface PageShape {\n pageNumber: number;\n content: string;\n tables?: Table[];\n isBlank?: boolean;\n}\n\n// Structural view of a binding ExtractedDocument. The binding types its own fields\n// through unexported `Js*` aliases; this interface uses the clean exported types plus\n// local chunk/page shapes so the mapper is fully typed and testable without the addon. ~keep\nexport interface ExtractedDoc {\n content?: string;\n mimeType?: string;\n metadata?: Metadata;\n tables?: Table[];\n counts?: DocumentCounts;\n detectedLanguages?: string[];\n chunks?: ChunkShape[];\n pages?: PageShape[];\n extractedKeywords?: Keyword[];\n qualityScore?: number;\n processingWarnings?: ProcessingWarning[];\n}\n\nexport interface ResultEnvelope {\n errors?: Array<{ source: string; message: string }>;\n results?: ExtractedDoc[];\n}\n\nexport interface SplitOptions {\n chunking: boolean;\n perPage: boolean;\n}\n\nexport function isChunkingEnabled(config?: ExtractionConfig | null): boolean {\n return config?.chunking != null;\n}\n\nexport function isPerPageEnabled(config?: ExtractionConfig | null): boolean {\n return Boolean(config?.pages?.extractPages);\n}\n\nexport function flattenMetadata(metadata?: Metadata): Record<string, unknown> {\n const flat: Record<string, unknown> = {};\n if (!metadata) {\n return flat;\n }\n\n for (const [snakeKey, camelKey] of METADATA_FIELDS) {\n const value = metadata[camelKey];\n if (value != null) {\n flat[snakeKey] = value;\n }\n }\n\n const additional = metadata.additional;\n if (additional && Object.keys(additional).length > 0) {\n flat.additional = { ...additional };\n }\n\n return flat;\n}\n\nexport function assembleContent(content: string | undefined, tables: Table[] | undefined): string {\n const text = content ?? \"\";\n if (!tables || tables.length === 0) {\n return text;\n }\n const parts = tables.map((table) => table.markdown ?? \"\").filter((markdown) => markdown.length > 0);\n if (parts.length === 0) {\n return text;\n }\n return [text, ...parts].join(CONTENT_SEPARATOR);\n}\n\nexport function buildMetadata(document: ExtractedDoc, source: string): Record<string, unknown> {\n const metadata = flattenMetadata(document.metadata);\n\n metadata.mime_type = document.mimeType;\n if (document.qualityScore != null) {\n metadata.quality_score = document.qualityScore;\n }\n if (document.detectedLanguages && document.detectedLanguages.length > 0) {\n metadata.detected_languages = document.detectedLanguages;\n }\n if (document.counts != null) {\n metadata.page_count = document.counts.pages;\n }\n\n if (document.extractedKeywords && document.extractedKeywords.length > 0) {\n metadata.extracted_keywords = document.extractedKeywords.map((keyword) => ({\n text: keyword.text,\n score: keyword.score,\n algorithm: String(keyword.algorithm),\n }));\n }\n\n const tables = document.tables ?? [];\n metadata.table_count = tables.length;\n if (tables.length > 0) {\n metadata.tables = tables.map((table) => ({\n cells: table.cells,\n markdown: table.markdown,\n page_number: table.pageNumber,\n }));\n }\n\n if (document.processingWarnings && document.processingWarnings.length > 0) {\n metadata.processing_warnings = document.processingWarnings.map((warning) => ({\n source: warning.source,\n message: warning.message,\n }));\n }\n\n metadata.source = source;\n return metadata;\n}\n\nfunction chunksToDocuments(document: ExtractedDoc, source: string): Document[] {\n const baseMetadata = buildMetadata(document, source);\n const documents: Document[] = [];\n\n for (const chunk of document.chunks ?? []) {\n const metadata: Record<string, unknown> = { ...baseMetadata };\n const chunkMetadata = chunk.metadata;\n metadata.chunk_index = chunkMetadata.chunkIndex;\n metadata.total_chunks = chunkMetadata.totalChunks;\n metadata.chunk_type = String(chunk.chunkType);\n if (chunkMetadata.headingPath && chunkMetadata.headingPath.length > 0) {\n metadata.heading_path = [...chunkMetadata.headingPath];\n }\n if (chunkMetadata.tokenCount != null) {\n metadata.token_count = chunkMetadata.tokenCount;\n }\n if (chunkMetadata.firstPage != null) {\n // Xberg uses 1-indexed pages; LangChain convention is 0-indexed. ~keep\n metadata.page = chunkMetadata.firstPage - 1;\n metadata.first_page = chunkMetadata.firstPage;\n }\n if (chunkMetadata.lastPage != null) {\n metadata.last_page = chunkMetadata.lastPage;\n }\n documents.push(new Document({ pageContent: chunk.content, metadata }));\n }\n\n return documents;\n}\n\nfunction pagesToDocuments(document: ExtractedDoc, source: string): Document[] {\n const baseMetadata = buildMetadata(document, source);\n const documents: Document[] = [];\n\n for (const page of document.pages ?? []) {\n // Xberg uses 1-indexed pages; LangChain convention is 0-indexed. ~keep\n const metadata: Record<string, unknown> = { ...baseMetadata, page: page.pageNumber - 1 };\n if (page.isBlank != null) {\n metadata.is_blank = page.isBlank;\n }\n const pageContent = assembleContent(page.content, page.tables);\n documents.push(new Document({ pageContent, metadata }));\n }\n\n return documents;\n}\n\nexport function documentToDocuments(document: ExtractedDoc, source: string, options: SplitOptions): Document[] {\n if (options.chunking && document.chunks && document.chunks.length > 0) {\n return chunksToDocuments(document, source);\n }\n if (options.perPage && document.pages && document.pages.length > 0) {\n return pagesToDocuments(document, source);\n }\n const metadata = buildMetadata(document, source);\n const pageContent = assembleContent(document.content, document.tables);\n return [new Document({ pageContent, metadata })];\n}\n\nexport function resultToDocuments(result: ResultEnvelope, sources: string[], options: SplitOptions): Document[] {\n if (result.errors && result.errors.length > 0) {\n const error = result.errors[0];\n throw new Error(`Failed to extract '${error.source}': ${error.message}`);\n }\n\n const documents: Document[] = [];\n const results = result.results ?? [];\n results.forEach((document, index) => {\n const source = sources[index] ?? (sources.length > 0 ? sources[sources.length - 1] : \"\");\n documents.push(...documentToDocuments(document, source, options));\n });\n return documents;\n}\n"],"mappings":"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","names":["fastGlob"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -15,7 +15,7 @@ interface XbergLoaderOptions {
|
|
|
15
15
|
config?: ExtractionConfig;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Load documents using Xberg, supporting
|
|
18
|
+
* Load documents using Xberg, supporting 107 file formats with true async extraction.
|
|
19
19
|
*
|
|
20
20
|
* By default each source becomes one Document. Enable `chunking` on the
|
|
21
21
|
* `ExtractionConfig` to emit one Document per chunk, or `pages` for one Document
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ interface XbergLoaderOptions {
|
|
|
15
15
|
config?: ExtractionConfig;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Load documents using Xberg, supporting
|
|
18
|
+
* Load documents using Xberg, supporting 107 file formats with true async extraction.
|
|
19
19
|
*
|
|
20
20
|
* By default each source becomes one Document. Enable `chunking` on the
|
|
21
21
|
* `ExtractionConfig` to emit one Document per chunk, or `pages` for one Document
|
package/dist/index.js
CHANGED
|
@@ -126,8 +126,7 @@ function pagesToDocuments(document, source) {
|
|
|
126
126
|
const baseMetadata = buildMetadata(document, source);
|
|
127
127
|
const documents = [];
|
|
128
128
|
for (const page of document.pages ?? []) {
|
|
129
|
-
const metadata = { ...baseMetadata };
|
|
130
|
-
metadata.page = page.pageNumber - 1;
|
|
129
|
+
const metadata = { ...baseMetadata, page: page.pageNumber - 1 };
|
|
131
130
|
if (page.isBlank != null) {
|
|
132
131
|
metadata.is_blank = page.isBlank;
|
|
133
132
|
}
|
|
@@ -197,7 +196,7 @@ var XbergLoader = class extends BaseDocumentLoader {
|
|
|
197
196
|
result = batch ? await extractBatch(inputs, this.config ?? null) : await extract(inputs[0], this.config ?? null);
|
|
198
197
|
} catch (error) {
|
|
199
198
|
const source = sources[0] ?? "input";
|
|
200
|
-
throw new Error(`Failed to extract '${source}': ${errorMessage(error)}
|
|
199
|
+
throw new Error(`Failed to extract '${source}': ${errorMessage(error)}`, { cause: error });
|
|
201
200
|
}
|
|
202
201
|
return resultToDocuments(result, sources, {
|
|
203
202
|
chunking: isChunkingEnabled(this.config),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/loader.ts","../src/mapping.ts"],"sourcesContent":["import { stat } from \"node:fs/promises\";\nimport { BaseDocumentLoader } from \"@langchain/core/document_loaders/base\";\nimport type { Document } from \"@langchain/core/documents\";\nimport { extract, extractBatch, ExtractInputKind } from \"@xberg-io/xberg\";\nimport type { ExtractInput, ExtractionConfig } from \"@xberg-io/xberg\";\nimport fastGlob from \"fast-glob\";\nimport { isChunkingEnabled, isPerPageEnabled, resultToDocuments, type ResultEnvelope } from \"./mapping\";\n\nconst DEFAULT_GLOB = \"**/*\";\n\nexport interface XbergLoaderOptions {\n /** File path, list of file paths, or directory path to load. */\n filePath?: string | string[];\n /** Raw bytes to extract from. Mutually exclusive with `filePath`. */\n data?: Uint8Array;\n /** MIME type hint. Required when using `data`, optional for `filePath`. */\n mimeType?: string;\n /** Glob pattern for directory mode. Defaults to matching all files. */\n glob?: string;\n /** Xberg extraction configuration controlling output format, OCR, pages, chunking, etc. */\n config?: ExtractionConfig;\n}\n\n/**\n * Load documents using Xberg, supporting 101 file formats with true async extraction.\n *\n * By default each source becomes one Document. Enable `chunking` on the\n * `ExtractionConfig` to emit one Document per chunk, or `pages` for one Document\n * per page. Multiple paths (a list or a directory glob) are extracted with a single\n * `extractBatch` call so concurrency happens Rust-side.\n */\nexport class XbergLoader extends BaseDocumentLoader {\n private readonly filePath?: string | string[];\n private readonly data?: Uint8Array;\n private readonly mimeType?: string;\n private readonly glob?: string;\n private readonly config?: ExtractionConfig;\n\n constructor(options: XbergLoaderOptions) {\n super();\n const { filePath, data, mimeType, glob, config } = options;\n if (filePath === undefined && data === undefined) {\n throw new Error(\"Either 'filePath' or 'data' must be provided.\");\n }\n if (filePath !== undefined && data !== undefined) {\n throw new Error(\"Cannot specify both 'filePath' and 'data'. Use one or the other.\");\n }\n if (data !== undefined && mimeType === undefined) {\n throw new Error(\"'mimeType' is required when using 'data'.\");\n }\n\n this.filePath = filePath;\n this.data = data;\n this.mimeType = mimeType;\n this.glob = glob;\n this.config = config;\n }\n\n async load(): Promise<Document[]> {\n const { inputs, sources, batch } = await this.buildInputs();\n if (inputs.length === 0) {\n return [];\n }\n\n let result;\n try {\n result = batch ? await extractBatch(inputs, this.config ?? null) : await extract(inputs[0], this.config ?? null);\n } catch (error) {\n const source = sources[0] ?? \"input\";\n throw new Error(`Failed to extract '${source}': ${errorMessage(error)}`);\n }\n\n return resultToDocuments(result as unknown as ResultEnvelope, sources, {\n chunking: isChunkingEnabled(this.config),\n perPage: isPerPageEnabled(this.config),\n });\n }\n\n private async buildInputs(): Promise<{ inputs: ExtractInput[]; sources: string[]; batch: boolean }> {\n if (this.data !== undefined) {\n const source = `bytes://${this.mimeType}`;\n const input: ExtractInput = { kind: ExtractInputKind.Bytes, bytes: this.data, mimeType: this.mimeType };\n return { inputs: [input], sources: [source], batch: false };\n }\n\n const { paths, batch } = await this.resolvePaths();\n const inputs: ExtractInput[] = paths.map((path) => ({\n kind: ExtractInputKind.Uri,\n uri: path,\n mimeType: this.mimeType,\n }));\n return { inputs, sources: paths, batch };\n }\n\n private async resolvePaths(): Promise<{ paths: string[]; batch: boolean }> {\n const filePath = this.filePath;\n if (Array.isArray(filePath)) {\n return { paths: filePath, batch: true };\n }\n if (typeof filePath === \"string\") {\n if (await isDirectory(filePath)) {\n const pattern = this.glob ?? DEFAULT_GLOB;\n const matches = await fastGlob(pattern, { cwd: filePath, onlyFiles: true, absolute: true });\n return { paths: matches.sort(), batch: true };\n }\n return { paths: [filePath], batch: false };\n }\n return { paths: [], batch: false };\n }\n}\n\nasync function isDirectory(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { Document } from \"@langchain/core/documents\";\nimport type { DocumentCounts, ExtractionConfig, Keyword, Metadata, ProcessingWarning, Table } from \"@xberg-io/xberg\";\n\n// Metadata fields (from Xberg Metadata) that carry JSON-friendly scalar/list values,\n// mapped from the binding's camelCase keys to the snake_case output keys used by the\n// Python adapter. Opaque nested fields (pages, format, imagePreprocessing, jsonSchema,\n// error) are skipped because they are native objects, not plain data. ~keep\nconst METADATA_FIELDS: ReadonlyArray<readonly [string, keyof Metadata]> = [\n [\"title\", \"title\"],\n [\"subject\", \"subject\"],\n [\"authors\", \"authors\"],\n [\"keywords\", \"keywords\"],\n [\"language\", \"language\"],\n [\"created_at\", \"createdAt\"],\n [\"modified_at\", \"modifiedAt\"],\n [\"created_by\", \"createdBy\"],\n [\"modified_by\", \"modifiedBy\"],\n [\"category\", \"category\"],\n [\"tags\", \"tags\"],\n [\"document_version\", \"documentVersion\"],\n [\"abstract_text\", \"abstractText\"],\n [\"output_format\", \"outputFormat\"],\n [\"ocr_used\", \"ocrUsed\"],\n [\"extraction_duration_ms\", \"extractionDurationMs\"],\n];\n\nconst CONTENT_SEPARATOR = \"\\n\\n\";\n\nexport interface ChunkMetadataShape {\n chunkIndex: number;\n totalChunks: number;\n headingPath?: string[];\n tokenCount?: number;\n firstPage?: number;\n lastPage?: number;\n}\n\nexport interface ChunkShape {\n content: string;\n chunkType: unknown;\n metadata: ChunkMetadataShape;\n}\n\nexport interface PageShape {\n pageNumber: number;\n content: string;\n tables?: Table[];\n isBlank?: boolean;\n}\n\n// Structural view of a binding ExtractedDocument. The binding types its own fields\n// through unexported `Js*` aliases; this interface uses the clean exported types plus\n// local chunk/page shapes so the mapper is fully typed and testable without the addon. ~keep\nexport interface ExtractedDoc {\n content?: string;\n mimeType?: string;\n metadata?: Metadata;\n tables?: Table[];\n counts?: DocumentCounts;\n detectedLanguages?: string[];\n chunks?: ChunkShape[];\n pages?: PageShape[];\n extractedKeywords?: Keyword[];\n qualityScore?: number;\n processingWarnings?: ProcessingWarning[];\n}\n\nexport interface ResultEnvelope {\n errors?: Array<{ source: string; message: string }>;\n results?: ExtractedDoc[];\n}\n\nexport interface SplitOptions {\n chunking: boolean;\n perPage: boolean;\n}\n\nexport function isChunkingEnabled(config?: ExtractionConfig | null): boolean {\n return config?.chunking != null;\n}\n\nexport function isPerPageEnabled(config?: ExtractionConfig | null): boolean {\n return Boolean(config?.pages?.extractPages);\n}\n\nexport function flattenMetadata(metadata?: Metadata): Record<string, unknown> {\n const flat: Record<string, unknown> = {};\n if (!metadata) {\n return flat;\n }\n\n for (const [snakeKey, camelKey] of METADATA_FIELDS) {\n const value = metadata[camelKey];\n if (value != null) {\n flat[snakeKey] = value;\n }\n }\n\n const additional = metadata.additional;\n if (additional && Object.keys(additional).length > 0) {\n flat.additional = { ...additional };\n }\n\n return flat;\n}\n\nexport function assembleContent(content: string | undefined, tables: Table[] | undefined): string {\n const text = content ?? \"\";\n if (!tables || tables.length === 0) {\n return text;\n }\n const parts = tables.map((table) => table.markdown ?? \"\").filter((markdown) => markdown.length > 0);\n if (parts.length === 0) {\n return text;\n }\n return [text, ...parts].join(CONTENT_SEPARATOR);\n}\n\nexport function buildMetadata(document: ExtractedDoc, source: string): Record<string, unknown> {\n const metadata = flattenMetadata(document.metadata);\n\n metadata.mime_type = document.mimeType;\n if (document.qualityScore != null) {\n metadata.quality_score = document.qualityScore;\n }\n if (document.detectedLanguages && document.detectedLanguages.length > 0) {\n metadata.detected_languages = document.detectedLanguages;\n }\n if (document.counts != null) {\n metadata.page_count = document.counts.pages;\n }\n\n if (document.extractedKeywords && document.extractedKeywords.length > 0) {\n metadata.extracted_keywords = document.extractedKeywords.map((keyword) => ({\n text: keyword.text,\n score: keyword.score,\n algorithm: String(keyword.algorithm),\n }));\n }\n\n const tables = document.tables ?? [];\n metadata.table_count = tables.length;\n if (tables.length > 0) {\n metadata.tables = tables.map((table) => ({\n cells: table.cells,\n markdown: table.markdown,\n page_number: table.pageNumber,\n }));\n }\n\n if (document.processingWarnings && document.processingWarnings.length > 0) {\n metadata.processing_warnings = document.processingWarnings.map((warning) => ({\n source: warning.source,\n message: warning.message,\n }));\n }\n\n metadata.source = source;\n return metadata;\n}\n\nfunction chunksToDocuments(document: ExtractedDoc, source: string): Document[] {\n const baseMetadata = buildMetadata(document, source);\n const documents: Document[] = [];\n\n for (const chunk of document.chunks ?? []) {\n const metadata: Record<string, unknown> = { ...baseMetadata };\n const chunkMetadata = chunk.metadata;\n metadata.chunk_index = chunkMetadata.chunkIndex;\n metadata.total_chunks = chunkMetadata.totalChunks;\n metadata.chunk_type = String(chunk.chunkType);\n if (chunkMetadata.headingPath && chunkMetadata.headingPath.length > 0) {\n metadata.heading_path = [...chunkMetadata.headingPath];\n }\n if (chunkMetadata.tokenCount != null) {\n metadata.token_count = chunkMetadata.tokenCount;\n }\n if (chunkMetadata.firstPage != null) {\n // Xberg uses 1-indexed pages; LangChain convention is 0-indexed. ~keep\n metadata.page = chunkMetadata.firstPage - 1;\n metadata.first_page = chunkMetadata.firstPage;\n }\n if (chunkMetadata.lastPage != null) {\n metadata.last_page = chunkMetadata.lastPage;\n }\n documents.push(new Document({ pageContent: chunk.content, metadata }));\n }\n\n return documents;\n}\n\nfunction pagesToDocuments(document: ExtractedDoc, source: string): Document[] {\n const baseMetadata = buildMetadata(document, source);\n const documents: Document[] = [];\n\n for (const page of document.pages ?? []) {\n const metadata: Record<string, unknown> = { ...baseMetadata };\n // Xberg uses 1-indexed pages; LangChain convention is 0-indexed. ~keep\n metadata.page = page.pageNumber - 1;\n if (page.isBlank != null) {\n metadata.is_blank = page.isBlank;\n }\n const pageContent = assembleContent(page.content, page.tables);\n documents.push(new Document({ pageContent, metadata }));\n }\n\n return documents;\n}\n\nexport function documentToDocuments(document: ExtractedDoc, source: string, options: SplitOptions): Document[] {\n if (options.chunking && document.chunks && document.chunks.length > 0) {\n return chunksToDocuments(document, source);\n }\n if (options.perPage && document.pages && document.pages.length > 0) {\n return pagesToDocuments(document, source);\n }\n const metadata = buildMetadata(document, source);\n const pageContent = assembleContent(document.content, document.tables);\n return [new Document({ pageContent, metadata })];\n}\n\nexport function resultToDocuments(result: ResultEnvelope, sources: string[], options: SplitOptions): Document[] {\n if (result.errors && result.errors.length > 0) {\n const error = result.errors[0];\n throw new Error(`Failed to extract '${error.source}': ${error.message}`);\n }\n\n const documents: Document[] = [];\n const results = result.results ?? [];\n results.forEach((document, index) => {\n const source = sources[index] ?? (sources.length > 0 ? sources[sources.length - 1] : \"\");\n documents.push(...documentToDocuments(document, source, options));\n });\n return documents;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/loader.ts","../src/mapping.ts"],"sourcesContent":["import { stat } from \"node:fs/promises\";\nimport { BaseDocumentLoader } from \"@langchain/core/document_loaders/base\";\nimport type { Document } from \"@langchain/core/documents\";\nimport { extract, extractBatch, ExtractInputKind } from \"@xberg-io/xberg\";\nimport type { ExtractInput, ExtractionConfig } from \"@xberg-io/xberg\";\nimport fastGlob from \"fast-glob\";\nimport { isChunkingEnabled, isPerPageEnabled, resultToDocuments, type ResultEnvelope } from \"./mapping\";\n\nconst DEFAULT_GLOB = \"**/*\";\n\nexport interface XbergLoaderOptions {\n /** File path, list of file paths, or directory path to load. */\n filePath?: string | string[];\n /** Raw bytes to extract from. Mutually exclusive with `filePath`. */\n data?: Uint8Array;\n /** MIME type hint. Required when using `data`, optional for `filePath`. */\n mimeType?: string;\n /** Glob pattern for directory mode. Defaults to matching all files. */\n glob?: string;\n /** Xberg extraction configuration controlling output format, OCR, pages, chunking, etc. */\n config?: ExtractionConfig;\n}\n\n/**\n * Load documents using Xberg, supporting 107 file formats with true async extraction.\n *\n * By default each source becomes one Document. Enable `chunking` on the\n * `ExtractionConfig` to emit one Document per chunk, or `pages` for one Document\n * per page. Multiple paths (a list or a directory glob) are extracted with a single\n * `extractBatch` call so concurrency happens Rust-side.\n */\nexport class XbergLoader extends BaseDocumentLoader {\n private readonly filePath?: string | string[];\n private readonly data?: Uint8Array;\n private readonly mimeType?: string;\n private readonly glob?: string;\n private readonly config?: ExtractionConfig;\n\n constructor(options: XbergLoaderOptions) {\n super();\n const { filePath, data, mimeType, glob, config } = options;\n if (filePath === undefined && data === undefined) {\n throw new Error(\"Either 'filePath' or 'data' must be provided.\");\n }\n if (filePath !== undefined && data !== undefined) {\n throw new Error(\"Cannot specify both 'filePath' and 'data'. Use one or the other.\");\n }\n if (data !== undefined && mimeType === undefined) {\n throw new Error(\"'mimeType' is required when using 'data'.\");\n }\n\n this.filePath = filePath;\n this.data = data;\n this.mimeType = mimeType;\n this.glob = glob;\n this.config = config;\n }\n\n async load(): Promise<Document[]> {\n const { inputs, sources, batch } = await this.buildInputs();\n if (inputs.length === 0) {\n return [];\n }\n\n let result;\n try {\n result = batch ? await extractBatch(inputs, this.config ?? null) : await extract(inputs[0], this.config ?? null);\n } catch (error) {\n const source = sources[0] ?? \"input\";\n throw new Error(`Failed to extract '${source}': ${errorMessage(error)}`, { cause: error });\n }\n\n return resultToDocuments(result as unknown as ResultEnvelope, sources, {\n chunking: isChunkingEnabled(this.config),\n perPage: isPerPageEnabled(this.config),\n });\n }\n\n private async buildInputs(): Promise<{ inputs: ExtractInput[]; sources: string[]; batch: boolean }> {\n if (this.data !== undefined) {\n const source = `bytes://${this.mimeType}`;\n const input: ExtractInput = { kind: ExtractInputKind.Bytes, bytes: this.data, mimeType: this.mimeType };\n return { inputs: [input], sources: [source], batch: false };\n }\n\n const { paths, batch } = await this.resolvePaths();\n const inputs: ExtractInput[] = paths.map((path) => ({\n kind: ExtractInputKind.Uri,\n uri: path,\n mimeType: this.mimeType,\n }));\n return { inputs, sources: paths, batch };\n }\n\n private async resolvePaths(): Promise<{ paths: string[]; batch: boolean }> {\n const filePath = this.filePath;\n if (Array.isArray(filePath)) {\n return { paths: filePath, batch: true };\n }\n if (typeof filePath === \"string\") {\n if (await isDirectory(filePath)) {\n const pattern = this.glob ?? DEFAULT_GLOB;\n const matches = await fastGlob(pattern, { cwd: filePath, onlyFiles: true, absolute: true });\n return { paths: matches.sort(), batch: true };\n }\n return { paths: [filePath], batch: false };\n }\n return { paths: [], batch: false };\n }\n}\n\nasync function isDirectory(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { Document } from \"@langchain/core/documents\";\nimport type { DocumentCounts, ExtractionConfig, Keyword, Metadata, ProcessingWarning, Table } from \"@xberg-io/xberg\";\n\n// Metadata fields (from Xberg Metadata) that carry JSON-friendly scalar/list values,\n// mapped from the binding's camelCase keys to the snake_case output keys used by the\n// Python adapter. Opaque nested fields (pages, format, imagePreprocessing, jsonSchema,\n// error) are skipped because they are native objects, not plain data. ~keep\nconst METADATA_FIELDS: ReadonlyArray<readonly [string, keyof Metadata]> = [\n [\"title\", \"title\"],\n [\"subject\", \"subject\"],\n [\"authors\", \"authors\"],\n [\"keywords\", \"keywords\"],\n [\"language\", \"language\"],\n [\"created_at\", \"createdAt\"],\n [\"modified_at\", \"modifiedAt\"],\n [\"created_by\", \"createdBy\"],\n [\"modified_by\", \"modifiedBy\"],\n [\"category\", \"category\"],\n [\"tags\", \"tags\"],\n [\"document_version\", \"documentVersion\"],\n [\"abstract_text\", \"abstractText\"],\n [\"output_format\", \"outputFormat\"],\n [\"ocr_used\", \"ocrUsed\"],\n [\"extraction_duration_ms\", \"extractionDurationMs\"],\n];\n\nconst CONTENT_SEPARATOR = \"\\n\\n\";\n\nexport interface ChunkMetadataShape {\n chunkIndex: number;\n totalChunks: number;\n headingPath?: string[];\n tokenCount?: number;\n firstPage?: number;\n lastPage?: number;\n}\n\nexport interface ChunkShape {\n content: string;\n chunkType: unknown;\n metadata: ChunkMetadataShape;\n}\n\nexport interface PageShape {\n pageNumber: number;\n content: string;\n tables?: Table[];\n isBlank?: boolean;\n}\n\n// Structural view of a binding ExtractedDocument. The binding types its own fields\n// through unexported `Js*` aliases; this interface uses the clean exported types plus\n// local chunk/page shapes so the mapper is fully typed and testable without the addon. ~keep\nexport interface ExtractedDoc {\n content?: string;\n mimeType?: string;\n metadata?: Metadata;\n tables?: Table[];\n counts?: DocumentCounts;\n detectedLanguages?: string[];\n chunks?: ChunkShape[];\n pages?: PageShape[];\n extractedKeywords?: Keyword[];\n qualityScore?: number;\n processingWarnings?: ProcessingWarning[];\n}\n\nexport interface ResultEnvelope {\n errors?: Array<{ source: string; message: string }>;\n results?: ExtractedDoc[];\n}\n\nexport interface SplitOptions {\n chunking: boolean;\n perPage: boolean;\n}\n\nexport function isChunkingEnabled(config?: ExtractionConfig | null): boolean {\n return config?.chunking != null;\n}\n\nexport function isPerPageEnabled(config?: ExtractionConfig | null): boolean {\n return Boolean(config?.pages?.extractPages);\n}\n\nexport function flattenMetadata(metadata?: Metadata): Record<string, unknown> {\n const flat: Record<string, unknown> = {};\n if (!metadata) {\n return flat;\n }\n\n for (const [snakeKey, camelKey] of METADATA_FIELDS) {\n const value = metadata[camelKey];\n if (value != null) {\n flat[snakeKey] = value;\n }\n }\n\n const additional = metadata.additional;\n if (additional && Object.keys(additional).length > 0) {\n flat.additional = { ...additional };\n }\n\n return flat;\n}\n\nexport function assembleContent(content: string | undefined, tables: Table[] | undefined): string {\n const text = content ?? \"\";\n if (!tables || tables.length === 0) {\n return text;\n }\n const parts = tables.map((table) => table.markdown ?? \"\").filter((markdown) => markdown.length > 0);\n if (parts.length === 0) {\n return text;\n }\n return [text, ...parts].join(CONTENT_SEPARATOR);\n}\n\nexport function buildMetadata(document: ExtractedDoc, source: string): Record<string, unknown> {\n const metadata = flattenMetadata(document.metadata);\n\n metadata.mime_type = document.mimeType;\n if (document.qualityScore != null) {\n metadata.quality_score = document.qualityScore;\n }\n if (document.detectedLanguages && document.detectedLanguages.length > 0) {\n metadata.detected_languages = document.detectedLanguages;\n }\n if (document.counts != null) {\n metadata.page_count = document.counts.pages;\n }\n\n if (document.extractedKeywords && document.extractedKeywords.length > 0) {\n metadata.extracted_keywords = document.extractedKeywords.map((keyword) => ({\n text: keyword.text,\n score: keyword.score,\n algorithm: String(keyword.algorithm),\n }));\n }\n\n const tables = document.tables ?? [];\n metadata.table_count = tables.length;\n if (tables.length > 0) {\n metadata.tables = tables.map((table) => ({\n cells: table.cells,\n markdown: table.markdown,\n page_number: table.pageNumber,\n }));\n }\n\n if (document.processingWarnings && document.processingWarnings.length > 0) {\n metadata.processing_warnings = document.processingWarnings.map((warning) => ({\n source: warning.source,\n message: warning.message,\n }));\n }\n\n metadata.source = source;\n return metadata;\n}\n\nfunction chunksToDocuments(document: ExtractedDoc, source: string): Document[] {\n const baseMetadata = buildMetadata(document, source);\n const documents: Document[] = [];\n\n for (const chunk of document.chunks ?? []) {\n const metadata: Record<string, unknown> = { ...baseMetadata };\n const chunkMetadata = chunk.metadata;\n metadata.chunk_index = chunkMetadata.chunkIndex;\n metadata.total_chunks = chunkMetadata.totalChunks;\n metadata.chunk_type = String(chunk.chunkType);\n if (chunkMetadata.headingPath && chunkMetadata.headingPath.length > 0) {\n metadata.heading_path = [...chunkMetadata.headingPath];\n }\n if (chunkMetadata.tokenCount != null) {\n metadata.token_count = chunkMetadata.tokenCount;\n }\n if (chunkMetadata.firstPage != null) {\n // Xberg uses 1-indexed pages; LangChain convention is 0-indexed. ~keep\n metadata.page = chunkMetadata.firstPage - 1;\n metadata.first_page = chunkMetadata.firstPage;\n }\n if (chunkMetadata.lastPage != null) {\n metadata.last_page = chunkMetadata.lastPage;\n }\n documents.push(new Document({ pageContent: chunk.content, metadata }));\n }\n\n return documents;\n}\n\nfunction pagesToDocuments(document: ExtractedDoc, source: string): Document[] {\n const baseMetadata = buildMetadata(document, source);\n const documents: Document[] = [];\n\n for (const page of document.pages ?? []) {\n // Xberg uses 1-indexed pages; LangChain convention is 0-indexed. ~keep\n const metadata: Record<string, unknown> = { ...baseMetadata, page: page.pageNumber - 1 };\n if (page.isBlank != null) {\n metadata.is_blank = page.isBlank;\n }\n const pageContent = assembleContent(page.content, page.tables);\n documents.push(new Document({ pageContent, metadata }));\n }\n\n return documents;\n}\n\nexport function documentToDocuments(document: ExtractedDoc, source: string, options: SplitOptions): Document[] {\n if (options.chunking && document.chunks && document.chunks.length > 0) {\n return chunksToDocuments(document, source);\n }\n if (options.perPage && document.pages && document.pages.length > 0) {\n return pagesToDocuments(document, source);\n }\n const metadata = buildMetadata(document, source);\n const pageContent = assembleContent(document.content, document.tables);\n return [new Document({ pageContent, metadata })];\n}\n\nexport function resultToDocuments(result: ResultEnvelope, sources: string[], options: SplitOptions): Document[] {\n if (result.errors && result.errors.length > 0) {\n const error = result.errors[0];\n throw new Error(`Failed to extract '${error.source}': ${error.message}`);\n }\n\n const documents: Document[] = [];\n const results = result.results ?? [];\n results.forEach((document, index) => {\n const source = sources[index] ?? (sources.length > 0 ? sources[sources.length - 1] : \"\");\n documents.push(...documentToDocuments(document, source, options));\n });\n return documents;\n}\n"],"mappings":";AAAA,SAAS,YAAY;AACrB,SAAS,0BAA0B;AAEnC,SAAS,SAAS,cAAc,wBAAwB;AAExD,OAAO,cAAc;;;ACLrB,SAAS,gBAAgB;AAOzB,IAAM,kBAAoE;AAAA,EACxE,CAAC,SAAS,OAAO;AAAA,EACjB,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,YAAY,UAAU;AAAA,EACvB,CAAC,YAAY,UAAU;AAAA,EACvB,CAAC,cAAc,WAAW;AAAA,EAC1B,CAAC,eAAe,YAAY;AAAA,EAC5B,CAAC,cAAc,WAAW;AAAA,EAC1B,CAAC,eAAe,YAAY;AAAA,EAC5B,CAAC,YAAY,UAAU;AAAA,EACvB,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,oBAAoB,iBAAiB;AAAA,EACtC,CAAC,iBAAiB,cAAc;AAAA,EAChC,CAAC,iBAAiB,cAAc;AAAA,EAChC,CAAC,YAAY,SAAS;AAAA,EACtB,CAAC,0BAA0B,sBAAsB;AACnD;AAEA,IAAM,oBAAoB;AAmDnB,SAAS,kBAAkB,QAA2C;AAC3E,SAAO,QAAQ,YAAY;AAC7B;AAEO,SAAS,iBAAiB,QAA2C;AAC1E,SAAO,QAAQ,QAAQ,OAAO,YAAY;AAC5C;AAEO,SAAS,gBAAgB,UAA8C;AAC5E,QAAM,OAAgC,CAAC;AACvC,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,aAAW,CAAC,UAAU,QAAQ,KAAK,iBAAiB;AAClD,UAAM,QAAQ,SAAS,QAAQ;AAC/B,QAAI,SAAS,MAAM;AACjB,WAAK,QAAQ,IAAI;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,aAAa,SAAS;AAC5B,MAAI,cAAc,OAAO,KAAK,UAAU,EAAE,SAAS,GAAG;AACpD,SAAK,aAAa,EAAE,GAAG,WAAW;AAAA,EACpC;AAEA,SAAO;AACT;AAEO,SAAS,gBAAgB,SAA6B,QAAqC;AAChG,QAAM,OAAO,WAAW;AACxB,MAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,OAAO,IAAI,CAAC,UAAU,MAAM,YAAY,EAAE,EAAE,OAAO,CAAC,aAAa,SAAS,SAAS,CAAC;AAClG,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO;AAAA,EACT;AACA,SAAO,CAAC,MAAM,GAAG,KAAK,EAAE,KAAK,iBAAiB;AAChD;AAEO,SAAS,cAAc,UAAwB,QAAyC;AAC7F,QAAM,WAAW,gBAAgB,SAAS,QAAQ;AAElD,WAAS,YAAY,SAAS;AAC9B,MAAI,SAAS,gBAAgB,MAAM;AACjC,aAAS,gBAAgB,SAAS;AAAA,EACpC;AACA,MAAI,SAAS,qBAAqB,SAAS,kBAAkB,SAAS,GAAG;AACvE,aAAS,qBAAqB,SAAS;AAAA,EACzC;AACA,MAAI,SAAS,UAAU,MAAM;AAC3B,aAAS,aAAa,SAAS,OAAO;AAAA,EACxC;AAEA,MAAI,SAAS,qBAAqB,SAAS,kBAAkB,SAAS,GAAG;AACvE,aAAS,qBAAqB,SAAS,kBAAkB,IAAI,CAAC,aAAa;AAAA,MACzE,MAAM,QAAQ;AAAA,MACd,OAAO,QAAQ;AAAA,MACf,WAAW,OAAO,QAAQ,SAAS;AAAA,IACrC,EAAE;AAAA,EACJ;AAEA,QAAM,SAAS,SAAS,UAAU,CAAC;AACnC,WAAS,cAAc,OAAO;AAC9B,MAAI,OAAO,SAAS,GAAG;AACrB,aAAS,SAAS,OAAO,IAAI,CAAC,WAAW;AAAA,MACvC,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,IACrB,EAAE;AAAA,EACJ;AAEA,MAAI,SAAS,sBAAsB,SAAS,mBAAmB,SAAS,GAAG;AACzE,aAAS,sBAAsB,SAAS,mBAAmB,IAAI,CAAC,aAAa;AAAA,MAC3E,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,IACnB,EAAE;AAAA,EACJ;AAEA,WAAS,SAAS;AAClB,SAAO;AACT;AAEA,SAAS,kBAAkB,UAAwB,QAA4B;AAC7E,QAAM,eAAe,cAAc,UAAU,MAAM;AACnD,QAAM,YAAwB,CAAC;AAE/B,aAAW,SAAS,SAAS,UAAU,CAAC,GAAG;AACzC,UAAM,WAAoC,EAAE,GAAG,aAAa;AAC5D,UAAM,gBAAgB,MAAM;AAC5B,aAAS,cAAc,cAAc;AACrC,aAAS,eAAe,cAAc;AACtC,aAAS,aAAa,OAAO,MAAM,SAAS;AAC5C,QAAI,cAAc,eAAe,cAAc,YAAY,SAAS,GAAG;AACrE,eAAS,eAAe,CAAC,GAAG,cAAc,WAAW;AAAA,IACvD;AACA,QAAI,cAAc,cAAc,MAAM;AACpC,eAAS,cAAc,cAAc;AAAA,IACvC;AACA,QAAI,cAAc,aAAa,MAAM;AAEnC,eAAS,OAAO,cAAc,YAAY;AAC1C,eAAS,aAAa,cAAc;AAAA,IACtC;AACA,QAAI,cAAc,YAAY,MAAM;AAClC,eAAS,YAAY,cAAc;AAAA,IACrC;AACA,cAAU,KAAK,IAAI,SAAS,EAAE,aAAa,MAAM,SAAS,SAAS,CAAC,CAAC;AAAA,EACvE;AAEA,SAAO;AACT;AAEA,SAAS,iBAAiB,UAAwB,QAA4B;AAC5E,QAAM,eAAe,cAAc,UAAU,MAAM;AACnD,QAAM,YAAwB,CAAC;AAE/B,aAAW,QAAQ,SAAS,SAAS,CAAC,GAAG;AAEvC,UAAM,WAAoC,EAAE,GAAG,cAAc,MAAM,KAAK,aAAa,EAAE;AACvF,QAAI,KAAK,WAAW,MAAM;AACxB,eAAS,WAAW,KAAK;AAAA,IAC3B;AACA,UAAM,cAAc,gBAAgB,KAAK,SAAS,KAAK,MAAM;AAC7D,cAAU,KAAK,IAAI,SAAS,EAAE,aAAa,SAAS,CAAC,CAAC;AAAA,EACxD;AAEA,SAAO;AACT;AAEO,SAAS,oBAAoB,UAAwB,QAAgB,SAAmC;AAC7G,MAAI,QAAQ,YAAY,SAAS,UAAU,SAAS,OAAO,SAAS,GAAG;AACrE,WAAO,kBAAkB,UAAU,MAAM;AAAA,EAC3C;AACA,MAAI,QAAQ,WAAW,SAAS,SAAS,SAAS,MAAM,SAAS,GAAG;AAClE,WAAO,iBAAiB,UAAU,MAAM;AAAA,EAC1C;AACA,QAAM,WAAW,cAAc,UAAU,MAAM;AAC/C,QAAM,cAAc,gBAAgB,SAAS,SAAS,SAAS,MAAM;AACrE,SAAO,CAAC,IAAI,SAAS,EAAE,aAAa,SAAS,CAAC,CAAC;AACjD;AAEO,SAAS,kBAAkB,QAAwB,SAAmB,SAAmC;AAC9G,MAAI,OAAO,UAAU,OAAO,OAAO,SAAS,GAAG;AAC7C,UAAM,QAAQ,OAAO,OAAO,CAAC;AAC7B,UAAM,IAAI,MAAM,sBAAsB,MAAM,MAAM,MAAM,MAAM,OAAO,EAAE;AAAA,EACzE;AAEA,QAAM,YAAwB,CAAC;AAC/B,QAAM,UAAU,OAAO,WAAW,CAAC;AACnC,UAAQ,QAAQ,CAAC,UAAU,UAAU;AACnC,UAAM,SAAS,QAAQ,KAAK,MAAM,QAAQ,SAAS,IAAI,QAAQ,QAAQ,SAAS,CAAC,IAAI;AACrF,cAAU,KAAK,GAAG,oBAAoB,UAAU,QAAQ,OAAO,CAAC;AAAA,EAClE,CAAC;AACD,SAAO;AACT;;;ADjOA,IAAM,eAAe;AAuBd,IAAM,cAAN,cAA0B,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA6B;AACvC,UAAM;AACN,UAAM,EAAE,UAAU,MAAM,UAAU,MAAM,OAAO,IAAI;AACnD,QAAI,aAAa,UAAa,SAAS,QAAW;AAChD,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AACA,QAAI,aAAa,UAAa,SAAS,QAAW;AAChD,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AACA,QAAI,SAAS,UAAa,aAAa,QAAW;AAChD,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AAEA,SAAK,WAAW;AAChB,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,OAA4B;AAChC,UAAM,EAAE,QAAQ,SAAS,MAAM,IAAI,MAAM,KAAK,YAAY;AAC1D,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO,CAAC;AAAA,IACV;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,QAAQ,MAAM,aAAa,QAAQ,KAAK,UAAU,IAAI,IAAI,MAAM,QAAQ,OAAO,CAAC,GAAG,KAAK,UAAU,IAAI;AAAA,IACjH,SAAS,OAAO;AACd,YAAM,SAAS,QAAQ,CAAC,KAAK;AAC7B,YAAM,IAAI,MAAM,sBAAsB,MAAM,MAAM,aAAa,KAAK,CAAC,IAAI,EAAE,OAAO,MAAM,CAAC;AAAA,IAC3F;AAEA,WAAO,kBAAkB,QAAqC,SAAS;AAAA,MACrE,UAAU,kBAAkB,KAAK,MAAM;AAAA,MACvC,SAAS,iBAAiB,KAAK,MAAM;AAAA,IACvC,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,cAAsF;AAClG,QAAI,KAAK,SAAS,QAAW;AAC3B,YAAM,SAAS,WAAW,KAAK,QAAQ;AACvC,YAAM,QAAsB,EAAE,MAAM,iBAAiB,OAAO,OAAO,KAAK,MAAM,UAAU,KAAK,SAAS;AACtG,aAAO,EAAE,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC,MAAM,GAAG,OAAO,MAAM;AAAA,IAC5D;AAEA,UAAM,EAAE,OAAO,MAAM,IAAI,MAAM,KAAK,aAAa;AACjD,UAAM,SAAyB,MAAM,IAAI,CAAC,UAAU;AAAA,MAClD,MAAM,iBAAiB;AAAA,MACvB,KAAK;AAAA,MACL,UAAU,KAAK;AAAA,IACjB,EAAE;AACF,WAAO,EAAE,QAAQ,SAAS,OAAO,MAAM;AAAA,EACzC;AAAA,EAEA,MAAc,eAA6D;AACzE,UAAM,WAAW,KAAK;AACtB,QAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,aAAO,EAAE,OAAO,UAAU,OAAO,KAAK;AAAA,IACxC;AACA,QAAI,OAAO,aAAa,UAAU;AAChC,UAAI,MAAM,YAAY,QAAQ,GAAG;AAC/B,cAAM,UAAU,KAAK,QAAQ;AAC7B,cAAM,UAAU,MAAM,SAAS,SAAS,EAAE,KAAK,UAAU,WAAW,MAAM,UAAU,KAAK,CAAC;AAC1F,eAAO,EAAE,OAAO,QAAQ,KAAK,GAAG,OAAO,KAAK;AAAA,MAC9C;AACA,aAAO,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,MAAM;AAAA,IAC3C;AACA,WAAO,EAAE,OAAO,CAAC,GAAG,OAAO,MAAM;AAAA,EACnC;AACF;AAEA,eAAe,YAAY,MAAgC;AACzD,MAAI;AACF,UAAM,QAAQ,MAAM,KAAK,IAAI;AAC7B,WAAO,MAAM,YAAY;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,OAAwB;AAC5C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xberg-io/langchain-xberg",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "LangChain.js document loader for Xberg — extract text, tables, and metadata from
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "LangChain.js document loader for Xberg — extract text, tables, and metadata from 107 document formats with optional OCR.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"langchain",
|
|
7
7
|
"langchainjs",
|
|
@@ -38,16 +38,14 @@
|
|
|
38
38
|
"require": "./dist/index.cjs"
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
|
-
"files": [
|
|
42
|
-
"dist"
|
|
43
|
-
],
|
|
41
|
+
"files": ["dist"],
|
|
44
42
|
"scripts": {
|
|
45
43
|
"build": "tsup",
|
|
46
44
|
"typecheck": "tsc --noEmit",
|
|
47
45
|
"test": "vitest run"
|
|
48
46
|
},
|
|
49
47
|
"dependencies": {
|
|
50
|
-
"@xberg-io/xberg": "1.0
|
|
48
|
+
"@xberg-io/xberg": "1.1.0",
|
|
51
49
|
"fast-glob": "^3.3.2"
|
|
52
50
|
},
|
|
53
51
|
"peerDependencies": {
|