modelmix 4.4.24 → 4.4.28

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 CHANGED
@@ -159,7 +159,7 @@ Here's a comprehensive list of available methods:
159
159
  | `grok41[think]()` | Grok | grok-4-1-fast | [\$0.20 / \$0.50][6] |
160
160
  | `deepseekV32()` | Fireworks | fireworks/models/deepseek-v3p2 | [\$0.56 / \$1.68][10] |
161
161
  | `GLM47()` | Fireworks | fireworks/models/glm-4p7 | [\$0.55 / \$2.19][10] |
162
- | `minimaxM25()` | MiniMax | MiniMax-M2.5 | [\$0.30 / \$1.20][9] |
162
+ | `minimaxM27()` | MiniMax | MiniMax-M2.7 | [\$0.30 / \$1.20][9] |
163
163
  | `sonar()` | Perplexity | sonar | [\$1.00 / \$1.00][4] |
164
164
  | `sonarPro()` | Perplexity | sonar-pro | [\$3.00 / \$15.00][4] |
165
165
  | `hermes3()` | Lambda | Hermes-3-Llama-3.1-405B-FP8 | [\$0.80 / \$0.80][8] |
package/demo/json.js CHANGED
@@ -2,7 +2,7 @@ import { ModelMix } from '../index.js';
2
2
  try { process.loadEnvFile(); } catch {}
3
3
 
4
4
  const model = await ModelMix.new({ options: { max_tokens: 10000 }, config: { debug: 3 } })
5
- .sonnet46()
5
+ .gpt51()
6
6
  // .gptOss()
7
7
  // .scout({ config: { temperature: 0 } })
8
8
  // .o4mini()
package/demo/minimax.js CHANGED
@@ -5,7 +5,7 @@ const main = async () => {
5
5
 
6
6
  const bot = ModelMix
7
7
  .new({ config: { debug: 3 } })
8
- .minimaxM25()
8
+ .minimaxM27()
9
9
  .setSystem('You are a helpful assistant.');
10
10
 
11
11
  bot.addText('What is the capital of France?');
package/index.js CHANGED
@@ -61,7 +61,9 @@ const MODEL_PRICING = {
61
61
  // MiniMax
62
62
  'MiniMax-M2.1': [0.30, 1.20],
63
63
  'MiniMax-M2.5': [0.30, 1.20],
64
+ 'MiniMax-M2.7': [0.30, 1.20],
64
65
  'fireworks/minimax-m2p5': [0.30, 1.20],
66
+ 'minimax/minimax-m2.7': [0.30, 1.20],
65
67
  // Perplexity
66
68
  'sonar': [1.00, 1.00],
67
69
  'sonar-pro': [3.00, 15.00],
@@ -478,6 +480,12 @@ class ModelMix {
478
480
  return this.attach('MiniMax-M2-Stable', new MixMiniMax({ options, config }));
479
481
  }
480
482
 
483
+ minimaxM27({ options = {}, config = {}, mix = { openrouter: true, minimax: true } } = {}) {
484
+ if (mix.openrouter) return this.attach('minimax/minimax-m2.7', new MixOpenRouter({ options, config }));
485
+ if (mix.minimax) return this.attach('MiniMax-M2.7', new MixMiniMax({ options, config }));
486
+ return this;
487
+ }
488
+
481
489
  deepseekV32({ options = {}, config = {}, mix = {} } = {}) {
482
490
  mix = { ...this.mix, ...mix };
483
491
  if (mix.fireworks) this.attach('accounts/fireworks/models/deepseek-v3p2', new MixFireworks({ options, config }));
@@ -1566,7 +1574,7 @@ class MixOpenAIResponses extends MixOpenAI {
1566
1574
  }
1567
1575
 
1568
1576
  const responsesUrl = this.config.url.replace('/chat/completions', '/responses');
1569
- const request = MixOpenAIResponses.buildResponsesRequest(options);
1577
+ const request = MixOpenAIResponses.buildResponsesRequest(options, config);
1570
1578
  const response = await axios.post(responsesUrl, request, {
1571
1579
  headers: this.headers
1572
1580
  });
@@ -1574,16 +1582,38 @@ class MixOpenAIResponses extends MixOpenAI {
1574
1582
  return MixOpenAIResponses.processResponsesResponse(response);
1575
1583
  }
1576
1584
 
1577
- static buildResponsesRequest(options = {}) {
1585
+ static buildResponsesRequest(options = {}, config = {}) {
1586
+ const input = MixOpenAIResponses.messagesToResponsesInput(options.messages);
1587
+ if (config.system) {
1588
+ input.unshift({ role: 'developer', content: [{ type: 'input_text', text: config.system }] });
1589
+ }
1578
1590
  const request = {
1579
1591
  model: options.model,
1580
- input: MixOpenAIResponses.messagesToResponsesInput(options.messages),
1592
+ input,
1581
1593
  stream: false
1582
1594
  };
1583
1595
 
1584
1596
  if (options.reasoning_effort) request.reasoning = { effort: options.reasoning_effort };
1585
1597
  if (options.verbosity) request.text = { verbosity: options.verbosity };
1586
1598
 
1599
+ if (options.response_format) {
1600
+ const rf = options.response_format;
1601
+ let format;
1602
+ if (rf.type === 'json_schema' && rf.json_schema) {
1603
+ format = {
1604
+ type: 'json_schema',
1605
+ name: rf.json_schema.name || 'response',
1606
+ strict: true,
1607
+ schema: rf.json_schema.schema
1608
+ };
1609
+ } else if (rf.type) {
1610
+ format = { type: rf.type };
1611
+ }
1612
+ if (format) {
1613
+ request.text = { ...request.text, format };
1614
+ }
1615
+ }
1616
+
1587
1617
  if (typeof options.max_completion_tokens === 'number') {
1588
1618
  request.max_output_tokens = options.max_completion_tokens;
1589
1619
  } else if (typeof options.max_tokens === 'number') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "4.4.24",
3
+ "version": "4.4.28",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "repository": {
package/test/json.test.js CHANGED
@@ -376,6 +376,42 @@ describe('JSON Schema and Structured Output Tests', () => {
376
376
  expect(result.countries[0]).to.have.property('capital');
377
377
  });
378
378
 
379
+ it('should send JSON mode and system instructions in Responses API request', async () => {
380
+ const example = {
381
+ countries: [{
382
+ name: 'France',
383
+ capital: 'Paris'
384
+ }]
385
+ };
386
+
387
+ model.gpt51().addText('Name and capital of 3 South American countries.');
388
+
389
+ let capturedBody;
390
+ nock('https://api.openai.com')
391
+ .post('/v1/responses', (body) => {
392
+ capturedBody = body;
393
+ return true;
394
+ })
395
+ .reply(200, testUtils.createMockResponse('openai-responses', JSON.stringify({
396
+ countries: [
397
+ { name: 'Argentina', capital: 'BUENOS AIRES' },
398
+ { name: 'Brazil', capital: 'BRASILIA' },
399
+ { name: 'Colombia', capital: 'BOGOTA' }
400
+ ]
401
+ })));
402
+
403
+ const result = await model.json(example);
404
+
405
+ expect(result.countries).to.be.an('array');
406
+ expect(result.countries).to.have.length(3);
407
+ expect(capturedBody).to.be.an('object');
408
+ expect(capturedBody.text).to.be.an('object');
409
+ expect(capturedBody.text.format).to.deep.equal({ type: 'json_object' });
410
+ expect(capturedBody.input).to.be.an('array').that.is.not.empty;
411
+ expect(capturedBody.input[0].role).to.equal('developer');
412
+ expect(capturedBody.input[0].content[0].text).to.include('JSON');
413
+ });
414
+
379
415
  it('should handle complex nested JSON schema', async () => {
380
416
  const example = {
381
417
  user: {