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

819 lines (818 loc) • 32.8 kB
/* eslint-disable no-console -- This proxy-only sink replaces console methods with OTLP emission. */ import { inspect } from "node:util"; import { randomUUID } from "node:crypto"; import { createRequire } from "node:module"; import { getProxyRequestTraceContext, proxyLogContext, } from "./proxyTraceContext.js"; import { setImmediate as yieldToRequests } from "node:timers/promises"; import { createProxyOtlpLogTransport, getProxyOtlpRetryAfter, } from "./otlpLogTransport.js"; import { sanitizeForLog } from "../utils/logSanitize.js"; import { splitUtf8StringByBytes } from "./bodyCaptureProcessing.js"; /** * @opentelemetry/sdk-logs, resources, api-logs and core are loaded * synchronously via require() on first actual use, not via a top-level * import — so merely requiring this module (reachable eagerly from * dist/server/index.js via codexProxyRoutes.ts/claudeProxyRoutes.ts) no * longer pulls in four heavy OTel packages for the overwhelming majority * of callers who never set NEUROLINK_PROXY_LOG_SINK=otel. All four ship * plain CommonJS builds, so require() works on any Node >=22.0.0 — no * experimental flag, unlike require(esm) of a local ES module (which only * works unflagged from Node 22.12). otlpLogTransport.js stays a normal * static import for exactly that reason; it lazily requires its own two * npm dependencies (@opentelemetry/core, otlp-transformer) internally. * Everything here stays fully synchronous — construction is still one * synchronous call, exactly as before — only the *import* is deferred, not * the initialization semantics. Do not convert this to a dynamic * `import()`: that would make initializeProxyOtelLogs() asynchronous and * break every caller (both production code and the tests below) that * relies on the queues/provider existing synchronously the moment it * returns. */ const require = createRequire(import.meta.url); let SeverityNumber; let ExportResultCode; let resourceFromAttributes; let BatchLogRecordProcessor; let LoggerProvider; let otelDepsLoaded = false; function loadOtelDeps() { if (otelDepsLoaded) { return; } ({ SeverityNumber } = require("@opentelemetry/api-logs")); ({ ExportResultCode } = require("@opentelemetry/core")); ({ resourceFromAttributes } = require("@opentelemetry/resources")); ({ BatchLogRecordProcessor, LoggerProvider, } = require("@opentelemetry/sdk-logs")); otelDepsLoaded = true; } let provider; let restoreConsole; const queues = []; const bodyPublications = new Map(); const bodyPublicationDeadlines = new WeakMap(); const bodyPublicationOperations = new Set(); let shuttingDown = false; const BODY_OTLP_CHUNK_SIZE = 128 * 1024; const OTLP_EXPORT_TIMEOUT_MS = 30_000; const OTLP_EXPORT_CALLBACK_DEADLINE_MS = OTLP_EXPORT_TIMEOUT_MS + 1_000; const BODY_PUBLICATION_ACTIVE_DEADLINE_MS = 20_000; const METADATA_RETENTION_MS = 120_000; const DIAGNOSTIC_KINDS = new Set(["console", "runtime", "telemetry_delivery"]); /** Payload accounting includes UTF-8 attributes and fixed per-record overhead. */ function retainedRecordBytes(record) { try { return (512 + Buffer.byteLength(typeof record.body === "string" ? record.body : (JSON.stringify(record.body) ?? ""), "utf8") + Buffer.byteLength(JSON.stringify(record.attributes), "utf8")); } catch { return Infinity; } } const bodyDelivery = { attempted: 0, transportAcknowledged: 0, exportUnconfirmed: 0, rejected: 0, partial: 0, pending: 0, pendingBytes: 0, highWaterPending: 0, highWaterBytes: 0, maxPending: 64, maxPendingBytes: 32 * 1024 * 1024, }; /** Explicit opt-in; configuration never silently falls back to file logging. */ export function isProxyOtelOnly() { return process.env.NEUROLINK_PROXY_LOG_SINK === "otel"; } /** Reserve capacity including exports in flight, independently for metadata and bodies. */ function createTrackedProcessor(url, capacity, kind, byteCapacity) { const unsettled = new Set(); const enqueuedAt = new WeakMap(); const retainedBytes = new WeakMap(); const stopRetentionWaiters = new Set(); const retentionMs = kind === "metadata" || kind === "indexes" ? METADATA_RETENTION_MS : 0; const capacityWaiters = new Set(); const flushWaiters = new Set(); const state = { attempted: 0, submitted: 0, transportAcknowledged: 0, exportUnconfirmed: 0, dropped: 0, outstanding: 0, outstandingBytes: 0, highWaterBytes: 0, byteLimitDrops: 0, retryingRecords: 0, retriedBatches: 0, retentionExpired: 0, lastTransientFailureAt: undefined, lastAcknowledgedAt: undefined, lastFailureAt: undefined, highWaterOutstanding: 0, recentFailures: [], failureHistoryEvicted: 0, }; const diagnostics = []; let diagnosticScheduled = false; const rememberFailure = (records, reason, error) => { const stringAttribute = (record, name) => { const value = record.attributes[name]; return typeof value === "string" ? value.slice(0, 128) : undefined; }; const failure = { id: randomUUID(), at: new Date().toISOString(), reason, ...(error ? { error: sanitizeForLog(error.message).slice(0, 256) } : {}), records: records.slice(0, 64).map((record) => ({ eventId: stringAttribute(record, "proxy.event_id") ?? "unavailable", kind: stringAttribute(record, "proxy.record_kind"), requestId: stringAttribute(record, "request.id"), captureId: stringAttribute(record, "body.capture_id"), })), }; if (state.recentFailures.length === 16) { state.recentFailures.shift(); state.failureHistoryEvicted++; } state.recentFailures.push(failure); // A failed diagnostic must not generate another diagnostic recursively. if (records.some((record) => record.attributes["proxy.record_kind"] !== "telemetry_delivery")) { if (diagnostics.length === 16) { diagnostics.shift(); } diagnostics.push(failure); } }; const publishRecoveredDiagnostics = () => { if (diagnosticScheduled || !diagnostics.length || shuttingDown) { return; } diagnosticScheduled = true; queueMicrotask(() => { diagnosticScheduled = false; for (const failure of diagnostics.splice(0)) { emitProxyOtelEvent("telemetry_delivery", { queue: kind, ...failure }); } }); }; const transport = createProxyOtlpLogTransport(url, OTLP_EXPORT_TIMEOUT_MS); const exporter = { export(records, callback) { const exportStartedAt = performance.now(); for (const record of records) { const id = record.attributes["body.capture_id"]; const publication = typeof id === "string" ? bodyPublications.get(id) : undefined; if (publication) { publication.maxChunkQueueWaitMs = Math.max(publication.maxChunkQueueWaitMs ?? 0, exportStartedAt - (enqueuedAt.get(record) ?? exportStartedAt)); } } let settled = false; let retrying = false; let retryTimer; let deadline; let lastFailure; let attempt = 0; const oldestEnqueuedAt = Math.min(...records.map((record) => enqueuedAt.get(record) ?? exportStartedAt)); const retainUntil = oldestEnqueuedAt + retentionMs; const stopRetention = () => { // Only a waiting retry is settled early. An active HTTP export keeps // ownership until its callback/deadline so capacity stays truthful. if (retryTimer && lastFailure) { settle(lastFailure); } }; stopRetentionWaiters.add(stopRetention); const settle = (result) => { if (settled) { return; } settled = true; clearTimeout(deadline); clearTimeout(retryTimer); stopRetentionWaiters.delete(stopRetention); if (retrying) { state.retryingRecords -= records.length; } state.outstanding -= records.length; if (result.code === ExportResultCode.SUCCESS) { state.transportAcknowledged += records.length; state.lastAcknowledgedAt = new Date().toISOString(); publishRecoveredDiagnostics(); } else { state.exportUnconfirmed += records.length; state.lastFailureAt = new Date().toISOString(); rememberFailure(records, "export_unconfirmed", result.error); } for (const record of records) { unsettled.delete(record); state.outstandingBytes -= retainedBytes.get(record) ?? 0; const id = record.attributes?.["body.capture_id"]; const publication = typeof id === "string" ? bodyPublications.get(id) : undefined; if (publication) { publication.maxChunkExportMs = Math.max(publication.maxChunkExportMs ?? 0, performance.now() - exportStartedAt); const activeDeadline = bodyPublicationDeadlines.get(publication); if (result.code === ExportResultCode.SUCCESS && (activeDeadline === undefined || Date.now() < activeDeadline)) { publication.acknowledged++; } else { publication.unconfirmed++; } publication.notify?.(); } } for (const notify of capacityWaiters) { notify(); } for (const notify of flushWaiters) { notify(); } callback(result); }; const send = () => { retryTimer = undefined; if (retentionMs && performance.now() >= retainUntil) { state.retentionExpired += records.length; settle({ code: ExportResultCode.FAILED, error: new Error("OTLP metadata retention budget exceeded"), }); return; } attempt++; if (attempt > 1) { state.retriedBatches++; } let attemptSettled = false; const completed = (result) => { if (attemptSettled || settled) { return; } attemptSettled = true; clearTimeout(deadline); const retryAfter = result.code === ExportResultCode.FAILED ? getProxyOtlpRetryAfter(result.error) : undefined; if (retentionMs && retryAfter !== undefined && !shuttingDown) { state.lastTransientFailureAt = new Date().toISOString(); const wait = Math.max(retryAfter, Math.min(10_000, 1000 * 2 ** Math.min(attempt - 1, 4)) * (0.8 + Math.random() * 0.4)); if (performance.now() + wait < retainUntil) { if (!retrying) { retrying = true; state.retryingRecords += records.length; } lastFailure = result; retryTimer = setTimeout(send, wait); retryTimer.unref(); return; } state.retentionExpired += records.length; } settle(result); }; deadline = setTimeout(() => completed({ code: ExportResultCode.FAILED, error: new Error("OTLP export callback deadline exceeded"), }), OTLP_EXPORT_CALLBACK_DEADLINE_MS); deadline.unref(); try { transport.export(records, completed); } catch (error) { completed({ code: ExportResultCode.FAILED, error: error instanceof Error ? error : new Error(String(error)), }); } }; send(); }, shutdown: () => transport.shutdown(), }; const batch = new BatchLogRecordProcessor(exporter, { maxQueueSize: capacity, maxExportBatchSize: 64, scheduledDelayMillis: kind === "bodies" ? 25 : 1000, exportTimeoutMillis: retentionMs + OTLP_EXPORT_CALLBACK_DEADLINE_MS + 1000, }); const processor = { onEmit(record) { record.attributes["proxy.event_id"] ??= randomUUID(); state.attempted++; const id = record.attributes?.["body.capture_id"]; const publication = typeof id === "string" ? bodyPublications.get(id) : undefined; if (publication) { publication.emitted++; } const bytes = retainedRecordBytes(record); if (state.outstanding >= capacity || state.outstandingBytes + bytes > byteCapacity) { state.dropped++; if (state.outstandingBytes + bytes > byteCapacity) { state.byteLimitDrops++; } rememberFailure([record], "queue_full"); if (publication) { publication.dropped++; publication.notify?.(); } return; } state.submitted++; state.outstanding++; state.outstandingBytes += bytes; state.highWaterBytes = Math.max(state.highWaterBytes, state.outstandingBytes); retainedBytes.set(record, bytes); unsettled.add(record); enqueuedAt.set(record, performance.now()); state.highWaterOutstanding = Math.max(state.highWaterOutstanding, state.outstanding); batch.onEmit(record); }, forceFlush: async () => { const boundary = new Set(unsettled); try { await batch.forceFlush(); } finally { await new Promise((resolve) => { const check = () => { if (![...boundary].some((record) => unsettled.has(record))) { flushWaiters.delete(check); resolve(); } }; flushWaiters.add(check); check(); }); } }, shutdown: async () => { try { await processor.forceFlush(); } finally { await batch.shutdown(); } }, }; /** Wait for a shared queue slot without force-flushing or owning another capture. */ const waitForCapacity = (deadline) => new Promise((resolve) => { const timer = setTimeout(() => finish(false), Math.max(0, deadline - Date.now())); timer.unref?.(); const finish = (available) => { clearTimeout(timer); capacityWaiters.delete(check); resolve(available); }; const check = () => { if (Date.now() >= deadline) { finish(false); } else if (state.outstanding < capacity) { finish(true); } }; capacityWaiters.add(check); check(); }); return { state, processor, capacity, byteCapacity, retentionMs, kind, waitForCapacity, abortPending: () => transport.abortPending(), stopRetention: () => { for (const stop of stopRetentionWaiters) { stop(); } }, oldestOutstandingAgeMs: () => { let oldest = performance.now(); for (const record of unsettled) { oldest = Math.min(oldest, enqueuedAt.get(record) ?? oldest); } return unsettled.size ? Math.max(0, performance.now() - oldest) : 0; }, }; } /** * Publish bounded captures concurrently into a shared batch queue. Transport * callbacks settle each chunk once; a flush is a process fence, not a per-body * network round trip. Metadata has a separate queue and transport. */ export async function publishProxyOtelBody(captureId, body, emit) { bodyDelivery.attempted++; const bytes = Buffer.byteLength(body, "utf8"); if (!provider || shuttingDown || bodyPublications.has(captureId) || bodyDelivery.pending >= bodyDelivery.maxPending || bodyDelivery.pendingBytes + bytes > bodyDelivery.maxPendingBytes) { bodyDelivery.rejected++; return { status: "rejected", acknowledgedChunks: 0, unconfirmedChunks: 0, droppedChunks: 0, reason: bodyPublications.has(captureId) ? "body_capture_id_in_use" : !provider || shuttingDown ? "body_exporter_unavailable" : "body_publication_queue_full", }; } bodyDelivery.pending++; bodyDelivery.pendingBytes += bytes; bodyDelivery.highWaterPending = Math.max(bodyDelivery.highWaterPending, bodyDelivery.pending); bodyDelivery.highWaterBytes = Math.max(bodyDelivery.highWaterBytes, bodyDelivery.pendingBytes); const progress = { acknowledged: 0, unconfirmed: 0, dropped: 0, emitted: 0, }; bodyPublications.set(captureId, progress); let deferredRelease; const operation = (async () => { const queue = queues.find((candidate) => candidate.kind === "bodies"); if (!queue) { return { status: "rejected", acknowledgedChunks: 0, unconfirmedChunks: 0, droppedChunks: 0, reason: "body_exporter_unavailable", }; } const startedAt = performance.now(); const deadline = Date.now() + BODY_PUBLICATION_ACTIVE_DEADLINE_MS; bodyPublicationDeadlines.set(progress, deadline); const chunks = splitUtf8StringByBytes(body, BODY_OTLP_CHUNK_SIZE); let capacityWaitMs = 0; let reason; try { for (let index = 0; index < chunks.length; index++) { while (queue.state.outstanding >= queue.capacity) { const waitingAt = performance.now(); const available = await queue.waitForCapacity(deadline); capacityWaitMs += performance.now() - waitingAt; if (!available) { reason = "body_publication_deadline"; break; } } if (Date.now() >= deadline) { reason = "body_publication_deadline"; break; } emit(chunks[index], index, chunks.length); // Multiple waiters can wake together; the next iteration checks the // shared capacity synchronously before emitting its next chunk. if (index % 4 === 3) { await yieldToRequests(); } } } catch { reason = "body_publication_failed"; } const settlement = new Promise((resolve) => { const check = () => { if (progress.acknowledged + progress.unconfirmed + progress.dropped >= progress.emitted) { progress.notify = undefined; resolve(); } }; progress.notify = check; check(); }); let timer; const completed = await Promise.race([ settlement.then(() => true), new Promise((resolve) => { timer = setTimeout(() => resolve(false), Math.max(0, deadline - Date.now())); timer.unref?.(); }), ]).finally(() => clearTimeout(timer)); if (!completed) { // Keep identity/bytes until the real callback or callback deadline settles // submitted chunks. Late success cannot rewrite an uncertainty result. deferredRelease = settlement; } if (!completed || Date.now() >= deadline) { reason ??= "body_publication_deadline"; } if (!reason && progress.unconfirmed) { reason = "body_export_unconfirmed"; } else if (!reason && progress.dropped) { reason = "body_export_queue_full"; } const pending = Math.max(0, progress.emitted - progress.acknowledged - progress.unconfirmed - progress.dropped); const unconfirmedChunks = progress.unconfirmed + pending; const status = progress.emitted === 0 ? "rejected" : progress.dropped || progress.emitted !== chunks.length ? "partial" : unconfirmedChunks ? "export_unconfirmed" : "transport_acknowledged"; return { status, expectedChunks: chunks.length, acknowledgedChunks: progress.acknowledged, unconfirmedChunks, droppedChunks: progress.dropped, notSubmittedChunks: chunks.length - progress.emitted, publicationMs: performance.now() - startedAt, capacityWaitMs, maxChunkQueueWaitMs: progress.maxChunkQueueWaitMs ?? 0, maxChunkExportMs: progress.maxChunkExportMs ?? 0, ...(reason ? { reason } : {}), }; })(); bodyPublicationOperations.add(operation); try { const result = await operation; if (result.status === "transport_acknowledged") { bodyDelivery.transportAcknowledged++; } else if (result.status === "export_unconfirmed") { bodyDelivery.exportUnconfirmed++; } else if (result.status === "rejected") { bodyDelivery.rejected++; } else { bodyDelivery.partial++; } return result; } finally { bodyPublicationOperations.delete(operation); const release = () => { bodyPublications.delete(captureId); bodyDelivery.pending--; bodyDelivery.pendingBytes -= bytes; }; if (deferredRelease) { void deferredRelease.then(release, release); } else { release(); } } } /** Initialize a log-only provider in every proxy process, including the supervisor. */ export function initializeProxyOtelLogs(role = "worker") { if (!isProxyOtelOnly()) { return undefined; } if (provider) { return provider; } const endpoint = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ?? (process.env.OTEL_EXPORTER_OTLP_ENDPOINT ? `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT.replace(/\/$/, "")}/v1/logs` : undefined); if (!endpoint) { throw new Error("OTel-only proxy logging requires an OTLP endpoint"); } const bodyEndpoint = process.env.NEUROLINK_PROXY_OTLP_BODIES_ENDPOINT ?? endpoint; for (const target of [endpoint, bodyEndpoint]) { const url = new URL(target); if (!["http:", "https:"].includes(url.protocol)) { throw new Error("Proxy OTLP logs endpoint must use HTTP or HTTPS"); } const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname); if (url.protocol === "http:" && !loopback) { throw new Error("Proxy OTLP logs require HTTPS for non-loopback collectors"); } } // Validation above must run — and be able to throw — before this: a // misconfigured endpoint must fail without ever loading the OTel SDK. loadOtelDeps(); const metadata = createTrackedProcessor(endpoint, 2048, "metadata", 8 * 1024 * 1024); const bodies = createTrackedProcessor(bodyEndpoint, 256, "bodies", 40 * 1024 * 1024); const diagnostics = createTrackedProcessor(endpoint, 512, "diagnostics", 2 * 1024 * 1024); const indexes = createTrackedProcessor(endpoint, 1024, "indexes", 8 * 1024 * 1024); queues.push(metadata, bodies, diagnostics, indexes); provider = new LoggerProvider({ forceFlushTimeoutMillis: METADATA_RETENTION_MS + OTLP_EXPORT_CALLBACK_DEADLINE_MS + 2000, resource: resourceFromAttributes({ "service.name": process.env.OTEL_SERVICE_NAME ?? "neurolink-proxy", "service.instance.id": `${role}-${process.pid}`, "process.pid": process.pid, "proxy.process.role": role, }), processors: [ { onEmit(record, context) { (record.attributes?.["proxy.record_kind"] === "body" ? bodies : record.attributes?.["proxy.record_kind"] === "body_capture_index" ? indexes : record.attributes?.["proxy.lifecycle.event"] === "runtime_sample" || DIAGNOSTIC_KINDS.has(String(record.attributes?.["proxy.record_kind"])) ? diagnostics : metadata).processor.onEmit(record, context); }, forceFlush: async () => { await Promise.all(queues.map((q) => q.processor.forceFlush())); }, shutdown: async () => { await Promise.all(queues.map((q) => q.processor.shutdown())); }, }, ], }); return provider; } /** Structured evidence without final-request dashboard fields on auxiliary events. */ export function emitProxyOtelEvent(kind, record) { if (!isProxyOtelOnly()) { return; } try { const ids = typeof record.requestId === "string" && !record.traceId ? getProxyRequestTraceContext(record.requestId) : undefined; const correlated = ids ? { ...record, ...ids } : record; initializeProxyOtelLogs() ?.getLogger("neurolink-proxy-events") .emit({ context: proxyLogContext(correlated), severityNumber: SeverityNumber.INFO, severityText: "INFO", body: JSON.stringify(correlated), attributes: { "proxy.record_kind": kind, "event.name": `proxy.${kind}`, ...(typeof record.requestId === "string" ? { "request.id": record.requestId } : {}), ...(typeof record.event === "string" ? { "proxy.lifecycle.event": record.event } : {}), }, }); } catch { // Telemetry must never fail a model request. Invalid records are observable. invalidRecords++; } } let invalidRecords = 0; /** Capture application console diagnostics only inside proxy service processes. */ export function routeProxyConsoleToOtel() { if (!isProxyOtelOnly() || restoreConsole) { return; } initializeProxyOtelLogs(); const originals = { log: console.log, info: console.info, warn: console.warn, error: console.error, debug: console.debug, }; let emitting = false; for (const level of Object.keys(originals)) { console[level] = (...args) => { if (emitting) { return; } emitting = true; try { const body = args .map((value) => typeof value === "string" ? value : inspect(value, { depth: 4, maxArrayLength: 30, maxStringLength: 16000, })) .join(" "); provider?.getLogger("neurolink-proxy-console").emit({ body: sanitizeForLog(body, 32000), severityText: level.toUpperCase(), severityNumber: level === "error" ? SeverityNumber.ERROR : level === "warn" ? SeverityNumber.WARN : level === "debug" ? SeverityNumber.DEBUG : SeverityNumber.INFO, attributes: { "proxy.record_kind": "console" }, }); } catch { invalidRecords++; } finally { emitting = false; } }; } restoreConsole = () => Object.assign(console, originals); } /** Counters acknowledge collector transport only, never backend persistence. */ export function getProxyOtelLogSnapshot() { return { mode: isProxyOtelOnly() ? "otel" : "file-and-otel", initialized: provider !== undefined, deliveryGuarantee: "best-effort; HTTP success is not per-record acceptance or backend persistence", invalidRecords, bodyDelivery: { ...bodyDelivery }, queues: queues.map((q) => ({ kind: q.kind, capacity: q.capacity, byteCapacity: q.byteCapacity, retentionMs: q.retentionMs, oldestOutstandingAgeMs: q.oldestOutstandingAgeMs(), ...q.state, recentFailures: q.state.recentFailures.map((failure) => ({ ...failure, records: failure.records.map((record) => ({ ...record })), })), })), }; } /** Bounded provider flush belongs after final request and lifecycle publication. */ export async function flushProxyOtelLogs() { await Promise.allSettled([...bodyPublicationOperations]); await provider?.forceFlush(); } /** Release this process's exporter and restore console ownership. */ export async function shutdownProxyOtelLogs() { shuttingDown = true; for (const queue of queues) { queue.stopRetention(); } // Service cleanup gives telemetry five seconds. End waiting retries now, // then let healthy exports drain; cancel unfinished HTTP work before that // outer deadline. Cancellation remains unconfirmed, never acknowledged. const deadline = setTimeout(() => { for (const queue of queues) { queue.abortPending(); } }, 4000); deadline.unref(); try { await Promise.allSettled([...bodyPublicationOperations]); restoreConsole?.(); restoreConsole = undefined; await provider?.shutdown(); } finally { clearTimeout(deadline); } provider = undefined; queues.length = 0; invalidRecords = 0; bodyPublications.clear(); bodyPublicationOperations.clear(); shuttingDown = false; for (const key of [ "attempted", "transportAcknowledged", "exportUnconfirmed", "rejected", "partial", "pending", "pendingBytes", "highWaterPending", "highWaterBytes", ]) { bodyDelivery[key] = 0; } } /** Flush short-lived proxy command diagnostics on every normal return or exception. */ export function withProxyOtelLogShutdown(handler) { return async (arg) => { try { await handler(arg); } finally { await shutdownProxyOtelLogs().catch(() => undefined); } }; }