@juspay/neurolink 12.14.13 → 12.14.15

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.14.13",
3
+ "version": "12.14.15",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {
@@ -95,6 +95,7 @@
95
95
  "test:proxy-sharing": "pnpm run build && pnpm exec tsx test/continuous-test-suite-proxy-sharing.ts",
96
96
  "test:openai-compat-streaming-retry": "pnpm exec tsx test/continuous-test-suite-openai-compat-streaming-retry.ts",
97
97
  "test:anthropic-streaming-retry": "pnpm exec tsx test/continuous-test-suite-anthropic-streaming-retry.ts",
98
+ "test:anthropic-silent-truncation": "pnpm exec tsx test/continuous-test-suite-anthropic-silent-truncation.ts",
98
99
  "test:adjust-body-after-400": "pnpm exec tsx test/continuous-test-suite-adjust-body-after-400.ts",
99
100
  "test:zod3-schema-native-path": "pnpm exec tsx test/continuous-test-suite-zod3-schema-native-path.ts",
100
101
  "test:error-classification-e2e": "pnpm exec tsx test/continuous-test-suite-error-classification-e2e.ts",
@@ -18,6 +18,7 @@ services:
18
18
  NEUROLINK_PROXY_STREAM_HEADER: ${NEUROLINK_PROXY_STREAM_HEADER:-neurolink-proxy}
19
19
  volumes:
20
20
  - ./otel-collector.proxy-observability.yaml:/etc/otelcol/config.yaml:ro
21
+ - otel-collector-data:/var/lib/otelcol
21
22
  ports:
22
23
  - "${NEUROLINK_OTLP_GRPC_PORT:-14317}:4317"
23
24
  - "${NEUROLINK_OTLP_HTTP_PORT:-14318}:4318"
@@ -41,6 +42,7 @@ services:
41
42
  ZO_DATA_DIR: /data
42
43
  RUST_LOG: ${NEUROLINK_OPENOBSERVE_LOG_LEVEL:-info}
43
44
  ZO_INGEST_ALLOWED_UPTO: ${NEUROLINK_OPENOBSERVE_INGEST_HOURS:-720}
45
+ ZO_COMPACT_DATA_RETENTION_DAYS: ${NEUROLINK_OPENOBSERVE_RETENTION_DAYS:-30}
44
46
  volumes:
45
47
  - openobserve-data:/data
46
48
  networks:
@@ -51,5 +53,7 @@ networks:
51
53
  driver: bridge
52
54
 
53
55
  volumes:
56
+ otel-collector-data:
57
+ name: ${NEUROLINK_OTEL_COLLECTOR_VOLUME_NAME:-neurolink-otel-collector-data}
54
58
  openobserve-data:
55
59
  name: ${NEUROLINK_OPENOBSERVE_VOLUME_NAME:-neurolink-openobserve-data}
@@ -38,8 +38,26 @@ exporters:
38
38
  stream-name: ${env:NEUROLINK_PROXY_STREAM_HEADER}
39
39
  tls:
40
40
  insecure: true
41
+ retry_on_failure:
42
+ enabled: true
43
+ initial_interval: 1s
44
+ max_interval: 30s
45
+ max_elapsed_time: 0s
46
+ sending_queue:
47
+ enabled: true
48
+ num_consumers: 4
49
+ queue_size: 4096
50
+ storage: file_storage
41
51
 
42
52
  extensions:
53
+ file_storage:
54
+ directory: /var/lib/otelcol/storage
55
+ create_directory: true
56
+ compaction:
57
+ directory: /var/lib/otelcol/compaction
58
+ on_start: true
59
+ on_rebound: true
60
+
43
61
  health_check:
44
62
  endpoint: 0.0.0.0:13133
45
63
 
@@ -47,7 +65,7 @@ extensions:
47
65
  endpoint: 0.0.0.0:1777
48
66
 
49
67
  service:
50
- extensions: [health_check, pprof]
68
+ extensions: [file_storage, health_check, pprof]
51
69
 
52
70
  pipelines:
53
71
  traces:
@@ -17,6 +17,10 @@ NEUROLINK_OTEL_PPROF_PORT=11777
17
17
  # Optional names if you want multiple local stacks in parallel.
18
18
  # NEUROLINK_OBSERVABILITY_PROJECT_NAME=neurolink-proxy-observability
19
19
  # NEUROLINK_OPENOBSERVE_VOLUME_NAME=neurolink-openobserve-data
20
+ # NEUROLINK_OTEL_COLLECTOR_VOLUME_NAME=neurolink-otel-collector-data
21
+
22
+ # Bound local telemetry storage while retaining enough incident history.
23
+ NEUROLINK_OPENOBSERVE_RETENTION_DAYS=30
20
24
 
21
25
  # The collector exports proxy telemetry into this OpenObserve stream header.
22
26
  # OpenObserve normalizes the logs and traces stream name to "neurolink_proxy".
@@ -112,30 +112,6 @@ export async function checkProxyTelemetry({
112
112
  failureHistoryEvicted: q.failureHistoryEvicted,
113
113
  }),
114
114
  );
115
- add(
116
- "producer_delivery",
117
- ["metadata", "bodies"].every((kind) =>
118
- failures.some((q) => q.kind === kind),
119
- ) &&
120
- failures.every(
121
- (q) =>
122
- q.dropped === 0 &&
123
- q.exportUnconfirmed === 0 &&
124
- q.outstanding < q.capacity,
125
- )
126
- ? "pass"
127
- : "warn",
128
- failures,
129
- );
130
- add(
131
- "capture_admission",
132
- logs?.bodyCapture &&
133
- logs.bodyCapture.rejected === 0 &&
134
- logs.bodyCapture.failed === 0
135
- ? "pass"
136
- : "warn",
137
- logs?.bodyCapture ?? { status: "unavailable" },
138
- );
139
115
  for (const [signal, table] of [
140
116
  ["logs", backend.stream],
141
117
  ["traces", backend.stream],
@@ -165,13 +141,24 @@ export async function checkProxyTelemetry({
165
141
  }
166
142
  /** @type {Record<string, import("../../src/lib/types/index.js").ProxyTelemetryStoredRecord[]>} */
167
143
  const history = Object.create(null);
168
- for (const kind of ["request_final", "body_capture_index", "lifecycle"]) {
144
+ for (const kind of [
145
+ "request_final",
146
+ "body_capture_index",
147
+ "lifecycle",
148
+ "telemetry_delivery",
149
+ ]) {
169
150
  const result = await queryProxyHistory({
170
151
  ...backend,
171
152
  // Capture publication is asynchronous. Include its bounded settling
172
153
  // window, and a request-start margin, when reconciling phases to finals.
173
- startTime: kind === "body_capture_index" ? Math.max(0, startTime - 120e6) : startTime,
174
- endTime: kind === "body_capture_index" ? Math.min(Date.now() * 1000, endTime + 120e6) : endTime,
154
+ startTime:
155
+ kind === "body_capture_index"
156
+ ? Math.max(0, startTime - 120e6)
157
+ : startTime,
158
+ endTime:
159
+ kind === "body_capture_index" || kind === "telemetry_delivery"
160
+ ? Math.min(Date.now() * 1000, endTime + 120e6)
161
+ : endTime,
175
162
  kind,
176
163
  maxRows,
177
164
  fetchImpl,
@@ -186,13 +173,143 @@ export async function checkProxyTelemetry({
186
173
  if (
187
174
  !value ||
188
175
  typeof value !== "object" ||
189
- typeof value.requestId !== "string"
176
+ (kind === "telemetry_delivery"
177
+ ? typeof value.id !== "string" ||
178
+ typeof value.at !== "string" ||
179
+ !Number.isFinite(Date.parse(value.at))
180
+ : typeof value.requestId !== "string")
190
181
  ) {
191
182
  throw new Error("Stored telemetry metadata has an invalid schema");
192
183
  }
193
184
  return { ...value, recordedAtMicroseconds: Number(row._timestamp) };
194
185
  });
195
186
  }
187
+ /** @param {{at?: unknown}} row */
188
+ const failedInsideInterval = (row) => {
189
+ const at = Date.parse(typeof row.at === "string" ? row.at : "");
190
+ return (
191
+ Number.isFinite(at) &&
192
+ at * 1000 >= startTime &&
193
+ at * 1000 < endTime
194
+ );
195
+ };
196
+ const storedDeliveryFailures = history.telemetry_delivery.filter(
197
+ failedInsideInterval,
198
+ );
199
+ const runtimeDeliveryFailures = failures.flatMap((queue) =>
200
+ Array.isArray(queue.recentFailures)
201
+ ? queue.recentFailures.filter(
202
+ (failure) =>
203
+ failure &&
204
+ typeof failure === "object" &&
205
+ failedInsideInterval(failure),
206
+ )
207
+ : [],
208
+ );
209
+ const deliveryFailures = [
210
+ ...new Map(
211
+ [...storedDeliveryFailures, ...runtimeDeliveryFailures].map(
212
+ (failure, index) => [
213
+ typeof failure.id === "string"
214
+ ? failure.id
215
+ : `unidentified-${index}`,
216
+ failure,
217
+ ],
218
+ ),
219
+ ).values(),
220
+ ];
221
+ const currentProducerPressure = failures.some(
222
+ (queue) => queue.outstanding >= queue.capacity,
223
+ );
224
+ const runtimeFailureHistoryIncomplete = failures.some((queue) => {
225
+ if (!(Number(queue.failureHistoryEvicted) > 0)) {
226
+ return false;
227
+ }
228
+ if (!Array.isArray(queue.recentFailures) || !queue.recentFailures.length) {
229
+ return true;
230
+ }
231
+ const oldestRetainedFailure = Math.min(
232
+ ...queue.recentFailures.map((failure) =>
233
+ Date.parse(typeof failure?.at === "string" ? failure.at : ""),
234
+ ),
235
+ );
236
+ // The bounded runtime buffer proves absence only from its oldest retained
237
+ // failure onward. Older selected windows need stored recovery diagnostics;
238
+ // if those are missing, report uncertainty instead of a false pass.
239
+ return (
240
+ !Number.isFinite(oldestRetainedFailure) ||
241
+ startTime < oldestRetainedFailure * 1000
242
+ );
243
+ });
244
+ add(
245
+ "producer_delivery",
246
+ !["metadata", "bodies"].every((kind) =>
247
+ failures.some((queue) => queue.kind === kind),
248
+ )
249
+ ? "unverified"
250
+ : currentProducerPressure || deliveryFailures.length
251
+ ? "warn"
252
+ : runtimeFailureHistoryIncomplete
253
+ ? "unverified"
254
+ : "pass",
255
+ {
256
+ scope: "selected_interval",
257
+ deliveryFailures: deliveryFailures.length,
258
+ failureIds: deliveryFailures.slice(0, 50).map((row) => row.id),
259
+ sources: {
260
+ storedRecoveryDiagnostics: storedDeliveryFailures.length,
261
+ runtimeRecentFailures: runtimeDeliveryFailures.length,
262
+ },
263
+ currentPressure: currentProducerPressure,
264
+ runtimeFailureHistoryComplete: !runtimeFailureHistoryIncomplete,
265
+ workerLifetime: {
266
+ scope: "worker_lifetime",
267
+ queues: failures,
268
+ },
269
+ },
270
+ );
271
+ const captureIndexes = history.body_capture_index.filter((row) => {
272
+ const eventTime = Date.parse(
273
+ typeof row.timestamp === "string" ? row.timestamp : "",
274
+ );
275
+ const atMicroseconds = Number.isFinite(eventTime)
276
+ ? eventTime * 1000
277
+ : row.recordedAtMicroseconds;
278
+ return (
279
+ Number.isFinite(atMicroseconds) &&
280
+ (atMicroseconds ?? 0) >= startTime &&
281
+ (atMicroseconds ?? Infinity) < endTime
282
+ );
283
+ });
284
+ const unhealthyCaptures = captureIndexes.filter(
285
+ (row) =>
286
+ row.captureError !== undefined ||
287
+ ![
288
+ "transport_acknowledged",
289
+ "no_body",
290
+ "policy_excluded",
291
+ ].includes(row.bodyDelivery?.status ?? ""),
292
+ );
293
+ add(
294
+ "capture_admission",
295
+ !captureIndexes.length
296
+ ? "unverified"
297
+ : unhealthyCaptures.length
298
+ ? "warn"
299
+ : "pass",
300
+ {
301
+ scope: "selected_interval",
302
+ captures: captureIndexes.length,
303
+ unhealthy: unhealthyCaptures.length,
304
+ requestIds: unhealthyCaptures
305
+ .slice(0, 50)
306
+ .map((row) => row.requestId),
307
+ workerLifetime: {
308
+ scope: "worker_lifetime",
309
+ counters: logs?.bodyCapture ?? { status: "unavailable" },
310
+ },
311
+ },
312
+ );
196
313
  const finals = history.request_final,
197
314
  unique = new Set(finals.map((r) => r.requestId));
198
315
  add(