@juspay/neurolink 9.62.0 → 9.63.0

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.
@@ -47,6 +47,11 @@ const BLOCKED_UPSTREAM_HEADERS = new Set([
47
47
  let primaryAccountIndex = 0;
48
48
  /** Track account count so we can reset primaryAccountIndex when it changes. */
49
49
  let lastKnownAccountCount = 0;
50
+ /** Stable account key (e.g. "anthropic:user@example.com") of the configured
51
+ * home/primary account. Set once at proxy boot from routing.primaryAccount.
52
+ * When undefined, home semantics fall back to enabledAccounts[0] (insertion
53
+ * order) — preserves pre-existing behavior. */
54
+ let configuredPrimaryAccountKey;
50
55
  const MAX_AUTH_RETRIES = 5;
51
56
  const MAX_CONSECUTIVE_REFRESH_FAILURES = 15;
52
57
  const MAX_TRANSIENT_SAME_ACCOUNT_RETRIES = 2;
@@ -82,22 +87,40 @@ function advancePrimaryIfCurrent(accountKey, enabledCount, primaryAccountKey) {
82
87
  }
83
88
  primaryAccountIndex = (primaryAccountIndex + 1) % enabledCount;
84
89
  }
85
- /** If the configured home primary (index 0) is no longer cooling, reset
86
- * primaryAccountIndex back to 0 so traffic returns to the preferred account
87
- * once its rate limit window expires. Called at the start of each request. */
90
+ /** Resolve the configured primary's stable key to its current index in the
91
+ * request's enabledAccounts list. Returns 0 (insertion-order fallback) when
92
+ * no key is configured or the key cannot be matched (account disabled/
93
+ * removed). The resolution is per-request because enabledAccounts membership
94
+ * can shift between requests. */
95
+ function resolveHomeIndex(enabledAccounts) {
96
+ if (!configuredPrimaryAccountKey) {
97
+ return 0;
98
+ }
99
+ const idx = enabledAccounts.findIndex((a) => a.key === configuredPrimaryAccountKey);
100
+ return idx >= 0 ? idx : 0;
101
+ }
102
+ /** If the configured home primary is no longer cooling, reset
103
+ * primaryAccountIndex back to its index so traffic returns to the preferred
104
+ * account once its rate limit window expires. Called at the start of each
105
+ * request. Home is resolved fresh per call via resolveHomeIndex. */
88
106
  function maybeResetPrimaryToHome(enabledAccounts) {
89
- if (enabledAccounts.length <= 1 || primaryAccountIndex === 0) {
107
+ if (enabledAccounts.length <= 1) {
108
+ return;
109
+ }
110
+ const homeIndex = resolveHomeIndex(enabledAccounts);
111
+ if (primaryAccountIndex === homeIndex) {
90
112
  return;
91
113
  }
92
- const homeState = accountRuntimeState.get(enabledAccounts[0].key);
114
+ const homeAccount = enabledAccounts[homeIndex];
115
+ const homeState = accountRuntimeState.get(homeAccount.key);
93
116
  if (!homeState ||
94
117
  !homeState.coolingUntil ||
95
118
  Date.now() >= homeState.coolingUntil) {
96
119
  // Home account is no longer cooling — reset to it
97
- primaryAccountIndex = 0;
120
+ primaryAccountIndex = homeIndex;
98
121
  if (homeState?.coolingUntil) {
99
122
  homeState.coolingUntil = undefined;
100
- logger.always(`[proxy] home primary account=${enabledAccounts[0].label} cooling expired, resetting primaryAccountIndex to 0`);
123
+ logger.always(`[proxy] home primary account=${homeAccount.label} cooling expired, resetting primaryAccountIndex to ${homeIndex}`);
101
124
  }
102
125
  }
103
126
  }
@@ -1239,7 +1262,7 @@ async function loadClaudeProxyAccounts(args) {
1239
1262
  const orderedAccounts = [...enabledAccounts];
1240
1263
  if (accountStrategy === "round-robin" &&
1241
1264
  orderedAccounts.length !== lastKnownAccountCount) {
1242
- primaryAccountIndex = 0;
1265
+ primaryAccountIndex = resolveHomeIndex(orderedAccounts);
1243
1266
  lastKnownAccountCount = orderedAccounts.length;
1244
1267
  }
1245
1268
  if (orderedAccounts.length > 1) {
@@ -3232,7 +3255,8 @@ async function handleAnthropicRoutedClaudeRequest(args) {
3232
3255
  * @param basePath - Base path prefix (default: "" since Claude API uses /v1/...).
3233
3256
  * @returns RouteGroup with Claude-compatible endpoints.
3234
3257
  */
3235
- export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrategy = "fill-first", passthroughMode = false) {
3258
+ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrategy = "fill-first", passthroughMode = false, primaryAccountKey) {
3259
+ configuredPrimaryAccountKey = primaryAccountKey;
3236
3260
  return {
3237
3261
  prefix: `${basePath}/v1`,
3238
3262
  routes: [
@@ -3691,4 +3715,32 @@ export function isTransientHttpFailure(status, errBody) {
3691
3715
  normalized.includes("cloudflare") ||
3692
3716
  normalized.includes("internal server error"));
3693
3717
  }
3718
+ // ---------------------------------------------------------------------------
3719
+ // Test hooks (not part of the public SDK API). Only consumed by the proxy
3720
+ // continuous-test-suite to drive the in-process resolver/reset logic without
3721
+ // spinning up a full proxy. Keep this surface small.
3722
+ // ---------------------------------------------------------------------------
3723
+ export const __testHooks = {
3724
+ resolveHomeIndex,
3725
+ maybeResetPrimaryToHome,
3726
+ setConfiguredPrimaryAccountKey: (key) => {
3727
+ configuredPrimaryAccountKey = key;
3728
+ },
3729
+ getConfiguredPrimaryAccountKey: () => configuredPrimaryAccountKey,
3730
+ setPrimaryAccountIndex: (index) => {
3731
+ primaryAccountIndex = index;
3732
+ },
3733
+ getPrimaryAccountIndex: () => primaryAccountIndex,
3734
+ setAccountRuntimeState: (key, state) => {
3735
+ const existing = accountRuntimeState.get(key) ?? getOrCreateRuntimeState(key);
3736
+ Object.assign(existing, state);
3737
+ accountRuntimeState.set(key, existing);
3738
+ },
3739
+ resetAllRuntimeState: () => {
3740
+ accountRuntimeState.clear();
3741
+ primaryAccountIndex = 0;
3742
+ lastKnownAccountCount = 0;
3743
+ configuredPrimaryAccountKey = undefined;
3744
+ },
3745
+ };
3694
3746
  //# sourceMappingURL=claudeProxyRoutes.js.map
@@ -859,6 +859,10 @@ export type AuthCommandArgs = BaseCommandArgs & {
859
859
  label?: string;
860
860
  account?: string;
861
861
  force?: boolean;
862
+ /** Path to the proxy config YAML, used by set-/get-/clear-primary */
863
+ config?: string;
864
+ /** Email passed to `auth set-primary <email>` */
865
+ email?: string;
862
866
  /** Yargs positional arguments */
863
867
  _?: (string | number)[];
864
868
  };
@@ -981,13 +981,35 @@ export type UpdateState = {
981
981
  lastUpdateAt: string | null;
982
982
  lastUpdateVersion: string | null;
983
983
  };
984
- /** Shape of the dynamically-imported js-yaml module. */
984
+ /** Shape of the dynamically-imported js-yaml module. `dump` is optional —
985
+ * read-only consumers (proxy config loader) only need `load`; writers
986
+ * (CLI primary-account commands) check `dump` before calling. */
985
987
  export type YamlModule = {
986
988
  load(content: string): unknown;
989
+ dump?: (obj: unknown, opts?: Record<string, unknown>) => string;
987
990
  default?: {
988
991
  load(content: string): unknown;
992
+ dump?: (obj: unknown, opts?: Record<string, unknown>) => string;
989
993
  };
990
994
  };
995
+ /** Snapshot of a parsed proxy config file used by CLI primary-account
996
+ * read/edit/write helpers. Tracks the original format and whether comments
997
+ * were present (so the CLI can warn that comments will not round-trip). */
998
+ export type CliProxyConfigDoc = {
999
+ data: Record<string, unknown>;
1000
+ format: "yaml" | "json";
1001
+ hadComments: boolean;
1002
+ };
1003
+ /** Primary-account info exposed by the proxy `/status` endpoint.
1004
+ * `source` is "configured" when the operator's `routing.primaryAccount` is
1005
+ * authenticated and enabled, otherwise "fallback" — either no primary set
1006
+ * or the configured one is missing/disabled. */
1007
+ export type ProxyStatusPrimaryAccount = {
1008
+ configured: string | null;
1009
+ key: string | null;
1010
+ label: string | null;
1011
+ source: "configured" | "fallback";
1012
+ };
991
1013
  /** Parsed fields captured from a Claude Code client request body. */
992
1014
  export type ClaudeSnapshotBody = {
993
1015
  metadataUserId?: string;
@@ -909,6 +909,11 @@ export type ProxyRoutingConfig = {
909
909
  modelMappings: ModelMapping[];
910
910
  fallbackChain: FallbackEntry[];
911
911
  passthroughModels?: string[];
912
+ /** Email/label of the Anthropic account that should be tried first
913
+ * ("home"). When absent, falls back to insertion-order index 0.
914
+ * Resolved per-request to a stable key (anthropic:<email>); does not
915
+ * encode an index. */
916
+ primaryAccount?: string;
912
917
  };
913
918
  /** Cloaking plugin config */
914
919
  export type CloakingConfig = {
@@ -336,6 +336,20 @@ function parseRoutingConfig(raw) {
336
336
  if (Array.isArray(rawPassthrough)) {
337
337
  result.passthroughModels = rawPassthrough.map(String);
338
338
  }
339
+ // Primary account (accept kebab-case or camelCase). Email or label of the
340
+ // Anthropic account that should be tried first ("home"). Resolved to a
341
+ // stable key (anthropic:<email>) at proxy boot; absence preserves the
342
+ // pre-existing insertion-order behavior.
343
+ const rawPrimary = (raw["primary-account"] ?? raw.primaryAccount);
344
+ if (rawPrimary !== undefined) {
345
+ if (typeof rawPrimary === "string" && rawPrimary.trim() !== "") {
346
+ result.primaryAccount = rawPrimary.trim();
347
+ }
348
+ else {
349
+ logger.warn(`[proxy-config] Ignoring routing.primaryAccount: expected non-empty ` +
350
+ `string, got ${typeof rawPrimary}`);
351
+ }
352
+ }
339
353
  return result;
340
354
  }
341
355
  // ---------------------------------------------------------------------------
@@ -10,7 +10,18 @@
10
10
  * Without a router, models are passed through to the Anthropic provider.
11
11
  */
12
12
  import type { ModelRouter } from "../../proxy/modelRouter.js";
13
- import type { ParsedClaudeError, ParsedClaudeRequest, RouteGroup } from "../../types/index.js";
13
+ import type { ParsedClaudeError, ParsedClaudeRequest, ProxyPassthroughAccount, RouteGroup, RuntimeAccountState } from "../../types/index.js";
14
+ /** Resolve the configured primary's stable key to its current index in the
15
+ * request's enabledAccounts list. Returns 0 (insertion-order fallback) when
16
+ * no key is configured or the key cannot be matched (account disabled/
17
+ * removed). The resolution is per-request because enabledAccounts membership
18
+ * can shift between requests. */
19
+ declare function resolveHomeIndex(enabledAccounts: ProxyPassthroughAccount[]): number;
20
+ /** If the configured home primary is no longer cooling, reset
21
+ * primaryAccountIndex back to its index so traffic returns to the preferred
22
+ * account once its rate limit window expires. Called at the start of each
23
+ * request. Home is resolved fresh per call via resolveHomeIndex. */
24
+ declare function maybeResetPrimaryToHome(enabledAccounts: ProxyPassthroughAccount[]): void;
14
25
  /**
15
26
  * Create Claude-compatible proxy routes.
16
27
  *
@@ -21,7 +32,7 @@ import type { ParsedClaudeError, ParsedClaudeRequest, RouteGroup } from "../../t
21
32
  * @param basePath - Base path prefix (default: "" since Claude API uses /v1/...).
22
33
  * @returns RouteGroup with Claude-compatible endpoints.
23
34
  */
24
- export declare function createClaudeProxyRoutes(modelRouter?: ModelRouter, basePath?: string, accountStrategy?: "round-robin" | "fill-first", passthroughMode?: boolean): RouteGroup;
35
+ export declare function createClaudeProxyRoutes(modelRouter?: ModelRouter, basePath?: string, accountStrategy?: "round-robin" | "fill-first", passthroughMode?: boolean, primaryAccountKey?: string): RouteGroup;
25
36
  export declare function getTransientSameAccountRetryDelayMs(retryNumber: number): number;
26
37
  /**
27
38
  * Parse a Claude error payload when available.
@@ -42,3 +53,14 @@ export declare function buildProxyFallbackOptions(parsed: ParsedClaudeRequest, o
42
53
  * carry transient HTML responses (e.g. 520 pages) inside `error.message`.
43
54
  */
44
55
  export declare function isTransientHttpFailure(status: number, errBody: string): boolean;
56
+ export declare const __testHooks: {
57
+ resolveHomeIndex: typeof resolveHomeIndex;
58
+ maybeResetPrimaryToHome: typeof maybeResetPrimaryToHome;
59
+ setConfiguredPrimaryAccountKey: (key: string | undefined) => void;
60
+ getConfiguredPrimaryAccountKey: () => string | undefined;
61
+ setPrimaryAccountIndex: (index: number) => void;
62
+ getPrimaryAccountIndex: () => number;
63
+ setAccountRuntimeState: (key: string, state: Partial<RuntimeAccountState>) => void;
64
+ resetAllRuntimeState: () => void;
65
+ };
66
+ export {};
@@ -47,6 +47,11 @@ const BLOCKED_UPSTREAM_HEADERS = new Set([
47
47
  let primaryAccountIndex = 0;
48
48
  /** Track account count so we can reset primaryAccountIndex when it changes. */
49
49
  let lastKnownAccountCount = 0;
50
+ /** Stable account key (e.g. "anthropic:user@example.com") of the configured
51
+ * home/primary account. Set once at proxy boot from routing.primaryAccount.
52
+ * When undefined, home semantics fall back to enabledAccounts[0] (insertion
53
+ * order) — preserves pre-existing behavior. */
54
+ let configuredPrimaryAccountKey;
50
55
  const MAX_AUTH_RETRIES = 5;
51
56
  const MAX_CONSECUTIVE_REFRESH_FAILURES = 15;
52
57
  const MAX_TRANSIENT_SAME_ACCOUNT_RETRIES = 2;
@@ -82,22 +87,40 @@ function advancePrimaryIfCurrent(accountKey, enabledCount, primaryAccountKey) {
82
87
  }
83
88
  primaryAccountIndex = (primaryAccountIndex + 1) % enabledCount;
84
89
  }
85
- /** If the configured home primary (index 0) is no longer cooling, reset
86
- * primaryAccountIndex back to 0 so traffic returns to the preferred account
87
- * once its rate limit window expires. Called at the start of each request. */
90
+ /** Resolve the configured primary's stable key to its current index in the
91
+ * request's enabledAccounts list. Returns 0 (insertion-order fallback) when
92
+ * no key is configured or the key cannot be matched (account disabled/
93
+ * removed). The resolution is per-request because enabledAccounts membership
94
+ * can shift between requests. */
95
+ function resolveHomeIndex(enabledAccounts) {
96
+ if (!configuredPrimaryAccountKey) {
97
+ return 0;
98
+ }
99
+ const idx = enabledAccounts.findIndex((a) => a.key === configuredPrimaryAccountKey);
100
+ return idx >= 0 ? idx : 0;
101
+ }
102
+ /** If the configured home primary is no longer cooling, reset
103
+ * primaryAccountIndex back to its index so traffic returns to the preferred
104
+ * account once its rate limit window expires. Called at the start of each
105
+ * request. Home is resolved fresh per call via resolveHomeIndex. */
88
106
  function maybeResetPrimaryToHome(enabledAccounts) {
89
- if (enabledAccounts.length <= 1 || primaryAccountIndex === 0) {
107
+ if (enabledAccounts.length <= 1) {
108
+ return;
109
+ }
110
+ const homeIndex = resolveHomeIndex(enabledAccounts);
111
+ if (primaryAccountIndex === homeIndex) {
90
112
  return;
91
113
  }
92
- const homeState = accountRuntimeState.get(enabledAccounts[0].key);
114
+ const homeAccount = enabledAccounts[homeIndex];
115
+ const homeState = accountRuntimeState.get(homeAccount.key);
93
116
  if (!homeState ||
94
117
  !homeState.coolingUntil ||
95
118
  Date.now() >= homeState.coolingUntil) {
96
119
  // Home account is no longer cooling — reset to it
97
- primaryAccountIndex = 0;
120
+ primaryAccountIndex = homeIndex;
98
121
  if (homeState?.coolingUntil) {
99
122
  homeState.coolingUntil = undefined;
100
- logger.always(`[proxy] home primary account=${enabledAccounts[0].label} cooling expired, resetting primaryAccountIndex to 0`);
123
+ logger.always(`[proxy] home primary account=${homeAccount.label} cooling expired, resetting primaryAccountIndex to ${homeIndex}`);
101
124
  }
102
125
  }
103
126
  }
@@ -1239,7 +1262,7 @@ async function loadClaudeProxyAccounts(args) {
1239
1262
  const orderedAccounts = [...enabledAccounts];
1240
1263
  if (accountStrategy === "round-robin" &&
1241
1264
  orderedAccounts.length !== lastKnownAccountCount) {
1242
- primaryAccountIndex = 0;
1265
+ primaryAccountIndex = resolveHomeIndex(orderedAccounts);
1243
1266
  lastKnownAccountCount = orderedAccounts.length;
1244
1267
  }
1245
1268
  if (orderedAccounts.length > 1) {
@@ -3232,7 +3255,8 @@ async function handleAnthropicRoutedClaudeRequest(args) {
3232
3255
  * @param basePath - Base path prefix (default: "" since Claude API uses /v1/...).
3233
3256
  * @returns RouteGroup with Claude-compatible endpoints.
3234
3257
  */
3235
- export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrategy = "fill-first", passthroughMode = false) {
3258
+ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrategy = "fill-first", passthroughMode = false, primaryAccountKey) {
3259
+ configuredPrimaryAccountKey = primaryAccountKey;
3236
3260
  return {
3237
3261
  prefix: `${basePath}/v1`,
3238
3262
  routes: [
@@ -3691,3 +3715,31 @@ export function isTransientHttpFailure(status, errBody) {
3691
3715
  normalized.includes("cloudflare") ||
3692
3716
  normalized.includes("internal server error"));
3693
3717
  }
3718
+ // ---------------------------------------------------------------------------
3719
+ // Test hooks (not part of the public SDK API). Only consumed by the proxy
3720
+ // continuous-test-suite to drive the in-process resolver/reset logic without
3721
+ // spinning up a full proxy. Keep this surface small.
3722
+ // ---------------------------------------------------------------------------
3723
+ export const __testHooks = {
3724
+ resolveHomeIndex,
3725
+ maybeResetPrimaryToHome,
3726
+ setConfiguredPrimaryAccountKey: (key) => {
3727
+ configuredPrimaryAccountKey = key;
3728
+ },
3729
+ getConfiguredPrimaryAccountKey: () => configuredPrimaryAccountKey,
3730
+ setPrimaryAccountIndex: (index) => {
3731
+ primaryAccountIndex = index;
3732
+ },
3733
+ getPrimaryAccountIndex: () => primaryAccountIndex,
3734
+ setAccountRuntimeState: (key, state) => {
3735
+ const existing = accountRuntimeState.get(key) ?? getOrCreateRuntimeState(key);
3736
+ Object.assign(existing, state);
3737
+ accountRuntimeState.set(key, existing);
3738
+ },
3739
+ resetAllRuntimeState: () => {
3740
+ accountRuntimeState.clear();
3741
+ primaryAccountIndex = 0;
3742
+ lastKnownAccountCount = 0;
3743
+ configuredPrimaryAccountKey = undefined;
3744
+ },
3745
+ };
@@ -859,6 +859,10 @@ export type AuthCommandArgs = BaseCommandArgs & {
859
859
  label?: string;
860
860
  account?: string;
861
861
  force?: boolean;
862
+ /** Path to the proxy config YAML, used by set-/get-/clear-primary */
863
+ config?: string;
864
+ /** Email passed to `auth set-primary <email>` */
865
+ email?: string;
862
866
  /** Yargs positional arguments */
863
867
  _?: (string | number)[];
864
868
  };
@@ -981,13 +981,35 @@ export type UpdateState = {
981
981
  lastUpdateAt: string | null;
982
982
  lastUpdateVersion: string | null;
983
983
  };
984
- /** Shape of the dynamically-imported js-yaml module. */
984
+ /** Shape of the dynamically-imported js-yaml module. `dump` is optional —
985
+ * read-only consumers (proxy config loader) only need `load`; writers
986
+ * (CLI primary-account commands) check `dump` before calling. */
985
987
  export type YamlModule = {
986
988
  load(content: string): unknown;
989
+ dump?: (obj: unknown, opts?: Record<string, unknown>) => string;
987
990
  default?: {
988
991
  load(content: string): unknown;
992
+ dump?: (obj: unknown, opts?: Record<string, unknown>) => string;
989
993
  };
990
994
  };
995
+ /** Snapshot of a parsed proxy config file used by CLI primary-account
996
+ * read/edit/write helpers. Tracks the original format and whether comments
997
+ * were present (so the CLI can warn that comments will not round-trip). */
998
+ export type CliProxyConfigDoc = {
999
+ data: Record<string, unknown>;
1000
+ format: "yaml" | "json";
1001
+ hadComments: boolean;
1002
+ };
1003
+ /** Primary-account info exposed by the proxy `/status` endpoint.
1004
+ * `source` is "configured" when the operator's `routing.primaryAccount` is
1005
+ * authenticated and enabled, otherwise "fallback" — either no primary set
1006
+ * or the configured one is missing/disabled. */
1007
+ export type ProxyStatusPrimaryAccount = {
1008
+ configured: string | null;
1009
+ key: string | null;
1010
+ label: string | null;
1011
+ source: "configured" | "fallback";
1012
+ };
991
1013
  /** Parsed fields captured from a Claude Code client request body. */
992
1014
  export type ClaudeSnapshotBody = {
993
1015
  metadataUserId?: string;
@@ -909,6 +909,11 @@ export type ProxyRoutingConfig = {
909
909
  modelMappings: ModelMapping[];
910
910
  fallbackChain: FallbackEntry[];
911
911
  passthroughModels?: string[];
912
+ /** Email/label of the Anthropic account that should be tried first
913
+ * ("home"). When absent, falls back to insertion-order index 0.
914
+ * Resolved per-request to a stable key (anthropic:<email>); does not
915
+ * encode an index. */
916
+ primaryAccount?: string;
912
917
  };
913
918
  /** Cloaking plugin config */
914
919
  export type CloakingConfig = {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.62.0",
3
+ "version": "9.63.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
- "description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 13 providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
5
+ "description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
6
6
  "author": {
7
7
  "name": "Juspay Technologies",
8
8
  "email": "support@juspay.in",
@@ -378,6 +378,25 @@
378
378
  "google",
379
379
  "bedrock",
380
380
  "vertex",
381
+ "azure",
382
+ "mistral",
383
+ "ollama",
384
+ "huggingface",
385
+ "litellm",
386
+ "openrouter",
387
+ "deepseek",
388
+ "nvidia-nim",
389
+ "lm-studio",
390
+ "llama-cpp",
391
+ "tts",
392
+ "stt",
393
+ "voice",
394
+ "realtime",
395
+ "elevenlabs",
396
+ "deepgram",
397
+ "whisper",
398
+ "azure-speech",
399
+ "rag",
381
400
  "streaming",
382
401
  "tools",
383
402
  "neurolink",