@juspay/neurolink 11.2.2 → 11.2.4

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 (67) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser/neurolink.min.js +408 -408
  3. package/dist/core/handlerRegistry.d.ts +29 -0
  4. package/dist/core/handlerRegistry.js +61 -0
  5. package/dist/factories/providerRegistry.js +14 -44
  6. package/dist/lib/core/handlerRegistry.d.ts +29 -0
  7. package/dist/lib/core/handlerRegistry.js +62 -0
  8. package/dist/lib/factories/providerRegistry.js +14 -44
  9. package/dist/lib/providers/configuredOpenAICompat.js +10 -3
  10. package/dist/lib/providers/openaiCompatCatalog.js +9 -11
  11. package/dist/lib/types/providers.d.ts +15 -1
  12. package/dist/lib/utils/avatarProcessor.d.ts +5 -1
  13. package/dist/lib/utils/avatarProcessor.js +13 -18
  14. package/dist/lib/utils/musicProcessor.d.ts +5 -1
  15. package/dist/lib/utils/musicProcessor.js +13 -18
  16. package/dist/lib/utils/sttProcessor.d.ts +10 -2
  17. package/dist/lib/utils/sttProcessor.js +22 -18
  18. package/dist/lib/utils/ttsProcessor.d.ts +10 -2
  19. package/dist/lib/utils/ttsProcessor.js +22 -18
  20. package/dist/lib/utils/videoProcessor.d.ts +5 -1
  21. package/dist/lib/utils/videoProcessor.js +13 -18
  22. package/dist/lib/voice/RealtimeVoiceAPI.d.ts +1 -1
  23. package/dist/lib/voice/RealtimeVoiceAPI.js +17 -28
  24. package/dist/providers/configuredOpenAICompat.js +10 -3
  25. package/dist/providers/openaiCompatCatalog.js +9 -11
  26. package/dist/types/providers.d.ts +15 -1
  27. package/dist/utils/avatarProcessor.d.ts +5 -1
  28. package/dist/utils/avatarProcessor.js +13 -18
  29. package/dist/utils/musicProcessor.d.ts +5 -1
  30. package/dist/utils/musicProcessor.js +13 -18
  31. package/dist/utils/sttProcessor.d.ts +10 -2
  32. package/dist/utils/sttProcessor.js +22 -18
  33. package/dist/utils/ttsProcessor.d.ts +10 -2
  34. package/dist/utils/ttsProcessor.js +22 -18
  35. package/dist/utils/videoProcessor.d.ts +5 -1
  36. package/dist/utils/videoProcessor.js +13 -18
  37. package/dist/voice/RealtimeVoiceAPI.d.ts +1 -1
  38. package/dist/voice/RealtimeVoiceAPI.js +17 -28
  39. package/package.json +8 -2
  40. package/dist/lib/providers/cloudflare.d.ts +0 -26
  41. package/dist/lib/providers/cloudflare.js +0 -83
  42. package/dist/lib/providers/fireworks.d.ts +0 -22
  43. package/dist/lib/providers/fireworks.js +0 -73
  44. package/dist/lib/providers/groq.d.ts +0 -24
  45. package/dist/lib/providers/groq.js +0 -83
  46. package/dist/lib/providers/mistral.d.ts +0 -25
  47. package/dist/lib/providers/mistral.js +0 -87
  48. package/dist/lib/providers/perplexity.d.ts +0 -24
  49. package/dist/lib/providers/perplexity.js +0 -69
  50. package/dist/lib/providers/togetherAi.d.ts +0 -23
  51. package/dist/lib/providers/togetherAi.js +0 -73
  52. package/dist/lib/providers/xai.d.ts +0 -22
  53. package/dist/lib/providers/xai.js +0 -77
  54. package/dist/providers/cloudflare.d.ts +0 -26
  55. package/dist/providers/cloudflare.js +0 -82
  56. package/dist/providers/fireworks.d.ts +0 -22
  57. package/dist/providers/fireworks.js +0 -72
  58. package/dist/providers/groq.d.ts +0 -24
  59. package/dist/providers/groq.js +0 -82
  60. package/dist/providers/mistral.d.ts +0 -25
  61. package/dist/providers/mistral.js +0 -86
  62. package/dist/providers/perplexity.d.ts +0 -24
  63. package/dist/providers/perplexity.js +0 -68
  64. package/dist/providers/togetherAi.d.ts +0 -23
  65. package/dist/providers/togetherAi.js +0 -72
  66. package/dist/providers/xai.d.ts +0 -22
  67. package/dist/providers/xai.js +0 -76
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Generic provider-name → handler registry shared by every media-generation
3
+ * ecosystem (TTS, STT, Realtime, Video, Music, Avatar). Each ecosystem's own
4
+ * processor class composes one instance of this class instead of hand-rolling
5
+ * its own `Map<string, THandler>` plus register/supports/get/list methods.
6
+ *
7
+ * Centralizes only the behavior that was byte-identical across all six
8
+ * hand-rolled registries: input validation, name normalization (lowercase),
9
+ * the overwrite-warning log line, and the four lookup/list/clear operations.
10
+ * Registration-outcome debug logging (whose exact phrasing differs per
11
+ * ecosystem — e.g. "Registered TTS handler..." vs "Registered video
12
+ * handler...") and any ecosystem-specific extra logging (e.g. TTS/STT's
13
+ * `supports()` diagnostics) stay in the owning processor's own wrapper
14
+ * methods; this class does not attempt to unify those.
15
+ */
16
+ export declare class HandlerRegistry<THandler> {
17
+ private readonly scopeName;
18
+ private readonly handlers;
19
+ /**
20
+ * @param scopeName Log-line prefix, e.g. "TTSProcessor" — matches the
21
+ * `[ClassName]` prefix each processor already uses in its own logs.
22
+ */
23
+ constructor(scopeName: string);
24
+ register(providerName: string, handler: THandler): void;
25
+ supports(providerName: string): boolean;
26
+ get(providerName: string): THandler | undefined;
27
+ list(): string[];
28
+ clear(): void;
29
+ }
@@ -0,0 +1,61 @@
1
+ import { logger } from "../utils/logger.js";
2
+ import { sanitizeForLog } from "../utils/logSanitize.js";
3
+ /**
4
+ * Generic provider-name → handler registry shared by every media-generation
5
+ * ecosystem (TTS, STT, Realtime, Video, Music, Avatar). Each ecosystem's own
6
+ * processor class composes one instance of this class instead of hand-rolling
7
+ * its own `Map<string, THandler>` plus register/supports/get/list methods.
8
+ *
9
+ * Centralizes only the behavior that was byte-identical across all six
10
+ * hand-rolled registries: input validation, name normalization (lowercase),
11
+ * the overwrite-warning log line, and the four lookup/list/clear operations.
12
+ * Registration-outcome debug logging (whose exact phrasing differs per
13
+ * ecosystem — e.g. "Registered TTS handler..." vs "Registered video
14
+ * handler...") and any ecosystem-specific extra logging (e.g. TTS/STT's
15
+ * `supports()` diagnostics) stay in the owning processor's own wrapper
16
+ * methods; this class does not attempt to unify those.
17
+ */
18
+ export class HandlerRegistry {
19
+ scopeName;
20
+ handlers = new Map();
21
+ /**
22
+ * @param scopeName Log-line prefix, e.g. "TTSProcessor" — matches the
23
+ * `[ClassName]` prefix each processor already uses in its own logs.
24
+ */
25
+ constructor(scopeName) {
26
+ this.scopeName = scopeName;
27
+ }
28
+ register(providerName, handler) {
29
+ if (!providerName) {
30
+ throw new Error("Provider name is required");
31
+ }
32
+ if (!handler) {
33
+ throw new Error("Handler is required");
34
+ }
35
+ const key = providerName.toLowerCase();
36
+ if (this.handlers.has(key)) {
37
+ // Every caller today passes a literal provider slug, but this class is
38
+ // generic over an arbitrary string — sanitize before logging it so a
39
+ // future caller building the key from untrusted input can't leak a
40
+ // bearer token/API key through this warning.
41
+ logger.warn(`[${this.scopeName}] Overwriting existing handler for provider: ${sanitizeForLog(key)}`);
42
+ }
43
+ this.handlers.set(key, handler);
44
+ }
45
+ supports(providerName) {
46
+ if (!providerName) {
47
+ return false;
48
+ }
49
+ return this.handlers.has(providerName.toLowerCase());
50
+ }
51
+ get(providerName) {
52
+ return this.handlers.get(providerName.toLowerCase());
53
+ }
54
+ list() {
55
+ return Array.from(this.handlers.keys());
56
+ }
57
+ clear() {
58
+ this.handlers.clear();
59
+ logger.debug(`[${this.scopeName}] Cleared all handlers`);
60
+ }
61
+ }
@@ -1,7 +1,8 @@
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";
5
6
  /**
6
7
  * Provider Registry - registers all providers with the factory
7
8
  * This is where we migrate providers one by one to the new pattern
@@ -111,12 +112,18 @@ export class ProviderRegistry {
111
112
  return new HuggingFaceProvider(modelName, sdk, region, hfCreds);
112
113
  }, process.env.HUGGINGFACE_MODEL ||
113
114
  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));
115
+ // Register the config-driven OpenAI-compatible catalog providers
116
+ // (groq, xai, together-ai, fireworks, perplexity, mistral, cloudflare).
117
+ // To add a new zero-quirk OpenAI-compatible provider, add one entry to
118
+ // OPENAI_COMPAT_CATALOG (openaiCompatCatalog.ts) not a new block here.
119
+ for (const entry of OPENAI_COMPAT_CATALOG) {
120
+ ProviderFactory.registerProvider(entry.providerName, async (modelName, _providerName, sdk, _region, credentials) => {
121
+ const { ConfiguredOpenAICompatProvider } = await import("../providers/configuredOpenAICompat.js");
122
+ return new ConfiguredOpenAICompatProvider(entry, modelName, sdk, credentials);
123
+ }, entry.registryDefaultModelChecksEnvVar
124
+ ? process.env[entry.modelEnvVar] || entry.registryDefaultModel
125
+ : entry.registryDefaultModel, entry.aliases, PROVIDER_DESCRIPTORS_BY_NAME.get(entry.providerName));
126
+ }
120
127
  // Register Ollama provider
121
128
  ProviderFactory.registerProvider(AIProviderName.OLLAMA, async (modelName, _providerName, sdk, _region, credentials) => {
122
129
  const ollamaCreds = credentials;
@@ -178,49 +185,12 @@ export class ProviderRegistry {
178
185
  const { LlamaCppProvider } = await import("../providers/llamaCpp.js");
179
186
  return new LlamaCppProvider(modelName, sdk, undefined, llamaCppCreds);
180
187
  }, 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
188
  // Register Cohere provider
194
189
  ProviderFactory.registerProvider(AIProviderName.COHERE, async (modelName, _providerName, sdk, _region, credentials) => {
195
190
  const cohereCreds = credentials;
196
191
  const { CohereProvider } = await import("../providers/cohere.js");
197
192
  return new CohereProvider(modelName, sdk, undefined, cohereCreds);
198
193
  }, 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
194
  // Register Voyage AI embeddings provider
225
195
  ProviderFactory.registerProvider(AIProviderName.VOYAGE, async (modelName, _providerName, sdk, _region, credentials) => {
226
196
  const voyageCreds = credentials;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Generic provider-name → handler registry shared by every media-generation
3
+ * ecosystem (TTS, STT, Realtime, Video, Music, Avatar). Each ecosystem's own
4
+ * processor class composes one instance of this class instead of hand-rolling
5
+ * its own `Map<string, THandler>` plus register/supports/get/list methods.
6
+ *
7
+ * Centralizes only the behavior that was byte-identical across all six
8
+ * hand-rolled registries: input validation, name normalization (lowercase),
9
+ * the overwrite-warning log line, and the four lookup/list/clear operations.
10
+ * Registration-outcome debug logging (whose exact phrasing differs per
11
+ * ecosystem — e.g. "Registered TTS handler..." vs "Registered video
12
+ * handler...") and any ecosystem-specific extra logging (e.g. TTS/STT's
13
+ * `supports()` diagnostics) stay in the owning processor's own wrapper
14
+ * methods; this class does not attempt to unify those.
15
+ */
16
+ export declare class HandlerRegistry<THandler> {
17
+ private readonly scopeName;
18
+ private readonly handlers;
19
+ /**
20
+ * @param scopeName Log-line prefix, e.g. "TTSProcessor" — matches the
21
+ * `[ClassName]` prefix each processor already uses in its own logs.
22
+ */
23
+ constructor(scopeName: string);
24
+ register(providerName: string, handler: THandler): void;
25
+ supports(providerName: string): boolean;
26
+ get(providerName: string): THandler | undefined;
27
+ list(): string[];
28
+ clear(): void;
29
+ }
@@ -0,0 +1,62 @@
1
+ import { logger } from "../utils/logger.js";
2
+ import { sanitizeForLog } from "../utils/logSanitize.js";
3
+ /**
4
+ * Generic provider-name → handler registry shared by every media-generation
5
+ * ecosystem (TTS, STT, Realtime, Video, Music, Avatar). Each ecosystem's own
6
+ * processor class composes one instance of this class instead of hand-rolling
7
+ * its own `Map<string, THandler>` plus register/supports/get/list methods.
8
+ *
9
+ * Centralizes only the behavior that was byte-identical across all six
10
+ * hand-rolled registries: input validation, name normalization (lowercase),
11
+ * the overwrite-warning log line, and the four lookup/list/clear operations.
12
+ * Registration-outcome debug logging (whose exact phrasing differs per
13
+ * ecosystem — e.g. "Registered TTS handler..." vs "Registered video
14
+ * handler...") and any ecosystem-specific extra logging (e.g. TTS/STT's
15
+ * `supports()` diagnostics) stay in the owning processor's own wrapper
16
+ * methods; this class does not attempt to unify those.
17
+ */
18
+ export class HandlerRegistry {
19
+ scopeName;
20
+ handlers = new Map();
21
+ /**
22
+ * @param scopeName Log-line prefix, e.g. "TTSProcessor" — matches the
23
+ * `[ClassName]` prefix each processor already uses in its own logs.
24
+ */
25
+ constructor(scopeName) {
26
+ this.scopeName = scopeName;
27
+ }
28
+ register(providerName, handler) {
29
+ if (!providerName) {
30
+ throw new Error("Provider name is required");
31
+ }
32
+ if (!handler) {
33
+ throw new Error("Handler is required");
34
+ }
35
+ const key = providerName.toLowerCase();
36
+ if (this.handlers.has(key)) {
37
+ // Every caller today passes a literal provider slug, but this class is
38
+ // generic over an arbitrary string — sanitize before logging it so a
39
+ // future caller building the key from untrusted input can't leak a
40
+ // bearer token/API key through this warning.
41
+ logger.warn(`[${this.scopeName}] Overwriting existing handler for provider: ${sanitizeForLog(key)}`);
42
+ }
43
+ this.handlers.set(key, handler);
44
+ }
45
+ supports(providerName) {
46
+ if (!providerName) {
47
+ return false;
48
+ }
49
+ return this.handlers.has(providerName.toLowerCase());
50
+ }
51
+ get(providerName) {
52
+ return this.handlers.get(providerName.toLowerCase());
53
+ }
54
+ list() {
55
+ return Array.from(this.handlers.keys());
56
+ }
57
+ clear() {
58
+ this.handlers.clear();
59
+ logger.debug(`[${this.scopeName}] Cleared all handlers`);
60
+ }
61
+ }
62
+ //# sourceMappingURL=handlerRegistry.js.map
@@ -1,7 +1,8 @@
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";
5
6
  /**
6
7
  * Provider Registry - registers all providers with the factory
7
8
  * This is where we migrate providers one by one to the new pattern
@@ -111,12 +112,18 @@ export class ProviderRegistry {
111
112
  return new HuggingFaceProvider(modelName, sdk, region, hfCreds);
112
113
  }, process.env.HUGGINGFACE_MODEL ||
113
114
  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));
115
+ // Register the config-driven OpenAI-compatible catalog providers
116
+ // (groq, xai, together-ai, fireworks, perplexity, mistral, cloudflare).
117
+ // To add a new zero-quirk OpenAI-compatible provider, add one entry to
118
+ // OPENAI_COMPAT_CATALOG (openaiCompatCatalog.ts) not a new block here.
119
+ for (const entry of OPENAI_COMPAT_CATALOG) {
120
+ ProviderFactory.registerProvider(entry.providerName, async (modelName, _providerName, sdk, _region, credentials) => {
121
+ const { ConfiguredOpenAICompatProvider } = await import("../providers/configuredOpenAICompat.js");
122
+ return new ConfiguredOpenAICompatProvider(entry, modelName, sdk, credentials);
123
+ }, entry.registryDefaultModelChecksEnvVar
124
+ ? process.env[entry.modelEnvVar] || entry.registryDefaultModel
125
+ : entry.registryDefaultModel, entry.aliases, PROVIDER_DESCRIPTORS_BY_NAME.get(entry.providerName));
126
+ }
120
127
  // Register Ollama provider
121
128
  ProviderFactory.registerProvider(AIProviderName.OLLAMA, async (modelName, _providerName, sdk, _region, credentials) => {
122
129
  const ollamaCreds = credentials;
@@ -178,49 +185,12 @@ export class ProviderRegistry {
178
185
  const { LlamaCppProvider } = await import("../providers/llamaCpp.js");
179
186
  return new LlamaCppProvider(modelName, sdk, undefined, llamaCppCreds);
180
187
  }, 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
188
  // Register Cohere provider
194
189
  ProviderFactory.registerProvider(AIProviderName.COHERE, async (modelName, _providerName, sdk, _region, credentials) => {
195
190
  const cohereCreds = credentials;
196
191
  const { CohereProvider } = await import("../providers/cohere.js");
197
192
  return new CohereProvider(modelName, sdk, undefined, cohereCreds);
198
193
  }, 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
194
  // Register Voyage AI embeddings provider
225
195
  ProviderFactory.registerProvider(AIProviderName.VOYAGE, async (modelName, _providerName, sdk, _region, credentials) => {
226
196
  const voyageCreds = credentials;
@@ -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
@@ -43,7 +43,7 @@ export declare class AvatarError extends NeuroLinkError {
43
43
  * Static processor managing the avatar handler registry.
44
44
  */
45
45
  export declare class AvatarProcessor {
46
- private static readonly handlers;
46
+ private static readonly registry;
47
47
  /**
48
48
  * Register an avatar handler for a specific provider.
49
49
  */
@@ -63,6 +63,10 @@ export declare class AvatarProcessor {
63
63
  * already-registered primary handler when backfilling its aliases.
64
64
  */
65
65
  static getHandler(providerName: string): AvatarHandler | undefined;
66
+ /**
67
+ * Clear all registered handlers (for testing).
68
+ */
69
+ static clearHandlers(): void;
66
70
  private static buildSpanAttributes;
67
71
  /**
68
72
  * Generate an avatar video via the registered handler.
@@ -13,6 +13,7 @@ import { ErrorCategory, ErrorSeverity } from "../constants/enums.js";
13
13
  import { SpanSerializer, SpanStatus, SpanType, getMetricsAggregator, } from "../observability/index.js";
14
14
  import { NeuroLinkError } from "./errorHandling.js";
15
15
  import { logger } from "./logger.js";
16
+ import { HandlerRegistry } from "../core/handlerRegistry.js";
16
17
  /**
17
18
  * Avatar-specific error codes.
18
19
  */
@@ -47,38 +48,26 @@ export class AvatarError extends NeuroLinkError {
47
48
  * Static processor managing the avatar handler registry.
48
49
  */
49
50
  export class AvatarProcessor {
50
- static handlers = new Map();
51
+ static registry = new HandlerRegistry("AvatarProcessor");
51
52
  /**
52
53
  * Register an avatar handler for a specific provider.
53
54
  */
54
55
  static registerHandler(providerName, handler) {
55
- if (!providerName) {
56
- throw new Error("Provider name is required");
57
- }
58
- if (!handler) {
59
- throw new Error("Handler is required");
60
- }
61
- const key = providerName.toLowerCase();
62
- if (this.handlers.has(key)) {
63
- logger.warn(`[AvatarProcessor] Overwriting existing handler for provider: ${key}`);
64
- }
65
- this.handlers.set(key, handler);
56
+ const key = providerName ? providerName.toLowerCase() : providerName;
57
+ this.registry.register(providerName, handler);
66
58
  logger.debug(`[AvatarProcessor] Registered avatar handler: ${key}`);
67
59
  }
68
60
  /**
69
61
  * Check if a provider has a registered avatar handler.
70
62
  */
71
63
  static supports(providerName) {
72
- if (!providerName) {
73
- return false;
74
- }
75
- return this.handlers.has(providerName.toLowerCase());
64
+ return this.registry.supports(providerName);
76
65
  }
77
66
  /**
78
67
  * List the names of all registered providers.
79
68
  */
80
69
  static listProviders() {
81
- return Array.from(this.handlers.keys());
70
+ return this.registry.list();
82
71
  }
83
72
  /**
84
73
  * Get a registered avatar handler by provider name.
@@ -87,7 +76,13 @@ export class AvatarProcessor {
87
76
  * already-registered primary handler when backfilling its aliases.
88
77
  */
89
78
  static getHandler(providerName) {
90
- return this.handlers.get(providerName.toLowerCase());
79
+ return this.registry.get(providerName);
80
+ }
81
+ /**
82
+ * Clear all registered handlers (for testing).
83
+ */
84
+ static clearHandlers() {
85
+ this.registry.clear();
91
86
  }
92
87
  static buildSpanAttributes(provider, options) {
93
88
  return {
@@ -42,7 +42,7 @@ export declare class MusicError extends NeuroLinkError {
42
42
  * Static processor managing the music handler registry.
43
43
  */
44
44
  export declare class MusicProcessor {
45
- private static readonly handlers;
45
+ private static readonly registry;
46
46
  /**
47
47
  * Register a music handler for a specific provider.
48
48
  */
@@ -62,6 +62,10 @@ export declare class MusicProcessor {
62
62
  * already-registered primary handler when backfilling its aliases.
63
63
  */
64
64
  static getHandler(providerName: string): MusicHandler | undefined;
65
+ /**
66
+ * Clear all registered handlers (for testing).
67
+ */
68
+ static clearHandlers(): void;
65
69
  private static buildSpanAttributes;
66
70
  /**
67
71
  * Generate a music track via the registered handler.
@@ -13,6 +13,7 @@ import { ErrorCategory, ErrorSeverity } from "../constants/enums.js";
13
13
  import { SpanSerializer, SpanStatus, SpanType, getMetricsAggregator, } from "../observability/index.js";
14
14
  import { NeuroLinkError } from "./errorHandling.js";
15
15
  import { logger } from "./logger.js";
16
+ import { HandlerRegistry } from "../core/handlerRegistry.js";
16
17
  /**
17
18
  * Music-specific error codes.
18
19
  */
@@ -46,38 +47,26 @@ export class MusicError extends NeuroLinkError {
46
47
  * Static processor managing the music handler registry.
47
48
  */
48
49
  export class MusicProcessor {
49
- static handlers = new Map();
50
+ static registry = new HandlerRegistry("MusicProcessor");
50
51
  /**
51
52
  * Register a music handler for a specific provider.
52
53
  */
53
54
  static registerHandler(providerName, handler) {
54
- if (!providerName) {
55
- throw new Error("Provider name is required");
56
- }
57
- if (!handler) {
58
- throw new Error("Handler is required");
59
- }
60
- const key = providerName.toLowerCase();
61
- if (this.handlers.has(key)) {
62
- logger.warn(`[MusicProcessor] Overwriting existing handler for provider: ${key}`);
63
- }
64
- this.handlers.set(key, handler);
55
+ const key = providerName ? providerName.toLowerCase() : providerName;
56
+ this.registry.register(providerName, handler);
65
57
  logger.debug(`[MusicProcessor] Registered music handler: ${key}`);
66
58
  }
67
59
  /**
68
60
  * Check if a provider has a registered music handler.
69
61
  */
70
62
  static supports(providerName) {
71
- if (!providerName) {
72
- return false;
73
- }
74
- return this.handlers.has(providerName.toLowerCase());
63
+ return this.registry.supports(providerName);
75
64
  }
76
65
  /**
77
66
  * List the names of all registered providers.
78
67
  */
79
68
  static listProviders() {
80
- return Array.from(this.handlers.keys());
69
+ return this.registry.list();
81
70
  }
82
71
  /**
83
72
  * Get a registered music handler by provider name.
@@ -86,7 +75,13 @@ export class MusicProcessor {
86
75
  * already-registered primary handler when backfilling its aliases.
87
76
  */
88
77
  static getHandler(providerName) {
89
- return this.handlers.get(providerName.toLowerCase());
78
+ return this.registry.get(providerName);
79
+ }
80
+ /**
81
+ * Clear all registered handlers (for testing).
82
+ */
83
+ static clearHandlers() {
84
+ this.registry.clear();
90
85
  }
91
86
  static buildSpanAttributes(provider, options) {
92
87
  return {