@juspay/neurolink 13.5.0 โ†’ 13.5.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.
Files changed (118) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/dist/adapters/video/videoAnalyzer.d.ts +1 -0
  3. package/dist/adapters/video/videoAnalyzer.js +4 -0
  4. package/dist/autoresearch/index.d.ts +1 -0
  5. package/dist/autoresearch/index.js +4 -0
  6. package/dist/autoresearch/worker.js +4 -0
  7. package/dist/browser/neurolink.min.js +467 -467
  8. package/dist/cli/commands/observability.js +11 -4
  9. package/dist/cli/commands/telemetry.js +36 -6
  10. package/dist/cli/utils/formatters.d.ts +17 -0
  11. package/dist/cli/utils/formatters.js +39 -0
  12. package/dist/constants/langfuse.d.ts +17 -1
  13. package/dist/constants/langfuse.js +23 -1
  14. package/dist/constants/telemetry.d.ts +6 -0
  15. package/dist/constants/telemetry.js +6 -0
  16. package/dist/core/baseProvider.js +2 -0
  17. package/dist/core/geminiLoopAdapter.js +19 -0
  18. package/dist/core/loopEngine.js +10 -7
  19. package/dist/core/modules/StreamHandler.js +5 -4
  20. package/dist/core/modules/TelemetryHandler.d.ts +5 -1
  21. package/dist/core/modules/TelemetryHandler.js +16 -0
  22. package/dist/core/modules/ToolsManager.js +3 -0
  23. package/dist/core/nativeGenerateLoop.js +1 -1
  24. package/dist/evaluation/index.d.ts +1 -0
  25. package/dist/evaluation/index.js +4 -0
  26. package/dist/evaluation/ragasEvaluator.js +1 -0
  27. package/dist/mcp/externalServerManager.js +6 -2
  28. package/dist/mcp/toolRegistry.js +3 -0
  29. package/dist/neurolink.d.ts +16 -8
  30. package/dist/neurolink.js +115 -61
  31. package/dist/observability/FEATURE-STATUS.md +0 -4
  32. package/dist/observability/exporterRegistry.d.ts +6 -1
  33. package/dist/observability/exporterRegistry.js +18 -0
  34. package/dist/observability/exporterSpanProcessor.d.ts +20 -6
  35. package/dist/observability/exporterSpanProcessor.js +166 -34
  36. package/dist/observability/exporters/baseExporter.d.ts +22 -3
  37. package/dist/observability/exporters/baseExporter.js +87 -5
  38. package/dist/observability/exporters/langfuseExporter.d.ts +29 -3
  39. package/dist/observability/exporters/langfuseExporter.js +251 -41
  40. package/dist/observability/langfuseMasking.d.ts +1 -1
  41. package/dist/observability/langfuseMasking.js +1 -1
  42. package/dist/observability/otlpEncoding.d.ts +18 -5
  43. package/dist/observability/otlpEncoding.js +69 -12
  44. package/dist/observability/utils/spanSerializer.d.ts +3 -2
  45. package/dist/observability/utils/spanSerializer.js +5 -3
  46. package/dist/processors/base/BaseFileProcessor.d.ts +1 -0
  47. package/dist/processors/base/BaseFileProcessor.js +4 -0
  48. package/dist/processors/index.d.ts +1 -0
  49. package/dist/processors/index.js +4 -0
  50. package/dist/processors/registry/ProcessorRegistry.d.ts +1 -0
  51. package/dist/processors/registry/ProcessorRegistry.js +5 -0
  52. package/dist/providers/amazonBedrock/client.js +7 -5
  53. package/dist/providers/amazonSagemaker.js +32 -12
  54. package/dist/providers/anthropic/client.js +1 -0
  55. package/dist/providers/anthropic/loopAdapter.js +7 -3
  56. package/dist/providers/googleAiStudio/client.js +2 -0
  57. package/dist/providers/googleVertex/client.d.ts +6 -5
  58. package/dist/providers/googleVertex/client.js +46 -19
  59. package/dist/providers/openaiChatCompletionsBase.js +4 -4
  60. package/dist/proxy/codexUsage.js +59 -2
  61. package/dist/proxy/proxyTracer.d.ts +25 -1
  62. package/dist/proxy/proxyTracer.js +146 -22
  63. package/dist/proxy/proxyTranslationEngine.js +4 -2
  64. package/dist/proxy/sseInterceptor.js +6 -4
  65. package/dist/rag/chunkers/BaseChunker.js +1 -0
  66. package/dist/rag/index.d.ts +1 -0
  67. package/dist/rag/index.js +4 -0
  68. package/dist/rag/ragIntegration.js +1 -0
  69. package/dist/rag/reranker/reranker.js +2 -0
  70. package/dist/rag/retrieval/vectorQueryTool.js +1 -0
  71. package/dist/server/routes/codexProxyRoutes.js +3 -1
  72. package/dist/server/utils/validation.d.ts +3 -3
  73. package/dist/services/server/ai/observability/instrumentation.d.ts +8 -7
  74. package/dist/services/server/ai/observability/instrumentation.js +217 -47
  75. package/dist/services/server/ai/observability/langfuseSetup.d.ts +19 -1
  76. package/dist/services/server/ai/observability/langfuseSetup.js +62 -4
  77. package/dist/services/server/ai/observability/otlpSetup.d.ts +12 -2
  78. package/dist/services/server/ai/observability/otlpSetup.js +55 -4
  79. package/dist/telemetry/aiMetrics.d.ts +56 -0
  80. package/dist/telemetry/aiMetrics.js +229 -0
  81. package/dist/telemetry/attributes.d.ts +24 -1
  82. package/dist/telemetry/attributes.js +94 -9
  83. package/dist/telemetry/chatMessages.d.ts +6 -0
  84. package/dist/telemetry/chatMessages.js +37 -0
  85. package/dist/telemetry/index.d.ts +1 -1
  86. package/dist/telemetry/index.js +1 -1
  87. package/dist/telemetry/observation.d.ts +5 -5
  88. package/dist/telemetry/observation.js +128 -39
  89. package/dist/telemetry/spanSanitizer.d.ts +2 -2
  90. package/dist/telemetry/spanSanitizer.js +76 -8
  91. package/dist/telemetry/telemetryStarter.d.ts +16 -0
  92. package/dist/telemetry/telemetryStarter.js +28 -0
  93. package/dist/telemetry/traceContext.d.ts +3 -3
  94. package/dist/telemetry/traceContext.js +3 -3
  95. package/dist/telemetry/tracers.d.ts +13 -1
  96. package/dist/telemetry/tracers.js +49 -10
  97. package/dist/telemetry/withSpan.d.ts +19 -3
  98. package/dist/telemetry/withSpan.js +149 -20
  99. package/dist/types/exporter.d.ts +98 -3
  100. package/dist/types/livekit.d.ts +16 -1
  101. package/dist/types/loopEngine.d.ts +16 -1
  102. package/dist/types/observability.d.ts +38 -5
  103. package/dist/types/proxy.d.ts +2 -0
  104. package/dist/types/span.d.ts +26 -0
  105. package/dist/utils/timeout.js +4 -2
  106. package/dist/voice/livekit/realtimeMcpTools.js +11 -2
  107. package/dist/voice/livekit/realtimeMemory.d.ts +20 -2
  108. package/dist/voice/livekit/realtimeMemory.js +139 -46
  109. package/dist/voice/livekit/realtimeVoiceAgent.js +20 -8
  110. package/dist/voice/livekit/turnRecorder.js +22 -9
  111. package/dist/workflow/core/ensembleExecutor.js +2 -0
  112. package/dist/workflow/core/judgeScorer.js +1 -0
  113. package/dist/workflow/core/responseConditioner.js +1 -0
  114. package/dist/workflow/core/workflowRunner.js +6 -1
  115. package/dist/workflow/index.d.ts +1 -0
  116. package/dist/workflow/index.js +4 -0
  117. package/docs-site/static/search-index.json +32 -27
  118. package/package.json +21 -2
@@ -2375,7 +2375,7 @@
2375
2375
  {"objectID":"4f094fd61e2348ac879d6bed5a47682726fba98bf348ab466f147eac476d4b7b","title":"Telemetry Cross-Check","url":"/docs/features/claude-proxy-observability#telemetry-cross-check","content":"Use this tab to validate the OTEL export path itself.\nThese panels are shown as per-window OTEL deltas, not raw cumulative counter values.\nMetric Requests in Range, Metric Errors in Range, and Metric Retries in Range should broadly agree with the earlier log-derived charts.\nIf Metric Request Trend (5m) is flat while Request Trend (5m) is moving, the metrics pipeline is broken or delayed.\nIf costs or request body volume stop moving here while logs keep arriving, OTEL metrics are unhealthy even if log export still works.","hierarchy":{"lvl0":"Features","lvl1":"Claude Proxy Observability","lvl2":"Telemetry Cross-Check","lvl3":""}},
2376
2376
  {"objectID":"c075b88266c39bebcadf533966f852a8499b58933fa0750e24a42a418d58ef3c","title":"Tokens, Cache & Cost","url":"/docs/features/claude-proxy-observability#tokens-cache-cost","content":"Use this tab to understand workload mix and cache behavior.\nPrompt Tokens (M) is prompt-side volume in millions: uncached input plus cache writes plus cache reads.\nCached Prompt Tokens Reused (M) is actual cache reuse. These tokens were read from an existing prompt cache entry.\nCached Prompt Tokens Written (M) is cache population. These tokens were written into a new cache entry on that request and can be reused by later requests.\nCache Reuse Ratio is reused cache tokens divided by newly written cache tokens. Values above 1 mean reuse is outpacing cache writes.\nMean Total Tokens per Request is average prompt-side plus output token volume per request, shown as raw tokens.\nInput vs Output Tokens per Request (5m) compares average input and output tokens per request as raw tokens, which is easier to read than total prompt-side volume when cache reuse is large.\nCache Reuse vs Cache Write Trend (5m, M) keeps cache movement on its own scale so cache traffic does not flatten the input/output chart.\nToken Volume by Model (M) tells you which model families are driving token volume.\nTop Sessions by Token Volume (M) helps identify unusually heavy sessions for trace drilldown.\nInput vs Output Tokens by Account / Route shows raw token totals by real account or fallback route, with internal final rows excluded.","hierarchy":{"lvl0":"Features","lvl1":"Claude Proxy Observability","lvl2":"Tokens, Cache & Cost","lvl3":""}},
2377
2377
  {"objectID":"5c0cf2922947c702cf935fb47c4d37e12d31ac9346dd702fd5589d31db75b7ad","title":"Trace Drilldown","url":"/docs/features/claude-proxy-observability#trace-drilldown","content":"Use this tab after you know there is a problem and need request-level evidence.\nSlowest Operations by Mean Duration is the best starting point for deep latency debugging.\nSpan Status Mix tells you whether failures are surfacing in traces as well as logs.\nSpan Volume by Operation and Trace Volume Trend help confirm whether the trace pipeline matches traffic volume.","hierarchy":{"lvl0":"Features","lvl1":"Claude Proxy Observability","lvl2":"Trace Drilldown","lvl3":""}},
2378
- {"objectID":"45db8d2cd8c2aa49d45464157eaab809fbc367f283acd0888a21e8a79d7f526f","title":"Key Correlation Fields","url":"/docs/features/claude-proxy-observability#key-correlation-fields","content":"These fields matter most when moving between logs, metrics, and traces:\n_timestamp: event time in OpenObserve\nrequest_id: request-level correlation key in proxy logs\ntrace_id: cross-signal trace correlation key\nspan_id: specific span correlation key\nevent.name: distinguishes request summaries from proxy.body_capture debug events in the shared OpenObserve log stream\naccount_name: which account handled the request\nai_model: which model served the request\nai_input_tokens: prompt/input tokens\nai_output_tokens: completion/output tokens\nai_cache_creation_tokens: tokens spent creating cache entries\nai_cache_read_tokens: tokens served from cache\n\nWhen a caller injects traceparent plus x-neurolink-session-id / x-neurolink-user-id / x-neurolink-conversation-id, the proxy attaches its spans to that upstream trace and preserves session-level attribution across SDK and proxy telemetry.\n\nai_cache_creation_tokens means prompt tokens written into a new cache entry.\nai_cache_read_tokens means prompt tokens reused from an existing cache entry.\nAll latency and duration panels are shown in whole seconds for faster scanning.\nCounts and token-heavy charts default to whole numbers when practical, while ratios, costs, and million/MB rollups are capped at two decimals.","hierarchy":{"lvl0":"Features","lvl1":"Claude Proxy Observability","lvl2":"Key Correlation Fields","lvl3":""}},
2378
+ {"objectID":"45db8d2cd8c2aa49d45464157eaab809fbc367f283acd0888a21e8a79d7f526f","title":"Key Correlation Fields","url":"/docs/features/claude-proxy-observability#key-correlation-fields","content":"These fields matter most when moving between logs, metrics, and traces:\n_timestamp: event time in OpenObserve\nrequest_id: request-level correlation key in proxy logs\ntrace_id: cross-signal trace correlation key\nspan_id: specific span correlation key\nevent.name: distinguishes request summaries from proxy.body_capture debug events in the shared OpenObserve log stream\naccount_name: which account handled the request\nai_model: which model served the request\nai_input_tokens: prompt/input tokens\nai_output_tokens: completion/output tokens\nai_cache_creation_tokens: tokens spent creating cache entries\nai_cache_read_tokens: tokens served from cache\n\nWhen a caller injects traceparent plus x-neurolink-session-id / x-neurolink-user-id / x-neurolink-conversation-id, the proxy attaches its spans to that upstream trace and preserves session-level attribution across SDK and proxy telemetry.\n\nRequest and response bodies are recorded on traces only when NEUROLINK_PROXY_TRACE_BODY_LOGGING=true, the same opt-in as before the Langfuse migration. The prompt and answer of a translated request follow the same opt-in. Upstream error text is recorded without it, as before the migration, with credentials redacted and the text capped, and header values are recorded as received, except those of credential headers, which read [REDACTED]. See Observability: Proxy for how mask, redactIO and NEUROLINK_RECORD_INPUTS apply to them.\n\nai_cache_creation_tokens means prompt tokens written into a new cache entry.\nai_cache_read_tokens means prompt tokens reused from an existing cache entry.\nAll latency and duration panels are shown in whole seconds for faster scanning.\nCounts and token-heavy charts default to whole numbers when practical, while ratios, costs, and million/MB rollups are capped at two decimals.","hierarchy":{"lvl0":"Features","lvl1":"Claude Proxy Observability","lvl2":"Key Correlation Fields","lvl3":""}},
2379
2379
  {"objectID":"8bb1c369b8726670a83b3b3a3153802808193a7695b7821cce795125774426ac","title":"Common Interpretation Patterns","url":"/docs/features/claude-proxy-observability#common-interpretation-patterns","content":"Rising Failed Request Share with flat traffic usually means a real reliability regression, not just more volume.\nRising 429 Rate-Limit Responses with high load on one account usually means the pool is exhausting the primary account as designed.\nLog traffic moving while the telemetry tab is flat means the OTEL metrics path needs attention.\nRising Cache Creation Tokens without matching Cache Read Tokens means prompt reuse is weak or the cache is still warming.\nA slow chart on the latency tab plus the same operation on the trace tab gives you the fastest path to a concrete trace investigation.","hierarchy":{"lvl0":"Features","lvl1":"Claude Proxy Observability","lvl2":"Common Interpretation Patterns","lvl3":""}},
2380
2380
  {"objectID":"b0d3dc20fd803ddbb9b70e250c08dcb61c0625f053b4d2b37d514d6c04811ad0","title":"Request telemetry and evidence quality","url":"/docs/features/claude-proxy-observability#request-telemetry-and-evidence-quality","content":"Use neurolink proxy analyze --since 1h --format json to reconcile retained\nrequest, attempt, lifecycle, and capture-index records. Read dataQuality before\ninterpreting success rates or latency. The analyzer does not require captured\nprompt or response bodies.\n\nA client request has one generated requestId. Internal Codex fallback attempts\nretain their own ID plus parentRequestId, model, account, and reasoningEffort.\nThe final Claude record retains the configured fallbackPlan; attempt records\nshow which entries were actually tried. This evidence survives body retention.","hierarchy":{"lvl0":"Features","lvl1":"Claude Proxy Observability","lvl2":"Request telemetry and evidence quality","lvl3":""}},
2381
2381
  {"objectID":"f8656d5ea4697344b223c17e9e251bb30391df25ba7f7abc5ea6f0c98a5ad33d","title":"Completion and timing","url":"/docs/features/claude-proxy-observability#completion-and-timing","content":"| Field | What it establishes |\n| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |\n| responseStatus on a lifecycle terminal | HTTP status committed by the adapter; a stream can still fail after HTTP 200 |\n| finalStatus, terminalOutcome, outcomeSource | Route outcome, joined before terminal publication; unknown means final evidence was unavailable |\n| transportOutcome | EOF, bodyless response, read error, or cancellation observed by the response adapter |\n| telemetryStatus | Whether terminal bookkeeping completed, timed out, failed, or lacked a final record; separate from the provider error |\n| latencyMs.firstChunk | First body chunk, including SSE control events |\n| latencyMs.firstUsefulOutput | First nonempty text or tool-argument delta parsed at the proxy; excludes thinking and control events |\n| latencyMs.terminal | Adapter terminal time, captured before waiting for bookkeeping or log writes |\n\nAnthropic streams require message_stop; native Codex streams require\nresponse.completed. In-band error events, incomplete responses, and EOF without\nthe expected completion event are failures even if HTTP 200 was already sent.\nAn unterminated SSE event is not dispatched completion evidence.","hierarchy":{"lvl0":"Features","lvl1":"Claude Proxy Observability","lvl2":"Completion and timing","lvl3":""}},
@@ -3492,29 +3492,34 @@
3492
3492
  {"objectID":"d6ae12d83a1d4ac329230b324df41139e12e69cc6b9716c9a488474f68a92e56","title":"Example 2: Document Comparison","url":"/docs/features/multimodal#example-2-document-comparison","content":"Compare two versions of a contract:","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Example 2: Document Comparison","lvl3":""}},
3493
3493
  {"objectID":"4efaf71953966b0390225a95bede8ba98ba13fc231e459aa3caff383bfe0d214","title":"Example 3: Data Visualization Analysis","url":"/docs/features/multimodal#example-3-data-visualization-analysis","content":"Analyze charts and underlying data together:","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Example 3: Data Visualization Analysis","lvl3":""}},
3494
3494
  {"objectID":"dc35015c48cf20bf4abe2a50399f0f6c0a87119e86f614f784bd9233318b779f","title":"Summary","url":"/docs/features/multimodal#summary","content":"NeuroLink's multimodal capabilities provide:\n\nโœ… Universal input support - Images, PDFs, CSV files\nโœ… Provider flexibility - Extensive provider compatibility matrix\nโœ… Automatic format detection - Smart file type recognition\nโœ… Accessibility features - Alt text support for images\nโœ… Production-ready - Battle-tested at enterprise scale\nโœ… Developer-friendly - Works seamlessly across CLI and SDK\n\nNext Steps:\nReview the provider support matrix to select the right provider\nTry the quick start examples with your use case\nExplore advanced recipes for complex scenarios\nCheck troubleshooting if you encounter issues","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Summary","lvl3":""}},
3495
- {"objectID":"c56b41cb0c1e8e9dfc542fc3c18cf1024985a68cd588c7616a5facff77a7613a","title":"Observability Guide","url":"/docs/features/observability","content":"Observability Guide\n\nNeuroLink exports every AI operation to Langfuse. Setup is environment variables only.\n\nArchitecture\n\nOne instrumentation feeds every backend:\n\nNeuroLink's own provider uses this pipeline, with LangfuseExporter registered when Langfuse is enabled. The Langfuse exporter queues spans without blocking the caller and posts them in batches to Langfuse's OTLP endpoint (POST /api/public/otel/v1/traces, OTLP/HTTP JSON, Basic auth with your keys). Spans that are not observations (health checks, auth, middleware) are skipped, and their children are re-parented onto the nearest exported ancestor so the tree stays connected.\n\nWarning: full input and output are captured by default. Prompts, system prompts, tool arguments, tool results and model output are sent to Langfuse as they are. If your data is sensitive, set mask before enabling tracing. See Masking sensitive data.\n\nWarning: tracing turns on automatically. If LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY are present in the environment, NeuroLink starts exporting with no code change. Upgrading with those variables already set starts sending data. Set observability: { langfuse: false } to opt out.\n\nSetup\n\nEnvironment variables\n\n| Variable | Purpose |\n| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| LANGFUSE_PUBLIC_KEY | Langfuse public key (required to auto-enable) |\n| LANGFUSE_SECRET_KEY | Langfuse secret key (required to auto-enable) |\n| LANGFUSE_BASE_URL | Langfuse host; defaults to Langfuse Cloud |\n| LANGFUSE_TRACING_ENVIRONMENT | Environment label on every trace |\n| LANGFUSE_RELEASE | Release label on every trace |\n| OTEL_EXPORTER_OTLP_ENDPOINT | Also export traces and metrics to an OTLP collector |\n| NEUROLINK_SPAN_ATTRIBUTE_MAX_CHARS | Largest serialized span input, output or tool payload in characters (default 40000, minimum 10000). Anything over it is trimmed to valid JSON, keeping the user's question, then this turn's newest messages, then history |\n\nDisable or override\n\nEvery field is optional and falls back to its LANGFUSE_* variable. The type is NeurolinkLangfuseConfig.\n\n| Field | Meaning |\n| --------------- | ------------------------------------------------------------ |\n| publicKey | Langfuse public key |\n| secretKey | Langfuse secret key |\n| baseUrl | Langfuse host |\n| environment | Environment label |\n| release | Release label |\n| mask | Function applied to input, output and metadata before export |\n| exportMode | \"batched\" (default) or \"immediate\" |\n| flushAt | Spans buffered before a batch is sent (default 15) |\n| flushInterval | Seconds between batch flushes (default 5) |\n\nPer-call context\n\nPass context on generate, stream and decide. The values are attached to every span the call creates, including tool calls and fallback attempts, so one call is one trace.\n\n| Field | Recorded as |\n| ----------------- | -------------------------------------------------------","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"","lvl3":""}},
3495
+ {"objectID":"c56b41cb0c1e8e9dfc542fc3c18cf1024985a68cd588c7616a5facff77a7613a","title":"Observability Guide","url":"/docs/features/observability","content":"Observability Guide\n\nNeuroLink exports every AI operation to Langfuse. Setup is environment variables only.\n\nArchitecture\n\nOne instrumentation feeds every backend:\n\nNeuroLink's own provider uses this pipeline, with LangfuseExporter registered when Langfuse is enabled. The Langfuse exporter queues spans without blocking the caller and posts them in batches to Langfuse's OTLP endpoint (POST /api/public/otel/v1/traces, OTLP/HTTP JSON, Basic auth with your keys). Every observation is exported: model calls, attempts, steps and tools, and the spans of the RAG, workflow, evaluation, processor and autoresearch features, which arrive as a plain span observation. Spans that are not observations (health checks, auth, middleware and NeuroLink's HTTP, MCP, memory and file helper spans), wrapper spans NeuroLink marks internal, and spans another library creates on a shared provider that do not describe a model call are skipped, and their children are re-parented onto the nearest exported ancestor so the tree stays connected.\n\nWarning: full input and output are captured by default. Prompts, system prompts, tool arguments, tool results and model output are sent to Langfuse as they are. If your data is sensitive, set mask (or redactIO) before enabling tracing; it applies to what is sent to Langfuse only. See Masking sensitive data.\n\nWarning: tracing turns on automatically. If LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY are present in the environment, NeuroLink starts exporting with no code change. Upgrading with those variables already set starts sending data. Set observability: { langfuse: false } to opt out.\n\nSetup\n\nEnvironment variables\n\n| Variable | Purpose |\n| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| LANGFUSE_PUBLIC_KEY | Langfuse public key (required to auto-enable) |\n| LANGFUSE_SECRET_KEY | Langfuse secret key (required to auto-enable) |\n| LANGFUSE_BASE_URL | Langfuse host; defaults to Langfuse Cloud |\n| LANGFUSE_TRACING_ENVIRONMENT | Environment label on every Langfuse trace. The OTLP resource's deployment.environment is the same value and falls back to LANGFUSE_ENVIRONMENT, then PUBLIC_APP_ENVIRONMENT; Langfuse traces do not read those two |\n| LANGFUSE_RELEASE | Release label on every Langfuse trace. The OTLP resource's service.version is the same value and falls back to PUBLIC_APP_VERSION; Langfuse traces do not read it |\n| LANGFUSE_ENABLED | false, 0, off or no keeps ","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"","lvl3":""}},
3496
3496
  {"objectID":"db6cc880d3b187c65a71c44dcdf928120d86ca14ee034da45f5dd218dcac59b5","title":"Observability Guide","url":"/docs/features/observability#observability-guide","content":"NeuroLink exports every AI operation to Langfuse. Setup is environment variables only.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Observability Guide","lvl3":""}},
3497
- {"objectID":"f66991b3ca536e199e647a4e425060cf8af3be12b320be25230f3fe79a3cf66c","title":"Architecture","url":"/docs/features/observability#architecture","content":"One instrumentation feeds every backend:\n\nNeuroLink's own provider uses this pipeline, with LangfuseExporter registered when Langfuse is enabled. The Langfuse exporter queues spans without blocking the caller and posts them in batches to Langfuse's OTLP endpoint (POST /api/public/otel/v1/traces, OTLP/HTTP JSON, Basic auth with your keys). Spans that are not observations (health checks, auth, middleware) are skipped, and their children are re-parented onto the nearest exported ancestor so the tree stays connected.\n\nWarning: full input and output are captured by default. Prompts, system prompts, tool arguments, tool results and model output are sent to Langfuse as they are. If your data is sensitive, set mask before enabling tracing. See Masking sensitive data.\n\nWarning: tracing turns on automatically. If LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY are present in the environment, NeuroLink starts exporting with no code change. Upgrading with those variables already set starts sending data. Set observability: { langfuse: false } to opt out.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Architecture","lvl3":""}},
3497
+ {"objectID":"f66991b3ca536e199e647a4e425060cf8af3be12b320be25230f3fe79a3cf66c","title":"Architecture","url":"/docs/features/observability#architecture","content":"One instrumentation feeds every backend:\n\nNeuroLink's own provider uses this pipeline, with LangfuseExporter registered when Langfuse is enabled. The Langfuse exporter queues spans without blocking the caller and posts them in batches to Langfuse's OTLP endpoint (POST /api/public/otel/v1/traces, OTLP/HTTP JSON, Basic auth with your keys). Every observation is exported: model calls, attempts, steps and tools, and the spans of the RAG, workflow, evaluation, processor and autoresearch features, which arrive as a plain span observation. Spans that are not observations (health checks, auth, middleware and NeuroLink's HTTP, MCP, memory and file helper spans), wrapper spans NeuroLink marks internal, and spans another library creates on a shared provider that do not describe a model call are skipped, and their children are re-parented onto the nearest exported ancestor so the tree stays connected.\n\nWarning: full input and output are captured by default. Prompts, system prompts, tool arguments, tool results and model output are sent to Langfuse as they are. If your data is sensitive, set mask (or redactIO) before enabling tracing; it applies to what is sent to Langfuse only. See Masking sensitive data.\n\nWarning: tracing turns on automatically. If LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY are present in the environment, NeuroLink starts exporting with no code change. Upgrading with those variables already set starts sending data. Set observability: { langfuse: false } to opt out.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Architecture","lvl3":""}},
3498
3498
  {"objectID":"c3139194cb90b179cd9656356b659b38ab57c2b36ff72a0ae85ba1d83fb78aee","title":"Setup","url":"/docs/features/observability#setup","content":"Setup","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Setup","lvl3":""}},
3499
- {"objectID":"f5b277d3ae78e90affc7f614bbe8f4ea0e076874c57d77d204bd0764d0952622","title":"Environment variables","url":"/docs/features/observability#environment-variables","content":"| Variable | Purpose |\n| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| LANGFUSE_PUBLIC_KEY | Langfuse public key (required to auto-enable) |\n| LANGFUSE_SECRET_KEY | Langfuse secret key (required to auto-enable) |\n| LANGFUSE_BASE_URL | Langfuse host; defaults to Langfuse Cloud |\n| LANGFUSE_TRACING_ENVIRONMENT | Environment label on every trace |\n| LANGFUSE_RELEASE | Release label on every trace |\n| OTEL_EXPORTER_OTLP_ENDPOINT | Also export traces and metrics to an OTLP collector","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Environment variables","lvl3":""}},
3500
- {"objectID":"304f3b3f1d3d11c91945e49e4ae3f60d2b9a826b87a3948f18fd137a16b38c45","title":"Disable or override","url":"/docs/features/observability#disable-or-override","content":"Every field is optional and falls back to its LANGFUSE_* variable. The type is NeurolinkLangfuseConfig.\n\n| Field | Meaning |\n| --------------- | ------------------------------------------------------------ |\n| publicKey | Langfuse public key |\n| secretKey | Langfuse secret key |\n| baseUrl | Langfuse host |\n| environment | Environment label |\n| release | Release label |\n| mask | Function applied to input, output and metadata before export |\n| exportMode | \"batched\" (default) or \"immediate\" |\n| flushAt | Spans buffered before a batch is sent (default 15) |\n| flushInterval | Seconds between batch flushes (default 5) |","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Disable or override","lvl3":""}},
3499
+ {"objectID":"f5b277d3ae78e90affc7f614bbe8f4ea0e076874c57d77d204bd0764d0952622","title":"Environment variables","url":"/docs/features/observability#environment-variables","content":"| Variable | Purpose |\n| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| LANGFUSE_PUBLIC_KEY | Langfuse public key (required to auto-enable) |\n| LANGFUSE_SECRET_KEY | Langfuse secret key (required to auto-enable) |\n| LANGFUSE_BASE_URL | Langfuse host; defaults to Langfuse Cloud","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Environment variables","lvl3":""}},
3500
+ {"objectID":"304f3b3f1d3d11c91945e49e4ae3f60d2b9a826b87a3948f18fd137a16b38c45","title":"Disable or override","url":"/docs/features/observability#disable-or-override","content":"Every field is optional. publicKey, secretKey, baseUrl, environment and release fall back to the variables above; the others have the defaults below. The type is NeurolinkLangfuseConfig.\n\n| Field | Meaning |\n| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| enabled | false turns Langfuse off although keys are set (from untyped config, the strings \"false\", \"0\", \"off\" and \"no\" do too); any other value, or leaving it out, leaves it on.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Disable or override","lvl3":""}},
3501
3501
  {"objectID":"d5c2e109ca689299bbc1e1068341dfd6ba1294b1ee3c11cebe6489977865892e","title":"Per-call context","url":"/docs/features/observability#per-call-context","content":"Pass context on generate, stream and decide. The values are attached to every span the call creates, including tool calls and fallback attempts, so one call is one trace.\n\n| Field | Recorded as |\n| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |\n| userId | Trace user |\n| sessionId | Trace session |\n| tags | Trace tags |\n| traceName | Trace name |\n| conversationId | Metadata key conversationId |\n| requestId | Metadata key requestId |\n| metadata | Metadata; values up to 200 characters are on every observation, longer values and arrays are recorded in full on the trace only |\n| observationName | Name of the root observation of this call; default generate or stream; not propagated to child observations or used as the trace name |","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Per-call context","lvl3":""}},
3502
3502
  {"objectID":"d262ba0787c1b57c76e303845fc9c6f447a3a2f747a880270f15c61fcc1863bb","title":"setLangfuseContext","url":"/docs/features/observability#setlangfusecontext","content":"Wrap your own code to attach the same attributes to everything inside it, including spans you create yourself.\n\nsetLangfuseContext(params: NeurolinkTraceContext, fn) returns whatever fn returns. It is a thin wrapper over Langfuse's propagateAttributes.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"setLangfuseContext","lvl3":""}},
3503
- {"objectID":"542eff7388511fe18f61b3de966dff32a0a0ccfa5c95df170298790e0233739d","title":"Naming","url":"/docs/features/observability#naming","content":"No span name, attribute key or metadata key contains the product name. Names never carry per-execution values: the model and provider are fields, the step number is metadata.\n\n| Observation | Name | Langfuse type | Parent |\n| -------------------------- | ------------------------------------- | ------------- | ---------------------- |\n| generate / stream call | generate / stream | agent | none (trace root) |\n| Fallback attempt | generate.attempt / stream.attempt | span | root |\n| Agent loop step | step (step_index metadata) | span | the attempt span |\n| One model request | generate / stream | generation | its step |\n| Tool execution | the tool's name, e.g. get_weather | tool | the step that ran it |\n| decide | decide | generation | caller or root |\n\nA model request is named after the API the caller used (NeuroLink.generate or provider.generate gives generate, NeuroLink.stream or provider.stream gives stream). Its model is the observation's model field (the model the provider reported, falling back to the requested one) and the provider id is the provider metadata key.\n\nThe trace name is context.traceName when given, otherwise the operation name (generate, stream). An outer setLangfuseContext takes precedence over per-call context for user, session and trace name.\n\nEmbeddings, RAG, workflow, evaluation, transcribe, TTS and media tracing are not yet recorded. The root also sets trace-level input and output; a generate or stream nested inside another turn does not.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Naming","lvl3":""}},
3503
+ {"objectID":"542eff7388511fe18f61b3de966dff32a0a0ccfa5c95df170298790e0233739d","title":"Naming","url":"/docs/features/observability#naming","content":"The observations below (generate, stream, decide, attempts, steps and tools) carry no product name in their span names, attribute keys or metadata keys. Names never carry per-execution values: the model and provider are fields, the step number is metadata. The product name remains in the plain spans listed under Other spans and in the proxy root's neurolink.user_id, neurolink.session_id and neurolink.conversation_id attributes.\n\n| Observation | Name | Langfuse type | Parent |\n| -------------------------- | ------------------------------------- | ------------- | ---------------------- |\n| generate / stream call | generate / stream | agent | none (trace root) |\n| Fallback attempt | generate.attempt / stream.attempt | span | root |\n| Agent loop step | step (step_index metadata) | span | the attempt span |\n| One model request | generate / stream | generation | its step |\n| Tool execution | the tool's name, e.g. get_weather | tool | the step that ran it |\n| decide | decide | generation | caller or root |\n\nA model request is named after the API the caller used (NeuroLink.generate or provider.generate gives generate, NeuroLink.stream or provider.stream gives stream). Its model is the observation's model field (the model the provider reported, falling back to the requested one) and the provider id is the provider metadata key.\n\nThe trace name is context.traceName when given, otherwise the operation name (generate, stream). An outer setLangfuseContext takes precedence over per-call context for user, session and trace name.\n\nEmbeddings, transcribe, TTS and media tracing are not yet recorded. The root also sets trace-level input and output; a generate or stream nested inside another turn does not.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Naming","lvl3":""}},
3504
+ {"objectID":"bec7707961c02d5123e4dceed9e13f5f5311e2f47783dd81fc290b467f50ff7a","title":"Other spans","url":"/docs/features/observability#other-spans","content":"Spans from the RAG, workflow, file, memory, context and evaluation code are exported as plain span records with their name, timing, status and their own attributes. They keep their neurolink.* names: neurolink.rag.search, neurolink.rag.rerank, neurolink.rag.chunk, neurolink.workflow.* (run, run.streaming, response.condition, judge.score, ensemble.execute, layers.execute), neurolink.file.process, neurolink.file.video.process, neurolink.context.compact, neurolink.memory.summarize and neurolink.evaluation.ragas.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Other spans","lvl3":""}},
3504
3505
  {"objectID":"ceb0e84f71b6a9deb6a57e236efa009236e933eb892ad569d59e36ba873292f3","title":"Steps and chat format","url":"/docs/features/observability#steps-and-chat-format","content":"Each iteration of the agent loop gets one step span with its one-based index in the step_index metadata. It parents that iteration's generation and the tool observations the model requested in it, so a multi-tool turn reads as step 1 (call, tools), step 2 (call, tools), and so on.\n\nGeneration input is a chat message array with the system message first. Generation output is an assistant message array: content, plus reasoning and tool_calls ({ id, type: \"function\", function: { name, arguments } }) when present. Langfuse renders both as a conversation.\n\nGeneration details:\nUsage includes input, output, total, cache read, cache creation and reasoning tokens, whatever naming the provider uses.\nCost is computed from NeuroLink's pricing table and recorded as costDetails.total.\nModel parameters are temperature, max tokens, top-p and thinking level.\nTime to first token is recorded as the generation's completion start time for streams.\nLevel is WARNING for a length or content-filter finish, an aborted call, an early break out of a stream, or an empty output, and ERROR when the call throws.\nStreams end their spans when the stream finishes, is abandoned or is aborted.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Steps and chat format","lvl3":""}},
3505
- {"objectID":"7dbc08e9d130f6a8e970396c854cb6afb0be27558cdbaa21122d1df517e4006a","title":"Masking sensitive data","url":"/docs/features/observability#masking-sensitive-data","content":"mask runs on span input and output before export. It receives { data } and returns the value to send.\n\nmask also runs on trace and observation metadata. If it throws, the value is replaced by <fully masked>. Provider credentials are already redacted from recorded values. Everything else is captured as is.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Masking sensitive data","lvl3":""}},
3506
- {"objectID":"bc90fcd87977756303a1a998fc869c12bbd8ef1f1b88e4f17746e4bd4463d15f","title":"Langfuse exporter behaviour","url":"/docs/features/observability#langfuse-exporter-behaviour","content":"Endpoint. POST {baseUrl}/api/public/otel/v1/traces as OTLP/HTTP JSON. Requests are cut at 500 spans or 3.5 MB.\nObservation types. Agent, generation, tool, step, attempt and the other observation types reach Langfuse with their native type, model, parameters, usage, cost and first-token time.\nIdentity. User, session, trace name, tags and trace metadata are set on every observation, not only the root.\nEnvironment and release. The config field, then LANGFUSE_TRACING_ENVIRONMENT / LANGFUSE_RELEASE; they fill spans that do not carry them already.\nBatching. Spans are sent when flushAt spans are queued, every flushInterval seconds, on flushOpenTelemetry() and on shutdown. exportMode: \"immediate\" sends each span as it ends.\nFailures. A request that fails with 429, 5xx or a network error is retried with exponential backoff. Other 4xx responses are not retried. Spans Langfuse reports as rejected (partialSuccess) are logged once as a warning and not re-sent. A 401 or 403 logs one warning per process (\"Langfuse credentials rejected\") and is not retried. Delivery never throws into your code.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Langfuse exporter behaviour","lvl3":""}},
3507
- {"objectID":"ba32010e859f5101a6247151e93a3db0fdaa79d0afe2d47c1492c74a0e04f5f5","title":"External OpenTelemetry provider","url":"/docs/features/observability#external-opentelemetry-provider","content":"If your application registers its own global TracerProvider, NeuroLink creates no provider. Other backends: add that vendor's span processor to your provider, or set OTEL_EXPORTER_OTLP_ENDPOINT. For Langfuse choose one of two recipes.\n\n(a) Exporter pipeline (recommended). Add createExporterSpanProcessor() to your provider and give NeuroLink the Langfuse keys, either through new NeuroLink({ observability: { langfuse } }) or the LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY environment variables. NeuroLink registers its LangfuseExporter on the shared exporter registry, which the processor reads, so mask, batching, chunking and retry from the section above all apply. Register the processor before the first NeuroLink call; spans that end before the exporter is registered are not exported.\n\n(b) LangfuseSpanProcessor. Add new LangfuseSpanProcessor() from @langfuse/otel with its defaults; no custom shouldExportSpan filter is needed. Every observation span NeuroLink emits carries a gen_ai.observation.type attribute, which the default filter exports, while infrastructure spans are not exported. This path sends OTLP and keeps native Langfuse observation types. The processor takes its keys, mask, flushAt, flushInterval, exportMode, environment and release from its own constructor options and LANGFUSE_* variables. It does not read observability.langfuse from new NeuroLink(...), so those settings must be passed to the processor, and NeuroLink's exporter masking, 500-span / 3.5 MB chunking and retry policy do not apply to it.\n\nRegister your provider before the first NeuroLink call. NeuroLink never flushes or shuts down a provider it did not create. @langfuse/otel is not a dependency of NeuroLink; install it for recipe (b).","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"External OpenTelemetry provider","lvl3":""}},
3508
- {"objectID":"be39053eddf107cc2916c84cc2e34d629a925ef52f26864ff9dd628f798d49e1","title":"Initialisation","url":"/docs/features/observability#initialisation","content":"Telemetry starts synchronously and lazily, once per process, the first time any NeuroLink span is created. That includes direct provider calls with no NeuroLink instance, for example AIProviderFactory.createProvider(...).generate(...), which are traced the same way.\n\nAn explicit observability config passed to new NeuroLink() wins over LANGFUSE_* variables when the instance is constructed before any span is created. If telemetry has already started, the config is ignored and a debug message is logged once.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Initialisation","lvl3":""}},
3509
- {"objectID":"2f01481e1bd920ff04b7d26da5bc86efababd09cbd1f763a1f119fb7d1443582","title":"Serverless and short-lived processes","url":"/docs/features/observability#serverless-and-short-lived-processes","content":"Spans are batched. Flush before the process freezes or exits:\n\nOr send each span as it ends:\n\nshutdownOpenTelemetry() is terminal for the process: telemetry cannot restart afterwards. While other NeuroLink instances are still alive it only flushes.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Serverless and short-lived processes","lvl3":""}},
3506
+ {"objectID":"7dbc08e9d130f6a8e970396c854cb6afb0be27558cdbaa21122d1df517e4006a","title":"Masking sensitive data","url":"/docs/features/observability#masking-sensitive-data","content":"mask runs on the input, output and metadata sent to Langfuse. It receives { data } and returns the value to send.\n\nmask and redactIO act on what NeuroLink sends to Langfuse, and on nothing else. They cover the trace and observation input and output (langfuse.observation.input, langfuse.observation.output, langfuse.trace.input, langfuse.trace.output); redactIO: true leaves those out of the request, and mask rewrites them instead. mask also runs on trace and observation metadata, whether or not the metadata is an object. If it throws, the value is replaced by <fully masked>.\n\nEverything else goes to Langfuse as it was recorded: the status message, exception.message, error.message, a tool's tool.error.message and a provider retry's retry.error, tool-call attributes, the proxy.body attribute of proxy body events, event attributes, and the prompt and completion attributes another library's GenAI instrumentation writes on spans that are exported with NeuroLink's. The policy does not reach any other sink either: an exporter you register with registerExporter, the spans NeuroLink sends to OTEL_EXPORTER_OTLP_ENDPOINT, and getSpans() receive the values as they were recorded. If one of them must not see content, take it out where it is recorded (NEUROLINK_RECORD_INPUTS=false) or in that sink's own processing.\n\nNeuroLink adds no credential masking of its own to error text. A failed call's status message, error.message, tool.error.message, retry.error and exception text are recorded as the provider returned them, in every sink, as the released build did, so an error that echoes a key reaches the sinks with the key. Input, output and tool arguments and results keep the credential masking they always had. To keep a credential out of error text, filter it in a custom exporter or keep it out of the provider's error.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Masking sensitive data","lvl3":""}},
3507
+ {"objectID":"bc90fcd87977756303a1a998fc869c12bbd8ef1f1b88e4f17746e4bd4463d15f","title":"Langfuse exporter behaviour","url":"/docs/features/observability#langfuse-exporter-behaviour","content":"Endpoint. POST {baseUrl}/api/public/otel/v1/traces as OTLP/HTTP JSON. Requests are cut at 500 spans or 3.5 MB.\nObservation types. Agent, generation, tool, step, attempt and the other observation types reach Langfuse with their native type, model, parameters, usage, cost and first-token time.\nIdentity. User, session, trace name, tags and trace metadata are set on every observation, not only the root.\nEnvironment and release. The config field, then LANGFUSE_TRACING_ENVIRONMENT / LANGFUSE_RELEASE; they fill spans that do not carry them already. The OTLP resource's deployment.environment and service.version also read LANGFUSE_ENVIRONMENT, PUBLIC_APP_ENVIRONMENT and PUBLIC_APP_VERSION; Langfuse traces do not.\nBatching. Spans are sent when flushAt spans are queued, every flushInterval seconds, on flushOpenTelemetry() and on shutdown. exportMode: \"immediate\" sends each span as it ends.\nFailures. A request that fails with 429, 5xx or a network error is retried with exponential backoff, or after the Retry-After a 429 or 503 names, up to 60 seconds and never past a flush's budget. Other 4xx responses are not retried. Spans Langfuse reports as rejected (partialSuccess) are logged once as a warning and not re-sent; a success whose body is not an object, or whose rejected count is not a whole number between zero and the spans sent, counts as all delivered. At most 4 export requests are in flight at once. Spans that end meanwhile wait, up to 10,000 of them; when Langfuse stops answering, the oldest waiting spans are dropped, counted in failedSpans and in the errors of healthCheck(), and warned about once a minute. Spans that end during shutdown are sent within the flush budget or counted as dropped. A 401 or 403 logs one warning per process (\"Langfuse credentials rejected\") and is not retried. Delivery never throws into your code.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Langfuse exporter behaviour","lvl3":""}},
3508
+ {"objectID":"07d102a2518a59d4afc8618d87ee066c3c7e36fefe22ee00772e172d8055a720","title":"Metrics","url":"/docs/features/observability#metrics","content":"NeuroLink also records OpenTelemetry metrics, on one meter named neurolink-ai. They are recorded only when OTEL_EXPORTER_OTLP_ENDPOINT is set, when NEUROLINK_TELEMETRY_ENABLED=true, or when a global MeterProvider is registered (your application's, or NeuroLink's own); a global TracerProvider alone, NeuroLink's included, does not turn them on. They are written to the global MeterProvider. NeuroLink registers one, exporting to $OTEL_EXPORTER_OTLP_ENDPOINT/v1/metrics every 15 seconds, only when it owns the tracer provider and the endpoint is set; beside a host provider they go to the MeterProvider your application registered, and nowhere when it registered none.\n\n| Metric | Kind | Labels | Recorded for |\n| ----------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| ai_requests_total | counter | provider, model | each generate() that finishes in BaseProvider.finalizeNativeGenerate (the native generate path of providers such as Anthropic, SageMaker and the OpenAI Chat Completions family), and each proxied request whose usage the proxy captured |\n| ai_request_duration_ms | histogram,","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Metrics","lvl3":""}},
3509
+ {"objectID":"ba32010e859f5101a6247151e93a3db0fdaa79d0afe2d47c1492c74a0e04f5f5","title":"External OpenTelemetry provider","url":"/docs/features/observability#external-opentelemetry-provider","content":"If your application registers its own global TracerProvider before tracing starts, NeuroLink creates no provider. OTEL_EXPORTER_OTLP_ENDPOINT is then ignored: NeuroLink logs one warning and exports no traces or metrics to it, so add an OTLP exporter, or any other vendor's span processor, to your own providers. neurolink.getTelemetryStatus().openTelemetry then reports usingExternalProvider: true (its enabled stays whether tracing is initialized, as before), and neurolink telemetry status and neurolink observability status print the endpoint as ignored. NeuroLink's metrics are recorded on the MeterProvider your application registers. For Langfuse choose one of two recipes.\n\nThe host provider must register a context manager, which NodeTracerProvider.register() and NodeSDK do. Without one OpenTelemetry cannot carry context between calls, so setLangfuseContext user, session and metadata never reach NeuroLink's spans and child spans lose their parents; NeuroLink logs a warning when it finds none. Shutting the host provider down shuts the shared exporters down, as createExporterSpanProcessor() always did. If the host rebuilds its provider, pass createExporterSpanProcessor(undefined, { shutdownExporters: false }): that processor only flushes the exporters on shutdown, so a rebuilt provider that adds one again keeps exporting.\n\n(a) Exporter pipeline (recommended). Add createExporterSpanProcessor() to your provider and give NeuroLink the Langfuse keys, either through new NeuroLink({ observability: { langfuse } }) or the LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY environment variables. NeuroLink registers its LangfuseExporter on the shared exporter registry, which the processor reads, so mask, batching, chunking and retry from the section above all apply. Register the processor before the first NeuroLink call; spans that end before the exporter is registered are not exported.\n\n(b) LangfuseSpanProcessor.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"External OpenTelemetry provider","lvl3":""}},
3510
+ {"objectID":"be39053eddf107cc2916c84cc2e34d629a925ef52f26864ff9dd628f798d49e1","title":"Initialisation","url":"/docs/features/observability#initialisation","content":"Tracing starts synchronously, once per process, at the first of these:\nnew NeuroLink({ observability }). A constructor without an observability block starts nothing.\nThe first generate, stream, decide or tryDecide call on any NeuroLink instance.\nThe first span NeuroLink's own code creates, on an entry that loads the start-up module: the main package entry, @juspay/neurolink/server and @juspay/neurolink/livekit, and the ./rag, ./workflow, ./evaluation, ./processors and ./autoresearch subpath entries. That includes direct provider calls with no NeuroLink instance, for example AIProviderFactory.createProvider(...).generate(...) imported from the main entry.\n\nThe lightweight @juspay/neurolink/client entry does not load the OpenTelemetry Node SDK and does not start tracing: its spans go to a TracerProvider your application registered, and are dropped when there is none. A Node application that relied on this entry alone to start tracing (the previous version did) adds import \"@juspay/neurolink\" once at start-up.\n\nTracing configuration is process-wide and the first start wins. An observability config passed to new NeuroLink() wins over the LANGFUSE_* variables only when that construction starts tracing. A later instance's config is ignored: if it differs from the first, NeuroLink logs a warning that names the options that differ, once per set of differing options; an identical config logs one debug line. A start before Langfuse or OTLP is configured sets nothing up, so a later config or environment can still start tracing, unless langfuse: false was passed. langfuse: true, or an options object, without both keys counts as Langfuse not configured: NeuroLink warns once that the keys are missing and, unless OTEL_EXPORTER_OTLP_ENDPOINT is set, starts nothing, so a later config with keys still starts tracing. To run beside your own OpenTelemetry setup, register your TracerProvider before tracing starts; see External OpenTelemetry provider.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Initialisation","lvl3":""}},
3511
+ {"objectID":"2f01481e1bd920ff04b7d26da5bc86efababd09cbd1f763a1f119fb7d1443582","title":"Serverless and short-lived processes","url":"/docs/features/observability#serverless-and-short-lived-processes","content":"Spans are batched. Flush before the process freezes or exits:\n\nOr send each span as it ends:\n\nA script that ends on its own flushes pending spans and metrics once the event loop is empty (beforeExit). An explicit process.exit(), a fatal signal or an uncaught exception skips that flush, so call await flushOpenTelemetry() before them. flushOpenTelemetry(), shutdownOpenTelemetry() and that exit flush wait at most the flush budget (observability.langfuse.flushBudgetMs, default 3000 ms) for the OTLP collector, and for Langfuse within the same budget. What did not complete is logged as a warning, and flushOpenTelemetry() then rejects with Flush failed (its cause lists the reasons), as it always did; the exit flush and shutdown() catch that themselves, so a collector or Langfuse that cannot be reached does not hold a script past the budget: NeuroLink gives its OTLP exporters the budget as their request timeout. When OTEL_EXPORTER_OTLP_TIMEOUT (or its _TRACES_ / _METRICS_ form) is set, that timeout applies instead, and a request still in flight can hold the process open until the exporters' timeouts expire. The exit flush is new: before it a script ended at once and delivered nothing it still held, and with a collector that cannot be reached it now ends up to the flush budget (3 s by default) after its last call. Set NEUROLINK_TELEMETRY_EXIT_FLUSH=false (also 0, off or no) to skip it and end as soon as there is no work.\n\nshutdownOpenTelemetry() is terminal for the process: telemetry cannot restart afterwards. While other NeuroLink instances are still alive it only flushes.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Serverless and short-lived processes","lvl3":""}},
3510
3512
  {"objectID":"721428a68bdcef28a5437443fe5a57565c6aa3b324a48ccc39728e5d7ff25e2d","title":"Health","url":"/docs/features/observability#health","content":"isHealthy and hasProcessor are true only while a Langfuse exporter is registered, whether NeuroLink owns the provider or runs beside a host provider. With a host provider, the exporter still needs createExporterSpanProcessor() on that provider to receive spans.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Health","lvl3":""}},
3511
- {"objectID":"68b0ab87bc1ff79e31454e9550e76c174e5a1a4628f3c9de0e74a7dc6e07bdd2","title":"Proxy","url":"/docs/features/observability#proxy","content":"The proxy records the pool account label (not an email address), sets userId and sessionId from x-neurolink-user-id and x-neurolink-session-id, names traces proxy:<model>, and records request and response bodies, usage, cost and time to first token. See the Telemetry Guide and Claude Proxy Observability.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Proxy","lvl3":""}},
3512
- {"objectID":"818b18185bdd4b8b7003f170da3113452c78895619ec762279322c8b2b587801","title":"Voice (LiveKit)","url":"/docs/features/observability#voice-livekit","content":"Pipeline mode traces through stream() and carries the voice tag. Realtime mode records one trace per conversation turn, with transcripts as input and output and tool calls as tool records, and only when voice memory is enabled. The realtime generation record carries the model id and the provider of the realtime backend (vertex for Gemini Live, xai for Grok Voice). Full transcripts are sent to Langfuse by default; use mask for sensitive data.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Voice (LiveKit)","lvl3":""}},
3513
+ {"objectID":"68b0ab87bc1ff79e31454e9550e76c174e5a1a4628f3c9de0e74a7dc6e07bdd2","title":"Proxy","url":"/docs/features/observability#proxy","content":"The proxy records the pool account label (not an email address), sets userId and sessionId from x-neurolink-user-id and x-neurolink-session-id (the session falls back to Claude Code's x-claude-code-session-id), names traces proxy:<model>, and records usage, cost and time to first token. The proxy.request root also carries neurolink.user_id, neurolink.session_id and neurolink.conversation_id as plain attributes for backends that index identity by key.\n\nRequest and response bodies are recorded on traces only when NEUROLINK_PROXY_TRACE_BODY_LOGGING=true, the same opt-in as before the Langfuse migration. With it on, the bodies become the input and output of the proxy observations, so mask and redactIO apply to them on the Langfuse request, and NEUROLINK_RECORD_INPUTS=false still drops the request body. The raw bodies are also attached as span events only when the same flag is on (proxy.client.request_body, proxy.upstream.request_body, proxy.upstream.response_body and proxy.stream.events); without it those events carry only sizes and counts. mask and redactIO do not cover those events, neither the proxy.body attribute nor proxy.stream.events, so leave the flag off for traffic whose content must not leave the process.\n\nUpstream error text is recorded without the flag, as it was before the migration: error.message and the status message (<error type>: <text>) hold the upstream text, capped, as received. mask and redactIO leave it as it was recorded, as they did before the migration. A translated request, which the proxy serves through a NeuroLink model call, follows the body opt-in: without the flag that call's records keep model, usage, cost, finish reason and timings but no input or output.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Proxy","lvl3":""}},
3514
+ {"objectID":"818b18185bdd4b8b7003f170da3113452c78895619ec762279322c8b2b587801","title":"Voice (LiveKit)","url":"/docs/features/observability#voice-livekit","content":"Pipeline mode traces through stream() and carries the voice tag. Realtime mode records one trace per conversation turn, with transcripts as input and output and tool calls as tool records. Every realtime turn is traced when tracing is on, with or without voice memory: a call without memory is traced through setUpRealtimeCallTracing, with the room name as the session and no user id. Each turn is traced as soon as it closes, never behind the memory store's writes, so a slow or stuck store loses no trace. At shutdown the call closes its open turn and delivers its traces while its pending turn writes finish, both within memory.flushBudgetMs (default 800 ms, because the worker is reaped about a second after shutdown starts); flushOpenTelemetry() itself is bounded by observability.langfuse.flushBudgetMs (default 3000 ms). The realtime generation record carries the model id and the provider of the realtime backend (vertex for Gemini Live, xai for Grok Voice). Full transcripts are sent to Langfuse by default; use mask for sensitive data.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Voice (LiveKit)","lvl3":""}},
3513
3515
  {"objectID":"6ddae5fd4f96e8c2a1c9d924054685440eb46353183984ff0b8d3167e12071b8","title":"Troubleshooting","url":"/docs/features/observability#troubleshooting","content":"| Symptom | Check |\n| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Nothing reaches Langfuse | Both keys set and langfuse not false; getLangfuseHealthStatus(); call flushOpenTelemetry() on exit |\n| Spans missing with own OTEL setup | Add createExporterSpanProcessor() to your provider and pass the Langfuse keys to NeuroLink (or set LANGFUSE_*), or use new LangfuseSpanProcessor() with its own keys; register the processor before the first NeuroLink call |\n| Wrong host | Variable is LANGFUSE_BASE_URL, not LANGFUSE_BASEURL |\n| Traces stop after shutdown | shutdownOpenTelemetry() cannot be undone in the same process; use flushOpenTelemetry() |","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Troubleshooting","lvl3":""}},
3514
3516
  {"objectID":"c26071897c848ba9cf00651234ebd459171008740460f6075451e866420e5881","title":"Migrating from the previous version","url":"/docs/features/observability#migrating-from-the-previous-version","content":"This release is breaking.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Migrating from the previous version","lvl3":""}},
3515
- {"objectID":"310e635468902de6eca8789d9ce5905c4ecb08a665b802119f33a6ae78a6f429","title":"Configuration fields","url":"/docs/features/observability#configuration-fields","content":"| Removed | Replacement |\n| ------------------------------------------------------------- | ----------------------------------------------------------------------- |\n| langfuse.enabled | Set the keys to enable; langfuse: false disables |\n| langfuse.userId, langfuse.sessionId | Per-call context.userId, context.sessionId, or setLangfuseContext |\n| useExternalTracerProvider, autoDetectExternalProvider | Automatic: a registered global provider is detected |\n| skipLangfuseSpanProcessor | Register your own provider; NeuroLink then adds nothing |\n| mediaUploadEnabled, LANGFUSE_MEDIA_UPLOAD_ENABLED | Removed; the exporter does not upload media |\n| autoDetectOperationName, operationName, traceNameFormat | context.traceName |\n| customAttributes | context.metadata |\n| conversationId, requestId on the config | context.conversationId, context.requestId (stored as metadata) |\n| NEUROLINK_TELEMETRY_ENABLED | Removed; the LANGFUSE_* keys enable tracing |\n| LANGFUSE_BASEURL hint | LANGFUSE_BASE_URL |","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Configuration fields","lvl3":""}},
3516
- {"objectID":"58496b05fc501a4521336522ee26a5eb64aa0e9d6b609b350cc0813f6d453feb","title":"Exports","url":"/docs/features/observability#exports","content":"| Removed | Replacement |\n| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |\n| initializeOpenTelemetry | Automatic on new NeuroLink() |\n| getLangfuseSpanProcessor | createExporterSpanProcessor(registry) or new LangfuseSpanProcessor() |\n| getTracerProvider, getTracer | trace.getTracer() from @opentelemetry/api |\n| getSpanProcessors | Add createExporterSpanProcessor() to your provider |\n| createContextEnricher | setLangfuseContext or per-call context |\n| isUsingExternalTracerProvider | getLangfuseHealthStatus().usingExternalProvider |\n| isLangfuseInternalSpan, langfuseShouldExportSpan | Removed; only observation spans are exported |\n| getLangfuseContext | Removed |\n| runWithCurrentLangfuseContext | context.bind(context.active(), fn) from @opentelemetry/api |\n| buildObservabilityConfigFromEnv | Removed; env keys are read automatically |\n| createMockLangfuseClient","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Exports","lvl3":""}},
3517
- {"objectID":"ddf06146284ab7554c88bfef97f75c42c4186a270ac952f6cb27eb9e3640217a","title":"CLI","url":"/docs/features/observability#cli","content":"| Removed | Replacement |\n| ------------------------------------ | -------------------------------------- |\n| neurolink telemetry configure | Set LANGFUSE_* environment variables |\n| neurolink telemetry list-exporters | neurolink telemetry status |\n| neurolink observability exporters | neurolink observability status |\n\nExporterRegistry, NoOpExporter, AlwaysSampler, NeverSampler and the vendor exporter classes are unchanged. NeuroLink wires only LangfuseExporter. LangfuseExporter, ExporterSpanProcessor and createExporterSpanProcessor are new root exports.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"CLI","lvl3":""}},
3517
+ {"objectID":"310e635468902de6eca8789d9ce5905c4ecb08a665b802119f33a6ae78a6f429","title":"Configuration fields","url":"/docs/features/observability#configuration-fields","content":"| Removed or changed | Replacement |\n| ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| langfuse.enabled | Kept, and optional: Langfuse is on when keys are present unless enabled: false (or langfuse: false); enabled: true is no longer needed |\n| langfuse.userId, langfuse.sessionId | Per-call context.userId, context.sessionId, or setLangfuseContext","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Configuration fields","lvl3":""}},
3518
+ {"objectID":"58496b05fc501a4521336522ee26a5eb64aa0e9d6b609b350cc0813f6d453feb","title":"Exports","url":"/docs/features/observability#exports","content":"| Removed or changed | Replacement |\n| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| initializeOpenTelemetry | Automatic; see Initialisation |\n| getLangfuseSpanProcessor | createExporterSpanProcessor(registry) or new LangfuseSpanProcessor() |\n| getTracerProvider, getTracer | trace.getTracer() from @opentelemetry/api |\n| getSpanProcessors | Add createExporterSpanProcessor() to","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Exports","lvl3":""}},
3519
+ {"objectID":"ddf06146284ab7554c88bfef97f75c42c4186a270ac952f6cb27eb9e3640217a","title":"CLI","url":"/docs/features/observability#cli","content":"| Removed | Replacement |\n| ------------------------------------ | -------------------------------------- |\n| neurolink telemetry configure | Set LANGFUSE_* environment variables |\n| neurolink telemetry list-exporters | neurolink telemetry status |\n| neurolink observability exporters | neurolink observability status |","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"CLI","lvl3":""}},
3520
+ {"objectID":"a53a05129c249c4b5998d14baf8ce8023a2e0039999f1f79520e3f0f58a92a54","title":"Types","url":"/docs/features/observability#types","content":"| Removed or changed | Replacement |\n| -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| LangfuseContext, TraceNameFormat | NeurolinkTraceContext (per-call context, setLangfuseContext); the trace name is context.traceName |\n| LangfuseConfig | NeurolinkLangfuseConfig |\n| ExporterName, ToolSpanCall, TelemetryConfigureArgs, TelemetryListExportersArgs, ObservabilityExportersArgs | Removed with the CLI commands and tool wrapper that used them |\n| AnthropicLoopAdapterConfig, GeminiLoopAdapterCoreConfig, NativeGenerateLoopArgs: provider | provider is now a required string, recorded on each step's generation and used to price it; GeminiLoopAdapterCoreConfig.buildRequest returns GeminiStepRequest instead of unknown |","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Types","lvl3":""}},
3521
+ {"objectID":"67b137f6a345340802aec49454b7f962e4ed3c240a395bc4c693dad84e4adf5e","title":"Dependencies","url":"/docs/features/observability#dependencies","content":"@opentelemetry/sdk-trace-node moved from a peer dependency to a dependency, so NeuroLink installs it itself.\n@langfuse/otel moved from an optional dependency to a devDependency of NeuroLink, so install it yourself for new LangfuseSpanProcessor().\n@langfuse/core and @langfuse/tracing are new dependencies.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Dependencies","lvl3":""}},
3522
+ {"objectID":"1bcd1955ce1f5ffdea68be64f3bfffc22035ae2006c1a19ca4bc41f0a5a74ef0","title":"Unchanged","url":"/docs/features/observability#unchanged","content":"ExporterRegistry, NoOpExporter, AlwaysSampler, NeverSampler, the vendor exporter classes, ExtendedObservabilityConfig and the *ExporterConfig types are unchanged (LangfuseExporterConfig configures new LangfuseExporter(...)). NeuroLink wires only LangfuseExporter. LangfuseExporter, ExporterSpanProcessor and createExporterSpanProcessor are new root exports.","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Unchanged","lvl3":""}},
3518
3523
  {"objectID":"9a2c99612604332f4b2bc32fb673fe19412249aba74ad41f2ed2cfd84a8d1185","title":"See Also","url":"/docs/features/observability#see-also","content":"Telemetry Guide\nClaude Proxy Observability\nAnalytics Reference","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"See Also","lvl3":""}},
3519
3524
  {"objectID":"1b22ff57a6c6e2525e50e02118d6029f5ee3779ef701e9fde1b7b97d1a782760","title":"Office Documents Support","url":"/docs/features/office-documents","content":"Office Documents Support\n\nNeuroLink reads Word, Excel, and PowerPoint files itself and puts their content into the prompt as text. Attach them through the same input.files array (or --file flag) that carries every other file type; there is no separate Office input field and no provider restriction.\n\nOverview\n\nWhen a request carries an Office file, NeuroLink:\nDetects the format from the file's bytes, MIME type, and extension.\nExtracts the content in-process: Word through mammoth (converted to Markdown), Excel through exceljs, and PowerPoint by reading the slide XML inside the .pptx package.\nAppends the extracted text to the prompt under a heading that names the file (## Document: \"report.docx\", ## Spreadsheet: \"sales.xlsx\", ## Presentation: \"deck.pptx\").\nSends an ordinary text prompt to whichever provider you chose.\n\nBecause the model receives text, Office files work with every provider and model, including ones with no document API (OpenAI, Ollama, Mistral, and so on). The model sees what the extractors can read: text, tables, and speaker notes. It does not see images, charts, or shapes that carry no text.\n\nSupported File Types\n\n| Format | Extension | MIME Type | What the model receives |\n| --------------------- | --------- | --------------------------------------------------------------------------- | ------------------------------------------------------- |\n| Word Document | .docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | Markdown (headings, lists, tables) and a metrics line |\n| PowerPoint | .pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation | Text of every slide, table rows, and each slide's notes |\n| Excel Spreadsheet | .xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | Every sheet (or one you name) in the format you choose |\n\nThe same three routes also carry OpenDocument text, spreadsheet and presentation files (.odt, .ods, .odp) and RTF (.rtf), which are read as plain text.\n\nLegacy binary formats (.doc, .xls, .ppt) are recognised but not parsed: the readers only understand the Office Open XML packages above. The request still succeeds, and the model receives a short note saying the file was attached but its content could not be extracted. Re-save such files as .docx, .xlsx, or .pptx first.\n\nQuick Start\n\nSDK Usage\n\nCLI Usage\n\nThe CLI has no flags for officeOptions; choosing a sheet or a spreadsheet format is an SDK feature (see below).\n\nWhat the Model Receives\n\nDOCX structure and metrics\n\nA .docx is converted to Markdown before it reaches the model, so headings, ordered and unordered lists, and tables survive as structure rather than being flattened into prose. Each document is prefixed with a metrics line:\n\nPPTX slides, tables, and speaker notes\n\nA .pptx is read slide by slide, in slide-number order. Each slide's text comes first, then its speaker notes on a **Speaker notes:** line:\nText boxes and titles come out one text run per line, with XML character references decoded (&amp; is shown as &, &lt; as <, &#169; as the copyright sign).\nA table row becomes one line with the cells joined by |. An empty cell keeps its separator, so the cells of a row can still be counted (Q3 | | has three, | Open | has three), and a cell with several paragraphs or line breaks is joined onto the one line with spaces. A row with no text at all is dropped. A literal | in cell text is not escaped, so it reads like a separator.\nSpeaker notes are always included. There is no option to turn them off.\nA slide with no text and no notes is left out. A deck of nothing but pictures therefore arrives as the Presentation: N slide(s) header alone.\nPictures, charts, SmartArt, and shapes that hold no text contribute nothing.\n\nSelecting and formatting spreadsheet data\n\nNaming a sheet that does not exist does not silently fall back to every sheet. The prompt reports the miss and lists the available sheet names.\n\nOptions\n\nofficeOptions is accepted by generate() and stream(). Two fields are read, both by the spreadsheet reader:\n\n| Option | Type | Default | Effect |\n| ------------- | ---------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| sheetName | string | all | Restrict the workbook to the named sheet. An unknown name is reported in the prompt together with the sheets that do exist. ","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"","lvl3":""}},
3520
3525
  {"objectID":"20c3155d2c1f39db07b8ffb72304d589cb601d27d43fb6f6f81ec8e83b5af4f2","title":"Office Documents Support","url":"/docs/features/office-documents#office-documents-support","content":"NeuroLink reads Word, Excel, and PowerPoint files itself and puts their content into the prompt as text. Attach them through the same input.files array (or --file flag) that carries every other file type; there is no separate Office input field and no provider restriction.","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Office Documents Support","lvl3":""}},
@@ -3740,7 +3745,7 @@
3740
3745
  {"objectID":"439bf40cab58d82bc1488de91f2152f91d3ff65586aaa4616577f8bdae040075","title":"1. The shape of the problem","url":"/docs/features/proxy-cli-onboarding#1-the-shape-of-the-problem","content":"The proxy is a Hono app bound to 127.0.0.1:55669 by default (proxy.ts:3864,\n:4532, :5590; port default 55669 at :4536). It terminates a CLI's own OAuth\ntoken, swaps in one from a pooled account, forwards upstream, and relays SSE back.\n\nIt exposes three wire surfaces:\n\n| Door | Factory | Upstream |\n| ----------------------------------- | ------------------------------------------------------- | --------------------------------------- |\n| POST /v1/messages | createClaudeProxyRoutes (claudeProxyRoutes.ts:8316) | api.anthropic.com |\n| POST /v1/chat/completions | createOpenAIProxyRoutes (openaiProxyRoutes.ts:351) | translation engine / Anthropic loopback |\n| POST /backend-api/codex/responses | createCodexProxyRoutes (codexProxyRoutes.ts:568) | chatgpt.com/backend-api |\n\nDispatch is by URL path. A CLI is \"onboarded\" by writing _that CLI's own config\nfile_ so it points at the right door. Three such writers exist, hand-authored, sharing\nno abstraction:\n\n| CLI | Writer | Restore | Target |\n| ----------- | --------------------------------------------- | --------------------------------------- | ---------------------------------- |\n| Claude Code | setClaudeProxySettings proxy.ts:625-652 | clearClaudeProxySettings :654 | ~/.claude/settings.json (:248) |\n| OpenCode | setOpenCodeProxySettings proxy.ts:734-784 | clearOpenCodeProxySettings :786-843 | opencode.json (:722) |\n| Codex | setCodexProxySettings proxy.ts:922-994 | clearCodexProxySettings :996 | ~/.codex/config.toml (:857) |\n\n/v1/chat/completions is the important door and nothing is pointed at it.","hierarchy":{"lvl0":"Features","lvl1":"Onboarding a New AI Coding CLI onto the NeuroLink Proxy","lvl2":"1. The shape of the problem","lvl3":""}},
3741
3746
  {"objectID":"c1678ee68815daa3446f700aafd2ef5a7536ca51886ffbeb8e8989d38fbf0b1b","title":"Coverage, verified on this machine","url":"/docs/features/proxy-cli-onboarding#coverage-verified-on-this-machine","content":"| CLI | Installed | Verdict | Mechanism |\n| ------------ | ----------------------------- | ------------------------- | -------------------------------------------------------------- |\n| Claude Code | yes | live | ANTHROPIC_BASE_URL |\n| Codex | yes | live | config.toml base_url + wire_api=\"responses\" |\n| OpenCode | 1.3.13 | live | provider.neurolink in opencode.json (fixed: #1366/#1367) |\n| Qwen Code | @qwen-code/qwen-code@0.17.0 | live | security.auth.baseUrl โ†’ /v1/chat/completions |\n| Copilot CLI | @github/copilot@1.0.56 | live | COPILOT_PROVIDER_* via a sourceable env script |\n| Hermes Agent | no | easy (unverified on disk) | ANTHROPIC_BASE_URL / OPENAI_BASE_URL |\n| Gemini CLI | @google/gemini-cli@0.53.0 | moderate | GOOGLE_GEMINI_BASE_URL, API-key mode only |\n| Amp | 0.0.1780291930-gf6d536 | hard | AMP_URL honoured, but fronts a proprietary backend โ€” see ยง7c |\n| Cursor | cursor-agent 2026.05.28 | refuted | env vars are dead code โ€” proven by live test |\n| Antigravity | 1.107.0 | hard | proprietary Cascade protobuf |\n| Grok CLI | no | unconfirmed | not installed |\n| Kiro CLI | no | hard | fixed AWS hosts, OAuth device flow |\n\nNear-term: 2 live โ†’ 6 โ€” five of those are now live (Claude Code, Codex,","hierarchy":{"lvl0":"Features","lvl1":"Onboarding a New AI Coding CLI onto the NeuroLink Proxy","lvl2":"Coverage, verified on this machine","lvl3":""}},
3742
3747
  {"objectID":"d002685bad412b4ea546cccc1ebd7441570e08708eb3fb3d8a6ead1898f5edb4","title":"2. Corrections to the v11.2.2 audit","url":"/docs/features/proxy-cli-onboarding#2-corrections-to-the-v1122-audit","content":"The earlier audit's structural claims hold. Five of its specific claims do not.","hierarchy":{"lvl0":"Features","lvl1":"Onboarding a New AI Coding CLI onto the NeuroLink Proxy","lvl2":"2. Corrections to the v11.2.2 audit","lvl3":""}},
3743
- {"objectID":"078d30f3c31c850d03257e95c48d42c113019b6fd26ccf46bb076eb1755cd92f","title":"2.1 The cost defect is attributed to the wrong route โ€” and the failure mode is different","url":"/docs/features/proxy-cli-onboarding#21-the-cost-defect-is-attributed-to-the-wrong-route-and-the-failure-mode-is-different","content":"Claimed: proxyTracer.ts:497 and :826 hard-code calculateCost(\"anthropic\", โ€ฆ),\nso Codex traffic to chatgpt.com is costed against Anthropic's price table.\n\nActually:\nThere are four hard-coded \"anthropic\" sites, not two: proxyTracer.ts:497,\n :826, :874 (all calculateCost) and :883\n (the recordAIRequest metric call).\ncodexProxyRoutes.ts never imports ProxyTracer and never calls\n calculateCost. Its logRequest() object literal (:322-339) has no token keys\n at all, and it never parses usage out of the SSE stream. Codex traffic is not\n mis-priced โ€” it is entirely unaccounted. Fixing it starts with parsing usage,\n not with the pricing call.\nThe hard-coded provider actually mis-prices /v1/chat/completions, which routes\n through ModelRouter to any provider.\nThe failure mode is usually $0, not a wrong number. findRates returns\n undefined when nothing matches, and calculateCost then returns 0\n (pricing.ts:755-758). The anthropic table (pricing.ts:26-161) has no\n _default sentinel โ€” the first one is at :406. So findRates(\"anthropic\",\n\"gpt-4o\") โ†’ undefined โ†’ $0. Real mis-pricing only happens when a\n Claude-named model is routed elsewhere (e.g. a claude-* alias mapped to Vertex\n Gemini), which prices Gemini traffic at Sonnet rates.\nCompounding it: ProxyTracer.model is private readonly (proxyTracer.ts:248),\n fixed at construction to the model the client asked for.\n setModelSubstitution() only writes span attributes. So even with a correct\n provider string, cost is computed against the requested model, not the served one.\n\nGood news: pricing.ts already ships 18 provider tables including openai with a\n\"gpt-5.1-codex\" entry (:162+). For the /v1/chat/completions path this is a\nparameter change, not new pricing data.","hierarchy":{"lvl0":"Features","lvl1":"Onboarding a New AI Coding CLI onto the NeuroLink Proxy","lvl2":"2.1 The cost defect is attributed to the wrong route โ€” and the failure mode is different","lvl3":""}},
3748
+ {"objectID":"078d30f3c31c850d03257e95c48d42c113019b6fd26ccf46bb076eb1755cd92f","title":"2.1 The cost defect is attributed to the wrong route โ€” and the failure mode is different","url":"/docs/features/proxy-cli-onboarding#21-the-cost-defect-is-attributed-to-the-wrong-route-and-the-failure-mode-is-different","content":"Claimed: proxyTracer.ts:497 and :826 hard-code calculateCost(\"anthropic\", โ€ฆ),\nso Codex traffic to chatgpt.com is costed against Anthropic's price table.\n\nActually:\nThere are four hard-coded \"anthropic\" sites, not two: proxyTracer.ts:497,\n :826, :874 (all calculateCost) and :883\n (the recordAIRequest metric call, which feeds ai_requests_total; the proxy\n also counts requests with its own proxy_requests_total counter).\ncodexProxyRoutes.ts never imports ProxyTracer and never calls\n calculateCost. Its logRequest() object literal (:322-339) has no token keys\n at all, and it never parses usage out of the SSE stream. Codex traffic is not\n mis-priced โ€” it is entirely unaccounted. Fixing it starts with parsing usage,\n not with the pricing call.\nThe hard-coded provider actually mis-prices /v1/chat/completions, which routes\n through ModelRouter to any provider.\nThe failure mode is usually $0, not a wrong number. findRates returns\n undefined when nothing matches, and calculateCost then returns 0\n (pricing.ts:755-758). The anthropic table (pricing.ts:26-161) has no\n _default sentinel โ€” the first one is at :406. So findRates(\"anthropic\",\n\"gpt-4o\") โ†’ undefined โ†’ $0. Real mis-pricing only happens when a\n Claude-named model is routed elsewhere (e.g. a claude-* alias mapped to Vertex\n Gemini), which prices Gemini traffic at Sonnet rates.\nCompounding it: ProxyTracer.model is private readonly (proxyTracer.ts:248),\n fixed at construction to the model the client asked for.\n setModelSubstitution() only writes span attributes. So even with a correct\n provider string, cost is computed against the requested model, not the served one.\n\nGood news: pricing.ts already ships 18 provider tables including openai with a\n\"gpt-5.1-codex\" entry (:162+). For the /v1/chat/completions path this is a\nparameter change, not new pricing data.","hierarchy":{"lvl0":"Features","lvl1":"Onboarding a New AI Coding CLI onto the NeuroLink Proxy","lvl2":"2.1 The cost defect is attributed to the wrong route โ€” and the failure mode is different","lvl3":""}},
3744
3749
  {"objectID":"0e958f52839eaf1faa4147d73bb91f8ed4ce2058629b7169b3ac3d59cae3dc30","title":"2.2 \"Nothing identifies the calling CLI at request time\" is false โ€” and this is an onboarding hazard","url":"/docs/features/proxy-cli-onboarding#22-nothing-identifies-the-calling-cli-at-request-time-is-false-and-this-is-an-onboarding-hazard","content":"Claimed: detectClientApp() (proxyTracer.ts:226-235) is the only User-Agent\nsniff and it only labels trace spans.\n\nActually: there is a second sniff that drives real request behaviour.\nisLikelyClaudeClient() (claudeProxyRoutes.ts:2341-2350) tests\nheaders[\"user-agent\"]?.startsWith(\"claude-cli/\") (among other signals), and its\nresult gates:\nwhich OAuth beta header set is sent โ€” CLAUDE_CODE_OAUTH_BETAS vs\n NON_CLAUDE_OAUTH_BETAS (:7221-7236);\nwhether polyfillOAuthBody() preserves the client's own system-prompt / agent\n identity blocks verbatim or strips and relocates them;\nmaybeRefreshClaudeSnapshot() (:2398).\n\nThis matters directly for CLI #4 and #5. Any non-Claude CLI pointed at\n/v1/messages via ANTHROPIC_BASE_URL โ€” Hermes is exactly this case โ€” takes the\nNON_CLAUDE_OAUTH_BETAS branch and a different system-prompt path. That is probably\ncorrect behaviour, but it is behaviour, and it must be tested rather than assumed.","hierarchy":{"lvl0":"Features","lvl1":"Onboarding a New AI Coding CLI onto the NeuroLink Proxy","lvl2":"2.2 \"Nothing identifies the calling CLI at request time\" is false โ€” and this is an onboarding hazard","lvl3":""}},
3745
3750
  {"objectID":"8997754a7d1d3258d747dd7590b34765a6d34d8c36e642f970247618d869e78e","title":"2.3 \"314,116 requests and no ledger\" is overstated","url":"/docs/features/proxy-cli-onboarding#23-314116-requests-and-no-ledger-is-overstated","content":"usageStats.ts (1,478 lines, zero occurrences of inputTokens / outputTokens /\ntotalTokens / cost) is confirmed. But neurolink proxy analyze is a real, wired\ncommand (src/cli/parser.ts:260) and proxyAnalysis.ts:437-527 reads the same\nproxy-YYYY-MM-DD.jsonl files requestLogger.ts writes and sums inputTokens,\ncacheReadTokens and cacheCreationTokens per window.\n\nThe gap is narrower and more specific than \"no ledger\": outputTokens and cost\nare absent from proxyAnalysis.ts entirely, and nothing aggregates the Codex engine\nat all (ยง2.1).","hierarchy":{"lvl0":"Features","lvl1":"Onboarding a New AI Coding CLI onto the NeuroLink Proxy","lvl2":"2.3 \"314,116 requests and no ledger\" is overstated","lvl3":""}},
3746
3751
  {"objectID":"081076044aa5320358b560458b66d5725f4a5776d94e808e16b6961541789dcc","title":"2.4 OpenCode is two bugs, not a one-line fix","url":"/docs/features/proxy-cli-onboarding#24-opencode-is-two-bugs-not-a-one-line-fix","content":"The path bug is confirmed and is genuinely one line โ€” but a path-only fix leaves a\nsecond, independent bug in place.\ngetOpenCodeConfigDir() (proxy.ts:711-720) returns\n ~/Library/Application Support/opencode on darwin. The installed OpenCode 1.3.13\n binary embeds the unmodified xdg-basedir npm package\n (XDG_CONFIG_HOME || join(homedir, \".config\")) with no platform branch at all.\n Empirically: ~/.config/opencode/opencode.json exists (2,640 bytes, holds a working\n custom provider that opencode models confirms is loaded);\n ~/Library/Application Support/opencode does not exist. Deleting the darwin\n branch is the whole fix.\nThe second bug survives that. setOpenCodeProxySettings returns Promise<void>\n (:734) and the call sites print โœ“ Auto-configured OpenCode settings\n unconditionally (:3491-3493, :5403-5405). setCodexProxySettings returns\n Promise<boolean> and its โœ“ is gated on it (:3502, :5413). So even after the\n path fix, a user without OpenCode installed still gets a success message for work\n that did not happen.\n\nOrigin of the mistake: the OpenCode binary does contain the literal\n/Library/Application Support/opencode โ€” as systemManagedConfigDir(), an MDM /\nenterprise policy tier at the filesystem root (no $HOME), paired with\n/etc/opencode and %ProgramData%\\opencode. Someone found that string and read it as\nthe per-user path.\n\nIt is also baked into our own docs. docs/features/opencode-proxy-support.md:67\nasserts the macOS path is ~/Library/Application Support/opencode/opencode.json,\nwhile :313-323 of the same file shows resolution code with no darwin branch that\nwould compute ~/.config/opencode. The doc contradicts itself, and it is titled\n\"Implemented & Verified\".\n\nHow it escaped verification: ยง11's E2E playbook runs the dev proxy with --dev,\nwhich by its own option description performs \"no client auto-configuration\"\n(proxy.ts:3910-3917), and hand-copies a fixture to\n/tmp/opencode-proxy-test/opencode.json. The writer is never executed.","hierarchy":{"lvl0":"Features","lvl1":"Onboarding a New AI Coding CLI onto the NeuroLink Proxy","lvl2":"2.4 OpenCode is two bugs, not a one-line fix","lvl3":""}},
@@ -8559,19 +8564,19 @@
8559
8564
  {"objectID":"ce9a217a697d608230d795d2d3fdaf830ae8e403667bbab2280a9fd82c0c3702","title":"3. Authentication Failures","url":"/docs/observability/provider-status#3-authentication-failures","content":"Solution: Verify and update the API key environment variable (OPENAI_API_KEY in this case).","hierarchy":{"lvl0":"Observability","lvl1":"Provider Status Monitoring and Health Management","lvl2":"3. Authentication Failures","lvl3":""}},
8560
8565
  {"objectID":"baa7998e7800a556b1f9c93a93974de4b5796fdb60415881e25a1d033ca1706d","title":"Debugging Commands","url":"/docs/observability/provider-status#debugging-commands","content":"Debugging Commands","hierarchy":{"lvl0":"Observability","lvl1":"Provider Status Monitoring and Health Management","lvl2":"Debugging Commands","lvl3":""}},
8561
8566
  {"objectID":"e938db7055d0bdcf6a8aae0a76a7fc8aa1dc9496409d6c457136e5765a1cc5cc","title":"Conclusion","url":"/docs/observability/provider-status#conclusion","content":"NeuroLink's Provider Status Monitoring system provides enterprise-grade health management for AI provider infrastructure. With real-time monitoring, intelligent recommendations, and comprehensive analytics, it ensures optimal provider selection and proactive issue resolution.\n\nKey benefits include:\nProactive Issue Detection: Identify problems before they impact production\nIntelligent Provider Selection: Automatic optimization for performance and cost\nOperational Excellence: Complete visibility into AI infrastructure health\nDeveloper Productivity: Actionable recommendations reduce debugging time\n\nThis system transforms AI provider management from reactive troubleshooting to proactive optimization, ensuring reliable and efficient AI operations at enterprise scale.","hierarchy":{"lvl0":"Observability","lvl1":"Provider Status Monitoring and Health Management","lvl2":"Conclusion","lvl3":""}},
8562
- {"objectID":"3abc8984be035e0f7eaeeee65f8d8f2fab3f41ecc96355a2456d62170d25c333","title":"๐Ÿ“Š Enterprise Telemetry Guide","url":"/docs/observability/telemetry","content":"๐Ÿ“Š Enterprise Telemetry Guide\n\nAdvanced OpenTelemetry Integration for NeuroLink\n\n๐Ÿ“‹ Overview\n\nNeuroLink includes optional OpenTelemetry integration for enterprise monitoring and observability. The telemetry system provides comprehensive insights into AI operations, performance metrics, and system health with zero overhead when disabled.\n\n๐Ÿš€ Key Features\nโœ… Zero Overhead by Default - Telemetry disabled unless explicitly configured\n๐Ÿค– AI Operation Tracking - Monitor text generation, token usage, costs, and response times\n๐Ÿ”ง MCP Tool Monitoring - Track tool calls, execution time, and success rates\n๐Ÿ“ˆ Performance Metrics - Response times, error rates, throughput monitoring\n๐Ÿ” Distributed Tracing - Full request tracing across AI providers and services\n๐Ÿ“Š Custom Dashboards - Grafana, Jaeger, and Prometheus integration\n๐ŸŽฏ Production Ready - Enterprise-grade monitoring for production deployments\n\nLangfuse Integration\n\nSet LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY and NeuroLink exports every AI operation to Langfuse. Pass observability: { langfuse: false } to disable. Full input and output are captured by default; use mask for sensitive data.\n\nIf your application already registers an OpenTelemetry TracerProvider, NeuroLink creates no provider but registers its Langfuse exporter when keys are configured; add createExporterSpanProcessor() to your provider (recommended), or add new LangfuseSpanProcessor() from @langfuse/otel with its own keys (no custom filter is needed because every NeuroLink observation span carries gen_ai.observation.type and infrastructure spans are not exported; NeuroLink's mask, chunking and retry do not apply to it). In serverless, call flushOpenTelemetry() before exit.\n\nThe full guide, including the migration table for removed fields and exports, is Observability.\n\nBasic Setup\n\nEnvironment Variables\n\n| Variable | Description | Default |\n| ----------------------------- | ---------------------------------------------------- | -------------- |\n| LANGFUSE_PUBLIC_KEY | Langfuse public key; with the secret key, enables it | - |\n| LANGFUSE_SECRET_KEY | Langfuse secret key | - |\n| LANGFUSE_BASE_URL | Langfuse host | Langfuse Cloud |\n| OTEL_EXPORTER_OTLP_ENDPOINT | OTLP collector endpoint for traces and metrics | - |\n| OTEL_SERVICE_NAME | Service name | neurolink-ai |\n\n๐Ÿ”ญ Proxy Telemetry (OTLP Triple-Signal Export)\n\nWhen running the NeuroLink proxy (neurolink proxy start), OpenTelemetry is automatically initialized. If OTEL_EXPORTER_OTLP_ENDPOINT is set, the proxy exports three signal types via OTLP HTTP:\n\n| Signal | Endpoint | What it captures |\n| ------- | ----------------------------------------- | ------------------------------------------------------------------ |\n| Traces | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/traces | Per-request spans: receive โ†’ account selection โ†’ upstream โ†’ stream |\n| Metrics | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/metrics | Request counters, latency histograms, token usage gauges |\n| Logs | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/logs | Structured request log records with traceId/spanId correlation |\n\nConfiguration: Set OTEL_EXPORTER_OTLP_ENDPOINT (e.g., http://localhost:4318) before starting the proxy. The proxy defaults to service.name=neurolink-proxy.\n\nTrace correlation: Every JSONL request log entry includes traceId and spanId fields, enabling cross-signal correlation in backends like Jaeger, Grafana Tempo, or OpenObserve.\n\nCaller trace linkage: When a calling SDK already has an active trace, NeuroLink forwards W3C traceparent/tracestate headers and x-neurolink-session-id / x-neurolink-user-id / x-neurolink-conversation-id headers into the proxy so proxy spans can attach to the caller trace and preserve session-level attribution.\n\nProvider reuse: If a global TracerProvider is already registered (e.g., by the host application), NeuroLink registers nothing and uses it, avoiding \"already registered\" errors.\n\nOpenObserve dashboard: The maintained proxy dashboard definition lives in docs/assets/dashboards/neurolink-proxy-observability-dashboard.json. For how to read that dashboard and which streams it should use, see Claude Proxy Observability.\n\nLocal OpenObserve Setup For The Proxy\n\nFor a new local setup, use the repo-owned files in scripts/observability/ so the proxy dashboard does not depend on the Curator repo.\nOptional: copy scripts/observability/proxy-observability.env.example to scripts/observability/proxy-observability.env if the default ports or credentials clash with your machine.\nStart OpenObserve, start the OTEL collector, and import the dashboard:\nStart the proxy with the collector endpoint printed by the setup script. W","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"","lvl3":""}},
8567
+ {"objectID":"3abc8984be035e0f7eaeeee65f8d8f2fab3f41ecc96355a2456d62170d25c333","title":"๐Ÿ“Š Enterprise Telemetry Guide","url":"/docs/observability/telemetry","content":"๐Ÿ“Š Enterprise Telemetry Guide\n\nAdvanced OpenTelemetry Integration for NeuroLink\n\n๐Ÿ“‹ Overview\n\nNeuroLink includes optional OpenTelemetry integration for enterprise monitoring and observability. The telemetry system provides insights into AI operations, performance metrics, and system health. Nothing is exported until Langfuse keys or an OTLP endpoint are configured, although importing the main package entry, or the ./rag, ./workflow, ./evaluation, ./processors or ./autoresearch entry, already loads the OpenTelemetry SDK (see Initialisation).\n\n๐Ÿš€ Key Features\nโœ… Off by Default - Nothing is exported unless explicitly configured\n๐Ÿค– AI Operation Tracking - Monitor text generation, token usage, costs, and response times\n๐Ÿ”ง MCP Tool Monitoring - Track tool calls, execution time, and success rates\n๐Ÿ“ˆ Performance Metrics - Response times and throughput; error counts for proxied traffic\n๐Ÿ” Distributed Tracing - Full request tracing across AI providers and services\n๐Ÿ“Š Custom Dashboards - Grafana, Jaeger, and Prometheus integration\n๐ŸŽฏ Production Ready - Enterprise-grade monitoring for production deployments\n\nLangfuse Integration\n\nSet LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY and NeuroLink exports every AI operation to Langfuse. Pass observability: { langfuse: false } to disable. Full input and output are captured by default; use mask for sensitive data.\n\nIf your application already registers an OpenTelemetry TracerProvider, NeuroLink creates no provider (and ignores OTEL_EXPORTER_OTLP_ENDPOINT, so add an OTLP exporter to your own providers) but registers its Langfuse exporter when keys are configured; add createExporterSpanProcessor() to your provider (recommended), or add new LangfuseSpanProcessor() from @langfuse/otel with its own keys (no custom filter is needed because every NeuroLink observation span carries gen_ai.observation.type and infrastructure spans are not exported; NeuroLink's mask, chunking and retry do not apply to it). In serverless, call flushOpenTelemetry() before exit.\n\nThe full guide, including the migration table for removed fields and exports, is Observability.\n\nBasic Setup\n\nEnvironment Variables\n\n| Variable | Description | Default |\n| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- |\n| LANGFUSE_PUBLIC_KEY | Langfuse public key; with the secret key, enables it | - |\n| LANGFUSE_SECRET_KEY | Langfuse secret key | - |\n| LANGFUSE_BASE_URL | Langfuse host | Langfuse Cloud |\n| LANGFUSE_ENABLED | false, 0, off or no keeps Langfuse off while the keys are in the environment, in the proxy and for options that leave the keys there; langfuse: true, enabled: true or keys passed in code win over it | - |\n| OTEL_EXPORTER_OTLP_ENDPOINT | OTLP collector base URL for traces and metrics, only when NeuroLink owns the tracer provider (ignored beside a host provider) | - |\n| OTEL_SERVICE_NAME | Service name | ai-service (neurolink-proxy for the proxy) |\n| NEUROLINK_TELEMETRY_ENABLED | true records NeuroLink's metrics whatever else is set; a registered MeterProvider or an OTLP endpoint already turns them on | - |\n\nThe metric names, labels and when each one records are in Metrics.\n\n๐Ÿ”ญ Proxy Telemetry (OTLP Triple-Signal Export)\n\nWhen running the NeuroLink proxy (neurolink proxy start), OpenTelemetry is automatically initialized. If O","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"","lvl3":""}},
8563
8568
  {"objectID":"5bca8b67352628f2ead424686c839896c0c9191fbe9bc9ce9f2b4f1a34fda7b4","title":"๐Ÿ“Š Enterprise Telemetry Guide","url":"/docs/observability/telemetry#-enterprise-telemetry-guide","content":"Advanced OpenTelemetry Integration for NeuroLink","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"๐Ÿ“Š Enterprise Telemetry Guide","lvl3":""}},
8564
- {"objectID":"01dd892f6d8681fb916960f5512d67fc1772f13906d80f188160040a7d13352b","title":"๐Ÿ“‹ Overview","url":"/docs/observability/telemetry#-overview","content":"NeuroLink includes optional OpenTelemetry integration for enterprise monitoring and observability. The telemetry system provides comprehensive insights into AI operations, performance metrics, and system health with zero overhead when disabled.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"๐Ÿ“‹ Overview","lvl3":""}},
8565
- {"objectID":"1560608dd386dd63e33cf859910b21ae292c6d2b3a5e5c20c6b758b509e91665","title":"๐Ÿš€ Key Features","url":"/docs/observability/telemetry#-key-features","content":"โœ… Zero Overhead by Default - Telemetry disabled unless explicitly configured\n๐Ÿค– AI Operation Tracking - Monitor text generation, token usage, costs, and response times\n๐Ÿ”ง MCP Tool Monitoring - Track tool calls, execution time, and success rates\n๐Ÿ“ˆ Performance Metrics - Response times, error rates, throughput monitoring\n๐Ÿ” Distributed Tracing - Full request tracing across AI providers and services\n๐Ÿ“Š Custom Dashboards - Grafana, Jaeger, and Prometheus integration\n๐ŸŽฏ Production Ready - Enterprise-grade monitoring for production deployments","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"๐Ÿš€ Key Features","lvl3":""}},
8566
- {"objectID":"94eeb9415209595c93b61952b4d4a2bca198440a69ba4ac78dbba15557cfb2e9","title":"Langfuse Integration","url":"/docs/observability/telemetry#langfuse-integration","content":"Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY and NeuroLink exports every AI operation to Langfuse. Pass observability: { langfuse: false } to disable. Full input and output are captured by default; use mask for sensitive data.\n\nIf your application already registers an OpenTelemetry TracerProvider, NeuroLink creates no provider but registers its Langfuse exporter when keys are configured; add createExporterSpanProcessor() to your provider (recommended), or add new LangfuseSpanProcessor() from @langfuse/otel with its own keys (no custom filter is needed because every NeuroLink observation span carries gen_ai.observation.type and infrastructure spans are not exported; NeuroLink's mask, chunking and retry do not apply to it). In serverless, call flushOpenTelemetry() before exit.\n\nThe full guide, including the migration table for removed fields and exports, is Observability.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Langfuse Integration","lvl3":""}},
8567
- {"objectID":"3043ef1d5fa3c9cdc3f25bb1d2c4d5f3e650160b54e2b3af630105e58e61f560","title":"Environment Variables","url":"/docs/observability/telemetry#environment-variables","content":"| Variable | Description | Default |\n| ----------------------------- | ---------------------------------------------------- | -------------- |\n| LANGFUSE_PUBLIC_KEY | Langfuse public key; with the secret key, enables it | - |\n| LANGFUSE_SECRET_KEY | Langfuse secret key | - |\n| LANGFUSE_BASE_URL | Langfuse host | Langfuse Cloud |\n| OTEL_EXPORTER_OTLP_ENDPOINT | OTLP collector endpoint for traces and metrics | - |\n| OTEL_SERVICE_NAME | Service name | neurolink-ai |","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Environment Variables","lvl3":""}},
8568
- {"objectID":"1196e9c4ad10ab88bcc4ee532ec941b2eebfe19b09eee44dafb9ee2d0b538e0b","title":"๐Ÿ”ญ Proxy Telemetry (OTLP Triple-Signal Export)","url":"/docs/observability/telemetry#-proxy-telemetry-otlp-triple-signal-export","content":"When running the NeuroLink proxy (neurolink proxy start), OpenTelemetry is automatically initialized. If OTEL_EXPORTER_OTLP_ENDPOINT is set, the proxy exports three signal types via OTLP HTTP:\n\n| Signal | Endpoint | What it captures |\n| ------- | ----------------------------------------- | ------------------------------------------------------------------ |\n| Traces | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/traces | Per-request spans: receive โ†’ account selection โ†’ upstream โ†’ stream |\n| Metrics | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/metrics | Request counters, latency histograms, token usage gauges |\n| Logs | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/logs | Structured request log records with traceId/spanId correlation |\n\nConfiguration: Set OTEL_EXPORTER_OTLP_ENDPOINT (e.g., http://localhost:4318) before starting the proxy. The proxy defaults to service.name=neurolink-proxy.\n\nTrace correlation: Every JSONL request log entry includes traceId and spanId fields, enabling cross-signal correlation in backends like Jaeger, Grafana Tempo, or OpenObserve.\n\nCaller trace linkage: When a calling SDK already has an active trace, NeuroLink forwards W3C traceparent/tracestate headers and x-neurolink-session-id / x-neurolink-user-id / x-neurolink-conversation-id headers into the proxy so proxy spans can attach to the caller trace and preserve session-level attribution.\n\nProvider reuse: If a global TracerProvider is already registered (e.g., by the host application), NeuroLink registers nothing and uses it, avoiding \"already registered\" errors.\n\nOpenObserve dashboard: The maintained proxy dashboard definition lives in docs/assets/dashboards/neurolink-proxy-observability-dashboard.json. For how to read that dashboard and which streams it should use, see Claude Proxy Observability.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"๐Ÿ”ญ Proxy Telemetry (OTLP Triple-Signal Export)","lvl3":""}},
8569
+ {"objectID":"01dd892f6d8681fb916960f5512d67fc1772f13906d80f188160040a7d13352b","title":"๐Ÿ“‹ Overview","url":"/docs/observability/telemetry#-overview","content":"NeuroLink includes optional OpenTelemetry integration for enterprise monitoring and observability. The telemetry system provides insights into AI operations, performance metrics, and system health. Nothing is exported until Langfuse keys or an OTLP endpoint are configured, although importing the main package entry, or the ./rag, ./workflow, ./evaluation, ./processors or ./autoresearch entry, already loads the OpenTelemetry SDK (see Initialisation).","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"๐Ÿ“‹ Overview","lvl3":""}},
8570
+ {"objectID":"1560608dd386dd63e33cf859910b21ae292c6d2b3a5e5c20c6b758b509e91665","title":"๐Ÿš€ Key Features","url":"/docs/observability/telemetry#-key-features","content":"โœ… Off by Default - Nothing is exported unless explicitly configured\n๐Ÿค– AI Operation Tracking - Monitor text generation, token usage, costs, and response times\n๐Ÿ”ง MCP Tool Monitoring - Track tool calls, execution time, and success rates\n๐Ÿ“ˆ Performance Metrics - Response times and throughput; error counts for proxied traffic\n๐Ÿ” Distributed Tracing - Full request tracing across AI providers and services\n๐Ÿ“Š Custom Dashboards - Grafana, Jaeger, and Prometheus integration\n๐ŸŽฏ Production Ready - Enterprise-grade monitoring for production deployments","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"๐Ÿš€ Key Features","lvl3":""}},
8571
+ {"objectID":"94eeb9415209595c93b61952b4d4a2bca198440a69ba4ac78dbba15557cfb2e9","title":"Langfuse Integration","url":"/docs/observability/telemetry#langfuse-integration","content":"Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY and NeuroLink exports every AI operation to Langfuse. Pass observability: { langfuse: false } to disable. Full input and output are captured by default; use mask for sensitive data.\n\nIf your application already registers an OpenTelemetry TracerProvider, NeuroLink creates no provider (and ignores OTEL_EXPORTER_OTLP_ENDPOINT, so add an OTLP exporter to your own providers) but registers its Langfuse exporter when keys are configured; add createExporterSpanProcessor() to your provider (recommended), or add new LangfuseSpanProcessor() from @langfuse/otel with its own keys (no custom filter is needed because every NeuroLink observation span carries gen_ai.observation.type and infrastructure spans are not exported; NeuroLink's mask, chunking and retry do not apply to it). In serverless, call flushOpenTelemetry() before exit.\n\nThe full guide, including the migration table for removed fields and exports, is Observability.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Langfuse Integration","lvl3":""}},
8572
+ {"objectID":"3043ef1d5fa3c9cdc3f25bb1d2c4d5f3e650160b54e2b3af630105e58e61f560","title":"Environment Variables","url":"/docs/observability/telemetry#environment-variables","content":"| Variable | Description | Default |\n| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- |\n| LANGFUSE_PUBLIC_KEY | Langfuse public key; with the secret key, enables it | - |\n| LANGFUSE_SECRET_KEY | Langfuse secret key | - |\n| LANGFUSE_BASE_URL | Langfuse host | Langfuse Cloud |\n| LANGFUSE_ENABLED | false, 0, off or no keeps Langfuse off while the keys are in the environment, in the proxy and for options that leave the keys there; langfuse: true, enabled: true or keys passed in code win over it | - |\n| OTEL_EXPORTER_OTLP_ENDPOINT | OTLP collector base URL for traces and metrics, only when NeuroLink owns the tracer provider (ignored beside a host provider) |","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Environment Variables","lvl3":""}},
8573
+ {"objectID":"1196e9c4ad10ab88bcc4ee532ec941b2eebfe19b09eee44dafb9ee2d0b538e0b","title":"๐Ÿ”ญ Proxy Telemetry (OTLP Triple-Signal Export)","url":"/docs/observability/telemetry#-proxy-telemetry-otlp-triple-signal-export","content":"When running the NeuroLink proxy (neurolink proxy start), OpenTelemetry is automatically initialized. If OTEL_EXPORTER_OTLP_ENDPOINT is set, the proxy exports three signal types via OTLP HTTP:\n\n| Signal | Endpoint | What it captures |\n| ------- | ----------------------------------------- | ------------------------------------------------------------------ |\n| Traces | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/traces | Per-request spans: receive โ†’ account selection โ†’ upstream โ†’ stream |\n| Metrics | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/metrics | Request counters, latency histograms, token usage gauges |\n| Logs | $OTEL_EXPORTER_OTLP_ENDPOINT/v1/logs | Structured request log records with traceId/spanId correlation |\n\nConfiguration: Set OTEL_EXPORTER_OTLP_ENDPOINT (e.g., http://localhost:4318) before starting the proxy. The proxy defaults to service.name=neurolink-proxy.\n\nTrace correlation: Every JSONL request log entry includes traceId and spanId fields, enabling cross-signal correlation in backends like Jaeger, Grafana Tempo, or OpenObserve.\n\nCaller trace linkage: When a calling SDK already has an active trace, NeuroLink forwards W3C traceparent/tracestate headers and x-neurolink-session-id / x-neurolink-user-id / x-neurolink-conversation-id headers into the proxy so proxy spans can attach to the caller trace and preserve session-level attribution.\n\nProvider reuse: If a global TracerProvider is already registered (e.g., by the host application), NeuroLink registers nothing and uses it, avoiding \"already registered\" errors. OTEL_EXPORTER_OTLP_ENDPOINT then still receives the proxy's request logs, but not its traces or metrics, which go to the host's providers.\n\nOpenObserve dashboard: The maintained proxy dashboard definition lives in docs/assets/dashboards/neurolink-proxy-observability-dashboard.json.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"๐Ÿ”ญ Proxy Telemetry (OTLP Triple-Signal Export)","lvl3":""}},
8569
8574
  {"objectID":"86edcd4d910e014c6ba3136e3beb5b3bac8acd20d33804cc1f315ce970a43458","title":"Local OpenObserve Setup For The Proxy","url":"/docs/observability/telemetry#local-openobserve-setup-for-the-proxy","content":"For a new local setup, use the repo-owned files in scripts/observability/ so the proxy dashboard does not depend on the Curator repo.\nOptional: copy scripts/observability/proxy-observability.env.example to scripts/observability/proxy-observability.env if the default ports or credentials clash with your machine.\nStart OpenObserve, start the OTEL collector, and import the dashboard:\nStart the proxy with the collector endpoint printed by the setup script. With the defaults, that is:\nOpen the UI at http://localhost:5080 and sign in with the configured OpenObserve credentials.\n\nUseful follow-up commands:\n\nWhen you are working from a local checkout instead of an installed CLI, pnpm run proxy:observability:* provides the same actions as repo shortcuts.\n\nWhat is machine-specific:\nOpenObserve URL, credentials, ports, container names, and volume names\nCompose project name if you intentionally run more than one local stack\nDashboard IDs and owners generated by OpenObserve when the dashboard is imported\n\nThe dashboard import helper strips dashboardId, owner, and created from the checked-in JSON before it calls the OpenObserve API, so those metadata fields do not need manual editing on a fresh machine.\n\nWhat is not machine-specific:\nThe dashboard query logic\nThe active streams neurolink_proxy and proxy_*\nThe proxy OTEL service name neurolink-proxy\nThe proxy log fields used for trace correlation and token analysis","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Local OpenObserve Setup For The Proxy","lvl3":""}},
8570
8575
  {"objectID":"1d93cd70de084c5782b6189b3f415762ac33859e4cc82b99e48b659deed9bfc5","title":"Proxy Log Conventions In OpenObserve","url":"/docs/observability/telemetry#proxy-log-conventions-in-openobserve","content":"Final request-summary rows are exported to the neurolink_proxy log stream and are the rows dashboard request panels should use.\nRaw body captures share that same log stream with event.name=proxy.body_capture, so log-backed request panels should filter to request-summary rows, for example http_method IS NOT NULL.\nPer-upstream-attempt diagnostics stay local in ~/.neurolink/logs/proxy-attempts-*.jsonl; they are useful for debugging retries but are intentionally not part of the main dashboard counts.\nAdditional proxy OTEL metrics may appear when relevant traffic exists, including model-substitution counters and response-body histograms alongside the cache, request, retry, duration, and cost metrics already used by the dashboard.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Proxy Log Conventions In OpenObserve","lvl3":""}},
8571
- {"objectID":"a941e8b4933de87676af26c7c7b094af2f5fed8891ab5253411454ecaade5eb1","title":"Docker Compose with Jaeger","url":"/docs/observability/telemetry#docker-compose-with-jaeger","content":"Docker Compose with Jaeger","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Docker Compose with Jaeger","lvl3":""}},
8572
- {"objectID":"7eb0866565462c132dd3cc9fa7c308f8ee6231bf6daf037b3dab796a9dd41d51","title":"AI Operation Metrics","url":"/docs/observability/telemetry#ai-operation-metrics","content":"Response Time: Time to generate AI responses\nToken Usage: Input/output tokens by provider and model\nCost Tracking: Estimated costs per operation\nError Rates: Failed AI requests by provider\nProvider Performance: Success rates and latency by provider","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"AI Operation Metrics","lvl3":""}},
8573
- {"objectID":"bd59daf6ceed52988c126b5f4b9791a9dd59dc3af55dd54cee5fadadbcfa0a41","title":"Sample Prometheus Queries","url":"/docs/observability/telemetry#sample-prometheus-queries","content":"Sample Prometheus Queries","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Sample Prometheus Queries","lvl3":""}},
8574
- {"objectID":"1901741e79f4b3ed72eae0c8765fd89ceac268e0540844b0ec8367c4a4131dc1","title":"โœ… Quick Setup (5 minutes)","url":"/docs/observability/telemetry#-quick-setup-5-minutes","content":"Set the service name\nStart Jaeger (Local Development)\nConfigure Endpoint\nRun your app\n\n Tracing starts with the first new NeuroLink(); no initialization call is needed.\nView Traces\nOpen http://localhost:16686\nGenerate some AI requests\nSearch for traces in Jaeger UI","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"โœ… Quick Setup (5 minutes)","lvl3":""}},
8576
+ {"objectID":"a941e8b4933de87676af26c7c7b094af2f5fed8891ab5253411454ecaade5eb1","title":"Docker Compose with Jaeger","url":"/docs/observability/telemetry#docker-compose-with-jaeger","content":"OTEL_EXPORTER_OTLP_ENDPOINT is the collector's base URL: NeuroLink appends /v1/traces and /v1/metrics itself. Jaeger stores the traces only, so send the metrics to an OpenTelemetry Collector or another backend that accepts OTLP metrics.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Docker Compose with Jaeger","lvl3":""}},
8577
+ {"objectID":"7eb0866565462c132dd3cc9fa7c308f8ee6231bf6daf037b3dab796a9dd41d51","title":"AI Operation Metrics","url":"/docs/observability/telemetry#ai-operation-metrics","content":"Request volume: ai_requests_total, by provider and model\nResponse time: ai_request_duration_ms, a histogram in milliseconds\nToken usage: ai_tokens_used_total, total tokens per request\nCost tracking: ai_cost_usd_total, for models with known pricing\nMCP tool calls: mcp_tool_calls_total, by tool, success and duration bucket\n\nNeuroLink records no AI error-rate or connection metric: ai_provider_errors_total, connections_total and response_time_ms exist as instruments, but nothing in the package records them, and there is no active-connection gauge. For proxied traffic use the proxy's own metrics, such as proxy_errors_total; see Claude Proxy Observability.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"AI Operation Metrics","lvl3":""}},
8578
+ {"objectID":"bd59daf6ceed52988c126b5f4b9791a9dd59dc3af55dd54cee5fadadbcfa0a41","title":"Sample Prometheus Queries","url":"/docs/observability/telemetry#sample-prometheus-queries","content":"The names below are the metric names NeuroLink creates; through an OTLP-to-Prometheus pipeline the histogram appears as _bucket, _sum and _count series.","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"Sample Prometheus Queries","lvl3":""}},
8579
+ {"objectID":"1901741e79f4b3ed72eae0c8765fd89ceac268e0540844b0ec8367c4a4131dc1","title":"โœ… Quick Setup (5 minutes)","url":"/docs/observability/telemetry#-quick-setup-5-minutes","content":"Set the service name\nStart Jaeger (Local Development)\nConfigure Endpoint (the base URL; NeuroLink appends /v1/traces and /v1/metrics)\nRun your app\n\n No initialization call is needed. Tracing starts at new NeuroLink({ observability }), at the first generate, stream or decide call, or at the first NeuroLink span; a plain new NeuroLink() starts nothing by itself. See Initialisation.\nView Traces\nOpen http://localhost:16686\nGenerate some AI requests\nSearch for traces in Jaeger UI","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"โœ… Quick Setup (5 minutes)","lvl3":""}},
8575
8580
  {"objectID":"790fed94c14846fea8574095e0237dcc2522ba7514f99d03d982c89d1ba6fbbf","title":"๐Ÿ“š Additional Resources","url":"/docs/observability/telemetry#-additional-resources","content":"API Reference - Complete telemetry API documentation\nReal-time Services - WebSocket infrastructure guide\nPerformance Optimization - Optimization strategies\n\nReady for enterprise-grade AI monitoring with NeuroLink! ๐Ÿ“Š","hierarchy":{"lvl0":"Observability","lvl1":"๐Ÿ“Š Enterprise Telemetry Guide","lvl2":"๐Ÿ“š Additional Resources","lvl3":""}},
8576
8581
  {"objectID":"5122caad8c90eaa4c6454b2f4a4185aaee8933ab47a08806fad335486580d4df","title":"Migration Design: Remove AI SDK Google Dependencies","url":"/docs/plans/2026-01-01-remove-ai-sdk-google-design","content":"Migration Design: Remove AI SDK Google Dependencies\n\nDate: 2026-01-01\nBranch: feat/remove-ai-sdk-google\nStatus: Done โ€” implemented in 076b9f4c3 (native @google/genai +\n@anthropic-ai/vertex-sdk). The Success Criteria below are all met.\n\nSummary\n\nRemove Vercel AI SDK wrappers (@ai-sdk/google, @ai-sdk/google-vertex) and migrate to the official unified Google SDK (@google/genai) for all Google AI Studio and Vertex AI Gemini models.\n\nMotivation\nReduce dependencies - Eliminate wrapper layer, use official SDK directly\nFuture-proof - @google-cloud/vertexai is deprecated (EOL June 2025); @google/genai is Google's recommended unified SDK\nConsistency - Native SDK path already exists for Gemini 3 with tools; extend pattern to all models\nBetter feature support - Direct access to thought_signature, extended thinking, and future Gemini features\n\nCurrent State Analysis\n\nDependencies to Remove\n\nDependencies to Keep\n\nProvider Files to Modify\n\n| File | Lines | Current Approach |\n| ------------------------------------- | ----- | ----------------------------------------- |\n| src/lib/providers/googleAiStudio.ts | ~1370 | AI SDK primary, native for Gemini 3+tools |\n| src/lib/providers/googleVertex.ts | ~3285 | AI SDK primary, native for Gemini 3+tools |\n\nArchitecture Design\n\nNew Unified Approach\n\nThe @google/genai SDK supports both authentication modes:\n\nStreaming Architecture\n\nReplace streamText() from AI SDK with native streaming:\n\nMessage Format Transformation\n\n| Vercel AI SDK Format | @google/genai Format |\n| ----------------------------------------------------- | ------------------------------------- |\n| { role: \"user\", content: [{ type: \"text\", text }] } | { role: \"user\", parts: [{ text }] } |\n| { type: \"image\", image: Buffer } | { inlineData: { mimeType, data } } |\n| { type: \"file\", data: Buffer } | { fileData: { mimeType, fileUri } } |\n\nTool Format Transformation\n\n| Vercel AI SDK Tool | @google/genai FunctionDeclaration |\n| -------------------------------------- | --------------------------------------------- |\n| { description, parameters, execute } | { name, description, parametersJsonSchema } |\n\nImplementation Plan\n\nPhase 1: Google AI Studio Provider (googleAiStudio.ts)\n\nStep 1.1: Remove AI SDK imports\n\nStep 1.2: Replace getAISDKModel() with native client\n\nStep 1.3: Convert message building\nAdd new convertToGoogleContents() method\nTransform CoreMessage[] to Content[] (Google format)\nHandle multimodal parts (text, images, PDFs)\n\nStep 1.4: Replace streaming implementation\nUse existing executeNativeGemini3Stream() as template\nRemove model detection logic (all models use native now)\nImplement unified streaming for all Gemini models\n\nStep 1.5: Replace generation implementation\nUse existing executeNativeGemini3Generate() as template\nExtend to all models\n\nPhase 2: Google Vertex Provider (googleVertex.ts)\n\nStep 2.1: Remove AI SDK imports\n\nNote: Anthropic Claude models via Vertex will still use @ai-sdk/google-vertex/anthropic since that's a separate API.\n\nStep 2.2: Replace Gemini model creation\n\nStep 2.3: Dual architecture\nKeep createVertexAnthropic() for Claude models\nUse @google/genai for all Gemini models\n\nStep 2.4: Unify streaming/generation paths\nReuse patterns from Google AI Studio\nHandle Vertex-specific authentication\n\nPhase 3: BaseProvider Updates\n\nStep 3.1: Update abstract methods\n\nStep 3.2: Make getAISDKModel() optional or provider-specific\nNon-Google providers still use AI SDK\nGoogle providers use native SDK directly\n\nPhase 4: Test Updates\n\nStep 4.1: Update test/setup.ts mocks\n\nStep 4.2: Update unit tests\ntest/unit/models/gemini-3-flash.test.ts\ntest/unit/thinking/thinking-configuration.test.ts\ntest/unit/providers/factory.test.ts\n\nStep 4.3: Run integration tests\n\nKey Transformations\nMessage Content Transformation\nTool Transformation\nThinking Configuration\n\nBackward Compatibility\n\nNo Breaking API Changes\nSDK public interface (neurolink.stream(), neurolink.generate()) unchanged\nCLI commands unchanged\nConfiguration unchanged\n\nInternal-Only Changes\nProvider implementation details\nSDK dependency swap\nTest mocks\n\nRisk Mitigation\n\n| Risk | Mitigation |\n| --------------------------- | ---------------------------------------------------------- |\n| Native SDK missing features | Existing Gemini 3 native implementation proves feasibility |\n| Test breakage | Comprehensive test suite with known patterns |\n| Authentication differences | @google/genai supports both API key and Vertex auth |\n| Performance regression | Native SDK eliminates wrapper overhead |\n\nTesting Strategy\nUnit Tests\nIntegration Tests\nEnd-to-End Tests\nManual Validation\n\nSuccess Criteria\nโœ… @ai-sdk/google and @ai-sdk/google-vertex removed from package.json\nโœ… Al","hierarchy":{"lvl0":"Plans","lvl1":"Migration Design: Remove AI SDK Google Dependencies","lvl2":"","lvl3":""}},
8577
8582
  {"objectID":"f22a2e266bd83093ebf4cc0b4fc75522e7ee728a0564d9d2f60d51bfbd6c3868","title":"Migration Design: Remove AI SDK Google Dependencies","url":"/docs/plans/2026-01-01-remove-ai-sdk-google-design#migration-design-remove-ai-sdk-google-dependencies","content":"Date: 2026-01-01\nBranch: feat/remove-ai-sdk-google\nStatus: Done โ€” implemented in 076b9f4c3 (native @google/genai +\n@anthropic-ai/vertex-sdk). The Success Criteria below are all met.","hierarchy":{"lvl0":"Plans","lvl1":"Migration Design: Remove AI SDK Google Dependencies","lvl2":"Migration Design: Remove AI SDK Google Dependencies","lvl3":""}},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "13.5.0",
3
+ "version": "13.5.1",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types โ€” generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI ยท Anthropic ยท Gemini ยท Bedrock ยท Azure ยท Ollama ยท TypeSafe Jev and more.",
6
6
  "author": {
@@ -131,6 +131,24 @@
131
131
  "test:security-check-filter": "pnpm exec tsx test/continuous-test-suite-security-check-filter.ts",
132
132
  "test:stream-teardown-abort": "pnpm exec tsx test/continuous-test-suite-stream-teardown-abort.ts",
133
133
  "test:observability": "pnpm exec tsx test/continuous-test-suite-observability.ts",
134
+ "test:observability:setup-metrics": "pnpm exec tsx test/continuous-test-suite-observability-setup-metrics.ts",
135
+ "test:observability:exporter-redaction": "pnpm exec tsx test/continuous-test-suite-observability-exporter-redaction.ts",
136
+ "test:observability:proxy-voice": "pnpm exec tsx test/continuous-test-suite-observability-proxy-voice.ts",
137
+ "test:observability:core-providers": "pnpm exec tsx test/continuous-test-suite-observability-core-providers.ts",
138
+ "test:observability:w2-extras-a": "pnpm exec tsx test/continuous-test-suite-observability-w2-extras-a.ts",
139
+ "test:observability:w2-extras-b": "pnpm exec tsx test/continuous-test-suite-observability-w2-extras-b.ts",
140
+ "test:observability:decisions": "pnpm exec tsx test/continuous-test-suite-observability-decisions.ts",
141
+ "test:observability:w2-docs": "pnpm exec tsx test/continuous-test-suite-observability-w2-docs.ts",
142
+ "test:observability:w2-tests-cli": "pnpm exec tsx test/continuous-test-suite-observability-w2-tests-cli.ts",
143
+ "test:observability:integration": "pnpm exec tsx test/continuous-test-suite-observability-integration.ts",
144
+ "test:observability:ci-wiring": "pnpm exec tsx test/continuous-test-suite-observability-ci-wiring.ts",
145
+ "test:observability:docs-pass2": "pnpm exec tsx test/continuous-test-suite-observability-docs-pass2.ts",
146
+ "test:observability:proxy-switch": "pnpm exec tsx test/continuous-test-suite-observability-proxy-switch.ts",
147
+ "test:observability:adv-exporter": "pnpm exec tsx test/continuous-test-suite-observability-adv-exporter.ts",
148
+ "test:observability:adv-providers": "pnpm exec tsx test/continuous-test-suite-observability-adv-providers.ts",
149
+ "test:observability:adv-setup": "pnpm exec tsx test/continuous-test-suite-observability-adv-setup.ts",
150
+ "test:observability:adv-proxy": "pnpm exec tsx test/continuous-test-suite-observability-adv-proxy.ts",
151
+ "test:observability:followups": "pnpm run test:observability:setup-metrics && pnpm run test:observability:exporter-redaction && pnpm run test:observability:proxy-voice && pnpm run test:observability:core-providers && pnpm run test:observability:w2-extras-a && pnpm run test:observability:w2-extras-b && pnpm run test:observability:decisions && pnpm run test:observability:w2-docs && pnpm run test:observability:w2-tests-cli && pnpm run test:observability:integration && pnpm run test:observability:ci-wiring && pnpm run test:observability:docs-pass2 && pnpm run test:observability:proxy-switch && pnpm run test:observability:adv-exporter && pnpm run test:observability:adv-providers && pnpm run test:observability:adv-setup && pnpm run test:observability:adv-proxy",
134
152
  "test:ppt": "pnpm exec tsx test/continuous-test-suite-ppt.ts",
135
153
  "test:vertex-loop-characterization": "tsx test/continuous-test-suite-vertex-loop-characterization.ts",
136
154
  "test:vertex-claude-thinking": "pnpm exec tsx test/continuous-test-suite-vertex-claude-thinking.ts",
@@ -364,7 +382,8 @@
364
382
  "**/*.css",
365
383
  "./dist/voice/index.js",
366
384
  "./dist/music/index.js",
367
- "./dist/avatar/index.js"
385
+ "./dist/avatar/index.js",
386
+ "./dist/services/server/ai/observability/instrumentation.js"
368
387
  ],
369
388
  "svelte": "./dist/index.js",
370
389
  "types": "./dist/index.d.ts",