@juspay/neurolink 11.2.2 → 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.
Files changed (41) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/browser/neurolink.min.js +392 -392
  3. package/dist/factories/providerRegistry.d.ts +16 -4
  4. package/dist/factories/providerRegistry.js +70 -48
  5. package/dist/lib/factories/providerRegistry.d.ts +16 -4
  6. package/dist/lib/factories/providerRegistry.js +70 -48
  7. package/dist/lib/providers/configuredOpenAICompat.js +10 -3
  8. package/dist/lib/providers/openaiCompatCatalog.js +9 -11
  9. package/dist/lib/types/providers.d.ts +15 -1
  10. package/dist/providers/configuredOpenAICompat.js +10 -3
  11. package/dist/providers/openaiCompatCatalog.js +9 -11
  12. package/dist/types/providers.d.ts +15 -1
  13. package/package.json +2 -2
  14. package/dist/lib/providers/cloudflare.d.ts +0 -26
  15. package/dist/lib/providers/cloudflare.js +0 -83
  16. package/dist/lib/providers/fireworks.d.ts +0 -22
  17. package/dist/lib/providers/fireworks.js +0 -73
  18. package/dist/lib/providers/groq.d.ts +0 -24
  19. package/dist/lib/providers/groq.js +0 -83
  20. package/dist/lib/providers/mistral.d.ts +0 -25
  21. package/dist/lib/providers/mistral.js +0 -87
  22. package/dist/lib/providers/perplexity.d.ts +0 -24
  23. package/dist/lib/providers/perplexity.js +0 -69
  24. package/dist/lib/providers/togetherAi.d.ts +0 -23
  25. package/dist/lib/providers/togetherAi.js +0 -73
  26. package/dist/lib/providers/xai.d.ts +0 -22
  27. package/dist/lib/providers/xai.js +0 -77
  28. package/dist/providers/cloudflare.d.ts +0 -26
  29. package/dist/providers/cloudflare.js +0 -82
  30. package/dist/providers/fireworks.d.ts +0 -22
  31. package/dist/providers/fireworks.js +0 -72
  32. package/dist/providers/groq.d.ts +0 -24
  33. package/dist/providers/groq.js +0 -82
  34. package/dist/providers/mistral.d.ts +0 -25
  35. package/dist/providers/mistral.js +0 -86
  36. package/dist/providers/perplexity.d.ts +0 -24
  37. package/dist/providers/perplexity.js +0 -68
  38. package/dist/providers/togetherAi.d.ts +0 -23
  39. package/dist/providers/togetherAi.js +0 -72
  40. package/dist/providers/xai.d.ts +0 -22
  41. 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
- * anthropicBaseProvider.ts (legacy; anthropic.ts is live),
37
- * googleNativeGemini3.ts (shared helpers). Filename != provider ID
38
- * (e.g. amazonBedrock -> "bedrock"); static scanners that miss dynamic
39
- * imports may false-positive (Pattern Analysis #1178).
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, MistralModels, OllamaModels, LiteLLMModels, HuggingFaceModels, DeepSeekModels, NvidiaNimModels, OpenRouterModels, XaiModels, GroqModels, CohereModels, TogetherAIModels, FireworksModels, PerplexityModels, CloudflareModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
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
- * anthropicBaseProvider.ts (legacy; anthropic.ts is live),
57
- * googleNativeGemini3.ts (shared helpers). Filename != provider ID
58
- * (e.g. amazonBedrock -> "bedrock"); static scanners that miss dynamic
59
- * imports may false-positive (Pattern Analysis #1178).
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 Mistral AI provider
115
- ProviderFactory.registerProvider(AIProviderName.MISTRAL, async (modelName, _providerName, sdk, _region, credentials) => {
116
- const mistralCreds = credentials;
117
- const { MistralProvider } = await import("../providers/mistral.js");
118
- return new MistralProvider(modelName, sdk, undefined, mistralCreds);
119
- }, MistralModels.MISTRAL_LARGE_LATEST, ["mistral"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.MISTRAL));
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
- * anthropicBaseProvider.ts (legacy; anthropic.ts is live),
37
- * googleNativeGemini3.ts (shared helpers). Filename != provider ID
38
- * (e.g. amazonBedrock -> "bedrock"); static scanners that miss dynamic
39
- * imports may false-positive (Pattern Analysis #1178).
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, MistralModels, OllamaModels, LiteLLMModels, HuggingFaceModels, DeepSeekModels, NvidiaNimModels, OpenRouterModels, XaiModels, GroqModels, CohereModels, TogetherAIModels, FireworksModels, PerplexityModels, CloudflareModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
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
- * anthropicBaseProvider.ts (legacy; anthropic.ts is live),
57
- * googleNativeGemini3.ts (shared helpers). Filename != provider ID
58
- * (e.g. amazonBedrock -> "bedrock"); static scanners that miss dynamic
59
- * imports may false-positive (Pattern Analysis #1178).
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 Mistral AI provider
115
- ProviderFactory.registerProvider(AIProviderName.MISTRAL, async (modelName, _providerName, sdk, _region, credentials) => {
116
- const mistralCreds = credentials;
117
- const { MistralProvider } = await import("../providers/mistral.js");
118
- return new MistralProvider(modelName, sdk, undefined, mistralCreds);
119
- }, MistralModels.MISTRAL_LARGE_LATEST, ["mistral"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.MISTRAL));
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 handles TimeoutError internally (always maps
55
- // to NetworkError, ahead of any rule table) no local pre-check
56
- // needed or wanted here; see this task's design note.
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
  }
@@ -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
- // KNOWN GAP (not reproducible by this data-only array flagged for PR C,
53
- // see plan-05/task-A-report.md): live GroqProvider.formatProviderError()
54
- // intercepts TimeoutError and returns a plain ProviderError BEFORE ever
55
- // calling classifyProviderError, overriding that function's own
56
- // (non-overridable) rule that TimeoutError always maps to NetworkError.
57
- // ConfiguredOpenAICompatProvider.formatProviderError() has no such
58
- // pre-check hook, so migrating Groq onto it as-is would silently
59
- // reclassify Groq timeouts as NetworkError. This entry's errorRules
60
- // still mirrors Groq's live rule array faithfully for every other error
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 handles TimeoutError internally (always maps
55
- // to NetworkError, ahead of any rule table) no local pre-check
56
- // needed or wanted here; see this task's design note.
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
  }
@@ -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
- // KNOWN GAP (not reproducible by this data-only array flagged for PR C,
53
- // see plan-05/task-A-report.md): live GroqProvider.formatProviderError()
54
- // intercepts TimeoutError and returns a plain ProviderError BEFORE ever
55
- // calling classifyProviderError, overriding that function's own
56
- // (non-overridable) rule that TimeoutError always maps to NetworkError.
57
- // ConfiguredOpenAICompatProvider.formatProviderError() has no such
58
- // pre-check hook, so migrating Groq onto it as-is would silently
59
- // reclassify Groq timeouts as NetworkError. This entry's errorRules
60
- // still mirrors Groq's live rule array faithfully for every other error
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "11.2.2",
3
+ "version": "11.2.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": {
@@ -125,7 +125,7 @@
125
125
  "test:tool-dedup": "npx tsx test/continuous-test-suite-tool-dedup.ts",
126
126
  "test:model-not-found-retryable": "npx tsx test/continuous-test-suite-model-not-found-retryable.ts",
127
127
  "test:ci": "pnpm run test && pnpm run test:client && pnpm run test:hitl",
128
- "// CI tier — fast, no live AI calls, safe for every commit (test:unit; also see the separate provider-safety-net CI job, which runs build + test:providers-mocked + test:provider-structure on every PR)": "",
128
+ "// CI tier — fast, no live AI calls, safe for every commit (test:unit; also see the separate provider-safety-net CI job, which runs build + test:providers-mocked + test:provider-structure + test:error-classifier-contract on every PR)": "",
129
129
  "test:tool-routing": "npx tsx test/continuous-test-suite-tool-routing.ts",
130
130
  "test:tool-routing-semantic": "npx tsx test/continuous-test-suite-tool-routing-semantic.ts",
131
131
  "test:unit": "pnpm run test:bugfixes && pnpm run test:mcp:infra && pnpm run test:mcp:spans && pnpm run test:tool-routing && pnpm run test:tool-routing-cli && pnpm run test:tool-dedup && pnpm run test:model-pool && pnpm run test:tool-routing-semantic && pnpm run test:mcp-result-cache && pnpm run test:model-not-found-retryable && pnpm run test:archive:security && pnpm run test:office:security && pnpm run test:vector-chroma && pnpm run test:vector-pgvector && pnpm run test:vector-pinecone && pnpm run test:provider-wiring",
@@ -1,26 +0,0 @@
1
- import type { AIProviderName } from "../constants/enums.js";
2
- import type { NeurolinkCredentials } from "../types/index.js";
3
- import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
4
- /**
5
- * Cloudflare Workers AI Provider — direct HTTP, no AI SDK.
6
- *
7
- * Edge-served open models (Llama / Mistral / Qwen / Gemma) at
8
- * `https://api.cloudflare.com/client/v4/accounts/{accountId}/ai/v1`
9
- * (OpenAI-compatible). Cheapest tier for high-volume usage.
10
- *
11
- * Required env: `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID`.
12
- *
13
- * All request/stream/tool-loop orchestration lives in
14
- * `OpenAIChatCompletionsProvider`; this class only declares configuration
15
- * and provider-specific error mapping.
16
- *
17
- * @see https://developers.cloudflare.com/workers-ai/configuration/open-ai-compatibility/
18
- */
19
- export declare class CloudflareProvider extends OpenAIChatCompletionsProvider {
20
- constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["cloudflare"]);
21
- protected getProviderName(): AIProviderName;
22
- protected getDefaultModel(): string;
23
- protected getFallbackModelName(): string;
24
- protected getFallbackModels(): string[];
25
- protected formatProviderError(error: unknown): Error;
26
- }