@juspay/neurolink 12.17.1 → 12.17.3

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 (73) hide show
  1. package/CHANGELOG.md +6 -2
  2. package/dist/browser/neurolink.min.js +430 -421
  3. package/dist/cli/commands/proxy.js +455 -259
  4. package/dist/constants/contextWindows.d.ts +2 -0
  5. package/dist/constants/contextWindows.js +4 -0
  6. package/dist/neurolink.js +8 -3
  7. package/dist/providers/openaiChatCompletionsBase.js +60 -51
  8. package/dist/proxy/anthropicBudgetResponse.d.ts +3 -0
  9. package/dist/proxy/anthropicBudgetResponse.js +153 -0
  10. package/dist/proxy/bodyCaptureProcessing.d.ts +1 -0
  11. package/dist/proxy/bodyCaptureProcessing.js +23 -4
  12. package/dist/proxy/bodyCaptureWorker.js +20 -2
  13. package/dist/proxy/codexFallback.d.ts +10 -0
  14. package/dist/proxy/codexFallback.js +108 -24
  15. package/dist/proxy/codexUsage.js +2 -0
  16. package/dist/proxy/globalInstaller.d.ts +32 -1
  17. package/dist/proxy/globalInstaller.js +386 -3
  18. package/dist/proxy/otelLogSink.d.ts +3 -3
  19. package/dist/proxy/otelLogSink.js +137 -144
  20. package/dist/proxy/otlpLogTransport.d.ts +3 -0
  21. package/dist/proxy/otlpLogTransport.js +334 -0
  22. package/dist/proxy/proxyActivity.d.ts +22 -2
  23. package/dist/proxy/proxyActivity.js +114 -9
  24. package/dist/proxy/proxyAnalysis.js +82 -52
  25. package/dist/proxy/proxyClientCancellation.d.ts +7 -0
  26. package/dist/proxy/proxyClientCancellation.js +23 -0
  27. package/dist/proxy/proxyContextPreflight.d.ts +25 -0
  28. package/dist/proxy/proxyContextPreflight.js +337 -0
  29. package/dist/proxy/proxyFailureDetails.d.ts +19 -0
  30. package/dist/proxy/proxyFailureDetails.js +96 -0
  31. package/dist/proxy/proxyLifecycle.js +7 -0
  32. package/dist/proxy/proxyRouteBodyCapture.d.ts +13 -0
  33. package/dist/proxy/proxyRouteBodyCapture.js +112 -0
  34. package/dist/proxy/proxyTokenBudget.d.ts +40 -0
  35. package/dist/proxy/proxyTokenBudget.js +523 -0
  36. package/dist/proxy/proxyTracer.d.ts +3 -2
  37. package/dist/proxy/proxyTracer.js +46 -20
  38. package/dist/proxy/proxyTranslationEngine.d.ts +8 -10
  39. package/dist/proxy/proxyTranslationEngine.js +736 -376
  40. package/dist/proxy/requestLogger.d.ts +1 -0
  41. package/dist/proxy/requestLogger.js +107 -5
  42. package/dist/proxy/restartControl.js +9 -4
  43. package/dist/proxy/rollingWorkerProcess.js +65 -0
  44. package/dist/proxy/rollingWorkerSupervisor.js +7 -0
  45. package/dist/proxy/updateCoordinator.d.ts +14 -1
  46. package/dist/proxy/updateCoordinator.js +57 -2
  47. package/dist/server/routes/claudeProxyRoutes.d.ts +4 -5
  48. package/dist/server/routes/claudeProxyRoutes.js +809 -577
  49. package/dist/server/routes/codexProxyRoutes.js +191 -9
  50. package/dist/server/routes/openaiProxyRoutes.d.ts +1 -1
  51. package/dist/server/routes/openaiProxyRoutes.js +261 -69
  52. package/dist/types/cli.d.ts +3 -0
  53. package/dist/types/codex.d.ts +5 -1
  54. package/dist/types/index.d.ts +2 -0
  55. package/dist/types/index.js +2 -0
  56. package/dist/types/proxy.d.ts +148 -3
  57. package/dist/types/proxy.js +0 -15
  58. package/dist/types/proxyBudget.d.ts +74 -0
  59. package/dist/types/proxyBudget.js +1 -0
  60. package/dist/types/proxyContext.d.ts +35 -0
  61. package/dist/types/proxyContext.js +1 -0
  62. package/dist/utils/fileDetector.js +28 -7
  63. package/dist/utils/messageBuilder.js +51 -26
  64. package/dist/utils/pdfProcessor.js +26 -4
  65. package/docs/assets/dashboards/README.md +37 -0
  66. package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +197 -89
  67. package/docs-site/static/search-index.json +1 -1
  68. package/package.json +7 -2
  69. package/scripts/observability/NATIVE-DURABLE.md +63 -0
  70. package/scripts/observability/otel-collector.proxy-native-durable.yaml +93 -0
  71. package/scripts/observability/proxy-telemetry-backend.mjs +32 -5
  72. package/scripts/observability/proxy-telemetry-check.mjs +396 -61
  73. package/scripts/observability/query-proxy-history.mjs +34 -5
@@ -0,0 +1,96 @@
1
+ import { sanitizeForLog } from "../utils/logSanitize.js";
2
+ /** Shared provider terminal codes across native and SDK transports. */
3
+ export function classifyProxyFailureCode(code) {
4
+ if ([
5
+ "cyber_policy",
6
+ "content_policy_violation",
7
+ "content_filter",
8
+ "safety_violation",
9
+ ].includes(code ?? "")) {
10
+ return { status: 403, retryable: false };
11
+ }
12
+ if ([
13
+ "max_output_tokens",
14
+ "context_length_exceeded",
15
+ "invalid_request_error",
16
+ "empty_response",
17
+ ].includes(code ?? "")) {
18
+ return { status: undefined, retryable: false };
19
+ }
20
+ return {
21
+ status: code === "rate_limit_exceeded" ? 429 : undefined,
22
+ retryable: undefined,
23
+ };
24
+ }
25
+ /** Read explicit SDK/API failure evidence without guessing from free-form text.
26
+ * Bounded traversal covers common error/cause/data and JSON response envelopes. */
27
+ export function getProxyUpstreamFailure(error) {
28
+ let status;
29
+ let code;
30
+ let retryable;
31
+ let message;
32
+ const queue = [error];
33
+ const seen = new Set();
34
+ for (let index = 0; index < queue.length && index < 16; index++) {
35
+ const item = queue[index];
36
+ if (!item ||
37
+ typeof item !== "object" ||
38
+ Array.isArray(item) ||
39
+ seen.has(item)) {
40
+ continue;
41
+ }
42
+ seen.add(item);
43
+ const value = item;
44
+ const candidateStatus = value.status ?? value.statusCode;
45
+ if (typeof candidateStatus === "number" &&
46
+ Number.isInteger(candidateStatus) &&
47
+ candidateStatus >= 400 &&
48
+ candidateStatus <= 599) {
49
+ status ??= candidateStatus;
50
+ }
51
+ if (code === undefined && typeof value.code === "string" && value.code) {
52
+ code = sanitizeForLog(value.code, 200);
53
+ if (typeof value.message === "string") {
54
+ message = sanitizeForLog(value.message, 500);
55
+ }
56
+ }
57
+ const candidateRetry = value.retryable ?? value.isRetryable;
58
+ if (candidateRetry === false) {
59
+ retryable = false;
60
+ }
61
+ else if (candidateRetry === true) {
62
+ retryable ??= true;
63
+ }
64
+ if (typeof value.message === "string" && message === undefined) {
65
+ message = sanitizeForLog(value.message, 500);
66
+ }
67
+ for (const key of ["error", "cause", "data", "response"]) {
68
+ if (value[key] !== undefined && queue.length < 16) {
69
+ queue.push(value[key]);
70
+ }
71
+ }
72
+ if (typeof value.responseBody === "string" &&
73
+ value.responseBody.length <= 65536 &&
74
+ queue.length < 16) {
75
+ try {
76
+ queue.push(JSON.parse(value.responseBody));
77
+ }
78
+ catch {
79
+ /* Plain text is not structured evidence. */
80
+ }
81
+ }
82
+ }
83
+ if (error instanceof Error && error.name === "TimeoutError") {
84
+ status ??= 504;
85
+ }
86
+ if (status === undefined && code === undefined && retryable === undefined) {
87
+ return undefined;
88
+ }
89
+ const known = classifyProxyFailureCode(code);
90
+ return {
91
+ status: status ?? known.status ?? 502,
92
+ code,
93
+ retryable: known.retryable ?? retryable,
94
+ message,
95
+ };
96
+ }
@@ -378,6 +378,8 @@ function enqueueLifecycleEvent(input, onPersisted) {
378
378
  const observedBodyBytes = nonNegativeInteger(input.observedBodyBytes);
379
379
  const responseChunks = nonNegativeInteger(input.responseChunks);
380
380
  const elapsedMs = finiteNonNegative(input.elapsedMs);
381
+ const requestTimeoutMs = finiteNonNegative(input.requestTimeoutMs);
382
+ const parentRequestId = clip(input.parentRequestId);
381
383
  const model = clip(input.model);
382
384
  const sessionHash = clip(input.sessionHash);
383
385
  const terminalOutcome = clip(input.terminalOutcome);
@@ -391,6 +393,11 @@ function enqueueLifecycleEvent(input, onPersisted) {
391
393
  sequence,
392
394
  event: clip(input.event) ?? "unknown",
393
395
  requestId: clip(input.requestId) ?? "unknown",
396
+ ...(parentRequestId !== undefined ? { parentRequestId } : {}),
397
+ ...(input.accountingScope !== undefined
398
+ ? { accountingScope: input.accountingScope }
399
+ : {}),
400
+ ...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}),
394
401
  method: clip(input.method) ?? "unknown",
395
402
  path: clip(input.path) ?? "unknown",
396
403
  ...(model !== undefined ? { model } : {}),
@@ -0,0 +1,13 @@
1
+ import type { ProxyBodyCaptureInput, ServerContext } from "../types/index.js";
2
+ /** Capture serialized client output and explicitly labelled SDK observations.
3
+ * This observer never reads a stream or creates a second consumer. */
4
+ export declare function createProxyRouteBodyCapture(ctx: ServerContext, model: string, stream: boolean, startedAt: number): {
5
+ log: (capture: ProxyBodyCaptureInput) => void;
6
+ request(): void;
7
+ json(body: unknown, responseStatus: number): void;
8
+ accumulator(phase: string, contentType: string, attempt?: number): {
9
+ append: (value: Uint8Array) => void;
10
+ appendEvent(value: unknown): void;
11
+ finish(responseStatus: number): void;
12
+ };
13
+ };
@@ -0,0 +1,112 @@
1
+ import { isProxyBodyCaptureEnabled, logBodyCapture } from "./requestLogger.js";
2
+ const MAX_PREFIX_BYTES = 1024 * 1024;
3
+ const MAX_ACTIVE_PREFIX_BYTES = 16 * 1024 * 1024;
4
+ let activePrefixBytes = 0;
5
+ /** Capture serialized client output and explicitly labelled SDK observations.
6
+ * This observer never reads a stream or creates a second consumer. */
7
+ export function createProxyRouteBodyCapture(ctx, model, stream, startedAt) {
8
+ const log = (capture) => {
9
+ void logBodyCapture({
10
+ timestamp: new Date().toISOString(),
11
+ requestId: ctx.requestId,
12
+ model,
13
+ stream,
14
+ durationMs: Date.now() - startedAt,
15
+ ...capture,
16
+ });
17
+ };
18
+ return {
19
+ log,
20
+ request() {
21
+ log({
22
+ phase: "client_request",
23
+ headers: ctx.headers,
24
+ body: ctx.body,
25
+ contentType: "application/json",
26
+ });
27
+ },
28
+ json(body, responseStatus) {
29
+ log({
30
+ phase: "client_response",
31
+ headers: { "content-type": "application/json" },
32
+ body,
33
+ contentType: "application/json",
34
+ responseStatus,
35
+ });
36
+ },
37
+ accumulator(phase, contentType, attempt) {
38
+ const enabled = isProxyBodyCaptureEnabled();
39
+ const chunks = [];
40
+ let totalBytes = 0;
41
+ let retainedBytes = 0;
42
+ let truncated = false;
43
+ let finished = false;
44
+ const append = (value) => {
45
+ if (finished || !enabled) {
46
+ return;
47
+ }
48
+ totalBytes += value.byteLength;
49
+ const remaining = Math.min(MAX_PREFIX_BYTES - retainedBytes, MAX_ACTIVE_PREFIX_BYTES - activePrefixBytes);
50
+ if (!truncated && remaining > 0) {
51
+ const prefix = Uint8Array.from(value.subarray(0, remaining));
52
+ chunks.push(prefix);
53
+ retainedBytes += prefix.byteLength;
54
+ activePrefixBytes += prefix.byteLength;
55
+ truncated = prefix.byteLength !== value.byteLength;
56
+ }
57
+ else {
58
+ truncated ||= value.byteLength > 0;
59
+ }
60
+ };
61
+ return {
62
+ append,
63
+ appendEvent(value) {
64
+ if (finished || !enabled) {
65
+ return;
66
+ }
67
+ try {
68
+ append(new TextEncoder().encode(`data: ${JSON.stringify(value) ?? "null"}\n\n`));
69
+ }
70
+ catch {
71
+ // Diagnostic serialization cannot fail a provider request. Mark
72
+ // unavailable bytes as an explicitly incomplete capture instead.
73
+ truncated = true;
74
+ }
75
+ },
76
+ finish(responseStatus) {
77
+ if (finished) {
78
+ return;
79
+ }
80
+ finished = true;
81
+ if (!enabled) {
82
+ return;
83
+ }
84
+ const decoder = new TextDecoder();
85
+ const body = chunks
86
+ .map((chunk) => decoder.decode(chunk, { stream: true }))
87
+ .join("") + (truncated ? "" : decoder.decode());
88
+ activePrefixBytes -= retainedBytes;
89
+ chunks.length = 0;
90
+ log({
91
+ phase,
92
+ headers: { "content-type": contentType },
93
+ body,
94
+ bodySize: totalBytes,
95
+ sourceTruncated: truncated,
96
+ contentType,
97
+ responseStatus,
98
+ attempt,
99
+ ...(phase.startsWith("sdk_")
100
+ ? {
101
+ metadata: {
102
+ representation: "neurolink_sdk_events",
103
+ wireCapture: false,
104
+ },
105
+ }
106
+ : {}),
107
+ });
108
+ },
109
+ };
110
+ },
111
+ };
112
+ }
@@ -0,0 +1,40 @@
1
+ import type { ProxyTokenBudgetLease, ProxyTokenBudgetPolicy, ProxyTokenBudgetReservation, ProxyTokenBudgetRpcResponse, ProxyTokenBudgetSnapshot } from "../types/index.js";
2
+ /** Shared private session scope across every protocol and provider fallback. */
3
+ export declare function getProxyTokenBudgetSessionKey(headers: Pick<Headers, "get">): string;
4
+ /** Local admission failures must not be hidden by automatic provider fallback. */
5
+ export declare function getProxyTokenBudgetError(error: unknown): {
6
+ code: string;
7
+ status: 400 | 429 | 503;
8
+ retryable: false;
9
+ message: string;
10
+ } | null;
11
+ /** Invalid configured limits fail closed instead of silently disabling protection. */
12
+ export declare function parseProxyTokenBudgetPolicy(value: unknown): ProxyTokenBudgetPolicy;
13
+ export declare function getProxyTokenBudgetPolicy(): ProxyTokenBudgetPolicy | undefined;
14
+ /** One atomic coordinator per supervisor; requests never write budget state to disk. */
15
+ export declare class ProxyTokenBudgetCoordinator {
16
+ private readonly scope;
17
+ private readonly now;
18
+ private readonly maxKeys;
19
+ private readonly maxLeases;
20
+ private readonly buckets;
21
+ private readonly leases;
22
+ constructor(scope?: "process" | "supervisor", now?: () => number, maxKeys?: number, maxLeases?: number);
23
+ private bucket;
24
+ reserve(input: ProxyTokenBudgetReservation, owner?: string): {
25
+ leaseId: string;
26
+ snapshot: ProxyTokenBudgetSnapshot;
27
+ };
28
+ settle(leaseId: string, owner: string, actualTotalTokens?: number, cancelled?: boolean): ProxyTokenBudgetSnapshot | undefined;
29
+ ownerExited(owner: string): void;
30
+ }
31
+ /** Parent-side IPC hook is shared by every active and draining generation. */
32
+ export declare function handleProxyTokenBudgetMessage(value: unknown, owner: {
33
+ pid: number;
34
+ generation: number;
35
+ }, reply: (response: ProxyTokenBudgetRpcResponse) => void): boolean;
36
+ export declare function releaseProxyTokenBudgetOwner(pid: number, generation: number): void;
37
+ /** Reserve before each upstream attempt; unknown provider usage retains its estimate. */
38
+ export declare function reserveProxyTokenBudget(input: ProxyTokenBudgetReservation): Promise<ProxyTokenBudgetLease>;
39
+ /** Accounting acknowledgement loss must not turn delivered provider success into a stream failure. */
40
+ export declare function settleProxyTokenBudget(lease: ProxyTokenBudgetLease, actualTotalTokens?: number): Promise<void>;