modelmix 5.1.10 → 5.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -10
- package/demo/free.js +1 -3
- package/demo/short.js +15 -13
- package/effort.js +13 -7
- package/http-client.js +6 -6
- package/index.d.ts +13 -6
- package/index.js +204 -144
- package/lib/abort-signal.js +57 -0
- package/lib/model-chain.js +2 -2
- package/lib/provider-api-key.js +9 -0
- package/lib/providers/anthropic.js +24 -22
- package/lib/providers/base.js +21 -28
- package/lib/providers/google.js +12 -8
- package/lib/providers/openai-compatible.js +19 -48
- package/lib/providers/openai-options.js +16 -0
- package/lib/providers/openai.js +48 -47
- package/lib/providers.js +5 -2
- package/lib/token-usage.js +19 -4
- package/mcp-tools.js +5 -2
- package/package.json +3 -2
- package/plugins/rlm/index.d.ts +1 -0
- package/plugins/rlm/lib/budget.js +2 -7
- package/plugins/rlm/lib/isolated-vm-sandbox.js +8 -1
- package/plugins/rlm/lib/planner-prompt.js +1 -7
- package/plugins/rlm/lib/plugin.js +24 -11
- package/plugins/rlm/lib/validation.js +14 -0
- package/plugins/rlm/lib/variable-descriptors.js +1 -5
- package/plugins/rlm/lib/worker-catalog.js +1 -5
- package/plugins/rlm/test/isolated-vm-sandbox.test.js +23 -0
- package/pnpm-workspace.yaml +10 -5
- package/schema.js +0 -28
- package/skills/modelmix/SKILL.md +34 -12
- package/test/abort.test.js +517 -0
- package/test/anthropic.test.js +77 -1
- package/test/bottleneck.test.js +2 -2
- package/test/effort.test.js +24 -1
- package/test/fallback.test.js +101 -1
- package/test/glm.test.js +2 -6
- package/test/kimi.test.js +2 -6
- package/test/live.test.js +10 -5
- package/test/muse.test.js +24 -7
- package/test/provider-expansion.test.js +17 -0
- package/test/public-api.test.js +2 -2
- package/test/qwen.test.js +2 -3
- package/test/tokens.test.js +23 -8
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
function assertAbortSignal(signal) {
|
|
2
|
+
if (signal === undefined) return;
|
|
3
|
+
if (!(signal instanceof AbortSignal)) {
|
|
4
|
+
throw new TypeError('signal must be an AbortSignal.');
|
|
5
|
+
}
|
|
6
|
+
signal.throwIfAborted();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function assertNoStoredSignal(value, label) {
|
|
10
|
+
if (value && Object.prototype.hasOwnProperty.call(value, 'signal')) {
|
|
11
|
+
throw new TypeError(`${label}.signal is not supported; pass the AbortSignal to the execution method.`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function throwIfAborted(signal) {
|
|
16
|
+
if (signal) signal.throwIfAborted();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function validateProviderExecution(provider, { config, options, signal }) {
|
|
20
|
+
assertAbortSignal(signal);
|
|
21
|
+
assertNoStoredSignal(provider.config, 'provider.config');
|
|
22
|
+
assertNoStoredSignal(provider.options, 'provider.options');
|
|
23
|
+
assertNoStoredSignal(config, 'config');
|
|
24
|
+
assertNoStoredSignal(options, 'options');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function raceWithSignal(promise, signal) {
|
|
28
|
+
if (!signal) return promise;
|
|
29
|
+
throwIfAborted(signal);
|
|
30
|
+
let onAbort;
|
|
31
|
+
const aborted = new Promise((_, reject) => {
|
|
32
|
+
onAbort = () => reject(signal.reason);
|
|
33
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
34
|
+
});
|
|
35
|
+
return Promise.race([promise, aborted]).finally(() => {
|
|
36
|
+
signal.removeEventListener('abort', onAbort);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sleepWithSignal(ms, signal) {
|
|
41
|
+
if (!signal) return new Promise(resolve => setTimeout(resolve, ms));
|
|
42
|
+
throwIfAborted(signal);
|
|
43
|
+
let timeout;
|
|
44
|
+
const sleep = new Promise(resolve => {
|
|
45
|
+
timeout = setTimeout(resolve, ms);
|
|
46
|
+
});
|
|
47
|
+
return raceWithSignal(sleep, signal).finally(() => clearTimeout(timeout));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
assertAbortSignal,
|
|
52
|
+
assertNoStoredSignal,
|
|
53
|
+
raceWithSignal,
|
|
54
|
+
sleepWithSignal,
|
|
55
|
+
throwIfAborted,
|
|
56
|
+
validateProviderExecution
|
|
57
|
+
};
|
package/lib/model-chain.js
CHANGED
|
@@ -5,12 +5,12 @@ const CHAIN_MODEL_SHORTCUTS = new Set([
|
|
|
5
5
|
'gpt51', 'gpt52', 'gpt54', 'gpt54mini', 'gpt54nano', 'gpt54pro',
|
|
6
6
|
'gpt55', 'gpt55pro', 'gpt56sol', 'gpt56terra', 'gpt56luna',
|
|
7
7
|
'gptRealtime', 'gptRealtimeMini', 'gpt53codex', 'gpt53chat', 'gptOss',
|
|
8
|
-
'fable50', 'fable5', 'opus50', 'opus5', 'opus48', 'opus47', 'opus46',
|
|
8
|
+
'fable51', 'fable50', 'fable5', 'opus50', 'opus5', 'opus48', 'opus47', 'opus46',
|
|
9
9
|
'sonnet50', 'sonnet5', 'sonnet46', 'sonnet45', 'haiku45',
|
|
10
10
|
'gemini31pro', 'gemini37flash', 'gemini36flash', 'gemini35flash',
|
|
11
11
|
'gemini35flashLite', 'gemini31flashLite', 'sonarPro', 'sonar',
|
|
12
12
|
'grok46', 'grok45', 'grok43', 'grok420multiAgent', 'grok420',
|
|
13
|
-
'museGlimmer30b',
|
|
13
|
+
'museGlimmer30b', 'museSpark12Contributor',
|
|
14
14
|
'qwen35397b', 'qwen36plus', 'qwen37plus', 'qwen38max', 'qwen3827b', 'qwen38flash',
|
|
15
15
|
'hermes470b', 'hermes4405b', 'hermes3',
|
|
16
16
|
'kimiK26', 'kimiK27Code', 'kimiK3', 'kimiK25',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
function requireProviderApiKey(customConfig, environmentVariable, providerName) {
|
|
2
|
+
const apiKey = customConfig.apiKey || process.env[environmentVariable];
|
|
3
|
+
if (!apiKey) {
|
|
4
|
+
throw new Error(`${providerName} API key not found. Please provide it in config or set ${environmentVariable} environment variable.`);
|
|
5
|
+
}
|
|
6
|
+
return apiKey;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = { requireProviderApiKey };
|
|
@@ -4,6 +4,25 @@ const {
|
|
|
4
4
|
stripContentCacheMetadata,
|
|
5
5
|
hasNeutralCacheBreakpoint
|
|
6
6
|
} = require('../content-cache');
|
|
7
|
+
const { requireProviderApiKey } = require('../provider-api-key');
|
|
8
|
+
|
|
9
|
+
function rejectsAnthropicSamplingParams(model = '') {
|
|
10
|
+
const id = String(model).toLowerCase();
|
|
11
|
+
if (!id.includes('claude')) return false;
|
|
12
|
+
if (id.includes('mythos') || id.includes('fable')) return true;
|
|
13
|
+
|
|
14
|
+
const opus = id.match(/claude-opus-(\d+)(?:-(\d+))?/);
|
|
15
|
+
if (opus) {
|
|
16
|
+
const major = Number(opus[1]);
|
|
17
|
+
const minor = opus[2] !== undefined ? Number(opus[2]) : 0;
|
|
18
|
+
return major > 4 || (major === 4 && minor >= 7);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const sonnet = id.match(/claude-sonnet-(\d+)/);
|
|
22
|
+
if (sonnet) return Number(sonnet[1]) >= 5;
|
|
23
|
+
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
7
26
|
|
|
8
27
|
function createAnthropicProviders({ ModelMix, MixCustom, log }) {
|
|
9
28
|
class MixAnthropic extends MixCustom {
|
|
@@ -30,28 +49,11 @@ function createAnthropicProviders({ ModelMix, MixCustom, log }) {
|
|
|
30
49
|
* See: https://platform.claude.com/docs/en/about-claude/models/migration-guide
|
|
31
50
|
*/
|
|
32
51
|
static rejectsSamplingParams(model = '') {
|
|
33
|
-
|
|
34
|
-
if (!id.includes('claude')) return false;
|
|
35
|
-
if (id.includes('mythos') || id.includes('fable')) return true;
|
|
36
|
-
|
|
37
|
-
const opus = id.match(/claude-opus-(\d+)(?:-(\d+))?/);
|
|
38
|
-
if (opus) {
|
|
39
|
-
const major = Number(opus[1]);
|
|
40
|
-
const minor = opus[2] !== undefined ? Number(opus[2]) : 0;
|
|
41
|
-
return major > 4 || (major === 4 && minor >= 7);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const sonnet = id.match(/claude-sonnet-(\d+)/);
|
|
45
|
-
if (sonnet) return Number(sonnet[1]) >= 5;
|
|
46
|
-
|
|
47
|
-
return false;
|
|
52
|
+
return rejectsAnthropicSamplingParams(model);
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
getDefaultConfig(customConfig) {
|
|
51
|
-
const apiKey = customConfig
|
|
52
|
-
if (!apiKey) {
|
|
53
|
-
throw new Error('Anthropic API key not found. Please provide it in config or set ANTHROPIC_API_KEY environment variable.');
|
|
54
|
-
}
|
|
56
|
+
const apiKey = requireProviderApiKey(customConfig, 'ANTHROPIC_API_KEY', 'Anthropic');
|
|
55
57
|
|
|
56
58
|
return super.getDefaultConfig({
|
|
57
59
|
url: 'https://api.anthropic.com/v1/messages',
|
|
@@ -60,7 +62,7 @@ function createAnthropicProviders({ ModelMix, MixCustom, log }) {
|
|
|
60
62
|
});
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
async create({ config = {}, options = {} } = {}) {
|
|
65
|
+
async create({ config = {}, options = {}, signal } = {}) {
|
|
64
66
|
|
|
65
67
|
delete options.response_format;
|
|
66
68
|
|
|
@@ -83,7 +85,7 @@ function createAnthropicProviders({ ModelMix, MixCustom, log }) {
|
|
|
83
85
|
options.system = config.system;
|
|
84
86
|
|
|
85
87
|
try {
|
|
86
|
-
return await super.create({ config: requestConfig, options });
|
|
88
|
+
return await super.create({ config: requestConfig, options, signal });
|
|
87
89
|
} catch (error) {
|
|
88
90
|
// Log the error details for debugging
|
|
89
91
|
if (error.response && error.response.data) {
|
|
@@ -334,4 +336,4 @@ function createAnthropicProviders({ ModelMix, MixCustom, log }) {
|
|
|
334
336
|
return { MixAnthropic };
|
|
335
337
|
}
|
|
336
338
|
|
|
337
|
-
module.exports = createAnthropicProviders;
|
|
339
|
+
module.exports = { createAnthropicProviders, rejectsAnthropicSamplingParams };
|
package/lib/providers/base.js
CHANGED
|
@@ -11,10 +11,19 @@ const {
|
|
|
11
11
|
stripContentCacheMetadata
|
|
12
12
|
} = require('../content-cache');
|
|
13
13
|
const { configForDebug, redactSecret } = require('../provider-debug');
|
|
14
|
+
const { requireProviderApiKey } = require('../provider-api-key');
|
|
15
|
+
const { normalizeOpenAIOptions } = require('./openai-options');
|
|
16
|
+
const {
|
|
17
|
+
assertNoStoredSignal,
|
|
18
|
+
throwIfAborted,
|
|
19
|
+
validateProviderExecution
|
|
20
|
+
} = require('../abort-signal');
|
|
14
21
|
|
|
15
22
|
function createBaseProviders({ ModelMix }) {
|
|
16
23
|
class MixCustom {
|
|
17
24
|
constructor({ config = {}, options = {}, headers = {} } = {}) {
|
|
25
|
+
assertNoStoredSignal(config, 'config');
|
|
26
|
+
assertNoStoredSignal(options, 'options');
|
|
18
27
|
this.config = this.getDefaultConfig(config);
|
|
19
28
|
this.options = this.getDefaultOptions(options);
|
|
20
29
|
this.headers = this.getDefaultHeaders(headers);
|
|
@@ -67,7 +76,8 @@ function createBaseProviders({ ModelMix }) {
|
|
|
67
76
|
return buildRequestBodyAndHeaders(options, headers);
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
async create({ config = {}, options = {} } = {}) {
|
|
79
|
+
async create({ config = {}, options = {}, signal } = {}) {
|
|
80
|
+
validateProviderExecution(this, { config, options, signal });
|
|
71
81
|
try {
|
|
72
82
|
this.sanitizeCacheOptions(options);
|
|
73
83
|
if (Array.isArray(options.messages)) {
|
|
@@ -88,19 +98,22 @@ function createBaseProviders({ ModelMix }) {
|
|
|
88
98
|
}
|
|
89
99
|
|
|
90
100
|
if (options.stream) {
|
|
91
|
-
return this.processStream(await fetchStreamResponse(this.config.url, {
|
|
101
|
+
return await this.processStream(await fetchStreamResponse(this.config.url, {
|
|
92
102
|
method: 'POST',
|
|
93
103
|
headers: request.headers,
|
|
94
|
-
body: request.body
|
|
104
|
+
body: request.body,
|
|
105
|
+
signal
|
|
95
106
|
}));
|
|
96
107
|
} else {
|
|
97
108
|
return this.processResponse(await fetchJsonResponse(this.config.url, {
|
|
98
109
|
method: 'POST',
|
|
99
110
|
headers: request.headers,
|
|
100
|
-
body: request.body
|
|
111
|
+
body: request.body,
|
|
112
|
+
signal
|
|
101
113
|
}));
|
|
102
114
|
}
|
|
103
115
|
} catch (error) {
|
|
116
|
+
throwIfAborted(signal);
|
|
104
117
|
throw this.handleError(error);
|
|
105
118
|
}
|
|
106
119
|
}
|
|
@@ -274,10 +287,7 @@ function createBaseProviders({ ModelMix }) {
|
|
|
274
287
|
}
|
|
275
288
|
|
|
276
289
|
getDefaultConfig(customConfig) {
|
|
277
|
-
const apiKey = customConfig
|
|
278
|
-
if (!apiKey) {
|
|
279
|
-
throw new Error('OpenAI API key not found. Please provide it in config or set OPENAI_API_KEY environment variable.');
|
|
280
|
-
}
|
|
290
|
+
const apiKey = requireProviderApiKey(customConfig, 'OPENAI_API_KEY', 'OpenAI');
|
|
281
291
|
|
|
282
292
|
return super.getDefaultConfig({
|
|
283
293
|
url: 'https://api.openai.com/v1/chat/completions',
|
|
@@ -286,24 +296,9 @@ function createBaseProviders({ ModelMix }) {
|
|
|
286
296
|
});
|
|
287
297
|
}
|
|
288
298
|
|
|
289
|
-
async create({ config = {}, options = {} } = {}) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if (options.model?.startsWith('o')) {
|
|
293
|
-
delete options.max_tokens;
|
|
294
|
-
delete options.temperature;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Use max_completion_tokens and remove temperature for GPT-5 models
|
|
298
|
-
if (options.model?.includes('gpt-5')) {
|
|
299
|
-
if (options.max_tokens) {
|
|
300
|
-
options.max_completion_tokens = options.max_tokens;
|
|
301
|
-
delete options.max_tokens;
|
|
302
|
-
}
|
|
303
|
-
delete options.temperature;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return super.create({ config, options });
|
|
299
|
+
async create({ config = {}, options = {}, signal } = {}) {
|
|
300
|
+
normalizeOpenAIOptions(options);
|
|
301
|
+
return super.create({ config, options, signal });
|
|
307
302
|
}
|
|
308
303
|
|
|
309
304
|
static convertMessages(messages, config) {
|
|
@@ -387,10 +382,8 @@ function createBaseProviders({ ModelMix }) {
|
|
|
387
382
|
}
|
|
388
383
|
}
|
|
389
384
|
|
|
390
|
-
// Solo incluir tools si el array no está vacío
|
|
391
385
|
if (toolsArray.length > 0) {
|
|
392
386
|
options.tools = toolsArray;
|
|
393
|
-
// options.tool_choice = "auto";
|
|
394
387
|
}
|
|
395
388
|
|
|
396
389
|
return options;
|
package/lib/providers/google.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
const { fetchJsonResponse } = require('../../http-client');
|
|
2
2
|
const { configForDebug } = require('../provider-debug');
|
|
3
|
+
const { requireProviderApiKey } = require('../provider-api-key');
|
|
4
|
+
const {
|
|
5
|
+
throwIfAborted,
|
|
6
|
+
validateProviderExecution
|
|
7
|
+
} = require('../abort-signal');
|
|
3
8
|
|
|
4
9
|
function createGoogleProviders({ ModelMix, MixCustom }) {
|
|
5
10
|
class MixGoogle extends MixCustom {
|
|
6
11
|
getDefaultConfig(customConfig) {
|
|
7
|
-
const apiKey = customConfig
|
|
12
|
+
const apiKey = requireProviderApiKey(customConfig, 'GEMINI_API_KEY', 'Gemini');
|
|
8
13
|
return super.getDefaultConfig({
|
|
9
14
|
url: 'https://generativelanguage.googleapis.com/v1beta/models',
|
|
10
15
|
apiKey,
|
|
@@ -19,7 +24,7 @@ function createGoogleProviders({ ModelMix, MixCustom }) {
|
|
|
19
24
|
};
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
static convertMessages(messages
|
|
27
|
+
static convertMessages(messages) {
|
|
23
28
|
return messages.map(message => {
|
|
24
29
|
|
|
25
30
|
// Handle assistant messages with tool_calls (content is null)
|
|
@@ -121,11 +126,8 @@ function createGoogleProviders({ ModelMix, MixCustom }) {
|
|
|
121
126
|
}, []);
|
|
122
127
|
}
|
|
123
128
|
|
|
124
|
-
async create({ config = {}, options = {} } = {}) {
|
|
125
|
-
|
|
126
|
-
throw new Error('Gemini API key not found. Please provide it in config or set GEMINI_API_KEY environment variable.');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
+
async create({ config = {}, options = {}, signal } = {}) {
|
|
130
|
+
validateProviderExecution(this, { config, options, signal });
|
|
129
131
|
const generateContentApi = options.stream ? 'streamGenerateContent' : 'generateContent';
|
|
130
132
|
|
|
131
133
|
const fullUrl = `${this.config.url}/${options.model}:${generateContentApi}?key=${this.config.apiKey}`;
|
|
@@ -190,10 +192,12 @@ function createGoogleProviders({ ModelMix, MixCustom }) {
|
|
|
190
192
|
return this.processResponse(await fetchJsonResponse(fullUrl, {
|
|
191
193
|
method: 'POST',
|
|
192
194
|
headers: this.headers,
|
|
193
|
-
body: JSON.stringify(payload)
|
|
195
|
+
body: JSON.stringify(payload),
|
|
196
|
+
signal
|
|
194
197
|
}));
|
|
195
198
|
}
|
|
196
199
|
} catch (error) {
|
|
200
|
+
throwIfAborted(signal);
|
|
197
201
|
throw this.handleError(error);
|
|
198
202
|
}
|
|
199
203
|
}
|
|
@@ -2,14 +2,12 @@ const {
|
|
|
2
2
|
GROK420_REASONING,
|
|
3
3
|
GROK420_NON_REASONING
|
|
4
4
|
} = require('../../effort');
|
|
5
|
+
const { requireProviderApiKey } = require('../provider-api-key');
|
|
5
6
|
|
|
6
7
|
function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
7
8
|
class MixMiniMax extends MixOpenAI {
|
|
8
9
|
getDefaultConfig(customConfig) {
|
|
9
|
-
const apiKey = customConfig
|
|
10
|
-
if (!apiKey) {
|
|
11
|
-
throw new Error('MiniMax API key not found. Please provide it in config or set MINIMAX_API_KEY environment variable.');
|
|
12
|
-
}
|
|
10
|
+
const apiKey = requireProviderApiKey(customConfig, 'MINIMAX_API_KEY', 'MiniMax');
|
|
13
11
|
|
|
14
12
|
return MixCustom.prototype.getDefaultConfig.call(this, {
|
|
15
13
|
url: 'https://api.minimax.io/v1/chat/completions',
|
|
@@ -29,10 +27,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
29
27
|
|
|
30
28
|
class MixMiMo extends MixOpenAI {
|
|
31
29
|
getDefaultConfig(customConfig) {
|
|
32
|
-
const apiKey = customConfig
|
|
33
|
-
if (!apiKey) {
|
|
34
|
-
throw new Error('MiMo API key not found. Please provide it in config or set MIMO_API_KEY environment variable.');
|
|
35
|
-
}
|
|
30
|
+
const apiKey = requireProviderApiKey(customConfig, 'MIMO_API_KEY', 'MiMo');
|
|
36
31
|
|
|
37
32
|
return MixCustom.prototype.getDefaultConfig.call(this, {
|
|
38
33
|
url: 'https://api.xiaomimimo.com/v1/chat/completions',
|
|
@@ -53,10 +48,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
53
48
|
|
|
54
49
|
class MixPerplexity extends MixCustom {
|
|
55
50
|
getDefaultConfig(customConfig) {
|
|
56
|
-
const apiKey = customConfig
|
|
57
|
-
if (!apiKey) {
|
|
58
|
-
throw new Error('Perplexity API key not found. Please provide it in config or set PPLX_API_KEY environment variable.');
|
|
59
|
-
}
|
|
51
|
+
const apiKey = requireProviderApiKey(customConfig, 'PPLX_API_KEY', 'Perplexity');
|
|
60
52
|
|
|
61
53
|
return super.getDefaultConfig({
|
|
62
54
|
url: 'https://api.perplexity.ai/chat/completions',
|
|
@@ -65,7 +57,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
65
57
|
});
|
|
66
58
|
}
|
|
67
59
|
|
|
68
|
-
async create({ config = {}, options = {} } = {}) {
|
|
60
|
+
async create({ config = {}, options = {}, signal } = {}) {
|
|
69
61
|
|
|
70
62
|
if (config.schema) {
|
|
71
63
|
options.response_format = {
|
|
@@ -74,7 +66,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
74
66
|
};
|
|
75
67
|
}
|
|
76
68
|
|
|
77
|
-
return super.create({ config, options });
|
|
69
|
+
return super.create({ config, options, signal });
|
|
78
70
|
}
|
|
79
71
|
}
|
|
80
72
|
|
|
@@ -133,10 +125,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
133
125
|
|
|
134
126
|
class MixGrok extends MixOpenAI {
|
|
135
127
|
getDefaultConfig(customConfig) {
|
|
136
|
-
const apiKey = customConfig
|
|
137
|
-
if (!apiKey) {
|
|
138
|
-
throw new Error('Grok API key not found. Please provide it in config or set XAI_API_KEY environment variable.');
|
|
139
|
-
}
|
|
128
|
+
const apiKey = requireProviderApiKey(customConfig, 'XAI_API_KEY', 'Grok');
|
|
140
129
|
|
|
141
130
|
return super.getDefaultConfig({
|
|
142
131
|
url: 'https://api.x.ai/v1/chat/completions',
|
|
@@ -145,20 +134,17 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
145
134
|
});
|
|
146
135
|
}
|
|
147
136
|
|
|
148
|
-
async create({ config = {}, options = {} } = {}) {
|
|
137
|
+
async create({ config = {}, options = {}, signal } = {}) {
|
|
149
138
|
if (options.model === GROK420_REASONING || options.model === GROK420_NON_REASONING) {
|
|
150
139
|
delete options.reasoning_effort;
|
|
151
140
|
}
|
|
152
|
-
return super.create({ config, options });
|
|
141
|
+
return super.create({ config, options, signal });
|
|
153
142
|
}
|
|
154
143
|
}
|
|
155
144
|
|
|
156
145
|
class MixLambda extends MixCustom {
|
|
157
146
|
getDefaultConfig(customConfig) {
|
|
158
|
-
const apiKey = customConfig
|
|
159
|
-
if (!apiKey) {
|
|
160
|
-
throw new Error('Lambda API key not found. Please provide it in config or set LAMBDA_API_KEY environment variable.');
|
|
161
|
-
}
|
|
147
|
+
const apiKey = requireProviderApiKey(customConfig, 'LAMBDA_API_KEY', 'Lambda');
|
|
162
148
|
|
|
163
149
|
return super.getDefaultConfig({
|
|
164
150
|
url: 'https://api.lambda.ai/v1/chat/completions',
|
|
@@ -176,14 +162,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
176
162
|
});
|
|
177
163
|
}
|
|
178
164
|
|
|
179
|
-
create({ config = {}, options = {} } = {}) {
|
|
165
|
+
create({ config = {}, options = {}, signal } = {}) {
|
|
180
166
|
if (config.schema) {
|
|
181
167
|
options.response_format = {
|
|
182
168
|
type: 'json_schema',
|
|
183
169
|
json_schema: { schema: config.schema }
|
|
184
170
|
};
|
|
185
171
|
}
|
|
186
|
-
return super.create({ config, options });
|
|
172
|
+
return super.create({ config, options, signal });
|
|
187
173
|
}
|
|
188
174
|
|
|
189
175
|
static extractThink(data) {
|
|
@@ -235,10 +221,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
235
221
|
|
|
236
222
|
class MixGroq extends MixCustom {
|
|
237
223
|
getDefaultConfig(customConfig) {
|
|
238
|
-
const apiKey = customConfig
|
|
239
|
-
if (!apiKey) {
|
|
240
|
-
throw new Error('Groq API key not found. Please provide it in config or set GROQ_API_KEY environment variable.');
|
|
241
|
-
}
|
|
224
|
+
const apiKey = requireProviderApiKey(customConfig, 'GROQ_API_KEY', 'Groq');
|
|
242
225
|
|
|
243
226
|
return super.getDefaultConfig({
|
|
244
227
|
url: 'https://api.groq.com/openai/v1/chat/completions',
|
|
@@ -250,10 +233,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
250
233
|
|
|
251
234
|
class MixTogether extends MixCustom {
|
|
252
235
|
getDefaultConfig(customConfig) {
|
|
253
|
-
const apiKey = customConfig
|
|
254
|
-
if (!apiKey) {
|
|
255
|
-
throw new Error('Together API key not found. Please provide it in config or set TOGETHER_API_KEY environment variable.');
|
|
256
|
-
}
|
|
236
|
+
const apiKey = requireProviderApiKey(customConfig, 'TOGETHER_API_KEY', 'Together');
|
|
257
237
|
|
|
258
238
|
return super.getDefaultConfig({
|
|
259
239
|
url: 'https://api.together.xyz/v1/chat/completions',
|
|
@@ -272,10 +252,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
272
252
|
|
|
273
253
|
class MixCerebras extends MixCustom {
|
|
274
254
|
getDefaultConfig(customConfig) {
|
|
275
|
-
const apiKey = customConfig
|
|
276
|
-
if (!apiKey) {
|
|
277
|
-
throw new Error('Cerebras API key not found. Please provide it in config or set CEREBRAS_API_KEY environment variable.');
|
|
278
|
-
}
|
|
255
|
+
const apiKey = requireProviderApiKey(customConfig, 'CEREBRAS_API_KEY', 'Cerebras');
|
|
279
256
|
|
|
280
257
|
return super.getDefaultConfig({
|
|
281
258
|
url: 'https://api.cerebras.ai/v1/chat/completions',
|
|
@@ -284,18 +261,15 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
284
261
|
});
|
|
285
262
|
}
|
|
286
263
|
|
|
287
|
-
create({ config = {}, options = {} } = {}) {
|
|
264
|
+
create({ config = {}, options = {}, signal } = {}) {
|
|
288
265
|
delete options.response_format;
|
|
289
|
-
return super.create({ config, options });
|
|
266
|
+
return super.create({ config, options, signal });
|
|
290
267
|
}
|
|
291
268
|
}
|
|
292
269
|
|
|
293
270
|
class MixFireworks extends MixCustom {
|
|
294
271
|
getDefaultConfig(customConfig) {
|
|
295
|
-
const apiKey = customConfig
|
|
296
|
-
if (!apiKey) {
|
|
297
|
-
throw new Error('Fireworks API key not found. Please provide it in config or set FIREWORKS_API_KEY environment variable.');
|
|
298
|
-
}
|
|
272
|
+
const apiKey = requireProviderApiKey(customConfig, 'FIREWORKS_API_KEY', 'Fireworks');
|
|
299
273
|
|
|
300
274
|
return super.getDefaultConfig({
|
|
301
275
|
url: 'https://api.fireworks.ai/inference/v1/chat/completions',
|
|
@@ -307,10 +281,7 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
307
281
|
|
|
308
282
|
class MixNVIDIA extends MixCustom {
|
|
309
283
|
getDefaultConfig(customConfig) {
|
|
310
|
-
const apiKey = customConfig
|
|
311
|
-
if (!apiKey) {
|
|
312
|
-
throw new Error('NVIDIA API key not found. Please provide it in config or set NVIDIA_API_KEY environment variable.');
|
|
313
|
-
}
|
|
284
|
+
const apiKey = requireProviderApiKey(customConfig, 'NVIDIA_API_KEY', 'NVIDIA');
|
|
314
285
|
|
|
315
286
|
return super.getDefaultConfig({
|
|
316
287
|
url: 'https://integrate.api.nvidia.com/v1/chat/completions',
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function normalizeOpenAIOptions(options) {
|
|
2
|
+
if (/^o\d/.test(options.model || '')) {
|
|
3
|
+
delete options.max_tokens;
|
|
4
|
+
delete options.temperature;
|
|
5
|
+
}
|
|
6
|
+
if (options.model?.includes('gpt-5')) {
|
|
7
|
+
if (options.max_tokens) {
|
|
8
|
+
options.max_completion_tokens = options.max_tokens;
|
|
9
|
+
delete options.max_tokens;
|
|
10
|
+
}
|
|
11
|
+
delete options.temperature;
|
|
12
|
+
}
|
|
13
|
+
return options;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { normalizeOpenAIOptions };
|