@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
221 lines (220 loc) • 8.62 kB
TypeScript
/**
* OpenTelemetry Instrumentation for Langfuse v4
*
* Configures OpenTelemetry TracerProvider with LangfuseSpanProcessor to capture
* traces from Vercel AI SDK's experimental_telemetry feature.
*
* Flow: Vercel AI SDK → OpenTelemetry Spans → LangfuseSpanProcessor → Langfuse Platform
*/
import { trace } from "@opentelemetry/api";
import { LoggerProvider } from "@opentelemetry/sdk-logs";
import { type SpanProcessor } from "@opentelemetry/sdk-trace-base";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import type { LangfuseConfig, LangfuseContext } from "../../../../types/index.js";
/**
* True when a span is an internal NeuroLink wrapper that should NOT be sent to
* Langfuse. Internal wrappers carry the `langfuse.internal: true` attribute.
*
* Exposed so host apps that bring their own `LangfuseSpanProcessor` (e.g.
* `skipLangfuseSpanProcessor: true`, or manual registration on an existing
* TracerProvider) can apply the same filter and avoid duplicate observations.
*/
export declare function isLangfuseInternalSpan(span: {
attributes?: Record<string, unknown>;
}): boolean;
/**
* Drop-in `shouldExportSpan` predicate for a `LangfuseSpanProcessor` that
* filters out NeuroLink internal wrapper spans.
*
* Usage in host apps:
* ```ts
* import { langfuseShouldExportSpan } from "@juspay/neurolink";
* new LangfuseSpanProcessor({ ..., shouldExportSpan: langfuseShouldExportSpan });
* ```
*/
export declare function langfuseShouldExportSpan({ otelSpan, }: {
otelSpan: {
attributes?: Record<string, unknown>;
};
}): boolean;
/**
* Initialize OpenTelemetry with Langfuse span processor
*
* This connects Vercel AI SDK's experimental_telemetry to Langfuse by:
* 1. Creating LangfuseSpanProcessor with Langfuse credentials
* 2. Creating a NodeTracerProvider with service metadata and span processor
* 3. Registering the provider globally for AI SDK to use
*
* NEW: If useExternalTracerProvider is true or autoDetectExternalProvider detects
* an existing provider, steps 2 and 3 are skipped. The span processors are still
* created and can be retrieved via getSpanProcessors().
*
* @param config - Langfuse configuration passed from parent application
*/
export declare function initializeOpenTelemetry(config: LangfuseConfig): Promise<void>;
/**
* Flush all pending spans to Langfuse
*/
export declare function flushOpenTelemetry(): Promise<void>;
/**
* Shutdown OpenTelemetry and Langfuse span processor
*/
export declare function shutdownOpenTelemetry(): Promise<void>;
/**
* Get the Langfuse span processor
*/
export declare function getLangfuseSpanProcessor(): SpanProcessor | null;
/**
* Get the tracer provider
*/
export declare function getTracerProvider(): NodeTracerProvider | null;
/**
* Get the logger provider for emitting OTLP log records.
* Returns null if OTLP is not configured or LoggerProvider was not created.
*/
export declare function getLoggerProvider(): LoggerProvider | null;
/**
* Check if OpenTelemetry is initialized
*/
export declare function isOpenTelemetryInitialized(): boolean;
/**
* Get health status for Langfuse observability
*
* @returns Health status object with initialization and configuration details
*/
export declare function getLangfuseHealthStatus(): {
isHealthy: boolean;
initialized: boolean;
credentialsValid: boolean;
enabled: boolean;
hasProcessor: boolean;
usingExternalProvider: boolean;
config?: {
baseUrl: string;
environment: string;
release: string;
};
};
/**
* Set user and session context for Langfuse spans in the current async context
*
* Merges the provided context with existing AsyncLocalStorage context. If a callback is provided,
* the context is scoped to that callback execution and returns the callback's result.
* Without a callback, the context applies to the current execution context and its children.
*
* Uses AsyncLocalStorage to properly scope context per request, avoiding race conditions
* in concurrent scenarios.
*
* @param context - Object containing context fields to merge with existing context
* @param callback - Optional callback to run within the context scope. If omitted, context applies to current execution
* @returns The callback's return value if provided, otherwise void
*
* @example
* // With callback - returns the result
* const result = await setLangfuseContext({ userId: "user123" }, async () => {
* return await generateText({ model: "gpt-4", prompt: "Hello" });
* });
*
* @example
* // Without callback - sets context for current execution
* await setLangfuseContext({ sessionId: "session456", traceName: "chat-completion" });
*/
export declare function setLangfuseContext<T = void>(context: {
userId?: string | null;
sessionId?: string | null;
conversationId?: string | null;
requestId?: string | null;
traceName?: string | null;
metadata?: Record<string, unknown> | null;
/** Explicit operation name (overrides auto-detection) */
operationName?: string | null;
/** Override global autoDetectOperationName for this context */
autoDetectOperationName?: boolean;
/** Custom attributes to set on all spans within this context */
customAttributes?: Record<string, string | number | boolean>;
}, callback?: () => T | Promise<T>): Promise<T | void>;
/**
* Get the current Langfuse context from AsyncLocalStorage
*
* Returns the current context including userId, sessionId, conversationId,
* requestId, traceName, and metadata. Returns undefined if no context is set.
*
* @returns The current LangfuseContext or undefined
*
* @example
* const context = getLangfuseContext();
* console.log(context?.userId, context?.sessionId);
*/
export declare function getLangfuseContext(): LangfuseContext | undefined;
/**
* Capture the current Langfuse AsyncLocalStorage context and return a wrapper
* that re-enters that context when executing the provided callback.
*
* This is essential for preserving trace context across async boundaries that
* break the automatic ALS propagation chain, such as `setImmediate()`,
* `setTimeout()`, or event-emitter callbacks. Without this, spans created
* inside those callbacks become orphaned traces in Langfuse.
*
* **How it works:**
* 1. Captures the current ALS store at call time (synchronously).
* 2. Returns an async function that, when invoked, re-enters the captured
* context via `contextStorage.run()` before executing the callback.
* 3. If no context exists at capture time, the callback runs without
* ALS wrapping (no-op passthrough).
*
* @param fn - The async function to execute within the captured context
* @returns A new async function that preserves the Langfuse ALS context
*
* @example
* // Before (broken — setImmediate loses ALS context):
* setImmediate(async () => {
* await this.checkAndSummarize(session, threshold);
* });
*
* // After (fixed — context is captured and re-entered):
* const wrappedFn = runWithCurrentLangfuseContext(async () => {
* await this.checkAndSummarize(session, threshold);
* });
* setImmediate(wrappedFn);
*/
export declare function runWithCurrentLangfuseContext<T>(fn: () => Promise<T>): () => Promise<T>;
/**
* Get an OpenTelemetry Tracer for creating custom spans
*
* This allows applications to create their own spans that will be
* processed by the same span processors (ContextEnricher + LangfuseSpanProcessor).
*
* @param name - Tracer name, defaults to "neurolink"
* @param version - Tracer version, optional
* @returns OpenTelemetry Tracer instance
*
* @example
* const tracer = getTracer("my-app");
* const span = tracer.startSpan("custom-operation");
* try {
* // ... do work
* } finally {
* span.end();
* }
*/
export declare function getTracer(name?: string, version?: string): ReturnType<typeof trace.getTracer>;
/**
* Create a new ContextEnricher span processor
* Use this when useExternalTracerProvider is true to add to your own TracerProvider
*
* @returns A new ContextEnricher instance
*/
export declare function createContextEnricher(): SpanProcessor;
/**
* Get all span processors that NeuroLink would use
* Convenience function that returns [ContextEnricher, LangfuseSpanProcessor]
*
* @returns Array of span processors, or empty array if not initialized
*/
export declare function getSpanProcessors(): SpanProcessor[];
/**
* Check if using external TracerProvider mode
*
* @returns true if operating in external TracerProvider mode
*/
export declare function isUsingExternalTracerProvider(): boolean;