@juspay/neurolink 8.2.0 → 8.4.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/README.md +13 -3
- package/dist/adapters/providerImageAdapter.d.ts +1 -1
- package/dist/adapters/providerImageAdapter.js +62 -0
- package/dist/agent/directTools.d.ts +0 -72
- package/dist/agent/directTools.js +3 -74
- package/dist/cli/commands/config.d.ts +18 -18
- package/dist/cli/factories/commandFactory.js +1 -0
- package/dist/cli/loop/conversationSelector.js +4 -0
- package/dist/cli/loop/session.js +27 -15
- package/dist/constants/enums.d.ts +1 -0
- package/dist/constants/enums.js +3 -1
- package/dist/constants/tokens.d.ts +3 -0
- package/dist/constants/tokens.js +3 -0
- package/dist/core/baseProvider.d.ts +56 -53
- package/dist/core/baseProvider.js +107 -1095
- package/dist/core/constants.d.ts +3 -0
- package/dist/core/constants.js +6 -3
- package/dist/core/modelConfiguration.js +10 -0
- package/dist/core/modules/GenerationHandler.d.ts +63 -0
- package/dist/core/modules/GenerationHandler.js +230 -0
- package/dist/core/modules/MessageBuilder.d.ts +39 -0
- package/dist/core/modules/MessageBuilder.js +179 -0
- package/dist/core/modules/StreamHandler.d.ts +52 -0
- package/dist/core/modules/StreamHandler.js +103 -0
- package/dist/core/modules/TelemetryHandler.d.ts +64 -0
- package/dist/core/modules/TelemetryHandler.js +170 -0
- package/dist/core/modules/ToolsManager.d.ts +98 -0
- package/dist/core/modules/ToolsManager.js +521 -0
- package/dist/core/modules/Utilities.d.ts +88 -0
- package/dist/core/modules/Utilities.js +329 -0
- package/dist/factories/providerRegistry.js +1 -1
- package/dist/lib/adapters/providerImageAdapter.d.ts +1 -1
- package/dist/lib/adapters/providerImageAdapter.js +62 -0
- package/dist/lib/agent/directTools.d.ts +0 -72
- package/dist/lib/agent/directTools.js +3 -74
- package/dist/lib/constants/enums.d.ts +1 -0
- package/dist/lib/constants/enums.js +3 -1
- package/dist/lib/constants/tokens.d.ts +3 -0
- package/dist/lib/constants/tokens.js +3 -0
- package/dist/lib/core/baseProvider.d.ts +56 -53
- package/dist/lib/core/baseProvider.js +107 -1095
- package/dist/lib/core/constants.d.ts +3 -0
- package/dist/lib/core/constants.js +6 -3
- package/dist/lib/core/modelConfiguration.js +10 -0
- package/dist/lib/core/modules/GenerationHandler.d.ts +63 -0
- package/dist/lib/core/modules/GenerationHandler.js +231 -0
- package/dist/lib/core/modules/MessageBuilder.d.ts +39 -0
- package/dist/lib/core/modules/MessageBuilder.js +180 -0
- package/dist/lib/core/modules/StreamHandler.d.ts +52 -0
- package/dist/lib/core/modules/StreamHandler.js +104 -0
- package/dist/lib/core/modules/TelemetryHandler.d.ts +64 -0
- package/dist/lib/core/modules/TelemetryHandler.js +171 -0
- package/dist/lib/core/modules/ToolsManager.d.ts +98 -0
- package/dist/lib/core/modules/ToolsManager.js +522 -0
- package/dist/lib/core/modules/Utilities.d.ts +88 -0
- package/dist/lib/core/modules/Utilities.js +330 -0
- package/dist/lib/factories/providerRegistry.js +1 -1
- package/dist/lib/mcp/servers/agent/directToolsServer.js +0 -1
- package/dist/lib/models/modelRegistry.js +44 -0
- package/dist/lib/neurolink.js +35 -3
- package/dist/lib/providers/amazonBedrock.js +59 -10
- package/dist/lib/providers/anthropic.js +2 -30
- package/dist/lib/providers/azureOpenai.js +2 -24
- package/dist/lib/providers/googleAiStudio.js +2 -24
- package/dist/lib/providers/googleVertex.js +2 -45
- package/dist/lib/providers/huggingFace.js +3 -31
- package/dist/lib/providers/litellm.d.ts +1 -1
- package/dist/lib/providers/litellm.js +110 -44
- package/dist/lib/providers/mistral.js +5 -32
- package/dist/lib/providers/ollama.d.ts +1 -0
- package/dist/lib/providers/ollama.js +476 -129
- package/dist/lib/providers/openAI.js +2 -28
- package/dist/lib/providers/openaiCompatible.js +3 -31
- package/dist/lib/types/content.d.ts +16 -113
- package/dist/lib/types/content.js +16 -2
- package/dist/lib/types/conversation.d.ts +3 -17
- package/dist/lib/types/generateTypes.d.ts +2 -2
- package/dist/lib/types/index.d.ts +2 -0
- package/dist/lib/types/index.js +2 -0
- package/dist/lib/types/multimodal.d.ts +282 -0
- package/dist/lib/types/multimodal.js +101 -0
- package/dist/lib/types/streamTypes.d.ts +2 -2
- package/dist/lib/utils/imageProcessor.d.ts +1 -1
- package/dist/lib/utils/messageBuilder.js +25 -2
- package/dist/lib/utils/multimodalOptionsBuilder.d.ts +1 -1
- package/dist/lib/utils/pdfProcessor.d.ts +9 -0
- package/dist/lib/utils/pdfProcessor.js +67 -9
- package/dist/mcp/servers/agent/directToolsServer.js +0 -1
- package/dist/models/modelRegistry.js +44 -0
- package/dist/neurolink.js +35 -3
- package/dist/providers/amazonBedrock.js +59 -10
- package/dist/providers/anthropic.js +2 -30
- package/dist/providers/azureOpenai.js +2 -24
- package/dist/providers/googleAiStudio.js +2 -24
- package/dist/providers/googleVertex.js +2 -45
- package/dist/providers/huggingFace.js +3 -31
- package/dist/providers/litellm.d.ts +1 -1
- package/dist/providers/litellm.js +110 -44
- package/dist/providers/mistral.js +5 -32
- package/dist/providers/ollama.d.ts +1 -0
- package/dist/providers/ollama.js +476 -129
- package/dist/providers/openAI.js +2 -28
- package/dist/providers/openaiCompatible.js +3 -31
- package/dist/types/content.d.ts +16 -113
- package/dist/types/content.js +16 -2
- package/dist/types/conversation.d.ts +3 -17
- package/dist/types/generateTypes.d.ts +2 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/multimodal.d.ts +282 -0
- package/dist/types/multimodal.js +100 -0
- package/dist/types/streamTypes.d.ts +2 -2
- package/dist/utils/imageProcessor.d.ts +1 -1
- package/dist/utils/messageBuilder.js +25 -2
- package/dist/utils/multimodalOptionsBuilder.d.ts +1 -1
- package/dist/utils/pdfProcessor.d.ts +9 -0
- package/dist/utils/pdfProcessor.js +67 -9
- package/package.json +5 -2
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multimodal Content Types for NeuroLink
|
|
3
|
+
*
|
|
4
|
+
* Central registry for all multimodal input/output types.
|
|
5
|
+
* This file consolidates types from content.ts and conversation.ts
|
|
6
|
+
* to provide a single source of truth for multimodal functionality.
|
|
7
|
+
*
|
|
8
|
+
* @module types/multimodal
|
|
9
|
+
*
|
|
10
|
+
* @example Basic Multimodal Input
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import type { MultimodalInput } from './types/multimodal.js';
|
|
13
|
+
*
|
|
14
|
+
* const input: MultimodalInput = {
|
|
15
|
+
* text: "What's in this image?",
|
|
16
|
+
* images: [imageBuffer, "https://example.com/image.jpg"],
|
|
17
|
+
* pdfFiles: [pdfBuffer]
|
|
18
|
+
* };
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example Audio/Video Input (Future)
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const avInput: MultimodalInput = {
|
|
24
|
+
* text: "Transcribe this audio and analyze this video",
|
|
25
|
+
* audioFiles: [audioBuffer],
|
|
26
|
+
* videoFiles: ["path/to/video.mp4"]
|
|
27
|
+
* };
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example Advanced Content Array
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const advanced: MultimodalInput = {
|
|
33
|
+
* text: "irrelevant", // ignored when content[] is provided
|
|
34
|
+
* content: [
|
|
35
|
+
* { type: "text", text: "Analyze these items:" },
|
|
36
|
+
* { type: "image", data: imageBuffer, mediaType: "image/jpeg" },
|
|
37
|
+
* { type: "pdf", data: pdfBuffer, metadata: { filename: "report.pdf" } }
|
|
38
|
+
* ]
|
|
39
|
+
* };
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
// ============================================
|
|
43
|
+
// TYPE GUARDS
|
|
44
|
+
// ============================================
|
|
45
|
+
/**
|
|
46
|
+
* Type guard to check if content is TextContent
|
|
47
|
+
*/
|
|
48
|
+
export function isTextContent(content) {
|
|
49
|
+
return content.type === "text";
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Type guard to check if content is ImageContent
|
|
53
|
+
*/
|
|
54
|
+
export function isImageContent(content) {
|
|
55
|
+
return content.type === "image";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Type guard to check if content is CSVContent
|
|
59
|
+
*/
|
|
60
|
+
export function isCSVContent(content) {
|
|
61
|
+
return content.type === "csv";
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Type guard to check if content is PDFContent
|
|
65
|
+
*/
|
|
66
|
+
export function isPDFContent(content) {
|
|
67
|
+
return content.type === "pdf";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Type guard to check if content is AudioContent
|
|
71
|
+
*/
|
|
72
|
+
export function isAudioContent(content) {
|
|
73
|
+
return content.type === "audio";
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Type guard to check if content is VideoContent
|
|
77
|
+
*/
|
|
78
|
+
export function isVideoContent(content) {
|
|
79
|
+
return content.type === "video";
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Type guard to check if input contains multimodal content
|
|
83
|
+
* Now includes audio and video detection
|
|
84
|
+
*/
|
|
85
|
+
export function isMultimodalInput(input) {
|
|
86
|
+
const maybeInput = input;
|
|
87
|
+
return !!(maybeInput?.images?.length ||
|
|
88
|
+
maybeInput?.csvFiles?.length ||
|
|
89
|
+
maybeInput?.pdfFiles?.length ||
|
|
90
|
+
maybeInput?.files?.length ||
|
|
91
|
+
maybeInput?.content?.length ||
|
|
92
|
+
maybeInput?.audioFiles?.length ||
|
|
93
|
+
maybeInput?.videoFiles?.length);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Type guard to check if message content is multimodal (array)
|
|
97
|
+
*/
|
|
98
|
+
export function isMultimodalMessageContent(content) {
|
|
99
|
+
return Array.isArray(content);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=multimodal.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Tool } from "ai";
|
|
2
2
|
import type { ValidationSchema, StandardRecord } from "./typeAliases.js";
|
|
3
3
|
import type { AIModelProviderConfig } from "./providers.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { Content } from "./content.js";
|
|
5
5
|
import type { AnalyticsData, ToolExecutionEvent, ToolExecutionSummary } from "../types/index.js";
|
|
6
6
|
import { AIProviderName } from "../constants/enums.js";
|
|
7
7
|
import type { TokenUsage } from "./analytics.js";
|
|
@@ -129,7 +129,7 @@ export type StreamOptions = {
|
|
|
129
129
|
csvFiles?: Array<Buffer | string>;
|
|
130
130
|
pdfFiles?: Array<Buffer | string>;
|
|
131
131
|
files?: Array<Buffer | string>;
|
|
132
|
-
content?:
|
|
132
|
+
content?: Content[];
|
|
133
133
|
};
|
|
134
134
|
output?: {
|
|
135
135
|
format?: "text" | "structured" | "json";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Image processing utilities for multimodal support
|
|
3
3
|
* Handles format conversion for different AI providers
|
|
4
4
|
*/
|
|
5
|
-
import type { ProcessedImage } from "../types/
|
|
5
|
+
import type { ProcessedImage } from "../types/multimodal.js";
|
|
6
6
|
import type { FileProcessingResult } from "../types/fileTypes.js";
|
|
7
7
|
/**
|
|
8
8
|
* Image processor class for handling provider-specific image formatting
|
|
@@ -473,6 +473,26 @@ export async function buildMultimodalMessagesArray(options, provider, model) {
|
|
|
473
473
|
if (hasConversationHistory) {
|
|
474
474
|
systemPrompt = `${systemPrompt.trim()}${CONVERSATION_INSTRUCTIONS}`;
|
|
475
475
|
}
|
|
476
|
+
// Add file handling guidance when multimodal files are present
|
|
477
|
+
const hasCSVFiles = (options.input.csvFiles && options.input.csvFiles.length > 0) ||
|
|
478
|
+
(options.input.files &&
|
|
479
|
+
options.input.files.some((f) => typeof f === "string" ? f.toLowerCase().endsWith(".csv") : false));
|
|
480
|
+
const hasPDFFiles = pdfFiles.length > 0;
|
|
481
|
+
if (hasCSVFiles || hasPDFFiles) {
|
|
482
|
+
const fileTypes = [];
|
|
483
|
+
if (hasPDFFiles) {
|
|
484
|
+
fileTypes.push("PDFs");
|
|
485
|
+
}
|
|
486
|
+
if (hasCSVFiles) {
|
|
487
|
+
fileTypes.push("CSVs");
|
|
488
|
+
}
|
|
489
|
+
systemPrompt += `\n\nIMPORTANT FILE HANDLING INSTRUCTIONS:
|
|
490
|
+
- File content (${fileTypes.join(", ")}, images) is already processed and included in this message
|
|
491
|
+
- DO NOT use GitHub tools (get_file_contents, search_code, etc.) for local files - they only work for remote repository files
|
|
492
|
+
- Analyze the provided file content directly without attempting to fetch or read files using tools
|
|
493
|
+
- GitHub MCP tools are ONLY for remote repository operations, not local filesystem access
|
|
494
|
+
- Use the file content shown in this message for your analysis`;
|
|
495
|
+
}
|
|
476
496
|
// Add system message if we have one
|
|
477
497
|
if (systemPrompt.trim()) {
|
|
478
498
|
messages.push({
|
|
@@ -739,7 +759,10 @@ async function convertMultimodalToProviderFormat(text, images, pdfFiles, provide
|
|
|
739
759
|
});
|
|
740
760
|
}
|
|
741
761
|
}
|
|
742
|
-
// Add PDFs using Vercel AI SDK standard format (works for all providers)
|
|
762
|
+
// Add PDFs using Vercel AI SDK standard format (works for all providers except Mistral)
|
|
763
|
+
// NOTE: Mistral API has a fundamental limitation - it does NOT support PDFs in any form.
|
|
764
|
+
// The API strictly requires image content to start with data:image/, rejecting data:application/pdf
|
|
765
|
+
// See: MISTRAL_PDF_FIX_SUMMARY.md for full investigation details
|
|
743
766
|
content.push(...pdfFiles.map((pdf) => {
|
|
744
767
|
logger.info(`[PDF] ✅ Added to content (Vercel AI SDK format): ${pdf.filename}`);
|
|
745
768
|
return {
|
|
@@ -769,6 +792,6 @@ function extractFilename(file, index = 0) {
|
|
|
769
792
|
return `file-${index + 1}`;
|
|
770
793
|
}
|
|
771
794
|
function buildCSVToolInstructions(filePath) {
|
|
772
|
-
return `\n**
|
|
795
|
+
return `\n**NOTE**: You can perform calculations directly on the CSV data shown above. For advanced operations on the full file (counting by column, grouping, etc.), you may optionally use the analyzeCSV tool with filePath="${filePath}".\n\nExample: analyzeCSV(filePath="${filePath}", operation="count_by_column", column="merchant_id")\n\n`;
|
|
773
796
|
}
|
|
774
797
|
//# sourceMappingURL=messageBuilder.js.map
|
|
@@ -45,7 +45,7 @@ export declare function buildMultimodalOptions(options: StreamOptions, providerN
|
|
|
45
45
|
input: {
|
|
46
46
|
text: string;
|
|
47
47
|
images: (string | Buffer<ArrayBufferLike>)[] | undefined;
|
|
48
|
-
content:
|
|
48
|
+
content: import("../types/multimodal.js").Content[] | undefined;
|
|
49
49
|
files: (string | Buffer<ArrayBufferLike>)[] | undefined;
|
|
50
50
|
csvFiles: (string | Buffer<ArrayBufferLike>)[] | undefined;
|
|
51
51
|
pdfFiles: (string | Buffer<ArrayBufferLike>)[] | undefined;
|
|
@@ -7,4 +7,13 @@ export declare class PDFProcessor {
|
|
|
7
7
|
private static isValidPDF;
|
|
8
8
|
private static extractBasicMetadata;
|
|
9
9
|
static estimateTokens(pageCount: number, mode?: "text-only" | "visual"): number;
|
|
10
|
+
static convertPDFToImages(pdfBuffer: Buffer, options?: {
|
|
11
|
+
maxPages?: number;
|
|
12
|
+
scale?: number;
|
|
13
|
+
format?: "png" | "jpeg";
|
|
14
|
+
quality?: number;
|
|
15
|
+
}): Promise<Array<{
|
|
16
|
+
buffer: Buffer;
|
|
17
|
+
pageNumber: number;
|
|
18
|
+
}>>;
|
|
10
19
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { logger } from "./logger.js";
|
|
2
|
+
import * as pdfjs from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
3
|
+
import { createCanvas } from "canvas";
|
|
2
4
|
const PDF_PROVIDER_CONFIGS = {
|
|
3
5
|
anthropic: {
|
|
4
6
|
maxSizeMB: 5,
|
|
@@ -87,7 +89,7 @@ const PDF_PROVIDER_CONFIGS = {
|
|
|
87
89
|
mistral: {
|
|
88
90
|
maxSizeMB: 10,
|
|
89
91
|
maxPages: 100,
|
|
90
|
-
supportsNative:
|
|
92
|
+
supportsNative: false,
|
|
91
93
|
requiresCitations: false,
|
|
92
94
|
apiType: "files-api",
|
|
93
95
|
},
|
|
@@ -115,16 +117,14 @@ export class PDFProcessor {
|
|
|
115
117
|
if (!this.isValidPDF(content)) {
|
|
116
118
|
throw new Error("Invalid PDF file format. File must start with %PDF- header.");
|
|
117
119
|
}
|
|
118
|
-
if (!config
|
|
119
|
-
const supportedProviders = Object.keys(PDF_PROVIDER_CONFIGS)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
throw new Error(`PDF files are not currently supported with ${provider} provider.\n` +
|
|
123
|
-
`Supported providers: ${supportedProviders}\n` +
|
|
120
|
+
if (!config) {
|
|
121
|
+
const supportedProviders = Object.keys(PDF_PROVIDER_CONFIGS).join(", ");
|
|
122
|
+
throw new Error(`PDF files are not configured for ${provider} provider.\n` +
|
|
123
|
+
`Configured providers: ${supportedProviders}\n` +
|
|
124
124
|
`Current provider: ${provider}\n\n` +
|
|
125
125
|
`Options:\n` +
|
|
126
|
-
`1. Switch to a
|
|
127
|
-
`2.
|
|
126
|
+
`1. Switch to a configured provider (--provider openai or --provider vertex)\n` +
|
|
127
|
+
`2. Contact support to add ${provider} PDF configuration`);
|
|
128
128
|
}
|
|
129
129
|
const sizeMB = content.length / (1024 * 1024);
|
|
130
130
|
if (sizeMB > config.maxSizeMB) {
|
|
@@ -195,5 +195,63 @@ export class PDFProcessor {
|
|
|
195
195
|
return Math.ceil((pageCount / 3) * 7000);
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
+
static async convertPDFToImages(pdfBuffer, options) {
|
|
199
|
+
const maxPages = options?.maxPages || 10;
|
|
200
|
+
const scale = options?.scale || 2.0;
|
|
201
|
+
const format = options?.format || "png";
|
|
202
|
+
const quality = options?.quality || 0.9;
|
|
203
|
+
let pdfDocument = null;
|
|
204
|
+
try {
|
|
205
|
+
const loadingTask = pdfjs.getDocument({
|
|
206
|
+
data: new Uint8Array(pdfBuffer),
|
|
207
|
+
useSystemFonts: true,
|
|
208
|
+
standardFontDataUrl: `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjs.version}/standard_fonts/`,
|
|
209
|
+
});
|
|
210
|
+
pdfDocument = await loadingTask.promise;
|
|
211
|
+
const numPages = Math.min(pdfDocument.numPages, maxPages);
|
|
212
|
+
const images = [];
|
|
213
|
+
logger.info(`[PDF→Image] Converting ${numPages} page(s) from PDF (total: ${pdfDocument.numPages})`);
|
|
214
|
+
for (let pageNum = 1; pageNum <= numPages; pageNum++) {
|
|
215
|
+
const page = await pdfDocument.getPage(pageNum);
|
|
216
|
+
const viewport = page.getViewport({ scale });
|
|
217
|
+
const canvas = createCanvas(viewport.width, viewport.height);
|
|
218
|
+
const context = canvas.getContext("2d");
|
|
219
|
+
await page.render({
|
|
220
|
+
canvasContext: context,
|
|
221
|
+
viewport,
|
|
222
|
+
// @ts-expect-error - canvas type mismatch between node-canvas and pdfjs-dist
|
|
223
|
+
canvas: canvas,
|
|
224
|
+
}).promise;
|
|
225
|
+
const imageBuffer = format === "png"
|
|
226
|
+
? canvas.toBuffer("image/png")
|
|
227
|
+
: canvas.toBuffer("image/jpeg", { quality });
|
|
228
|
+
images.push({ buffer: imageBuffer, pageNumber: pageNum });
|
|
229
|
+
logger.debug(`[PDF→Image] ✅ Converted page ${pageNum}/${numPages} (${(imageBuffer.length / 1024).toFixed(1)}KB)`);
|
|
230
|
+
}
|
|
231
|
+
if (pdfDocument.numPages > maxPages) {
|
|
232
|
+
logger.warn(`[PDF→Image] PDF has ${pdfDocument.numPages} pages, converted only first ${maxPages} pages`);
|
|
233
|
+
}
|
|
234
|
+
logger.info(`[PDF→Image] ✅ Successfully converted ${images.length} page(s) to images`);
|
|
235
|
+
return images;
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
logger.error(`[PDF→Image] ❌ Failed to convert PDF to images:`, error instanceof Error ? error.message : String(error));
|
|
239
|
+
throw new Error(`PDF to image conversion failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
// Ensure pdfDocument is destroyed regardless of success or failure
|
|
243
|
+
if (pdfDocument) {
|
|
244
|
+
try {
|
|
245
|
+
pdfDocument.destroy();
|
|
246
|
+
logger.debug("[PDF→Image] PDF document resources cleaned up");
|
|
247
|
+
}
|
|
248
|
+
catch (destroyError) {
|
|
249
|
+
logger.warn("[PDF→Image] Error destroying PDF document:", destroyError instanceof Error
|
|
250
|
+
? destroyError.message
|
|
251
|
+
: String(destroyError));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
198
256
|
}
|
|
199
257
|
//# sourceMappingURL=pdfProcessor.js.map
|
|
@@ -188,6 +188,50 @@ export const MODEL_REGISTRY = {
|
|
|
188
188
|
category: "general",
|
|
189
189
|
},
|
|
190
190
|
// Anthropic Models
|
|
191
|
+
[AnthropicModels.CLAUDE_4_5_HAIKU]: {
|
|
192
|
+
id: AnthropicModels.CLAUDE_4_5_HAIKU,
|
|
193
|
+
name: "Claude 4.5 Haiku",
|
|
194
|
+
provider: AIProviderName.ANTHROPIC,
|
|
195
|
+
description: "Latest fast and efficient Claude model with vision support",
|
|
196
|
+
capabilities: {
|
|
197
|
+
vision: true,
|
|
198
|
+
functionCalling: true,
|
|
199
|
+
codeGeneration: true,
|
|
200
|
+
reasoning: true,
|
|
201
|
+
multimodal: true,
|
|
202
|
+
streaming: true,
|
|
203
|
+
jsonMode: false,
|
|
204
|
+
},
|
|
205
|
+
pricing: {
|
|
206
|
+
inputCostPer1K: 0.001,
|
|
207
|
+
outputCostPer1K: 0.005,
|
|
208
|
+
currency: "USD",
|
|
209
|
+
},
|
|
210
|
+
performance: {
|
|
211
|
+
speed: "fast",
|
|
212
|
+
quality: "high",
|
|
213
|
+
accuracy: "high",
|
|
214
|
+
},
|
|
215
|
+
limits: {
|
|
216
|
+
maxContextTokens: 200000,
|
|
217
|
+
maxOutputTokens: 64000,
|
|
218
|
+
maxRequestsPerMinute: 100,
|
|
219
|
+
},
|
|
220
|
+
useCases: {
|
|
221
|
+
coding: 8,
|
|
222
|
+
creative: 8,
|
|
223
|
+
analysis: 8,
|
|
224
|
+
conversation: 9,
|
|
225
|
+
reasoning: 8,
|
|
226
|
+
translation: 8,
|
|
227
|
+
summarization: 9,
|
|
228
|
+
},
|
|
229
|
+
aliases: ["claude-4.5-haiku", "claude-haiku-latest", "haiku-4.5"],
|
|
230
|
+
deprecated: false,
|
|
231
|
+
isLocal: false,
|
|
232
|
+
releaseDate: "2025-10-15",
|
|
233
|
+
category: "general",
|
|
234
|
+
},
|
|
191
235
|
[AnthropicModels.CLAUDE_3_5_SONNET]: {
|
|
192
236
|
id: AnthropicModels.CLAUDE_3_5_SONNET,
|
|
193
237
|
name: "Claude 3.5 Sonnet",
|
package/dist/neurolink.js
CHANGED
|
@@ -455,7 +455,7 @@ Current user's request: ${currentInput}`;
|
|
|
455
455
|
try {
|
|
456
456
|
this.externalServerManager = new ExternalServerManager({
|
|
457
457
|
maxServers: 20,
|
|
458
|
-
defaultTimeout:
|
|
458
|
+
defaultTimeout: 30000, // Increased from 15s to 30s for proxy latency (e.g., LiteLLM)
|
|
459
459
|
enableAutoRestart: true,
|
|
460
460
|
enablePerformanceMonitoring: true,
|
|
461
461
|
}, {
|
|
@@ -1963,6 +1963,29 @@ Current user's request: ${currentInput}`;
|
|
|
1963
1963
|
// Continue with original options if orchestration fails
|
|
1964
1964
|
}
|
|
1965
1965
|
}
|
|
1966
|
+
// 🔧 AUTO-DISABLE TOOLS: For Ollama models that don't support tools (same logic as generate())
|
|
1967
|
+
// This prevents overwhelming smaller models with massive tool descriptions in the system message
|
|
1968
|
+
if ((options.provider === "ollama" ||
|
|
1969
|
+
options.provider?.toLowerCase().includes("ollama")) &&
|
|
1970
|
+
!options.disableTools) {
|
|
1971
|
+
const { ModelConfigurationManager } = await import("./core/modelConfiguration.js");
|
|
1972
|
+
const modelConfig = ModelConfigurationManager.getInstance();
|
|
1973
|
+
const ollamaConfig = modelConfig.getProviderConfiguration("ollama");
|
|
1974
|
+
const toolCapableModels = ollamaConfig?.modelBehavior?.toolCapableModels || [];
|
|
1975
|
+
// Only disable tools if we have explicit evidence the model doesn't support them
|
|
1976
|
+
// If toolCapableModels is empty or model is not specified, don't make assumptions
|
|
1977
|
+
const modelName = options.model;
|
|
1978
|
+
if (toolCapableModels.length > 0 && modelName) {
|
|
1979
|
+
const modelSupportsTools = toolCapableModels.some((capableModel) => modelName.toLowerCase().includes(capableModel.toLowerCase()));
|
|
1980
|
+
if (!modelSupportsTools) {
|
|
1981
|
+
options.disableTools = true;
|
|
1982
|
+
logger.debug("Auto-disabled tools for Ollama model that doesn't support them (stream)", {
|
|
1983
|
+
model: options.model,
|
|
1984
|
+
toolCapableModels: toolCapableModels.slice(0, 3), // Show first 3 for brevity
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1966
1989
|
factoryResult = processStreamingFactoryOptions(options);
|
|
1967
1990
|
enhancedOptions = createCleanStreamOptions(options);
|
|
1968
1991
|
if (options.input?.text) {
|
|
@@ -2091,17 +2114,26 @@ Current user's request: ${currentInput}`;
|
|
|
2091
2114
|
customTools: this.getCustomTools(),
|
|
2092
2115
|
executeTool: this.executeTool.bind(this),
|
|
2093
2116
|
}, "NeuroLink.createMCPStream");
|
|
2117
|
+
// 🔧 FIX: Get available tools and create tool-aware system prompt
|
|
2118
|
+
// Use SAME pattern as tryMCPGeneration (generate mode)
|
|
2119
|
+
const availableTools = await this.getAllAvailableTools();
|
|
2120
|
+
const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
|
|
2094
2121
|
// Get conversation messages for context
|
|
2095
2122
|
const conversationMessages = await getConversationMessages(this.conversationMemory, {
|
|
2096
2123
|
prompt: options.input.text,
|
|
2097
2124
|
context: options.context,
|
|
2098
2125
|
});
|
|
2099
|
-
//
|
|
2100
|
-
//
|
|
2126
|
+
// 🔧 FIX: Pass enhanced system prompt to real streaming
|
|
2127
|
+
// Tools will be accessed through the streamText call in executeStream
|
|
2101
2128
|
const streamResult = await provider.stream({
|
|
2102
2129
|
...options,
|
|
2130
|
+
systemPrompt: enhancedSystemPrompt, // Use enhanced prompt with tool descriptions
|
|
2103
2131
|
conversationMessages,
|
|
2104
2132
|
});
|
|
2133
|
+
logger.debug("[createMCPStream] Stream created successfully", {
|
|
2134
|
+
provider: providerName,
|
|
2135
|
+
systemPromptPassedLength: enhancedSystemPrompt.length,
|
|
2136
|
+
});
|
|
2105
2137
|
return { stream: streamResult.stream, provider: providerName };
|
|
2106
2138
|
}
|
|
2107
2139
|
/**
|
|
@@ -8,6 +8,7 @@ import { buildMultimodalMessagesArray } from "../utils/messageBuilder.js";
|
|
|
8
8
|
import { buildMultimodalOptions } from "../utils/multimodalOptionsBuilder.js";
|
|
9
9
|
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
10
10
|
import { createAnalytics } from "../core/analytics.js";
|
|
11
|
+
import path from "path";
|
|
11
12
|
// Bedrock-specific types now imported from ../types/providerSpecific.js
|
|
12
13
|
export class AmazonBedrockProvider extends BaseProvider {
|
|
13
14
|
bedrockClient;
|
|
@@ -95,13 +96,45 @@ export class AmazonBedrockProvider extends BaseProvider {
|
|
|
95
96
|
: optionsOrPrompt;
|
|
96
97
|
// Clear conversation history for new generation
|
|
97
98
|
this.conversationHistory = [];
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
// Check for multimodal input (images, PDFs, CSVs, files)
|
|
100
|
+
// Cast to any to access multimodal properties (runtime check is safe)
|
|
101
|
+
const input = options.input;
|
|
102
|
+
const hasMultimodalInput = !!(input?.images?.length ||
|
|
103
|
+
input?.content?.length ||
|
|
104
|
+
input?.files?.length ||
|
|
105
|
+
input?.csvFiles?.length ||
|
|
106
|
+
input?.pdfFiles?.length);
|
|
107
|
+
if (hasMultimodalInput) {
|
|
108
|
+
logger.debug(`[AmazonBedrockProvider] Detected multimodal input in generate(), using multimodal message builder`, {
|
|
109
|
+
hasImages: !!input?.images?.length,
|
|
110
|
+
imageCount: input?.images?.length || 0,
|
|
111
|
+
hasContent: !!input?.content?.length,
|
|
112
|
+
contentCount: input?.content?.length || 0,
|
|
113
|
+
hasFiles: !!input?.files?.length,
|
|
114
|
+
fileCount: input?.files?.length || 0,
|
|
115
|
+
hasCSVFiles: !!input?.csvFiles?.length,
|
|
116
|
+
csvFileCount: input?.csvFiles?.length || 0,
|
|
117
|
+
hasPDFFiles: !!input?.pdfFiles?.length,
|
|
118
|
+
pdfFileCount: input?.pdfFiles?.length || 0,
|
|
119
|
+
});
|
|
120
|
+
// Cast options to StreamOptions for multimodal processing
|
|
121
|
+
const streamOptions = options;
|
|
122
|
+
const multimodalOptions = buildMultimodalOptions(streamOptions, this.providerName, this.modelName);
|
|
123
|
+
const multimodalMessages = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
|
124
|
+
// Convert to Bedrock format
|
|
125
|
+
this.conversationHistory =
|
|
126
|
+
this.convertToBedrockMessages(multimodalMessages);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
logger.debug(`[AmazonBedrockProvider] Text-only input in generate(), using simple message builder`);
|
|
130
|
+
// Add user message to conversation - simple text-only case
|
|
131
|
+
const userMessage = {
|
|
132
|
+
role: "user",
|
|
133
|
+
content: [{ text: options.prompt }],
|
|
134
|
+
};
|
|
135
|
+
this.conversationHistory.push(userMessage);
|
|
136
|
+
}
|
|
137
|
+
logger.debug(`[AmazonBedrockProvider] Starting conversation with ${this.conversationHistory.length} message(s)`);
|
|
105
138
|
// Start conversation loop and return enhanced result
|
|
106
139
|
const text = await this.conversationLoop(options);
|
|
107
140
|
return {
|
|
@@ -585,12 +618,28 @@ export class AmazonBedrockProvider extends BaseProvider {
|
|
|
585
618
|
else {
|
|
586
619
|
docData = contentItem.data;
|
|
587
620
|
}
|
|
621
|
+
// Extract basename and sanitize for Bedrock's filename requirements
|
|
622
|
+
// Bedrock only allows: alphanumeric, whitespace, hyphens, parentheses, brackets
|
|
623
|
+
// NOTE: Periods (.) are NOT allowed, so we remove the extension
|
|
624
|
+
let filename = typeof contentItem.name === "string" && contentItem.name
|
|
625
|
+
? path.basename(contentItem.name)
|
|
626
|
+
: "document-pdf";
|
|
627
|
+
// Remove file extension
|
|
628
|
+
filename = filename.replace(/\.[^.]+$/, "");
|
|
629
|
+
// Replace all disallowed characters with hyphens
|
|
630
|
+
// Bedrock constraint: only alphanumeric, whitespace, hyphens, parentheses, brackets allowed
|
|
631
|
+
filename = filename.replace(/[^a-zA-Z0-9\s\-()[\]]/g, "-");
|
|
632
|
+
// Clean up: remove multiple consecutive hyphens and trim
|
|
633
|
+
filename = filename
|
|
634
|
+
.replace(/-+/g, "-")
|
|
635
|
+
.trim()
|
|
636
|
+
.replace(/^-+|-+$/g, "");
|
|
637
|
+
// Fallback if filename becomes empty after sanitization
|
|
638
|
+
filename = filename || "document";
|
|
588
639
|
bedrockMessage.content.push({
|
|
589
640
|
document: {
|
|
590
641
|
format: "pdf",
|
|
591
|
-
name:
|
|
592
|
-
? contentItem.name
|
|
593
|
-
: "document.pdf",
|
|
642
|
+
name: filename,
|
|
594
643
|
source: {
|
|
595
644
|
bytes: docData,
|
|
596
645
|
},
|
|
@@ -7,8 +7,6 @@ import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
|
|
|
7
7
|
import { AuthenticationError, NetworkError, ProviderError, RateLimitError, } from "../types/errors.js";
|
|
8
8
|
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
9
9
|
import { validateApiKey, createAnthropicConfig, getProviderModel, } from "../utils/providerConfig.js";
|
|
10
|
-
import { buildMessagesArray, buildMultimodalMessagesArray, convertToCoreMessages, } from "../utils/messageBuilder.js";
|
|
11
|
-
import { buildMultimodalOptions } from "../utils/multimodalOptionsBuilder.js";
|
|
12
10
|
import { createProxyFetch } from "../proxy/proxyFetch.js";
|
|
13
11
|
// Configuration helpers - now using consolidated utility
|
|
14
12
|
const getAnthropicApiKey = () => {
|
|
@@ -94,34 +92,8 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
94
92
|
const shouldUseTools = !options.disableTools && this.supportsTools();
|
|
95
93
|
const tools = shouldUseTools ? await this.getAllTools() : {};
|
|
96
94
|
// Build message array from options with multimodal support
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
options.input?.files?.length ||
|
|
100
|
-
options.input?.csvFiles?.length ||
|
|
101
|
-
options.input?.pdfFiles?.length);
|
|
102
|
-
let messages;
|
|
103
|
-
if (hasMultimodalInput) {
|
|
104
|
-
logger.debug(`Anthropic: Detected multimodal input, using multimodal message builder`, {
|
|
105
|
-
hasImages: !!options.input?.images?.length,
|
|
106
|
-
imageCount: options.input?.images?.length || 0,
|
|
107
|
-
hasContent: !!options.input?.content?.length,
|
|
108
|
-
contentCount: options.input?.content?.length || 0,
|
|
109
|
-
hasFiles: !!options.input?.files?.length,
|
|
110
|
-
fileCount: options.input?.files?.length || 0,
|
|
111
|
-
hasCSVFiles: !!options.input?.csvFiles?.length,
|
|
112
|
-
csvFileCount: options.input?.csvFiles?.length || 0,
|
|
113
|
-
hasPDFFiles: !!options.input?.pdfFiles?.length,
|
|
114
|
-
pdfFileCount: options.input?.pdfFiles?.length || 0,
|
|
115
|
-
});
|
|
116
|
-
const multimodalOptions = buildMultimodalOptions(options, this.providerName, this.modelName);
|
|
117
|
-
const mm = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
|
118
|
-
// Convert multimodal messages to Vercel AI SDK format (CoreMessage[])
|
|
119
|
-
messages = convertToCoreMessages(mm);
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
logger.debug(`Anthropic: Text-only input, using standard message builder`);
|
|
123
|
-
messages = await buildMessagesArray(options);
|
|
124
|
-
}
|
|
95
|
+
// Using protected helper from BaseProvider to eliminate code duplication
|
|
96
|
+
const messages = await this.buildMessagesForStream(options);
|
|
125
97
|
const model = await this.getAISDKModelWithMiddleware(options);
|
|
126
98
|
const result = await streamText({
|
|
127
99
|
model: model,
|
|
@@ -4,8 +4,6 @@ import { BaseProvider } from "../core/baseProvider.js";
|
|
|
4
4
|
import { AIProviderName, APIVersions } from "../constants/enums.js";
|
|
5
5
|
import { validateApiKey, createAzureAPIKeyConfig, createAzureEndpointConfig, } from "../utils/providerConfig.js";
|
|
6
6
|
import { logger } from "../utils/logger.js";
|
|
7
|
-
import { buildMessagesArray, buildMultimodalMessagesArray, convertToCoreMessages, } from "../utils/messageBuilder.js";
|
|
8
|
-
import { buildMultimodalOptions } from "../utils/multimodalOptionsBuilder.js";
|
|
9
7
|
import { createProxyFetch } from "../proxy/proxyFetch.js";
|
|
10
8
|
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
11
9
|
export class AzureOpenAIProvider extends BaseProvider {
|
|
@@ -111,28 +109,8 @@ export class AzureOpenAIProvider extends BaseProvider {
|
|
|
111
109
|
});
|
|
112
110
|
}
|
|
113
111
|
// Build message array from options with multimodal support
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
options.input?.files?.length ||
|
|
117
|
-
options.input?.csvFiles?.length ||
|
|
118
|
-
options.input?.pdfFiles?.length);
|
|
119
|
-
let messages;
|
|
120
|
-
if (hasMultimodalInput) {
|
|
121
|
-
logger.debug(`Azure OpenAI: Detected multimodal input, using multimodal message builder`, {
|
|
122
|
-
hasImages: !!options.input?.images?.length,
|
|
123
|
-
imageCount: options.input?.images?.length || 0,
|
|
124
|
-
hasContent: !!options.input?.content?.length,
|
|
125
|
-
contentCount: options.input?.content?.length || 0,
|
|
126
|
-
});
|
|
127
|
-
const multimodalOptions = buildMultimodalOptions(options, this.providerName, this.modelName);
|
|
128
|
-
const mm = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
|
129
|
-
// Convert multimodal messages to Vercel AI SDK format (CoreMessage[])
|
|
130
|
-
messages = convertToCoreMessages(mm);
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
logger.debug(`Azure OpenAI: Text-only input, using standard message builder`);
|
|
134
|
-
messages = await buildMessagesArray(options);
|
|
135
|
-
}
|
|
112
|
+
// Using protected helper from BaseProvider to eliminate code duplication
|
|
113
|
+
const messages = await this.buildMessagesForStream(options);
|
|
136
114
|
const model = await this.getAISDKModelWithMiddleware(options);
|
|
137
115
|
const stream = await streamText({
|
|
138
116
|
model,
|
|
@@ -7,8 +7,6 @@ import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
|
|
|
7
7
|
import { AuthenticationError, NetworkError, ProviderError, RateLimitError, } from "../types/errors.js";
|
|
8
8
|
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
9
9
|
import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
|
|
10
|
-
import { buildMessagesArray, buildMultimodalMessagesArray, convertToCoreMessages, } from "../utils/messageBuilder.js";
|
|
11
|
-
import { buildMultimodalOptions } from "../utils/multimodalOptionsBuilder.js";
|
|
12
10
|
// Google AI Live API types now imported from ../types/providerSpecific.js
|
|
13
11
|
// Import proper types for multimodal message handling
|
|
14
12
|
// Create Google GenAI client
|
|
@@ -93,28 +91,8 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
93
91
|
const shouldUseTools = !options.disableTools && this.supportsTools();
|
|
94
92
|
const tools = shouldUseTools ? await this.getAllTools() : {};
|
|
95
93
|
// Build message array from options with multimodal support
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
options.input?.files?.length ||
|
|
99
|
-
options.input?.csvFiles?.length ||
|
|
100
|
-
options.input?.pdfFiles?.length);
|
|
101
|
-
let messages;
|
|
102
|
-
if (hasMultimodalInput) {
|
|
103
|
-
logger.debug(`Google AI Studio: Detected multimodal input, using multimodal message builder`, {
|
|
104
|
-
hasImages: !!options.input?.images?.length,
|
|
105
|
-
imageCount: options.input?.images?.length || 0,
|
|
106
|
-
hasContent: !!options.input?.content?.length,
|
|
107
|
-
contentCount: options.input?.content?.length || 0,
|
|
108
|
-
});
|
|
109
|
-
const multimodalOptions = buildMultimodalOptions(options, this.providerName, this.modelName);
|
|
110
|
-
const mm = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
|
111
|
-
// Convert multimodal messages to Vercel AI SDK format (CoreMessage[])
|
|
112
|
-
messages = convertToCoreMessages(mm);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
logger.debug(`Google AI Studio: Text-only input, using standard message builder`);
|
|
116
|
-
messages = await buildMessagesArray(options);
|
|
117
|
-
}
|
|
94
|
+
// Using protected helper from BaseProvider to eliminate code duplication
|
|
95
|
+
const messages = await this.buildMessagesForStream(options);
|
|
118
96
|
const result = await streamText({
|
|
119
97
|
model,
|
|
120
98
|
messages: messages,
|