@juspay/neurolink 7.1.0 → 7.3.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 +15 -2
- package/README.md +16 -11
- package/dist/cli/commands/config.d.ts +2 -2
- package/dist/cli/commands/config.js +22 -21
- package/dist/cli/commands/mcp.d.ts +79 -0
- package/dist/cli/commands/mcp.js +916 -0
- package/dist/cli/commands/models.d.ts +63 -0
- package/dist/cli/commands/models.js +653 -0
- package/dist/cli/commands/ollama.js +56 -55
- package/dist/cli/factories/commandFactory.d.ts +14 -0
- package/dist/cli/factories/commandFactory.js +346 -47
- package/dist/cli/index.js +25 -10
- package/dist/cli/utils/completeSetup.js +9 -8
- package/dist/cli/utils/envManager.js +7 -6
- package/dist/cli/utils/interactiveSetup.js +20 -19
- package/dist/core/analytics.js +25 -38
- package/dist/core/baseProvider.d.ts +8 -0
- package/dist/core/baseProvider.js +177 -68
- package/dist/core/constants.d.ts +11 -0
- package/dist/core/constants.js +17 -0
- package/dist/core/evaluation.js +25 -14
- package/dist/core/factory.js +19 -18
- package/dist/core/streamAnalytics.d.ts +65 -0
- package/dist/core/streamAnalytics.js +125 -0
- package/dist/lib/core/analytics.js +25 -38
- package/dist/lib/core/baseProvider.d.ts +8 -0
- package/dist/lib/core/baseProvider.js +177 -68
- package/dist/lib/core/constants.d.ts +11 -0
- package/dist/lib/core/constants.js +17 -0
- package/dist/lib/core/evaluation.js +25 -14
- package/dist/lib/core/factory.js +19 -18
- package/dist/lib/core/streamAnalytics.d.ts +65 -0
- package/dist/lib/core/streamAnalytics.js +125 -0
- package/dist/lib/models/modelRegistry.d.ts +132 -0
- package/dist/lib/models/modelRegistry.js +483 -0
- package/dist/lib/models/modelResolver.d.ts +115 -0
- package/dist/lib/models/modelResolver.js +467 -0
- package/dist/lib/neurolink.d.ts +4 -1
- package/dist/lib/neurolink.js +101 -67
- package/dist/lib/providers/anthropic.js +3 -0
- package/dist/lib/providers/googleAiStudio.js +13 -0
- package/dist/lib/providers/huggingFace.js +15 -3
- package/dist/lib/providers/mistral.js +19 -7
- package/dist/lib/providers/ollama.js +31 -7
- package/dist/lib/providers/openAI.js +12 -0
- package/dist/lib/sdk/toolRegistration.js +2 -2
- package/dist/lib/types/cli.d.ts +56 -1
- package/dist/lib/types/contextTypes.d.ts +110 -0
- package/dist/lib/types/contextTypes.js +176 -0
- package/dist/lib/types/index.d.ts +4 -1
- package/dist/lib/types/mcpTypes.d.ts +118 -7
- package/dist/lib/types/providers.d.ts +81 -0
- package/dist/lib/types/streamTypes.d.ts +44 -7
- package/dist/lib/types/tools.d.ts +9 -0
- package/dist/lib/types/universalProviderOptions.d.ts +3 -1
- package/dist/lib/types/universalProviderOptions.js +2 -1
- package/dist/lib/utils/logger.d.ts +7 -0
- package/dist/lib/utils/logger.js +11 -0
- package/dist/lib/utils/performance.d.ts +105 -0
- package/dist/lib/utils/performance.js +210 -0
- package/dist/lib/utils/retryHandler.d.ts +89 -0
- package/dist/lib/utils/retryHandler.js +269 -0
- package/dist/models/modelRegistry.d.ts +132 -0
- package/dist/models/modelRegistry.js +483 -0
- package/dist/models/modelResolver.d.ts +115 -0
- package/dist/models/modelResolver.js +468 -0
- package/dist/neurolink.d.ts +4 -1
- package/dist/neurolink.js +101 -67
- package/dist/providers/anthropic.js +3 -0
- package/dist/providers/googleAiStudio.js +13 -0
- package/dist/providers/huggingFace.js +15 -3
- package/dist/providers/mistral.js +19 -7
- package/dist/providers/ollama.js +31 -7
- package/dist/providers/openAI.js +12 -0
- package/dist/sdk/toolRegistration.js +2 -2
- package/dist/types/cli.d.ts +56 -1
- package/dist/types/contextTypes.d.ts +110 -0
- package/dist/types/contextTypes.js +177 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/mcpTypes.d.ts +118 -7
- package/dist/types/providers.d.ts +81 -0
- package/dist/types/streamTypes.d.ts +44 -7
- package/dist/types/tools.d.ts +9 -0
- package/dist/types/universalProviderOptions.d.ts +3 -1
- package/dist/types/universalProviderOptions.js +3 -1
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/performance.d.ts +105 -0
- package/dist/utils/performance.js +210 -0
- package/dist/utils/retryHandler.d.ts +89 -0
- package/dist/utils/retryHandler.js +269 -0
- package/package.json +2 -1
|
@@ -50,52 +50,39 @@ export function createAnalytics(provider, model, result, responseTime, context)
|
|
|
50
50
|
* Extract token usage from various AI result formats
|
|
51
51
|
*/
|
|
52
52
|
function extractTokenUsage(result) {
|
|
53
|
-
//
|
|
53
|
+
// Use properly typed usage object from BaseProvider or direct AI SDK
|
|
54
54
|
if (result.usage &&
|
|
55
55
|
typeof result.usage === "object" &&
|
|
56
56
|
result.usage !== null) {
|
|
57
57
|
const usage = result.usage;
|
|
58
|
-
//
|
|
59
|
-
if (typeof usage.
|
|
60
|
-
typeof usage.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
};
|
|
58
|
+
// Try BaseProvider normalized format first (inputTokens/outputTokens)
|
|
59
|
+
if (typeof usage.inputTokens === "number" ||
|
|
60
|
+
typeof usage.outputTokens === "number") {
|
|
61
|
+
const input = typeof usage.inputTokens === "number" ? usage.inputTokens : 0;
|
|
62
|
+
const output = typeof usage.outputTokens === "number" ? usage.outputTokens : 0;
|
|
63
|
+
const total = typeof usage.totalTokens === "number"
|
|
64
|
+
? usage.totalTokens
|
|
65
|
+
: input + output;
|
|
66
|
+
return { input, output, total };
|
|
68
67
|
}
|
|
69
|
-
//
|
|
70
|
-
if (typeof usage.
|
|
71
|
-
typeof usage.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
68
|
+
// Try OpenAI/Mistral format (promptTokens/completionTokens)
|
|
69
|
+
if (typeof usage.promptTokens === "number" ||
|
|
70
|
+
typeof usage.completionTokens === "number") {
|
|
71
|
+
const input = typeof usage.promptTokens === "number" ? usage.promptTokens : 0;
|
|
72
|
+
const output = typeof usage.completionTokens === "number" ? usage.completionTokens : 0;
|
|
73
|
+
const total = typeof usage.totalTokens === "number"
|
|
74
|
+
? usage.totalTokens
|
|
75
|
+
: input + output;
|
|
76
|
+
return { input, output, total };
|
|
79
77
|
}
|
|
80
|
-
//
|
|
81
|
-
if (typeof usage.
|
|
82
|
-
return {
|
|
83
|
-
input: 0,
|
|
84
|
-
output: 0,
|
|
85
|
-
total: usage.tokens,
|
|
86
|
-
};
|
|
78
|
+
// Handle total-only case
|
|
79
|
+
if (typeof usage.totalTokens === "number") {
|
|
80
|
+
return { input: 0, output: 0, total: usage.totalTokens };
|
|
87
81
|
}
|
|
88
82
|
}
|
|
89
|
-
// Fallback
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
0;
|
|
93
|
-
const estimatedTokens = Math.ceil(textLength / 4); // ~4 chars per token
|
|
94
|
-
return {
|
|
95
|
-
input: 0,
|
|
96
|
-
output: estimatedTokens,
|
|
97
|
-
total: estimatedTokens,
|
|
98
|
-
};
|
|
83
|
+
// Fallback for edge cases
|
|
84
|
+
logger.debug("Token extraction failed: unknown usage format", { result });
|
|
85
|
+
return { input: 0, output: 0, total: 0 };
|
|
99
86
|
}
|
|
100
87
|
/**
|
|
101
88
|
* Estimate cost based on provider, model, and token usage
|
|
@@ -447,5 +447,13 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
447
447
|
* Get timeout value in milliseconds
|
|
448
448
|
*/
|
|
449
449
|
getTimeout(options: TextGenerationOptions | StreamOptions): number;
|
|
450
|
+
/**
|
|
451
|
+
* Utility method to chunk large prompts into smaller pieces
|
|
452
|
+
* @param prompt The prompt to chunk
|
|
453
|
+
* @param maxChunkSize Maximum size per chunk (default: 900,000 characters)
|
|
454
|
+
* @param overlap Overlap between chunks to maintain context (default: 100 characters)
|
|
455
|
+
* @returns Array of prompt chunks
|
|
456
|
+
*/
|
|
457
|
+
static chunkPrompt(prompt: string, maxChunkSize?: number, overlap?: number): string[];
|
|
450
458
|
}
|
|
451
459
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { logger } from "../utils/logger.js";
|
|
2
|
+
import { SYSTEM_LIMITS } from "../core/constants.js";
|
|
2
3
|
import { directAgentTools } from "../agent/directTools.js";
|
|
3
4
|
/**
|
|
4
5
|
* Validates if a result contains a valid toolsObject structure
|
|
@@ -49,80 +50,96 @@ export class BaseProvider {
|
|
|
49
50
|
*/
|
|
50
51
|
async stream(optionsOrPrompt, analysisSchema) {
|
|
51
52
|
const options = this.normalizeStreamOptions(optionsOrPrompt);
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
53
|
+
// CRITICAL FIX: Always prefer real streaming over fake streaming
|
|
54
|
+
// Try real streaming first, use fake streaming only as fallback
|
|
55
|
+
try {
|
|
56
|
+
const realStreamResult = await this.executeStream(options, analysisSchema);
|
|
57
|
+
// If real streaming succeeds, return it (with tools support via Vercel AI SDK)
|
|
58
|
+
return realStreamResult;
|
|
59
|
+
}
|
|
60
|
+
catch (realStreamError) {
|
|
61
|
+
logger.warn(`Real streaming failed for ${this.providerName}, falling back to fake streaming:`, realStreamError);
|
|
62
|
+
// Fallback to fake streaming only if real streaming fails AND tools are enabled
|
|
63
|
+
if (!options.disableTools && this.supportsTools()) {
|
|
64
|
+
try {
|
|
65
|
+
// Convert stream options to text generation options
|
|
66
|
+
const textOptions = {
|
|
67
|
+
prompt: options.input?.text || "",
|
|
68
|
+
systemPrompt: options.systemPrompt,
|
|
69
|
+
temperature: options.temperature,
|
|
70
|
+
maxTokens: options.maxTokens,
|
|
71
|
+
disableTools: false,
|
|
72
|
+
maxSteps: options.maxSteps || 5,
|
|
73
|
+
provider: options.provider,
|
|
74
|
+
model: options.model,
|
|
75
|
+
// 🔧 FIX: Include analytics and evaluation options from stream options
|
|
76
|
+
enableAnalytics: options.enableAnalytics,
|
|
77
|
+
enableEvaluation: options.enableEvaluation,
|
|
78
|
+
evaluationDomain: options.evaluationDomain,
|
|
79
|
+
toolUsageContext: options.toolUsageContext,
|
|
80
|
+
context: options.context,
|
|
81
|
+
};
|
|
82
|
+
const result = await this.generate(textOptions, analysisSchema);
|
|
83
|
+
// Create a synthetic stream from the generate result that simulates progressive delivery
|
|
84
|
+
return {
|
|
85
|
+
stream: (async function* () {
|
|
86
|
+
if (result?.content) {
|
|
87
|
+
// Split content into words for more natural streaming
|
|
88
|
+
const words = result.content.split(/(\s+)/); // Keep whitespace
|
|
89
|
+
let buffer = "";
|
|
90
|
+
for (let i = 0; i < words.length; i++) {
|
|
91
|
+
buffer += words[i];
|
|
92
|
+
// Yield chunks of roughly 5-10 words or at punctuation
|
|
93
|
+
const shouldYield = i === words.length - 1 || // Last word
|
|
94
|
+
buffer.length > 50 || // Buffer getting long
|
|
95
|
+
/[.!?;,]\s*$/.test(buffer); // End of sentence/clause
|
|
96
|
+
if (shouldYield && buffer.trim()) {
|
|
97
|
+
yield { content: buffer };
|
|
98
|
+
buffer = "";
|
|
99
|
+
// Small delay to simulate streaming (1-10ms)
|
|
100
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 9 + 1));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Yield any remaining content
|
|
104
|
+
if (buffer.trim()) {
|
|
81
105
|
yield { content: buffer };
|
|
82
|
-
buffer = "";
|
|
83
|
-
// Small delay to simulate streaming (1-10ms)
|
|
84
|
-
await new Promise((resolve) => setTimeout(resolve, Math.random() * 9 + 1));
|
|
85
106
|
}
|
|
86
107
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
})(),
|
|
109
|
+
usage: result?.usage,
|
|
110
|
+
provider: result?.provider,
|
|
111
|
+
model: result?.model,
|
|
112
|
+
toolCalls: result?.toolCalls?.map((call) => ({
|
|
113
|
+
toolName: call.toolName,
|
|
114
|
+
parameters: call.args,
|
|
115
|
+
id: call.toolCallId,
|
|
116
|
+
})),
|
|
117
|
+
toolResults: result?.toolResults
|
|
118
|
+
? result.toolResults.map((tr) => ({
|
|
119
|
+
toolName: tr.toolName || "unknown",
|
|
120
|
+
status: (tr.status === "error"
|
|
121
|
+
? "failure"
|
|
122
|
+
: "success"),
|
|
123
|
+
result: tr.result,
|
|
124
|
+
error: tr.error,
|
|
125
|
+
}))
|
|
126
|
+
: undefined,
|
|
127
|
+
// 🔧 FIX: Include analytics and evaluation from generate result
|
|
128
|
+
analytics: result?.analytics,
|
|
129
|
+
evaluation: result?.evaluation,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
logger.error(`Fake streaming fallback failed for ${this.providerName}:`, error);
|
|
134
|
+
throw this.handleProviderError(error);
|
|
135
|
+
}
|
|
112
136
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
137
|
+
else {
|
|
138
|
+
// If real streaming failed and no tools are enabled, re-throw the original error
|
|
139
|
+
logger.error(`Real streaming failed for ${this.providerName}:`, realStreamError);
|
|
140
|
+
throw this.handleProviderError(realStreamError);
|
|
116
141
|
}
|
|
117
142
|
}
|
|
118
|
-
// Traditional streaming without tools
|
|
119
|
-
try {
|
|
120
|
-
return await this.executeStream(options, analysisSchema);
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
logger.error(`Stream failed for ${this.providerName}:`, error);
|
|
124
|
-
throw this.handleProviderError(error);
|
|
125
|
-
}
|
|
126
143
|
}
|
|
127
144
|
/**
|
|
128
145
|
* Text generation method - implements AIProvider interface
|
|
@@ -150,6 +167,31 @@ export class BaseProvider {
|
|
|
150
167
|
temperature: options.temperature,
|
|
151
168
|
maxTokens: options.maxTokens || 8192,
|
|
152
169
|
});
|
|
170
|
+
// Extract tool names from tool calls for tracking
|
|
171
|
+
// AI SDK puts tool calls in steps array for multi-step generation
|
|
172
|
+
const toolsUsed = [];
|
|
173
|
+
// First check direct tool calls (fallback)
|
|
174
|
+
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
175
|
+
toolsUsed.push(...result.toolCalls.map((tc) => {
|
|
176
|
+
return (tc.toolName ||
|
|
177
|
+
tc.name ||
|
|
178
|
+
"unknown");
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
// Then check steps for tool calls (primary source for multi-step)
|
|
182
|
+
if (result.steps &&
|
|
183
|
+
Array.isArray(result.steps)) {
|
|
184
|
+
for (const step of result.steps ||
|
|
185
|
+
[]) {
|
|
186
|
+
if (step?.toolCalls && Array.isArray(step.toolCalls)) {
|
|
187
|
+
toolsUsed.push(...step.toolCalls.map((tc) => {
|
|
188
|
+
return tc.toolName || tc.name || "unknown";
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Remove duplicates
|
|
194
|
+
const uniqueToolsUsed = [...new Set(toolsUsed)];
|
|
153
195
|
// Format the result with tool executions included
|
|
154
196
|
const enhancedResult = {
|
|
155
197
|
content: result.text,
|
|
@@ -174,6 +216,7 @@ export class BaseProvider {
|
|
|
174
216
|
}))
|
|
175
217
|
: [],
|
|
176
218
|
toolResults: result.toolResults,
|
|
219
|
+
toolsUsed: uniqueToolsUsed,
|
|
177
220
|
};
|
|
178
221
|
// Enhanced result with analytics and evaluation
|
|
179
222
|
return await this.enhanceResult(enhancedResult, options, startTime);
|
|
@@ -348,9 +391,43 @@ export class BaseProvider {
|
|
|
348
391
|
return evaluation;
|
|
349
392
|
}
|
|
350
393
|
validateOptions(options) {
|
|
394
|
+
// 🔧 EDGE CASE: Basic prompt validation
|
|
351
395
|
if (!options.prompt || options.prompt.trim().length === 0) {
|
|
352
396
|
throw new Error("Prompt is required and cannot be empty");
|
|
353
397
|
}
|
|
398
|
+
// 🔧 EDGE CASE: Handle very large prompts (>1M characters)
|
|
399
|
+
if (options.prompt.length > SYSTEM_LIMITS.MAX_PROMPT_LENGTH) {
|
|
400
|
+
throw new Error(`Prompt too large: ${options.prompt.length} characters (max: ${SYSTEM_LIMITS.MAX_PROMPT_LENGTH}). Consider breaking into smaller chunks. Use BaseProvider.chunkPrompt(prompt, maxSize, overlap) static method for chunking.`);
|
|
401
|
+
}
|
|
402
|
+
// 🔧 EDGE CASE: Validate token limits
|
|
403
|
+
if (options.maxTokens && options.maxTokens > 200000) {
|
|
404
|
+
throw new Error(`Max tokens too high: ${options.maxTokens} (recommended max: 200,000). This may cause timeouts or API errors.`);
|
|
405
|
+
}
|
|
406
|
+
if (options.maxTokens && options.maxTokens < 1) {
|
|
407
|
+
throw new Error("Max tokens must be at least 1");
|
|
408
|
+
}
|
|
409
|
+
// 🔧 EDGE CASE: Validate temperature range
|
|
410
|
+
if (options.temperature !== undefined) {
|
|
411
|
+
if (options.temperature < 0 || options.temperature > 2) {
|
|
412
|
+
throw new Error(`Temperature must be between 0 and 2, got: ${options.temperature}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// 🔧 EDGE CASE: Validate timeout values
|
|
416
|
+
if (options.timeout !== undefined) {
|
|
417
|
+
const timeoutMs = typeof options.timeout === "string"
|
|
418
|
+
? parseInt(options.timeout, 10)
|
|
419
|
+
: options.timeout;
|
|
420
|
+
if (isNaN(timeoutMs) || timeoutMs < 1000) {
|
|
421
|
+
throw new Error(`Timeout must be at least 1000ms (1 second), got: ${options.timeout}`);
|
|
422
|
+
}
|
|
423
|
+
if (timeoutMs > SYSTEM_LIMITS.LONG_TIMEOUT_WARNING) {
|
|
424
|
+
logger.warn(`⚠️ Very long timeout: ${timeoutMs}ms. This may cause the CLI to hang.`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// 🔧 EDGE CASE: Validate maxSteps for tool execution
|
|
428
|
+
if (options.maxSteps !== undefined && options.maxSteps > 20) {
|
|
429
|
+
throw new Error(`Max steps too high: ${options.maxSteps} (recommended max: 20). This may cause long execution times.`);
|
|
430
|
+
}
|
|
354
431
|
}
|
|
355
432
|
getProviderInfo() {
|
|
356
433
|
return {
|
|
@@ -382,4 +459,36 @@ export class BaseProvider {
|
|
|
382
459
|
}
|
|
383
460
|
return this.defaultTimeout;
|
|
384
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Utility method to chunk large prompts into smaller pieces
|
|
464
|
+
* @param prompt The prompt to chunk
|
|
465
|
+
* @param maxChunkSize Maximum size per chunk (default: 900,000 characters)
|
|
466
|
+
* @param overlap Overlap between chunks to maintain context (default: 100 characters)
|
|
467
|
+
* @returns Array of prompt chunks
|
|
468
|
+
*/
|
|
469
|
+
static chunkPrompt(prompt, maxChunkSize = 900000, overlap = 100) {
|
|
470
|
+
if (prompt.length <= maxChunkSize) {
|
|
471
|
+
return [prompt];
|
|
472
|
+
}
|
|
473
|
+
const chunks = [];
|
|
474
|
+
let start = 0;
|
|
475
|
+
while (start < prompt.length) {
|
|
476
|
+
const end = Math.min(start + maxChunkSize, prompt.length);
|
|
477
|
+
chunks.push(prompt.slice(start, end));
|
|
478
|
+
// Break if we've reached the end
|
|
479
|
+
if (end >= prompt.length) {
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
// Move start forward, accounting for overlap
|
|
483
|
+
const nextStart = end - overlap;
|
|
484
|
+
// Ensure we make progress (avoid infinite loops)
|
|
485
|
+
if (nextStart <= start) {
|
|
486
|
+
start = end;
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
start = Math.max(nextStart, 0);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return chunks;
|
|
493
|
+
}
|
|
385
494
|
}
|
|
@@ -36,6 +36,17 @@ export declare const CLI_LIMITS: {
|
|
|
36
36
|
default: number;
|
|
37
37
|
};
|
|
38
38
|
};
|
|
39
|
+
export declare const SYSTEM_LIMITS: {
|
|
40
|
+
MAX_PROMPT_LENGTH: number;
|
|
41
|
+
HIGH_MEMORY_THRESHOLD: number;
|
|
42
|
+
LONG_TIMEOUT_WARNING: number;
|
|
43
|
+
DEFAULT_CONCURRENCY_LIMIT: number;
|
|
44
|
+
MAX_CONCURRENCY_LIMIT: number;
|
|
45
|
+
DEFAULT_RETRY_ATTEMPTS: number;
|
|
46
|
+
DEFAULT_INITIAL_DELAY: number;
|
|
47
|
+
DEFAULT_MAX_DELAY: number;
|
|
48
|
+
DEFAULT_BACKOFF_MULTIPLIER: number;
|
|
49
|
+
};
|
|
39
50
|
export declare const ENV_DEFAULTS: {
|
|
40
51
|
maxTokens: number;
|
|
41
52
|
temperature: number;
|
|
@@ -40,6 +40,23 @@ export const CLI_LIMITS = {
|
|
|
40
40
|
default: DEFAULT_TEMPERATURE,
|
|
41
41
|
},
|
|
42
42
|
};
|
|
43
|
+
// Performance and System Limits
|
|
44
|
+
export const SYSTEM_LIMITS = {
|
|
45
|
+
// Prompt size limits (baseProvider.ts magic number fix)
|
|
46
|
+
MAX_PROMPT_LENGTH: 1000000, // 1M characters - prevents memory issues
|
|
47
|
+
// Memory monitoring thresholds (performance.ts)
|
|
48
|
+
HIGH_MEMORY_THRESHOLD: 100, // MB - when to warn about memory usage
|
|
49
|
+
// Timeout warnings (baseProvider.ts)
|
|
50
|
+
LONG_TIMEOUT_WARNING: 300000, // 5 minutes - when to warn about long timeouts
|
|
51
|
+
// Concurrency control (neurolink.ts provider testing)
|
|
52
|
+
DEFAULT_CONCURRENCY_LIMIT: 3, // Max parallel provider tests
|
|
53
|
+
MAX_CONCURRENCY_LIMIT: 5, // Upper bound for concurrency
|
|
54
|
+
// Retry system defaults (retryHandler.ts)
|
|
55
|
+
DEFAULT_RETRY_ATTEMPTS: 3,
|
|
56
|
+
DEFAULT_INITIAL_DELAY: 1000, // 1 second
|
|
57
|
+
DEFAULT_MAX_DELAY: 30000, // 30 seconds
|
|
58
|
+
DEFAULT_BACKOFF_MULTIPLIER: 2,
|
|
59
|
+
};
|
|
43
60
|
// Environment Variable Support (for future use)
|
|
44
61
|
export const ENV_DEFAULTS = {
|
|
45
62
|
maxTokens: process.env.NEUROLINK_DEFAULT_MAX_TOKENS
|
|
@@ -84,24 +84,32 @@ function parseUnifiedEvaluationResult(response, context) {
|
|
|
84
84
|
accuracy: /accuracy[:\s]*([0-9]+(?:\.[0-9]+)?)/i,
|
|
85
85
|
completeness: /completeness[:\s]*([0-9]+(?:\.[0-9]+)?)/i,
|
|
86
86
|
overall: /overall[:\s]*([0-9]+(?:\.[0-9]+)?)/i,
|
|
87
|
+
reasoning: /reasoning[:\s]*(.+?)(?=\n\s*\w+:|\n\s*$|$)/is,
|
|
87
88
|
};
|
|
88
89
|
for (const [key, pattern] of Object.entries(patterns)) {
|
|
89
90
|
const match = response.match(pattern);
|
|
90
91
|
if (match) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
if (key === "reasoning") {
|
|
93
|
+
// Extract reasoning text
|
|
94
|
+
result.reasoning = match[1].trim();
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Extract numerical scores
|
|
98
|
+
const value = parseFloat(match[1]);
|
|
99
|
+
if (value >= 1 && value <= 10) {
|
|
100
|
+
const roundedValue = Math.round(value);
|
|
101
|
+
if (key === "relevance") {
|
|
102
|
+
result.relevance = roundedValue;
|
|
103
|
+
}
|
|
104
|
+
else if (key === "accuracy") {
|
|
105
|
+
result.accuracy = roundedValue;
|
|
106
|
+
}
|
|
107
|
+
else if (key === "completeness") {
|
|
108
|
+
result.completeness = roundedValue;
|
|
109
|
+
}
|
|
110
|
+
else if (key === "overall") {
|
|
111
|
+
result.overall = roundedValue;
|
|
112
|
+
}
|
|
105
113
|
}
|
|
106
114
|
}
|
|
107
115
|
}
|
|
@@ -112,6 +120,7 @@ function parseUnifiedEvaluationResult(response, context) {
|
|
|
112
120
|
accuracy: result.accuracy || 1,
|
|
113
121
|
completeness: result.completeness || 1,
|
|
114
122
|
overall: result.overall || 1,
|
|
123
|
+
reasoning: result.reasoning || "No detailed reasoning provided",
|
|
115
124
|
};
|
|
116
125
|
}
|
|
117
126
|
catch (error) {
|
|
@@ -123,6 +132,7 @@ function parseUnifiedEvaluationResult(response, context) {
|
|
|
123
132
|
accuracy: 1,
|
|
124
133
|
completeness: 1,
|
|
125
134
|
overall: 1,
|
|
135
|
+
reasoning: "Error occurred during evaluation parsing",
|
|
126
136
|
};
|
|
127
137
|
}
|
|
128
138
|
}
|
|
@@ -167,6 +177,7 @@ Relevance: [score]
|
|
|
167
177
|
Accuracy: [score]
|
|
168
178
|
Completeness: [score]
|
|
169
179
|
Overall: [score]
|
|
180
|
+
Reasoning: [Provide a detailed explanation of your evaluation, explaining why you gave these scores. Include specific observations about the response's strengths and any areas for improvement.]
|
|
170
181
|
`;
|
|
171
182
|
// Generate evaluation
|
|
172
183
|
const result = await provider.generate(prompt);
|
package/dist/lib/core/factory.js
CHANGED
|
@@ -37,24 +37,25 @@ export class AIProviderFactory {
|
|
|
37
37
|
enableMCP,
|
|
38
38
|
});
|
|
39
39
|
try {
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
40
|
+
// DYNAMIC MODEL PROVIDER STATUS (2025): Disabled due to reliability issues
|
|
41
|
+
//
|
|
42
|
+
// Root Cause: Dynamic model provider initialization can hang when:
|
|
43
|
+
// - Local model server (localhost:3001) is not running or responding
|
|
44
|
+
// - GitHub raw URL requests timeout due to network issues
|
|
45
|
+
// - Local config file doesn't exist
|
|
46
|
+
//
|
|
47
|
+
// Current Behavior: Static model resolution works reliably
|
|
48
|
+
// Impact: No functionality loss - providers use built-in model defaults
|
|
49
|
+
//
|
|
50
|
+
// Implementation Requirements (if re-enabling):
|
|
51
|
+
// 1. Add robust timeout handling (3s max per source)
|
|
52
|
+
// 2. Implement exponential backoff for network requests
|
|
53
|
+
// 3. Add graceful degradation when all sources fail
|
|
54
|
+
// 4. Create health check for localhost:3001 before attempting connection
|
|
55
|
+
// 5. Add comprehensive error handling and logging
|
|
56
|
+
//
|
|
57
|
+
// Until these improvements are implemented, dynamic model provider remains disabled
|
|
58
|
+
// for system reliability. Static model defaults provide stable functionality.
|
|
58
59
|
// COMPREHENSIVE FIX: Disable dynamic model resolution completely until provider is fixed
|
|
59
60
|
// This prevents stale gemini-1.5-pro-latest from overriding correct gemini-2.5-pro defaults
|
|
60
61
|
const resolvedModelName = modelName;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AnalyticsData } from "./types.js";
|
|
2
|
+
import type { TokenUsage } from "../types/providers.js";
|
|
3
|
+
import type { ToolCall, ToolResult } from "../types/streamTypes.js";
|
|
4
|
+
/**
|
|
5
|
+
* Stream analytics result from Vercel AI SDK streamText
|
|
6
|
+
*/
|
|
7
|
+
export interface StreamTextResult {
|
|
8
|
+
textStream: AsyncIterable<string>;
|
|
9
|
+
text: Promise<string>;
|
|
10
|
+
usage: Promise<{
|
|
11
|
+
promptTokens: number;
|
|
12
|
+
completionTokens: number;
|
|
13
|
+
totalTokens: number;
|
|
14
|
+
} | undefined>;
|
|
15
|
+
response: Promise<{
|
|
16
|
+
id?: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
timestamp?: number | Date;
|
|
19
|
+
} | undefined>;
|
|
20
|
+
finishReason: Promise<"stop" | "length" | "content-filter" | "tool-calls" | "error" | "other" | "unknown">;
|
|
21
|
+
toolResults?: Promise<ToolResult[]>;
|
|
22
|
+
toolCalls?: Promise<ToolCall[]>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Interface for collecting analytics from streaming results
|
|
26
|
+
*/
|
|
27
|
+
export interface StreamAnalyticsCollector {
|
|
28
|
+
collectUsage(result: StreamTextResult): Promise<TokenUsage>;
|
|
29
|
+
collectMetadata(result: StreamTextResult): Promise<ResponseMetadata>;
|
|
30
|
+
createAnalytics(provider: string, model: string, result: StreamTextResult, responseTime: number, metadata?: Record<string, unknown>): Promise<AnalyticsData>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Response metadata from stream result
|
|
34
|
+
*/
|
|
35
|
+
export interface ResponseMetadata {
|
|
36
|
+
id?: string;
|
|
37
|
+
model?: string;
|
|
38
|
+
timestamp?: number;
|
|
39
|
+
finishReason?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Base implementation for collecting analytics from Vercel AI SDK stream results
|
|
43
|
+
*/
|
|
44
|
+
export declare class BaseStreamAnalyticsCollector implements StreamAnalyticsCollector {
|
|
45
|
+
/**
|
|
46
|
+
* Collect token usage from stream result
|
|
47
|
+
*/
|
|
48
|
+
collectUsage(result: StreamTextResult): Promise<TokenUsage>;
|
|
49
|
+
/**
|
|
50
|
+
* Collect response metadata from stream result
|
|
51
|
+
*/
|
|
52
|
+
collectMetadata(result: StreamTextResult): Promise<ResponseMetadata>;
|
|
53
|
+
/**
|
|
54
|
+
* Create comprehensive analytics from stream result
|
|
55
|
+
*/
|
|
56
|
+
createAnalytics(provider: string, model: string, result: StreamTextResult, responseTime: number, metadata?: Record<string, unknown>): Promise<AnalyticsData>;
|
|
57
|
+
/**
|
|
58
|
+
* Clean up resources and force garbage collection if needed
|
|
59
|
+
*/
|
|
60
|
+
cleanup(): void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Global instance of stream analytics collector
|
|
64
|
+
*/
|
|
65
|
+
export declare const streamAnalyticsCollector: BaseStreamAnalyticsCollector;
|