modelmix 4.4.4 → 4.4.7
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 +44 -16
- package/demo/json.js +13 -11
- package/index.js +138 -25
- package/package.json +2 -2
- package/schema.js +49 -5
- package/skills/modelmix/SKILL.md +35 -5
- package/test/bottleneck.test.js +14 -10
- package/test/fallback.test.js +13 -13
- package/test/history.test.js +572 -0
- package/test/images.test.js +3 -3
- package/test/json.test.js +188 -3
- package/test/live.mcp.js +7 -7
- package/test/live.test.js +8 -8
- package/test/templates.test.js +15 -15
- package/test/tokens.test.js +19 -4
package/test/bottleneck.test.js
CHANGED
|
@@ -72,7 +72,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
72
72
|
it('should enforce minimum time between requests', async () => {
|
|
73
73
|
const startTimes = [];
|
|
74
74
|
|
|
75
|
-
model.
|
|
75
|
+
model.gpt51();
|
|
76
76
|
|
|
77
77
|
// Mock API responses
|
|
78
78
|
nock('https://api.openai.com')
|
|
@@ -115,6 +115,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
115
115
|
model = ModelMix.new({
|
|
116
116
|
config: {
|
|
117
117
|
debug: false,
|
|
118
|
+
max_history: -1, // concurrent calls need history to preserve queued messages
|
|
118
119
|
bottleneck: {
|
|
119
120
|
maxConcurrent: 2,
|
|
120
121
|
minTime: 50
|
|
@@ -122,7 +123,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
122
123
|
}
|
|
123
124
|
});
|
|
124
125
|
|
|
125
|
-
model.
|
|
126
|
+
model.gpt51();
|
|
126
127
|
|
|
127
128
|
// Mock API with delay to simulate concurrent requests
|
|
128
129
|
nock('https://api.openai.com')
|
|
@@ -184,7 +185,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
184
185
|
it('should apply rate limiting to OpenAI requests', async () => {
|
|
185
186
|
const requestTimes = [];
|
|
186
187
|
|
|
187
|
-
model.
|
|
188
|
+
model.gpt51();
|
|
188
189
|
|
|
189
190
|
nock('https://api.openai.com')
|
|
190
191
|
.post('/v1/chat/completions')
|
|
@@ -215,7 +216,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
215
216
|
it('should apply rate limiting to Anthropic requests', async () => {
|
|
216
217
|
const requestTimes = [];
|
|
217
218
|
|
|
218
|
-
model.
|
|
219
|
+
model.sonnet46();
|
|
219
220
|
|
|
220
221
|
nock('https://api.anthropic.com')
|
|
221
222
|
.post('/v1/messages')
|
|
@@ -267,7 +268,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
267
268
|
});
|
|
268
269
|
|
|
269
270
|
it('should handle rate limiting with API errors', async () => {
|
|
270
|
-
model.
|
|
271
|
+
model.gpt51();
|
|
271
272
|
|
|
272
273
|
nock('https://api.openai.com')
|
|
273
274
|
.post('/v1/chat/completions')
|
|
@@ -289,7 +290,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
289
290
|
it('should continue rate limiting after errors', async () => {
|
|
290
291
|
const requestTimes = [];
|
|
291
292
|
|
|
292
|
-
model.
|
|
293
|
+
model.gpt51();
|
|
293
294
|
|
|
294
295
|
// First request fails
|
|
295
296
|
nock('https://api.openai.com')
|
|
@@ -335,6 +336,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
335
336
|
const model = ModelMix.new({
|
|
336
337
|
config: {
|
|
337
338
|
debug: false,
|
|
339
|
+
max_history: -1,
|
|
338
340
|
bottleneck: {
|
|
339
341
|
maxConcurrent: 5,
|
|
340
342
|
minTime: 100,
|
|
@@ -345,7 +347,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
345
347
|
}
|
|
346
348
|
});
|
|
347
349
|
|
|
348
|
-
model.
|
|
350
|
+
model.gpt51();
|
|
349
351
|
|
|
350
352
|
let requestCount = 0;
|
|
351
353
|
|
|
@@ -385,6 +387,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
385
387
|
const model = ModelMix.new({
|
|
386
388
|
config: {
|
|
387
389
|
debug: false,
|
|
390
|
+
max_history: -1,
|
|
388
391
|
bottleneck: {
|
|
389
392
|
maxConcurrent: 1,
|
|
390
393
|
minTime: 200
|
|
@@ -392,7 +395,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
392
395
|
}
|
|
393
396
|
});
|
|
394
397
|
|
|
395
|
-
model.
|
|
398
|
+
model.gpt51();
|
|
396
399
|
|
|
397
400
|
const results = [];
|
|
398
401
|
|
|
@@ -432,6 +435,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
432
435
|
const model = ModelMix.new({
|
|
433
436
|
config: {
|
|
434
437
|
debug: false,
|
|
438
|
+
max_history: -1,
|
|
435
439
|
bottleneck: {
|
|
436
440
|
maxConcurrent: 2,
|
|
437
441
|
minTime: 100,
|
|
@@ -440,7 +444,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
440
444
|
}
|
|
441
445
|
});
|
|
442
446
|
|
|
443
|
-
model.
|
|
447
|
+
model.gpt51();
|
|
444
448
|
|
|
445
449
|
nock('https://api.openai.com')
|
|
446
450
|
.post('/v1/chat/completions')
|
|
@@ -489,7 +493,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
|
|
|
489
493
|
done();
|
|
490
494
|
});
|
|
491
495
|
|
|
492
|
-
model.
|
|
496
|
+
model.gpt51();
|
|
493
497
|
|
|
494
498
|
nock('https://api.openai.com')
|
|
495
499
|
.post('/v1/chat/completions')
|
package/test/fallback.test.js
CHANGED
|
@@ -25,7 +25,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
it('should use primary provider when available', async () => {
|
|
28
|
-
model.gpt5mini().
|
|
28
|
+
model.gpt5mini().sonnet46().addText('Hello');
|
|
29
29
|
|
|
30
30
|
// Mock successful OpenAI response
|
|
31
31
|
nock('https://api.openai.com')
|
|
@@ -45,7 +45,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
it('should fallback to secondary provider when primary fails', async () => {
|
|
48
|
-
model.gpt5mini().
|
|
48
|
+
model.gpt5mini().sonnet46().addText('Hello');
|
|
49
49
|
|
|
50
50
|
// Mock failed OpenAI response (GPT-5 mini)
|
|
51
51
|
nock('https://api.openai.com')
|
|
@@ -68,7 +68,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
it('should cascade through multiple fallbacks', async () => {
|
|
71
|
-
model.gpt5mini().
|
|
71
|
+
model.gpt5mini().sonnet46().gemini3flash().addText('Hello');
|
|
72
72
|
|
|
73
73
|
// Mock failed OpenAI response
|
|
74
74
|
nock('https://api.openai.com')
|
|
@@ -99,7 +99,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it('should throw error when all providers fail', async () => {
|
|
102
|
-
model.gpt5mini().
|
|
102
|
+
model.gpt5mini().sonnet46().addText('Hello');
|
|
103
103
|
|
|
104
104
|
// Mock all providers failing
|
|
105
105
|
nock('https://api.openai.com')
|
|
@@ -129,7 +129,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
it('should fallback from OpenAI to Anthropic', async () => {
|
|
132
|
-
model.gpt5mini().
|
|
132
|
+
model.gpt5mini().sonnet46().addText('Test message');
|
|
133
133
|
|
|
134
134
|
// Mock OpenAI failure
|
|
135
135
|
nock('https://api.openai.com')
|
|
@@ -152,7 +152,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
it('should fallback from Anthropic to Google', async () => {
|
|
155
|
-
model.
|
|
155
|
+
model.sonnet46().gemini3flash().addText('Test message');
|
|
156
156
|
|
|
157
157
|
// Mock Anthropic failure
|
|
158
158
|
nock('https://api.anthropic.com')
|
|
@@ -178,7 +178,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
it('should handle network timeout fallback', async () => {
|
|
181
|
-
model.gpt5mini().
|
|
181
|
+
model.gpt5mini().sonnet46().addText('Hello');
|
|
182
182
|
|
|
183
183
|
// Mock timeout error on first provider (using 408 Request Timeout)
|
|
184
184
|
nock('https://api.openai.com')
|
|
@@ -212,7 +212,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
212
212
|
|
|
213
213
|
it('should handle JSON fallback correctly', async () => {
|
|
214
214
|
const schema = { name: 'Alice', age: 30 };
|
|
215
|
-
model.gpt5mini().
|
|
215
|
+
model.gpt5mini().sonnet46().addText('Generate user data');
|
|
216
216
|
|
|
217
217
|
// Mock OpenAI failure
|
|
218
218
|
nock('https://api.openai.com')
|
|
@@ -238,7 +238,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
it('should preserve message history through fallbacks', async () => {
|
|
241
|
-
model.gpt5mini().
|
|
241
|
+
model.gpt5mini().sonnet46()
|
|
242
242
|
.addText('First message')
|
|
243
243
|
.addText('Second message');
|
|
244
244
|
|
|
@@ -272,7 +272,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
272
272
|
|
|
273
273
|
// Configure with custom temperature for fallback
|
|
274
274
|
model.gpt5mini({ options: { temperature: 0.6 } })
|
|
275
|
-
.
|
|
275
|
+
.sonnet46({ options: { temperature: 0.7 } })
|
|
276
276
|
.addText('Creative response');
|
|
277
277
|
|
|
278
278
|
// Mock first provider failure
|
|
@@ -305,7 +305,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
305
305
|
});
|
|
306
306
|
|
|
307
307
|
model.gpt5mini({ options: { max_tokens: 100 } })
|
|
308
|
-
.
|
|
308
|
+
.sonnet46({ options: { max_tokens: 200 } })
|
|
309
309
|
.addText('Generate text');
|
|
310
310
|
|
|
311
311
|
// Mock OpenAI failure
|
|
@@ -342,7 +342,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
342
342
|
});
|
|
343
343
|
|
|
344
344
|
it('should provide detailed error information when all fallbacks fail', async () => {
|
|
345
|
-
model.gpt5mini().
|
|
345
|
+
model.gpt5mini().sonnet46().gemini3flash().addText('Test');
|
|
346
346
|
|
|
347
347
|
// Mock all providers failing with different errors
|
|
348
348
|
nock('https://api.openai.com')
|
|
@@ -367,7 +367,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
367
367
|
});
|
|
368
368
|
|
|
369
369
|
it('should handle malformed responses in fallback', async () => {
|
|
370
|
-
model.gpt5mini().
|
|
370
|
+
model.gpt5mini().sonnet46().addText('Test');
|
|
371
371
|
|
|
372
372
|
// Mock malformed response from first provider
|
|
373
373
|
nock('https://api.openai.com')
|