modelmix 5.2.3 → 5.3.1
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 +22 -31
- package/demo/benchmark.js +4 -7
- package/demo/demo.js +2 -2
- package/demo/fallback.js +1 -1
- package/demo/free.js +3 -3
- package/demo/gemini.js +3 -3
- package/demo/json.js +0 -1
- package/demo/round-robin.js +2 -2
- package/demo/short.js +1 -1
- package/effort.js +3 -1
- package/index.d.ts +2 -12
- package/index.js +7 -50
- package/lib/model-chain.js +8 -8
- package/lib/providers/openai-options.js +1 -1
- package/lib/providers/openai.js +14 -9
- package/lib/token-usage.js +3 -1
- package/package.json +1 -1
- package/plugins/benchmark/test/benchmark.test.js +33 -29
- package/skills/modelmix/SKILL.md +26 -35
- package/test/abort.test.js +1 -1
- package/test/bottleneck.test.js +12 -12
- package/test/effort.test.js +9 -0
- package/test/fallback.test.js +63 -23
- package/test/grok.test.js +1 -4
- package/test/history.test.js +2 -2
- package/test/images.test.js +3 -3
- package/test/json.test.js +2 -2
- package/test/live.test.js +6 -8
- package/test/provider-expansion.test.js +10 -17
- package/test/templates.test.js +32 -32
- package/test/tokens.test.js +63 -23
package/test/tokens.test.js
CHANGED
|
@@ -252,29 +252,35 @@ describe('Token Usage Tracking', () => {
|
|
|
252
252
|
]);
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
-
it('should translate neutral cache breakpoints for GPT-5.6 and filter them for older models', async function () {
|
|
255
|
+
it('should translate neutral cache breakpoints for GPT-5.6 and GPT-6 and filter them for older models', async function () {
|
|
256
256
|
const breakpoint = { mode: 'explicit' };
|
|
257
257
|
const model = ModelMix.new()
|
|
258
258
|
.addText('Stable text', { role: 'developer', cache: { breakpoint: true } })
|
|
259
259
|
.addImageFromUrl('data:image/png;base64,AAAA', { cache: { breakpoint: true } });
|
|
260
260
|
const messages = await model.prepareMessages();
|
|
261
|
-
const gpt56Request = MixOpenAIResponses.buildResponsesRequest({
|
|
262
|
-
model: 'gpt-5.6-luna',
|
|
263
|
-
messages,
|
|
264
|
-
prompt_cache_options: { mode: 'explicit', ttl: '30m' }
|
|
265
|
-
});
|
|
266
|
-
const olderRequest = MixOpenAIResponses.buildResponsesRequest({
|
|
267
|
-
model: 'gpt-5.4',
|
|
268
|
-
messages
|
|
269
|
-
});
|
|
270
261
|
|
|
271
262
|
expect(messages[0].content[0]).to.deep.include({ cache: { breakpoint: true } });
|
|
272
263
|
expect(messages[0].content[0]).to.not.have.property('prompt_cache_breakpoint');
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
264
|
+
|
|
265
|
+
for (const key of ['gpt-5.6-luna', 'gpt-6-astra', 'gpt-6-sol', 'gpt-6-luna']) {
|
|
266
|
+
const request = MixOpenAIResponses.buildResponsesRequest({
|
|
267
|
+
model: key,
|
|
268
|
+
messages,
|
|
269
|
+
prompt_cache_options: { mode: 'explicit', ttl: '30m' }
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
expect(request.prompt_cache_options).to.deep.equal({ mode: 'explicit', ttl: '30m' });
|
|
273
|
+
expect(request.input[0].content[0].prompt_cache_breakpoint).to.deep.equal(breakpoint);
|
|
274
|
+
expect(request.input[1].content[0]).to.deep.equal({
|
|
275
|
+
type: 'input_image',
|
|
276
|
+
image_url: 'data:image/png;base64,AAAA',
|
|
277
|
+
prompt_cache_breakpoint: breakpoint
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const olderRequest = MixOpenAIResponses.buildResponsesRequest({
|
|
282
|
+
model: 'gpt-5.4',
|
|
283
|
+
messages
|
|
278
284
|
});
|
|
279
285
|
expect(olderRequest.input[0].content[0]).to.not.have.property('cache');
|
|
280
286
|
expect(olderRequest.input[0].content[0]).to.not.have.property('prompt_cache_breakpoint');
|
|
@@ -347,6 +353,12 @@ describe('Token Usage Tracking', () => {
|
|
|
347
353
|
prompt_cache_retention: '24h'
|
|
348
354
|
})).to.throw('prompt_cache_options.ttl');
|
|
349
355
|
|
|
356
|
+
expect(() => MixOpenAIResponses.buildResponsesRequest({
|
|
357
|
+
model: 'gpt-6-sol',
|
|
358
|
+
messages: [{ role: 'user', content: 'Hi' }],
|
|
359
|
+
prompt_cache_retention: '24h'
|
|
360
|
+
})).to.throw('prompt_cache_options.ttl');
|
|
361
|
+
|
|
350
362
|
expect(() => MixOpenAIResponses.buildResponsesRequest({
|
|
351
363
|
model: 'gpt-5.4',
|
|
352
364
|
messages: [{ role: 'user', content: 'Hi' }],
|
|
@@ -417,6 +429,35 @@ describe('Token Usage Tracking', () => {
|
|
|
417
429
|
}
|
|
418
430
|
});
|
|
419
431
|
|
|
432
|
+
it('should register GPT-6 Sol and Luna shortcuts with OpenAI Responses provider', function () {
|
|
433
|
+
const model = ModelMix.new({ mix: { openrouter: true } })
|
|
434
|
+
.gpt6sol()
|
|
435
|
+
.gpt6luna();
|
|
436
|
+
|
|
437
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
438
|
+
'gpt-6-sol',
|
|
439
|
+
'openai/gpt-6-sol',
|
|
440
|
+
'gpt-6-luna',
|
|
441
|
+
'openai/gpt-6-luna'
|
|
442
|
+
]);
|
|
443
|
+
expect(model.models[0].provider).to.be.instanceOf(MixOpenAIResponses);
|
|
444
|
+
expect(model.models[1].provider).to.be.instanceOf(MixOpenRouter);
|
|
445
|
+
expect(model.models[2].provider).to.be.instanceOf(MixOpenAIResponses);
|
|
446
|
+
expect(model.models[3].provider).to.be.instanceOf(MixOpenRouter);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('should account for GPT-6 Sol and Luna cache usage and the long-context boundary', function () {
|
|
450
|
+
const tokens = { input: 272_000, cached: 100_000, cacheWrite: 20_000, output: 1_000 };
|
|
451
|
+
for (const key of ['gpt-6-sol', 'openai/gpt-6-sol']) {
|
|
452
|
+
expect(ModelMix.calculateCost(key, tokens)).to.be.closeTo(0.384, 1e-10);
|
|
453
|
+
expect(ModelMix.calculateCost(key, { ...tokens, input: 272_001 })).to.be.closeTo(0.763004, 1e-10);
|
|
454
|
+
}
|
|
455
|
+
for (const key of ['gpt-6-luna', 'openai/gpt-6-luna']) {
|
|
456
|
+
expect(ModelMix.calculateCost(key, tokens)).to.be.closeTo(0.0192, 1e-10);
|
|
457
|
+
expect(ModelMix.calculateCost(key, { ...tokens, input: 272_001 })).to.be.closeTo(0.0381502, 1e-10);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
420
461
|
it('should register GPT-5.6 shortcuts with OpenAI Responses provider', function () {
|
|
421
462
|
const model = ModelMix.new({ mix: { openrouter: true } })
|
|
422
463
|
.gpt56sol()
|
|
@@ -437,8 +478,8 @@ describe('Token Usage Tracking', () => {
|
|
|
437
478
|
expect(model.models[3].provider).to.be.instanceOf(MixOpenRouter);
|
|
438
479
|
expect(model.models[4].provider).to.be.instanceOf(MixOpenAIResponses);
|
|
439
480
|
expect(model.models[5].provider).to.be.instanceOf(MixOpenRouter);
|
|
440
|
-
expect(ModelMix.calculateCost('gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(
|
|
441
|
-
expect(ModelMix.calculateCost('openai/gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(
|
|
481
|
+
expect(ModelMix.calculateCost('gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(38);
|
|
482
|
+
expect(ModelMix.calculateCost('openai/gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(38);
|
|
442
483
|
expect(ModelMix.calculateCost('gpt-5.6-terra', { input: 1_000_000, output: 1_000_000 })).to.equal(22);
|
|
443
484
|
expect(ModelMix.calculateCost('gpt-5.6-luna', { input: 1_000_000, output: 1_000_000 })).to.equal(2.2);
|
|
444
485
|
expect(ModelMix.calculateCost('openai/gpt-5.3-chat', { input: 1_000_000, output: 1_000_000 })).to.equal(15.75);
|
|
@@ -706,20 +747,19 @@ describe('Token Usage Tracking', () => {
|
|
|
706
747
|
}
|
|
707
748
|
});
|
|
708
749
|
|
|
709
|
-
it('should register
|
|
750
|
+
it('should register Kimi K2.6 through the current OpenRouter model ID', function () {
|
|
710
751
|
const originalOpenRouterApiKey = process.env.OPENROUTER_API_KEY;
|
|
711
752
|
process.env.OPENROUTER_API_KEY = 'test-openrouter-key';
|
|
712
753
|
|
|
713
754
|
try {
|
|
714
|
-
const model = ModelMix.new().
|
|
715
|
-
mix: {
|
|
755
|
+
const model = ModelMix.new().kimiK26({
|
|
756
|
+
mix: { fireworks: false, together: false, openrouter: true }
|
|
716
757
|
});
|
|
717
758
|
|
|
718
759
|
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
719
|
-
'
|
|
720
|
-
'openai/gpt-oss-120b'
|
|
760
|
+
'moonshotai/kimi-k2.6'
|
|
721
761
|
]);
|
|
722
|
-
expect(model.models[
|
|
762
|
+
expect(model.models[0].provider).to.be.instanceOf(MixOpenRouter);
|
|
723
763
|
} finally {
|
|
724
764
|
if (originalOpenRouterApiKey === undefined) delete process.env.OPENROUTER_API_KEY;
|
|
725
765
|
else process.env.OPENROUTER_API_KEY = originalOpenRouterApiKey;
|