@juspay/neurolink 9.41.0 → 9.42.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/README.md +7 -1
- package/dist/auth/anthropicOAuth.d.ts +18 -3
- package/dist/auth/anthropicOAuth.js +137 -4
- package/dist/auth/providers/firebase.js +5 -1
- package/dist/auth/providers/jwt.js +5 -1
- package/dist/auth/providers/workos.js +5 -1
- package/dist/auth/sessionManager.d.ts +1 -1
- package/dist/auth/sessionManager.js +58 -27
- package/dist/browser/neurolink.min.js +337 -318
- package/dist/cli/commands/mcp.js +3 -0
- package/dist/cli/commands/proxy.d.ts +2 -1
- package/dist/cli/commands/proxy.js +279 -16
- package/dist/cli/commands/task.js +3 -0
- package/dist/cli/factories/commandFactory.d.ts +2 -0
- package/dist/cli/factories/commandFactory.js +38 -0
- package/dist/cli/parser.js +4 -3
- package/dist/client/aiSdkAdapter.js +3 -0
- package/dist/client/streamingClient.js +30 -10
- package/dist/core/modules/GenerationHandler.js +3 -2
- package/dist/core/redisConversationMemoryManager.js +7 -3
- package/dist/evaluation/BatchEvaluator.js +4 -1
- package/dist/evaluation/hooks/observabilityHooks.js +5 -3
- package/dist/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
- package/dist/evaluation/pipeline/evaluationPipeline.js +20 -8
- package/dist/evaluation/pipeline/strategies/batchStrategy.js +6 -3
- package/dist/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
- package/dist/lib/auth/anthropicOAuth.d.ts +18 -3
- package/dist/lib/auth/anthropicOAuth.js +137 -4
- package/dist/lib/auth/providers/firebase.js +5 -1
- package/dist/lib/auth/providers/jwt.js +5 -1
- package/dist/lib/auth/providers/workos.js +5 -1
- package/dist/lib/auth/sessionManager.d.ts +1 -1
- package/dist/lib/auth/sessionManager.js +58 -27
- package/dist/lib/client/aiSdkAdapter.js +3 -0
- package/dist/lib/client/streamingClient.js +30 -10
- package/dist/lib/core/modules/GenerationHandler.js +3 -2
- package/dist/lib/core/redisConversationMemoryManager.js +7 -3
- package/dist/lib/evaluation/BatchEvaluator.js +4 -1
- package/dist/lib/evaluation/hooks/observabilityHooks.js +5 -3
- package/dist/lib/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
- package/dist/lib/evaluation/pipeline/evaluationPipeline.js +20 -8
- package/dist/lib/evaluation/pipeline/strategies/batchStrategy.js +6 -3
- package/dist/lib/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
- package/dist/lib/neurolink.d.ts +3 -2
- package/dist/lib/neurolink.js +260 -494
- package/dist/lib/observability/otelBridge.d.ts +2 -2
- package/dist/lib/observability/otelBridge.js +12 -3
- package/dist/lib/providers/amazonBedrock.js +2 -4
- package/dist/lib/providers/anthropic.d.ts +9 -5
- package/dist/lib/providers/anthropic.js +19 -14
- package/dist/lib/providers/anthropicBaseProvider.d.ts +3 -3
- package/dist/lib/providers/anthropicBaseProvider.js +5 -4
- package/dist/lib/providers/azureOpenai.d.ts +1 -1
- package/dist/lib/providers/azureOpenai.js +5 -4
- package/dist/lib/providers/googleAiStudio.js +30 -1
- package/dist/lib/providers/googleVertex.js +28 -6
- package/dist/lib/providers/huggingFace.d.ts +3 -3
- package/dist/lib/providers/huggingFace.js +6 -8
- package/dist/lib/providers/litellm.js +41 -29
- package/dist/lib/providers/mistral.js +2 -1
- package/dist/lib/providers/ollama.js +80 -23
- package/dist/lib/providers/openAI.js +3 -2
- package/dist/lib/providers/openRouter.js +2 -1
- package/dist/lib/providers/openaiCompatible.d.ts +4 -4
- package/dist/lib/providers/openaiCompatible.js +4 -4
- package/dist/lib/proxy/claudeFormat.d.ts +3 -2
- package/dist/lib/proxy/claudeFormat.js +25 -20
- package/dist/lib/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
- package/dist/lib/proxy/cloaking/plugins/sessionIdentity.js +9 -33
- package/dist/lib/proxy/modelRouter.js +3 -0
- package/dist/lib/proxy/oauthFetch.d.ts +1 -1
- package/dist/lib/proxy/oauthFetch.js +65 -72
- package/dist/lib/proxy/proxyConfig.js +44 -24
- package/dist/lib/proxy/proxyEnv.d.ts +19 -0
- package/dist/lib/proxy/proxyEnv.js +73 -0
- package/dist/lib/proxy/proxyFetch.js +50 -4
- package/dist/lib/proxy/proxyTracer.d.ts +133 -0
- package/dist/lib/proxy/proxyTracer.js +645 -0
- package/dist/lib/proxy/rawStreamCapture.d.ts +10 -0
- package/dist/lib/proxy/rawStreamCapture.js +83 -0
- package/dist/lib/proxy/requestLogger.d.ts +32 -5
- package/dist/lib/proxy/requestLogger.js +406 -37
- package/dist/lib/proxy/sseInterceptor.d.ts +97 -0
- package/dist/lib/proxy/sseInterceptor.js +402 -0
- package/dist/lib/proxy/usageStats.d.ts +4 -3
- package/dist/lib/proxy/usageStats.js +25 -12
- package/dist/lib/rag/chunkers/MarkdownChunker.js +13 -5
- package/dist/lib/rag/chunking/markdownChunker.js +15 -6
- package/dist/lib/server/routes/claudeProxyRoutes.d.ts +7 -2
- package/dist/lib/server/routes/claudeProxyRoutes.js +1737 -508
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +7 -1
- package/dist/lib/services/server/ai/observability/instrumentation.js +240 -40
- package/dist/lib/tasks/backends/bullmqBackend.d.ts +1 -0
- package/dist/lib/tasks/backends/bullmqBackend.js +14 -7
- package/dist/lib/tasks/store/redisTaskStore.d.ts +1 -0
- package/dist/lib/tasks/store/redisTaskStore.js +34 -26
- package/dist/lib/tasks/taskManager.d.ts +3 -0
- package/dist/lib/tasks/taskManager.js +63 -30
- package/dist/lib/telemetry/index.d.ts +2 -1
- package/dist/lib/telemetry/index.js +2 -1
- package/dist/lib/telemetry/telemetryService.d.ts +3 -0
- package/dist/lib/telemetry/telemetryService.js +65 -5
- package/dist/lib/types/cli.d.ts +10 -0
- package/dist/lib/types/proxyTypes.d.ts +37 -5
- package/dist/lib/types/streamTypes.d.ts +25 -3
- package/dist/lib/utils/messageBuilder.js +3 -2
- package/dist/lib/utils/providerHealth.d.ts +18 -0
- package/dist/lib/utils/providerHealth.js +240 -9
- package/dist/lib/utils/providerUtils.js +14 -8
- package/dist/lib/utils/toolChoice.d.ts +4 -0
- package/dist/lib/utils/toolChoice.js +7 -0
- package/dist/neurolink.d.ts +3 -2
- package/dist/neurolink.js +260 -494
- package/dist/observability/otelBridge.d.ts +2 -2
- package/dist/observability/otelBridge.js +12 -3
- package/dist/providers/amazonBedrock.js +2 -4
- package/dist/providers/anthropic.d.ts +9 -5
- package/dist/providers/anthropic.js +19 -14
- package/dist/providers/anthropicBaseProvider.d.ts +3 -3
- package/dist/providers/anthropicBaseProvider.js +5 -4
- package/dist/providers/azureOpenai.d.ts +1 -1
- package/dist/providers/azureOpenai.js +5 -4
- package/dist/providers/googleAiStudio.js +30 -1
- package/dist/providers/googleVertex.js +28 -6
- package/dist/providers/huggingFace.d.ts +3 -3
- package/dist/providers/huggingFace.js +6 -7
- package/dist/providers/litellm.js +41 -29
- package/dist/providers/mistral.js +2 -1
- package/dist/providers/ollama.js +80 -23
- package/dist/providers/openAI.js +3 -2
- package/dist/providers/openRouter.js +2 -1
- package/dist/providers/openaiCompatible.d.ts +4 -4
- package/dist/providers/openaiCompatible.js +4 -3
- package/dist/proxy/claudeFormat.d.ts +3 -2
- package/dist/proxy/claudeFormat.js +25 -20
- package/dist/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
- package/dist/proxy/cloaking/plugins/sessionIdentity.js +9 -33
- package/dist/proxy/modelRouter.js +3 -0
- package/dist/proxy/oauthFetch.d.ts +1 -1
- package/dist/proxy/oauthFetch.js +65 -72
- package/dist/proxy/proxyConfig.js +44 -24
- package/dist/proxy/proxyEnv.d.ts +19 -0
- package/dist/proxy/proxyEnv.js +72 -0
- package/dist/proxy/proxyFetch.js +50 -4
- package/dist/proxy/proxyTracer.d.ts +133 -0
- package/dist/proxy/proxyTracer.js +644 -0
- package/dist/proxy/rawStreamCapture.d.ts +10 -0
- package/dist/proxy/rawStreamCapture.js +82 -0
- package/dist/proxy/requestLogger.d.ts +32 -5
- package/dist/proxy/requestLogger.js +406 -37
- package/dist/proxy/sseInterceptor.d.ts +97 -0
- package/dist/proxy/sseInterceptor.js +401 -0
- package/dist/proxy/usageStats.d.ts +4 -3
- package/dist/proxy/usageStats.js +25 -12
- package/dist/rag/chunkers/MarkdownChunker.js +13 -5
- package/dist/rag/chunking/markdownChunker.js +15 -6
- package/dist/server/routes/claudeProxyRoutes.d.ts +7 -2
- package/dist/server/routes/claudeProxyRoutes.js +1737 -508
- package/dist/services/server/ai/observability/instrumentation.d.ts +7 -1
- package/dist/services/server/ai/observability/instrumentation.js +240 -40
- package/dist/tasks/backends/bullmqBackend.d.ts +1 -0
- package/dist/tasks/backends/bullmqBackend.js +14 -7
- package/dist/tasks/store/redisTaskStore.d.ts +1 -0
- package/dist/tasks/store/redisTaskStore.js +34 -26
- package/dist/tasks/taskManager.d.ts +3 -0
- package/dist/tasks/taskManager.js +63 -30
- package/dist/telemetry/index.d.ts +2 -1
- package/dist/telemetry/index.js +2 -1
- package/dist/telemetry/telemetryService.d.ts +3 -0
- package/dist/telemetry/telemetryService.js +65 -5
- package/dist/types/cli.d.ts +10 -0
- package/dist/types/proxyTypes.d.ts +37 -5
- package/dist/types/streamTypes.d.ts +25 -3
- package/dist/utils/messageBuilder.js +3 -2
- package/dist/utils/providerHealth.d.ts +18 -0
- package/dist/utils/providerHealth.js +240 -9
- package/dist/utils/providerUtils.js +14 -8
- package/dist/utils/toolChoice.d.ts +4 -0
- package/dist/utils/toolChoice.js +6 -0
- package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +6609 -0
- package/docs/changelog.md +252 -0
- package/package.json +17 -1
- package/scripts/observability/check-proxy-telemetry.mjs +235 -0
- package/scripts/observability/docker-compose.proxy-observability.yaml +55 -0
- package/scripts/observability/import-openobserve-dashboard.mjs +240 -0
- package/scripts/observability/manage-local-openobserve.sh +184 -0
- package/scripts/observability/otel-collector.proxy-observability.yaml +78 -0
- package/scripts/observability/proxy-observability.env.example +23 -0
|
@@ -70,15 +70,35 @@ export class TaskManager {
|
|
|
70
70
|
store: this.store.type,
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
+
getStore() {
|
|
74
|
+
if (!this.store) {
|
|
75
|
+
throw TaskError.create("BACKEND_NOT_INITIALIZED", "[TaskManager] Store not initialized. Call initialize() first.");
|
|
76
|
+
}
|
|
77
|
+
return this.store;
|
|
78
|
+
}
|
|
79
|
+
getBackend() {
|
|
80
|
+
if (!this.backend) {
|
|
81
|
+
throw TaskError.create("BACKEND_NOT_INITIALIZED", "[TaskManager] Backend not initialized. Call initialize() first.");
|
|
82
|
+
}
|
|
83
|
+
return this.backend;
|
|
84
|
+
}
|
|
85
|
+
getExecutor() {
|
|
86
|
+
if (!this.executor) {
|
|
87
|
+
throw TaskError.create("BACKEND_NOT_INITIALIZED", "[TaskManager] Executor not initialized. Call initialize() first.");
|
|
88
|
+
}
|
|
89
|
+
return this.executor;
|
|
90
|
+
}
|
|
73
91
|
// ── Public API ────────────────────────────────────────
|
|
74
92
|
async create(definition) {
|
|
75
93
|
if (this.config.enabled === false) {
|
|
76
94
|
throw TaskError.create("TASK_DISABLED", "TaskManager is disabled. Set tasks.enabled to true in config.");
|
|
77
95
|
}
|
|
78
96
|
await this.ensureInitialized();
|
|
97
|
+
const store = this.getStore();
|
|
98
|
+
const backend = this.getBackend();
|
|
79
99
|
// Enforce maximum task limit to prevent unbounded task creation
|
|
80
100
|
const maxTasks = this.config.maxTasks ?? TASK_DEFAULTS.maxTasks;
|
|
81
|
-
const existingTasks = await
|
|
101
|
+
const existingTasks = await store.list();
|
|
82
102
|
if (existingTasks.length >= maxTasks) {
|
|
83
103
|
throw TaskError.create("TASK_LIMIT_REACHED", `Task limit reached (${maxTasks}). Delete existing tasks or increase maxTasks config.`);
|
|
84
104
|
}
|
|
@@ -124,7 +144,7 @@ export class TaskManager {
|
|
|
124
144
|
task.sessionId = `session_${nanoid(12)}`;
|
|
125
145
|
}
|
|
126
146
|
// Save to store
|
|
127
|
-
await
|
|
147
|
+
await store.save(task);
|
|
128
148
|
// Register callbacks (in-memory only)
|
|
129
149
|
if (definition.onSuccess || definition.onError || definition.onComplete) {
|
|
130
150
|
this.callbacks.set(task.id, {
|
|
@@ -135,10 +155,10 @@ export class TaskManager {
|
|
|
135
155
|
}
|
|
136
156
|
// Schedule
|
|
137
157
|
try {
|
|
138
|
-
await
|
|
158
|
+
await backend.schedule(task, (t) => this.onTaskTick(t));
|
|
139
159
|
}
|
|
140
160
|
catch (err) {
|
|
141
|
-
await
|
|
161
|
+
await store.delete(task.id);
|
|
142
162
|
throw err;
|
|
143
163
|
}
|
|
144
164
|
this.emit("task:created", task);
|
|
@@ -152,15 +172,17 @@ export class TaskManager {
|
|
|
152
172
|
}
|
|
153
173
|
async get(taskId) {
|
|
154
174
|
await this.ensureInitialized();
|
|
155
|
-
return this.
|
|
175
|
+
return this.getStore().get(taskId);
|
|
156
176
|
}
|
|
157
177
|
async list(filter) {
|
|
158
178
|
await this.ensureInitialized();
|
|
159
|
-
return this.
|
|
179
|
+
return this.getStore().list(filter);
|
|
160
180
|
}
|
|
161
181
|
async update(taskId, updates) {
|
|
162
182
|
await this.ensureInitialized();
|
|
163
|
-
const
|
|
183
|
+
const store = this.getStore();
|
|
184
|
+
const backend = this.getBackend();
|
|
185
|
+
const existing = await store.get(taskId);
|
|
164
186
|
if (!existing) {
|
|
165
187
|
throw TaskError.create("TASK_NOT_FOUND", `Task not found: ${taskId}`);
|
|
166
188
|
}
|
|
@@ -194,21 +216,21 @@ export class TaskManager {
|
|
|
194
216
|
}
|
|
195
217
|
else if (updates.mode !== "continuation") {
|
|
196
218
|
taskUpdates.sessionId = undefined;
|
|
197
|
-
await
|
|
219
|
+
await store.clearHistory(taskId);
|
|
198
220
|
}
|
|
199
221
|
}
|
|
200
|
-
const updated = await
|
|
222
|
+
const updated = await store.update(taskId, taskUpdates);
|
|
201
223
|
// Re-schedule if schedule changed and task is active
|
|
202
224
|
if (updates.schedule && updated.status === "active") {
|
|
203
|
-
await
|
|
204
|
-
await
|
|
225
|
+
await backend.cancel(taskId);
|
|
226
|
+
await backend.schedule(updated, (t) => this.onTaskTick(t));
|
|
205
227
|
}
|
|
206
228
|
return updated;
|
|
207
229
|
}
|
|
208
230
|
/** Run a task immediately (outside of its schedule) */
|
|
209
231
|
async run(taskId) {
|
|
210
232
|
await this.ensureInitialized();
|
|
211
|
-
const task = await this.
|
|
233
|
+
const task = await this.getStore().get(taskId);
|
|
212
234
|
if (!task) {
|
|
213
235
|
throw TaskError.create("TASK_NOT_FOUND", `Task not found: ${taskId}`);
|
|
214
236
|
}
|
|
@@ -216,42 +238,48 @@ export class TaskManager {
|
|
|
216
238
|
}
|
|
217
239
|
async pause(taskId) {
|
|
218
240
|
await this.ensureInitialized();
|
|
219
|
-
const
|
|
241
|
+
const store = this.getStore();
|
|
242
|
+
const backend = this.getBackend();
|
|
243
|
+
const task = await store.get(taskId);
|
|
220
244
|
if (!task) {
|
|
221
245
|
throw TaskError.create("TASK_NOT_FOUND", `Task not found: ${taskId}`);
|
|
222
246
|
}
|
|
223
247
|
if (task.status !== "active") {
|
|
224
248
|
throw TaskError.create("INVALID_TASK_STATUS", `Cannot pause task with status: ${task.status}`);
|
|
225
249
|
}
|
|
226
|
-
await
|
|
227
|
-
const updated = await
|
|
250
|
+
await backend.pause(taskId);
|
|
251
|
+
const updated = await store.update(taskId, { status: "paused" });
|
|
228
252
|
this.emit("task:paused", updated);
|
|
229
253
|
return updated;
|
|
230
254
|
}
|
|
231
255
|
async resume(taskId) {
|
|
232
256
|
await this.ensureInitialized();
|
|
233
|
-
const
|
|
257
|
+
const store = this.getStore();
|
|
258
|
+
const backend = this.getBackend();
|
|
259
|
+
const task = await store.get(taskId);
|
|
234
260
|
if (!task) {
|
|
235
261
|
throw TaskError.create("TASK_NOT_FOUND", `Task not found: ${taskId}`);
|
|
236
262
|
}
|
|
237
263
|
if (task.status !== "paused") {
|
|
238
264
|
throw TaskError.create("INVALID_TASK_STATUS", `Cannot resume task with status: ${task.status}`);
|
|
239
265
|
}
|
|
240
|
-
const updated = await
|
|
241
|
-
await
|
|
266
|
+
const updated = await store.update(taskId, { status: "active" });
|
|
267
|
+
await backend.schedule(updated, (t) => this.onTaskTick(t));
|
|
242
268
|
this.emit("task:resumed", updated);
|
|
243
269
|
return updated;
|
|
244
270
|
}
|
|
245
271
|
async delete(taskId) {
|
|
246
272
|
await this.ensureInitialized();
|
|
247
|
-
|
|
248
|
-
|
|
273
|
+
const backend = this.getBackend();
|
|
274
|
+
const store = this.getStore();
|
|
275
|
+
await backend.cancel(taskId);
|
|
276
|
+
await store.delete(taskId);
|
|
249
277
|
this.callbacks.delete(taskId);
|
|
250
278
|
this.emit("task:deleted", taskId);
|
|
251
279
|
}
|
|
252
280
|
async runs(taskId, options) {
|
|
253
281
|
await this.ensureInitialized();
|
|
254
|
-
return this.
|
|
282
|
+
return this.getStore().getRuns(taskId, options);
|
|
255
283
|
}
|
|
256
284
|
async shutdown() {
|
|
257
285
|
if (this.backend) {
|
|
@@ -279,8 +307,11 @@ export class TaskManager {
|
|
|
279
307
|
*/
|
|
280
308
|
async onTaskTick(task) {
|
|
281
309
|
this.emit("task:started", task);
|
|
310
|
+
const store = this.getStore();
|
|
311
|
+
const backend = this.getBackend();
|
|
312
|
+
const executor = this.getExecutor();
|
|
282
313
|
// Re-read latest task state (may have been updated/paused since scheduling)
|
|
283
|
-
const current = await
|
|
314
|
+
const current = await store.get(task.id);
|
|
284
315
|
if (!current || current.status !== "active") {
|
|
285
316
|
logger.debug("[TaskManager] Skipping tick for non-active task", {
|
|
286
317
|
taskId: task.id,
|
|
@@ -295,9 +326,9 @@ export class TaskManager {
|
|
|
295
326
|
timestamp: new Date().toISOString(),
|
|
296
327
|
};
|
|
297
328
|
}
|
|
298
|
-
const result = await
|
|
329
|
+
const result = await executor.execute(current);
|
|
299
330
|
// Log the run
|
|
300
|
-
await
|
|
331
|
+
await store.appendRun(task.id, result);
|
|
301
332
|
// Update task tracking
|
|
302
333
|
const updates = {
|
|
303
334
|
runCount: current.runCount + 1,
|
|
@@ -306,18 +337,18 @@ export class TaskManager {
|
|
|
306
337
|
// Check if task should complete
|
|
307
338
|
if (current.maxRuns && current.runCount + 1 >= current.maxRuns) {
|
|
308
339
|
updates.status = "completed";
|
|
309
|
-
await
|
|
340
|
+
await backend.cancel(task.id);
|
|
310
341
|
}
|
|
311
342
|
// Mark successful once tasks as completed
|
|
312
343
|
if (result.status === "success" && current.schedule.type === "once") {
|
|
313
344
|
updates.status = "completed";
|
|
314
|
-
await
|
|
345
|
+
await backend.cancel(task.id);
|
|
315
346
|
}
|
|
316
347
|
// Mark as failed on permanent error
|
|
317
348
|
if (result.status === "error" && current.schedule.type === "once") {
|
|
318
349
|
updates.status = "failed";
|
|
319
350
|
}
|
|
320
|
-
await
|
|
351
|
+
await store.update(task.id, updates);
|
|
321
352
|
// Fire callbacks
|
|
322
353
|
const cbs = this.callbacks.get(task.id);
|
|
323
354
|
if (cbs) {
|
|
@@ -337,7 +368,7 @@ export class TaskManager {
|
|
|
337
368
|
});
|
|
338
369
|
}
|
|
339
370
|
if (updates.status === "completed" || updates.status === "failed") {
|
|
340
|
-
const finalTask = await
|
|
371
|
+
const finalTask = await store.get(task.id);
|
|
341
372
|
if (finalTask && cbs.onComplete) {
|
|
342
373
|
await cbs.onComplete(finalTask);
|
|
343
374
|
}
|
|
@@ -364,10 +395,12 @@ export class TaskManager {
|
|
|
364
395
|
* Called on initialization to handle process restarts.
|
|
365
396
|
*/
|
|
366
397
|
async rescheduleActiveTasks() {
|
|
367
|
-
const
|
|
398
|
+
const store = this.getStore();
|
|
399
|
+
const backend = this.getBackend();
|
|
400
|
+
const activeTasks = await store.list({ status: "active" });
|
|
368
401
|
for (const task of activeTasks) {
|
|
369
402
|
try {
|
|
370
|
-
await
|
|
403
|
+
await backend.schedule(task, (t) => this.onTaskTick(t));
|
|
371
404
|
logger.debug("[TaskManager] Re-scheduled task", {
|
|
372
405
|
taskId: task.id,
|
|
373
406
|
name: task.name,
|
|
@@ -4,7 +4,8 @@ export { withSpan, withClientSpan, type SpanOptions } from "./withSpan.js";
|
|
|
4
4
|
export { ATTR } from "./attributes.js";
|
|
5
5
|
/**
|
|
6
6
|
* Initialize telemetry for NeuroLink
|
|
7
|
-
*
|
|
7
|
+
* Reuses an existing global TracerProvider when one is already registered,
|
|
8
|
+
* otherwise bootstraps Neurolink telemetry when an exporter endpoint is configured.
|
|
8
9
|
*/
|
|
9
10
|
export declare function initializeTelemetry(): Promise<import("./telemetryService.js").TelemetryService>;
|
|
10
11
|
/**
|
package/dist/telemetry/index.js
CHANGED
|
@@ -6,7 +6,8 @@ export { ATTR } from "./attributes.js";
|
|
|
6
6
|
import { logger } from "../utils/logger.js";
|
|
7
7
|
/**
|
|
8
8
|
* Initialize telemetry for NeuroLink
|
|
9
|
-
*
|
|
9
|
+
* Reuses an existing global TracerProvider when one is already registered,
|
|
10
|
+
* otherwise bootstraps Neurolink telemetry when an exporter endpoint is configured.
|
|
10
11
|
*/
|
|
11
12
|
export async function initializeTelemetry() {
|
|
12
13
|
const { TelemetryService } = await import("./telemetryService.js");
|
|
@@ -11,6 +11,7 @@ export declare class TelemetryService {
|
|
|
11
11
|
private tracerProvider?;
|
|
12
12
|
private enabled;
|
|
13
13
|
private initialized;
|
|
14
|
+
private usingExternalTracerProvider;
|
|
14
15
|
private meter?;
|
|
15
16
|
private tracer?;
|
|
16
17
|
private aiRequestCounter?;
|
|
@@ -29,6 +30,8 @@ export declare class TelemetryService {
|
|
|
29
30
|
private constructor();
|
|
30
31
|
static getInstance(): TelemetryService;
|
|
31
32
|
private isTelemetryEnabled;
|
|
33
|
+
private hasExternalTracerProvider;
|
|
34
|
+
private adoptExternalTracerProvider;
|
|
32
35
|
private initializeTelemetry;
|
|
33
36
|
private initializeMetrics;
|
|
34
37
|
initialize(): Promise<void>;
|
|
@@ -9,6 +9,7 @@ export class TelemetryService {
|
|
|
9
9
|
tracerProvider;
|
|
10
10
|
enabled = false;
|
|
11
11
|
initialized = false;
|
|
12
|
+
usingExternalTracerProvider = false;
|
|
12
13
|
meter;
|
|
13
14
|
tracer;
|
|
14
15
|
// Optional Metrics (only created when enabled)
|
|
@@ -43,11 +44,48 @@ export class TelemetryService {
|
|
|
43
44
|
return TelemetryService.instance;
|
|
44
45
|
}
|
|
45
46
|
isTelemetryEnabled() {
|
|
46
|
-
return (
|
|
47
|
+
return (this.hasExternalTracerProvider() ||
|
|
48
|
+
process.env.NEUROLINK_TELEMETRY_ENABLED === "true" ||
|
|
47
49
|
process.env.OTEL_EXPORTER_OTLP_ENDPOINT !== undefined);
|
|
48
50
|
}
|
|
51
|
+
hasExternalTracerProvider() {
|
|
52
|
+
try {
|
|
53
|
+
const provider = trace.getTracerProvider();
|
|
54
|
+
if (!provider) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const delegateName = provider._delegate?.constructor?.name || "";
|
|
58
|
+
if (delegateName && delegateName !== "NoopTracerProvider") {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
const providerName = provider.constructor?.name || "";
|
|
62
|
+
return (providerName !== "ProxyTracerProvider" &&
|
|
63
|
+
providerName !== "NoopTracerProvider");
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
logger.warn("[Telemetry] Failed checking for external TracerProvider", {
|
|
67
|
+
error: error instanceof Error ? error.message : String(error),
|
|
68
|
+
});
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
adoptExternalTracerProvider(reason) {
|
|
73
|
+
this.usingExternalTracerProvider = true;
|
|
74
|
+
this.tracerProvider = undefined;
|
|
75
|
+
this.meter = metrics.getMeter("neurolink-ai");
|
|
76
|
+
this.tracer = trace.getTracer("neurolink-ai");
|
|
77
|
+
this.initializeMetrics();
|
|
78
|
+
logger.debug("[Telemetry] Reusing externally managed TracerProvider", {
|
|
79
|
+
reason,
|
|
80
|
+
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
49
83
|
initializeTelemetry() {
|
|
50
84
|
try {
|
|
85
|
+
if (this.hasExternalTracerProvider()) {
|
|
86
|
+
this.adoptExternalTracerProvider("global tracer provider already registered");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
51
89
|
const resource = resourceFromAttributes({
|
|
52
90
|
[ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || "neurolink-ai",
|
|
53
91
|
[ATTR_SERVICE_VERSION]: process.env.OTEL_SERVICE_VERSION || "3.0.1",
|
|
@@ -61,13 +99,23 @@ export class TelemetryService {
|
|
|
61
99
|
resource,
|
|
62
100
|
spanProcessors: [new BatchSpanProcessor(exporter)],
|
|
63
101
|
});
|
|
64
|
-
trace.setGlobalTracerProvider(this.tracerProvider);
|
|
65
102
|
this.meter = metrics.getMeter("neurolink-ai");
|
|
66
|
-
this.tracer =
|
|
103
|
+
this.tracer = this.tracerProvider.getTracer("neurolink-ai");
|
|
67
104
|
this.initializeMetrics();
|
|
68
|
-
logger.debug("[Telemetry] Initialized
|
|
105
|
+
logger.debug("[Telemetry] Initialized local telemetry exporter", {
|
|
106
|
+
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
107
|
+
globalTracerProviderOwnedBy: "observability/instrumentation",
|
|
108
|
+
});
|
|
69
109
|
}
|
|
70
110
|
catch (error) {
|
|
111
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
112
|
+
const isDuplicateRegistration = errorMessage.includes("duplicate registration") ||
|
|
113
|
+
errorMessage.includes("already registered") ||
|
|
114
|
+
errorMessage.includes("already set");
|
|
115
|
+
if (isDuplicateRegistration && this.hasExternalTracerProvider()) {
|
|
116
|
+
this.adoptExternalTracerProvider("duplicate global tracer registration detected");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
71
119
|
logger.error("[Telemetry] Failed to initialize:", error);
|
|
72
120
|
this.enabled = false;
|
|
73
121
|
}
|
|
@@ -105,6 +153,16 @@ export class TelemetryService {
|
|
|
105
153
|
if (!this.enabled) {
|
|
106
154
|
return;
|
|
107
155
|
}
|
|
156
|
+
if (this.usingExternalTracerProvider) {
|
|
157
|
+
this.initialized = true;
|
|
158
|
+
logger.debug("[Telemetry] External TracerProvider already initialized by host");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!this.tracerProvider) {
|
|
162
|
+
this.initialized = true;
|
|
163
|
+
logger.debug("[Telemetry] Tracer provider already prepared during constructor");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
108
166
|
try {
|
|
109
167
|
// Register AsyncLocalStorage context manager for proper parent-child
|
|
110
168
|
// span relationships across async boundaries (required for startActiveSpan)
|
|
@@ -308,7 +366,9 @@ export class TelemetryService {
|
|
|
308
366
|
}
|
|
309
367
|
// Cleanup
|
|
310
368
|
async shutdown() {
|
|
311
|
-
if (this.enabled &&
|
|
369
|
+
if (this.enabled &&
|
|
370
|
+
this.tracerProvider &&
|
|
371
|
+
!this.usingExternalTracerProvider) {
|
|
312
372
|
try {
|
|
313
373
|
await this.tracerProvider.shutdown();
|
|
314
374
|
this.initialized = false;
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -763,6 +763,8 @@ export type ProxyStartArgs = {
|
|
|
763
763
|
quiet?: boolean;
|
|
764
764
|
debug?: boolean;
|
|
765
765
|
config?: string;
|
|
766
|
+
envFile?: string;
|
|
767
|
+
passthrough?: boolean;
|
|
766
768
|
};
|
|
767
769
|
/** Arguments accepted by `neurolink proxy status` */
|
|
768
770
|
export type ProxyStatusArgs = {
|
|
@@ -779,6 +781,11 @@ export type ProxyGuardArgs = {
|
|
|
779
781
|
pollIntervalMs?: number;
|
|
780
782
|
quiet?: boolean;
|
|
781
783
|
};
|
|
784
|
+
/** Arguments accepted by `neurolink proxy telemetry <subcommand>` */
|
|
785
|
+
export type ProxyTelemetryArgs = {
|
|
786
|
+
action?: "setup" | "start" | "stop" | "status" | "logs" | "import-dashboard";
|
|
787
|
+
quiet?: boolean;
|
|
788
|
+
};
|
|
782
789
|
/** A fallback chain entry (serialisable subset of FallbackEntry) */
|
|
783
790
|
export type FallbackInfo = {
|
|
784
791
|
provider: string;
|
|
@@ -791,12 +798,15 @@ export type ProxyState = {
|
|
|
791
798
|
host: string;
|
|
792
799
|
strategy: string;
|
|
793
800
|
startTime: string;
|
|
801
|
+
envFile?: string;
|
|
794
802
|
/** Fallback chain from proxy config (persisted at start time) */
|
|
795
803
|
fallbackChain?: FallbackInfo[];
|
|
796
804
|
/** Optional fail-open guard PID that reverts Claude settings if proxy dies */
|
|
797
805
|
guardPid?: number;
|
|
798
806
|
/** How the proxy was launched — "launchd" if installed as service, "manual" otherwise */
|
|
799
807
|
managedBy?: "launchd" | "manual";
|
|
808
|
+
/** Whether the proxy is running in transparent passthrough mode */
|
|
809
|
+
passthrough?: boolean;
|
|
800
810
|
};
|
|
801
811
|
/** Stored credentials for an authenticated provider. */
|
|
802
812
|
export type StoredCredentials = {
|
|
@@ -239,10 +239,11 @@ export type ParsedClaudeRequest = {
|
|
|
239
239
|
role: string;
|
|
240
240
|
content: string;
|
|
241
241
|
}>;
|
|
242
|
-
/** Tools
|
|
242
|
+
/** Tools translated to AI SDK-compatible shape for provider fallback. */
|
|
243
243
|
tools: Record<string, {
|
|
244
|
-
description
|
|
245
|
-
|
|
244
|
+
description?: string;
|
|
245
|
+
inputSchema?: unknown;
|
|
246
|
+
execute?: (...args: unknown[]) => unknown;
|
|
246
247
|
}>;
|
|
247
248
|
/**
|
|
248
249
|
* Tool choice mapping from Claude format.
|
|
@@ -421,21 +422,52 @@ export type RequestLogEntry = {
|
|
|
421
422
|
errorMessage?: string;
|
|
422
423
|
inputTokens?: number;
|
|
423
424
|
outputTokens?: number;
|
|
425
|
+
cacheCreationTokens?: number;
|
|
426
|
+
cacheReadTokens?: number;
|
|
427
|
+
/** OTel trace ID for correlation with distributed traces */
|
|
428
|
+
traceId?: string;
|
|
429
|
+
/** OTel span ID for correlation with distributed traces */
|
|
430
|
+
spanId?: string;
|
|
431
|
+
};
|
|
432
|
+
export type RequestAttemptLogEntry = {
|
|
433
|
+
timestamp: string;
|
|
434
|
+
requestId: string;
|
|
435
|
+
attempt: number;
|
|
436
|
+
method: string;
|
|
437
|
+
path: string;
|
|
438
|
+
model: string;
|
|
439
|
+
stream: boolean;
|
|
440
|
+
toolCount: number;
|
|
441
|
+
account: string;
|
|
442
|
+
accountType: string;
|
|
443
|
+
responseStatus: number;
|
|
444
|
+
responseTimeMs: number;
|
|
445
|
+
errorType?: string;
|
|
446
|
+
errorMessage?: string;
|
|
447
|
+
inputTokens?: number;
|
|
448
|
+
outputTokens?: number;
|
|
449
|
+
cacheCreationTokens?: number;
|
|
450
|
+
cacheReadTokens?: number;
|
|
451
|
+
/** OTel trace ID for correlation with distributed traces */
|
|
452
|
+
traceId?: string;
|
|
453
|
+
/** OTel span ID for correlation with distributed traces */
|
|
454
|
+
spanId?: string;
|
|
424
455
|
};
|
|
425
456
|
export type AccountStats = {
|
|
426
457
|
label: string;
|
|
427
458
|
type: string;
|
|
428
|
-
|
|
459
|
+
attemptCount: number;
|
|
429
460
|
successCount: number;
|
|
430
461
|
errorCount: number;
|
|
431
462
|
rateLimitCount: number;
|
|
432
|
-
|
|
463
|
+
lastAttemptAt: number;
|
|
433
464
|
lastErrorAt?: number;
|
|
434
465
|
currentBackoffLevel: number;
|
|
435
466
|
coolingUntil?: number;
|
|
436
467
|
};
|
|
437
468
|
export type ProxyStats = {
|
|
438
469
|
startedAt: number;
|
|
470
|
+
totalAttempts: number;
|
|
439
471
|
totalRequests: number;
|
|
440
472
|
totalSuccess: number;
|
|
441
473
|
totalErrors: number;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { Tool } from "ai";
|
|
1
|
+
import type { LanguageModel, StepResult, Tool, ToolChoice } from "ai";
|
|
2
2
|
import type { AIProviderName } from "../constants/enums.js";
|
|
3
3
|
import type { EvaluationData } from "../index.js";
|
|
4
4
|
import type { RAGConfig } from "../rag/types.js";
|
|
5
5
|
import type { AnalyticsData, ToolExecutionEvent, ToolExecutionSummary } from "../types/index.js";
|
|
6
|
-
import type { MiddlewareFactoryOptions,
|
|
6
|
+
import type { MiddlewareFactoryOptions, OnChunkCallback, OnErrorCallback, OnFinishCallback } from "../types/middlewareTypes.js";
|
|
7
7
|
import type { TokenUsage } from "./analytics.js";
|
|
8
8
|
import type { JsonValue, UnknownRecord } from "./common.js";
|
|
9
9
|
import type { Content, ImageWithAltText } from "./content.js";
|
|
10
10
|
import type { ChatMessage } from "./conversation.js";
|
|
11
|
+
import type { AdditionalMemoryUser } from "./generateTypes.js";
|
|
11
12
|
import type { AIModelProviderConfig } from "./providers.js";
|
|
12
13
|
import type { TTSChunk, TTSOptions } from "./ttsTypes.js";
|
|
13
|
-
import type { AdditionalMemoryUser } from "./generateTypes.js";
|
|
14
14
|
import type { StandardRecord, ValidationSchema } from "./typeAliases.js";
|
|
15
15
|
/**
|
|
16
16
|
* Progress tracking and metadata for streaming operations
|
|
@@ -326,12 +326,34 @@ export type StreamOptions = {
|
|
|
326
326
|
abortSignal?: AbortSignal;
|
|
327
327
|
disableTools?: boolean;
|
|
328
328
|
maxSteps?: number;
|
|
329
|
+
/**
|
|
330
|
+
* Tool choice configuration for streaming generation.
|
|
331
|
+
* Mirrors generate() so translated/fallback requests can preserve forced tool use.
|
|
332
|
+
*/
|
|
333
|
+
toolChoice?: ToolChoice<Record<string, Tool>>;
|
|
334
|
+
/**
|
|
335
|
+
* Optional callback that runs before each stream step in a multi-step generation.
|
|
336
|
+
*/
|
|
337
|
+
prepareStep?: (options: {
|
|
338
|
+
steps: StepResult<Record<string, Tool>>[];
|
|
339
|
+
stepNumber: number;
|
|
340
|
+
maxSteps: number;
|
|
341
|
+
model: LanguageModel;
|
|
342
|
+
}) => PromiseLike<{
|
|
343
|
+
toolChoice?: ToolChoice<Record<string, Tool>>;
|
|
344
|
+
activeTools?: Record<string, Tool>;
|
|
345
|
+
} | undefined>;
|
|
329
346
|
/** Include only these tools by name (whitelist). If set, only matching tools are available. */
|
|
330
347
|
toolFilter?: string[];
|
|
331
348
|
/** Exclude these tools by name (blacklist). Applied after toolFilter. */
|
|
332
349
|
excludeTools?: string[];
|
|
333
350
|
/** Disable tool result caching for this request (overrides global mcp.cache.enabled) */
|
|
334
351
|
disableToolCache?: boolean;
|
|
352
|
+
/**
|
|
353
|
+
* Disable NeuroLink's internal provider fallback for this request.
|
|
354
|
+
* Used by the Claude proxy so the proxy itself can own fallback order.
|
|
355
|
+
*/
|
|
356
|
+
disableInternalFallback?: boolean;
|
|
335
357
|
/**
|
|
336
358
|
* Skip injecting tool schemas into the system prompt.
|
|
337
359
|
* When true, tools are ONLY passed natively via the provider's `tools` parameter,
|
|
@@ -775,6 +775,7 @@ async function processUnifiedFilesArray(options, maxSize, provider) {
|
|
|
775
775
|
return;
|
|
776
776
|
}
|
|
777
777
|
const totalFiles = options.input.files.length;
|
|
778
|
+
const files = options.input.files;
|
|
778
779
|
return withSpan({
|
|
779
780
|
name: "neurolink.file.process_all",
|
|
780
781
|
tracer: tracers.file,
|
|
@@ -787,8 +788,8 @@ async function processUnifiedFilesArray(options, maxSize, provider) {
|
|
|
787
788
|
options.input.text = options.input.text || "";
|
|
788
789
|
let includedCount = 0;
|
|
789
790
|
const fileRegistry = options.fileRegistry;
|
|
790
|
-
for (let fileIdx = 0; fileIdx <
|
|
791
|
-
const file =
|
|
791
|
+
for (let fileIdx = 0; fileIdx < files.length; fileIdx++) {
|
|
792
|
+
const file = files[fileIdx];
|
|
792
793
|
const filename = extractFilename(file, fileIdx);
|
|
793
794
|
try {
|
|
794
795
|
// ─── Lazy file registration path ──────────────────────────────
|
|
@@ -98,6 +98,20 @@ export declare class ProviderHealthChecker {
|
|
|
98
98
|
* Check Azure OpenAI configuration
|
|
99
99
|
*/
|
|
100
100
|
private static checkAzureConfig;
|
|
101
|
+
private static getLiteLLMBaseUrl;
|
|
102
|
+
private static getLiteLLMModelsUrl;
|
|
103
|
+
private static getConfiguredLiteLLMModel;
|
|
104
|
+
private static getOllamaBaseUrl;
|
|
105
|
+
private static getOllamaTagsUrl;
|
|
106
|
+
private static getConfiguredOllamaModel;
|
|
107
|
+
private static fetchJsonWithTimeout;
|
|
108
|
+
private static normalizeModelList;
|
|
109
|
+
private static hasRequestedModel;
|
|
110
|
+
private static getOllamaAvailableModels;
|
|
111
|
+
private static getLiteLLMAvailableModels;
|
|
112
|
+
private static checkOllamaAvailability;
|
|
113
|
+
private static checkLiteLLMAvailability;
|
|
114
|
+
private static checkLiteLLMConfig;
|
|
101
115
|
/**
|
|
102
116
|
* Check Ollama configuration
|
|
103
117
|
*/
|
|
@@ -169,6 +183,10 @@ export declare class ProviderHealthChecker {
|
|
|
169
183
|
* Clear health cache for a provider or all providers
|
|
170
184
|
*/
|
|
171
185
|
static clearHealthCache(providerName?: AIProviderName): void;
|
|
186
|
+
static checkFallbackProviderAvailability(providerName: string, model: string): Promise<{
|
|
187
|
+
available: boolean;
|
|
188
|
+
reason?: string;
|
|
189
|
+
}>;
|
|
172
190
|
/**
|
|
173
191
|
* Get the best healthy provider from a list of options (NON-BLOCKING)
|
|
174
192
|
* Prioritizes healthy providers over configured but unhealthy ones
|