@mlx-node/vlm 0.0.6 → 0.0.7
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 +34 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/models/qianfan-ocr.d.ts +27 -0
- package/dist/models/qianfan-ocr.d.ts.map +1 -0
- package/dist/models/qianfan-ocr.js +63 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @mlx-node/vlm
|
|
2
2
|
|
|
3
|
-
Vision-language models and document processing pipelines for Node.js on Apple Silicon. Extract text, tables, and structure from documents and images using PaddleOCR-VL and the PP-StructureV3 pipeline — all running locally on Metal GPU.
|
|
3
|
+
Vision-language models and document processing pipelines for Node.js on Apple Silicon. Extract text, tables, and structure from documents and images using PaddleOCR-VL, Qianfan-OCR (InternVL), and the PP-StructureV3 pipeline — all running locally on Metal GPU.
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
@@ -13,6 +13,39 @@ Vision-language models and document processing pipelines for Node.js on Apple Si
|
|
|
13
13
|
npm install @mlx-node/vlm
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## Multi-turn chat with VLMs
|
|
17
|
+
|
|
18
|
+
`QianfanOCRModel` conforms to the same `ChatSession<M>` surface as the language models in `@mlx-node/lm`, so a single session handle drives both single-shot and multi-turn VLM conversations:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { ChatSession } from '@mlx-node/lm';
|
|
22
|
+
import { QianfanOCRModel } from '@mlx-node/vlm';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const model = await QianfanOCRModel.load('./models/Qianfan-VL');
|
|
26
|
+
const session = new ChatSession(model, { system: 'You read documents precisely.' });
|
|
27
|
+
|
|
28
|
+
// First turn with an image.
|
|
29
|
+
const r1 = await session.send('Extract the text from this receipt.', {
|
|
30
|
+
images: [readFileSync('./receipt.jpg')],
|
|
31
|
+
});
|
|
32
|
+
console.log(r1.text);
|
|
33
|
+
|
|
34
|
+
// Text-only follow-up reuses the same KV cache against the same image.
|
|
35
|
+
const r2 = await session.send('What is the total price?');
|
|
36
|
+
console.log(r2.text);
|
|
37
|
+
|
|
38
|
+
// Swapping the image mid-session forcibly restarts the cache with the new image.
|
|
39
|
+
const r3 = await session.send('And this one?', {
|
|
40
|
+
images: [readFileSync('./other-receipt.jpg')],
|
|
41
|
+
});
|
|
42
|
+
console.log(r3.text);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`loadSession()` from `@mlx-node/lm` cannot load Qianfan-OCR (that would introduce a circular package dependency), so construct the wrapper here with `QianfanOCRModel.load()` and pass it to `ChatSession` directly.
|
|
46
|
+
|
|
47
|
+
The one-shot `VLModel.chat()` API shown below for PaddleOCR-VL is a single-turn OCR entry point and is intentionally kept out of the session surface.
|
|
48
|
+
|
|
16
49
|
## Quick Start
|
|
17
50
|
|
|
18
51
|
### Document Structure Analysis
|
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,9 @@ export { DocOrientationModel, type OrientationResult, type ClassifyRotateResult,
|
|
|
33
33
|
export { StructureV3Pipeline, type StructureV3Config, type AnalyzeOptions, type StructuredElement, type StructuredDocument, type TextLine, } from './pipeline/structure-v3.js';
|
|
34
34
|
export type { VisionConfig, TextConfig, ModelConfig, VlmChatConfig, VlmChatMessage, VlmBatchItem, } from '@mlx-node/core';
|
|
35
35
|
export { PADDLEOCR_VL_CONFIGS, type PaddleOCRVLConfig } from './models/paddleocr-vl-configs.js';
|
|
36
|
+
export { QianfanOCRModel } from './models/qianfan-ocr.js';
|
|
37
|
+
export { createQianfanOcrConfig } from '@mlx-node/core';
|
|
38
|
+
export type { QianfanOcrConfig, InternVisionConfig, Qwen3LmConfig } from '@mlx-node/core';
|
|
36
39
|
export { VlmChatResult, type VLMChatResult } from '@mlx-node/core';
|
|
37
40
|
export { parsePaddleResponse, parseVlmOutput, formatDocument, type ParsedDocument, type DocumentElement, type Table, type TableRow, type TableCell, type Paragraph, type ParserConfig, OutputFormat, } from '@mlx-node/core';
|
|
38
41
|
export { documentToXlsx, saveToXlsx } from '@mlx-node/core';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGlE,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,KAAK,OAAO,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG1F,OAAO,EACL,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,cAAc,EACd,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,QAAQ,GACd,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,GACb,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,oBAAoB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAGhG,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGnE,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG5D,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGlE,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,KAAK,OAAO,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG1F,OAAO,EACL,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,cAAc,EACd,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,QAAQ,GACd,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,GACb,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,oBAAoB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAGhG,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG1F,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGnE,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG5D,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,9 @@ export { DocOrientationModel, DocUnwarpModel, } from '@mlx-node/core';
|
|
|
39
39
|
export { StructureV3Pipeline, } from './pipeline/structure-v3.js';
|
|
40
40
|
// Model-specific configs
|
|
41
41
|
export { PADDLEOCR_VL_CONFIGS } from './models/paddleocr-vl-configs.js';
|
|
42
|
+
// Qianfan-OCR model (InternVL architecture)
|
|
43
|
+
export { QianfanOCRModel } from './models/qianfan-ocr.js';
|
|
44
|
+
export { createQianfanOcrConfig } from '@mlx-node/core';
|
|
42
45
|
// Chat result type
|
|
43
46
|
export { VlmChatResult } from '@mlx-node/core';
|
|
44
47
|
// Output parsing and formatting (Rust implementation)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { QianfanOCRModel as QianfanOCRModelNative } from '@mlx-node/core';
|
|
2
|
+
import type { ChatConfig, ChatMessage } from '@mlx-node/core';
|
|
3
|
+
import type { ChatStreamEvent } from '@mlx-node/lm';
|
|
4
|
+
/**
|
|
5
|
+
* Qianfan-OCR Vision-Language Model wrapper.
|
|
6
|
+
*
|
|
7
|
+
* Streaming is driven through the `ChatSession` API — the overrides
|
|
8
|
+
* below adapt the callback-based native methods to
|
|
9
|
+
* `AsyncGenerator<ChatStreamEvent>` so the wrapper structurally
|
|
10
|
+
* satisfies `SessionCapableModel` from `@mlx-node/lm`.
|
|
11
|
+
*
|
|
12
|
+
* Qianfan-OCR is a VLM (InternViT + Qwen3 language model). The
|
|
13
|
+
* continue path cannot splice new vision features into a live KV
|
|
14
|
+
* cache — image changes always require a fresh session start, which
|
|
15
|
+
* the high-level `ChatSession` wrapper handles via its
|
|
16
|
+
* `lastImagesKey` check.
|
|
17
|
+
*/
|
|
18
|
+
export declare class QianfanOCRModel extends QianfanOCRModelNative {
|
|
19
|
+
static load(modelPath: string): Promise<QianfanOCRModel>;
|
|
20
|
+
/** Streaming variant of {@link QianfanOCRModel#chatSessionStart}. */
|
|
21
|
+
chatStreamSessionStart(messages: ChatMessage[], config?: ChatConfig | null, signal?: AbortSignal): AsyncGenerator<ChatStreamEvent>;
|
|
22
|
+
/** Streaming variant of {@link QianfanOCRModel#chatSessionContinue}. */
|
|
23
|
+
chatStreamSessionContinue(userMessage: string, images: Uint8Array[] | null, config?: ChatConfig | null, signal?: AbortSignal): AsyncGenerator<ChatStreamEvent>;
|
|
24
|
+
/** Streaming variant of {@link QianfanOCRModel#chatSessionContinueTool}. */
|
|
25
|
+
chatStreamSessionContinueTool(toolCallId: string, content: string, config?: ChatConfig | null, signal?: AbortSignal): AsyncGenerator<ChatStreamEvent>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=qianfan-ocr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qianfan-ocr.d.ts","sourceRoot":"","sources":["../../src/models/qianfan-ocr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,eAAe,EAAuB,MAAM,cAAc,CAAC;AAezE;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAgB,SAAQ,qBAAqB;WAClC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAMvE,qEAAqE;IAE9D,sBAAsB,CAC3B,QAAQ,EAAE,WAAW,EAAE,EACvB,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,eAAe,CAAC;IAOlC,wEAAwE;IAEjE,yBAAyB,CAC9B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,EAC3B,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,eAAe,CAAC;IAQlC,4EAA4E;IAErE,6BAA6B,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,eAAe,CAAC;CAOnC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { QianfanOCRModel as QianfanOCRModelNative } from '@mlx-node/core';
|
|
2
|
+
import { _runChatStream } from '@mlx-node/lm';
|
|
3
|
+
// Save references to the native callback-based session streaming methods
|
|
4
|
+
// before we override them. Captured at module load time so the subclass
|
|
5
|
+
// overrides can delegate without recursing into themselves. Each wrapper
|
|
6
|
+
// method below bridges the callback API to `AsyncGenerator<ChatStreamEvent>`
|
|
7
|
+
// so the wrapper structurally satisfies `SessionCapableModel` and can be
|
|
8
|
+
// passed to `ChatSession<QianfanOCRModel>` from `@mlx-node/lm`.
|
|
9
|
+
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
10
|
+
const _nativeQianfanOcrChatStreamSessionStart = QianfanOCRModelNative.prototype.chatStreamSessionStart;
|
|
11
|
+
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
12
|
+
const _nativeQianfanOcrChatStreamSessionContinue = QianfanOCRModelNative.prototype.chatStreamSessionContinue;
|
|
13
|
+
// oxlint-disable-next-line @typescript-eslint/unbound-method
|
|
14
|
+
const _nativeQianfanOcrChatStreamSessionContinueTool = QianfanOCRModelNative.prototype.chatStreamSessionContinueTool;
|
|
15
|
+
/**
|
|
16
|
+
* Qianfan-OCR Vision-Language Model wrapper.
|
|
17
|
+
*
|
|
18
|
+
* Streaming is driven through the `ChatSession` API — the overrides
|
|
19
|
+
* below adapt the callback-based native methods to
|
|
20
|
+
* `AsyncGenerator<ChatStreamEvent>` so the wrapper structurally
|
|
21
|
+
* satisfies `SessionCapableModel` from `@mlx-node/lm`.
|
|
22
|
+
*
|
|
23
|
+
* Qianfan-OCR is a VLM (InternViT + Qwen3 language model). The
|
|
24
|
+
* continue path cannot splice new vision features into a live KV
|
|
25
|
+
* cache — image changes always require a fresh session start, which
|
|
26
|
+
* the high-level `ChatSession` wrapper handles via its
|
|
27
|
+
* `lastImagesKey` check.
|
|
28
|
+
*/
|
|
29
|
+
export class QianfanOCRModel extends QianfanOCRModelNative {
|
|
30
|
+
static async load(modelPath) {
|
|
31
|
+
const instance = await QianfanOCRModelNative.load(modelPath);
|
|
32
|
+
Object.setPrototypeOf(instance, QianfanOCRModel.prototype);
|
|
33
|
+
return instance;
|
|
34
|
+
}
|
|
35
|
+
/** Streaming variant of {@link QianfanOCRModel#chatSessionStart}. */
|
|
36
|
+
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
37
|
+
async *chatStreamSessionStart(messages, config, signal) {
|
|
38
|
+
yield* _runChatStream((callback) => _nativeQianfanOcrChatStreamSessionStart.call(this, messages, config ?? null, callback), signal);
|
|
39
|
+
}
|
|
40
|
+
/** Streaming variant of {@link QianfanOCRModel#chatSessionContinue}. */
|
|
41
|
+
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
42
|
+
async *chatStreamSessionContinue(userMessage, images, config, signal) {
|
|
43
|
+
yield* _runChatStream((callback) => _nativeQianfanOcrChatStreamSessionContinue.call(this, userMessage, images, config ?? null, callback), signal);
|
|
44
|
+
}
|
|
45
|
+
/** Streaming variant of {@link QianfanOCRModel#chatSessionContinueTool}. */
|
|
46
|
+
// @ts-expect-error — override callback-based native method with AsyncGenerator
|
|
47
|
+
async *chatStreamSessionContinueTool(toolCallId, content, config, signal) {
|
|
48
|
+
yield* _runChatStream((callback) => _nativeQianfanOcrChatStreamSessionContinueTool.call(this, toolCallId, content, config ?? null, callback), signal);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// -------------------------------------------------------------------
|
|
52
|
+
// Compile-time conformance check
|
|
53
|
+
// -------------------------------------------------------------------
|
|
54
|
+
//
|
|
55
|
+
// Ensures the wrapper structurally satisfies `SessionCapableModel` so
|
|
56
|
+
// `ChatSession<QianfanOCRModel>` will type-check in downstream code.
|
|
57
|
+
// The assignment is compile-only — the `null as unknown as T`
|
|
58
|
+
// placeholder never runs.
|
|
59
|
+
function _assertSessionCapable() {
|
|
60
|
+
const _qianfan = null;
|
|
61
|
+
void _qianfan;
|
|
62
|
+
}
|
|
63
|
+
void _assertSessionCapable;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mlx-node/vlm",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"homepage": "https://github.com/mlx-node/mlx-node",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/mlx-node/mlx-node/issues"
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"test": "vite test run"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@mlx-node/core": "0.0.
|
|
32
|
-
"@mlx-node/lm": "0.0.
|
|
31
|
+
"@mlx-node/core": "0.0.7",
|
|
32
|
+
"@mlx-node/lm": "0.0.7",
|
|
33
33
|
"@napi-rs/image": "^1.12.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@types/node": "@types/node@
|
|
36
|
+
"@types/node": "@types/node@24.12.2"
|
|
37
37
|
}
|
|
38
38
|
}
|