@juspay/neurolink 12.59.0 → 13.0.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 +3 -3
- package/dist/auth/anthropicOAuth.js +3 -3
- package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
- package/dist/auth/sessionManager.js +4 -4
- package/dist/auth/tokenStore.js +4 -4
- package/dist/browser/neurolink.min.js +474 -473
- package/dist/cli/commands/observability.d.ts +1 -6
- package/dist/cli/commands/observability.js +0 -106
- package/dist/cli/commands/proxy.js +9 -18
- package/dist/cli/commands/telemetry.d.ts +3 -13
- package/dist/cli/commands/telemetry.js +7 -354
- package/dist/cli/loop/session.js +1 -1
- package/dist/client/httpClient.js +1 -1
- package/dist/client/sseClient.js +1 -1
- package/dist/client/streamingClient.js +2 -2
- package/dist/client/wsClient.js +1 -1
- package/dist/constants/langfuse.d.ts +25 -0
- package/dist/constants/langfuse.js +38 -0
- package/dist/constants/telemetry.d.ts +20 -0
- package/dist/constants/telemetry.js +34 -0
- package/dist/core/baseProvider.d.ts +2 -1
- package/dist/core/baseProvider.js +4 -11
- package/dist/core/conversationMemoryManager.js +5 -8
- package/dist/core/factory.js +1 -1
- package/dist/core/geminiLoopAdapter.d.ts +8 -1
- package/dist/core/geminiLoopAdapter.js +25 -1
- package/dist/core/loopEngine.js +69 -40
- package/dist/core/modules/MessageBuilder.js +2 -2
- package/dist/core/modules/StreamHandler.js +2 -2
- package/dist/core/modules/TelemetryHandler.d.ts +2 -18
- package/dist/core/modules/TelemetryHandler.js +1 -66
- package/dist/core/modules/ToolsManager.js +40 -31
- package/dist/core/nativeGenerateLoop.js +78 -8
- package/dist/core/redisConversationMemoryManager.js +8 -15
- package/dist/core/toolExecutionGuards.js +1 -13
- package/dist/index.d.ts +5 -12
- package/dist/index.js +5 -37
- package/dist/mcp/batching/requestBatcher.js +1 -1
- package/dist/mcp/externalServerManager.js +3 -21
- package/dist/mcp/toolDiscoveryService.js +4 -16
- package/dist/mcp/toolRegistry.js +2 -5
- package/dist/memory/memoryRetrievalTools.js +1 -1
- package/dist/neurolink.d.ts +16 -27
- package/dist/neurolink.js +788 -792
- package/dist/observability/exporterSpanProcessor.d.ts +50 -0
- package/dist/observability/exporterSpanProcessor.js +181 -0
- package/dist/observability/exporters/langfuseExporter.d.ts +13 -34
- package/dist/observability/exporters/langfuseExporter.js +101 -154
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.js +1 -0
- package/dist/observability/langfuseMasking.d.ts +8 -0
- package/dist/observability/langfuseMasking.js +47 -0
- package/dist/observability/otlpEncoding.d.ts +20 -0
- package/dist/observability/otlpEncoding.js +85 -0
- package/dist/providers/amazonBedrock/client.js +142 -176
- package/dist/providers/amazonBedrock/loopAdapter.js +14 -1
- package/dist/providers/amazonSagemaker.js +118 -80
- package/dist/providers/anthropic/client.js +3 -52
- package/dist/providers/anthropic/loopAdapter.js +24 -1
- package/dist/providers/anthropic/rateLimitCapture.js +9 -12
- package/dist/providers/googleAiStudio/client.d.ts +4 -5
- package/dist/providers/googleAiStudio/client.js +38 -40
- package/dist/providers/googleVertex/client.d.ts +2 -7
- package/dist/providers/googleVertex/client.js +282 -378
- package/dist/providers/googleVertex/constants.js +2 -2
- package/dist/providers/litellm/client.d.ts +2 -9
- package/dist/providers/litellm/client.js +1 -63
- package/dist/providers/openAI/client.d.ts +1 -8
- package/dist/providers/openAI/client.js +0 -65
- package/dist/providers/openaiChatCompletionsBase.d.ts +1 -0
- package/dist/providers/openaiChatCompletionsBase.js +61 -4
- package/dist/providers/replicate.js +22 -4
- package/dist/proxy/codexUsage.js +7 -0
- package/dist/proxy/proxyFetch.js +19 -25
- package/dist/proxy/proxyTracer.d.ts +14 -7
- package/dist/proxy/proxyTracer.js +129 -83
- package/dist/proxy/proxyTranslationEngine.js +0 -1
- package/dist/proxy/requestLogger.d.ts +1 -1
- package/dist/proxy/requestLogger.js +39 -45
- package/dist/server/middleware/auth.d.ts +5 -1
- package/dist/server/middleware/auth.js +10 -3
- package/dist/server/middleware/common.js +1 -1
- package/dist/server/routes/agentRoutes.js +4 -4
- package/dist/server/routes/claudeProxyRoutes.js +99 -196
- package/dist/server/routes/codexProxyRoutes.js +621 -591
- package/dist/server/routes/healthRoutes.js +6 -6
- package/dist/server/routes/mcpRoutes.js +7 -7
- package/dist/server/routes/memoryRoutes.js +7 -7
- package/dist/server/routes/openApiRoutes.js +3 -3
- package/dist/server/routes/toolRoutes.js +5 -5
- package/dist/server/routes/transcribeRoutes.js +1 -1
- package/dist/server/utils/validation.d.ts +3 -3
- package/dist/services/server/ai/observability/instrumentation.d.ts +34 -201
- package/dist/services/server/ai/observability/instrumentation.js +220 -1267
- package/dist/services/server/ai/observability/langfuseSetup.d.ts +14 -0
- package/dist/services/server/ai/observability/langfuseSetup.js +55 -0
- package/dist/services/server/ai/observability/otlpSetup.d.ts +16 -0
- package/dist/services/server/ai/observability/otlpSetup.js +58 -0
- package/dist/session/globalSessionState.js +0 -11
- package/dist/telemetry/attributes.d.ts +23 -46
- package/dist/telemetry/attributes.js +76 -162
- package/dist/telemetry/chatMessages.d.ts +13 -0
- package/dist/telemetry/chatMessages.js +59 -0
- package/dist/telemetry/index.d.ts +5 -19
- package/dist/telemetry/index.js +5 -26
- package/dist/telemetry/observation.d.ts +37 -0
- package/dist/telemetry/observation.js +112 -0
- package/dist/telemetry/observationNames.d.ts +18 -0
- package/dist/telemetry/observationNames.js +24 -0
- package/dist/telemetry/spanSanitizer.d.ts +39 -0
- package/dist/telemetry/spanSanitizer.js +92 -0
- package/dist/telemetry/spanSerialization.d.ts +13 -0
- package/dist/telemetry/spanSerialization.js +46 -0
- package/dist/telemetry/tracers.d.ts +37 -18
- package/dist/telemetry/tracers.js +97 -19
- package/dist/telemetry/withSpan.d.ts +47 -25
- package/dist/telemetry/withSpan.js +158 -71
- package/dist/types/cli.d.ts +0 -11
- package/dist/types/decision.d.ts +3 -0
- package/dist/types/exporter.d.ts +54 -0
- package/dist/types/exporter.js +0 -4
- package/dist/types/generate.d.ts +4 -1
- package/dist/types/isolatedAgent.d.ts +5 -4
- package/dist/types/loopEngine.d.ts +18 -28
- package/dist/types/observability.d.ts +127 -160
- package/dist/types/providers.d.ts +15 -0
- package/dist/types/proxy.d.ts +41 -2
- package/dist/types/server.d.ts +9 -5
- package/dist/utils/anthropicTraceSanitizer.d.ts +2 -2
- package/dist/utils/anthropicTraceSanitizer.js +3 -1
- package/dist/utils/conversationMemory.js +4 -4
- package/dist/utils/fileDetector.js +1 -1
- package/dist/utils/messageBuilder.js +1 -1
- package/dist/utils/noOutputSentinel.d.ts +4 -16
- package/dist/utils/noOutputSentinel.js +12 -28
- package/dist/utils/providerRetry.js +4 -4
- package/dist/voice/livekit/brain.js +1 -1
- package/dist/voice/livekit/realtimeMemory.d.ts +1 -1
- package/dist/voice/livekit/realtimeMemory.js +66 -1
- package/dist/voice/livekit/realtimeVoiceAgent.js +1 -1
- package/docs-site/static/search-index.json +42 -64
- package/package.json +5 -4
- package/dist/telemetry/telemetryService.d.ts +0 -79
- package/dist/telemetry/telemetryService.js +0 -572
- package/dist/utils/observabilityHelpers.d.ts +0 -27
- package/dist/utils/observabilityHelpers.js +0 -47
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
* Commands for monitoring and managing observability features:
|
|
6
6
|
* - status: Show telemetry status
|
|
7
7
|
* - metrics: Show metrics summary
|
|
8
|
-
* - exporters: List configured exporters
|
|
9
8
|
* - costs: Show cost breakdown
|
|
10
9
|
*/
|
|
11
10
|
import type { CommandModule } from "yargs";
|
|
12
|
-
import type { ObservabilityStatusArgs as StatusArgs, ObservabilityMetricsArgs as MetricsArgs,
|
|
11
|
+
import type { ObservabilityStatusArgs as StatusArgs, ObservabilityMetricsArgs as MetricsArgs, ObservabilityCostsArgs as CostsArgs } from "../../types/index.js";
|
|
13
12
|
/**
|
|
14
13
|
* Observability Command Factory
|
|
15
14
|
*/
|
|
@@ -26,10 +25,6 @@ export declare class ObservabilityCommandFactory {
|
|
|
26
25
|
* Create the metrics subcommand
|
|
27
26
|
*/
|
|
28
27
|
static createMetricsCommand(): CommandModule<object, MetricsArgs>;
|
|
29
|
-
/**
|
|
30
|
-
* Create the exporters subcommand
|
|
31
|
-
*/
|
|
32
|
-
static createExportersCommand(): CommandModule<object, ExportersArgs>;
|
|
33
28
|
/**
|
|
34
29
|
* Create the costs subcommand
|
|
35
30
|
*/
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* Commands for monitoring and managing observability features:
|
|
6
6
|
* - status: Show telemetry status
|
|
7
7
|
* - metrics: Show metrics summary
|
|
8
|
-
* - exporters: List configured exporters
|
|
9
8
|
* - costs: Show cost breakdown
|
|
10
9
|
*/
|
|
11
10
|
import chalk from "chalk";
|
|
@@ -30,7 +29,6 @@ export class ObservabilityCommandFactory {
|
|
|
30
29
|
return yargs
|
|
31
30
|
.command(ObservabilityCommandFactory.createStatusCommand())
|
|
32
31
|
.command(ObservabilityCommandFactory.createMetricsCommand())
|
|
33
|
-
.command(ObservabilityCommandFactory.createExportersCommand())
|
|
34
32
|
.command(ObservabilityCommandFactory.createCostsCommand())
|
|
35
33
|
.demandCommand(1, "Please specify a subcommand")
|
|
36
34
|
.strict();
|
|
@@ -118,17 +116,6 @@ export class ObservabilityCommandFactory {
|
|
|
118
116
|
logger.always(formatRow(" Service:", status.openTelemetry.serviceName));
|
|
119
117
|
}
|
|
120
118
|
}
|
|
121
|
-
// Exporters summary
|
|
122
|
-
if (status.exporters && status.exporters.length > 0) {
|
|
123
|
-
logger.always("");
|
|
124
|
-
logger.always(chalk.bold("Active Exporters:"));
|
|
125
|
-
for (const exporter of status.exporters) {
|
|
126
|
-
const exporterStatus = exporter.healthy
|
|
127
|
-
? chalk.green("Healthy")
|
|
128
|
-
: chalk.red("Unhealthy");
|
|
129
|
-
logger.always(formatRow(` ${exporter.name}:`, exporterStatus));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
119
|
logger.always("");
|
|
133
120
|
}
|
|
134
121
|
}
|
|
@@ -252,99 +239,6 @@ export class ObservabilityCommandFactory {
|
|
|
252
239
|
},
|
|
253
240
|
};
|
|
254
241
|
}
|
|
255
|
-
/**
|
|
256
|
-
* Create the exporters subcommand
|
|
257
|
-
*/
|
|
258
|
-
static createExportersCommand() {
|
|
259
|
-
return {
|
|
260
|
-
command: "exporters",
|
|
261
|
-
aliases: ["exp"],
|
|
262
|
-
describe: "List configured exporters",
|
|
263
|
-
builder: (yargs) => {
|
|
264
|
-
return yargs
|
|
265
|
-
.option("format", {
|
|
266
|
-
alias: "f",
|
|
267
|
-
type: "string",
|
|
268
|
-
choices: ["text", "json", "table"],
|
|
269
|
-
default: "text",
|
|
270
|
-
describe: "Output format",
|
|
271
|
-
})
|
|
272
|
-
.option("quiet", {
|
|
273
|
-
alias: "q",
|
|
274
|
-
type: "boolean",
|
|
275
|
-
default: false,
|
|
276
|
-
describe: "Minimal output",
|
|
277
|
-
});
|
|
278
|
-
},
|
|
279
|
-
handler: async (args) => {
|
|
280
|
-
const spinner = args.quiet
|
|
281
|
-
? null
|
|
282
|
-
: ora("Checking exporters...").start();
|
|
283
|
-
try {
|
|
284
|
-
const neurolink = new NeuroLink();
|
|
285
|
-
const status = neurolink.getTelemetryStatus();
|
|
286
|
-
if (spinner) {
|
|
287
|
-
spinner.succeed("Exporters retrieved");
|
|
288
|
-
}
|
|
289
|
-
const exporters = status.exporters ?? [];
|
|
290
|
-
if (args.format === "json") {
|
|
291
|
-
logger.always(JSON.stringify(exporters, null, 2));
|
|
292
|
-
}
|
|
293
|
-
else {
|
|
294
|
-
logger.always("");
|
|
295
|
-
logger.always(chalk.bold.cyan("=== Configured Exporters ==="));
|
|
296
|
-
logger.always("");
|
|
297
|
-
if (exporters.length === 0) {
|
|
298
|
-
logger.always(chalk.gray("No exporters configured."));
|
|
299
|
-
logger.always("");
|
|
300
|
-
logger.always("Available exporters:");
|
|
301
|
-
logger.always(" - Langfuse (langfuse)");
|
|
302
|
-
logger.always(" - LangSmith (langsmith)");
|
|
303
|
-
logger.always(" - OpenTelemetry (otel)");
|
|
304
|
-
logger.always(" - Datadog (datadog)");
|
|
305
|
-
logger.always(" - Sentry (sentry)");
|
|
306
|
-
logger.always(" - Braintrust (braintrust)");
|
|
307
|
-
logger.always(" - Arize (arize)");
|
|
308
|
-
logger.always(" - PostHog (posthog)");
|
|
309
|
-
logger.always(" - Laminar (laminar)");
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
for (const exporter of exporters) {
|
|
313
|
-
const healthIcon = exporter.healthy
|
|
314
|
-
? chalk.green("[OK]")
|
|
315
|
-
: chalk.red("[ERROR]");
|
|
316
|
-
logger.always(`${healthIcon} ${chalk.bold(exporter.name)}`);
|
|
317
|
-
if (exporter.latencyMs !== undefined) {
|
|
318
|
-
logger.always(formatRow(" Latency:", `${exporter.latencyMs.toFixed(2)}ms`));
|
|
319
|
-
}
|
|
320
|
-
if (exporter.pendingSpans !== undefined) {
|
|
321
|
-
logger.always(formatRow(" Pending spans:", exporter.pendingSpans.toLocaleString()));
|
|
322
|
-
}
|
|
323
|
-
if (exporter.lastExportTime) {
|
|
324
|
-
const lastExport = new Date(exporter.lastExportTime);
|
|
325
|
-
logger.always(formatRow(" Last export:", lastExport.toISOString()));
|
|
326
|
-
}
|
|
327
|
-
if (exporter.errors && exporter.errors.length > 0) {
|
|
328
|
-
logger.always(chalk.yellow(" Errors:"));
|
|
329
|
-
for (const error of exporter.errors.slice(0, 3)) {
|
|
330
|
-
logger.always(chalk.red(` - ${error}`));
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
logger.always("");
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
catch (error) {
|
|
339
|
-
if (spinner) {
|
|
340
|
-
spinner.fail("Failed to get exporters");
|
|
341
|
-
}
|
|
342
|
-
logger.error("Error:", error instanceof Error ? error.message : String(error));
|
|
343
|
-
process.exit(1);
|
|
344
|
-
}
|
|
345
|
-
},
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
242
|
/**
|
|
349
243
|
* Create the costs subcommand
|
|
350
244
|
*/
|
|
@@ -8,6 +8,7 @@ import { stripVTControlCharacters } from "node:util";
|
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import ora from "ora";
|
|
10
10
|
import { buildProxyHealthResponse, createProxyReadinessState, markProxyDrainingForUpdate, markProxyReady, resumeProxyConnections, waitForProxyReadiness, } from "../../proxy/proxyHealth.js";
|
|
11
|
+
import { ensureTelemetry, getLangfuseHealthStatus, } from "../../services/server/ai/observability/instrumentation.js";
|
|
11
12
|
import { logger } from "../../utils/logger.js";
|
|
12
13
|
import { applyAllClients, applyClientsOnProxyStart, restoreAllClients, } from "../proxy-clients/registry.js";
|
|
13
14
|
import { resolveProxyConfigPath } from "../../proxy/proxyConfig.js";
|
|
@@ -2350,26 +2351,16 @@ async function initializeProxyOpenTelemetry() {
|
|
|
2350
2351
|
]
|
|
2351
2352
|
.filter(Boolean)
|
|
2352
2353
|
.join(",");
|
|
2353
|
-
const
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
const langfuseConfig = observabilityConfig?.langfuse;
|
|
2360
|
-
const langfuseEnabled = langfuseConfig?.enabled === true;
|
|
2361
|
-
await initializeOpenTelemetry({
|
|
2362
|
-
enabled: langfuseEnabled,
|
|
2363
|
-
publicKey: langfuseConfig?.publicKey || "",
|
|
2364
|
-
secretKey: langfuseConfig?.secretKey || "",
|
|
2365
|
-
baseUrl: langfuseConfig?.baseUrl,
|
|
2366
|
-
environment: "proxy",
|
|
2367
|
-
release: PROXY_VERSION,
|
|
2368
|
-
userId: "neurolink-proxy",
|
|
2369
|
-
autoDetectOperationName: true,
|
|
2354
|
+
const hasLangfuseKeys = Boolean(process.env.LANGFUSE_PUBLIC_KEY && process.env.LANGFUSE_SECRET_KEY);
|
|
2355
|
+
ensureTelemetry({
|
|
2356
|
+
langfuse: hasLangfuseKeys && {
|
|
2357
|
+
environment: process.env.LANGFUSE_TRACING_ENVIRONMENT || "proxy",
|
|
2358
|
+
release: process.env.LANGFUSE_RELEASE || PROXY_VERSION,
|
|
2359
|
+
},
|
|
2370
2360
|
});
|
|
2361
|
+
const langfuseEnabled = getLangfuseHealthStatus().hasProcessor;
|
|
2371
2362
|
if (langfuseEnabled) {
|
|
2372
|
-
logger.always(`[proxy] Langfuse enabled — exporting to ${
|
|
2363
|
+
logger.always(`[proxy] Langfuse enabled — exporting to ${process.env.LANGFUSE_BASE_URL || "https://cloud.langfuse.com"}`);
|
|
2373
2364
|
}
|
|
2374
2365
|
if (otlpEndpoint) {
|
|
2375
2366
|
logger.always(`[proxy] OTLP exporter enabled — exporting to ${otlpEndpoint} (service.name=neurolink-proxy)`);
|
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* NeuroLink CLI Telemetry Commands
|
|
4
4
|
*
|
|
5
|
-
* Commands for managing telemetry
|
|
6
|
-
* - status: Show
|
|
7
|
-
* - configure: Configure an exporter
|
|
8
|
-
* - list-exporters: List configured exporters
|
|
5
|
+
* Commands for managing telemetry:
|
|
6
|
+
* - status: Show telemetry status
|
|
9
7
|
* - flush: Flush pending spans
|
|
10
8
|
* - stats: Show token/cost stats
|
|
11
9
|
*/
|
|
12
10
|
import type { CommandModule } from "yargs";
|
|
13
|
-
import type { TelemetryStatusArgs as StatusArgs,
|
|
11
|
+
import type { TelemetryStatusArgs as StatusArgs, TelemetryFlushArgs as FlushArgs, TelemetryStatsArgs as StatsArgs, TelemetryNativePrepareArgs, TelemetryNativeValidateArgs } from "../../types/index.js";
|
|
14
12
|
/**
|
|
15
13
|
* Telemetry Command Factory
|
|
16
14
|
*/
|
|
@@ -26,14 +24,6 @@ export declare class TelemetryCommandFactory {
|
|
|
26
24
|
* Create the status subcommand
|
|
27
25
|
*/
|
|
28
26
|
static createStatusCommand(): CommandModule<object, StatusArgs>;
|
|
29
|
-
/**
|
|
30
|
-
* Create the configure subcommand
|
|
31
|
-
*/
|
|
32
|
-
static createConfigureCommand(): CommandModule<object, ConfigureArgs>;
|
|
33
|
-
/**
|
|
34
|
-
* Create the list-exporters subcommand
|
|
35
|
-
*/
|
|
36
|
-
static createListExportersCommand(): CommandModule<object, ListExportersArgs>;
|
|
37
27
|
/**
|
|
38
28
|
* Create the flush subcommand
|
|
39
29
|
*/
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* NeuroLink CLI Telemetry Commands
|
|
4
4
|
*
|
|
5
|
-
* Commands for managing telemetry
|
|
6
|
-
* - status: Show
|
|
7
|
-
* - configure: Configure an exporter
|
|
8
|
-
* - list-exporters: List configured exporters
|
|
5
|
+
* Commands for managing telemetry:
|
|
6
|
+
* - status: Show telemetry status
|
|
9
7
|
* - flush: Flush pending spans
|
|
10
8
|
* - stats: Show token/cost stats
|
|
11
9
|
*/
|
|
@@ -17,20 +15,6 @@ import { flushOpenTelemetry } from "../../services/server/ai/observability/instr
|
|
|
17
15
|
import { formatRow, formatCost } from "../utils/formatters.js";
|
|
18
16
|
import { redactUrlCredentials } from "../../utils/logSanitize.js";
|
|
19
17
|
import { prepareNativeTelemetry, validateNativeTelemetry, } from "../utils/nativeTelemetry.js";
|
|
20
|
-
/**
|
|
21
|
-
* Available exporter names
|
|
22
|
-
*/
|
|
23
|
-
const AVAILABLE_EXPORTERS = [
|
|
24
|
-
"langfuse",
|
|
25
|
-
"langsmith",
|
|
26
|
-
"otel",
|
|
27
|
-
"datadog",
|
|
28
|
-
"sentry",
|
|
29
|
-
"braintrust",
|
|
30
|
-
"arize",
|
|
31
|
-
"posthog",
|
|
32
|
-
"laminar",
|
|
33
|
-
];
|
|
34
18
|
/**
|
|
35
19
|
* Telemetry Command Factory
|
|
36
20
|
*/
|
|
@@ -42,12 +26,10 @@ export class TelemetryCommandFactory {
|
|
|
42
26
|
return {
|
|
43
27
|
command: "telemetry <subcommand>",
|
|
44
28
|
aliases: ["tel"],
|
|
45
|
-
describe: "Telemetry
|
|
29
|
+
describe: "Telemetry management",
|
|
46
30
|
builder: (yargs) => {
|
|
47
31
|
return yargs
|
|
48
32
|
.command(TelemetryCommandFactory.createStatusCommand())
|
|
49
|
-
.command(TelemetryCommandFactory.createConfigureCommand())
|
|
50
|
-
.command(TelemetryCommandFactory.createListExportersCommand())
|
|
51
33
|
.command(TelemetryCommandFactory.createFlushCommand())
|
|
52
34
|
.command(TelemetryCommandFactory.createStatsCommand())
|
|
53
35
|
.command(TelemetryCommandFactory.createNativePrepareCommand())
|
|
@@ -176,7 +158,7 @@ export class TelemetryCommandFactory {
|
|
|
176
158
|
static createStatusCommand() {
|
|
177
159
|
return {
|
|
178
160
|
command: "status",
|
|
179
|
-
describe: "Show
|
|
161
|
+
describe: "Show telemetry status",
|
|
180
162
|
builder: (yargs) => {
|
|
181
163
|
return yargs
|
|
182
164
|
.option("format", {
|
|
@@ -196,7 +178,7 @@ export class TelemetryCommandFactory {
|
|
|
196
178
|
handler: async (args) => {
|
|
197
179
|
const spinner = args.quiet
|
|
198
180
|
? null
|
|
199
|
-
: ora("Checking
|
|
181
|
+
: ora("Checking telemetry status...").start();
|
|
200
182
|
try {
|
|
201
183
|
const neurolink = new NeuroLink();
|
|
202
184
|
const status = neurolink.getTelemetryStatus();
|
|
@@ -248,29 +230,6 @@ export class TelemetryCommandFactory {
|
|
|
248
230
|
logger.always(formatRow(" Environment:", status.langfuse.environment));
|
|
249
231
|
}
|
|
250
232
|
}
|
|
251
|
-
// Exporters health summary
|
|
252
|
-
if (status.exporters && status.exporters.length > 0) {
|
|
253
|
-
logger.always("");
|
|
254
|
-
logger.always(chalk.bold("Exporter Health:"));
|
|
255
|
-
for (const exporter of status.exporters) {
|
|
256
|
-
const healthIcon = exporter.healthy
|
|
257
|
-
? chalk.green("[OK]")
|
|
258
|
-
: chalk.red("[ERROR]");
|
|
259
|
-
const pendingInfo = exporter.pendingSpans
|
|
260
|
-
? chalk.gray(` (${exporter.pendingSpans} pending)`)
|
|
261
|
-
: "";
|
|
262
|
-
logger.always(` ${healthIcon} ${exporter.name}${pendingInfo}`);
|
|
263
|
-
if (exporter.errors && exporter.errors.length > 0) {
|
|
264
|
-
for (const error of exporter.errors.slice(0, 2)) {
|
|
265
|
-
logger.always(chalk.red(` Error: ${error}`));
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
logger.always("");
|
|
272
|
-
logger.always(chalk.gray("No exporters configured."));
|
|
273
|
-
}
|
|
274
233
|
logger.always("");
|
|
275
234
|
}
|
|
276
235
|
}
|
|
@@ -284,211 +243,13 @@ export class TelemetryCommandFactory {
|
|
|
284
243
|
},
|
|
285
244
|
};
|
|
286
245
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Create the configure subcommand
|
|
289
|
-
*/
|
|
290
|
-
static createConfigureCommand() {
|
|
291
|
-
return {
|
|
292
|
-
command: "configure",
|
|
293
|
-
describe: "Configure an exporter with JSON settings",
|
|
294
|
-
builder: (yargs) => {
|
|
295
|
-
return yargs
|
|
296
|
-
.option("exporter", {
|
|
297
|
-
alias: "e",
|
|
298
|
-
type: "string",
|
|
299
|
-
demandOption: true,
|
|
300
|
-
choices: AVAILABLE_EXPORTERS,
|
|
301
|
-
describe: "Exporter name to configure",
|
|
302
|
-
})
|
|
303
|
-
.option("config", {
|
|
304
|
-
alias: "c",
|
|
305
|
-
type: "string",
|
|
306
|
-
demandOption: true,
|
|
307
|
-
describe: "JSON configuration string",
|
|
308
|
-
})
|
|
309
|
-
.option("format", {
|
|
310
|
-
alias: "f",
|
|
311
|
-
type: "string",
|
|
312
|
-
choices: ["text", "json", "table"],
|
|
313
|
-
default: "text",
|
|
314
|
-
describe: "Output format",
|
|
315
|
-
})
|
|
316
|
-
.option("quiet", {
|
|
317
|
-
alias: "q",
|
|
318
|
-
type: "boolean",
|
|
319
|
-
default: false,
|
|
320
|
-
describe: "Minimal output",
|
|
321
|
-
});
|
|
322
|
-
},
|
|
323
|
-
handler: async (args) => {
|
|
324
|
-
const spinner = args.quiet
|
|
325
|
-
? null
|
|
326
|
-
: ora(`Configuring ${args.exporter} exporter...`).start();
|
|
327
|
-
try {
|
|
328
|
-
// Parse the JSON config
|
|
329
|
-
let config;
|
|
330
|
-
try {
|
|
331
|
-
config = JSON.parse(args.config);
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
if (spinner) {
|
|
335
|
-
spinner.fail("Invalid JSON configuration");
|
|
336
|
-
}
|
|
337
|
-
logger.always(chalk.red("Error: Configuration must be valid JSON"));
|
|
338
|
-
logger.always("");
|
|
339
|
-
logger.always("Example:");
|
|
340
|
-
logger.always(chalk.gray(` neurolink telemetry configure --exporter langfuse --config '{"publicKey":"pk-...", "secretKey":"sk-..."}'`));
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
343
|
-
// Validate required fields based on exporter type
|
|
344
|
-
const validationResult = validateExporterConfig(args.exporter, config);
|
|
345
|
-
if (!validationResult.valid) {
|
|
346
|
-
if (spinner) {
|
|
347
|
-
spinner.fail("Configuration validation failed");
|
|
348
|
-
}
|
|
349
|
-
logger.always(chalk.red(`Error: ${validationResult.error}`));
|
|
350
|
-
logger.always("");
|
|
351
|
-
logger.always(chalk.yellow(`Required fields for ${args.exporter}:`));
|
|
352
|
-
for (const field of validationResult.requiredFields ?? []) {
|
|
353
|
-
logger.always(chalk.gray(` - ${field}`));
|
|
354
|
-
}
|
|
355
|
-
process.exit(1);
|
|
356
|
-
}
|
|
357
|
-
// Currently, exporter configuration is done via environment variables
|
|
358
|
-
// or SDK initialization. This command provides guidance on how to configure.
|
|
359
|
-
if (spinner) {
|
|
360
|
-
spinner.succeed(`${args.exporter} configuration validated`);
|
|
361
|
-
}
|
|
362
|
-
if (args.format === "json") {
|
|
363
|
-
logger.always(JSON.stringify({
|
|
364
|
-
exporter: args.exporter,
|
|
365
|
-
config: config,
|
|
366
|
-
valid: true,
|
|
367
|
-
message: "Configuration validated. Set environment variables or use SDK initialization.",
|
|
368
|
-
}, null, 2));
|
|
369
|
-
}
|
|
370
|
-
else {
|
|
371
|
-
logger.always("");
|
|
372
|
-
logger.always(chalk.bold.cyan(`=== ${args.exporter} Configuration ===`));
|
|
373
|
-
logger.always("");
|
|
374
|
-
logger.always(chalk.green("Configuration validated successfully!"));
|
|
375
|
-
logger.always("");
|
|
376
|
-
logger.always(chalk.bold("To apply this configuration:"));
|
|
377
|
-
logger.always("");
|
|
378
|
-
// Show environment variable instructions
|
|
379
|
-
const envVars = getExporterEnvVars(args.exporter);
|
|
380
|
-
logger.always(chalk.yellow("Option 1: Set environment variables"));
|
|
381
|
-
for (const [key, description] of Object.entries(envVars)) {
|
|
382
|
-
logger.always(chalk.gray(` export ${key}="<${description}>"`));
|
|
383
|
-
}
|
|
384
|
-
logger.always("");
|
|
385
|
-
logger.always(chalk.yellow("Option 2: SDK initialization"));
|
|
386
|
-
logger.always(chalk.gray(` const neurolink = new NeuroLink({`));
|
|
387
|
-
logger.always(chalk.gray(` observability: {`));
|
|
388
|
-
logger.always(chalk.gray(` ${args.exporter}: ${JSON.stringify(config, null, 6).split("\n").join("\n ")}`));
|
|
389
|
-
logger.always(chalk.gray(` }`));
|
|
390
|
-
logger.always(chalk.gray(` });`));
|
|
391
|
-
logger.always("");
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
catch (error) {
|
|
395
|
-
if (spinner) {
|
|
396
|
-
spinner.fail("Failed to configure exporter");
|
|
397
|
-
}
|
|
398
|
-
logger.error("Error:", error instanceof Error ? error.message : String(error));
|
|
399
|
-
process.exit(1);
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Create the list-exporters subcommand
|
|
406
|
-
*/
|
|
407
|
-
static createListExportersCommand() {
|
|
408
|
-
return {
|
|
409
|
-
command: "list-exporters",
|
|
410
|
-
aliases: ["list", "ls"],
|
|
411
|
-
describe: "List all available and configured exporters",
|
|
412
|
-
builder: (yargs) => {
|
|
413
|
-
return yargs
|
|
414
|
-
.option("format", {
|
|
415
|
-
alias: "f",
|
|
416
|
-
type: "string",
|
|
417
|
-
choices: ["text", "json", "table"],
|
|
418
|
-
default: "text",
|
|
419
|
-
describe: "Output format",
|
|
420
|
-
})
|
|
421
|
-
.option("quiet", {
|
|
422
|
-
alias: "q",
|
|
423
|
-
type: "boolean",
|
|
424
|
-
default: false,
|
|
425
|
-
describe: "Minimal output",
|
|
426
|
-
});
|
|
427
|
-
},
|
|
428
|
-
handler: async (args) => {
|
|
429
|
-
const spinner = args.quiet ? null : ora("Listing exporters...").start();
|
|
430
|
-
try {
|
|
431
|
-
const neurolink = new NeuroLink();
|
|
432
|
-
const status = neurolink.getTelemetryStatus();
|
|
433
|
-
if (spinner) {
|
|
434
|
-
spinner.succeed("Exporters listed");
|
|
435
|
-
}
|
|
436
|
-
const configuredExporters = status.exporters ?? [];
|
|
437
|
-
const configuredNames = new Set(configuredExporters.map((e) => e.name.toLowerCase()));
|
|
438
|
-
if (args.format === "json") {
|
|
439
|
-
logger.always(JSON.stringify({
|
|
440
|
-
available: AVAILABLE_EXPORTERS,
|
|
441
|
-
configured: configuredExporters,
|
|
442
|
-
}, null, 2));
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
logger.always("");
|
|
446
|
-
logger.always(chalk.bold.cyan("=== Available Exporters ==="));
|
|
447
|
-
logger.always("");
|
|
448
|
-
for (const exporter of AVAILABLE_EXPORTERS) {
|
|
449
|
-
const isConfigured = configuredNames.has(exporter);
|
|
450
|
-
const configuredExporter = configuredExporters.find((e) => e.name.toLowerCase() === exporter);
|
|
451
|
-
const statusIcon = isConfigured
|
|
452
|
-
? configuredExporter?.healthy
|
|
453
|
-
? chalk.green("[ACTIVE]")
|
|
454
|
-
: chalk.yellow("[CONFIGURED]")
|
|
455
|
-
: chalk.gray("[AVAILABLE]");
|
|
456
|
-
const description = getExporterDescription(exporter);
|
|
457
|
-
logger.always(`${statusIcon} ${chalk.bold(exporter)}`);
|
|
458
|
-
logger.always(chalk.gray(` ${description}`));
|
|
459
|
-
if (isConfigured && configuredExporter) {
|
|
460
|
-
if (configuredExporter.pendingSpans) {
|
|
461
|
-
logger.always(chalk.gray(` Pending spans: ${configuredExporter.pendingSpans}`));
|
|
462
|
-
}
|
|
463
|
-
if (configuredExporter.lastExportTime) {
|
|
464
|
-
const lastExport = new Date(configuredExporter.lastExportTime);
|
|
465
|
-
logger.always(chalk.gray(` Last export: ${lastExport.toISOString()}`));
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
logger.always("");
|
|
469
|
-
}
|
|
470
|
-
logger.always(chalk.bold("Configuration Help:"));
|
|
471
|
-
logger.always(chalk.gray(" Use 'neurolink telemetry configure --exporter <name> --config <json>' to configure an exporter"));
|
|
472
|
-
logger.always("");
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
catch (error) {
|
|
476
|
-
if (spinner) {
|
|
477
|
-
spinner.fail("Failed to list exporters");
|
|
478
|
-
}
|
|
479
|
-
logger.error("Error:", error instanceof Error ? error.message : String(error));
|
|
480
|
-
process.exit(1);
|
|
481
|
-
}
|
|
482
|
-
},
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
246
|
/**
|
|
486
247
|
* Create the flush subcommand
|
|
487
248
|
*/
|
|
488
249
|
static createFlushCommand() {
|
|
489
250
|
return {
|
|
490
251
|
command: "flush",
|
|
491
|
-
describe: "Flush all pending spans
|
|
252
|
+
describe: "Flush all pending spans",
|
|
492
253
|
builder: (yargs) => {
|
|
493
254
|
return yargs
|
|
494
255
|
.option("timeout", {
|
|
@@ -516,10 +277,6 @@ export class TelemetryCommandFactory {
|
|
|
516
277
|
? null
|
|
517
278
|
: ora("Flushing pending spans...").start();
|
|
518
279
|
try {
|
|
519
|
-
const neurolink = new NeuroLink();
|
|
520
|
-
const statusBefore = neurolink.getTelemetryStatus();
|
|
521
|
-
// Count pending spans before flush
|
|
522
|
-
const pendingBefore = statusBefore.exporters?.reduce((sum, e) => sum + (e.pendingSpans ?? 0), 0) ?? 0;
|
|
523
280
|
// Create a timeout promise
|
|
524
281
|
let flushTimer;
|
|
525
282
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -533,28 +290,16 @@ export class TelemetryCommandFactory {
|
|
|
533
290
|
if (flushTimer !== undefined) {
|
|
534
291
|
clearTimeout(flushTimer);
|
|
535
292
|
}
|
|
536
|
-
// Get status after flush
|
|
537
|
-
const statusAfter = neurolink.getTelemetryStatus();
|
|
538
|
-
const pendingAfter = statusAfter.exporters?.reduce((sum, e) => sum + (e.pendingSpans ?? 0), 0) ?? 0;
|
|
539
|
-
const flushedCount = Math.max(0, pendingBefore - pendingAfter);
|
|
540
293
|
if (spinner) {
|
|
541
294
|
spinner.succeed("Flush completed");
|
|
542
295
|
}
|
|
543
296
|
if (args.format === "json") {
|
|
544
|
-
logger.always(JSON.stringify({
|
|
545
|
-
success: true,
|
|
546
|
-
pendingBefore,
|
|
547
|
-
pendingAfter,
|
|
548
|
-
flushed: flushedCount,
|
|
549
|
-
}, null, 2));
|
|
297
|
+
logger.always(JSON.stringify({ success: true }, null, 2));
|
|
550
298
|
}
|
|
551
299
|
else {
|
|
552
300
|
logger.always("");
|
|
553
301
|
logger.always(chalk.bold.cyan("=== Flush Complete ==="));
|
|
554
302
|
logger.always("");
|
|
555
|
-
logger.always(formatRow("Spans before:", pendingBefore.toString()));
|
|
556
|
-
logger.always(formatRow("Spans after:", pendingAfter.toString()));
|
|
557
|
-
logger.always(formatRow("Flushed:", chalk.green(flushedCount.toString())));
|
|
558
303
|
logger.always("");
|
|
559
304
|
}
|
|
560
305
|
}
|
|
@@ -727,96 +472,4 @@ export class TelemetryCommandFactory {
|
|
|
727
472
|
};
|
|
728
473
|
}
|
|
729
474
|
}
|
|
730
|
-
/**
|
|
731
|
-
* Validate exporter configuration
|
|
732
|
-
*/
|
|
733
|
-
function validateExporterConfig(exporter, config) {
|
|
734
|
-
const requiredFieldsMap = {
|
|
735
|
-
langfuse: ["publicKey", "secretKey"],
|
|
736
|
-
langsmith: ["apiKey"],
|
|
737
|
-
otel: ["endpoint"],
|
|
738
|
-
datadog: ["apiKey"],
|
|
739
|
-
sentry: ["dsn"],
|
|
740
|
-
braintrust: ["apiKey", "projectName"],
|
|
741
|
-
arize: ["spaceKey", "apiKey"],
|
|
742
|
-
posthog: ["apiKey"],
|
|
743
|
-
laminar: ["apiKey"],
|
|
744
|
-
};
|
|
745
|
-
const requiredFields = requiredFieldsMap[exporter];
|
|
746
|
-
const missingFields = requiredFields.filter((field) => !(field in config) || !config[field]);
|
|
747
|
-
if (missingFields.length > 0) {
|
|
748
|
-
return {
|
|
749
|
-
valid: false,
|
|
750
|
-
error: `Missing required fields: ${missingFields.join(", ")}`,
|
|
751
|
-
requiredFields,
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
return { valid: true };
|
|
755
|
-
}
|
|
756
|
-
/**
|
|
757
|
-
* Get environment variable names for an exporter
|
|
758
|
-
*/
|
|
759
|
-
function getExporterEnvVars(exporter) {
|
|
760
|
-
const envVarsMap = {
|
|
761
|
-
langfuse: {
|
|
762
|
-
LANGFUSE_PUBLIC_KEY: "your-public-key",
|
|
763
|
-
LANGFUSE_SECRET_KEY: "your-secret-key",
|
|
764
|
-
LANGFUSE_BASEURL: "https://cloud.langfuse.com (optional)",
|
|
765
|
-
},
|
|
766
|
-
langsmith: {
|
|
767
|
-
LANGCHAIN_API_KEY: "your-api-key",
|
|
768
|
-
LANGCHAIN_PROJECT: "your-project-name (optional)",
|
|
769
|
-
LANGCHAIN_ENDPOINT: "https://api.smith.langchain.com (optional)",
|
|
770
|
-
},
|
|
771
|
-
otel: {
|
|
772
|
-
OTEL_EXPORTER_OTLP_ENDPOINT: "http://localhost:4318",
|
|
773
|
-
OTEL_SERVICE_NAME: "your-service-name",
|
|
774
|
-
OTEL_EXPORTER_OTLP_PROTOCOL: "http (or grpc)",
|
|
775
|
-
},
|
|
776
|
-
datadog: {
|
|
777
|
-
DD_API_KEY: "your-api-key",
|
|
778
|
-
DD_SITE: "datadoghq.com (or regional site)",
|
|
779
|
-
DD_SERVICE: "your-service-name",
|
|
780
|
-
},
|
|
781
|
-
sentry: {
|
|
782
|
-
SENTRY_DSN: "your-dsn-url",
|
|
783
|
-
SENTRY_TRACES_SAMPLE_RATE: "1.0 (optional)",
|
|
784
|
-
SENTRY_RELEASE: "your-release-version (optional)",
|
|
785
|
-
},
|
|
786
|
-
braintrust: {
|
|
787
|
-
BRAINTRUST_API_KEY: "your-api-key",
|
|
788
|
-
BRAINTRUST_PROJECT: "your-project-name",
|
|
789
|
-
},
|
|
790
|
-
arize: {
|
|
791
|
-
ARIZE_SPACE_KEY: "your-space-key",
|
|
792
|
-
ARIZE_API_KEY: "your-api-key",
|
|
793
|
-
},
|
|
794
|
-
posthog: {
|
|
795
|
-
POSTHOG_API_KEY: "your-api-key",
|
|
796
|
-
POSTHOG_HOST: "https://app.posthog.com (optional)",
|
|
797
|
-
},
|
|
798
|
-
laminar: {
|
|
799
|
-
LAMINAR_API_KEY: "your-api-key",
|
|
800
|
-
LAMINAR_BASE_URL: "https://api.laminar.run (optional)",
|
|
801
|
-
},
|
|
802
|
-
};
|
|
803
|
-
return envVarsMap[exporter];
|
|
804
|
-
}
|
|
805
|
-
/**
|
|
806
|
-
* Get description for an exporter
|
|
807
|
-
*/
|
|
808
|
-
function getExporterDescription(exporter) {
|
|
809
|
-
const descriptions = {
|
|
810
|
-
langfuse: "Open-source LLM observability platform with traces and analytics",
|
|
811
|
-
langsmith: "LangChain's platform for LLM application debugging and monitoring",
|
|
812
|
-
otel: "OpenTelemetry Protocol (OTLP) for distributed tracing",
|
|
813
|
-
datadog: "APM and infrastructure monitoring platform",
|
|
814
|
-
sentry: "Error tracking and performance monitoring",
|
|
815
|
-
braintrust: "AI evaluation and experimentation platform",
|
|
816
|
-
arize: "ML observability platform for model monitoring",
|
|
817
|
-
posthog: "Product analytics with LLM event tracking",
|
|
818
|
-
laminar: "LLM application monitoring and debugging",
|
|
819
|
-
};
|
|
820
|
-
return descriptions[exporter];
|
|
821
|
-
}
|
|
822
475
|
//# sourceMappingURL=telemetry.js.map
|