UNPKG

@juspay/neurolink

Version:

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

478 lines (477 loc) • 16.4 kB
/** * Observability Configuration Types * These configs are passed from the parent application (e.g., Lighthouse) * to enable telemetry and observability features in Neurolink SDK */ import type { AttributeValue } from "@opentelemetry/api"; import type { JsonValue, RawUsageObject, Unknown } from "./common.js"; import type { SpanData } from "./span.js"; /** * Standard GenAI semantic convention attributes from OpenTelemetry * These are the attributes that Vercel AI SDK's experimental_telemetry creates * @see https://opentelemetry.io/docs/specs/semconv/gen-ai/ */ export type LangfuseSpanAttributes = { "gen_ai.system"?: string; "gen_ai.request.model"?: string; "gen_ai.response.model"?: string; "gen_ai.request.max_tokens"?: number; "gen_ai.request.temperature"?: number; "gen_ai.request.top_p"?: number; "gen_ai.usage.input_tokens"?: number; "gen_ai.usage.output_tokens"?: number; "gen_ai.usage.total_tokens"?: number; "gen_ai.response.finish_reasons"?: string[]; "gen_ai.prompt"?: string; "gen_ai.completion"?: string; "ai.model.id"?: string; "ai.model.provider"?: string; "ai.operationId"?: string; "ai.telemetry.functionId"?: string; "ai.finishReason"?: string; "ai.usage.promptTokens"?: number; "ai.usage.completionTokens"?: number; [key: string]: AttributeValue | undefined; }; /** * @description Langfuse exporter options. Every field falls back to its `LANGFUSE_*` environment variable when omitted. */ export type NeurolinkLangfuseConfig = { /** Project public key; defaults to `LANGFUSE_PUBLIC_KEY`. */ publicKey?: string; /** Project secret key; defaults to `LANGFUSE_SECRET_KEY`. */ secretKey?: string; /** Langfuse host; defaults to `LANGFUSE_BASE_URL`, then Langfuse Cloud. */ baseUrl?: string; /** Environment name; defaults to `LANGFUSE_TRACING_ENVIRONMENT`. */ environment?: string; /** Release identifier; defaults to `LANGFUSE_RELEASE`. */ release?: string; /** Applied to input, output and metadata before they leave the process. */ mask?: (params: { data: JsonValue; }) => JsonValue; /** `"immediate"` sends every span as it ends; `"batched"` (default) queues them. */ exportMode?: "batched" | "immediate"; /** Spans queued before a batch is sent; defaults to 15. */ flushAt?: number; /** Seconds between batch sends; defaults to 5. */ flushInterval?: number; }; /** * @description Observability configuration for the NeuroLink SDK. Langfuse turns on automatically when `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` are set; `false` turns it off. */ export type ObservabilityConfig = { /** `false` disables Langfuse, `true` or an options object enables it. */ langfuse?: boolean | NeurolinkLangfuseConfig; }; /** * @description Per-call trace attributes propagated to every span created inside the call. */ export type NeurolinkTraceContext = { /** End user the trace belongs to. */ userId?: string | null; /** Session that groups related traces. */ sessionId?: string | null; /** Conversation identifier, recorded as trace metadata. */ conversationId?: string | null; /** Request identifier, recorded as trace metadata. */ requestId?: string | null; /** Trace name shown in Langfuse. */ traceName?: string | null; /** Trace tags. */ tags?: string[]; /** Trace metadata; values up to 200 characters once stringified are on every observation, longer values and arrays only on the trace. */ metadata?: Record<string, SpanContent | undefined>; /** Name of the root observation of this `generate` or `stream` call; not propagated to child observations and not the trace name. */ observationName?: string | null; }; /** * Retry policy type for observability exporters. */ export type RetryPolicy = { /** Policy name for identification */ readonly name: string; /** Decide whether to retry */ shouldRetry(context: RetryContext): RetryDecision; /** Maximum attempts allowed */ readonly maxAttempts: number; /** Maximum total time allowed for retries */ readonly maxTotalTimeMs: number; }; /** * Sampler type for controlling which spans are exported. */ export type Sampler = { /** Sampler name for identification */ readonly name: string; /** Determine if a span should be sampled */ shouldSample(span: SpanData): boolean; /** Get sampling decision description */ getDescription(): string; }; /** * Span processor type for composable span processing pipelines. */ export type SpanProcessor = { /** Processor name for identification */ readonly name: string; /** Process a span before export, returns null to drop the span */ process(span: SpanData): SpanData | null; /** Optional async processing (for external lookups, etc.) */ processAsync?(span: SpanData): Promise<SpanData | null>; /** Shutdown the processor (cleanup resources) */ shutdown?(): Promise<void>; }; /** * Hierarchical trace view grouping related spans */ export type TraceView = { /** Trace identifier shared by all spans in this trace */ traceId: string; /** The root/parent span of this trace */ rootSpan: SpanData; /** Child spans linked to the root */ childSpans: SpanData[]; /** Total duration from first to last span */ totalDurationMs: number; /** Total number of spans in this trace */ spanCount: number; /** Overall trace status */ status: "ok" | "error" | "partial"; }; /** A model request serialized onto a span: the system prompt plus messages, or a bare prompt. */ export type SpanModelRequest = { system?: unknown; messages?: readonly unknown[]; prompt?: string; }; export type GenerationOperation = "generate" | "stream"; /** @description Any value a span attribute can carry: JSON, or an object serialized on the way out. */ export type SpanContent = JsonValue | object; /** @description Observation kinds Langfuse renders a span as. */ export type ObservationType = "span" | "generation" | "event" | "embedding" | "agent" | "tool" | "chain" | "retriever" | "evaluator" | "guardrail"; /** @description Severity of an observation. */ export type ObservationLevel = "DEBUG" | "DEFAULT" | "WARNING" | "ERROR"; /** @description Attributes stamped on an observation span by `observe`. */ export type ObservationAttributes = { input?: Unknown; output?: Unknown; metadata?: Record<string, SpanContent | undefined>; level?: ObservationLevel; statusMessage?: string; completionStartTime?: Date; model?: string; modelParameters?: Record<string, string | number>; usageDetails?: Record<string, number>; costDetails?: Record<string, number>; }; /** @description Trace-level input, output and per-key metadata recorded by `observeTrace`. */ export type TraceContent = Pick<ObservationAttributes, "input" | "output"> & { metadata?: Record<string, SpanContent | undefined>; }; /** @description A tool call as a provider, the agentic loop or a tool execution names its fields. */ export type AssistantToolCall = { id?: string; toolCallId?: string; name?: string; toolName?: string; args?: SpanContent; input?: SpanContent; arguments?: SpanContent; function?: { name?: string; arguments?: SpanContent; }; }; /** @description A model response split into text, reasoning and tool calls. */ export type AssistantReply = { text?: string; reasoning?: string; toolCalls?: AssistantToolCall[]; }; /** @description A model response as plain text, a structured reply, or message or content-part arrays. */ export type AssistantOutput = string | AssistantReply | object[]; /** @description A tool call in OpenAI chat shape, as Langfuse renders it. */ export type ObservationToolCall = { id?: string; type: "function"; function: { name?: string; arguments: string; }; }; /** * @description One model request as a Langfuse generation. Every field is optional so the request half and the response half can be recorded separately. */ export type GenerationRecord = { /** Public API the call serves; inferred from the enclosing span when unset. */ operation?: GenerationOperation; /** Provider name, used with the model to price the call. */ provider?: string; /** Model the request asked for. */ model?: string; /** Model the response says served it, when the API reports one. */ responseModel?: string; /** Sampling and thinking parameters sent; undefined entries are dropped. */ modelParameters?: Record<string, string | number | undefined>; /** System prompt and messages, or the bare prompt, sent. */ input?: SpanModelRequest; /** Text and tool calls returned. */ output?: AssistantOutput; /** Token usage in any naming convention `extractTokenUsage` reads. */ usage?: RawUsageObject | null; /** Provider finish reason. */ finishReason?: string; /** Whether the caller aborted the call before it finished. */ aborted?: boolean; /** When the first chunk of a streamed response arrived. */ completionStartTime?: Date; }; /** * Latency statistics with percentile calculations */ export type LatencyStats = { /** Minimum latency in milliseconds */ min: number; /** Maximum latency in milliseconds */ max: number; /** Mean/average latency in milliseconds */ mean: number; /** Median latency (p50) in milliseconds */ median: number; /** 50th percentile latency in milliseconds */ p50: number; /** 75th percentile latency in milliseconds */ p75: number; /** 90th percentile latency in milliseconds */ p90: number; /** 95th percentile latency in milliseconds */ p95: number; /** 99th percentile latency in milliseconds */ p99: number; /** Standard deviation in milliseconds */ stdDev: number; /** Total number of samples */ count: number; }; /** * Cost breakdown by provider */ export type ProviderCostStats = { provider: string; totalCost: number; requestCount: number; avgCostPerRequest: number; inputCost: number; outputCost: number; }; /** * Cost breakdown by model */ export type ModelCostStats = { model: string; provider: string; totalCost: number; requestCount: number; avgCostPerRequest: number; inputTokens: number; outputTokens: number; inputCost: number; outputCost: number; }; /** * Aggregated metrics summary */ export type MetricsSummary = { /** Total number of spans tracked */ totalSpans: number; /** Number of successful spans */ successfulSpans: number; /** Number of failed spans */ failedSpans: number; /** Overall success rate (0-1) */ successRate: number; /** Latency statistics */ latency: LatencyStats; /** Token usage statistics */ tokens: TokenUsageStats; /** Cost by provider */ costByProvider: ProviderCostStats[]; /** Cost by model */ costByModel: ModelCostStats[]; /** Total cost across all providers */ totalCost: number; /** Span count by type */ spansByType: Record<string, number>; /** Timestamp of first span */ firstSpanTime?: Date; /** Timestamp of last span */ lastSpanTime?: Date; /** Tracking duration in milliseconds */ trackingDurationMs?: number; }; /** * Result of a retry decision */ export type RetryDecision = { /** Whether to retry */ shouldRetry: boolean; /** Delay before retry in milliseconds */ delayMs: number; /** Reason for the decision */ reason: string; }; /** * Context for retry decision making */ export type RetryContext = { /** Current attempt number (0-indexed) */ attempt: number; /** The error that triggered the retry */ error: Error; /** Total elapsed time since first attempt */ elapsedMs: number; /** Operation name for logging */ operationName: string; /** Additional metadata */ metadata?: Record<string, unknown>; }; /** * Token usage statistics by provider */ export type ProviderTokenStats = { provider: string; inputTokens: number; outputTokens: number; totalTokens: number; cost: number; requestCount: number; }; /** * Token usage statistics by model */ export type ModelTokenStats = { model: string; provider: string; inputTokens: number; outputTokens: number; totalTokens: number; cost: number; requestCount: number; avgTokensPerRequest: number; }; /** * Aggregated token usage statistics */ export type TokenUsageStats = { totalInputTokens: number; totalOutputTokens: number; totalTokens: number; cacheReadTokens: number; cacheCreationTokens: number; reasoningTokens: number; totalCost: number; byProvider: Map<string, ProviderTokenStats>; byModel: Map<string, ModelTokenStats>; bySpanType: Map<string, number>; }; export type HealthMetrics = { timestamp: number; memoryUsage: NodeJS.MemoryUsage; uptime: number; activeConnections: number; errorRate: number; averageResponseTime: number; }; export type SpanOptions = { name: string; tracer: import("@opentelemetry/api").Tracer; kind?: import("@opentelemetry/api").SpanKind; attributes?: Record<string, string | number | boolean | undefined>; /** Marks a thrown error as an expected skip whose Langfuse level the span set itself, so `withSpan` keeps it instead of raising ERROR. */ expectedFailure?: (error: unknown) => boolean; /** Keeps the span out of the active context, so spans started inside `fn` parent to the nearest ancestor that is exported. */ detached?: boolean; }; /** Trace + parent span IDs used to correlate metric records with spans. */ export type MetricsTraceContext = { traceId: string; parentSpanId: string; }; /** * Runtime state for the observability exporter circuit breaker. * Prefixed to disambiguate from the richer MCP CircuitBreakerState in mcp.ts. */ export type ObservabilityCircuitBreakerState = { failures: number; lastFailure: number; state: "closed" | "open" | "half-open"; }; /** * Minimal config for the observability exporter circuit breaker. * Prefixed to disambiguate from the richer MCP CircuitBreakerConfig in mcp.ts. */ export type ObservabilityCircuitBreakerConfig = { failureThreshold: number; resetTimeout: number; }; /** Minimal view of the dynamically-imported @sentry/node module. */ export type SentryModule = { init: (options: { dsn: string; tracesSampleRate: number; release?: string; environment: string; }) => void; withScope: (callback: (scope: SentryScope) => void) => void; captureException: (error: Error) => void; startInactiveSpan: (options: { name: string; op: string; startTime: number; attributes?: Record<string, unknown>; }) => { end: (timestamp?: number) => void; }; flush: (timeout: number) => Promise<boolean>; close: (timeout: number) => Promise<boolean>; }; /** Sentry scope surface used by SentryExporter.withScope callbacks. */ export type SentryScope = { setTags: (tags: Record<string, string>) => void; setContext: (name: string, context: Record<string, unknown>) => void; setUser: (user: { id: string; }) => void; }; /** Aggregated metrics for a single time window. */ export type TimeWindowStats = { windowStart: Date; windowEnd: Date; windowDurationMs: number; requestCount: number; errorCount: number; successRate: number; throughput: number; latency: LatencyStats; tokens: TokenUsageStats; costByProvider: Map<string, ProviderCostStats>; costByModel: Map<string, ModelCostStats>; }; /** Configuration for MetricsAggregator. */ export type MetricsAggregatorConfig = { maxSpansRetained?: number; enableTimeWindows?: boolean; timeWindowMs?: number; maxTimeWindows?: number; }; /** * Per-million-token pricing used by the observability TokenTracker. * Prefixed to disambiguate from the richer providers.ts ModelPricing. */ export type ObservabilityModelPricing = { inputPricePerMillion: number; outputPricePerMillion: number; cachedInputPricePerMillion?: number; };