modelmix 5.1.6 → 5.1.8

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.
@@ -48,14 +48,14 @@ function createAnthropicProviders({ ModelMix, MixCustom, log }) {
48
48
  }
49
49
 
50
50
  getDefaultConfig(customConfig) {
51
-
52
- if (!process.env.ANTHROPIC_API_KEY) {
51
+ const apiKey = customConfig.apiKey || process.env.ANTHROPIC_API_KEY;
52
+ if (!apiKey) {
53
53
  throw new Error('Anthropic API key not found. Please provide it in config or set ANTHROPIC_API_KEY environment variable.');
54
54
  }
55
55
 
56
56
  return super.getDefaultConfig({
57
57
  url: 'https://api.anthropic.com/v1/messages',
58
- apiKey: process.env.ANTHROPIC_API_KEY,
58
+ apiKey,
59
59
  ...customConfig
60
60
  });
61
61
  }
@@ -274,14 +274,14 @@ function createBaseProviders({ ModelMix }) {
274
274
  }
275
275
 
276
276
  getDefaultConfig(customConfig) {
277
-
278
- if (!process.env.OPENAI_API_KEY) {
277
+ const apiKey = customConfig.apiKey || process.env.OPENAI_API_KEY;
278
+ if (!apiKey) {
279
279
  throw new Error('OpenAI API key not found. Please provide it in config or set OPENAI_API_KEY environment variable.');
280
280
  }
281
281
 
282
282
  return super.getDefaultConfig({
283
283
  url: 'https://api.openai.com/v1/chat/completions',
284
- apiKey: process.env.OPENAI_API_KEY,
284
+ apiKey,
285
285
  ...customConfig
286
286
  });
287
287
  }
@@ -4,9 +4,10 @@ const { configForDebug } = require('../provider-debug');
4
4
  function createGoogleProviders({ ModelMix, MixCustom }) {
5
5
  class MixGoogle extends MixCustom {
6
6
  getDefaultConfig(customConfig) {
7
+ const apiKey = customConfig.apiKey || process.env.GEMINI_API_KEY;
7
8
  return super.getDefaultConfig({
8
9
  url: 'https://generativelanguage.googleapis.com/v1beta/models',
9
- apiKey: process.env.GEMINI_API_KEY,
10
+ apiKey,
10
11
  ...customConfig
11
12
  });
12
13
  }
@@ -6,14 +6,14 @@ const {
6
6
  function createCompatibleProviders({ MixCustom, MixOpenAI }) {
7
7
  class MixMiniMax extends MixOpenAI {
8
8
  getDefaultConfig(customConfig) {
9
-
10
- if (!process.env.MINIMAX_API_KEY) {
9
+ const apiKey = customConfig.apiKey || process.env.MINIMAX_API_KEY;
10
+ if (!apiKey) {
11
11
  throw new Error('MiniMax API key not found. Please provide it in config or set MINIMAX_API_KEY environment variable.');
12
12
  }
13
13
 
14
14
  return MixCustom.prototype.getDefaultConfig.call(this, {
15
15
  url: 'https://api.minimax.io/v1/chat/completions',
16
- apiKey: process.env.MINIMAX_API_KEY,
16
+ apiKey,
17
17
  ...customConfig
18
18
  });
19
19
  }
@@ -29,13 +29,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
29
29
 
30
30
  class MixMiMo extends MixOpenAI {
31
31
  getDefaultConfig(customConfig) {
32
- if (!process.env.MIMO_API_KEY) {
32
+ const apiKey = customConfig.apiKey || process.env.MIMO_API_KEY;
33
+ if (!apiKey) {
33
34
  throw new Error('MiMo API key not found. Please provide it in config or set MIMO_API_KEY environment variable.');
34
35
  }
35
36
 
36
37
  return MixCustom.prototype.getDefaultConfig.call(this, {
37
38
  url: 'https://api.xiaomimimo.com/v1/chat/completions',
38
- apiKey: process.env.MIMO_API_KEY,
39
+ apiKey,
39
40
  ...customConfig
40
41
  });
41
42
  }
@@ -52,14 +53,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
52
53
 
53
54
  class MixPerplexity extends MixCustom {
54
55
  getDefaultConfig(customConfig) {
55
-
56
- if (!process.env.PPLX_API_KEY) {
56
+ const apiKey = customConfig.apiKey || process.env.PPLX_API_KEY;
57
+ if (!apiKey) {
57
58
  throw new Error('Perplexity API key not found. Please provide it in config or set PPLX_API_KEY environment variable.');
58
59
  }
59
60
 
60
61
  return super.getDefaultConfig({
61
62
  url: 'https://api.perplexity.ai/chat/completions',
62
- apiKey: process.env.PPLX_API_KEY,
63
+ apiKey,
63
64
  ...customConfig
64
65
  });
65
66
  }
@@ -132,14 +133,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
132
133
 
133
134
  class MixGrok extends MixOpenAI {
134
135
  getDefaultConfig(customConfig) {
135
-
136
- if (!process.env.XAI_API_KEY) {
136
+ const apiKey = customConfig.apiKey || process.env.XAI_API_KEY;
137
+ if (!apiKey) {
137
138
  throw new Error('Grok API key not found. Please provide it in config or set XAI_API_KEY environment variable.');
138
139
  }
139
140
 
140
141
  return super.getDefaultConfig({
141
142
  url: 'https://api.x.ai/v1/chat/completions',
142
- apiKey: process.env.XAI_API_KEY,
143
+ apiKey,
143
144
  ...customConfig
144
145
  });
145
146
  }
@@ -154,14 +155,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
154
155
 
155
156
  class MixLambda extends MixCustom {
156
157
  getDefaultConfig(customConfig) {
157
-
158
- if (!process.env.LAMBDA_API_KEY) {
158
+ const apiKey = customConfig.apiKey || process.env.LAMBDA_API_KEY;
159
+ if (!apiKey) {
159
160
  throw new Error('Lambda API key not found. Please provide it in config or set LAMBDA_API_KEY environment variable.');
160
161
  }
161
162
 
162
163
  return super.getDefaultConfig({
163
164
  url: 'https://api.lambda.ai/v1/chat/completions',
164
- apiKey: process.env.LAMBDA_API_KEY,
165
+ apiKey,
165
166
  ...customConfig
166
167
  });
167
168
  }
@@ -234,14 +235,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
234
235
 
235
236
  class MixGroq extends MixCustom {
236
237
  getDefaultConfig(customConfig) {
237
-
238
- if (!process.env.GROQ_API_KEY) {
238
+ const apiKey = customConfig.apiKey || process.env.GROQ_API_KEY;
239
+ if (!apiKey) {
239
240
  throw new Error('Groq API key not found. Please provide it in config or set GROQ_API_KEY environment variable.');
240
241
  }
241
242
 
242
243
  return super.getDefaultConfig({
243
244
  url: 'https://api.groq.com/openai/v1/chat/completions',
244
- apiKey: process.env.GROQ_API_KEY,
245
+ apiKey,
245
246
  ...customConfig
246
247
  });
247
248
  }
@@ -249,14 +250,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
249
250
 
250
251
  class MixTogether extends MixCustom {
251
252
  getDefaultConfig(customConfig) {
252
-
253
- if (!process.env.TOGETHER_API_KEY) {
253
+ const apiKey = customConfig.apiKey || process.env.TOGETHER_API_KEY;
254
+ if (!apiKey) {
254
255
  throw new Error('Together API key not found. Please provide it in config or set TOGETHER_API_KEY environment variable.');
255
256
  }
256
257
 
257
258
  return super.getDefaultConfig({
258
259
  url: 'https://api.together.xyz/v1/chat/completions',
259
- apiKey: process.env.TOGETHER_API_KEY,
260
+ apiKey,
260
261
  ...customConfig
261
262
  });
262
263
  }
@@ -271,14 +272,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
271
272
 
272
273
  class MixCerebras extends MixCustom {
273
274
  getDefaultConfig(customConfig) {
274
-
275
- if (!process.env.CEREBRAS_API_KEY) {
276
- throw new Error('Together API key not found. Please provide it in config or set CEREBRAS_API_KEY environment variable.');
275
+ const apiKey = customConfig.apiKey || process.env.CEREBRAS_API_KEY;
276
+ if (!apiKey) {
277
+ throw new Error('Cerebras API key not found. Please provide it in config or set CEREBRAS_API_KEY environment variable.');
277
278
  }
278
279
 
279
280
  return super.getDefaultConfig({
280
281
  url: 'https://api.cerebras.ai/v1/chat/completions',
281
- apiKey: process.env.CEREBRAS_API_KEY,
282
+ apiKey,
282
283
  ...customConfig
283
284
  });
284
285
  }
@@ -291,14 +292,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
291
292
 
292
293
  class MixFireworks extends MixCustom {
293
294
  getDefaultConfig(customConfig) {
294
-
295
- if (!process.env.FIREWORKS_API_KEY) {
295
+ const apiKey = customConfig.apiKey || process.env.FIREWORKS_API_KEY;
296
+ if (!apiKey) {
296
297
  throw new Error('Fireworks API key not found. Please provide it in config or set FIREWORKS_API_KEY environment variable.');
297
298
  }
298
299
 
299
300
  return super.getDefaultConfig({
300
301
  url: 'https://api.fireworks.ai/inference/v1/chat/completions',
301
- apiKey: process.env.FIREWORKS_API_KEY,
302
+ apiKey,
302
303
  ...customConfig
303
304
  });
304
305
  }
@@ -306,14 +307,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
306
307
 
307
308
  class MixNVIDIA extends MixCustom {
308
309
  getDefaultConfig(customConfig) {
309
-
310
- if (!process.env.NVIDIA_API_KEY) {
310
+ const apiKey = customConfig.apiKey || process.env.NVIDIA_API_KEY;
311
+ if (!apiKey) {
311
312
  throw new Error('NVIDIA API key not found. Please provide it in config or set NVIDIA_API_KEY environment variable.');
312
313
  }
313
314
 
314
315
  return super.getDefaultConfig({
315
316
  url: 'https://integrate.api.nvidia.com/v1/chat/completions',
316
- apiKey: process.env.NVIDIA_API_KEY,
317
+ apiKey,
317
318
  ...customConfig
318
319
  });
319
320
  }
@@ -556,14 +556,14 @@ function createOpenAIProviders({
556
556
 
557
557
  class MixOpenRouter extends MixOpenAI {
558
558
  getDefaultConfig(customConfig) {
559
-
560
- if (!process.env.OPENROUTER_API_KEY) {
559
+ const apiKey = customConfig.apiKey || process.env.OPENROUTER_API_KEY;
560
+ if (!apiKey) {
561
561
  throw new Error('OpenRouter API key not found. Please provide it in config or set OPENROUTER_API_KEY environment variable.');
562
562
  }
563
563
 
564
564
  return MixCustom.prototype.getDefaultConfig.call(this, {
565
565
  url: 'https://openrouter.ai/api/v1/chat/completions',
566
- apiKey: process.env.OPENROUTER_API_KEY,
566
+ apiKey,
567
567
  ...customConfig
568
568
  });
569
569
  }
@@ -571,13 +571,14 @@ function createOpenAIProviders({
571
571
 
572
572
  class MixKimi extends MixOpenAI {
573
573
  getDefaultConfig(customConfig) {
574
- if (!process.env.MOONSHOT_API_KEY) {
574
+ const apiKey = customConfig.apiKey || process.env.MOONSHOT_API_KEY;
575
+ if (!apiKey) {
575
576
  throw new Error('Moonshot API key not found. Please provide it in config or set MOONSHOT_API_KEY environment variable.');
576
577
  }
577
578
 
578
579
  return MixCustom.prototype.getDefaultConfig.call(this, {
579
580
  url: 'https://api.moonshot.ai/v1/chat/completions',
580
- apiKey: process.env.MOONSHOT_API_KEY,
581
+ apiKey,
581
582
  ...customConfig
582
583
  });
583
584
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "5.1.6",
3
+ "version": "5.1.8",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -48,6 +48,48 @@ describe('public module boundary', () => {
48
48
  expect(model.models[0].provider.constructor).to.equal(api.MixOpenRouter);
49
49
  });
50
50
 
51
+ const explicitApiKeyCases = [
52
+ ['OpenAI', config => new api.MixOpenAIResponses({ config })],
53
+ ['OpenAI moderation', config => new api.MixOpenAIModeration({ config })],
54
+ ['Anthropic', config => new api.MixAnthropic({ config })],
55
+ ['Gemini', config => new api.MixGoogle({ config })],
56
+ ['MiniMax', config => new api.MixMiniMax({ config })],
57
+ ['MiMo', config => new api.MixMiMo({ config })],
58
+ ['Perplexity', config => new api.MixPerplexity({ config })],
59
+ ['Grok', config => new api.MixGrok({ config })],
60
+ ['Lambda', config => api.ModelMix.new({ mix: { openrouter: false, lambda: true } })
61
+ .hermes3({ config }).models[0].provider],
62
+ ['Groq', config => new api.MixGroq({ config })],
63
+ ['Together', config => new api.MixTogether({ config })],
64
+ ['Cerebras', config => new api.MixCerebras({ config })],
65
+ ['Fireworks', config => new api.MixFireworks({ config })],
66
+ ['NVIDIA', config => new api.MixNVIDIA({ config })],
67
+ ['OpenRouter', config => new api.MixOpenRouter({ config })],
68
+ ['Moonshot', config => new api.MixKimi({ config })]
69
+ ];
70
+
71
+ for (const [providerName, createProvider] of explicitApiKeyCases) {
72
+ it(`uses an explicit API key for ${providerName} without reading environment credentials`, () => {
73
+ const originalEnv = process.env;
74
+ process.env = new Proxy(originalEnv, {
75
+ get(target, property, receiver) {
76
+ if (typeof property === 'string' && property.endsWith('_API_KEY')) {
77
+ throw new Error(`${property} should not be read`);
78
+ }
79
+ return Reflect.get(target, property, receiver);
80
+ }
81
+ });
82
+
83
+ try {
84
+ const provider = createProvider({ apiKey: 'explicit-key' });
85
+
86
+ expect(provider.config.apiKey).to.equal('explicit-key');
87
+ } finally {
88
+ process.env = originalEnv;
89
+ }
90
+ });
91
+ }
92
+
51
93
  it('keeps the mutable pricing catalog private', () => {
52
94
  expect(require('../lib/token-usage')).to.not.have.property('MODEL_PRICING');
53
95
  });