@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.
- package/CHANGELOG.md +3 -7
- package/dist/browser/neurolink.min.js +385 -385
- package/dist/core/baseProvider.d.ts +6 -3
- package/dist/core/baseProvider.js +7 -3
- package/dist/providers/amazonBedrock/client.d.ts +7 -5
- package/dist/providers/amazonBedrock/client.js +125 -16
- package/dist/providers/cohere.d.ts +2 -2
- package/dist/providers/cohere.js +5 -1
- package/dist/providers/googleAiStudio/client.d.ts +3 -3
- package/dist/providers/googleAiStudio/client.js +6 -2
- package/dist/providers/googleVertex/client.d.ts +2 -2
- package/dist/providers/googleVertex/client.js +5 -1
- package/dist/providers/jina.d.ts +2 -2
- package/dist/providers/jina.js +5 -1
- package/dist/providers/litellm/client.d.ts +2 -2
- package/dist/providers/litellm/client.js +5 -1
- package/dist/providers/ollama/client.d.ts +2 -2
- package/dist/providers/ollama/client.js +5 -1
- package/dist/providers/openAI/client.d.ts +3 -3
- package/dist/providers/openAI/client.js +6 -2
- package/dist/providers/voyage.d.ts +2 -2
- package/dist/providers/voyage.js +5 -1
- package/dist/rag/document/MDocument.d.ts +19 -1
- package/dist/rag/document/MDocument.js +30 -3
- package/dist/rag/document/imageLoader.d.ts +55 -0
- package/dist/rag/document/imageLoader.js +183 -0
- package/dist/rag/index.d.ts +1 -0
- package/dist/rag/index.js +2 -0
- package/dist/rag/pipeline/RAGPipeline.d.ts +54 -1
- package/dist/rag/pipeline/RAGPipeline.js +358 -1
- package/dist/rag/ragIntegration.js +146 -25
- package/dist/types/providers.d.ts +21 -1
- package/dist/types/rag.d.ts +144 -0
- package/dist/types/rag.js +12 -1
- package/package.json +2 -1
|
@@ -3,8 +3,11 @@ import { extname, resolve } from "path";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { SpanSerializer, SpanType, SpanStatus, getMetricsAggregator, } from "../observability/index.js";
|
|
5
5
|
import { logger } from "../utils/logger.js";
|
|
6
|
+
import { redactUrlForError } from "../utils/logSanitize.js";
|
|
6
7
|
import { createChunker } from "./ChunkerFactory.js";
|
|
7
8
|
import { createVectorQueryTool, InMemoryVectorStore, } from "./retrieval/vectorQueryTool.js";
|
|
9
|
+
import { ImageLoader } from "./document/imageLoader.js";
|
|
10
|
+
import { IMAGE_EXTENSIONS } from "../processors/config/index.js";
|
|
8
11
|
import { withSpan } from "../telemetry/withSpan.js";
|
|
9
12
|
import { tracers } from "../telemetry/tracers.js";
|
|
10
13
|
/**
|
|
@@ -43,6 +46,8 @@ function detectStrategy(filePath) {
|
|
|
43
46
|
const ext = extname(filePath).toLowerCase();
|
|
44
47
|
return EXTENSION_TO_STRATEGY[ext] || "recursive";
|
|
45
48
|
}
|
|
49
|
+
/** Embedding dimension used by {@link generateSimpleEmbedding}. */
|
|
50
|
+
const EMBEDDING_DIMENSION = 128;
|
|
46
51
|
/**
|
|
47
52
|
* Simple hash function for strings (FNV-1a variant).
|
|
48
53
|
* Maps a word to a bucket index deterministically.
|
|
@@ -177,14 +182,62 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
177
182
|
if (!files || files.length === 0) {
|
|
178
183
|
throw new Error("RAG config requires at least one file path in 'files'");
|
|
179
184
|
}
|
|
180
|
-
// 1. Load files
|
|
185
|
+
// 1. Load files — separate text and image files
|
|
181
186
|
const fileContents = [];
|
|
187
|
+
const imageFiles = [];
|
|
182
188
|
for (const filePath of files) {
|
|
189
|
+
// HTTP(S) sources are handled by ImageLoader directly and must not go
|
|
190
|
+
// through resolve()/existsSync(), which would mangle the URL into a
|
|
191
|
+
// bogus local path and always report it as missing.
|
|
192
|
+
if (/^https?:\/\//i.test(filePath)) {
|
|
193
|
+
// `new URL()` throws on a string the guarding regex still accepts —
|
|
194
|
+
// "https://" is the minimal case. Uncaught, it leaves this loop, is
|
|
195
|
+
// rethrown by prepareRAGTool, and disables RAG for the WHOLE call, so
|
|
196
|
+
// one bad entry discards every file already loaded. Skip the source
|
|
197
|
+
// instead, matching how an unreadable local file is handled below.
|
|
198
|
+
let ext;
|
|
199
|
+
try {
|
|
200
|
+
ext = extname(new URL(filePath).pathname).toLowerCase();
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
logger.warn(`[RAG] Malformed URL source, skipping: ${redactUrlForError(filePath)}`);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (ext === ".svg") {
|
|
207
|
+
// IMAGE_EXTENSIONS includes .svg, but every consumer downstream of
|
|
208
|
+
// here treats an image as raster bytes: ImageLoader returns the file
|
|
209
|
+
// verbatim, captioning base64s it into a vision call, and the Bedrock
|
|
210
|
+
// embed path ships it as-is — Nova rejects the format outright and
|
|
211
|
+
// Titan accepts markup it cannot embed meaningfully. Skipping is
|
|
212
|
+
// honest; supporting it needs a rasterize-or-sanitize step in
|
|
213
|
+
// ImageLoader, not a classification change here.
|
|
214
|
+
logger.warn(`[RAG] SVG is not supported as a RAG image source, skipping: ${redactUrlForError(filePath)}`);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (IMAGE_EXTENSIONS.includes(ext)) {
|
|
218
|
+
imageFiles.push({ path: filePath, ext });
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
logger.warn(`[RAG] Unsupported URL source, skipping: ${redactUrlForError(filePath)}`);
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
183
225
|
const resolvedPath = resolve(filePath);
|
|
184
226
|
if (!existsSync(resolvedPath)) {
|
|
185
227
|
logger.warn(`[RAG] File not found, skipping: ${resolvedPath}`);
|
|
186
228
|
continue;
|
|
187
229
|
}
|
|
230
|
+
// Detect image files by extension
|
|
231
|
+
const ext = extname(resolvedPath).toLowerCase();
|
|
232
|
+
if (ext === ".svg") {
|
|
233
|
+
// See the URL branch above for why SVG is excluded from the raster path.
|
|
234
|
+
logger.warn(`[RAG] SVG is not supported as a RAG image source, skipping: ${resolvedPath}`);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (IMAGE_EXTENSIONS.includes(ext)) {
|
|
238
|
+
imageFiles.push({ path: resolvedPath, ext });
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
188
241
|
try {
|
|
189
242
|
const content = readFileSync(resolvedPath, "utf-8");
|
|
190
243
|
const strategy = userStrategy || detectStrategy(resolvedPath);
|
|
@@ -195,11 +248,12 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
195
248
|
}
|
|
196
249
|
}
|
|
197
250
|
// Auto-increase topK for multi-file scenarios to ensure coverage
|
|
198
|
-
// (computed after loading so it reflects only files that actually exist)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
251
|
+
// (computed after loading so it reflects only files that actually exist).
|
|
252
|
+
// Image files count toward the file total so image-only RAG gets a topK
|
|
253
|
+
// that covers every source.
|
|
254
|
+
const totalSources = fileContents.length + imageFiles.length;
|
|
255
|
+
const topK = totalSources > 1 ? Math.max(userTopK, totalSources * 3) : userTopK;
|
|
256
|
+
if (fileContents.length === 0 && imageFiles.length === 0) {
|
|
203
257
|
throw new Error("RAG: No files could be loaded. Check that file paths exist and are readable.");
|
|
204
258
|
}
|
|
205
259
|
logger.info(`[RAG] Loaded ${fileContents.length} files for indexing`);
|
|
@@ -230,9 +284,44 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
230
284
|
});
|
|
231
285
|
}
|
|
232
286
|
}
|
|
233
|
-
logger.info(`[RAG] Created ${allChunks.length} chunks from ${fileContents.length} files`);
|
|
287
|
+
logger.info(`[RAG] Created ${allChunks.length} chunks from ${fileContents.length} text files`);
|
|
288
|
+
// 2b. Load image files (multi-modal RAG).
|
|
289
|
+
//
|
|
290
|
+
// Deliberately NOT embedded here. Images are vectorised below through the
|
|
291
|
+
// same `embedFn` as the text chunks, because index-wide a single embedding
|
|
292
|
+
// space is the whole precondition for similarity to mean anything. Embedding
|
|
293
|
+
// them here — as this path originally did, with a hash of the caption —
|
|
294
|
+
// produced a 128-dimension image vector sitting in an index whose text
|
|
295
|
+
// vectors came from a provider at 768 or 1536, which is not a worse ranking
|
|
296
|
+
// but an incomparable one.
|
|
297
|
+
const imageChunks = [];
|
|
298
|
+
if (imageFiles.length > 0) {
|
|
299
|
+
const imageLoader = new ImageLoader();
|
|
300
|
+
for (const { path: imgPath } of imageFiles) {
|
|
301
|
+
try {
|
|
302
|
+
const imageDoc = await imageLoader.load(imgPath);
|
|
303
|
+
// Use filename-based text representation for simple embedding
|
|
304
|
+
const imageText = imageDoc.text;
|
|
305
|
+
imageChunks.push({
|
|
306
|
+
id: `img-chunk-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
307
|
+
text: imageText,
|
|
308
|
+
metadata: {
|
|
309
|
+
text: imageText,
|
|
310
|
+
source: imgPath,
|
|
311
|
+
hasImage: true,
|
|
312
|
+
mimeType: imageDoc.mimeType,
|
|
313
|
+
imageWidth: imageDoc.metadata.width,
|
|
314
|
+
imageHeight: imageDoc.metadata.height,
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
logger.warn(`[RAG] Failed to load image: ${imgPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
logger.info(`[RAG] Loaded ${imageChunks.length} image files for multi-modal RAG`);
|
|
323
|
+
}
|
|
234
324
|
// 3. Generate embeddings and store in vector store
|
|
235
|
-
const EMBEDDING_DIMENSION = 128;
|
|
236
325
|
const vectorStore = new InMemoryVectorStore();
|
|
237
326
|
const indexName = "rag-index";
|
|
238
327
|
// When the caller configured an embedding provider/model, embed BOTH the
|
|
@@ -262,9 +351,19 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
262
351
|
logger.warn("[RAG] Failed to create embedding provider; falling back to hash embeddings", { error: error instanceof Error ? error.message : String(error) });
|
|
263
352
|
}
|
|
264
353
|
}
|
|
354
|
+
// Text and image captions embed together, in one call and under one
|
|
355
|
+
// fallback. Splitting them into two try/catch blocks would let the text side
|
|
356
|
+
// succeed on the provider while the image side fell back to the hash, which
|
|
357
|
+
// is the mixed-space index this is trying to prevent.
|
|
265
358
|
let chunkVectors;
|
|
359
|
+
let imageVectors;
|
|
266
360
|
try {
|
|
267
|
-
|
|
361
|
+
const allVectors = await Promise.all([
|
|
362
|
+
...allChunks.map((chunk) => embedFn(chunk.text)),
|
|
363
|
+
...imageChunks.map((chunk) => embedFn(chunk.text)),
|
|
364
|
+
]);
|
|
365
|
+
chunkVectors = allVectors.slice(0, allChunks.length);
|
|
366
|
+
imageVectors = allVectors.slice(allChunks.length);
|
|
268
367
|
}
|
|
269
368
|
catch (error) {
|
|
270
369
|
// One failed chunk must not leave a mixed-space index — flip the whole
|
|
@@ -272,17 +371,36 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
272
371
|
logger.warn("[RAG] Provider embedding failed mid-index; falling back to hash embeddings for index and queries", { error: error instanceof Error ? error.message : String(error) });
|
|
273
372
|
embedFn = (text) => Promise.resolve(generateSimpleEmbedding(text, EMBEDDING_DIMENSION));
|
|
274
373
|
chunkVectors = allChunks.map((chunk) => generateSimpleEmbedding(chunk.text, EMBEDDING_DIMENSION));
|
|
374
|
+
imageVectors = imageChunks.map((chunk) => generateSimpleEmbedding(chunk.text, EMBEDDING_DIMENSION));
|
|
375
|
+
}
|
|
376
|
+
const items = [
|
|
377
|
+
...allChunks.map((chunk, i) => ({
|
|
378
|
+
id: `rag-chunk-${i}`,
|
|
379
|
+
vector: chunkVectors[i],
|
|
380
|
+
metadata: {
|
|
381
|
+
text: chunk.text,
|
|
382
|
+
...chunk.metadata,
|
|
383
|
+
},
|
|
384
|
+
})),
|
|
385
|
+
...imageChunks.map((chunk, i) => ({
|
|
386
|
+
id: chunk.id,
|
|
387
|
+
vector: imageVectors[i],
|
|
388
|
+
metadata: chunk.metadata,
|
|
389
|
+
})),
|
|
390
|
+
];
|
|
391
|
+
// The guard near the top of this function fires when no source was found
|
|
392
|
+
// at all. This one covers the other way to end up with an empty index:
|
|
393
|
+
// sources WERE found and every one of them failed to load — an image over
|
|
394
|
+
// the size ceiling, a corrupt file, an unreachable URL. Each of those is
|
|
395
|
+
// caught and warned individually, so without this the call returns
|
|
396
|
+
// `chunksIndexed: 0` alongside a positive `filesLoaded` and reports
|
|
397
|
+
// success, and the caller gets a silently empty knowledge base.
|
|
398
|
+
if (items.length === 0) {
|
|
399
|
+
throw new Error("RAG: every source failed to load, so nothing was indexed. " +
|
|
400
|
+
"Check the warnings above for the per-source reason.");
|
|
275
401
|
}
|
|
276
|
-
const items = allChunks.map((chunk, i) => ({
|
|
277
|
-
id: `rag-chunk-${i}`,
|
|
278
|
-
vector: chunkVectors[i],
|
|
279
|
-
metadata: {
|
|
280
|
-
text: chunk.text,
|
|
281
|
-
...chunk.metadata,
|
|
282
|
-
},
|
|
283
|
-
}));
|
|
284
402
|
await vectorStore.upsert(indexName, items);
|
|
285
|
-
logger.info(`[RAG] Indexed ${items.length} chunks in vector store`);
|
|
403
|
+
logger.info(`[RAG] Indexed ${items.length} chunks (${allChunks.length} text + ${imageChunks.length} images) in vector store`);
|
|
286
404
|
// 4. Create the search tool
|
|
287
405
|
// Determine embedding provider/model for the query tool
|
|
288
406
|
const provider = embeddingProvider || fallbackProvider || "vertex";
|
|
@@ -315,15 +433,16 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
315
433
|
// Query through the same embedding space the index was built in —
|
|
316
434
|
// provider embeddings when configured (and healthy), else the hash.
|
|
317
435
|
const queryEmbedding = await embedFn(query).catch(() => generateSimpleEmbedding(query, EMBEDDING_DIMENSION));
|
|
318
|
-
// Fetch more candidates than needed so diversity can select across files
|
|
319
|
-
const
|
|
436
|
+
// Fetch more candidates than needed so diversity can select across files and images
|
|
437
|
+
const loadedSources = fileContents.length + imageChunks.length;
|
|
438
|
+
const fetchK = loadedSources > 1 ? topK * 3 : topK;
|
|
320
439
|
const rawResults = await vectorStore.query({
|
|
321
440
|
indexName,
|
|
322
441
|
queryVector: queryEmbedding,
|
|
323
442
|
topK: fetchK,
|
|
324
443
|
});
|
|
325
|
-
// Apply source-file diversity for multi-file RAG
|
|
326
|
-
const results =
|
|
444
|
+
// Apply source-file diversity for multi-file/multi-image RAG
|
|
445
|
+
const results = loadedSources > 1
|
|
327
446
|
? diversifyResults(rawResults, topK)
|
|
328
447
|
: rawResults.slice(0, topK);
|
|
329
448
|
if (results.length === 0) {
|
|
@@ -335,7 +454,8 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
335
454
|
};
|
|
336
455
|
}
|
|
337
456
|
const relevantContext = results
|
|
338
|
-
.map((r, i) => `[${i + 1}] ${r.metadata?.text || r.text || ""}`
|
|
457
|
+
.map((r, i) => `[${i + 1}] ${r.metadata?.text || r.text || ""}` +
|
|
458
|
+
(r.metadata?.hasImage ? " [image]" : ""))
|
|
339
459
|
.join("\n\n");
|
|
340
460
|
span.setAttribute("rag.results_count", results.length);
|
|
341
461
|
return {
|
|
@@ -345,6 +465,7 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
345
465
|
score: r.score,
|
|
346
466
|
source: r.metadata?.source,
|
|
347
467
|
text: (r.metadata?.text || r.text || "").slice(0, 200),
|
|
468
|
+
hasImage: r.metadata?.hasImage ?? false,
|
|
348
469
|
})),
|
|
349
470
|
totalResults: results.length,
|
|
350
471
|
};
|
|
@@ -354,8 +475,8 @@ async function _prepareRAGToolInner(ragConfig, fallbackProvider) {
|
|
|
354
475
|
return {
|
|
355
476
|
tool: aiTool,
|
|
356
477
|
toolName,
|
|
357
|
-
chunksIndexed: allChunks.length,
|
|
358
|
-
filesLoaded: fileContents.length,
|
|
478
|
+
chunksIndexed: allChunks.length + imageChunks.length,
|
|
479
|
+
filesLoaded: fileContents.length + imageFiles.length,
|
|
359
480
|
};
|
|
360
481
|
}
|
|
361
482
|
/** @internal Exported for testing only */
|
|
@@ -28,6 +28,18 @@ export type SupportedModelName = BedrockModels | DeepSeekModels | OpenAIModels |
|
|
|
28
28
|
* Extract provider names from enum
|
|
29
29
|
*/
|
|
30
30
|
export type ProviderName = (typeof AIProviderName)[keyof typeof AIProviderName];
|
|
31
|
+
/**
|
|
32
|
+
* Multi-modal embedding input — accepts text, image, or both.
|
|
33
|
+
* Used by providers that support multi-modal embeddings (e.g. Bedrock Titan Image, Nova Multimodal).
|
|
34
|
+
*/
|
|
35
|
+
export type EmbedInput = {
|
|
36
|
+
/** Text content to embed */
|
|
37
|
+
text?: string;
|
|
38
|
+
/** Image data as Buffer or base64 string */
|
|
39
|
+
image?: Buffer | string;
|
|
40
|
+
/** MIME type of the image (e.g. "image/png", "image/jpeg") */
|
|
41
|
+
mimeType?: string;
|
|
42
|
+
};
|
|
31
43
|
/**
|
|
32
44
|
* Provider status information
|
|
33
45
|
*/
|
|
@@ -741,7 +753,15 @@ export type AIProvider = {
|
|
|
741
753
|
stream(optionsOrPrompt: StreamOptions | string, analysisSchema?: ValidationSchema): Promise<StreamResult>;
|
|
742
754
|
generate(optionsOrPrompt: TextGenerationOptions | string, analysisSchema?: ValidationSchema): Promise<EnhancedGenerateResult | null>;
|
|
743
755
|
gen(optionsOrPrompt: TextGenerationOptions | string, analysisSchema?: ValidationSchema): Promise<EnhancedGenerateResult | null>;
|
|
744
|
-
|
|
756
|
+
/**
|
|
757
|
+
* Generate an embedding vector for text or multi-modal input.
|
|
758
|
+
* Accepts either a plain string (text-only) or an EmbedInput object
|
|
759
|
+
* for multi-modal embeddings (text + image).
|
|
760
|
+
*/
|
|
761
|
+
embed(input: string | EmbedInput, modelName?: string): Promise<number[]>;
|
|
762
|
+
/**
|
|
763
|
+
* Generate embedding vectors for multiple text inputs in batch.
|
|
764
|
+
*/
|
|
745
765
|
embedMany(texts: string[], modelName?: string): Promise<number[][]>;
|
|
746
766
|
setupToolExecutor(sdk: {
|
|
747
767
|
customTools: Map<string, unknown>;
|
package/dist/types/rag.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Tool } from "./tools.js";
|
|
2
|
+
import type { EmbedInput } from "./providers.js";
|
|
2
3
|
/**
|
|
3
4
|
* RAG Type Definitions
|
|
4
5
|
*
|
|
@@ -231,6 +232,8 @@ export type RAGPipelineConfig = {
|
|
|
231
232
|
enableReranking?: boolean;
|
|
232
233
|
/** Reranking model configuration */
|
|
233
234
|
rerankingModel?: EmbeddingModelConfig;
|
|
235
|
+
/** Multi-modal RAG configuration (image + text embeddings) */
|
|
236
|
+
multiModal?: MultiModalRAGConfig;
|
|
234
237
|
};
|
|
235
238
|
/**
|
|
236
239
|
* Ingestion options
|
|
@@ -419,6 +422,18 @@ export type VectorStore = {
|
|
|
419
422
|
includeVectors?: boolean;
|
|
420
423
|
}): Promise<VectorQueryResult[]>;
|
|
421
424
|
};
|
|
425
|
+
/**
|
|
426
|
+
* Structural extension of {@link VectorStore} for stores that can also write
|
|
427
|
+
* vectors via `upsert()` (e.g. {@link InMemoryVectorStore}). Custom stores
|
|
428
|
+
* that expose an `upsert` method satisfy this type without a cast.
|
|
429
|
+
*/
|
|
430
|
+
export type UpsertableVectorStore = VectorStore & {
|
|
431
|
+
upsert(indexName: string, items: Array<{
|
|
432
|
+
id: string;
|
|
433
|
+
vector: number[];
|
|
434
|
+
metadata?: Record<string, unknown>;
|
|
435
|
+
}>): Promise<void>;
|
|
436
|
+
};
|
|
422
437
|
/** Minimal shape of a query result, satisfied by both `pg` and `pglite`. */
|
|
423
438
|
export type PgQueryResult<T = unknown> = {
|
|
424
439
|
rows: T[];
|
|
@@ -1330,3 +1345,132 @@ export type DocumentState = {
|
|
|
1330
1345
|
};
|
|
1331
1346
|
/** Canonical RAG error code. */
|
|
1332
1347
|
export type RAGErrorCode = "RAG_CHUNKING_ERROR" | "RAG_CHUNKING_INVALID_CONFIG" | "RAG_CHUNKING_STRATEGY_NOT_FOUND" | "RAG_CHUNKING_EMPTY_CONTENT" | "RAG_CHUNKING_SIZE_EXCEEDED" | "RAG_METADATA_EXTRACTION_ERROR" | "RAG_METADATA_EXTRACTION_TIMEOUT" | "RAG_METADATA_SCHEMA_INVALID" | "RAG_METADATA_EXTRACTOR_NOT_FOUND" | "RAG_EMBEDDING_ERROR" | "RAG_EMBEDDING_DIMENSION_MISMATCH" | "RAG_EMBEDDING_RATE_LIMIT" | "RAG_EMBEDDING_PROVIDER_ERROR" | "RAG_VECTOR_QUERY_ERROR" | "RAG_VECTOR_QUERY_TIMEOUT" | "RAG_VECTOR_STORE_UNAVAILABLE" | "RAG_VECTOR_STORE_CONNECTION_ERROR" | "RAG_VECTOR_INDEX_NOT_FOUND" | "RAG_RERANKER_ERROR" | "RAG_RERANKER_NOT_FOUND" | "RAG_RERANKER_API_ERROR" | "RAG_GRAPH_ERROR" | "RAG_GRAPH_TRAVERSAL_ERROR" | "RAG_GRAPH_NODE_NOT_FOUND" | "RAG_PIPELINE_ERROR" | "RAG_PIPELINE_STAGE_FAILED" | "RAG_PIPELINE_PARTIAL_FAILURE" | "RAG_CIRCUIT_BREAKER_OPEN" | "RAG_CIRCUIT_BREAKER_HALF_OPEN_LIMIT" | "RAG_OPERATION_TIMEOUT" | "RAG_RETRY_EXHAUSTED" | "RAG_INVALID_CONFIGURATION";
|
|
1348
|
+
/**
|
|
1349
|
+
* Modality of an embedding model
|
|
1350
|
+
*/
|
|
1351
|
+
export type EmbeddingModality = "text" | "image" | "multimodal";
|
|
1352
|
+
/**
|
|
1353
|
+
* Multi-modal embedding model configuration
|
|
1354
|
+
*/
|
|
1355
|
+
export type MultiModalEmbeddingConfig = {
|
|
1356
|
+
/** Provider name (e.g. "bedrock") */
|
|
1357
|
+
provider: string;
|
|
1358
|
+
/** Model name (e.g. "amazon.titan-embed-image-v1") */
|
|
1359
|
+
modelName: string;
|
|
1360
|
+
/** What modalities this model supports */
|
|
1361
|
+
modality: EmbeddingModality;
|
|
1362
|
+
/** Embedding dimension (e.g. 1024 for Titan Image) */
|
|
1363
|
+
dimensions?: number;
|
|
1364
|
+
};
|
|
1365
|
+
/**
|
|
1366
|
+
* Image metadata attached to multi-modal chunks
|
|
1367
|
+
*/
|
|
1368
|
+
export type ImageChunkMetadata = {
|
|
1369
|
+
/** Image width in pixels */
|
|
1370
|
+
width?: number;
|
|
1371
|
+
/** Image height in pixels */
|
|
1372
|
+
height?: number;
|
|
1373
|
+
/** Image MIME type */
|
|
1374
|
+
mimeType?: string;
|
|
1375
|
+
/** Image format (jpeg, png, webp, etc.) */
|
|
1376
|
+
format?: string;
|
|
1377
|
+
/** Original file path or URL */
|
|
1378
|
+
source?: string;
|
|
1379
|
+
/** Whether this chunk contains image data */
|
|
1380
|
+
hasImage: boolean;
|
|
1381
|
+
};
|
|
1382
|
+
/**
|
|
1383
|
+
* Extended chunk type for multi-modal RAG.
|
|
1384
|
+
* Contains optional image data alongside text for image+text search.
|
|
1385
|
+
*/
|
|
1386
|
+
export type MultiModalChunk = Chunk & {
|
|
1387
|
+
/** Image data as Buffer or base64 string (present for image chunks) */
|
|
1388
|
+
image?: Buffer | string;
|
|
1389
|
+
/** Image MIME type */
|
|
1390
|
+
imageMimeType?: string;
|
|
1391
|
+
/** Image-specific metadata */
|
|
1392
|
+
imageMeta?: ImageChunkMetadata;
|
|
1393
|
+
};
|
|
1394
|
+
/**
|
|
1395
|
+
* Multi-modal query input — can be text, image, or both.
|
|
1396
|
+
* Same shape as EmbedInput.
|
|
1397
|
+
*/
|
|
1398
|
+
export type MultiModalQuery = EmbedInput;
|
|
1399
|
+
/**
|
|
1400
|
+
* Multi-modal RAG pipeline configuration extension.
|
|
1401
|
+
* Passed alongside RAGPipelineConfig to enable multi-modal features.
|
|
1402
|
+
*/
|
|
1403
|
+
export type MultiModalRAGConfig = {
|
|
1404
|
+
/** Whether multi-modal RAG is enabled */
|
|
1405
|
+
enabled: boolean;
|
|
1406
|
+
/** Embedding model for multi-modal content */
|
|
1407
|
+
embeddingModel: MultiModalEmbeddingConfig;
|
|
1408
|
+
/** How to generate text from images for indexing: "caption" uses LLM vision, "filename" uses filename, "none" skips text */
|
|
1409
|
+
imageTextStrategy?: "caption" | "filename" | "none";
|
|
1410
|
+
/** Maximum image file size in bytes (default: 10MB) */
|
|
1411
|
+
maxImageSize?: number;
|
|
1412
|
+
/** Supported image MIME types */
|
|
1413
|
+
supportedFormats?: string[];
|
|
1414
|
+
/** Provider for generating image captions (used with imageTextStrategy: "caption") */
|
|
1415
|
+
captionProvider?: string;
|
|
1416
|
+
/** Model for generating image captions */
|
|
1417
|
+
captionModel?: string;
|
|
1418
|
+
};
|
|
1419
|
+
/**
|
|
1420
|
+
* Match type indicating how the result was found
|
|
1421
|
+
*/
|
|
1422
|
+
export type MultiModalMatchType = "text-to-text" | "text-to-image" | "image-to-image" | "image-to-text";
|
|
1423
|
+
/**
|
|
1424
|
+
* Image search result from multi-modal retrieval
|
|
1425
|
+
*/
|
|
1426
|
+
export type MultiModalSearchResult = {
|
|
1427
|
+
/** The chunk containing the match */
|
|
1428
|
+
chunk: MultiModalChunk;
|
|
1429
|
+
/** Similarity score (0-1) */
|
|
1430
|
+
score: number;
|
|
1431
|
+
/** How this match was found */
|
|
1432
|
+
matchType: MultiModalMatchType;
|
|
1433
|
+
};
|
|
1434
|
+
/**
|
|
1435
|
+
* Supported image MIME types for multi-modal RAG
|
|
1436
|
+
*/
|
|
1437
|
+
export declare const SUPPORTED_IMAGE_MIME_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp", "image/bmp", "image/tiff", "image/avif"];
|
|
1438
|
+
/**
|
|
1439
|
+
* Image document loaded for RAG ingestion
|
|
1440
|
+
*/
|
|
1441
|
+
export type ImageDocument = {
|
|
1442
|
+
/** Unique identifier for this image document */
|
|
1443
|
+
id: string;
|
|
1444
|
+
/** Text representation (filename, alt text, or caption) */
|
|
1445
|
+
text: string;
|
|
1446
|
+
/** Raw image data */
|
|
1447
|
+
image: Buffer;
|
|
1448
|
+
/** Image MIME type */
|
|
1449
|
+
mimeType: string;
|
|
1450
|
+
/** Metadata about the image */
|
|
1451
|
+
metadata: {
|
|
1452
|
+
/** Original file path or URL */
|
|
1453
|
+
source: string;
|
|
1454
|
+
/** Image width in pixels (if detectable) */
|
|
1455
|
+
width?: number;
|
|
1456
|
+
/** Image height in pixels (if detectable) */
|
|
1457
|
+
height?: number;
|
|
1458
|
+
/** Image format (jpeg, png, webp, etc.) */
|
|
1459
|
+
format?: string;
|
|
1460
|
+
/** File size in bytes */
|
|
1461
|
+
size: number;
|
|
1462
|
+
};
|
|
1463
|
+
};
|
|
1464
|
+
/**
|
|
1465
|
+
* Options for loading images
|
|
1466
|
+
*/
|
|
1467
|
+
export type ImageLoaderOptions = {
|
|
1468
|
+
/** Custom metadata to attach to the image document */
|
|
1469
|
+
metadata?: Record<string, unknown>;
|
|
1470
|
+
/** Maximum image file size in bytes (default: 10MB) */
|
|
1471
|
+
maxImageSize?: number;
|
|
1472
|
+
/** Timeout for URL fetches in milliseconds (default: 30000) */
|
|
1473
|
+
fetchTimeout?: number;
|
|
1474
|
+
/** Custom headers for URL fetches */
|
|
1475
|
+
headers?: Record<string, string>;
|
|
1476
|
+
};
|
package/dist/types/rag.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.15.0",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
|
|
6
6
|
"author": {
|
|
@@ -92,6 +92,7 @@
|
|
|
92
92
|
"test:media": "pnpm exec tsx test/continuous-test-suite-media-gen.ts",
|
|
93
93
|
"test:media-registry-collisions": "pnpm exec tsx test/continuous-test-suite-media-registry-collisions.ts",
|
|
94
94
|
"test:memory": "pnpm exec tsx test/continuous-test-suite-memory.ts",
|
|
95
|
+
"test:multimodal-rag": "pnpm exec tsx test/continuous-test-suite-multimodal-rag.ts",
|
|
95
96
|
"test:proxy-sharing": "pnpm run build && pnpm exec tsx test/continuous-test-suite-proxy-sharing.ts",
|
|
96
97
|
"test:openai-compat-streaming-retry": "pnpm exec tsx test/continuous-test-suite-openai-compat-streaming-retry.ts",
|
|
97
98
|
"test:anthropic-streaming-retry": "pnpm exec tsx test/continuous-test-suite-anthropic-streaming-retry.ts",
|