modelmix 5.3.0 → 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.
@@ -117,13 +117,9 @@ describe('Provider Fallback Chain Tests', () => {
117
117
  ['gpt6astra', 'gpt-6-astra', 'openai/gpt-6-astra', MixOpenAIResponses],
118
118
  ['gpt6sol', 'gpt-6-sol', 'openai/gpt-6-sol', MixOpenAIResponses],
119
119
  ['gpt6luna', 'gpt-6-luna', 'openai/gpt-6-luna', MixOpenAIResponses],
120
- ['gpt5', 'gpt-5', 'openai/gpt-5', MixOpenAI],
121
120
  ['gpt5mini', 'gpt-5-mini', 'openai/gpt-5-mini', MixOpenAI],
122
121
  ['gpt5nano', 'gpt-5-nano', 'openai/gpt-5-nano', MixOpenAI],
123
- ['gpt51', 'gpt-5.1', 'openai/gpt-5.1', MixOpenAIResponses],
124
122
  ['gpt52', 'gpt-5.2', 'openai/gpt-5.2', MixOpenAIResponses],
125
- ['gpt53codex', 'gpt-5.3-codex', 'openai/gpt-5.3-codex', MixOpenAIResponses],
126
- ['gpt53chat', 'gpt-5.3-chat-latest', 'openai/gpt-5.3-chat', MixOpenAIResponses],
127
123
  ['gpt54', 'gpt-5.4', 'openai/gpt-5.4', MixOpenAIResponses],
128
124
  ['gpt54mini', 'gpt-5.4-mini', 'openai/gpt-5.4-mini', MixOpenAIResponses],
129
125
  ['gpt54nano', 'gpt-5.4-nano', 'openai/gpt-5.4-nano', MixOpenAIResponses],
@@ -153,10 +149,10 @@ describe('Provider Fallback Chain Tests', () => {
153
149
  .gpt56sol({ mix: { openrouter: false } });
154
150
  expect(locallyDisabled.models.map(({ key }) => key)).to.deep.equal(['gpt-5.6-sol']);
155
151
 
156
- const inherited = ModelMix.new({ mix: { openrouter: true } }).new().gpt5();
152
+ const inherited = ModelMix.new({ mix: { openrouter: true } }).new().gpt5mini();
157
153
  expect(inherited.models.map(({ key }) => key)).to.deep.equal([
158
- 'gpt-5',
159
- 'openai/gpt-5'
154
+ 'gpt-5-mini',
155
+ 'openai/gpt-5-mini'
160
156
  ]);
161
157
  });
162
158
 
@@ -309,7 +305,7 @@ describe('Provider Fallback Chain Tests', () => {
309
305
  });
310
306
 
311
307
  it('should use primary provider when available', async () => {
312
- model.gpt5mini().sonnet46().addText('Hello');
308
+ model.gpt5mini().sonnet5().addText('Hello');
313
309
 
314
310
  // Mock successful OpenAI response
315
311
  nock('https://api.openai.com')
@@ -329,7 +325,7 @@ describe('Provider Fallback Chain Tests', () => {
329
325
  });
330
326
 
331
327
  it('should fallback to secondary provider when primary fails', async () => {
332
- model.gpt5mini().sonnet46().addText('Hello');
328
+ model.gpt5mini().sonnet5().addText('Hello');
333
329
 
334
330
  // Mock failed OpenAI response (GPT-5 mini)
335
331
  nock('https://api.openai.com')
@@ -443,7 +439,7 @@ describe('Provider Fallback Chain Tests', () => {
443
439
  });
444
440
 
445
441
  it('should cascade through multiple fallbacks', async () => {
446
- model.gpt5mini().sonnet46().gemini37flash().addText('Hello');
442
+ model.gpt5mini().sonnet5().gemini37flash().addText('Hello');
447
443
 
448
444
  // Mock failed OpenAI response
449
445
  nock('https://api.openai.com')
@@ -474,7 +470,7 @@ describe('Provider Fallback Chain Tests', () => {
474
470
  });
475
471
 
476
472
  it('should throw error when all providers fail', async () => {
477
- model.gpt5mini().sonnet46().addText('Hello');
473
+ model.gpt5mini().sonnet5().addText('Hello');
478
474
 
479
475
  // Mock all providers failing
480
476
  nock('https://api.openai.com')
@@ -504,7 +500,7 @@ describe('Provider Fallback Chain Tests', () => {
504
500
  });
505
501
 
506
502
  it('should fallback from OpenAI to Anthropic', async () => {
507
- model.gpt5mini().sonnet46().addText('Test message');
503
+ model.gpt5mini().sonnet5().addText('Test message');
508
504
 
509
505
  // Mock OpenAI failure
510
506
  nock('https://api.openai.com')
@@ -527,7 +523,7 @@ describe('Provider Fallback Chain Tests', () => {
527
523
  });
528
524
 
529
525
  it('should fallback from Anthropic to Google', async () => {
530
- model.sonnet46().gemini37flash().addText('Test message');
526
+ model.sonnet5().gemini37flash().addText('Test message');
531
527
 
532
528
  // Mock Anthropic failure
533
529
  nock('https://api.anthropic.com')
@@ -553,7 +549,7 @@ describe('Provider Fallback Chain Tests', () => {
553
549
  });
554
550
 
555
551
  it('should handle network timeout fallback', async () => {
556
- model.gpt5mini().sonnet46().addText('Hello');
552
+ model.gpt5mini().sonnet5().addText('Hello');
557
553
 
558
554
  // Mock timeout error on first provider (using 408 Request Timeout)
559
555
  nock('https://api.openai.com')
@@ -587,7 +583,7 @@ describe('Provider Fallback Chain Tests', () => {
587
583
 
588
584
  it('should handle JSON fallback correctly', async () => {
589
585
  const schema = { name: 'Alice', age: 30 };
590
- model.gpt5mini().sonnet46().addText('Generate user data');
586
+ model.gpt5mini().sonnet5().addText('Generate user data');
591
587
 
592
588
  // Mock OpenAI failure
593
589
  nock('https://api.openai.com')
@@ -613,7 +609,7 @@ describe('Provider Fallback Chain Tests', () => {
613
609
  });
614
610
 
615
611
  it('should preserve message history through fallbacks', async () => {
616
- model.gpt5mini().sonnet46()
612
+ model.gpt5mini().sonnet5()
617
613
  .addText('First message')
618
614
  .addText('Second message');
619
615
 
@@ -644,7 +640,7 @@ describe('Provider Fallback Chain Tests', () => {
644
640
  config: { debug: false, retry: { enabled: false } }
645
641
  });
646
642
 
647
- model.gpt5mini().sonnet46().addText('Hello');
643
+ model.gpt5mini().sonnet5().addText('Hello');
648
644
 
649
645
  nock('https://api.openai.com')
650
646
  .post('/v1/chat/completions')
@@ -692,7 +688,7 @@ describe('Provider Fallback Chain Tests', () => {
692
688
  config: { debug: false, retry: { enabled: true, retries: 2, baseDelayMs: 0, maxDelayMs: 0 } }
693
689
  });
694
690
 
695
- model.gpt5mini().sonnet46().addText('Hello');
691
+ model.gpt5mini().sonnet5().addText('Hello');
696
692
 
697
693
  nock('https://api.openai.com')
698
694
  .post('/v1/chat/completions')
@@ -716,7 +712,7 @@ describe('Provider Fallback Chain Tests', () => {
716
712
  config: { debug: false, retry: { enabled: true, retries: 1, baseDelayMs: 0, maxDelayMs: 0 } }
717
713
  });
718
714
 
719
- model.gpt5mini().sonnet46().addText('Hello');
715
+ model.gpt5mini().sonnet5().addText('Hello');
720
716
 
721
717
  nock('https://api.openai.com')
722
718
  .post('/v1/chat/completions')
@@ -746,7 +742,7 @@ describe('Provider Fallback Chain Tests', () => {
746
742
 
747
743
  // Configure with custom temperature for fallback
748
744
  model.gpt5mini({ options: { temperature: 0.6 } })
749
- .sonnet46({ options: { temperature: 0.7 } })
745
+ .sonnet45({ options: { temperature: 0.7 } })
750
746
  .addText('Creative response');
751
747
 
752
748
  // Mock first provider failure
@@ -779,7 +775,7 @@ describe('Provider Fallback Chain Tests', () => {
779
775
  });
780
776
 
781
777
  model.gpt5mini({ options: { max_tokens: 100 } })
782
- .sonnet46({ options: { max_tokens: 200 } })
778
+ .sonnet5({ options: { max_tokens: 200 } })
783
779
  .addText('Generate text');
784
780
 
785
781
  // Mock OpenAI failure
@@ -913,7 +909,7 @@ describe('Provider Fallback Chain Tests', () => {
913
909
  });
914
910
 
915
911
  it('should provide detailed error information when all fallbacks fail', async () => {
916
- model.gpt5mini().sonnet46().gemini37flash().addText('Test');
912
+ model.gpt5mini().sonnet5().gemini37flash().addText('Test');
917
913
 
918
914
  // Mock all providers failing with different errors
919
915
  nock('https://api.openai.com')
@@ -938,7 +934,7 @@ describe('Provider Fallback Chain Tests', () => {
938
934
  });
939
935
 
940
936
  it('should handle malformed responses in fallback', async () => {
941
- model.gpt5mini().sonnet46().addText('Test');
937
+ model.gpt5mini().sonnet5().addText('Test');
942
938
 
943
939
  // Mock malformed response from first provider
944
940
  nock('https://api.openai.com')
package/test/grok.test.js CHANGED
@@ -13,10 +13,7 @@ describe('Grok Model Registration Tests', () => {
13
13
  const grokModels = [
14
14
  { method: 'grok47', key: 'grok-4.7' },
15
15
  { method: 'grok46', key: 'grok-4.6' },
16
- { method: 'grok45', key: 'grok-4.5' },
17
- { method: 'grok43', key: 'grok-4.3' },
18
- { method: 'grok420multiAgent', key: 'grok-4.20-multi-agent-0309' },
19
- { method: 'grok420', key: GROK420_ALIAS }
16
+ { method: 'grok43', key: 'grok-4.3' }
20
17
  ];
21
18
 
22
19
  for (const grokModel of grokModels) {
@@ -47,7 +47,7 @@ describe('Conversation History Tests', () => {
47
47
  });
48
48
 
49
49
  it('should add assistant response to message history after raw()', async () => {
50
- model.sonnet46().addText('Hello');
50
+ model.sonnet5().addText('Hello');
51
51
 
52
52
  nock('https://api.anthropic.com')
53
53
  .post('/v1/messages')
@@ -128,7 +128,7 @@ describe('Conversation History Tests', () => {
128
128
  const model = ModelMix.new({
129
129
  config: { debug: false, max_history: 10 }
130
130
  });
131
- model.sonnet46();
131
+ model.sonnet5();
132
132
 
133
133
  // First turn
134
134
  model.addText('Capital of France?');
@@ -51,10 +51,10 @@ describe('Image Processing and Multimodal Support Tests', () => {
51
51
  expect(response).to.include('I can see a small test image');
52
52
  });
53
53
 
54
- it('should support multimodal with sonnet46()', async () => {
54
+ it('should support multimodal with sonnet5()', async () => {
55
55
  const base64Image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mP8z8BQz0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC';
56
56
 
57
- model.sonnet46()
57
+ model.sonnet5()
58
58
  .addText('Describe this image')
59
59
  .addImageFromUrl(base64Image);
60
60
 
@@ -87,7 +87,7 @@ describe('Image Processing and Multimodal Support Tests', () => {
87
87
  const pngBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mP8z8BQz0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC';
88
88
  const pngBuffer = Buffer.from(pngBase64, 'base64');
89
89
 
90
- model.sonnet46()
90
+ model.sonnet5()
91
91
  .addText('Describe this image')
92
92
  .addImageFromUrl(imageUrl);
93
93
 
package/test/json.test.js CHANGED
@@ -412,7 +412,7 @@ describe('JSON Schema and Structured Output Tests', () => {
412
412
  }]
413
413
  };
414
414
 
415
- model.gpt51().addText('Name and capital of 3 South American countries.');
415
+ model.gpt52().addText('Name and capital of 3 South American countries.');
416
416
 
417
417
  let capturedBody;
418
418
  nock('https://api.openai.com')
@@ -462,7 +462,7 @@ describe('JSON Schema and Structured Output Tests', () => {
462
462
  }
463
463
  };
464
464
 
465
- model.sonnet46().addText('Generate user data');
465
+ model.sonnet5().addText('Generate user data');
466
466
 
467
467
  // Mock the API response
468
468
  nock('https://api.anthropic.com')
package/test/live.test.js CHANGED
@@ -180,24 +180,22 @@ describe('Live Integration Tests', function () {
180
180
 
181
181
  describe('Additional Model Tests', function () {
182
182
 
183
- it('should work with GPT-OSS model', async function () {
184
- const model = ModelMix.new(setup).gptOss({
183
+ it('should work with Kimi K2.6 model', async function () {
184
+ const model = ModelMix.new(setup).kimiK26({
185
185
  options: { max_tokens: 128 }
186
186
  });
187
187
 
188
- model.addText('Say "gptoss test successful" and nothing else.');
188
+ model.addText('Say "Kimik26 test successful" and nothing else.');
189
189
 
190
190
  const response = await model.message();
191
- console.log(`GPT-OSS response: ${response}`);
191
+ console.log(`Kimi K2.6 response: ${response}`);
192
192
 
193
193
  expect(response).to.be.a('string');
194
- expect(response.toLowerCase()).to.include('gptoss test successful');
194
+ expect(response.toLowerCase()).to.include('kimik26 test successful');
195
195
  });
196
196
 
197
197
  const grokSeriesTests = [
198
- { name: 'Grok 4.6', factory: (m) => m.grok46(), token: 'grok46' },
199
- { name: 'Grok 4.20 reasoning', factory: (m) => m.effort(50).grok420(), token: 'grok420' },
200
- { name: 'Grok 4.20 non-reasoning', factory: (m) => m.grok420(), token: 'grok420nr' }
198
+ { name: 'Grok 4.6', factory: (m) => m.grok46(), token: 'grok46' }
201
199
  ];
202
200
 
203
201
  for (const grokModel of grokSeriesTests) {
@@ -16,10 +16,10 @@ describe('Provider expansion regressions', () => {
16
16
  process.env.MINIMAX_API_KEY = 'test-minimax-key';
17
17
 
18
18
  try {
19
- const gptOss = ModelMix.new().gptOss();
19
+ const kimi = ModelMix.new().kimiK26();
20
20
  const minimax = ModelMix.new().minimaxM27();
21
21
 
22
- expect(gptOss.models.some(({ provider }) => provider instanceof MixOpenRouter)).to.equal(false);
22
+ expect(kimi.models.some(({ provider }) => provider instanceof MixOpenRouter)).to.equal(false);
23
23
  expect(minimax.models.map(({ key }) => key)).to.deep.equal(['MiniMax-M2.7']);
24
24
  expect(minimax.models[0].provider).to.be.instanceOf(MixMiniMax);
25
25
  } finally {
@@ -28,33 +28,26 @@ describe('Provider expansion regressions', () => {
28
28
  }
29
29
  });
30
30
 
31
- it('should retain every enabled GPT OSS provider, including shared model keys', () => {
32
- const model = ModelMix.new().gptOss({
31
+ it('should retain every enabled Muse Glimmer 30B provider, including shared model keys', () => {
32
+ const model = ModelMix.new().museGlimmer30b({
33
33
  mix: {
34
34
  nvidia: true,
35
35
  fireworks: true,
36
36
  together: true,
37
- cerebras: true,
38
- groq: true,
39
37
  openrouter: true
40
38
  }
41
39
  });
42
40
 
43
41
  expect(model.models.map(({ key }) => key)).to.deep.equal([
44
- 'openai/gpt-oss-120b',
45
- 'accounts/fireworks/models/gpt-oss-120b',
46
- 'openai/gpt-oss-120b',
47
- 'gpt-oss-120b',
48
- 'openai/gpt-oss-120b',
49
- 'openai/gpt-oss-120b'
42
+ 'meta/muse-glimmer-30b',
43
+ 'accounts/fireworks/models/muse-glimmer-30b',
44
+ 'meta/muse-glimmer-30b',
45
+ 'meta-models/Muse-Glimmer-30B'
50
46
  ]);
51
47
  expect(model.models[0].provider).to.be.instanceOf(MixNVIDIA);
52
48
  expect(model.models[1].provider).to.be.instanceOf(MixFireworks);
53
- expect(ModelMix.calculateCost('accounts/fireworks/models/gpt-oss-120b', {
54
- input: 1_000_000,
55
- cached: 500_000,
56
- output: 1_000_000
57
- })).to.equal(0.682);
49
+ expect(model.models[2].provider).to.be.instanceOf(MixOpenRouter);
50
+ expect(model.models[3].provider).to.be.instanceOf(MixTogether);
58
51
  });
59
52
 
60
53
  it('should retain every enabled MiniMax M2.7 provider in fallback order', () => {
@@ -38,7 +38,7 @@ describe('EJS Template and File Operations Tests', () => {
38
38
  describe('EJS rendering', () => {
39
39
  it('renders inline variables with plain data keys', async () => {
40
40
  const model = ModelMix.new()
41
- .gpt51()
41
+ .gpt52()
42
42
  .assign({ name: 'Alice', age: 30, city: 'New York' })
43
43
  .addText('Hello <%- name %>, you are <%- age %> years old and live in <%- city %>.');
44
44
 
@@ -53,7 +53,7 @@ describe('EJS Template and File Operations Tests', () => {
53
53
 
54
54
  it('assigns one template data key', async () => {
55
55
  const model = ModelMix.new()
56
- .gpt51()
56
+ .gpt52()
57
57
  .assignKey('name', 'Martin')
58
58
  .addText('Hello <%- name %>.');
59
59
 
@@ -66,7 +66,7 @@ describe('EJS Template and File Operations Tests', () => {
66
66
 
67
67
  it('supports nested data, conditionals, and loops', async () => {
68
68
  const model = ModelMix.new()
69
- .gpt51()
69
+ .gpt52()
70
70
  .assign({
71
71
  user: {
72
72
  name: 'Charlie',
@@ -86,7 +86,7 @@ describe('EJS Template and File Operations Tests', () => {
86
86
  it('keeps raw and XML-escaped output distinct', async () => {
87
87
  const value = 'Hello & "World" <test>';
88
88
  const model = ModelMix.new()
89
- .gpt51()
89
+ .gpt52()
90
90
  .assign({ value })
91
91
  .addText('Escaped: <%= value %>\nRaw: <%- value %>');
92
92
 
@@ -101,7 +101,7 @@ describe('EJS Template and File Operations Tests', () => {
101
101
 
102
102
  it('does not execute EJS received through template data', async () => {
103
103
  const model = ModelMix.new()
104
- .gpt51()
104
+ .gpt52()
105
105
  .assign({ payload: '<%- secret %>', secret: 'must-not-render' })
106
106
  .addText('Payload: <%- payload %>');
107
107
 
@@ -114,7 +114,7 @@ describe('EJS Template and File Operations Tests', () => {
114
114
 
115
115
  it('selects uniformly when choice options omit weights', async () => {
116
116
  const model = ModelMix.new()
117
- .gpt51()
117
+ .gpt52()
118
118
  .addText(`<% choice %>
119
119
  <% option %>
120
120
  Use emojis.
@@ -134,7 +134,7 @@ Do not use emojis.
134
134
 
135
135
  it('selects weighted options using relative weights', async () => {
136
136
  const model = ModelMix.new()
137
- .gpt51()
137
+ .gpt52()
138
138
  .assign({ language: 'Spanish' })
139
139
  .addText(`<% choice %>
140
140
  <% option 20 %>
@@ -155,7 +155,7 @@ Do not use emojis in <%- language %>.
155
155
 
156
156
  it('supports nested choices', async () => {
157
157
  const model = ModelMix.new()
158
- .gpt51()
158
+ .gpt52()
159
159
  .addText(`<% choice %>
160
160
  <% option %>
161
161
  Tone:
@@ -187,7 +187,7 @@ first
187
187
  <% option %>
188
188
  second
189
189
  <% /choice %>`;
190
- const model = ModelMix.new().gpt51().addText(template);
190
+ const model = ModelMix.new().gpt52().addText(template);
191
191
  const random = sinon.stub(model, '_choiceRandom');
192
192
  random.onFirstCall().returns(0.1);
193
193
  random.onSecondCall().returns(0.9);
@@ -208,7 +208,7 @@ second
208
208
 
209
209
  it('fails before the request when a variable is missing', async () => {
210
210
  const model = ModelMix.new()
211
- .gpt51()
211
+ .gpt52()
212
212
  .assign({ name: 'David' })
213
213
  .addText('Hello <%- name %>, status: <%- status %>');
214
214
 
@@ -225,7 +225,7 @@ second
225
225
  });
226
226
 
227
227
  it('rejects invalid template data immediately', () => {
228
- const model = ModelMix.new().gpt51();
228
+ const model = ModelMix.new().gpt52();
229
229
 
230
230
  expect(() => model.assign(null)).to.throw(TypeError, 'Template data must be a plain non-null object.');
231
231
  expect(() => model.assign(undefined)).to.throw(TypeError, 'Template data must be a plain non-null object.');
@@ -278,7 +278,7 @@ second
278
278
  ];
279
279
 
280
280
  for (const testCase of cases) {
281
- const model = ModelMix.new().gpt51().addText(testCase.source);
281
+ const model = ModelMix.new().gpt52().addText(testCase.source);
282
282
  let error;
283
283
  try {
284
284
  await model.message();
@@ -293,7 +293,7 @@ second
293
293
 
294
294
  it('rerolls earlier choices after a later template fails to render', async () => {
295
295
  const model = ModelMix.new()
296
- .gpt51()
296
+ .gpt52()
297
297
  .addText(`<% choice %>
298
298
  <% option %>
299
299
  A
@@ -333,7 +333,7 @@ A
333
333
  <% option %>
334
334
  B
335
335
  <% /choice %>`;
336
- const model = ModelMix.new().gpt51().addText(template);
336
+ const model = ModelMix.new().gpt52().addText(template);
337
337
  const random = sinon.stub(model, '_choiceRandom');
338
338
  random.onFirstCall().returns(0.1);
339
339
  random.onSecondCall().returns(0.9);
@@ -370,7 +370,7 @@ B
370
370
  max_history: 10,
371
371
  bottleneck: { maxConcurrent: 2, minTime: 0 }
372
372
  }
373
- }).gpt51().addText(template);
373
+ }).gpt52().addText(template);
374
374
  const content = model.messages[0].content[0];
375
375
  const random = sinon.stub(model, '_choiceRandom');
376
376
  random.onFirstCall().returns(0.1);
@@ -396,7 +396,7 @@ B
396
396
  describe('File templates and data', () => {
397
397
  it('renders a file template with a relative include', async () => {
398
398
  const model = ModelMix.new()
399
- .gpt51()
399
+ .gpt52()
400
400
  .assign({
401
401
  name: 'Eve',
402
402
  platform: 'ModelMix',
@@ -423,7 +423,7 @@ B
423
423
 
424
424
  it('resolves a dynamic include path relative to its template', async () => {
425
425
  const model = ModelMix.new()
426
- .gpt51()
426
+ .gpt52()
427
427
  .assign({ rulesFile: 'system-rules.txt', language: 'Spanish' })
428
428
  .addTextFromFile(path.join(fixturesPath, 'dynamic-include.txt'));
429
429
 
@@ -436,7 +436,7 @@ B
436
436
 
437
437
  it('processes choice directives inside relative includes', async () => {
438
438
  const model = ModelMix.new()
439
- .gpt51()
439
+ .gpt52()
440
440
  .addTextFromFile(path.join(fixturesPath, 'choice-template.txt'));
441
441
  sinon.stub(model, '_choiceRandom').returns(0.75);
442
442
 
@@ -449,7 +449,7 @@ B
449
449
 
450
450
  it('supports recursive includes with an explicit depth limit', async () => {
451
451
  const model = ModelMix.new()
452
- .gpt51()
452
+ .gpt52()
453
453
  .assign({
454
454
  node: {
455
455
  text: 'Root',
@@ -481,7 +481,7 @@ B
481
481
  const base = ModelMix.new()
482
482
  .setSystemFromFile(path.join(fixturesPath, 'system-template.txt'))
483
483
  .assign({ role: 'data analyst', language: 'Spanish' });
484
- const model = base.new().gpt51().addText('Analyze this.');
484
+ const model = base.new().gpt52().addText('Analyze this.');
485
485
 
486
486
  mockOpenAI(body => {
487
487
  const system = body.input.find(message => message.role === 'developer');
@@ -494,7 +494,7 @@ B
494
494
 
495
495
  it('renders assigned files through EJS includes, including their relative includes', async () => {
496
496
  const model = ModelMix.new()
497
- .gpt51()
497
+ .gpt52()
498
498
  .assign({
499
499
  name: 'Eve',
500
500
  platform: 'ModelMix',
@@ -523,7 +523,7 @@ B
523
523
  const base = ModelMix.new()
524
524
  .assign({ language: 'Spanish' })
525
525
  .assignKeyFromFile('rules', path.join(fixturesPath, 'system-rules.txt'));
526
- const model = base.new().gpt51().addText('Rules:\n<%- rules %>');
526
+ const model = base.new().gpt52().addText('Rules:\n<%- rules %>');
527
527
 
528
528
  mockOpenAI(body => {
529
529
  expect(userTexts(body)[0].trim()).to.equal('Rules:\nAlways respond in Spanish.');
@@ -534,7 +534,7 @@ B
534
534
 
535
535
  it('uses the latest assignment when a plain value and a file share a key', async () => {
536
536
  const plainValue = ModelMix.new()
537
- .gpt51()
537
+ .gpt52()
538
538
  .assign({ language: 'Spanish' })
539
539
  .assignKeyFromFile('rules', path.join(fixturesPath, 'system-rules.txt'))
540
540
  .assignKey('rules', 'Use the plain value.')
@@ -546,7 +546,7 @@ B
546
546
  await plainValue.message();
547
547
 
548
548
  const fileValue = ModelMix.new()
549
- .gpt51()
549
+ .gpt52()
550
550
  .assign({ language: 'Spanish', rules: 'Ignore this value.' })
551
551
  .assignKeyFromFile('rules', path.join(fixturesPath, 'system-rules.txt'))
552
552
  .addText('<%- rules %>');
@@ -559,7 +559,7 @@ B
559
559
 
560
560
  it('injects JSON file contents without XML escaping', async () => {
561
561
  const model = ModelMix.new()
562
- .gpt51()
562
+ .gpt52()
563
563
  .assignKeyFromFile('data', path.join(fixturesPath, 'data.json'))
564
564
  .addText('Process this data:\n<%- data %>');
565
565
 
@@ -580,7 +580,7 @@ B
580
580
  try {
581
581
  fs.writeFileSync(assignedFile, 'Version one for <%- name %>.');
582
582
  const model = ModelMix.new()
583
- .gpt51()
583
+ .gpt52()
584
584
  .assign({ name: 'Eve' })
585
585
  .assignKeyFromFile('content', assignedFile)
586
586
  .addText('<%- content %>');
@@ -602,7 +602,7 @@ B
602
602
  });
603
603
 
604
604
  it('throws immediately when a template or data file is missing', () => {
605
- const model = ModelMix.new().gpt51();
605
+ const model = ModelMix.new().gpt52();
606
606
  const missingPath = path.join(fixturesPath, 'nonexistent.txt');
607
607
 
608
608
  expect(() => model.addTextFromFile(missingPath)).to.throw(`File not found: ${missingPath}`);
@@ -622,7 +622,7 @@ B
622
622
  it('renders system and message templates for JSON output', async () => {
623
623
  const schema = { summary: 'Analysis summary', userCount: 0 };
624
624
  const model = ModelMix.new()
625
- .gpt51()
625
+ .gpt52()
626
626
  .setSystem('You are a <%- role %>.')
627
627
  .assign({ role: 'data analyst', instruction: 'Count active users' })
628
628
  .assignKeyFromFile('data', path.join(fixturesPath, 'data.json'))
@@ -653,7 +653,7 @@ B
653
653
 
654
654
  it('renders the system before adding block instructions', async () => {
655
655
  const model = ModelMix.new()
656
- .gpt51()
656
+ .gpt52()
657
657
  .setSystem('Act as <%- role %>.')
658
658
  .assign({ role: 'reviewer' })
659
659
  .addText('Review this.');
@@ -670,7 +670,7 @@ B
670
670
 
671
671
  it('keeps rendered history snapshots when template data changes', async () => {
672
672
  const model = ModelMix.new({ config: { max_history: 10 } })
673
- .gpt51()
673
+ .gpt52()
674
674
  .assign({ name: 'Alice' })
675
675
  .addText('Hello <%- name %>.');
676
676
 
@@ -689,8 +689,8 @@ B
689
689
  it('keeps a system choice stable across provider fallback', async () => {
690
690
  const systems = [];
691
691
  const model = ModelMix.new()
692
- .gpt51()
693
- .sonnet46()
692
+ .gpt52()
693
+ .sonnet5()
694
694
  .setSystem(`<% choice %>
695
695
  <% option %>
696
696
  First system.
@@ -747,20 +747,19 @@ describe('Token Usage Tracking', () => {
747
747
  }
748
748
  });
749
749
 
750
- 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 () {
751
751
  const originalOpenRouterApiKey = process.env.OPENROUTER_API_KEY;
752
752
  process.env.OPENROUTER_API_KEY = 'test-openrouter-key';
753
753
 
754
754
  try {
755
- const model = ModelMix.new().gptOss({
756
- mix: { cerebras: false, groq: true, openrouter: true, together: false }
755
+ const model = ModelMix.new().kimiK26({
756
+ mix: { fireworks: false, together: false, openrouter: true }
757
757
  });
758
758
 
759
759
  expect(model.models.map(({ key }) => key)).to.deep.equal([
760
- 'openai/gpt-oss-120b',
761
- 'openai/gpt-oss-120b'
760
+ 'moonshotai/kimi-k2.6'
762
761
  ]);
763
- expect(model.models[1].provider).to.be.instanceOf(MixOpenRouter);
762
+ expect(model.models[0].provider).to.be.instanceOf(MixOpenRouter);
764
763
  } finally {
765
764
  if (originalOpenRouterApiKey === undefined) delete process.env.OPENROUTER_API_KEY;
766
765
  else process.env.OPENROUTER_API_KEY = originalOpenRouterApiKey;