@juspay/neurolink 7.35.0 → 7.37.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 +12 -0
- package/dist/adapters/providerImageAdapter.d.ts +56 -0
- package/dist/adapters/providerImageAdapter.js +257 -0
- package/dist/cli/commands/config.d.ts +20 -20
- package/dist/cli/factories/commandFactory.d.ts +1 -0
- package/dist/cli/factories/commandFactory.js +26 -3
- package/dist/config/taskClassificationConfig.d.ts +51 -0
- package/dist/config/taskClassificationConfig.js +148 -0
- package/dist/core/baseProvider.js +99 -45
- package/dist/core/types.d.ts +3 -0
- package/dist/lib/adapters/providerImageAdapter.d.ts +56 -0
- package/dist/lib/adapters/providerImageAdapter.js +257 -0
- package/dist/lib/config/taskClassificationConfig.d.ts +51 -0
- package/dist/lib/config/taskClassificationConfig.js +148 -0
- package/dist/lib/core/baseProvider.js +99 -45
- package/dist/lib/core/types.d.ts +3 -0
- package/dist/lib/neurolink.d.ts +20 -0
- package/dist/lib/neurolink.js +276 -8
- package/dist/lib/types/content.d.ts +78 -0
- package/dist/lib/types/content.js +5 -0
- package/dist/lib/types/conversation.d.ts +19 -0
- package/dist/lib/types/generateTypes.d.ts +4 -1
- package/dist/lib/types/index.d.ts +2 -0
- package/dist/lib/types/index.js +2 -0
- package/dist/lib/types/streamTypes.d.ts +6 -3
- package/dist/lib/types/taskClassificationTypes.d.ts +52 -0
- package/dist/lib/types/taskClassificationTypes.js +5 -0
- package/dist/lib/utils/imageProcessor.d.ts +84 -0
- package/dist/lib/utils/imageProcessor.js +362 -0
- package/dist/lib/utils/messageBuilder.d.ts +8 -1
- package/dist/lib/utils/messageBuilder.js +279 -0
- package/dist/lib/utils/modelRouter.d.ts +107 -0
- package/dist/lib/utils/modelRouter.js +292 -0
- package/dist/lib/utils/promptRedaction.d.ts +29 -0
- package/dist/lib/utils/promptRedaction.js +62 -0
- package/dist/lib/utils/taskClassificationUtils.d.ts +55 -0
- package/dist/lib/utils/taskClassificationUtils.js +149 -0
- package/dist/lib/utils/taskClassifier.d.ts +23 -0
- package/dist/lib/utils/taskClassifier.js +94 -0
- package/dist/neurolink.d.ts +20 -0
- package/dist/neurolink.js +276 -8
- package/dist/types/content.d.ts +78 -0
- package/dist/types/content.js +5 -0
- package/dist/types/conversation.d.ts +19 -0
- package/dist/types/generateTypes.d.ts +4 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/streamTypes.d.ts +6 -3
- package/dist/types/taskClassificationTypes.d.ts +52 -0
- package/dist/types/taskClassificationTypes.js +5 -0
- package/dist/utils/imageProcessor.d.ts +84 -0
- package/dist/utils/imageProcessor.js +362 -0
- package/dist/utils/messageBuilder.d.ts +8 -1
- package/dist/utils/messageBuilder.js +279 -0
- package/dist/utils/modelRouter.d.ts +107 -0
- package/dist/utils/modelRouter.js +292 -0
- package/dist/utils/promptRedaction.d.ts +29 -0
- package/dist/utils/promptRedaction.js +62 -0
- package/dist/utils/taskClassificationUtils.d.ts +55 -0
- package/dist/utils/taskClassificationUtils.js +149 -0
- package/dist/utils/taskClassifier.d.ts +23 -0
- package/dist/utils/taskClassifier.js +94 -0
- package/package.json +1 -1
@@ -0,0 +1,84 @@
|
|
1
|
+
/**
|
2
|
+
* Image processing utilities for multimodal support
|
3
|
+
* Handles format conversion for different AI providers
|
4
|
+
*/
|
5
|
+
import type { ProcessedImage } from "../types/content.js";
|
6
|
+
/**
|
7
|
+
* Image processor class for handling provider-specific image formatting
|
8
|
+
*/
|
9
|
+
export declare class ImageProcessor {
|
10
|
+
/**
|
11
|
+
* Process image for OpenAI (requires data URI format)
|
12
|
+
*/
|
13
|
+
static processImageForOpenAI(image: Buffer | string): string;
|
14
|
+
/**
|
15
|
+
* Process image for Google AI (requires base64 without data URI prefix)
|
16
|
+
*/
|
17
|
+
static processImageForGoogle(image: Buffer | string): {
|
18
|
+
mimeType: string;
|
19
|
+
data: string;
|
20
|
+
};
|
21
|
+
/**
|
22
|
+
* Process image for Anthropic (requires base64 without data URI prefix)
|
23
|
+
*/
|
24
|
+
static processImageForAnthropic(image: Buffer | string): {
|
25
|
+
mediaType: string;
|
26
|
+
data: string;
|
27
|
+
};
|
28
|
+
/**
|
29
|
+
* Process image for Vertex AI (model-specific routing)
|
30
|
+
*/
|
31
|
+
static processImageForVertex(image: Buffer | string, model: string): {
|
32
|
+
mimeType?: string;
|
33
|
+
mediaType?: string;
|
34
|
+
data: string;
|
35
|
+
};
|
36
|
+
/**
|
37
|
+
* Detect image type from filename or data
|
38
|
+
*/
|
39
|
+
static detectImageType(input: string | Buffer): string;
|
40
|
+
/**
|
41
|
+
* Validate image size (default 10MB limit)
|
42
|
+
*/
|
43
|
+
static validateImageSize(data: Buffer | string, maxSize?: number): boolean;
|
44
|
+
/**
|
45
|
+
* Validate image format
|
46
|
+
*/
|
47
|
+
static validateImageFormat(mediaType: string): boolean;
|
48
|
+
/**
|
49
|
+
* Get image dimensions from Buffer (basic implementation)
|
50
|
+
*/
|
51
|
+
static getImageDimensions(buffer: Buffer): {
|
52
|
+
width: number;
|
53
|
+
height: number;
|
54
|
+
} | null;
|
55
|
+
/**
|
56
|
+
* Convert image to ProcessedImage format
|
57
|
+
*/
|
58
|
+
static processImage(image: Buffer | string, provider: string, model?: string): ProcessedImage;
|
59
|
+
}
|
60
|
+
/**
|
61
|
+
* Utility functions for image handling
|
62
|
+
*/
|
63
|
+
export declare const imageUtils: {
|
64
|
+
/**
|
65
|
+
* Check if a string is a valid data URI
|
66
|
+
*/
|
67
|
+
isDataUri: (str: string) => boolean;
|
68
|
+
/**
|
69
|
+
* Check if a string is a valid URL
|
70
|
+
*/
|
71
|
+
isUrl: (str: string) => boolean;
|
72
|
+
/**
|
73
|
+
* Check if a string is base64 encoded
|
74
|
+
*/
|
75
|
+
isBase64: (str: string) => boolean;
|
76
|
+
/**
|
77
|
+
* Extract file extension from filename or URL
|
78
|
+
*/
|
79
|
+
getFileExtension: (filename: string) => string | null;
|
80
|
+
/**
|
81
|
+
* Convert file size to human readable format
|
82
|
+
*/
|
83
|
+
formatFileSize: (bytes: number) => string;
|
84
|
+
};
|
@@ -0,0 +1,362 @@
|
|
1
|
+
/**
|
2
|
+
* Image processing utilities for multimodal support
|
3
|
+
* Handles format conversion for different AI providers
|
4
|
+
*/
|
5
|
+
import { logger } from "./logger.js";
|
6
|
+
/**
|
7
|
+
* Image processor class for handling provider-specific image formatting
|
8
|
+
*/
|
9
|
+
export class ImageProcessor {
|
10
|
+
/**
|
11
|
+
* Process image for OpenAI (requires data URI format)
|
12
|
+
*/
|
13
|
+
static processImageForOpenAI(image) {
|
14
|
+
try {
|
15
|
+
if (typeof image === "string") {
|
16
|
+
// Handle URLs
|
17
|
+
if (image.startsWith("http")) {
|
18
|
+
return image;
|
19
|
+
}
|
20
|
+
// Handle data URIs
|
21
|
+
if (image.startsWith("data:")) {
|
22
|
+
return image;
|
23
|
+
}
|
24
|
+
// Handle base64 - convert to data URI
|
25
|
+
return `data:image/jpeg;base64,${image}`;
|
26
|
+
}
|
27
|
+
// Handle Buffer - convert to data URI
|
28
|
+
const base64 = image.toString("base64");
|
29
|
+
return `data:image/jpeg;base64,${base64}`;
|
30
|
+
}
|
31
|
+
catch (error) {
|
32
|
+
logger.error("Failed to process image for OpenAI:", error);
|
33
|
+
throw new Error(`Image processing failed for OpenAI: ${error instanceof Error ? error.message : "Unknown error"}`);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
/**
|
37
|
+
* Process image for Google AI (requires base64 without data URI prefix)
|
38
|
+
*/
|
39
|
+
static processImageForGoogle(image) {
|
40
|
+
try {
|
41
|
+
let base64Data;
|
42
|
+
let mimeType = "image/jpeg"; // Default
|
43
|
+
if (typeof image === "string") {
|
44
|
+
if (image.startsWith("data:")) {
|
45
|
+
// Extract mime type and base64 from data URI
|
46
|
+
const match = image.match(/^data:([^;]+);base64,(.+)$/);
|
47
|
+
if (match) {
|
48
|
+
mimeType = match[1];
|
49
|
+
base64Data = match[2];
|
50
|
+
}
|
51
|
+
else {
|
52
|
+
base64Data = image.split(",")[1] || image;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
else {
|
56
|
+
base64Data = image;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
else {
|
60
|
+
base64Data = image.toString("base64");
|
61
|
+
}
|
62
|
+
return {
|
63
|
+
mimeType,
|
64
|
+
data: base64Data, // Google wants base64 WITHOUT data URI prefix
|
65
|
+
};
|
66
|
+
}
|
67
|
+
catch (error) {
|
68
|
+
logger.error("Failed to process image for Google AI:", error);
|
69
|
+
throw new Error(`Image processing failed for Google AI: ${error instanceof Error ? error.message : "Unknown error"}`);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
/**
|
73
|
+
* Process image for Anthropic (requires base64 without data URI prefix)
|
74
|
+
*/
|
75
|
+
static processImageForAnthropic(image) {
|
76
|
+
try {
|
77
|
+
let base64Data;
|
78
|
+
let mediaType = "image/jpeg"; // Default
|
79
|
+
if (typeof image === "string") {
|
80
|
+
if (image.startsWith("data:")) {
|
81
|
+
// Extract mime type and base64 from data URI
|
82
|
+
const match = image.match(/^data:([^;]+);base64,(.+)$/);
|
83
|
+
if (match) {
|
84
|
+
mediaType = match[1];
|
85
|
+
base64Data = match[2];
|
86
|
+
}
|
87
|
+
else {
|
88
|
+
base64Data = image.split(",")[1] || image;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
base64Data = image;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
else {
|
96
|
+
base64Data = image.toString("base64");
|
97
|
+
}
|
98
|
+
return {
|
99
|
+
mediaType,
|
100
|
+
data: base64Data, // Anthropic wants base64 WITHOUT data URI prefix
|
101
|
+
};
|
102
|
+
}
|
103
|
+
catch (error) {
|
104
|
+
logger.error("Failed to process image for Anthropic:", error);
|
105
|
+
throw new Error(`Image processing failed for Anthropic: ${error instanceof Error ? error.message : "Unknown error"}`);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
/**
|
109
|
+
* Process image for Vertex AI (model-specific routing)
|
110
|
+
*/
|
111
|
+
static processImageForVertex(image, model) {
|
112
|
+
try {
|
113
|
+
// Route based on model type
|
114
|
+
if (model.includes("gemini")) {
|
115
|
+
// Use Google AI format for Gemini models
|
116
|
+
return ImageProcessor.processImageForGoogle(image);
|
117
|
+
}
|
118
|
+
else if (model.includes("claude")) {
|
119
|
+
// Use Anthropic format for Claude models
|
120
|
+
return ImageProcessor.processImageForAnthropic(image);
|
121
|
+
}
|
122
|
+
else {
|
123
|
+
// Default to Google format
|
124
|
+
return ImageProcessor.processImageForGoogle(image);
|
125
|
+
}
|
126
|
+
}
|
127
|
+
catch (error) {
|
128
|
+
logger.error("Failed to process image for Vertex AI:", error);
|
129
|
+
throw new Error(`Image processing failed for Vertex AI: ${error instanceof Error ? error.message : "Unknown error"}`);
|
130
|
+
}
|
131
|
+
}
|
132
|
+
/**
|
133
|
+
* Detect image type from filename or data
|
134
|
+
*/
|
135
|
+
static detectImageType(input) {
|
136
|
+
try {
|
137
|
+
if (typeof input === "string") {
|
138
|
+
// Check if it's a data URI
|
139
|
+
if (input.startsWith("data:")) {
|
140
|
+
const match = input.match(/^data:([^;]+);/);
|
141
|
+
return match ? match[1] : "image/jpeg";
|
142
|
+
}
|
143
|
+
// Check if it's a filename
|
144
|
+
const extension = input.toLowerCase().split(".").pop();
|
145
|
+
const imageTypes = {
|
146
|
+
jpg: "image/jpeg",
|
147
|
+
jpeg: "image/jpeg",
|
148
|
+
png: "image/png",
|
149
|
+
gif: "image/gif",
|
150
|
+
webp: "image/webp",
|
151
|
+
bmp: "image/bmp",
|
152
|
+
tiff: "image/tiff",
|
153
|
+
tif: "image/tiff",
|
154
|
+
};
|
155
|
+
return imageTypes[extension || ""] || "image/jpeg";
|
156
|
+
}
|
157
|
+
// For Buffer, try to detect from magic bytes
|
158
|
+
if (input.length >= 4) {
|
159
|
+
const header = input.subarray(0, 4);
|
160
|
+
// PNG: 89 50 4E 47
|
161
|
+
if (header[0] === 0x89 &&
|
162
|
+
header[1] === 0x50 &&
|
163
|
+
header[2] === 0x4e &&
|
164
|
+
header[3] === 0x47) {
|
165
|
+
return "image/png";
|
166
|
+
}
|
167
|
+
// JPEG: FF D8 FF
|
168
|
+
if (header[0] === 0xff && header[1] === 0xd8 && header[2] === 0xff) {
|
169
|
+
return "image/jpeg";
|
170
|
+
}
|
171
|
+
// GIF: 47 49 46 38
|
172
|
+
if (header[0] === 0x47 &&
|
173
|
+
header[1] === 0x49 &&
|
174
|
+
header[2] === 0x46 &&
|
175
|
+
header[3] === 0x38) {
|
176
|
+
return "image/gif";
|
177
|
+
}
|
178
|
+
// WebP: check for RIFF and WEBP
|
179
|
+
if (input.length >= 12) {
|
180
|
+
const riff = input.subarray(0, 4);
|
181
|
+
const webp = input.subarray(8, 12);
|
182
|
+
if (riff.toString() === "RIFF" && webp.toString() === "WEBP") {
|
183
|
+
return "image/webp";
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
return "image/jpeg"; // Default fallback
|
188
|
+
}
|
189
|
+
catch (error) {
|
190
|
+
logger.warn("Failed to detect image type, using default:", error);
|
191
|
+
return "image/jpeg";
|
192
|
+
}
|
193
|
+
}
|
194
|
+
/**
|
195
|
+
* Validate image size (default 10MB limit)
|
196
|
+
*/
|
197
|
+
static validateImageSize(data, maxSize = 10 * 1024 * 1024) {
|
198
|
+
try {
|
199
|
+
const size = typeof data === "string"
|
200
|
+
? Buffer.byteLength(data, "base64")
|
201
|
+
: data.length;
|
202
|
+
return size <= maxSize;
|
203
|
+
}
|
204
|
+
catch (error) {
|
205
|
+
logger.warn("Failed to validate image size:", error);
|
206
|
+
return false;
|
207
|
+
}
|
208
|
+
}
|
209
|
+
/**
|
210
|
+
* Validate image format
|
211
|
+
*/
|
212
|
+
static validateImageFormat(mediaType) {
|
213
|
+
const supportedFormats = [
|
214
|
+
"image/jpeg",
|
215
|
+
"image/png",
|
216
|
+
"image/gif",
|
217
|
+
"image/webp",
|
218
|
+
"image/bmp",
|
219
|
+
"image/tiff",
|
220
|
+
];
|
221
|
+
return supportedFormats.includes(mediaType.toLowerCase());
|
222
|
+
}
|
223
|
+
/**
|
224
|
+
* Get image dimensions from Buffer (basic implementation)
|
225
|
+
*/
|
226
|
+
static getImageDimensions(buffer) {
|
227
|
+
try {
|
228
|
+
// Basic PNG dimension extraction
|
229
|
+
if (buffer.length >= 24 &&
|
230
|
+
buffer.subarray(0, 8).toString("hex") === "89504e470d0a1a0a") {
|
231
|
+
const width = buffer.readUInt32BE(16);
|
232
|
+
const height = buffer.readUInt32BE(20);
|
233
|
+
return { width, height };
|
234
|
+
}
|
235
|
+
// Basic JPEG dimension extraction (simplified)
|
236
|
+
if (buffer.length >= 4 && buffer[0] === 0xff && buffer[1] === 0xd8) {
|
237
|
+
// This is a very basic implementation
|
238
|
+
// For production, consider using a proper image library
|
239
|
+
return null;
|
240
|
+
}
|
241
|
+
return null;
|
242
|
+
}
|
243
|
+
catch (error) {
|
244
|
+
logger.warn("Failed to extract image dimensions:", error);
|
245
|
+
return null;
|
246
|
+
}
|
247
|
+
}
|
248
|
+
/**
|
249
|
+
* Convert image to ProcessedImage format
|
250
|
+
*/
|
251
|
+
static processImage(image, provider, model) {
|
252
|
+
try {
|
253
|
+
const mediaType = ImageProcessor.detectImageType(image);
|
254
|
+
const size = typeof image === "string"
|
255
|
+
? Buffer.byteLength(image, "base64")
|
256
|
+
: image.length;
|
257
|
+
let data;
|
258
|
+
let format;
|
259
|
+
switch (provider.toLowerCase()) {
|
260
|
+
case "openai":
|
261
|
+
data = ImageProcessor.processImageForOpenAI(image);
|
262
|
+
format = "data_uri";
|
263
|
+
break;
|
264
|
+
case "google-ai":
|
265
|
+
case "google": {
|
266
|
+
const googleResult = ImageProcessor.processImageForGoogle(image);
|
267
|
+
data = googleResult.data;
|
268
|
+
format = "base64";
|
269
|
+
break;
|
270
|
+
}
|
271
|
+
case "anthropic": {
|
272
|
+
const anthropicResult = ImageProcessor.processImageForAnthropic(image);
|
273
|
+
data = anthropicResult.data;
|
274
|
+
format = "base64";
|
275
|
+
break;
|
276
|
+
}
|
277
|
+
case "vertex": {
|
278
|
+
const vertexResult = ImageProcessor.processImageForVertex(image, model || "");
|
279
|
+
data = vertexResult.data;
|
280
|
+
format = "base64";
|
281
|
+
break;
|
282
|
+
}
|
283
|
+
default:
|
284
|
+
// Default to base64
|
285
|
+
if (typeof image === "string") {
|
286
|
+
data = image.startsWith("data:")
|
287
|
+
? image.split(",")[1] || image
|
288
|
+
: image;
|
289
|
+
}
|
290
|
+
else {
|
291
|
+
data = image.toString("base64");
|
292
|
+
}
|
293
|
+
format = "base64";
|
294
|
+
}
|
295
|
+
return {
|
296
|
+
data,
|
297
|
+
mediaType,
|
298
|
+
size,
|
299
|
+
format,
|
300
|
+
};
|
301
|
+
}
|
302
|
+
catch (error) {
|
303
|
+
logger.error(`Failed to process image for ${provider}:`, error);
|
304
|
+
throw new Error(`Image processing failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
305
|
+
}
|
306
|
+
}
|
307
|
+
}
|
308
|
+
/**
|
309
|
+
* Utility functions for image handling
|
310
|
+
*/
|
311
|
+
export const imageUtils = {
|
312
|
+
/**
|
313
|
+
* Check if a string is a valid data URI
|
314
|
+
*/
|
315
|
+
isDataUri: (str) => {
|
316
|
+
return (typeof str === "string" &&
|
317
|
+
str.startsWith("data:") &&
|
318
|
+
str.includes("base64,"));
|
319
|
+
},
|
320
|
+
/**
|
321
|
+
* Check if a string is a valid URL
|
322
|
+
*/
|
323
|
+
isUrl: (str) => {
|
324
|
+
try {
|
325
|
+
new URL(str);
|
326
|
+
return str.startsWith("http://") || str.startsWith("https://");
|
327
|
+
}
|
328
|
+
catch {
|
329
|
+
return false;
|
330
|
+
}
|
331
|
+
},
|
332
|
+
/**
|
333
|
+
* Check if a string is base64 encoded
|
334
|
+
*/
|
335
|
+
isBase64: (str) => {
|
336
|
+
try {
|
337
|
+
return btoa(atob(str)) === str;
|
338
|
+
}
|
339
|
+
catch {
|
340
|
+
return false;
|
341
|
+
}
|
342
|
+
},
|
343
|
+
/**
|
344
|
+
* Extract file extension from filename or URL
|
345
|
+
*/
|
346
|
+
getFileExtension: (filename) => {
|
347
|
+
const match = filename.match(/\.([^.]+)$/);
|
348
|
+
return match ? match[1].toLowerCase() : null;
|
349
|
+
},
|
350
|
+
/**
|
351
|
+
* Convert file size to human readable format
|
352
|
+
*/
|
353
|
+
formatFileSize: (bytes) => {
|
354
|
+
if (bytes === 0) {
|
355
|
+
return "0 Bytes";
|
356
|
+
}
|
357
|
+
const k = 1024;
|
358
|
+
const sizes = ["Bytes", "KB", "MB", "GB"];
|
359
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
360
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
361
|
+
},
|
362
|
+
};
|
@@ -1,13 +1,20 @@
|
|
1
1
|
/**
|
2
2
|
* Message Builder Utility
|
3
3
|
* Centralized logic for building message arrays from TextGenerationOptions
|
4
|
+
* Enhanced with multimodal support for images
|
4
5
|
*/
|
5
|
-
import type { ChatMessage } from "../types/conversation.js";
|
6
|
+
import type { ChatMessage, MultimodalChatMessage } from "../types/conversation.js";
|
6
7
|
import type { TextGenerationOptions } from "../types/index.js";
|
7
8
|
import type { StreamOptions } from "../types/streamTypes.js";
|
9
|
+
import type { GenerateOptions } from "../types/generateTypes.js";
|
8
10
|
/**
|
9
11
|
* Build a properly formatted message array for AI providers
|
10
12
|
* Combines system prompt, conversation history, and current user prompt
|
11
13
|
* Supports both TextGenerationOptions and StreamOptions
|
12
14
|
*/
|
13
15
|
export declare function buildMessagesArray(options: TextGenerationOptions | StreamOptions): ChatMessage[];
|
16
|
+
/**
|
17
|
+
* Build multimodal message array with image support
|
18
|
+
* Detects when images are present and routes through provider adapter
|
19
|
+
*/
|
20
|
+
export declare function buildMultimodalMessagesArray(options: GenerateOptions, provider: string, model: string): Promise<MultimodalChatMessage[]>;
|