@juspay/neurolink 12.14.21 → 12.15.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +3 -7
  2. package/dist/browser/neurolink.min.js +385 -385
  3. package/dist/core/baseProvider.d.ts +6 -3
  4. package/dist/core/baseProvider.js +7 -3
  5. package/dist/providers/amazonBedrock/client.d.ts +7 -5
  6. package/dist/providers/amazonBedrock/client.js +125 -16
  7. package/dist/providers/cohere.d.ts +2 -2
  8. package/dist/providers/cohere.js +5 -1
  9. package/dist/providers/googleAiStudio/client.d.ts +3 -3
  10. package/dist/providers/googleAiStudio/client.js +6 -2
  11. package/dist/providers/googleVertex/client.d.ts +2 -2
  12. package/dist/providers/googleVertex/client.js +5 -1
  13. package/dist/providers/jina.d.ts +2 -2
  14. package/dist/providers/jina.js +5 -1
  15. package/dist/providers/litellm/client.d.ts +2 -2
  16. package/dist/providers/litellm/client.js +5 -1
  17. package/dist/providers/ollama/client.d.ts +2 -2
  18. package/dist/providers/ollama/client.js +5 -1
  19. package/dist/providers/openAI/client.d.ts +3 -3
  20. package/dist/providers/openAI/client.js +6 -2
  21. package/dist/providers/voyage.d.ts +2 -2
  22. package/dist/providers/voyage.js +5 -1
  23. package/dist/rag/document/MDocument.d.ts +19 -1
  24. package/dist/rag/document/MDocument.js +30 -3
  25. package/dist/rag/document/imageLoader.d.ts +55 -0
  26. package/dist/rag/document/imageLoader.js +183 -0
  27. package/dist/rag/index.d.ts +1 -0
  28. package/dist/rag/index.js +2 -0
  29. package/dist/rag/pipeline/RAGPipeline.d.ts +54 -1
  30. package/dist/rag/pipeline/RAGPipeline.js +358 -1
  31. package/dist/rag/ragIntegration.js +146 -25
  32. package/dist/types/providers.d.ts +21 -1
  33. package/dist/types/rag.d.ts +144 -0
  34. package/dist/types/rag.js +12 -1
  35. package/package.json +2 -1
@@ -194,9 +194,14 @@ export class MDocument {
194
194
  * Generate embeddings for all chunks
195
195
  * @param provider - Embedding provider name
196
196
  * @param modelName - Embedding model name
197
+ * @param imageData - Optional array of image data (Buffer or base64) aligned with chunks,
198
+ * enabling multi-modal embeddings for providers that support them
199
+ * @param mimeType - MIME type of the images (default: "image/png"). Pass an
200
+ * array to declare a per-image MIME type aligned with
201
+ * `imageData`; a plain string applies to every image.
197
202
  * @returns This MDocument instance (for chaining)
198
203
  */
199
- async embed(provider = "openai", modelName = "text-embedding-3-small") {
204
+ async embed(provider = "openai", modelName = "text-embedding-3-small", imageData, mimeType = "image/png") {
200
205
  if (this.state.chunks.length === 0) {
201
206
  logger.warn("[MDocument] No chunks to embed. Call chunk() first.");
202
207
  return this;
@@ -208,14 +213,21 @@ export class MDocument {
208
213
  chunkCount: this.state.chunks.length,
209
214
  provider,
210
215
  model: modelName,
216
+ hasImages: imageData !== undefined && imageData.length > 0,
211
217
  });
212
218
  const embeddingProvider = await ProviderFactory.createProvider(provider, modelName);
213
219
  if (typeof embeddingProvider.embed !== "function") {
214
220
  throw new Error(`Provider ${provider} does not support embeddings`);
215
221
  }
216
222
  this.state.embeddings = [];
217
- for (const chunk of this.state.chunks) {
218
- const embedding = await embeddingProvider.embed(chunk.text);
223
+ for (let i = 0; i < this.state.chunks.length; i++) {
224
+ const chunk = this.state.chunks[i];
225
+ const image = imageData?.[i];
226
+ const imageMimeType = typeof mimeType === "string" ? mimeType : (mimeType[i] ?? "image/png");
227
+ const input = image
228
+ ? { text: chunk.text, image, mimeType: imageMimeType }
229
+ : chunk.text;
230
+ const embedding = await embeddingProvider.embed(input);
219
231
  this.state.embeddings.push(embedding);
220
232
  chunk.embedding = embedding;
221
233
  }
@@ -227,6 +239,21 @@ export class MDocument {
227
239
  });
228
240
  return this;
229
241
  }
242
+ /**
243
+ * Generate multi-modal embeddings for all chunks using text + optional image data.
244
+ * Uses a provider that supports multi-modal embeddings (e.g. Bedrock Titan Image).
245
+ *
246
+ * @param provider - Embedding provider name
247
+ * @param modelName - Embedding model name
248
+ * @param imageData - Optional array of image data (Buffer or base64) aligned with chunks
249
+ * @param mimeType - MIME type of the images (default: "image/png"). Pass an
250
+ * array to declare a per-image MIME type aligned with
251
+ * `imageData`; a plain string applies to every image.
252
+ * @returns This MDocument instance (for chaining)
253
+ */
254
+ async embedMultiModal(provider = "bedrock", modelName = "amazon.titan-embed-image-v1", imageData, mimeType = "image/png") {
255
+ return this.embed(provider, modelName, imageData, mimeType);
256
+ }
230
257
  // ============================================================================
231
258
  // Accessor Methods
232
259
  // ============================================================================
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Image Loader for Multi-Modal RAG
3
+ *
4
+ * Loads image files (from disk or URLs) into ImageDocument objects
5
+ * that can be embedded and stored in a vector store alongside text chunks.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { ImageLoader } from './imageLoader.js';
10
+ *
11
+ * const loader = new ImageLoader();
12
+ *
13
+ * // Load from file path
14
+ * const doc = await loader.load('./photo.jpg');
15
+ *
16
+ * // Load from URL
17
+ * const doc2 = await loader.load('https://example.com/image.png');
18
+ *
19
+ * // Load from buffer
20
+ * const doc3 = loader.loadFromBuffer(imageBuffer, 'image/jpeg');
21
+ * ```
22
+ */
23
+ import type { ImageDocument, ImageLoaderOptions } from "../../types/index.js";
24
+ /**
25
+ * ImageLoader for multi-modal RAG.
26
+ * Loads images from file paths, URLs, or buffers into ImageDocument objects.
27
+ */
28
+ export declare class ImageLoader {
29
+ private options;
30
+ constructor(options?: ImageLoaderOptions);
31
+ /**
32
+ * Load an image from a file path or URL.
33
+ */
34
+ load(source: string, options?: Partial<ImageLoaderOptions>): Promise<ImageDocument>;
35
+ /**
36
+ * Load an image from a local file path.
37
+ */
38
+ private loadFromPath;
39
+ /**
40
+ * Load an image from a URL.
41
+ */
42
+ private loadFromURL;
43
+ /**
44
+ * Load an image directly from a Buffer.
45
+ */
46
+ loadFromBuffer(buffer: Buffer, mimeType: string, source?: string, options?: Partial<ImageLoaderOptions>): ImageDocument;
47
+ /**
48
+ * Check if a file path or extension is a supported image format.
49
+ */
50
+ static isImageFile(source: string): boolean;
51
+ /**
52
+ * Get the MIME type for a file extension.
53
+ */
54
+ static getMimeType(filePath: string): string | undefined;
55
+ }
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Image Loader for Multi-Modal RAG
3
+ *
4
+ * Loads image files (from disk or URLs) into ImageDocument objects
5
+ * that can be embedded and stored in a vector store alongside text chunks.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { ImageLoader } from './imageLoader.js';
10
+ *
11
+ * const loader = new ImageLoader();
12
+ *
13
+ * // Load from file path
14
+ * const doc = await loader.load('./photo.jpg');
15
+ *
16
+ * // Load from URL
17
+ * const doc2 = await loader.load('https://example.com/image.png');
18
+ *
19
+ * // Load from buffer
20
+ * const doc3 = loader.loadFromBuffer(imageBuffer, 'image/jpeg');
21
+ * ```
22
+ */
23
+ import { readFileSync, existsSync, statSync } from "fs";
24
+ import { extname } from "path";
25
+ import { logger } from "../../utils/logger.js";
26
+ import { safeDownload } from "../../utils/safeFetch.js";
27
+ import { redactUrlForError } from "../../utils/logSanitize.js";
28
+ import { ImageProcessor, VALID_IMAGE_EXTENSIONS, } from "../../utils/imageProcessor.js";
29
+ import { EXTENSION_MIME_MAP } from "../../processors/config/index.js";
30
+ /**
31
+ * Generate a text representation from the image source.
32
+ */
33
+ function generateTextDescription(source) {
34
+ // Drop any query string or fragment before deriving the name. On a presigned
35
+ // URL the signature lives in the query, and the query is part of the final
36
+ // slash-separated segment — so `.pop()` alone keeps it, and this caption is
37
+ // not a transient log line: it becomes embedded, BM25-indexed chunk text that
38
+ // persists in the vector store and is read back into model context.
39
+ const withoutQueryOrFragment = source.split(/[?#]/)[0];
40
+ const filename = withoutQueryOrFragment.split("/").pop() ?? source;
41
+ // Remove extension and clean up
42
+ const name = filename
43
+ .replace(/\.[^.]+$/, "")
44
+ .replace(/[-_]/g, " ")
45
+ .replace(/\s+/g, " ")
46
+ .trim();
47
+ return `Image: ${name}`;
48
+ }
49
+ /**
50
+ * ImageLoader for multi-modal RAG.
51
+ * Loads images from file paths, URLs, or buffers into ImageDocument objects.
52
+ */
53
+ export class ImageLoader {
54
+ options;
55
+ constructor(options) {
56
+ this.options = {
57
+ metadata: options?.metadata ?? {},
58
+ maxImageSize: options?.maxImageSize ?? 10 * 1024 * 1024, // 10MB
59
+ fetchTimeout: options?.fetchTimeout ?? 30_000,
60
+ headers: options?.headers ?? {},
61
+ };
62
+ }
63
+ /**
64
+ * Load an image from a file path or URL.
65
+ */
66
+ async load(source, options) {
67
+ const opts = { ...this.options, ...options };
68
+ if (source.startsWith("http://") || source.startsWith("https://")) {
69
+ return this.loadFromURL(source, opts);
70
+ }
71
+ return this.loadFromPath(source, opts);
72
+ }
73
+ /**
74
+ * Load an image from a local file path.
75
+ */
76
+ async loadFromPath(filePath, options) {
77
+ if (!existsSync(filePath)) {
78
+ throw new Error(`Image file not found: ${filePath}`);
79
+ }
80
+ const { size } = statSync(filePath);
81
+ if (size > options.maxImageSize) {
82
+ throw new Error(`Image size (${size} bytes) exceeds maximum (${options.maxImageSize} bytes)`);
83
+ }
84
+ const buffer = readFileSync(filePath);
85
+ const ext = extname(filePath).toLowerCase();
86
+ const mimeType = EXTENSION_MIME_MAP[ext] ?? ImageProcessor.detectImageType(buffer);
87
+ const dimensions = ImageProcessor.getImageDimensions(buffer);
88
+ logger.debug("[ImageLoader] Loaded image from path", {
89
+ path: filePath,
90
+ size: buffer.byteLength,
91
+ mimeType,
92
+ dimensions,
93
+ });
94
+ return {
95
+ id: `img-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
96
+ text: generateTextDescription(filePath),
97
+ image: buffer,
98
+ mimeType,
99
+ metadata: {
100
+ source: filePath,
101
+ width: dimensions?.width,
102
+ height: dimensions?.height,
103
+ format: ext.replace(".", ""),
104
+ size: buffer.byteLength,
105
+ ...options.metadata,
106
+ },
107
+ };
108
+ }
109
+ /**
110
+ * Load an image from a URL.
111
+ */
112
+ async loadFromURL(url, options) {
113
+ const buffer = await safeDownload(url, {
114
+ maxBytes: options.maxImageSize,
115
+ label: "RAG image",
116
+ timeoutMs: options.fetchTimeout,
117
+ });
118
+ const mimeType = ImageProcessor.detectImageType(buffer);
119
+ const dimensions = ImageProcessor.getImageDimensions(buffer);
120
+ logger.debug("[ImageLoader] Loaded image from URL", {
121
+ url: redactUrlForError(url),
122
+ size: buffer.byteLength,
123
+ mimeType,
124
+ dimensions,
125
+ });
126
+ return {
127
+ id: `img-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
128
+ text: generateTextDescription(url),
129
+ image: buffer,
130
+ mimeType,
131
+ metadata: {
132
+ // Redacted for the same reason as the caption above: `source` is
133
+ // persisted into the vector store and returned verbatim to the model
134
+ // as the RAG tool's citation, so a signed URL stored here outlives the
135
+ // request that fetched it.
136
+ source: redactUrlForError(url),
137
+ width: dimensions?.width,
138
+ height: dimensions?.height,
139
+ format: mimeType.split("/").at(1),
140
+ size: buffer.byteLength,
141
+ ...options.metadata,
142
+ },
143
+ };
144
+ }
145
+ /**
146
+ * Load an image directly from a Buffer.
147
+ */
148
+ loadFromBuffer(buffer, mimeType, source, options) {
149
+ const opts = { ...this.options, ...options };
150
+ if (buffer.byteLength > opts.maxImageSize) {
151
+ throw new Error(`Image size (${buffer.byteLength} bytes) exceeds maximum (${opts.maxImageSize} bytes)`);
152
+ }
153
+ const dimensions = ImageProcessor.getImageDimensions(buffer);
154
+ return {
155
+ id: `img-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
156
+ text: generateTextDescription(source ?? "inline-image"),
157
+ image: buffer,
158
+ mimeType,
159
+ metadata: {
160
+ source: source ?? "inline",
161
+ width: dimensions?.width,
162
+ height: dimensions?.height,
163
+ format: mimeType.split("/").at(1),
164
+ size: buffer.byteLength,
165
+ ...opts.metadata,
166
+ },
167
+ };
168
+ }
169
+ /**
170
+ * Check if a file path or extension is a supported image format.
171
+ */
172
+ static isImageFile(source) {
173
+ const ext = extname(source).toLowerCase().replace(/^\./, "");
174
+ return VALID_IMAGE_EXTENSIONS.includes(ext);
175
+ }
176
+ /**
177
+ * Get the MIME type for a file extension.
178
+ */
179
+ static getMimeType(filePath) {
180
+ const ext = extname(filePath).toLowerCase();
181
+ return EXTENSION_MIME_MAP[ext];
182
+ }
183
+ }
@@ -48,6 +48,7 @@ export { ChunkerRegistry as ChunkerRegistryV2, chunkerRegistry, getAvailableChun
48
48
  export * from "./chunkers/index.js";
49
49
  export { CharacterChunker, ChunkerRegistry, chunkText, HTMLChunker, JSONChunker, LaTeXChunker, MarkdownChunker, RecursiveChunker, SemanticChunker, SentenceChunker, TokenChunker, } from "./chunking/index.js";
50
50
  export { CSVLoader, HTMLLoader, JSONLoader, loadDocument, loadDocuments, MarkdownLoader, MDocument, PDFLoader, TextLoader, WebLoader, } from "./document/index.js";
51
+ export { ImageLoader } from "./document/imageLoader.js";
51
52
  export * from "./errors/index.js";
52
53
  export { GraphRAG } from "./graphRag/index.js";
53
54
  export { createMetadataExtractor, extractMetadata, getAvailableExtractors, getAvailableExtractorTypes, getExtractor, getExtractorDefaultConfig, getExtractorMetadata, getRegisteredExtractorMetadata, LLMMetadataExtractor, MetadataExtractorFactory, MetadataExtractorRegistry, metadataExtractorFactory, metadataExtractorRegistry, } from "./metadata/index.js";
package/dist/rag/index.js CHANGED
@@ -52,6 +52,8 @@ export * from "./chunkers/index.js";
52
52
  export { CharacterChunker, ChunkerRegistry, chunkText, HTMLChunker, JSONChunker, LaTeXChunker, MarkdownChunker, RecursiveChunker, SemanticChunker, SentenceChunker, TokenChunker, } from "./chunking/index.js";
53
53
  // Document Processing
54
54
  export { CSVLoader, HTMLLoader, JSONLoader, loadDocument, loadDocuments, MarkdownLoader, MDocument, PDFLoader, TextLoader, WebLoader, } from "./document/index.js";
55
+ // Image Loader for multi-modal RAG
56
+ export { ImageLoader } from "./document/imageLoader.js";
55
57
  // Error handling
56
58
  export * from "./errors/index.js";
57
59
  // Graph RAG
@@ -25,7 +25,7 @@
25
25
  * console.log(response.answer, response.sources);
26
26
  * ```
27
27
  */
28
- import type { RAGPipelineConfig, IngestOptions, QueryOptions, RAGResponse, PipelineStats } from "../../types/index.js";
28
+ import type { RAGPipelineConfig, IngestOptions, QueryOptions, RAGResponse, PipelineStats, MultiModalRAGConfig, MultiModalQuery, MultiModalSearchResult } from "../../types/index.js";
29
29
  import { MDocument } from "../document/MDocument.js";
30
30
  export declare class RAGPipeline {
31
31
  private id;
@@ -34,10 +34,21 @@ export declare class RAGPipeline {
34
34
  private bm25Index;
35
35
  private graphRAG;
36
36
  private embeddingProvider?;
37
+ private multiModalEmbeddingProvider?;
37
38
  private generationProvider?;
39
+ /**
40
+ * Vision provider for image captioning, when `multiModal.captionProvider` /
41
+ * `captionModel` name one. Undefined means captioning uses
42
+ * {@link generationProvider} — captioning has always run on that provider,
43
+ * and these two fields were declared but read nowhere, so a caller who set
44
+ * them got the pipeline's generation model with no indication otherwise.
45
+ */
46
+ private captionProvider?;
38
47
  private hybridSearch?;
39
48
  private documents;
40
49
  private allChunks;
50
+ private multiModalConfig?;
51
+ private imageLoader;
41
52
  constructor(config: RAGPipelineConfig);
42
53
  /**
43
54
  * Initialize the pipeline (lazy loading of providers)
@@ -73,6 +84,47 @@ export declare class RAGPipeline {
73
84
  * Clear all indexed data
74
85
  */
75
86
  clear(): Promise<void>;
87
+ /**
88
+ * Ingest images into the pipeline for multi-modal RAG.
89
+ * Loads images, generates embeddings via the configured multi-modal provider,
90
+ * and stores them alongside text chunks in the vector store.
91
+ *
92
+ * @param sources - Array of image file paths, URLs, or Buffer objects
93
+ * @param options - Ingestion options
94
+ */
95
+ ingestImages(sources: Array<string | Buffer>, options?: IngestOptions): Promise<{
96
+ imagesProcessed: number;
97
+ chunksCreated: number;
98
+ }>;
99
+ /**
100
+ * Query the pipeline with multi-modal input (text, image, or both).
101
+ *
102
+ * @param query - Text, image, or combined query
103
+ * @param options - Query options
104
+ * @returns Array of multi-modal search results
105
+ */
106
+ queryMultiModal(query: MultiModalQuery, options?: QueryOptions): Promise<MultiModalSearchResult[]>;
107
+ /**
108
+ * Get multi-modal pipeline statistics
109
+ */
110
+ getMultiModalStats(): {
111
+ totalImages: number;
112
+ totalTextChunks: number;
113
+ multiModalEnabled: boolean;
114
+ };
115
+ /**
116
+ * Generate a multi-modal embedding for text, image, or both.
117
+ * Uses the configured multi-modal embedding provider (e.g. Bedrock Titan Image).
118
+ */
119
+ private generateMultiModalEmbedding;
120
+ /**
121
+ * Generate a caption for an image using the generation provider's vision model.
122
+ */
123
+ private generateImageCaption;
124
+ /**
125
+ * Diversify multi-modal results to ensure a mix of text and image results.
126
+ */
127
+ private diversifyMultiModalResults;
76
128
  /**
77
129
  * Ensure pipeline is initialized
78
130
  */
@@ -102,4 +154,5 @@ export declare function createRAGPipeline(options: {
102
154
  generationModel?: string;
103
155
  enableHybrid?: boolean;
104
156
  enableGraph?: boolean;
157
+ multiModal?: MultiModalRAGConfig;
105
158
  }): RAGPipeline;