@juspay/neurolink 12.47.6 → 12.47.8
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/CHANGELOG.md +2 -2
- package/dist/browser/neurolink.min.js +394 -394
- package/dist/localUsage/copilotCliReader.js +1 -1
- package/dist/localUsage/cursorReader.js +1 -1
- package/dist/localUsage/hermesReader.js +1 -1
- package/dist/localUsage/openCodeReader.js +3 -3
- package/dist/providers/anthropic/client.js +3 -2
- package/dist/providers/azureOpenai.js +13 -8
- package/dist/providers/googleVertex/client.js +5 -3
- package/dist/providers/nvidiaNim/client.js +9 -5
- package/dist/utils/errorClassifier.d.ts +21 -0
- package/dist/utils/errorClassifier.js +27 -0
- package/package.json +1 -2
|
@@ -125,7 +125,7 @@ export async function createCopilotCliReader() {
|
|
|
125
125
|
errors.push({
|
|
126
126
|
cliId: CLI_ID,
|
|
127
127
|
filePath: dbPath,
|
|
128
|
-
message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (
|
|
128
|
+
message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (unflagged since Node 22.13.0 and 23.4.0; on 22.5.0 to 22.12.x and 23.0 to 23.3 pass --experimental-sqlite)`,
|
|
129
129
|
});
|
|
130
130
|
return { cliId: CLI_ID, totals, filesScanned: 0, errors };
|
|
131
131
|
}
|
|
@@ -375,7 +375,7 @@ export async function createCursorReader() {
|
|
|
375
375
|
errors.push({
|
|
376
376
|
cliId: CLI_ID,
|
|
377
377
|
filePath: chatsRoot(),
|
|
378
|
-
message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (
|
|
378
|
+
message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (unflagged since Node 22.13.0 and 23.4.0; on 22.5.0 to 22.12.x and 23.0 to 23.3 pass --experimental-sqlite)`,
|
|
379
379
|
});
|
|
380
380
|
return { cliId: CLI_ID, totals, filesScanned: 0, errors };
|
|
381
381
|
}
|
|
@@ -318,7 +318,7 @@ export async function createHermesReader() {
|
|
|
318
318
|
errors.push({
|
|
319
319
|
cliId: CLI_ID,
|
|
320
320
|
filePath: hermesHome(),
|
|
321
|
-
message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (
|
|
321
|
+
message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (unflagged since Node 22.13.0 and 23.4.0; on 22.5.0 to 22.12.x and 23.0 to 23.3 pass --experimental-sqlite)`,
|
|
322
322
|
});
|
|
323
323
|
return { cliId: CLI_ID, totals, filesScanned: 0, errors };
|
|
324
324
|
}
|
|
@@ -81,8 +81,8 @@ export async function createOpenCodeReader() {
|
|
|
81
81
|
const models = new Set();
|
|
82
82
|
const dbPath = databasePath();
|
|
83
83
|
// `node:sqlite` arrived in Node 22.5.0 behind `--experimental-sqlite`, was
|
|
84
|
-
// unflagged in 22.13.0 and is still marked experimental, so it
|
|
85
|
-
// absent or change shape. Imported lazily and behind a try/catch: a
|
|
84
|
+
// unflagged in 22.13.0 and 23.4.0 and is still marked experimental, so it
|
|
85
|
+
// can be absent or change shape. Imported lazily and behind a try/catch: a
|
|
86
86
|
// runtime without it must degrade to a reported failure for this one
|
|
87
87
|
// reader, not take down a scan of all the others.
|
|
88
88
|
let DatabaseSync;
|
|
@@ -103,7 +103,7 @@ export async function createOpenCodeReader() {
|
|
|
103
103
|
errors.push({
|
|
104
104
|
cliId: CLI_ID,
|
|
105
105
|
filePath: dbPath,
|
|
106
|
-
message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (
|
|
106
|
+
message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (unflagged since Node 22.13.0 and 23.4.0; on 22.5.0 to 22.12.x and 23.0 to 23.3 pass --experimental-sqlite)`,
|
|
107
107
|
});
|
|
108
108
|
return { cliId: CLI_ID, totals, filesScanned: 0, errors };
|
|
109
109
|
}
|
|
@@ -13,7 +13,7 @@ import { createOAuthFetch } from "../../proxy/oauthFetch.js";
|
|
|
13
13
|
import { createProxyFetch } from "../../proxy/proxyFetch.js";
|
|
14
14
|
import { getCapturedLimitSnapshot, getCapturedResponseHeaders, logClaudeLimitSnapshot, runInLimitCaptureScope, setLimitSpanAttributes, withLimitCapture, wrapFetchWithLimitCapture, } from "./rateLimitCapture.js";
|
|
15
15
|
import { AuthenticationError, NetworkError, ProviderError, RateLimitError, } from "../../types/index.js";
|
|
16
|
-
import { classifyProviderError } from "../../utils/errorClassifier.js";
|
|
16
|
+
import { classifyProviderError, messageNamesStatus, } from "../../utils/errorClassifier.js";
|
|
17
17
|
import { logger } from "../../utils/logger.js";
|
|
18
18
|
import { drainDetachedPump } from "../../utils/drainDetachedPump.js";
|
|
19
19
|
import { ANTHROPIC_ELISION_NOTE, planAnthropicLoopReclaim, previewAnthropicToolResultText, } from "../../context/anthropicLoopGuard.js";
|
|
@@ -1442,7 +1442,8 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1442
1442
|
},
|
|
1443
1443
|
{
|
|
1444
1444
|
match: (ctx) => ctx.statusCode === 429 ||
|
|
1445
|
-
/rate limit|too_many_requests
|
|
1445
|
+
/rate limit|too_many_requests/i.test(ctx.message) ||
|
|
1446
|
+
messageNamesStatus(ctx.message, 429),
|
|
1446
1447
|
errorClass: RateLimitError,
|
|
1447
1448
|
message: "Anthropic rate limit exceeded. Please try again later.",
|
|
1448
1449
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { APIVersions } from "../constants/enums.js";
|
|
2
2
|
import { AuthenticationError } from "../types/index.js";
|
|
3
3
|
import { logger } from "../utils/logger.js";
|
|
4
|
-
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../utils/errorClassifier.js";
|
|
4
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, messageNamesStatus, } from "../utils/errorClassifier.js";
|
|
5
5
|
import { createAzureAPIKeyConfig, createAzureEndpointConfig, validateApiKey, } from "../utils/providerConfig.js";
|
|
6
6
|
import { transformParamsForLogging } from "../utils/transformationUtils.js";
|
|
7
7
|
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
@@ -158,15 +158,20 @@ export class AzureOpenAIProvider extends OpenAIChatCompletionsProvider {
|
|
|
158
158
|
return this.azureDeployment;
|
|
159
159
|
}
|
|
160
160
|
formatProviderError(error) {
|
|
161
|
-
// RULING 1 fix: previously only a
|
|
162
|
-
// everything else (429, 404, network errors, 5xx) fell
|
|
163
|
-
// generic ProviderError. The 401 branch below
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
161
|
+
// RULING 1 fix: previously only a "401" in the message text was
|
|
162
|
+
// special-cased — everything else (429, 404, network errors, 5xx) fell
|
|
163
|
+
// through to one generic ProviderError. The 401 branch below keeps its
|
|
164
|
+
// message text byte-for-byte; DEFAULT_ERROR_RULES now gives
|
|
165
|
+
// 429/404/network/5xx their correct classifications instead of the old
|
|
166
|
+
// undifferentiated catch-all. The branch still reads the text, not the
|
|
167
|
+
// structured status, so a 401 whose message names no status still falls
|
|
168
|
+
// through to DEFAULT_ERROR_RULES as before. What changed is the match: a
|
|
169
|
+
// status-shaped "401" ("status code 401", a leading "401 ...") rather than
|
|
170
|
+
// any three digits, so a request id or token count no longer reads as an
|
|
171
|
+
// auth failure.
|
|
167
172
|
const rules = [
|
|
168
173
|
{
|
|
169
|
-
match: (ctx) =>
|
|
174
|
+
match: (ctx) => messageNamesStatus(ctx.message, 401),
|
|
170
175
|
errorClass: AuthenticationError,
|
|
171
176
|
message: "Invalid Azure OpenAI API key or endpoint.",
|
|
172
177
|
},
|
|
@@ -15,7 +15,7 @@ import { isSchemaComplexityError } from "../../core/modules/structuredOutputPoli
|
|
|
15
15
|
import { redactUrlForError, stringifyContentSafe, } from "../../utils/logSanitize.js";
|
|
16
16
|
import { googleSdkProxyHttpOptions } from "../../proxy/proxyFetch.js";
|
|
17
17
|
import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../../types/index.js";
|
|
18
|
-
import { classifyProviderError } from "../../utils/errorClassifier.js";
|
|
18
|
+
import { classifyProviderError, messageNamesStatus, } from "../../utils/errorClassifier.js";
|
|
19
19
|
import { ERROR_CODES, NeuroLinkError } from "../../utils/errorHandling.js";
|
|
20
20
|
import { applyVertexAnthropicCacheBreakpoints } from "../../utils/anthropicCacheBreakpoints.js";
|
|
21
21
|
import { FileDetector } from "../../utils/fileDetector.js";
|
|
@@ -6523,7 +6523,8 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
6523
6523
|
// exhaustion surfaces as overloaded_error (HTTP 529) — same
|
|
6524
6524
|
// operational meaning as a 429, so classify it here instead of the
|
|
6525
6525
|
// generic 5xx branch below.
|
|
6526
|
-
match: (ctx) => /QUOTA_EXCEEDED|RATE_LIMIT_EXCEEDED|rate limit
|
|
6526
|
+
match: (ctx) => /QUOTA_EXCEEDED|RATE_LIMIT_EXCEEDED|rate limit/i.test(ctx.message) ||
|
|
6527
|
+
messageNamesStatus(ctx.message, 429) ||
|
|
6527
6528
|
statusCode === 429 ||
|
|
6528
6529
|
statusCode === 529 ||
|
|
6529
6530
|
/overloaded/i.test(ctx.message),
|
|
@@ -6564,7 +6565,8 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
6564
6565
|
message: (ctx) => `Connection error: ${ctx.message}`,
|
|
6565
6566
|
},
|
|
6566
6567
|
{
|
|
6567
|
-
match: (ctx) => /
|
|
6568
|
+
match: (ctx) => /server error|Internal Server Error|INTERNAL|UNAVAILABLE/i.test(ctx.message) ||
|
|
6569
|
+
[500, 502, 503, 504].some((code) => messageNamesStatus(ctx.message, code)) ||
|
|
6568
6570
|
(statusCode !== undefined && statusCode >= 500 && statusCode < 600),
|
|
6569
6571
|
errorClass: ProviderError,
|
|
6570
6572
|
message: (ctx) => `Google Vertex AI server error: ${ctx.message}. Please try again later.`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AIProviderName, NvidiaNimModels } from "../../constants/enums.js";
|
|
2
2
|
import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../../types/index.js";
|
|
3
|
-
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../../utils/errorClassifier.js";
|
|
3
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, messageNamesStatus, } from "../../utils/errorClassifier.js";
|
|
4
4
|
import { logger } from "../../utils/logger.js";
|
|
5
5
|
import { redactUrlCredentials } from "../../utils/logSanitize.js";
|
|
6
6
|
import { createNvidiaNimConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
|
|
@@ -260,24 +260,28 @@ export class NvidiaNimProvider extends OpenAIChatCompletionsProvider {
|
|
|
260
260
|
// parameter retry (K5) need the original "Bad Request" message to
|
|
261
261
|
// surface.
|
|
262
262
|
{
|
|
263
|
-
match: (ctx) => /Invalid API key|
|
|
263
|
+
match: (ctx) => /Invalid API key|Unauthorized/.test(ctx.message) ||
|
|
264
|
+
messageNamesStatus(ctx.message, 401),
|
|
264
265
|
errorClass: AuthenticationError,
|
|
265
266
|
message: "Invalid NVIDIA NIM API key. Get one at https://build.nvidia.com/settings/api-keys",
|
|
266
267
|
},
|
|
267
268
|
{
|
|
268
|
-
match: (ctx) => /rate limit
|
|
269
|
+
match: (ctx) => /rate limit/.test(ctx.message) ||
|
|
270
|
+
messageNamesStatus(ctx.message, 429),
|
|
269
271
|
errorClass: RateLimitError,
|
|
270
272
|
message: "NVIDIA NIM rate limit exceeded",
|
|
271
273
|
},
|
|
272
274
|
{
|
|
273
275
|
// NIM answers most of its roster with a 404 whose text ("Function …
|
|
274
276
|
// not found for account …") names no model, so the status decides.
|
|
275
|
-
match: (ctx) => ctx.statusCode === 404 ||
|
|
277
|
+
match: (ctx) => ctx.statusCode === 404 ||
|
|
278
|
+
/model_not_found/.test(ctx.message) ||
|
|
279
|
+
messageNamesStatus(ctx.message, 404),
|
|
276
280
|
errorClass: InvalidModelError,
|
|
277
281
|
message: () => `NVIDIA NIM model '${this.modelName}' not available. Browse the catalog at https://build.nvidia.com/models`,
|
|
278
282
|
},
|
|
279
283
|
{
|
|
280
|
-
match: (ctx) => /quota
|
|
284
|
+
match: (ctx) => /quota/.test(ctx.message) || messageNamesStatus(ctx.message, 403),
|
|
281
285
|
errorClass: ProviderError,
|
|
282
286
|
message: "NVIDIA NIM quota exceeded for your account",
|
|
283
287
|
},
|
|
@@ -21,6 +21,27 @@ import { type ProviderErrorRule } from "../types/index.js";
|
|
|
21
21
|
* it identically before this change, so it is not made overridable.
|
|
22
22
|
*/
|
|
23
23
|
export declare function classifyProviderError(error: unknown, rules: ProviderErrorRule[], provider: string, modelName?: string): Error;
|
|
24
|
+
/**
|
|
25
|
+
* True when `status` is written in `message` as an HTTP status, as opposed to
|
|
26
|
+
* an unrelated number that happens to have the same digits ("max_tokens (429)
|
|
27
|
+
* exceeds the model limit", a request id, a token count). Provider rules that
|
|
28
|
+
* once tested the bare digits (`/429/`) classified such a 400 as a rate limit.
|
|
29
|
+
*
|
|
30
|
+
* Status-shaped means one of:
|
|
31
|
+
* - the text starts with it, the way some SDKs format an API error
|
|
32
|
+
* ("429 {...}", "401 Unauthorized");
|
|
33
|
+
* - it follows "status", "status code" or "HTTP" ("Request failed with status
|
|
34
|
+
* code 429", "HTTP/1.1 429");
|
|
35
|
+
* - it sits next to the word "error" in either order ("error 429", "429 error",
|
|
36
|
+
* and the nested JSON body `{"error":{"code":429`).
|
|
37
|
+
*
|
|
38
|
+
* The gap between the word and the number is bounded and holds no digit, so
|
|
39
|
+
* unrelated digits further along cannot bridge the match. This is the shape the
|
|
40
|
+
* shared 5xx rule in `DEFAULT_ERROR_RULES` already uses, so the two stay in
|
|
41
|
+
* step. It is a heuristic over text: a rule that can read the structured
|
|
42
|
+
* status should still check `ctx.statusCode` first.
|
|
43
|
+
*/
|
|
44
|
+
export declare function messageNamesStatus(message: string, status: number): boolean;
|
|
24
45
|
/**
|
|
25
46
|
* Generic fallback rule table covering the five categories every
|
|
26
47
|
* OpenAI-compatible provider already hand-rolled near-identically:
|
|
@@ -127,6 +127,33 @@ const MODEL_WORD = `\\bmodel\\b${NOT_A_ROUTE_MODIFIER}`;
|
|
|
127
127
|
const MODEL_OR_DEPLOYMENT = `\\b(?:model|deployment)\\b${NOT_A_ROUTE_MODIFIER}`;
|
|
128
128
|
const NAMED_MODEL_ERROR = `(?:unknown|no such|invalid|unsupported) model${NOT_A_ROUTE_MODIFIER}`;
|
|
129
129
|
const MODEL_404_TEXT = new RegExp(`model[_ ]?not[_ ]?found|${NAMED_MODEL_ERROR}|${MODEL_OR_DEPLOYMENT}.{0,120}\\b(?:does not exist|not found|unavailable|not (?:available|supported))\\b|\\b(?:does not exist|not found)\\b.{0,120}${MODEL_OR_DEPLOYMENT}|unable to access.{0,60}${MODEL_WORD}`, "i");
|
|
130
|
+
/**
|
|
131
|
+
* True when `status` is written in `message` as an HTTP status, as opposed to
|
|
132
|
+
* an unrelated number that happens to have the same digits ("max_tokens (429)
|
|
133
|
+
* exceeds the model limit", a request id, a token count). Provider rules that
|
|
134
|
+
* once tested the bare digits (`/429/`) classified such a 400 as a rate limit.
|
|
135
|
+
*
|
|
136
|
+
* Status-shaped means one of:
|
|
137
|
+
* - the text starts with it, the way some SDKs format an API error
|
|
138
|
+
* ("429 {...}", "401 Unauthorized");
|
|
139
|
+
* - it follows "status", "status code" or "HTTP" ("Request failed with status
|
|
140
|
+
* code 429", "HTTP/1.1 429");
|
|
141
|
+
* - it sits next to the word "error" in either order ("error 429", "429 error",
|
|
142
|
+
* and the nested JSON body `{"error":{"code":429`).
|
|
143
|
+
*
|
|
144
|
+
* The gap between the word and the number is bounded and holds no digit, so
|
|
145
|
+
* unrelated digits further along cannot bridge the match. This is the shape the
|
|
146
|
+
* shared 5xx rule in `DEFAULT_ERROR_RULES` already uses, so the two stay in
|
|
147
|
+
* step. It is a heuristic over text: a rule that can read the structured
|
|
148
|
+
* status should still check `ctx.statusCode` first.
|
|
149
|
+
*/
|
|
150
|
+
export function messageNamesStatus(message, status) {
|
|
151
|
+
if (!Number.isInteger(status) || status < 100 || status > 599) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
const code = String(status);
|
|
155
|
+
return new RegExp(`^\\s*${code}\\b|\\b(?:error|status(?:\\s*code)?)\\b\\D{0,12}\\b${code}\\b|\\bhttp(?:/\\d(?:\\.\\d)?)?[\\s:-]{0,3}${code}\\b|\\b${code}\\b\\D{0,12}\\berror\\b`, "i").test(message);
|
|
156
|
+
}
|
|
130
157
|
/**
|
|
131
158
|
* Generic fallback rule table covering the five categories every
|
|
132
159
|
* OpenAI-compatible provider already hand-rolled near-identically:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "12.47.
|
|
3
|
+
"version": "12.47.8",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "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/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
|
|
6
6
|
"author": {
|
|
@@ -80,7 +80,6 @@
|
|
|
80
80
|
"test:client": "pnpm exec tsx test/continuous-test-suite-client.ts",
|
|
81
81
|
"test:context": "pnpm exec tsx test/continuous-test-suite-context.ts",
|
|
82
82
|
"test:evaluation": "pnpm exec tsx test/continuous-test-suite-evaluation.ts",
|
|
83
|
-
"test:handler-registry": "pnpm exec tsx test/continuous-test-suite-handler-registry.ts",
|
|
84
83
|
"test:harness-offline-timeout": "pnpm exec tsx test/continuous-test-suite-harness-offline-timeout.ts",
|
|
85
84
|
"test:resolve-request-kind": "pnpm exec tsx test/continuous-test-suite-resolve-request-kind.ts",
|
|
86
85
|
"test:mcp": "pnpm exec tsx test/continuous-test-suite-mcp-infra.ts",
|