@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
@@ -24,7 +24,11 @@ import { loadAccountQuotas, saveAccountQuota, } from "../../proxy/accountQuota.j
24
24
  import { createCodexUsageTap } from "../../proxy/codexUsage.js";
25
25
  import { CODEX_ACCOUNT_PREFIX, parseCodexRateLimitHeaders, } from "../../proxy/codexAccountUsage.js";
26
26
  import { buildClientAttribution } from "../../proxy/clientAttribution.js";
27
- import { registerProxyResponseObserver } from "../../proxy/proxyActivity.js";
27
+ import { registerProxyResponseObserver, isProxyRequestFinalized, } from "../../proxy/proxyActivity.js";
28
+ import { prepareProxyRequestContext, ProxyContextPreflightError, } from "../../proxy/proxyContextPreflight.js";
29
+ import { reserveProxyTokenBudget, settleProxyTokenBudget, getProxyTokenBudgetError, getProxyTokenBudgetSessionKey, } from "../../proxy/proxyTokenBudget.js";
30
+ import { emitProxyOtelEvent } from "../../proxy/otelLogSink.js";
31
+ import { getRuntimeContextWindow, clearRuntimeOutputCeiling, registerRuntimeContextWindow, registerRuntimeOutputCeiling, } from "../../constants/contextWindows.js";
28
32
  import { logRequest, logRequestAttempt, logBodyCapture, isProxyBodyCaptureEnabled, } from "../../proxy/requestLogger.js";
29
33
  import { createRawStreamCapture } from "../../proxy/rawStreamCapture.js";
30
34
  import { resolveProxyLogTraceContext } from "../../proxy/proxyTraceContext.js";
@@ -130,8 +134,14 @@ const BLOCKED_UPSTREAM_HEADERS = new Set([
130
134
  "accept-encoding",
131
135
  ]);
132
136
  /** Build a Codex error body as a Response with the intended status. */
133
- function buildCodexErrorResponse(status, message) {
134
- return new Response(JSON.stringify({ error: { type: "proxy_error", message } }), { status, headers: { "content-type": "application/json" } });
137
+ function buildCodexErrorResponse(status, message, code) {
138
+ return new Response(JSON.stringify({
139
+ error: {
140
+ type: "proxy_error",
141
+ message,
142
+ ...(code ? { code, retryable: false } : {}),
143
+ },
144
+ }), { status, headers: { "content-type": "application/json" } });
135
145
  }
136
146
  /**
137
147
  * Load the Codex account pool from the token store, refreshing expired OAuth
@@ -404,8 +414,39 @@ function captureCodexResponse(ctx, response, phase, account, attempt) {
404
414
  }
405
415
  async function executeCodexResponsesRequest(ctx) {
406
416
  const requestStartTime = Date.now();
407
- const body = ctx.body ?? {};
408
- const bodyStr = JSON.stringify(body);
417
+ let body = (ctx.body ?? {});
418
+ let bodyStr;
419
+ let preparedContext;
420
+ let contextPreflight;
421
+ let budgetLease;
422
+ let budgetDispatched = false;
423
+ const settleBudget = async (actualTokens) => {
424
+ if (!budgetLease) {
425
+ return;
426
+ }
427
+ try {
428
+ if (budgetDispatched) {
429
+ await settleProxyTokenBudget(budgetLease, actualTokens);
430
+ if (budgetLease.snapshot.settlement === "unconfirmed") {
431
+ emitProxyOtelEvent("token_budget", {
432
+ requestId: ctx.requestId,
433
+ event: "settlement_unconfirmed",
434
+ });
435
+ }
436
+ }
437
+ else {
438
+ await budgetLease.cancelBeforeDispatch();
439
+ }
440
+ }
441
+ catch (error) {
442
+ emitProxyOtelEvent("token_budget", {
443
+ requestId: ctx.requestId,
444
+ event: "settlement_unconfirmed",
445
+ errorCode: getProxyTokenBudgetError(error)?.code ??
446
+ "PROXY_TOKEN_BUDGET_UNAVAILABLE",
447
+ });
448
+ }
449
+ };
409
450
  const model = typeof body.model === "string"
410
451
  ? body.model
411
452
  : "-";
@@ -434,6 +475,7 @@ async function executeCodexResponsesRequest(ctx) {
434
475
  provider: "openai",
435
476
  userAgent: ctx.headers["user-agent"],
436
477
  recordRequestMetrics: !isFallbackRequest,
478
+ recordUsageMetrics: true,
437
479
  }, ctx.headers);
438
480
  }
439
481
  catch {
@@ -445,6 +487,9 @@ async function executeCodexResponsesRequest(ctx) {
445
487
  method: ctx.method,
446
488
  path: ctx.path,
447
489
  model,
490
+ requestedModel: model,
491
+ contextPreflight,
492
+ tokenBudget: budgetLease ? { ...budgetLease.snapshot } : undefined,
448
493
  stream: true,
449
494
  toolCount: Array.isArray(body.tools)
450
495
  ? body.tools.length
@@ -466,10 +511,28 @@ async function executeCodexResponsesRequest(ctx) {
466
511
  });
467
512
  let finalOutcomeRecorded = false;
468
513
  const recordFinalOutcome = async (account, responseStatus, extra = {}) => {
514
+ await settleBudget(extra.inputTokens !== undefined && extra.outputTokens !== undefined
515
+ ? extra.inputTokens + extra.outputTokens
516
+ : undefined);
469
517
  if (finalOutcomeRecorded) {
470
518
  return;
471
519
  }
472
520
  finalOutcomeRecorded = true;
521
+ if (isProxyRequestFinalized(ctx.requestId)) {
522
+ // Runtime owns the client final, but this route still owns its span.
523
+ const status = ctx.abortSignal?.aborted
524
+ ? ctx.abortSignal.reason?.name === "TimeoutError"
525
+ ? 504
526
+ : 499
527
+ : responseStatus;
528
+ try {
529
+ tracer?.end(status, Date.now() - requestStartTime);
530
+ }
531
+ catch {
532
+ /* best effort */
533
+ }
534
+ return;
535
+ }
473
536
  try {
474
537
  if (extra.errorType) {
475
538
  tracer?.setError(extra.errorType, extra.errorMessage ?? extra.errorType);
@@ -510,6 +573,9 @@ async function executeCodexResponsesRequest(ctx) {
510
573
  method: ctx.method,
511
574
  path: ctx.path,
512
575
  model,
576
+ requestedModel: model,
577
+ contextPreflight,
578
+ tokenBudget: budgetLease ? { ...budgetLease.snapshot } : undefined,
513
579
  stream: true,
514
580
  toolCount: Array.isArray(body.tools)
515
581
  ? body.tools.length
@@ -525,6 +591,30 @@ async function executeCodexResponsesRequest(ctx) {
525
591
  }).catch(() => undefined);
526
592
  };
527
593
  const dispatch = async () => {
594
+ try {
595
+ preparedContext = prepareProxyRequestContext({
596
+ provider: "codex",
597
+ model,
598
+ body,
599
+ });
600
+ contextPreflight = preparedContext.evidence;
601
+ body = preparedContext.body;
602
+ bodyStr = JSON.stringify(body);
603
+ }
604
+ catch (error) {
605
+ if (!(error instanceof ProxyContextPreflightError)) {
606
+ throw error;
607
+ }
608
+ contextPreflight = error.evidence;
609
+ await recordFinalOutcome(undefined, error.status, {
610
+ errorType: "context_preflight",
611
+ errorCode: error.code,
612
+ errorMessage: error.message,
613
+ retryable: false,
614
+ terminalOutcome: "handler_error",
615
+ });
616
+ return buildCodexErrorResponse(error.status, error.message, error.code);
617
+ }
528
618
  const accounts = await loadCodexProxyAccounts();
529
619
  const cancelRequest = async (account) => {
530
620
  await recordFinalOutcome(account, 499, {
@@ -601,6 +691,36 @@ async function executeCodexResponsesRequest(ctx) {
601
691
  if (ctx.abortSignal?.aborted) {
602
692
  return cancelRequest(lastAttemptedAccount);
603
693
  }
694
+ await settleBudget();
695
+ budgetLease = undefined;
696
+ budgetDispatched = false;
697
+ try {
698
+ budgetLease = await reserveProxyTokenBudget({
699
+ provider: "codex",
700
+ accountKey: account.key,
701
+ sessionKey: getProxyTokenBudgetSessionKey(new Headers(ctx.headers)),
702
+ requestId: ctx.requestId,
703
+ reservationTokens: preparedContext.totalTokensReservation,
704
+ estimateProvenance: "estimated_input_plus_serving_model_output_reserve",
705
+ });
706
+ }
707
+ catch (error) {
708
+ const budgetError = getProxyTokenBudgetError(error);
709
+ if (!budgetError) {
710
+ throw error;
711
+ }
712
+ await recordFinalOutcome(account, budgetError.status, {
713
+ errorType: "token_budget",
714
+ errorCode: budgetError.code,
715
+ errorMessage: budgetError.message,
716
+ retryable: false,
717
+ terminalOutcome: "handler_error",
718
+ });
719
+ return buildCodexErrorResponse(budgetError.status, budgetError.message, budgetError.code);
720
+ }
721
+ if (ctx.abortSignal?.aborted) {
722
+ return cancelRequest(account);
723
+ }
604
724
  attempt += 1;
605
725
  const attemptStartedAt = Date.now();
606
726
  lastAttemptedAccount = account;
@@ -622,6 +742,7 @@ async function executeCodexResponsesRequest(ctx) {
622
742
  attempt,
623
743
  ...tracer?.getTraceContext(),
624
744
  });
745
+ budgetDispatched = true;
625
746
  upstream = await fetch(CODEX_RESPONSES_URL, {
626
747
  method: "POST",
627
748
  headers: upstreamHeaders,
@@ -742,8 +863,12 @@ async function executeCodexResponsesRequest(ctx) {
742
863
  !semantic.completed);
743
864
  const usageExtra = usage
744
865
  ? {
745
- inputTokens: usage.inputTokens,
746
- outputTokens: usage.outputTokens,
866
+ inputTokens: usage.inputTokensObserved
867
+ ? usage.inputTokens
868
+ : undefined,
869
+ outputTokens: usage.outputTokensObserved
870
+ ? usage.outputTokens
871
+ : undefined,
747
872
  cacheReadTokens: usage.cacheReadTokens,
748
873
  cacheCreationTokens: usage.cacheCreationTokens,
749
874
  reasoningTokens: usage.reasoningTokensObserved
@@ -751,7 +876,8 @@ async function executeCodexResponsesRequest(ctx) {
751
876
  : undefined,
752
877
  }
753
878
  : {};
754
- if (usage) {
879
+ if (usage?.inputTokensObserved &&
880
+ usage.outputTokensObserved) {
755
881
  try {
756
882
  tracer?.setUsage({
757
883
  ...usage,
@@ -781,6 +907,11 @@ async function executeCodexResponsesRequest(ctx) {
781
907
  if (failed) {
782
908
  recordAttemptError(account.label, CODEX_ACCOUNT_TYPE, 502);
783
909
  writeAttempt(account, attempt, attemptStartedAt, 502, {
910
+ // An internal fallback has no separate final row. Keep
911
+ // its observed usage on this attempt when a later
912
+ // provider owns the client final.
913
+ ...usageExtra,
914
+ inputIncludesCachedTokens: true,
784
915
  errorType: semantic.errorType ?? "incomplete_stream",
785
916
  errorCode: semantic.errorCode,
786
917
  errorMessage: semantic.errorMessage ??
@@ -963,6 +1094,7 @@ async function executeCodexResponsesRequest(ctx) {
963
1094
  return await dispatch();
964
1095
  }
965
1096
  catch (error) {
1097
+ await settleBudget();
966
1098
  try {
967
1099
  tracer?.end(502, Date.now() - requestStartTime);
968
1100
  }
@@ -972,6 +1104,50 @@ async function executeCodexResponsesRequest(ctx) {
972
1104
  throw error;
973
1105
  }
974
1106
  }
1107
+ /** Accept exact positive limits advertised by successful Codex model discovery. */
1108
+ function registerCodexDiscoveredLimits(body) {
1109
+ if (Buffer.byteLength(body, "utf8") > 2 * 1024 * 1024) {
1110
+ return;
1111
+ }
1112
+ let parsed;
1113
+ try {
1114
+ parsed = JSON.parse(body);
1115
+ }
1116
+ catch {
1117
+ return;
1118
+ }
1119
+ if (!parsed ||
1120
+ typeof parsed !== "object" ||
1121
+ !("models" in parsed) ||
1122
+ !Array.isArray(parsed.models)) {
1123
+ return;
1124
+ }
1125
+ for (const candidate of parsed.models.slice(0, 1024)) {
1126
+ if (!candidate || typeof candidate !== "object") {
1127
+ continue;
1128
+ }
1129
+ const model = candidate.slug ?? candidate.id;
1130
+ if (typeof model !== "string" || model.length === 0 || model.length > 256) {
1131
+ continue;
1132
+ }
1133
+ const context = candidate.context_window;
1134
+ const output = candidate.max_output_tokens;
1135
+ if (Number.isSafeInteger(context) && context > 0) {
1136
+ registerRuntimeContextWindow("codex", model, context);
1137
+ }
1138
+ const knownContext = getRuntimeContextWindow("codex", model);
1139
+ if (Number.isSafeInteger(output) &&
1140
+ output > 0 &&
1141
+ (knownContext === undefined || output < knownContext)) {
1142
+ registerRuntimeOutputCeiling("codex", model, output);
1143
+ }
1144
+ else {
1145
+ // Rediscovery may shrink the context or stop advertising an output cap.
1146
+ // Do not preserve a stale ceiling that can make every input invalid.
1147
+ clearRuntimeOutputCeiling("codex", model);
1148
+ }
1149
+ }
1150
+ }
975
1151
  /**
976
1152
  * Relay Codex model discovery upstream.
977
1153
  *
@@ -1035,7 +1211,10 @@ async function handleCodexModelsRequest(ctx) {
1035
1211
  // Bound the upstream call, as the responses route does. Without a
1036
1212
  // signal a stalled connection holds the proxy request open with no
1037
1213
  // ceiling, and the CLI blocks on model discovery at startup.
1038
- signal: AbortSignal.timeout(CODEX_UPSTREAM_TIMEOUT_MS),
1214
+ signal: AbortSignal.any([
1215
+ ...(ctx.abortSignal ? [ctx.abortSignal] : []),
1216
+ AbortSignal.timeout(CODEX_UPSTREAM_TIMEOUT_MS),
1217
+ ]),
1039
1218
  });
1040
1219
  }
1041
1220
  catch (error) {
@@ -1075,6 +1254,9 @@ async function handleCodexModelsRequest(ctx) {
1075
1254
  // query was not forwarded correctly, and hiding it behind a retry would
1076
1255
  // bury the exact regression this route was added to fix.
1077
1256
  const body = await upstream.text();
1257
+ if (upstream.ok) {
1258
+ registerCodexDiscoveredLimits(body);
1259
+ }
1078
1260
  const contentType = upstream.headers.get("content-type") ?? "application/json";
1079
1261
  return new Response(body, {
1080
1262
  status: upstream.status,
@@ -31,7 +31,7 @@ declare function resolveStreamCancellationLifecycle(terminalStreamError: string
31
31
  * headers — to avoid SSRF.
32
32
  * @returns RouteGroup with OpenAI-compatible endpoints.
33
33
  */
34
- export declare function createOpenAIProxyRoutes(modelRouter?: ModelRouterInterface, basePath?: string, loopbackPort?: number, runtimeConfigProvider?: ProxyRuntimeConfigProvider): RouteGroup;
34
+ export declare function createOpenAIProxyRoutes(modelRouter?: ModelRouterInterface, basePath?: string, loopbackPort?: number, runtimeConfigProvider?: ProxyRuntimeConfigProvider, internalDispatch?: (request: Request) => Response | Promise<Response>): RouteGroup;
35
35
  export declare const __testHooks: {
36
36
  resolveStreamCancellationLifecycle: typeof resolveStreamCancellationLifecycle;
37
37
  };