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.
@@ -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
- expect(gpt56Request.input[0].content[0].prompt_cache_breakpoint).to.deep.equal(breakpoint);
274
- expect(gpt56Request.input[1].content[0]).to.deep.equal({
275
- type: 'input_image',
276
- image_url: 'data:image/png;base64,AAAA',
277
- prompt_cache_breakpoint: breakpoint
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(55);
441
- expect(ModelMix.calculateCost('openai/gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(55);
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 GPT-OSS 120B through the current OpenRouter model ID', function () {
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().gptOss({
715
- mix: { cerebras: false, groq: true, openrouter: true, together: false }
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
- 'openai/gpt-oss-120b',
720
- 'openai/gpt-oss-120b'
760
+ 'moonshotai/kimi-k2.6'
721
761
  ]);
722
- expect(model.models[1].provider).to.be.instanceOf(MixOpenRouter);
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;