@juspay/neurolink 8.3.0 → 8.4.1
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 +1 -0
- 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/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/memory/mem0Initializer.d.ts +32 -1
- package/dist/lib/memory/mem0Initializer.js +55 -2
- package/dist/lib/models/modelRegistry.js +44 -0
- package/dist/lib/neurolink.d.ts +1 -1
- package/dist/lib/neurolink.js +43 -10
- 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/memory/mem0Initializer.d.ts +32 -1
- package/dist/memory/mem0Initializer.js +55 -2
- package/dist/models/modelRegistry.js +44 -0
- package/dist/neurolink.d.ts +1 -1
- package/dist/neurolink.js +43 -10
- 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,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities Module
|
|
3
|
+
*
|
|
4
|
+
* Handles validation, normalization, schema conversion, and error handling utilities.
|
|
5
|
+
* Extracted from BaseProvider to follow Single Responsibility Principle.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Options validation (text generation and stream options)
|
|
9
|
+
* - Options normalization (string to object conversion)
|
|
10
|
+
* - Provider information formatting
|
|
11
|
+
* - Timeout parsing and calculation
|
|
12
|
+
* - Schema utilities (Zod detection, permissive schema creation, OpenAI strict mode fixes)
|
|
13
|
+
* - Tool result conversion
|
|
14
|
+
* - Middleware options extraction
|
|
15
|
+
* - Common error pattern handling
|
|
16
|
+
*
|
|
17
|
+
* @module core/modules/Utilities
|
|
18
|
+
*/
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import { logger } from "../../utils/logger.js";
|
|
21
|
+
import { getSafeMaxTokens } from "../../utils/tokenLimits.js";
|
|
22
|
+
import { TimeoutError } from "../../utils/timeout.js";
|
|
23
|
+
import { validateStreamOptions as validateStreamOpts, validateTextGenerationOptions, ValidationError, createValidationSummary, } from "../../utils/parameterValidation.js";
|
|
24
|
+
import { STEP_LIMITS } from "../constants.js";
|
|
25
|
+
/**
|
|
26
|
+
* Utilities class - Provides validation, normalization, and utility methods
|
|
27
|
+
*/
|
|
28
|
+
export class Utilities {
|
|
29
|
+
providerName;
|
|
30
|
+
modelName;
|
|
31
|
+
defaultTimeout;
|
|
32
|
+
middlewareOptions;
|
|
33
|
+
constructor(providerName, modelName, defaultTimeout = 30000, middlewareOptions) {
|
|
34
|
+
this.providerName = providerName;
|
|
35
|
+
this.modelName = modelName;
|
|
36
|
+
this.defaultTimeout = defaultTimeout;
|
|
37
|
+
this.middlewareOptions = middlewareOptions;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate text generation options
|
|
41
|
+
*/
|
|
42
|
+
validateOptions(options) {
|
|
43
|
+
const validation = validateTextGenerationOptions(options);
|
|
44
|
+
if (!validation.isValid) {
|
|
45
|
+
const summary = createValidationSummary(validation);
|
|
46
|
+
throw new ValidationError(`Text generation options validation failed: ${summary}`, "options", "VALIDATION_FAILED", validation.suggestions);
|
|
47
|
+
}
|
|
48
|
+
// Log warnings if any
|
|
49
|
+
if (validation.warnings.length > 0) {
|
|
50
|
+
logger.warn("Text generation options validation warnings:", validation.warnings);
|
|
51
|
+
}
|
|
52
|
+
// Additional BaseProvider-specific validation
|
|
53
|
+
if (options.maxSteps !== undefined) {
|
|
54
|
+
if (options.maxSteps < STEP_LIMITS.min ||
|
|
55
|
+
options.maxSteps > STEP_LIMITS.max) {
|
|
56
|
+
throw new ValidationError(`maxSteps must be between ${STEP_LIMITS.min} and ${STEP_LIMITS.max}`, "maxSteps", "OUT_OF_RANGE", [
|
|
57
|
+
`Use a value between ${STEP_LIMITS.min} and ${STEP_LIMITS.max} for optimal performance`,
|
|
58
|
+
]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validate stream options
|
|
64
|
+
*/
|
|
65
|
+
validateStreamOptions(options) {
|
|
66
|
+
const validation = validateStreamOpts(options);
|
|
67
|
+
if (!validation.isValid) {
|
|
68
|
+
const summary = createValidationSummary(validation);
|
|
69
|
+
throw new ValidationError(`Stream options validation failed: ${summary}`, "options", "VALIDATION_FAILED", validation.suggestions);
|
|
70
|
+
}
|
|
71
|
+
// Log warnings if any
|
|
72
|
+
if (validation.warnings.length > 0) {
|
|
73
|
+
logger.warn("Stream options validation warnings:", validation.warnings);
|
|
74
|
+
}
|
|
75
|
+
// Additional BaseProvider-specific validation
|
|
76
|
+
if (options.maxSteps !== undefined) {
|
|
77
|
+
if (options.maxSteps < STEP_LIMITS.min ||
|
|
78
|
+
options.maxSteps > STEP_LIMITS.max) {
|
|
79
|
+
throw new ValidationError(`maxSteps must be between ${STEP_LIMITS.min} and ${STEP_LIMITS.max}`, "maxSteps", "OUT_OF_RANGE", [
|
|
80
|
+
`Use a value between ${STEP_LIMITS.min} and ${STEP_LIMITS.max} for optimal performance`,
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Normalize text generation options from string or object
|
|
87
|
+
*/
|
|
88
|
+
normalizeTextOptions(optionsOrPrompt) {
|
|
89
|
+
if (typeof optionsOrPrompt === "string") {
|
|
90
|
+
const safeMaxTokens = getSafeMaxTokens(this.providerName, this.modelName);
|
|
91
|
+
return {
|
|
92
|
+
prompt: optionsOrPrompt,
|
|
93
|
+
provider: this.providerName,
|
|
94
|
+
model: this.modelName,
|
|
95
|
+
maxTokens: safeMaxTokens,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Handle both prompt and input.text formats
|
|
99
|
+
const prompt = optionsOrPrompt.prompt || optionsOrPrompt.input?.text || "";
|
|
100
|
+
const modelName = optionsOrPrompt.model || this.modelName;
|
|
101
|
+
const providerName = optionsOrPrompt.provider || this.providerName;
|
|
102
|
+
// Apply safe maxTokens based on provider and model
|
|
103
|
+
const safeMaxTokens = getSafeMaxTokens(providerName, modelName, optionsOrPrompt.maxTokens);
|
|
104
|
+
// CRITICAL FIX: Preserve the entire input object for multimodal support
|
|
105
|
+
// This ensures images and content arrays are not lost during normalization
|
|
106
|
+
const normalizedOptions = {
|
|
107
|
+
...optionsOrPrompt,
|
|
108
|
+
prompt,
|
|
109
|
+
provider: providerName,
|
|
110
|
+
model: modelName,
|
|
111
|
+
maxTokens: safeMaxTokens,
|
|
112
|
+
};
|
|
113
|
+
// Ensure input object is preserved if it exists (for multimodal support)
|
|
114
|
+
if (optionsOrPrompt.input) {
|
|
115
|
+
normalizedOptions.input = {
|
|
116
|
+
...optionsOrPrompt.input,
|
|
117
|
+
text: prompt, // Ensure text is consistent
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return normalizedOptions;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Normalize stream options from string or object
|
|
124
|
+
*/
|
|
125
|
+
normalizeStreamOptions(optionsOrPrompt) {
|
|
126
|
+
if (typeof optionsOrPrompt === "string") {
|
|
127
|
+
const safeMaxTokens = getSafeMaxTokens(this.providerName, this.modelName);
|
|
128
|
+
return {
|
|
129
|
+
input: { text: optionsOrPrompt },
|
|
130
|
+
provider: this.providerName,
|
|
131
|
+
model: this.modelName,
|
|
132
|
+
maxTokens: safeMaxTokens,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const modelName = optionsOrPrompt.model || this.modelName;
|
|
136
|
+
const providerName = optionsOrPrompt.provider || this.providerName;
|
|
137
|
+
// Apply safe maxTokens based on provider and model
|
|
138
|
+
const safeMaxTokens = getSafeMaxTokens(providerName, modelName, optionsOrPrompt.maxTokens);
|
|
139
|
+
return {
|
|
140
|
+
...optionsOrPrompt,
|
|
141
|
+
provider: providerName,
|
|
142
|
+
model: modelName,
|
|
143
|
+
maxTokens: safeMaxTokens,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get provider information
|
|
148
|
+
*/
|
|
149
|
+
getProviderInfo() {
|
|
150
|
+
return {
|
|
151
|
+
provider: this.providerName,
|
|
152
|
+
model: this.modelName,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get timeout value in milliseconds from options
|
|
157
|
+
* Supports number or string formats (e.g., '30s', '2m', '1h')
|
|
158
|
+
*/
|
|
159
|
+
getTimeout(options) {
|
|
160
|
+
if (!options.timeout) {
|
|
161
|
+
return this.defaultTimeout;
|
|
162
|
+
}
|
|
163
|
+
if (typeof options.timeout === "number") {
|
|
164
|
+
return options.timeout;
|
|
165
|
+
}
|
|
166
|
+
// Parse string timeout (e.g., '30s', '2m', '1h')
|
|
167
|
+
const timeoutStr = options.timeout.toLowerCase();
|
|
168
|
+
const value = parseInt(timeoutStr);
|
|
169
|
+
if (timeoutStr.includes("h")) {
|
|
170
|
+
return value * 60 * 60 * 1000;
|
|
171
|
+
}
|
|
172
|
+
else if (timeoutStr.includes("m")) {
|
|
173
|
+
return value * 60 * 1000;
|
|
174
|
+
}
|
|
175
|
+
else if (timeoutStr.includes("s")) {
|
|
176
|
+
return value * 1000;
|
|
177
|
+
}
|
|
178
|
+
return this.defaultTimeout;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Check if a schema is a Zod schema
|
|
182
|
+
*/
|
|
183
|
+
isZodSchema(schema) {
|
|
184
|
+
return (typeof schema === "object" &&
|
|
185
|
+
schema !== null &&
|
|
186
|
+
// Most Zod schemas have an internal _def and a parse method
|
|
187
|
+
typeof schema.parse === "function");
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Convert tool execution result from MCP format to standard format
|
|
191
|
+
* Handles tool failures gracefully to prevent stream termination
|
|
192
|
+
*/
|
|
193
|
+
async convertToolResult(result) {
|
|
194
|
+
// Handle MCP-style results
|
|
195
|
+
if (result && typeof result === "object" && "success" in result) {
|
|
196
|
+
const mcpResult = result;
|
|
197
|
+
if (mcpResult.success) {
|
|
198
|
+
return mcpResult.data;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// Instead of throwing, return a structured error result
|
|
202
|
+
// This prevents tool failures from terminating streams
|
|
203
|
+
const errorMsg = typeof mcpResult.error === "string"
|
|
204
|
+
? mcpResult.error
|
|
205
|
+
: "Tool execution failed";
|
|
206
|
+
// Log the error for debugging but don't throw
|
|
207
|
+
logger.warn(`Tool execution failed: ${errorMsg}`);
|
|
208
|
+
// Return error as structured data that can be processed by the AI
|
|
209
|
+
return {
|
|
210
|
+
isError: true,
|
|
211
|
+
error: errorMsg,
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: "text",
|
|
215
|
+
text: `Tool execution failed: ${errorMsg}`,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Create a permissive Zod schema that accepts all parameters as-is
|
|
225
|
+
*/
|
|
226
|
+
createPermissiveZodSchema() {
|
|
227
|
+
// Create a permissive record that accepts any object structure
|
|
228
|
+
// This allows all parameters to pass through without validation issues
|
|
229
|
+
return z.record(z.unknown()).transform((data) => {
|
|
230
|
+
// Return the data as-is to preserve all parameter information
|
|
231
|
+
return data;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Recursively fix JSON Schema for OpenAI strict mode compatibility
|
|
236
|
+
* OpenAI requires additionalProperties: false at ALL levels and preserves required array
|
|
237
|
+
*/
|
|
238
|
+
fixSchemaForOpenAIStrictMode(schema) {
|
|
239
|
+
const fixedSchema = JSON.parse(JSON.stringify(schema));
|
|
240
|
+
if (fixedSchema.type === "object" &&
|
|
241
|
+
fixedSchema.properties &&
|
|
242
|
+
typeof fixedSchema.properties === "object") {
|
|
243
|
+
const allPropertyNames = Object.keys(fixedSchema.properties);
|
|
244
|
+
if (!fixedSchema.required || !Array.isArray(fixedSchema.required)) {
|
|
245
|
+
fixedSchema.required = [];
|
|
246
|
+
}
|
|
247
|
+
fixedSchema.additionalProperties = false;
|
|
248
|
+
for (const propName of allPropertyNames) {
|
|
249
|
+
const propValue = fixedSchema.properties[propName];
|
|
250
|
+
if (propValue && typeof propValue === "object") {
|
|
251
|
+
if (propValue.type === "object") {
|
|
252
|
+
fixedSchema.properties[propName] =
|
|
253
|
+
this.fixSchemaForOpenAIStrictMode(propValue);
|
|
254
|
+
}
|
|
255
|
+
else if (propValue.type === "array" &&
|
|
256
|
+
propValue.items &&
|
|
257
|
+
typeof propValue.items === "object") {
|
|
258
|
+
fixedSchema.properties[propName].items =
|
|
259
|
+
this.fixSchemaForOpenAIStrictMode(propValue.items);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return fixedSchema;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Extract middleware options from generation/stream options
|
|
268
|
+
* This is the single source of truth for deciding if middleware should be applied
|
|
269
|
+
*/
|
|
270
|
+
extractMiddlewareOptions(options) {
|
|
271
|
+
// 1. Determine effective middleware config: per-request overrides global.
|
|
272
|
+
const middlewareOpts = options.middleware ??
|
|
273
|
+
this.middlewareOptions;
|
|
274
|
+
if (!middlewareOpts) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
// 2. The middleware property must be an object with configuration.
|
|
278
|
+
if (typeof middlewareOpts !== "object" ||
|
|
279
|
+
middlewareOpts === null ||
|
|
280
|
+
middlewareOpts instanceof Date ||
|
|
281
|
+
middlewareOpts instanceof RegExp) {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
// 3. Check if the middleware object has any actual configuration keys.
|
|
285
|
+
const fullOpts = middlewareOpts;
|
|
286
|
+
const hasArray = (arr) => Array.isArray(arr) && arr.length > 0;
|
|
287
|
+
const hasConfig = !!fullOpts.middlewareConfig ||
|
|
288
|
+
hasArray(fullOpts.enabledMiddleware) ||
|
|
289
|
+
hasArray(fullOpts.disabledMiddleware) ||
|
|
290
|
+
!!fullOpts.preset ||
|
|
291
|
+
hasArray(fullOpts.middleware);
|
|
292
|
+
if (!hasConfig) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
// 4. Return the formatted options if configuration is present.
|
|
296
|
+
return {
|
|
297
|
+
...fullOpts,
|
|
298
|
+
global: {
|
|
299
|
+
collectStats: true,
|
|
300
|
+
continueOnError: true,
|
|
301
|
+
...(fullOpts.global || {}),
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Handle common error patterns across providers
|
|
307
|
+
* Returns transformed error or null if not a common pattern
|
|
308
|
+
*/
|
|
309
|
+
handleCommonErrors(error) {
|
|
310
|
+
if (error instanceof TimeoutError) {
|
|
311
|
+
return new Error(`${this.providerName} request timed out after ${error.timeout}ms. Consider increasing timeout or using a lighter model.`);
|
|
312
|
+
}
|
|
313
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
314
|
+
// Common API key errors
|
|
315
|
+
if (message.includes("API_KEY_INVALID") ||
|
|
316
|
+
message.includes("Invalid API key") ||
|
|
317
|
+
message.includes("authentication") ||
|
|
318
|
+
message.includes("unauthorized")) {
|
|
319
|
+
return new Error(`Invalid API key for ${this.providerName}. Please check your API key environment variable.`);
|
|
320
|
+
}
|
|
321
|
+
// Common rate limit errors
|
|
322
|
+
if (message.includes("rate limit") ||
|
|
323
|
+
message.includes("quota") ||
|
|
324
|
+
message.includes("429")) {
|
|
325
|
+
return new Error(`Rate limit exceeded for ${this.providerName}. Please wait before making more requests.`);
|
|
326
|
+
}
|
|
327
|
+
return null; // Not a common error, let provider handle it
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=Utilities.js.map
|
|
@@ -24,7 +24,7 @@ export class ProviderRegistry {
|
|
|
24
24
|
ProviderFactory.registerProvider(AIProviderName.GOOGLE_AI, async (modelName, _providerName, sdk) => {
|
|
25
25
|
const { GoogleAIStudioProvider } = await import("../providers/googleAiStudio.js");
|
|
26
26
|
return new GoogleAIStudioProvider(modelName, sdk);
|
|
27
|
-
}, GoogleAIModels.GEMINI_2_5_FLASH, ["googleAiStudio", "google", "gemini", "google-ai"]);
|
|
27
|
+
}, GoogleAIModels.GEMINI_2_5_FLASH, ["googleAiStudio", "google", "gemini", "google-ai", "google-ai-studio"]);
|
|
28
28
|
// Register OpenAI provider
|
|
29
29
|
ProviderFactory.registerProvider(AIProviderName.OPENAI, async (modelName, _providerName, sdk) => {
|
|
30
30
|
const { OpenAIProvider } = await import("../providers/openAI.js");
|
|
@@ -8,8 +8,39 @@ import { MemoryClient } from "mem0ai";
|
|
|
8
8
|
*/
|
|
9
9
|
export interface Mem0Config {
|
|
10
10
|
apiKey: string;
|
|
11
|
+
/**
|
|
12
|
+
* Optional organization ID - if not provided, will be auto-populated from ping() response
|
|
13
|
+
*/
|
|
14
|
+
organizationId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Optional project ID - if not provided, will be auto-populated from ping() response
|
|
17
|
+
*/
|
|
18
|
+
projectId?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Whether to update project-level custom instructions during initialization
|
|
21
|
+
* Default: false (don't update project settings)
|
|
22
|
+
*
|
|
23
|
+
* Note: organizationId and projectId are NOT required - they will be auto-populated
|
|
24
|
+
* from the mem0 API via ping() if not provided
|
|
25
|
+
*/
|
|
26
|
+
updateProjectSettings?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Custom instructions and categories for mem0 extraction behavior
|
|
29
|
+
* Only used if updateProjectSettings is true
|
|
30
|
+
*/
|
|
31
|
+
customPrompts?: {
|
|
32
|
+
/**
|
|
33
|
+
* Custom instructions for how mem0 should extract and store memories
|
|
34
|
+
* This applies to ALL memories added to the project
|
|
35
|
+
*/
|
|
36
|
+
custom_instructions?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Custom categories for organizing memories
|
|
39
|
+
*/
|
|
40
|
+
custom_categories?: Array<Record<string, unknown>>;
|
|
41
|
+
};
|
|
11
42
|
}
|
|
12
43
|
/**
|
|
13
|
-
* Initialize mem0 memory instance with cloud API
|
|
44
|
+
* Initialize mem0 memory instance with cloud API and optional project settings
|
|
14
45
|
*/
|
|
15
46
|
export declare function initializeMem0(mem0Config: Mem0Config): Promise<MemoryClient | null>;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { MemoryClient } from "mem0ai";
|
|
6
6
|
import { logger } from "../utils/logger.js";
|
|
7
7
|
/**
|
|
8
|
-
* Initialize mem0 memory instance with cloud API
|
|
8
|
+
* Initialize mem0 memory instance with cloud API and optional project settings
|
|
9
9
|
*/
|
|
10
10
|
export async function initializeMem0(mem0Config) {
|
|
11
11
|
// Guard: skip initialization if API key is missing
|
|
@@ -18,8 +18,61 @@ export async function initializeMem0(mem0Config) {
|
|
|
18
18
|
// Create MemoryClient instance with cloud API
|
|
19
19
|
const client = new MemoryClient({
|
|
20
20
|
apiKey: mem0Config.apiKey,
|
|
21
|
+
organizationId: mem0Config.organizationId,
|
|
22
|
+
projectId: mem0Config.projectId,
|
|
23
|
+
});
|
|
24
|
+
// Track whether project settings were actually updated (not just requested)
|
|
25
|
+
let projectSettingsUpdated = false;
|
|
26
|
+
// Update project-level settings if requested
|
|
27
|
+
if (mem0Config.updateProjectSettings && mem0Config.customPrompts) {
|
|
28
|
+
// Build update payload - only include fields that are actually provided
|
|
29
|
+
const updatePayload = {};
|
|
30
|
+
if (mem0Config.customPrompts.custom_instructions &&
|
|
31
|
+
mem0Config.customPrompts.custom_instructions.trim() !== "") {
|
|
32
|
+
updatePayload.custom_instructions =
|
|
33
|
+
mem0Config.customPrompts.custom_instructions;
|
|
34
|
+
}
|
|
35
|
+
if (Array.isArray(mem0Config.customPrompts.custom_categories) &&
|
|
36
|
+
mem0Config.customPrompts.custom_categories.length > 0) {
|
|
37
|
+
updatePayload.custom_categories =
|
|
38
|
+
mem0Config.customPrompts.custom_categories;
|
|
39
|
+
}
|
|
40
|
+
// Only proceed if there's something to update
|
|
41
|
+
if (Object.keys(updatePayload).length > 0) {
|
|
42
|
+
try {
|
|
43
|
+
// Note: updateProject() internally calls ping() first, which auto-populates
|
|
44
|
+
// organizationId and projectId from the server, so they're not required
|
|
45
|
+
await client.updateProject(updatePayload);
|
|
46
|
+
projectSettingsUpdated = true; // Only set to true on successful update
|
|
47
|
+
logger.info("[mem0Initializer] Project settings updated successfully", {
|
|
48
|
+
hasInstructions: !!updatePayload.custom_instructions,
|
|
49
|
+
hasCategories: !!updatePayload.custom_categories,
|
|
50
|
+
// Note: These IDs are auto-populated by ping() inside updateProject()
|
|
51
|
+
organizationId: client.organizationId,
|
|
52
|
+
projectId: client.projectId,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
logger.warn("[mem0Initializer] Failed to update project settings", {
|
|
57
|
+
error: error instanceof Error ? error.message : String(error),
|
|
58
|
+
hint: "Ensure your MEM0_API_KEY has permission to update project settings",
|
|
59
|
+
});
|
|
60
|
+
// Continue initialization even if project update fails
|
|
61
|
+
// projectSettingsUpdated remains false
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
logger.warn("[mem0Initializer] updateProjectSettings=true but no custom instructions or categories provided - nothing to update");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (mem0Config.updateProjectSettings && !mem0Config.customPrompts) {
|
|
69
|
+
logger.warn("[mem0Initializer] updateProjectSettings=true but customPrompts not provided - nothing to update");
|
|
70
|
+
}
|
|
71
|
+
logger.info("[mem0Initializer] Mem0 cloud API initialized successfully", {
|
|
72
|
+
hasOrgId: !!client.organizationId,
|
|
73
|
+
hasProjectId: !!client.projectId,
|
|
74
|
+
projectSettingsUpdated,
|
|
21
75
|
});
|
|
22
|
-
logger.info("[mem0Initializer] Mem0 cloud API initialized successfully");
|
|
23
76
|
return client;
|
|
24
77
|
}
|
|
25
78
|
catch (error) {
|
|
@@ -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/lib/neurolink.d.ts
CHANGED
|
@@ -137,7 +137,7 @@ export declare class NeuroLink {
|
|
|
137
137
|
/** Extract memory context from search results */
|
|
138
138
|
private extractMemoryContext;
|
|
139
139
|
/** Store conversation turn in mem0 */
|
|
140
|
-
private
|
|
140
|
+
private storeMem0ConversationTurn;
|
|
141
141
|
/**
|
|
142
142
|
* Set up HITL event forwarding to main emitter
|
|
143
143
|
*/
|
package/dist/lib/neurolink.js
CHANGED
|
@@ -402,9 +402,12 @@ Current user's request: ${currentInput}`;
|
|
|
402
402
|
.join("\n");
|
|
403
403
|
}
|
|
404
404
|
/** Store conversation turn in mem0 */
|
|
405
|
-
async
|
|
406
|
-
// Store user message
|
|
407
|
-
const conversationTurn = [
|
|
405
|
+
async storeMem0ConversationTurn(mem0, userContent, aiResponse, userId, metadata) {
|
|
406
|
+
// Store both user message and AI response for better context extraction
|
|
407
|
+
const conversationTurn = [
|
|
408
|
+
{ role: "user", content: userContent },
|
|
409
|
+
{ role: "assistant", content: aiResponse },
|
|
410
|
+
];
|
|
408
411
|
await mem0.add(conversationTurn, {
|
|
409
412
|
user_id: userId,
|
|
410
413
|
metadata,
|
|
@@ -455,7 +458,7 @@ Current user's request: ${currentInput}`;
|
|
|
455
458
|
try {
|
|
456
459
|
this.externalServerManager = new ExternalServerManager({
|
|
457
460
|
maxServers: 20,
|
|
458
|
-
defaultTimeout:
|
|
461
|
+
defaultTimeout: 30000, // Increased from 15s to 30s for proxy latency (e.g., LiteLLM)
|
|
459
462
|
enableAutoRestart: true,
|
|
460
463
|
enablePerformanceMonitoring: true,
|
|
461
464
|
}, {
|
|
@@ -1366,7 +1369,7 @@ Current user's request: ${currentInput}`;
|
|
|
1366
1369
|
try {
|
|
1367
1370
|
const mem0 = await this.ensureMem0Ready();
|
|
1368
1371
|
if (mem0) {
|
|
1369
|
-
await this.
|
|
1372
|
+
await this.storeMem0ConversationTurn(mem0, originalPrompt, generateResult.content, options.context?.userId, {
|
|
1370
1373
|
timestamp: new Date().toISOString(),
|
|
1371
1374
|
provider: generateResult.provider,
|
|
1372
1375
|
model: generateResult.model,
|
|
@@ -1963,6 +1966,29 @@ Current user's request: ${currentInput}`;
|
|
|
1963
1966
|
// Continue with original options if orchestration fails
|
|
1964
1967
|
}
|
|
1965
1968
|
}
|
|
1969
|
+
// 🔧 AUTO-DISABLE TOOLS: For Ollama models that don't support tools (same logic as generate())
|
|
1970
|
+
// This prevents overwhelming smaller models with massive tool descriptions in the system message
|
|
1971
|
+
if ((options.provider === "ollama" ||
|
|
1972
|
+
options.provider?.toLowerCase().includes("ollama")) &&
|
|
1973
|
+
!options.disableTools) {
|
|
1974
|
+
const { ModelConfigurationManager } = await import("./core/modelConfiguration.js");
|
|
1975
|
+
const modelConfig = ModelConfigurationManager.getInstance();
|
|
1976
|
+
const ollamaConfig = modelConfig.getProviderConfiguration("ollama");
|
|
1977
|
+
const toolCapableModels = ollamaConfig?.modelBehavior?.toolCapableModels || [];
|
|
1978
|
+
// Only disable tools if we have explicit evidence the model doesn't support them
|
|
1979
|
+
// If toolCapableModels is empty or model is not specified, don't make assumptions
|
|
1980
|
+
const modelName = options.model;
|
|
1981
|
+
if (toolCapableModels.length > 0 && modelName) {
|
|
1982
|
+
const modelSupportsTools = toolCapableModels.some((capableModel) => modelName.toLowerCase().includes(capableModel.toLowerCase()));
|
|
1983
|
+
if (!modelSupportsTools) {
|
|
1984
|
+
options.disableTools = true;
|
|
1985
|
+
logger.debug("Auto-disabled tools for Ollama model that doesn't support them (stream)", {
|
|
1986
|
+
model: options.model,
|
|
1987
|
+
toolCapableModels: toolCapableModels.slice(0, 3), // Show first 3 for brevity
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1966
1992
|
factoryResult = processStreamingFactoryOptions(options);
|
|
1967
1993
|
enhancedOptions = createCleanStreamOptions(options);
|
|
1968
1994
|
if (options.input?.text) {
|
|
@@ -2014,11 +2040,9 @@ Current user's request: ${currentInput}`;
|
|
|
2014
2040
|
try {
|
|
2015
2041
|
const mem0 = await self.ensureMem0Ready();
|
|
2016
2042
|
if (mem0) {
|
|
2017
|
-
await self.
|
|
2043
|
+
await self.storeMem0ConversationTurn(mem0, originalPrompt, accumulatedContent.trim(), enhancedOptions.context?.userId, {
|
|
2018
2044
|
timestamp: new Date().toISOString(),
|
|
2019
2045
|
type: "conversation_turn_stream",
|
|
2020
|
-
userMessage: originalPrompt,
|
|
2021
|
-
aiResponse: accumulatedContent.trim(),
|
|
2022
2046
|
});
|
|
2023
2047
|
}
|
|
2024
2048
|
}
|
|
@@ -2091,17 +2115,26 @@ Current user's request: ${currentInput}`;
|
|
|
2091
2115
|
customTools: this.getCustomTools(),
|
|
2092
2116
|
executeTool: this.executeTool.bind(this),
|
|
2093
2117
|
}, "NeuroLink.createMCPStream");
|
|
2118
|
+
// 🔧 FIX: Get available tools and create tool-aware system prompt
|
|
2119
|
+
// Use SAME pattern as tryMCPGeneration (generate mode)
|
|
2120
|
+
const availableTools = await this.getAllAvailableTools();
|
|
2121
|
+
const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
|
|
2094
2122
|
// Get conversation messages for context
|
|
2095
2123
|
const conversationMessages = await getConversationMessages(this.conversationMemory, {
|
|
2096
2124
|
prompt: options.input.text,
|
|
2097
2125
|
context: options.context,
|
|
2098
2126
|
});
|
|
2099
|
-
//
|
|
2100
|
-
//
|
|
2127
|
+
// 🔧 FIX: Pass enhanced system prompt to real streaming
|
|
2128
|
+
// Tools will be accessed through the streamText call in executeStream
|
|
2101
2129
|
const streamResult = await provider.stream({
|
|
2102
2130
|
...options,
|
|
2131
|
+
systemPrompt: enhancedSystemPrompt, // Use enhanced prompt with tool descriptions
|
|
2103
2132
|
conversationMessages,
|
|
2104
2133
|
});
|
|
2134
|
+
logger.debug("[createMCPStream] Stream created successfully", {
|
|
2135
|
+
provider: providerName,
|
|
2136
|
+
systemPromptPassedLength: enhancedSystemPrompt.length,
|
|
2137
|
+
});
|
|
2105
2138
|
return { stream: streamResult.stream, provider: providerName };
|
|
2106
2139
|
}
|
|
2107
2140
|
/**
|