@juspay/neurolink 9.15.0 → 9.16.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 +6 -0
- package/dist/adapters/video/videoAnalyzer.d.ts +1 -1
- package/dist/adapters/video/videoAnalyzer.js +10 -8
- package/dist/cli/commands/setup-anthropic.js +1 -14
- package/dist/cli/commands/setup-azure.js +1 -12
- package/dist/cli/commands/setup-bedrock.js +1 -9
- package/dist/cli/commands/setup-google-ai.js +1 -12
- package/dist/cli/commands/setup-openai.js +1 -14
- package/dist/cli/commands/workflow.d.ts +27 -0
- package/dist/cli/commands/workflow.js +216 -0
- package/dist/cli/factories/commandFactory.js +79 -20
- package/dist/cli/index.js +0 -1
- package/dist/cli/parser.js +4 -1
- package/dist/cli/utils/maskCredential.d.ts +11 -0
- package/dist/cli/utils/maskCredential.js +23 -0
- package/dist/constants/contextWindows.js +107 -16
- package/dist/constants/enums.d.ts +99 -15
- package/dist/constants/enums.js +152 -22
- package/dist/context/budgetChecker.js +1 -1
- package/dist/context/contextCompactor.js +31 -4
- package/dist/context/emergencyTruncation.d.ts +21 -0
- package/dist/context/emergencyTruncation.js +88 -0
- package/dist/context/errorDetection.d.ts +16 -0
- package/dist/context/errorDetection.js +48 -1
- package/dist/context/errors.d.ts +19 -0
- package/dist/context/errors.js +21 -0
- package/dist/context/stages/slidingWindowTruncator.d.ts +6 -0
- package/dist/context/stages/slidingWindowTruncator.js +159 -24
- package/dist/core/baseProvider.js +306 -200
- package/dist/core/conversationMemoryManager.js +104 -61
- package/dist/core/evaluationProviders.js +16 -33
- package/dist/core/factory.js +237 -164
- package/dist/core/modules/GenerationHandler.js +175 -116
- package/dist/core/modules/MessageBuilder.js +222 -170
- package/dist/core/modules/StreamHandler.d.ts +1 -0
- package/dist/core/modules/StreamHandler.js +95 -27
- package/dist/core/modules/TelemetryHandler.d.ts +10 -1
- package/dist/core/modules/TelemetryHandler.js +25 -7
- package/dist/core/modules/ToolsManager.js +115 -191
- package/dist/core/redisConversationMemoryManager.js +418 -282
- package/dist/factories/providerRegistry.d.ts +5 -0
- package/dist/factories/providerRegistry.js +20 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/dist/lib/adapters/video/videoAnalyzer.d.ts +1 -1
- package/dist/lib/adapters/video/videoAnalyzer.js +10 -8
- package/dist/lib/constants/contextWindows.js +107 -16
- package/dist/lib/constants/enums.d.ts +99 -15
- package/dist/lib/constants/enums.js +152 -22
- package/dist/lib/context/budgetChecker.js +1 -1
- package/dist/lib/context/contextCompactor.js +31 -4
- package/dist/lib/context/emergencyTruncation.d.ts +21 -0
- package/dist/lib/context/emergencyTruncation.js +89 -0
- package/dist/lib/context/errorDetection.d.ts +16 -0
- package/dist/lib/context/errorDetection.js +48 -1
- package/dist/lib/context/errors.d.ts +19 -0
- package/dist/lib/context/errors.js +22 -0
- package/dist/lib/context/stages/slidingWindowTruncator.d.ts +6 -0
- package/dist/lib/context/stages/slidingWindowTruncator.js +159 -24
- package/dist/lib/core/baseProvider.js +306 -200
- package/dist/lib/core/conversationMemoryManager.js +104 -61
- package/dist/lib/core/evaluationProviders.js +16 -33
- package/dist/lib/core/factory.js +237 -164
- package/dist/lib/core/modules/GenerationHandler.js +175 -116
- package/dist/lib/core/modules/MessageBuilder.js +222 -170
- package/dist/lib/core/modules/StreamHandler.d.ts +1 -0
- package/dist/lib/core/modules/StreamHandler.js +95 -27
- package/dist/lib/core/modules/TelemetryHandler.d.ts +10 -1
- package/dist/lib/core/modules/TelemetryHandler.js +25 -7
- package/dist/lib/core/modules/ToolsManager.js +115 -191
- package/dist/lib/core/redisConversationMemoryManager.js +418 -282
- package/dist/lib/factories/providerRegistry.d.ts +5 -0
- package/dist/lib/factories/providerRegistry.js +20 -2
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +4 -2
- package/dist/lib/mcp/externalServerManager.js +66 -0
- package/dist/lib/mcp/mcpCircuitBreaker.js +24 -0
- package/dist/lib/mcp/mcpClientFactory.js +16 -0
- package/dist/lib/mcp/toolDiscoveryService.js +32 -6
- package/dist/lib/mcp/toolRegistry.js +193 -123
- package/dist/lib/neurolink.d.ts +6 -0
- package/dist/lib/neurolink.js +1162 -646
- package/dist/lib/providers/amazonBedrock.d.ts +1 -1
- package/dist/lib/providers/amazonBedrock.js +521 -319
- package/dist/lib/providers/anthropic.js +73 -17
- package/dist/lib/providers/anthropicBaseProvider.js +77 -17
- package/dist/lib/providers/googleAiStudio.d.ts +1 -1
- package/dist/lib/providers/googleAiStudio.js +292 -227
- package/dist/lib/providers/googleVertex.d.ts +36 -1
- package/dist/lib/providers/googleVertex.js +553 -260
- package/dist/lib/providers/ollama.js +329 -278
- package/dist/lib/providers/openAI.js +77 -19
- package/dist/lib/providers/sagemaker/parsers.js +3 -3
- package/dist/lib/providers/sagemaker/streaming.js +3 -3
- package/dist/lib/proxy/proxyFetch.js +81 -48
- package/dist/lib/rag/ChunkerFactory.js +1 -1
- package/dist/lib/rag/chunkers/MarkdownChunker.d.ts +22 -0
- package/dist/lib/rag/chunkers/MarkdownChunker.js +213 -9
- package/dist/lib/rag/chunking/markdownChunker.d.ts +16 -0
- package/dist/lib/rag/chunking/markdownChunker.js +174 -2
- package/dist/lib/rag/pipeline/contextAssembly.js +2 -1
- package/dist/lib/rag/ragIntegration.d.ts +18 -1
- package/dist/lib/rag/ragIntegration.js +94 -14
- package/dist/lib/rag/retrieval/vectorQueryTool.js +21 -4
- package/dist/lib/server/abstract/baseServerAdapter.js +4 -1
- package/dist/lib/server/adapters/fastifyAdapter.js +35 -30
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +32 -0
- package/dist/lib/services/server/ai/observability/instrumentation.js +39 -0
- package/dist/lib/telemetry/attributes.d.ts +52 -0
- package/dist/lib/telemetry/attributes.js +61 -0
- package/dist/lib/telemetry/index.d.ts +3 -0
- package/dist/lib/telemetry/index.js +3 -0
- package/dist/lib/telemetry/telemetryService.d.ts +6 -0
- package/dist/lib/telemetry/telemetryService.js +6 -0
- package/dist/lib/telemetry/tracers.d.ts +15 -0
- package/dist/lib/telemetry/tracers.js +17 -0
- package/dist/lib/telemetry/withSpan.d.ts +9 -0
- package/dist/lib/telemetry/withSpan.js +35 -0
- package/dist/lib/types/contextTypes.d.ts +10 -0
- package/dist/lib/types/streamTypes.d.ts +14 -0
- package/dist/lib/utils/conversationMemory.js +121 -82
- package/dist/lib/utils/logger.d.ts +5 -0
- package/dist/lib/utils/logger.js +50 -2
- package/dist/lib/utils/messageBuilder.js +22 -42
- package/dist/lib/utils/modelDetection.js +3 -3
- package/dist/lib/utils/providerRetry.d.ts +41 -0
- package/dist/lib/utils/providerRetry.js +114 -0
- package/dist/lib/utils/retryability.d.ts +14 -0
- package/dist/lib/utils/retryability.js +23 -0
- package/dist/lib/utils/sanitizers/svg.js +4 -5
- package/dist/lib/utils/tokenEstimation.d.ts +11 -1
- package/dist/lib/utils/tokenEstimation.js +19 -4
- package/dist/lib/utils/videoAnalysisProcessor.js +7 -3
- package/dist/mcp/externalServerManager.js +66 -0
- package/dist/mcp/mcpCircuitBreaker.js +24 -0
- package/dist/mcp/mcpClientFactory.js +16 -0
- package/dist/mcp/toolDiscoveryService.js +32 -6
- package/dist/mcp/toolRegistry.js +193 -123
- package/dist/neurolink.d.ts +6 -0
- package/dist/neurolink.js +1162 -646
- package/dist/providers/amazonBedrock.d.ts +1 -1
- package/dist/providers/amazonBedrock.js +521 -319
- package/dist/providers/anthropic.js +73 -17
- package/dist/providers/anthropicBaseProvider.js +77 -17
- package/dist/providers/googleAiStudio.d.ts +1 -1
- package/dist/providers/googleAiStudio.js +292 -227
- package/dist/providers/googleVertex.d.ts +36 -1
- package/dist/providers/googleVertex.js +553 -260
- package/dist/providers/ollama.js +329 -278
- package/dist/providers/openAI.js +77 -19
- package/dist/providers/sagemaker/parsers.js +3 -3
- package/dist/providers/sagemaker/streaming.js +3 -3
- package/dist/proxy/proxyFetch.js +81 -48
- package/dist/rag/ChunkerFactory.js +1 -1
- package/dist/rag/chunkers/MarkdownChunker.d.ts +22 -0
- package/dist/rag/chunkers/MarkdownChunker.js +213 -9
- package/dist/rag/chunking/markdownChunker.d.ts +16 -0
- package/dist/rag/chunking/markdownChunker.js +174 -2
- package/dist/rag/pipeline/contextAssembly.js +2 -1
- package/dist/rag/ragIntegration.d.ts +18 -1
- package/dist/rag/ragIntegration.js +94 -14
- package/dist/rag/retrieval/vectorQueryTool.js +21 -4
- package/dist/server/abstract/baseServerAdapter.js +4 -1
- package/dist/server/adapters/fastifyAdapter.js +35 -30
- package/dist/services/server/ai/observability/instrumentation.d.ts +32 -0
- package/dist/services/server/ai/observability/instrumentation.js +39 -0
- package/dist/telemetry/attributes.d.ts +52 -0
- package/dist/telemetry/attributes.js +60 -0
- package/dist/telemetry/index.d.ts +3 -0
- package/dist/telemetry/index.js +3 -0
- package/dist/telemetry/telemetryService.d.ts +6 -0
- package/dist/telemetry/telemetryService.js +6 -0
- package/dist/telemetry/tracers.d.ts +15 -0
- package/dist/telemetry/tracers.js +16 -0
- package/dist/telemetry/withSpan.d.ts +9 -0
- package/dist/telemetry/withSpan.js +34 -0
- package/dist/types/contextTypes.d.ts +10 -0
- package/dist/types/streamTypes.d.ts +14 -0
- package/dist/utils/conversationMemory.js +121 -82
- package/dist/utils/logger.d.ts +5 -0
- package/dist/utils/logger.js +50 -2
- package/dist/utils/messageBuilder.js +22 -42
- package/dist/utils/modelDetection.js +3 -3
- package/dist/utils/providerRetry.d.ts +41 -0
- package/dist/utils/providerRetry.js +113 -0
- package/dist/utils/retryability.d.ts +14 -0
- package/dist/utils/retryability.js +22 -0
- package/dist/utils/sanitizers/svg.js +4 -5
- package/dist/utils/tokenEstimation.d.ts +11 -1
- package/dist/utils/tokenEstimation.js +19 -4
- package/dist/utils/videoAnalysisProcessor.js +7 -3
- package/dist/workflow/config.d.ts +26 -26
- package/package.json +1 -1
|
@@ -8,7 +8,9 @@ import { TokenUtils } from "../constants/tokens.js";
|
|
|
8
8
|
import { SummarizationEngine } from "../context/summarizationEngine.js";
|
|
9
9
|
import { ConversationMemoryError } from "../types/conversation.js";
|
|
10
10
|
import { buildContextFromPointer, getEffectiveTokenThreshold, } from "../utils/conversationMemory.js";
|
|
11
|
+
import { runWithCurrentLangfuseContext } from "../services/server/ai/observability/instrumentation.js";
|
|
11
12
|
import { logger } from "../utils/logger.js";
|
|
13
|
+
import { tracers, withSpan } from "../telemetry/index.js";
|
|
12
14
|
export class ConversationMemoryManager {
|
|
13
15
|
sessions = new Map();
|
|
14
16
|
config;
|
|
@@ -62,61 +64,76 @@ export class ConversationMemoryManager {
|
|
|
62
64
|
* TOKEN-BASED: Validates message size and triggers summarization based on tokens
|
|
63
65
|
*/
|
|
64
66
|
async storeConversationTurn(options) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
session
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
session.messages.push(userMsg, assistantMsg);
|
|
82
|
-
session.lastActivity = Date.now();
|
|
83
|
-
// Store API-reported token counts if available
|
|
84
|
-
if (options.tokenUsage) {
|
|
85
|
-
session.lastApiTokenCount = options.tokenUsage;
|
|
86
|
-
}
|
|
87
|
-
const shouldSummarize = options.enableSummarization !== undefined
|
|
88
|
-
? options.enableSummarization
|
|
89
|
-
: this.config.enableSummarization;
|
|
90
|
-
if (shouldSummarize) {
|
|
91
|
-
// Only trigger summarization if not already in progress for this session
|
|
92
|
-
if (!this.summarizationInProgress.has(options.sessionId)) {
|
|
93
|
-
setImmediate(async () => {
|
|
94
|
-
try {
|
|
95
|
-
await this.checkAndSummarize(session, tokenThreshold, options.requestId);
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
logger.error("Background summarization failed", {
|
|
99
|
-
sessionId: session.sessionId,
|
|
100
|
-
requestId: options.requestId,
|
|
101
|
-
error: error instanceof Error ? error.message : String(error),
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
});
|
|
67
|
+
return withSpan({
|
|
68
|
+
name: "neurolink.memory.storeTurn",
|
|
69
|
+
tracer: tracers.memory,
|
|
70
|
+
attributes: {
|
|
71
|
+
"memory.type": "in-memory",
|
|
72
|
+
"session.id": options.sessionId,
|
|
73
|
+
"memory.operation": "store_turn",
|
|
74
|
+
},
|
|
75
|
+
}, async (span) => {
|
|
76
|
+
await this.ensureInitialized();
|
|
77
|
+
try {
|
|
78
|
+
// Get or create session
|
|
79
|
+
let session = this.sessions.get(options.sessionId);
|
|
80
|
+
if (!session) {
|
|
81
|
+
session = this.createNewSession(options.sessionId, options.userId);
|
|
82
|
+
this.sessions.set(options.sessionId, session);
|
|
105
83
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
84
|
+
const tokenThreshold = options.providerDetails
|
|
85
|
+
? getEffectiveTokenThreshold(options.providerDetails.provider, options.providerDetails.model, this.config.tokenThreshold, session.tokenThreshold)
|
|
86
|
+
: this.config.tokenThreshold || 50000;
|
|
87
|
+
const userMsg = await this.validateAndPrepareMessage(options.userMessage, "user", tokenThreshold);
|
|
88
|
+
const assistantMsg = await this.validateAndPrepareMessage(options.aiResponse, "assistant", tokenThreshold);
|
|
89
|
+
if (options.events && options.events.length > 0) {
|
|
90
|
+
assistantMsg.events = options.events;
|
|
110
91
|
}
|
|
92
|
+
session.messages.push(userMsg, assistantMsg);
|
|
93
|
+
session.lastActivity = Date.now();
|
|
94
|
+
// Store API-reported token counts if available
|
|
95
|
+
if (options.tokenUsage) {
|
|
96
|
+
session.lastApiTokenCount = options.tokenUsage;
|
|
97
|
+
}
|
|
98
|
+
span.setAttribute("memory.message_count", session.messages.length);
|
|
99
|
+
const shouldSummarize = options.enableSummarization !== undefined
|
|
100
|
+
? options.enableSummarization
|
|
101
|
+
: this.config.enableSummarization;
|
|
102
|
+
if (shouldSummarize) {
|
|
103
|
+
// Only trigger summarization if not already in progress for this session
|
|
104
|
+
if (!this.summarizationInProgress.has(options.sessionId)) {
|
|
105
|
+
// Capture the current Langfuse ALS context before setImmediate,
|
|
106
|
+
// which breaks automatic AsyncLocalStorage propagation and would
|
|
107
|
+
// otherwise cause orphaned traces in Langfuse.
|
|
108
|
+
const summarizeWithContext = runWithCurrentLangfuseContext(async () => {
|
|
109
|
+
try {
|
|
110
|
+
await this.checkAndSummarize(session, tokenThreshold, options.requestId);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
logger.error("Background summarization failed", {
|
|
114
|
+
sessionId: session.sessionId,
|
|
115
|
+
requestId: options.requestId,
|
|
116
|
+
error: error instanceof Error ? error.message : String(error),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
setImmediate(summarizeWithContext);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
logger.debug("[ConversationMemoryManager] Summarization already in progress, skipping", {
|
|
124
|
+
sessionId: options.sessionId,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
this.enforceSessionLimit();
|
|
111
129
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
throw new ConversationMemoryError(`Failed to store conversation turn for session ${options.sessionId}`, "STORAGE_ERROR", {
|
|
132
|
+
sessionId: options.sessionId,
|
|
133
|
+
error: error instanceof Error ? error.message : String(error),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
120
137
|
}
|
|
121
138
|
/**
|
|
122
139
|
* Validate and prepare a message before adding to session
|
|
@@ -197,8 +214,22 @@ export class ConversationMemoryManager {
|
|
|
197
214
|
* Now consistently async to match Redis implementation
|
|
198
215
|
*/
|
|
199
216
|
async buildContextMessages(sessionId, _userId, _enableSummarization, requestId) {
|
|
200
|
-
|
|
201
|
-
|
|
217
|
+
return withSpan({
|
|
218
|
+
name: "neurolink.memory.buildContext",
|
|
219
|
+
tracer: tracers.memory,
|
|
220
|
+
attributes: {
|
|
221
|
+
"memory.type": "in-memory",
|
|
222
|
+
"session.id": sessionId,
|
|
223
|
+
"memory.operation": "build_context",
|
|
224
|
+
},
|
|
225
|
+
}, async (span) => {
|
|
226
|
+
const session = this.sessions.get(sessionId);
|
|
227
|
+
const messages = session
|
|
228
|
+
? buildContextFromPointer(session, requestId)
|
|
229
|
+
: [];
|
|
230
|
+
span.setAttribute("memory.message_count", messages.length);
|
|
231
|
+
return messages;
|
|
232
|
+
});
|
|
202
233
|
}
|
|
203
234
|
getSession(sessionId, _userId) {
|
|
204
235
|
return this.sessions.get(sessionId);
|
|
@@ -251,13 +282,25 @@ export class ConversationMemoryManager {
|
|
|
251
282
|
};
|
|
252
283
|
}
|
|
253
284
|
async clearSession(sessionId) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
285
|
+
return withSpan({
|
|
286
|
+
name: "neurolink.memory.clear",
|
|
287
|
+
tracer: tracers.memory,
|
|
288
|
+
attributes: {
|
|
289
|
+
"memory.type": "in-memory",
|
|
290
|
+
"session.id": sessionId,
|
|
291
|
+
"memory.operation": "clear_session",
|
|
292
|
+
},
|
|
293
|
+
}, async (span) => {
|
|
294
|
+
const session = this.sessions.get(sessionId);
|
|
295
|
+
if (!session) {
|
|
296
|
+
span.setAttribute("memory.session_found", false);
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
this.sessions.delete(sessionId);
|
|
300
|
+
span.setAttribute("memory.session_found", true);
|
|
301
|
+
logger.info("Session cleared", { sessionId });
|
|
302
|
+
return true;
|
|
303
|
+
});
|
|
261
304
|
}
|
|
262
305
|
async clearAllSessions() {
|
|
263
306
|
const sessionIds = Array.from(this.sessions.keys());
|
|
@@ -56,7 +56,7 @@ export function getAvailableProviders() {
|
|
|
56
56
|
* Sort providers by preference (cost, speed, quality)
|
|
57
57
|
*/
|
|
58
58
|
export function sortProvidersByPreference(providers, preferCheap = true) {
|
|
59
|
-
return providers.sort((a, b) => {
|
|
59
|
+
return [...providers].sort((a, b) => {
|
|
60
60
|
const aPerf = a.performance || { cost: 0, speed: 0, quality: 0 };
|
|
61
61
|
const bPerf = b.performance || { cost: 0, speed: 0, quality: 0 };
|
|
62
62
|
if (preferCheap) {
|
|
@@ -160,16 +160,17 @@ export function getPerformanceOptimizedProvider(priority = "speed") {
|
|
|
160
160
|
if (availableProviders.length === 0) {
|
|
161
161
|
return null;
|
|
162
162
|
}
|
|
163
|
-
// Score providers based on real performance data
|
|
164
|
-
|
|
163
|
+
// Score providers based on real performance data.
|
|
164
|
+
// Only consider providers that have runtime metrics (sampleCount >= 3).
|
|
165
|
+
// Providers without metrics are excluded to avoid misleading recommendations
|
|
166
|
+
// (e.g. ollama appearing as "recommended" when it has no requiredEnvVars and
|
|
167
|
+
// thus always passes the "available" check, even when not actually in use).
|
|
168
|
+
const scoredProviders = [];
|
|
169
|
+
for (const provider of availableProviders) {
|
|
165
170
|
const metrics = providerMetrics.get(provider.provider);
|
|
166
171
|
if (!metrics || metrics.sampleCount < 3) {
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
provider,
|
|
170
|
-
score: getStaticPerformanceScore(provider, priority),
|
|
171
|
-
metrics: null,
|
|
172
|
-
};
|
|
172
|
+
// Skip providers without sufficient runtime data
|
|
173
|
+
continue;
|
|
173
174
|
}
|
|
174
175
|
let score = 0;
|
|
175
176
|
switch (priority) {
|
|
@@ -190,34 +191,16 @@ export function getPerformanceOptimizedProvider(priority = "speed") {
|
|
|
190
191
|
break;
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
|
-
|
|
194
|
-
}
|
|
194
|
+
scoredProviders.push({ provider, score });
|
|
195
|
+
}
|
|
196
|
+
if (scoredProviders.length === 0) {
|
|
197
|
+
// No providers have sufficient runtime data to make a recommendation
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
195
200
|
// Sort by score and return best
|
|
196
201
|
scoredProviders.sort((a, b) => b.score - a.score);
|
|
197
202
|
return scoredProviders[0].provider;
|
|
198
203
|
}
|
|
199
|
-
/**
|
|
200
|
-
* Helper function for providers without performance data
|
|
201
|
-
*/
|
|
202
|
-
function getStaticPerformanceScore(provider, priority) {
|
|
203
|
-
switch (priority) {
|
|
204
|
-
case "speed": {
|
|
205
|
-
const speedScore = provider.performance?.speed || 1;
|
|
206
|
-
return speedScore;
|
|
207
|
-
}
|
|
208
|
-
case "cost": {
|
|
209
|
-
const costScore = provider.performance?.cost || 1;
|
|
210
|
-
return costScore;
|
|
211
|
-
}
|
|
212
|
-
case "reliability": {
|
|
213
|
-
const qualityScore = provider.performance?.quality || 1;
|
|
214
|
-
return qualityScore;
|
|
215
|
-
}
|
|
216
|
-
default: {
|
|
217
|
-
throw new Error(`Invalid priority: "${priority}". Must be one of: speed, cost, reliability`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
204
|
export function getProviderPerformanceAnalytics() {
|
|
222
205
|
const analytics = {};
|
|
223
206
|
for (const [providerName, metrics] of providerMetrics.entries()) {
|