@juspay/neurolink 11.2.1 → 11.2.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 +16 -0
- package/dist/browser/neurolink.min.js +392 -392
- package/dist/factories/providerRegistry.d.ts +16 -4
- package/dist/factories/providerRegistry.js +70 -48
- package/dist/lib/factories/providerRegistry.d.ts +16 -4
- package/dist/lib/factories/providerRegistry.js +70 -48
- package/dist/lib/providers/configuredOpenAICompat.js +10 -3
- package/dist/lib/providers/openaiChatCompletionsBase.js +26 -14
- package/dist/lib/providers/openaiCompatCatalog.js +9 -11
- package/dist/lib/types/providers.d.ts +15 -1
- package/dist/providers/configuredOpenAICompat.js +10 -3
- package/dist/providers/openaiChatCompletionsBase.js +26 -14
- package/dist/providers/openaiCompatCatalog.js +9 -11
- package/dist/types/providers.d.ts +15 -1
- package/package.json +3 -2
- package/dist/lib/providers/cloudflare.d.ts +0 -26
- package/dist/lib/providers/cloudflare.js +0 -83
- package/dist/lib/providers/fireworks.d.ts +0 -22
- package/dist/lib/providers/fireworks.js +0 -73
- package/dist/lib/providers/groq.d.ts +0 -24
- package/dist/lib/providers/groq.js +0 -83
- package/dist/lib/providers/mistral.d.ts +0 -25
- package/dist/lib/providers/mistral.js +0 -87
- package/dist/lib/providers/perplexity.d.ts +0 -24
- package/dist/lib/providers/perplexity.js +0 -69
- package/dist/lib/providers/togetherAi.d.ts +0 -23
- package/dist/lib/providers/togetherAi.js +0 -73
- package/dist/lib/providers/xai.d.ts +0 -22
- package/dist/lib/providers/xai.js +0 -77
- package/dist/providers/cloudflare.d.ts +0 -26
- package/dist/providers/cloudflare.js +0 -82
- package/dist/providers/fireworks.d.ts +0 -22
- package/dist/providers/fireworks.js +0 -72
- package/dist/providers/groq.d.ts +0 -24
- package/dist/providers/groq.js +0 -82
- package/dist/providers/mistral.d.ts +0 -25
- package/dist/providers/mistral.js +0 -86
- package/dist/providers/perplexity.d.ts +0 -24
- package/dist/providers/perplexity.js +0 -68
- package/dist/providers/togetherAi.d.ts +0 -23
- package/dist/providers/togetherAi.js +0 -72
- package/dist/providers/xai.d.ts +0 -22
- package/dist/providers/xai.js +0 -76
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { ProviderRegistryOptions } from "../types/index.js";
|
|
2
|
+
import { AIProviderName } from "../constants/enums.js";
|
|
3
|
+
/**
|
|
4
|
+
* Static module -> provider-ID manifest for every provider registered below.
|
|
5
|
+
* Module names (file/dir under src/lib/providers/) intentionally differ from
|
|
6
|
+
* their canonical IDs (e.g. amazonBedrock -> "bedrock", googleVertex ->
|
|
7
|
+
* "vertex"); static scanners that cannot resolve the dynamic import() calls
|
|
8
|
+
* use this mapping to confirm a module is registered. Enforced at runtime so
|
|
9
|
+
* the registry cannot silently drift from this manifest.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PROVIDER_MODULE_TO_ID: Readonly<Record<string, AIProviderName>>;
|
|
2
12
|
/**
|
|
3
13
|
* Provider Registry - registers all providers with the factory
|
|
4
14
|
* This is where we migrate providers one by one to the new pattern
|
|
@@ -33,10 +43,12 @@ export declare class ProviderRegistry {
|
|
|
33
43
|
* (avoids circular dependencies; see CLAUDE.md).
|
|
34
44
|
*
|
|
35
45
|
* Not registered (by design): index.ts, providerTypeUtils.ts,
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* (
|
|
39
|
-
*
|
|
46
|
+
* openaiChatCompletionsBase.ts, openaiChatCompletionsClient.ts,
|
|
47
|
+
* anthropicImageBlocks.ts (shared base/helper modules), anthropicBaseProvider.ts
|
|
48
|
+
* (legacy; anthropic.ts is live), googleNativeGemini3.ts (shared helpers).
|
|
49
|
+
* Filename != provider ID (e.g. amazonBedrock -> "bedrock"); the
|
|
50
|
+
* statically-scannable PROVIDER_MODULE_TO_ID manifest maps every registered
|
|
51
|
+
* module to its provider ID and is enforced below (Pattern Analysis #1178/#1317).
|
|
40
52
|
*/
|
|
41
53
|
private static _doRegister;
|
|
42
54
|
/**
|
|
@@ -1,7 +1,48 @@
|
|
|
1
1
|
import { ProviderFactory } from "./providerFactory.js";
|
|
2
2
|
import { logger } from "../utils/logger.js";
|
|
3
|
-
import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels,
|
|
3
|
+
import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, HuggingFaceModels, DeepSeekModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
|
|
4
4
|
import { PROVIDER_DESCRIPTORS_BY_NAME } from "./providerDescriptors.js";
|
|
5
|
+
import { OPENAI_COMPAT_CATALOG } from "../providers/openaiCompatCatalog.js";
|
|
6
|
+
/**
|
|
7
|
+
* Static module -> provider-ID manifest for every provider registered below.
|
|
8
|
+
* Module names (file/dir under src/lib/providers/) intentionally differ from
|
|
9
|
+
* their canonical IDs (e.g. amazonBedrock -> "bedrock", googleVertex ->
|
|
10
|
+
* "vertex"); static scanners that cannot resolve the dynamic import() calls
|
|
11
|
+
* use this mapping to confirm a module is registered. Enforced at runtime so
|
|
12
|
+
* the registry cannot silently drift from this manifest.
|
|
13
|
+
*/
|
|
14
|
+
export const PROVIDER_MODULE_TO_ID = {
|
|
15
|
+
amazonBedrock: AIProviderName.BEDROCK,
|
|
16
|
+
amazonSagemaker: AIProviderName.SAGEMAKER,
|
|
17
|
+
anthropic: AIProviderName.ANTHROPIC,
|
|
18
|
+
azureOpenai: AIProviderName.AZURE,
|
|
19
|
+
cloudflare: AIProviderName.CLOUDFLARE,
|
|
20
|
+
cohere: AIProviderName.COHERE,
|
|
21
|
+
deepseek: AIProviderName.DEEPSEEK,
|
|
22
|
+
fireworks: AIProviderName.FIREWORKS,
|
|
23
|
+
googleAiStudio: AIProviderName.GOOGLE_AI,
|
|
24
|
+
googleVertex: AIProviderName.VERTEX,
|
|
25
|
+
groq: AIProviderName.GROQ,
|
|
26
|
+
huggingFace: AIProviderName.HUGGINGFACE,
|
|
27
|
+
ideogram: AIProviderName.IDEOGRAM,
|
|
28
|
+
jina: AIProviderName.JINA,
|
|
29
|
+
litellm: AIProviderName.LITELLM,
|
|
30
|
+
llamaCpp: AIProviderName.LLAMACPP,
|
|
31
|
+
lmStudio: AIProviderName.LM_STUDIO,
|
|
32
|
+
mistral: AIProviderName.MISTRAL,
|
|
33
|
+
nvidiaNim: AIProviderName.NVIDIA_NIM,
|
|
34
|
+
ollama: AIProviderName.OLLAMA,
|
|
35
|
+
openAI: AIProviderName.OPENAI,
|
|
36
|
+
openaiCompatible: AIProviderName.OPENAI_COMPATIBLE,
|
|
37
|
+
openRouter: AIProviderName.OPENROUTER,
|
|
38
|
+
perplexity: AIProviderName.PERPLEXITY,
|
|
39
|
+
recraft: AIProviderName.RECRAFT,
|
|
40
|
+
replicate: AIProviderName.REPLICATE,
|
|
41
|
+
stability: AIProviderName.STABILITY,
|
|
42
|
+
togetherAi: AIProviderName.TOGETHER_AI,
|
|
43
|
+
voyage: AIProviderName.VOYAGE,
|
|
44
|
+
xai: AIProviderName.XAI,
|
|
45
|
+
};
|
|
5
46
|
/**
|
|
6
47
|
* Provider Registry - registers all providers with the factory
|
|
7
48
|
* This is where we migrate providers one by one to the new pattern
|
|
@@ -53,10 +94,12 @@ export class ProviderRegistry {
|
|
|
53
94
|
* (avoids circular dependencies; see CLAUDE.md).
|
|
54
95
|
*
|
|
55
96
|
* Not registered (by design): index.ts, providerTypeUtils.ts,
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* (
|
|
59
|
-
*
|
|
97
|
+
* openaiChatCompletionsBase.ts, openaiChatCompletionsClient.ts,
|
|
98
|
+
* anthropicImageBlocks.ts (shared base/helper modules), anthropicBaseProvider.ts
|
|
99
|
+
* (legacy; anthropic.ts is live), googleNativeGemini3.ts (shared helpers).
|
|
100
|
+
* Filename != provider ID (e.g. amazonBedrock -> "bedrock"); the
|
|
101
|
+
* statically-scannable PROVIDER_MODULE_TO_ID manifest maps every registered
|
|
102
|
+
* module to its provider ID and is enforced below (Pattern Analysis #1178/#1317).
|
|
60
103
|
*/
|
|
61
104
|
// eslint-disable-next-line max-lines-per-function
|
|
62
105
|
static async _doRegister() {
|
|
@@ -111,12 +154,18 @@ export class ProviderRegistry {
|
|
|
111
154
|
return new HuggingFaceProvider(modelName, sdk, region, hfCreds);
|
|
112
155
|
}, process.env.HUGGINGFACE_MODEL ||
|
|
113
156
|
HuggingFaceModels.QWEN_2_5_72B_INSTRUCT, ["huggingface", "hf"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.HUGGINGFACE));
|
|
114
|
-
// Register
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
157
|
+
// Register the config-driven OpenAI-compatible catalog providers
|
|
158
|
+
// (groq, xai, together-ai, fireworks, perplexity, mistral, cloudflare).
|
|
159
|
+
// To add a new zero-quirk OpenAI-compatible provider, add one entry to
|
|
160
|
+
// OPENAI_COMPAT_CATALOG (openaiCompatCatalog.ts) — not a new block here.
|
|
161
|
+
for (const entry of OPENAI_COMPAT_CATALOG) {
|
|
162
|
+
ProviderFactory.registerProvider(entry.providerName, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
163
|
+
const { ConfiguredOpenAICompatProvider } = await import("../providers/configuredOpenAICompat.js");
|
|
164
|
+
return new ConfiguredOpenAICompatProvider(entry, modelName, sdk, credentials);
|
|
165
|
+
}, entry.registryDefaultModelChecksEnvVar
|
|
166
|
+
? process.env[entry.modelEnvVar] || entry.registryDefaultModel
|
|
167
|
+
: entry.registryDefaultModel, entry.aliases, PROVIDER_DESCRIPTORS_BY_NAME.get(entry.providerName));
|
|
168
|
+
}
|
|
120
169
|
// Register Ollama provider
|
|
121
170
|
ProviderFactory.registerProvider(AIProviderName.OLLAMA, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
122
171
|
const ollamaCreds = credentials;
|
|
@@ -178,49 +227,12 @@ export class ProviderRegistry {
|
|
|
178
227
|
const { LlamaCppProvider } = await import("../providers/llamaCpp.js");
|
|
179
228
|
return new LlamaCppProvider(modelName, sdk, undefined, llamaCppCreds);
|
|
180
229
|
}, process.env.LLAMACPP_MODEL || undefined, ["llamacpp", "llama.cpp", "llama-cpp"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.LLAMACPP));
|
|
181
|
-
// Register xAI Grok provider
|
|
182
|
-
ProviderFactory.registerProvider(AIProviderName.XAI, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
183
|
-
const xaiCreds = credentials;
|
|
184
|
-
const { XaiProvider } = await import("../providers/xai.js");
|
|
185
|
-
return new XaiProvider(modelName, sdk, undefined, xaiCreds);
|
|
186
|
-
}, process.env.XAI_MODEL || XaiModels.GROK_3, ["xai", "grok"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.XAI));
|
|
187
|
-
// Register Groq provider
|
|
188
|
-
ProviderFactory.registerProvider(AIProviderName.GROQ, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
189
|
-
const groqCreds = credentials;
|
|
190
|
-
const { GroqProvider } = await import("../providers/groq.js");
|
|
191
|
-
return new GroqProvider(modelName, sdk, undefined, groqCreds);
|
|
192
|
-
}, process.env.GROQ_MODEL || GroqModels.LLAMA_3_3_70B_VERSATILE, ["groq"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.GROQ));
|
|
193
230
|
// Register Cohere provider
|
|
194
231
|
ProviderFactory.registerProvider(AIProviderName.COHERE, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
195
232
|
const cohereCreds = credentials;
|
|
196
233
|
const { CohereProvider } = await import("../providers/cohere.js");
|
|
197
234
|
return new CohereProvider(modelName, sdk, undefined, cohereCreds);
|
|
198
235
|
}, process.env.COHERE_MODEL || CohereModels.COMMAND_R_PLUS, ["cohere"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.COHERE));
|
|
199
|
-
// Register Together AI provider
|
|
200
|
-
ProviderFactory.registerProvider(AIProviderName.TOGETHER_AI, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
201
|
-
const togetherCreds = credentials;
|
|
202
|
-
const { TogetherAIProvider } = await import("../providers/togetherAi.js");
|
|
203
|
-
return new TogetherAIProvider(modelName, sdk, undefined, togetherCreds);
|
|
204
|
-
}, process.env.TOGETHER_MODEL ||
|
|
205
|
-
TogetherAIModels.LLAMA_3_3_70B_INSTRUCT_TURBO, ["together-ai", "together"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.TOGETHER_AI));
|
|
206
|
-
// Register Fireworks AI provider
|
|
207
|
-
ProviderFactory.registerProvider(AIProviderName.FIREWORKS, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
208
|
-
const fireworksCreds = credentials;
|
|
209
|
-
const { FireworksProvider } = await import("../providers/fireworks.js");
|
|
210
|
-
return new FireworksProvider(modelName, sdk, undefined, fireworksCreds);
|
|
211
|
-
}, process.env.FIREWORKS_MODEL || FireworksModels.DEEPSEEK_V4_PRO, ["fireworks"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.FIREWORKS));
|
|
212
|
-
// Register Perplexity provider
|
|
213
|
-
ProviderFactory.registerProvider(AIProviderName.PERPLEXITY, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
214
|
-
const perplexityCreds = credentials;
|
|
215
|
-
const { PerplexityProvider } = await import("../providers/perplexity.js");
|
|
216
|
-
return new PerplexityProvider(modelName, sdk, undefined, perplexityCreds);
|
|
217
|
-
}, process.env.PERPLEXITY_MODEL || PerplexityModels.SONAR, ["perplexity", "pplx"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.PERPLEXITY));
|
|
218
|
-
// Register Cloudflare Workers AI provider
|
|
219
|
-
ProviderFactory.registerProvider(AIProviderName.CLOUDFLARE, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
220
|
-
const cloudflareCreds = credentials;
|
|
221
|
-
const { CloudflareProvider } = await import("../providers/cloudflare.js");
|
|
222
|
-
return new CloudflareProvider(modelName, sdk, undefined, cloudflareCreds);
|
|
223
|
-
}, process.env.CLOUDFLARE_MODEL || CloudflareModels.LLAMA_3_3_70B_FAST, ["cloudflare", "workers-ai", "cf-ai"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.CLOUDFLARE));
|
|
224
236
|
// Register Voyage AI embeddings provider
|
|
225
237
|
ProviderFactory.registerProvider(AIProviderName.VOYAGE, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
226
238
|
const voyageCreds = credentials;
|
|
@@ -258,6 +270,16 @@ export class ProviderRegistry {
|
|
|
258
270
|
const { RecraftProvider } = await import("../providers/recraft.js");
|
|
259
271
|
return new RecraftProvider(modelName, sdk, undefined, recraftCreds);
|
|
260
272
|
}, process.env.RECRAFT_MODEL || RecraftModels.RECRAFT_V3, ["recraft"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.RECRAFT));
|
|
273
|
+
const unregistered = Object.entries(PROVIDER_MODULE_TO_ID).filter(([module, id]) => {
|
|
274
|
+
if (!ProviderFactory.hasProvider(id)) {
|
|
275
|
+
logger.error(`[ProviderRegistry] drift: module "${module}" not registered as "${id}"`);
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
});
|
|
280
|
+
if (unregistered.length > 0) {
|
|
281
|
+
throw new Error(`ProviderRegistry drift: ${unregistered.length} module(s) in PROVIDER_MODULE_TO_ID are not registered`);
|
|
282
|
+
}
|
|
261
283
|
logger.debug("All AI providers registered successfully");
|
|
262
284
|
// ===== TTS HANDLER REGISTRATION =====
|
|
263
285
|
try {
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { ProviderRegistryOptions } from "../types/index.js";
|
|
2
|
+
import { AIProviderName } from "../constants/enums.js";
|
|
3
|
+
/**
|
|
4
|
+
* Static module -> provider-ID manifest for every provider registered below.
|
|
5
|
+
* Module names (file/dir under src/lib/providers/) intentionally differ from
|
|
6
|
+
* their canonical IDs (e.g. amazonBedrock -> "bedrock", googleVertex ->
|
|
7
|
+
* "vertex"); static scanners that cannot resolve the dynamic import() calls
|
|
8
|
+
* use this mapping to confirm a module is registered. Enforced at runtime so
|
|
9
|
+
* the registry cannot silently drift from this manifest.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PROVIDER_MODULE_TO_ID: Readonly<Record<string, AIProviderName>>;
|
|
2
12
|
/**
|
|
3
13
|
* Provider Registry - registers all providers with the factory
|
|
4
14
|
* This is where we migrate providers one by one to the new pattern
|
|
@@ -33,10 +43,12 @@ export declare class ProviderRegistry {
|
|
|
33
43
|
* (avoids circular dependencies; see CLAUDE.md).
|
|
34
44
|
*
|
|
35
45
|
* Not registered (by design): index.ts, providerTypeUtils.ts,
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* (
|
|
39
|
-
*
|
|
46
|
+
* openaiChatCompletionsBase.ts, openaiChatCompletionsClient.ts,
|
|
47
|
+
* anthropicImageBlocks.ts (shared base/helper modules), anthropicBaseProvider.ts
|
|
48
|
+
* (legacy; anthropic.ts is live), googleNativeGemini3.ts (shared helpers).
|
|
49
|
+
* Filename != provider ID (e.g. amazonBedrock -> "bedrock"); the
|
|
50
|
+
* statically-scannable PROVIDER_MODULE_TO_ID manifest maps every registered
|
|
51
|
+
* module to its provider ID and is enforced below (Pattern Analysis #1178/#1317).
|
|
40
52
|
*/
|
|
41
53
|
private static _doRegister;
|
|
42
54
|
/**
|
|
@@ -1,7 +1,48 @@
|
|
|
1
1
|
import { ProviderFactory } from "./providerFactory.js";
|
|
2
2
|
import { logger } from "../utils/logger.js";
|
|
3
|
-
import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels,
|
|
3
|
+
import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, HuggingFaceModels, DeepSeekModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
|
|
4
4
|
import { PROVIDER_DESCRIPTORS_BY_NAME } from "./providerDescriptors.js";
|
|
5
|
+
import { OPENAI_COMPAT_CATALOG } from "../providers/openaiCompatCatalog.js";
|
|
6
|
+
/**
|
|
7
|
+
* Static module -> provider-ID manifest for every provider registered below.
|
|
8
|
+
* Module names (file/dir under src/lib/providers/) intentionally differ from
|
|
9
|
+
* their canonical IDs (e.g. amazonBedrock -> "bedrock", googleVertex ->
|
|
10
|
+
* "vertex"); static scanners that cannot resolve the dynamic import() calls
|
|
11
|
+
* use this mapping to confirm a module is registered. Enforced at runtime so
|
|
12
|
+
* the registry cannot silently drift from this manifest.
|
|
13
|
+
*/
|
|
14
|
+
export const PROVIDER_MODULE_TO_ID = {
|
|
15
|
+
amazonBedrock: AIProviderName.BEDROCK,
|
|
16
|
+
amazonSagemaker: AIProviderName.SAGEMAKER,
|
|
17
|
+
anthropic: AIProviderName.ANTHROPIC,
|
|
18
|
+
azureOpenai: AIProviderName.AZURE,
|
|
19
|
+
cloudflare: AIProviderName.CLOUDFLARE,
|
|
20
|
+
cohere: AIProviderName.COHERE,
|
|
21
|
+
deepseek: AIProviderName.DEEPSEEK,
|
|
22
|
+
fireworks: AIProviderName.FIREWORKS,
|
|
23
|
+
googleAiStudio: AIProviderName.GOOGLE_AI,
|
|
24
|
+
googleVertex: AIProviderName.VERTEX,
|
|
25
|
+
groq: AIProviderName.GROQ,
|
|
26
|
+
huggingFace: AIProviderName.HUGGINGFACE,
|
|
27
|
+
ideogram: AIProviderName.IDEOGRAM,
|
|
28
|
+
jina: AIProviderName.JINA,
|
|
29
|
+
litellm: AIProviderName.LITELLM,
|
|
30
|
+
llamaCpp: AIProviderName.LLAMACPP,
|
|
31
|
+
lmStudio: AIProviderName.LM_STUDIO,
|
|
32
|
+
mistral: AIProviderName.MISTRAL,
|
|
33
|
+
nvidiaNim: AIProviderName.NVIDIA_NIM,
|
|
34
|
+
ollama: AIProviderName.OLLAMA,
|
|
35
|
+
openAI: AIProviderName.OPENAI,
|
|
36
|
+
openaiCompatible: AIProviderName.OPENAI_COMPATIBLE,
|
|
37
|
+
openRouter: AIProviderName.OPENROUTER,
|
|
38
|
+
perplexity: AIProviderName.PERPLEXITY,
|
|
39
|
+
recraft: AIProviderName.RECRAFT,
|
|
40
|
+
replicate: AIProviderName.REPLICATE,
|
|
41
|
+
stability: AIProviderName.STABILITY,
|
|
42
|
+
togetherAi: AIProviderName.TOGETHER_AI,
|
|
43
|
+
voyage: AIProviderName.VOYAGE,
|
|
44
|
+
xai: AIProviderName.XAI,
|
|
45
|
+
};
|
|
5
46
|
/**
|
|
6
47
|
* Provider Registry - registers all providers with the factory
|
|
7
48
|
* This is where we migrate providers one by one to the new pattern
|
|
@@ -53,10 +94,12 @@ export class ProviderRegistry {
|
|
|
53
94
|
* (avoids circular dependencies; see CLAUDE.md).
|
|
54
95
|
*
|
|
55
96
|
* Not registered (by design): index.ts, providerTypeUtils.ts,
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* (
|
|
59
|
-
*
|
|
97
|
+
* openaiChatCompletionsBase.ts, openaiChatCompletionsClient.ts,
|
|
98
|
+
* anthropicImageBlocks.ts (shared base/helper modules), anthropicBaseProvider.ts
|
|
99
|
+
* (legacy; anthropic.ts is live), googleNativeGemini3.ts (shared helpers).
|
|
100
|
+
* Filename != provider ID (e.g. amazonBedrock -> "bedrock"); the
|
|
101
|
+
* statically-scannable PROVIDER_MODULE_TO_ID manifest maps every registered
|
|
102
|
+
* module to its provider ID and is enforced below (Pattern Analysis #1178/#1317).
|
|
60
103
|
*/
|
|
61
104
|
// eslint-disable-next-line max-lines-per-function
|
|
62
105
|
static async _doRegister() {
|
|
@@ -111,12 +154,18 @@ export class ProviderRegistry {
|
|
|
111
154
|
return new HuggingFaceProvider(modelName, sdk, region, hfCreds);
|
|
112
155
|
}, process.env.HUGGINGFACE_MODEL ||
|
|
113
156
|
HuggingFaceModels.QWEN_2_5_72B_INSTRUCT, ["huggingface", "hf"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.HUGGINGFACE));
|
|
114
|
-
// Register
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
157
|
+
// Register the config-driven OpenAI-compatible catalog providers
|
|
158
|
+
// (groq, xai, together-ai, fireworks, perplexity, mistral, cloudflare).
|
|
159
|
+
// To add a new zero-quirk OpenAI-compatible provider, add one entry to
|
|
160
|
+
// OPENAI_COMPAT_CATALOG (openaiCompatCatalog.ts) — not a new block here.
|
|
161
|
+
for (const entry of OPENAI_COMPAT_CATALOG) {
|
|
162
|
+
ProviderFactory.registerProvider(entry.providerName, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
163
|
+
const { ConfiguredOpenAICompatProvider } = await import("../providers/configuredOpenAICompat.js");
|
|
164
|
+
return new ConfiguredOpenAICompatProvider(entry, modelName, sdk, credentials);
|
|
165
|
+
}, entry.registryDefaultModelChecksEnvVar
|
|
166
|
+
? process.env[entry.modelEnvVar] || entry.registryDefaultModel
|
|
167
|
+
: entry.registryDefaultModel, entry.aliases, PROVIDER_DESCRIPTORS_BY_NAME.get(entry.providerName));
|
|
168
|
+
}
|
|
120
169
|
// Register Ollama provider
|
|
121
170
|
ProviderFactory.registerProvider(AIProviderName.OLLAMA, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
122
171
|
const ollamaCreds = credentials;
|
|
@@ -178,49 +227,12 @@ export class ProviderRegistry {
|
|
|
178
227
|
const { LlamaCppProvider } = await import("../providers/llamaCpp.js");
|
|
179
228
|
return new LlamaCppProvider(modelName, sdk, undefined, llamaCppCreds);
|
|
180
229
|
}, process.env.LLAMACPP_MODEL || undefined, ["llamacpp", "llama.cpp", "llama-cpp"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.LLAMACPP));
|
|
181
|
-
// Register xAI Grok provider
|
|
182
|
-
ProviderFactory.registerProvider(AIProviderName.XAI, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
183
|
-
const xaiCreds = credentials;
|
|
184
|
-
const { XaiProvider } = await import("../providers/xai.js");
|
|
185
|
-
return new XaiProvider(modelName, sdk, undefined, xaiCreds);
|
|
186
|
-
}, process.env.XAI_MODEL || XaiModels.GROK_3, ["xai", "grok"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.XAI));
|
|
187
|
-
// Register Groq provider
|
|
188
|
-
ProviderFactory.registerProvider(AIProviderName.GROQ, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
189
|
-
const groqCreds = credentials;
|
|
190
|
-
const { GroqProvider } = await import("../providers/groq.js");
|
|
191
|
-
return new GroqProvider(modelName, sdk, undefined, groqCreds);
|
|
192
|
-
}, process.env.GROQ_MODEL || GroqModels.LLAMA_3_3_70B_VERSATILE, ["groq"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.GROQ));
|
|
193
230
|
// Register Cohere provider
|
|
194
231
|
ProviderFactory.registerProvider(AIProviderName.COHERE, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
195
232
|
const cohereCreds = credentials;
|
|
196
233
|
const { CohereProvider } = await import("../providers/cohere.js");
|
|
197
234
|
return new CohereProvider(modelName, sdk, undefined, cohereCreds);
|
|
198
235
|
}, process.env.COHERE_MODEL || CohereModels.COMMAND_R_PLUS, ["cohere"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.COHERE));
|
|
199
|
-
// Register Together AI provider
|
|
200
|
-
ProviderFactory.registerProvider(AIProviderName.TOGETHER_AI, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
201
|
-
const togetherCreds = credentials;
|
|
202
|
-
const { TogetherAIProvider } = await import("../providers/togetherAi.js");
|
|
203
|
-
return new TogetherAIProvider(modelName, sdk, undefined, togetherCreds);
|
|
204
|
-
}, process.env.TOGETHER_MODEL ||
|
|
205
|
-
TogetherAIModels.LLAMA_3_3_70B_INSTRUCT_TURBO, ["together-ai", "together"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.TOGETHER_AI));
|
|
206
|
-
// Register Fireworks AI provider
|
|
207
|
-
ProviderFactory.registerProvider(AIProviderName.FIREWORKS, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
208
|
-
const fireworksCreds = credentials;
|
|
209
|
-
const { FireworksProvider } = await import("../providers/fireworks.js");
|
|
210
|
-
return new FireworksProvider(modelName, sdk, undefined, fireworksCreds);
|
|
211
|
-
}, process.env.FIREWORKS_MODEL || FireworksModels.DEEPSEEK_V4_PRO, ["fireworks"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.FIREWORKS));
|
|
212
|
-
// Register Perplexity provider
|
|
213
|
-
ProviderFactory.registerProvider(AIProviderName.PERPLEXITY, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
214
|
-
const perplexityCreds = credentials;
|
|
215
|
-
const { PerplexityProvider } = await import("../providers/perplexity.js");
|
|
216
|
-
return new PerplexityProvider(modelName, sdk, undefined, perplexityCreds);
|
|
217
|
-
}, process.env.PERPLEXITY_MODEL || PerplexityModels.SONAR, ["perplexity", "pplx"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.PERPLEXITY));
|
|
218
|
-
// Register Cloudflare Workers AI provider
|
|
219
|
-
ProviderFactory.registerProvider(AIProviderName.CLOUDFLARE, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
220
|
-
const cloudflareCreds = credentials;
|
|
221
|
-
const { CloudflareProvider } = await import("../providers/cloudflare.js");
|
|
222
|
-
return new CloudflareProvider(modelName, sdk, undefined, cloudflareCreds);
|
|
223
|
-
}, process.env.CLOUDFLARE_MODEL || CloudflareModels.LLAMA_3_3_70B_FAST, ["cloudflare", "workers-ai", "cf-ai"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.CLOUDFLARE));
|
|
224
236
|
// Register Voyage AI embeddings provider
|
|
225
237
|
ProviderFactory.registerProvider(AIProviderName.VOYAGE, async (modelName, _providerName, sdk, _region, credentials) => {
|
|
226
238
|
const voyageCreds = credentials;
|
|
@@ -258,6 +270,16 @@ export class ProviderRegistry {
|
|
|
258
270
|
const { RecraftProvider } = await import("../providers/recraft.js");
|
|
259
271
|
return new RecraftProvider(modelName, sdk, undefined, recraftCreds);
|
|
260
272
|
}, process.env.RECRAFT_MODEL || RecraftModels.RECRAFT_V3, ["recraft"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.RECRAFT));
|
|
273
|
+
const unregistered = Object.entries(PROVIDER_MODULE_TO_ID).filter(([module, id]) => {
|
|
274
|
+
if (!ProviderFactory.hasProvider(id)) {
|
|
275
|
+
logger.error(`[ProviderRegistry] drift: module "${module}" not registered as "${id}"`);
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
});
|
|
280
|
+
if (unregistered.length > 0) {
|
|
281
|
+
throw new Error(`ProviderRegistry drift: ${unregistered.length} module(s) in PROVIDER_MODULE_TO_ID are not registered`);
|
|
282
|
+
}
|
|
261
283
|
logger.debug("All AI providers registered successfully");
|
|
262
284
|
// ===== TTS HANDLER REGISTRATION =====
|
|
263
285
|
try {
|
|
@@ -2,6 +2,7 @@ import { logger } from "../utils/logger.js";
|
|
|
2
2
|
import { redactUrlCredentials } from "../utils/logSanitize.js";
|
|
3
3
|
import { getProviderModel, resolveOpenAICompatConfig, } from "../utils/providerConfig.js";
|
|
4
4
|
import { classifyProviderError } from "../utils/errorClassifier.js";
|
|
5
|
+
import { TimeoutError } from "../utils/timeout.js";
|
|
5
6
|
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
6
7
|
/**
|
|
7
8
|
* Generic OpenAI-compatible provider driven entirely by an
|
|
@@ -51,9 +52,15 @@ export class ConfiguredOpenAICompatProvider extends OpenAIChatCompletionsProvide
|
|
|
51
52
|
return this.entry.fallbackModels;
|
|
52
53
|
}
|
|
53
54
|
formatProviderError(error) {
|
|
54
|
-
// classifyProviderError
|
|
55
|
-
//
|
|
56
|
-
//
|
|
55
|
+
// classifyProviderError hard-codes TimeoutError -> NetworkError ahead
|
|
56
|
+
// of any rule table and does not allow a per-provider override. An
|
|
57
|
+
// entry can opt out of that default via timeoutErrorClass (currently
|
|
58
|
+
// only Groq, reproducing its pre-migration subclass's own TimeoutError
|
|
59
|
+
// interception) — checked here, before ever reaching the shared
|
|
60
|
+
// classifier, so every other entry still gets its unmodified default.
|
|
61
|
+
if (error instanceof TimeoutError && this.entry.timeoutErrorClass) {
|
|
62
|
+
return new this.entry.timeoutErrorClass(`${this.entry.configOptions.providerName} request timed out: ${error.message}`, this.entry.providerName);
|
|
63
|
+
}
|
|
57
64
|
return classifyProviderError(error, this.entry.errorRules, this.entry.providerName, this.modelName);
|
|
58
65
|
}
|
|
59
66
|
}
|
|
@@ -464,15 +464,22 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
464
464
|
const apiErr = await buildAPIError(url, body, res);
|
|
465
465
|
// One-shot 400 retry. The overflow corrector runs FIRST (it can
|
|
466
466
|
// re-fit max_tokens from the provider's own numbers and also
|
|
467
|
-
// self-heals the runtime window registry);
|
|
468
|
-
// may strip a rejected field
|
|
469
|
-
//
|
|
470
|
-
//
|
|
471
|
-
//
|
|
472
|
-
//
|
|
467
|
+
// self-heals the runtime window registry); its output then feeds
|
|
468
|
+
// a subclass hook that may strip a rejected field (e.g. NIM's
|
|
469
|
+
// chat_template / reasoning_budget), so a body that needs BOTH
|
|
470
|
+
// fixes gets both — a plain `??` between the two would let
|
|
471
|
+
// whichever ran first silently win and drop the other's fix. The
|
|
472
|
+
// retry runs under the SAME timeout controller as the first
|
|
473
|
+
// attempt, so the configured timeout caps the overall call —
|
|
474
|
+
// matching the streaming path, which reuses its composed signal
|
|
475
|
+
// for the retry.
|
|
473
476
|
const retryBody = res.status === 400
|
|
474
|
-
? (
|
|
475
|
-
|
|
477
|
+
? (() => {
|
|
478
|
+
const typedErr = apiErr;
|
|
479
|
+
const overflowCorrected = correctBodyAfterContextOverflow(body, typedErr);
|
|
480
|
+
return (adjustBodyAfter400(overflowCorrected ?? body, typedErr) ??
|
|
481
|
+
overflowCorrected);
|
|
482
|
+
})()
|
|
476
483
|
: undefined;
|
|
477
484
|
if (!retryBody) {
|
|
478
485
|
throw apiErr;
|
|
@@ -967,13 +974,18 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
967
974
|
// consumed inside doFetch's closure, either returned on success or
|
|
968
975
|
// discarded after buildAPIError read its body on failure.
|
|
969
976
|
const apiErr = err;
|
|
970
|
-
// Overflow corrector first (re-fits max_tokens from the provider's
|
|
971
|
-
//
|
|
972
|
-
// hook (e.g. NIM strips chat_template / reasoning_budget when
|
|
973
|
-
// rejects them)
|
|
977
|
+
// Overflow corrector first (re-fits max_tokens from the provider's own
|
|
978
|
+
// numbers + self-heals the window registry); its output then feeds the
|
|
979
|
+
// subclass hook (e.g. NIM strips chat_template / reasoning_budget when
|
|
980
|
+
// a model rejects them), so a body needing BOTH fixes gets both — a
|
|
981
|
+
// plain `??` between the two would let whichever ran first silently
|
|
982
|
+
// win and drop the other's fix.
|
|
974
983
|
const retryBody = apiErr.statusCode === 400
|
|
975
|
-
? (
|
|
976
|
-
this.
|
|
984
|
+
? (() => {
|
|
985
|
+
const overflowCorrected = this.correctBodyAfterContextOverflow(body, apiErr);
|
|
986
|
+
return (this.adjustBodyAfter400(overflowCorrected ?? body, apiErr) ??
|
|
987
|
+
overflowCorrected);
|
|
988
|
+
})()
|
|
977
989
|
: undefined;
|
|
978
990
|
if (!retryBody) {
|
|
979
991
|
throw apiErr;
|
|
@@ -49,17 +49,15 @@ export const OPENAI_COMPAT_CATALOG = [
|
|
|
49
49
|
GroqModels.LLAMA_3_2_90B_VISION_PREVIEW,
|
|
50
50
|
GroqModels.LLAMA_3_2_11B_VISION_PREVIEW,
|
|
51
51
|
],
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
// shape; the TimeoutError special case is a structural gap in
|
|
62
|
-
// ConfiguredOpenAICompatProvider, not a data error here.
|
|
52
|
+
// Groq's pre-migration subclass intercepted TimeoutError itself and
|
|
53
|
+
// returned a plain ProviderError, ahead of classifyProviderError's own
|
|
54
|
+
// non-overridable TimeoutError -> NetworkError default. Expressed here
|
|
55
|
+
// as data — see OpenAICompatCatalogEntry.timeoutErrorClass and
|
|
56
|
+
// ConfiguredOpenAICompatProvider.formatProviderError, which consults
|
|
57
|
+
// this field before ever delegating to the shared classifier. No other
|
|
58
|
+
// entry in this catalog sets it, so every other provider still gets
|
|
59
|
+
// the classifier's unmodified default.
|
|
60
|
+
timeoutErrorClass: ProviderError,
|
|
63
61
|
errorRules: [
|
|
64
62
|
{
|
|
65
63
|
match: (ctx) => ctx.statusCode === 401 ||
|
|
@@ -8,7 +8,7 @@ import type { ValidationSchema } from "./aliases.js";
|
|
|
8
8
|
import type { EnhancedGenerateResult, GenerateResult, TextGenerationOptions } from "./generate.js";
|
|
9
9
|
import type { MultimodalAudioEntry } from "./file.js";
|
|
10
10
|
import type { StreamOptions, StreamResult } from "./stream.js";
|
|
11
|
-
import type { ProviderErrorRule } from "./errors.js";
|
|
11
|
+
import type { ProviderError, ProviderErrorRule } from "./errors.js";
|
|
12
12
|
import type { ExternalMCPToolInfo } from "./externalMcp.js";
|
|
13
13
|
import type { ClaudeSubscriptionTier, AnthropicAuthMethod, AnthropicAuthConfig, SubscriptionInfo, OAuthToken } from "./subscription.js";
|
|
14
14
|
import type { Tool } from "./tools.js";
|
|
@@ -671,6 +671,20 @@ export type OpenAICompatCatalogEntry = {
|
|
|
671
671
|
* Task 4, so nothing actually mutates it at runtime.
|
|
672
672
|
*/
|
|
673
673
|
errorRules: ProviderErrorRule[];
|
|
674
|
+
/**
|
|
675
|
+
* Optional override for the Error subclass a TimeoutError should produce
|
|
676
|
+
* for this entry. classifyProviderError() hard-codes
|
|
677
|
+
* TimeoutError -> NetworkError unconditionally, ahead of any rule table,
|
|
678
|
+
* and does not make that mapping overridable per-provider (see
|
|
679
|
+
* errorClassifier.ts). Groq's pre-migration subclass predates that
|
|
680
|
+
* shared classifier and intercepted TimeoutError itself, returning a
|
|
681
|
+
* plain ProviderError instead — this field lets
|
|
682
|
+
* ConfiguredOpenAICompatProvider reproduce that one documented
|
|
683
|
+
* divergence as data (see its formatProviderError), rather than adding a
|
|
684
|
+
* class-level hook back in. Omit for every entry whose timeout should use
|
|
685
|
+
* the classifier's default (six of the seven catalog entries).
|
|
686
|
+
*/
|
|
687
|
+
timeoutErrorClass?: new (message: string, provider?: string) => ProviderError;
|
|
674
688
|
};
|
|
675
689
|
/** The subset of OpenAICompatCatalogEntry that resolveOpenAICompatConfig()
|
|
676
690
|
* needs — lets call sites pass a minimal object without the full catalog
|
|
@@ -2,6 +2,7 @@ import { logger } from "../utils/logger.js";
|
|
|
2
2
|
import { redactUrlCredentials } from "../utils/logSanitize.js";
|
|
3
3
|
import { getProviderModel, resolveOpenAICompatConfig, } from "../utils/providerConfig.js";
|
|
4
4
|
import { classifyProviderError } from "../utils/errorClassifier.js";
|
|
5
|
+
import { TimeoutError } from "../utils/timeout.js";
|
|
5
6
|
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
6
7
|
/**
|
|
7
8
|
* Generic OpenAI-compatible provider driven entirely by an
|
|
@@ -51,9 +52,15 @@ export class ConfiguredOpenAICompatProvider extends OpenAIChatCompletionsProvide
|
|
|
51
52
|
return this.entry.fallbackModels;
|
|
52
53
|
}
|
|
53
54
|
formatProviderError(error) {
|
|
54
|
-
// classifyProviderError
|
|
55
|
-
//
|
|
56
|
-
//
|
|
55
|
+
// classifyProviderError hard-codes TimeoutError -> NetworkError ahead
|
|
56
|
+
// of any rule table and does not allow a per-provider override. An
|
|
57
|
+
// entry can opt out of that default via timeoutErrorClass (currently
|
|
58
|
+
// only Groq, reproducing its pre-migration subclass's own TimeoutError
|
|
59
|
+
// interception) — checked here, before ever reaching the shared
|
|
60
|
+
// classifier, so every other entry still gets its unmodified default.
|
|
61
|
+
if (error instanceof TimeoutError && this.entry.timeoutErrorClass) {
|
|
62
|
+
return new this.entry.timeoutErrorClass(`${this.entry.configOptions.providerName} request timed out: ${error.message}`, this.entry.providerName);
|
|
63
|
+
}
|
|
57
64
|
return classifyProviderError(error, this.entry.errorRules, this.entry.providerName, this.modelName);
|
|
58
65
|
}
|
|
59
66
|
}
|
|
@@ -464,15 +464,22 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
464
464
|
const apiErr = await buildAPIError(url, body, res);
|
|
465
465
|
// One-shot 400 retry. The overflow corrector runs FIRST (it can
|
|
466
466
|
// re-fit max_tokens from the provider's own numbers and also
|
|
467
|
-
// self-heals the runtime window registry);
|
|
468
|
-
// may strip a rejected field
|
|
469
|
-
//
|
|
470
|
-
//
|
|
471
|
-
//
|
|
472
|
-
//
|
|
467
|
+
// self-heals the runtime window registry); its output then feeds
|
|
468
|
+
// a subclass hook that may strip a rejected field (e.g. NIM's
|
|
469
|
+
// chat_template / reasoning_budget), so a body that needs BOTH
|
|
470
|
+
// fixes gets both — a plain `??` between the two would let
|
|
471
|
+
// whichever ran first silently win and drop the other's fix. The
|
|
472
|
+
// retry runs under the SAME timeout controller as the first
|
|
473
|
+
// attempt, so the configured timeout caps the overall call —
|
|
474
|
+
// matching the streaming path, which reuses its composed signal
|
|
475
|
+
// for the retry.
|
|
473
476
|
const retryBody = res.status === 400
|
|
474
|
-
? (
|
|
475
|
-
|
|
477
|
+
? (() => {
|
|
478
|
+
const typedErr = apiErr;
|
|
479
|
+
const overflowCorrected = correctBodyAfterContextOverflow(body, typedErr);
|
|
480
|
+
return (adjustBodyAfter400(overflowCorrected ?? body, typedErr) ??
|
|
481
|
+
overflowCorrected);
|
|
482
|
+
})()
|
|
476
483
|
: undefined;
|
|
477
484
|
if (!retryBody) {
|
|
478
485
|
throw apiErr;
|
|
@@ -967,13 +974,18 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
|
|
|
967
974
|
// consumed inside doFetch's closure, either returned on success or
|
|
968
975
|
// discarded after buildAPIError read its body on failure.
|
|
969
976
|
const apiErr = err;
|
|
970
|
-
// Overflow corrector first (re-fits max_tokens from the provider's
|
|
971
|
-
//
|
|
972
|
-
// hook (e.g. NIM strips chat_template / reasoning_budget when
|
|
973
|
-
// rejects them)
|
|
977
|
+
// Overflow corrector first (re-fits max_tokens from the provider's own
|
|
978
|
+
// numbers + self-heals the window registry); its output then feeds the
|
|
979
|
+
// subclass hook (e.g. NIM strips chat_template / reasoning_budget when
|
|
980
|
+
// a model rejects them), so a body needing BOTH fixes gets both — a
|
|
981
|
+
// plain `??` between the two would let whichever ran first silently
|
|
982
|
+
// win and drop the other's fix.
|
|
974
983
|
const retryBody = apiErr.statusCode === 400
|
|
975
|
-
? (
|
|
976
|
-
this.
|
|
984
|
+
? (() => {
|
|
985
|
+
const overflowCorrected = this.correctBodyAfterContextOverflow(body, apiErr);
|
|
986
|
+
return (this.adjustBodyAfter400(overflowCorrected ?? body, apiErr) ??
|
|
987
|
+
overflowCorrected);
|
|
988
|
+
})()
|
|
977
989
|
: undefined;
|
|
978
990
|
if (!retryBody) {
|
|
979
991
|
throw apiErr;
|