@juspay/neurolink 11.23.2 → 11.23.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.
- package/CHANGELOG.md +2 -2
- package/dist/browser/neurolink.min.js +328 -328
- package/dist/providers/openAI/client.js +18 -0
- package/dist/utils/providerRetry.d.ts +18 -0
- package/dist/utils/providerRetry.js +46 -0
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ import { assertSafeUrl } from "../../utils/ssrfGuard.js";
|
|
|
12
12
|
import { createTimeoutController } from "../../utils/timeout.js";
|
|
13
13
|
import { stripTrailingSlash } from "../openaiChatCompletionsClient.js";
|
|
14
14
|
import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
|
|
15
|
+
import { isOpenAIQuotaExhaustedError } from "../../utils/providerRetry.js";
|
|
15
16
|
const OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
16
17
|
/**
|
|
17
18
|
* Resolve the effective OpenAI base URL from optional credential / env
|
|
@@ -113,6 +114,23 @@ export class OpenAIProvider extends OpenAIChatCompletionsProvider {
|
|
|
113
114
|
? ctx.message
|
|
114
115
|
: "Invalid OpenAI API key. Please check your OPENAI_API_KEY environment variable.",
|
|
115
116
|
},
|
|
117
|
+
// MUST precede the 429 rule below. OpenAI returns 429 for two
|
|
118
|
+
// unrelated conditions: transient throttling, and a permanently
|
|
119
|
+
// exhausted quota (out of credit, or a spend cap reached). Matching on
|
|
120
|
+
// the status code alone collapses them, and the resulting advice —
|
|
121
|
+
// "try again later" — is the one thing that can never resolve the
|
|
122
|
+
// second. The signal is the error TYPE, not the wording.
|
|
123
|
+
{
|
|
124
|
+
// One definition of "permanent billing state", in the helper. The
|
|
125
|
+
// earlier cut also tested `errorType === "insufficient_quota"` here,
|
|
126
|
+
// which is precisely the helper's own first branch — two places to
|
|
127
|
+
// keep in step for no benefit.
|
|
128
|
+
match: (ctx) => isOpenAIQuotaExhaustedError(ctx.error),
|
|
129
|
+
errorClass: ProviderError,
|
|
130
|
+
message: "OpenAI quota exhausted — this will not resolve by retrying. " +
|
|
131
|
+
"Check your plan and billing details at " +
|
|
132
|
+
"https://platform.openai.com/account/billing",
|
|
133
|
+
},
|
|
116
134
|
{
|
|
117
135
|
match: (ctx) => ctx.statusCode === 429 ||
|
|
118
136
|
errorType === "rate_limit_error" ||
|
|
@@ -52,6 +52,24 @@ export declare function duckTypedStatusCode(error: unknown): number | undefined;
|
|
|
52
52
|
* hand-rolled OpenAI-compatible client's record). Shared with baseProvider.
|
|
53
53
|
*/
|
|
54
54
|
export declare function extractRetryAfterMsFromError(error: unknown): number | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* An OpenAI-wire `insufficient_quota` error: the account is out of credit or
|
|
57
|
+
* has hit a spend cap.
|
|
58
|
+
*
|
|
59
|
+
* Exported and shared with the OpenAI error table so the retry predicate and
|
|
60
|
+
* the message a caller sees cannot disagree about what a permanent billing
|
|
61
|
+
* state looks like. The type is NOT a field on the error — OpenAI puts it
|
|
62
|
+
* inside the JSON response body, which the SDK keeps as a string — so any
|
|
63
|
+
* caller that needs it has to parse, and none should do so on its own.
|
|
64
|
+
*
|
|
65
|
+
* Deliberately keyed on the error TYPE, never on the word "quota". OpenAI and
|
|
66
|
+
* xAI send 429 + `type: "insufficient_quota"` for a permanent billing state,
|
|
67
|
+
* but other providers use "quota" for ordinary throttling — Google's 429 reads
|
|
68
|
+
* "Quota exceeded for quota metric ...", which IS retryable and must stay that
|
|
69
|
+
* way. Matching free text here would make Gemini throttling non-retryable and
|
|
70
|
+
* quietly remove a layer of resilience.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isOpenAIQuotaExhaustedError(error: unknown): boolean;
|
|
55
73
|
export declare function isRetryableProviderError(error: unknown): boolean;
|
|
56
74
|
/**
|
|
57
75
|
* Extract the HTTP status code from an AI SDK error, if available.
|
|
@@ -105,7 +105,53 @@ export function extractRetryAfterMsFromError(error) {
|
|
|
105
105
|
}
|
|
106
106
|
return undefined;
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* An OpenAI-wire `insufficient_quota` error: the account is out of credit or
|
|
110
|
+
* has hit a spend cap.
|
|
111
|
+
*
|
|
112
|
+
* Exported and shared with the OpenAI error table so the retry predicate and
|
|
113
|
+
* the message a caller sees cannot disagree about what a permanent billing
|
|
114
|
+
* state looks like. The type is NOT a field on the error — OpenAI puts it
|
|
115
|
+
* inside the JSON response body, which the SDK keeps as a string — so any
|
|
116
|
+
* caller that needs it has to parse, and none should do so on its own.
|
|
117
|
+
*
|
|
118
|
+
* Deliberately keyed on the error TYPE, never on the word "quota". OpenAI and
|
|
119
|
+
* xAI send 429 + `type: "insufficient_quota"` for a permanent billing state,
|
|
120
|
+
* but other providers use "quota" for ordinary throttling — Google's 429 reads
|
|
121
|
+
* "Quota exceeded for quota metric ...", which IS retryable and must stay that
|
|
122
|
+
* way. Matching free text here would make Gemini throttling non-retryable and
|
|
123
|
+
* quietly remove a layer of resilience.
|
|
124
|
+
*/
|
|
125
|
+
export function isOpenAIQuotaExhaustedError(error) {
|
|
126
|
+
if (typeof error !== "object" || error === null) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const err = error;
|
|
130
|
+
if (err.type === "insufficient_quota") {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
// The SDK keeps the raw payload as a string; the type lives inside it.
|
|
134
|
+
if (typeof err.responseBody === "string") {
|
|
135
|
+
try {
|
|
136
|
+
const parsed = JSON.parse(err.responseBody);
|
|
137
|
+
const inner = parsed?.error;
|
|
138
|
+
return inner?.type === "insufficient_quota";
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
108
146
|
export function isRetryableProviderError(error) {
|
|
147
|
+
// Before every other branch, including the SDK's own flag: a 429 carrying
|
|
148
|
+
// `insufficient_quota` is marked retryable by the AI SDK because it only
|
|
149
|
+
// looks at the status code. Retrying it burns the full ladder — 3 attempts
|
|
150
|
+
// and ~20s of backoff, since no Retry-After accompanies these — on a state
|
|
151
|
+
// that cannot change until someone tops up the account.
|
|
152
|
+
if (isOpenAIQuotaExhaustedError(error)) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
109
155
|
// Preferred path: use the AI SDK's own branded type check + isRetryable flag
|
|
110
156
|
if (APICallError.isInstance(error)) {
|
|
111
157
|
return error.isRetryable;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "11.23.
|
|
3
|
+
"version": "11.23.3",
|
|
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": {
|