modelmix 4.4.18 → 4.4.22

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
@@ -185,7 +185,7 @@ const result = await ModelMix.new({
185
185
  options: { temperature: 0.7 },
186
186
  config: { system: "You are a helpful assistant" }
187
187
  })
188
- .sonnet37()
188
+ .sonnet46()
189
189
  .addText("Tell me a story about a cat");
190
190
  .message();
191
191
  ```
package/demo/fallback.js CHANGED
@@ -15,7 +15,7 @@ const mmix = new ModelMix({
15
15
  }
16
16
  });
17
17
 
18
- mmix.sonnet37({ config: { url: 'fail' } }).gpt41nano();
18
+ mmix.sonnet46({ config: { url: 'fail' } }).gpt41nano();
19
19
 
20
20
  async function main() {
21
21
  mmix.addText('hola, como estas?');
package/demo/json.js CHANGED
@@ -6,7 +6,6 @@ const model = await ModelMix.new({ options: { max_tokens: 10000 }, config: { deb
6
6
  // .gptOss()
7
7
  // .scout({ config: { temperature: 0 } })
8
8
  // .o4mini()
9
- // .sonnet37think()
10
9
  // .gpt45()
11
10
  // .gemini25flash()
12
11
  .addText("Name and capital of 3 South American countries.")
package/index.js CHANGED
@@ -343,13 +343,6 @@ class ModelMix {
343
343
  options = { ...MixAnthropic.thinkingOptions, ...options };
344
344
  return this.attach('claude-sonnet-4-5-20250929', new MixAnthropic({ options, config }));
345
345
  }
346
- sonnet37({ options = {}, config = {} } = {}) {
347
- return this.attach('claude-3-7-sonnet-20250219', new MixAnthropic({ options, config }));
348
- }
349
- sonnet37think({ options = {}, config = {} } = {}) {
350
- options = { ...MixAnthropic.thinkingOptions, ...options };
351
- return this.attach('claude-3-7-sonnet-20250219', new MixAnthropic({ options, config }));
352
- }
353
346
  haiku35({ options = {}, config = {} } = {}) {
354
347
  return this.attach('claude-3-5-haiku-20241022', new MixAnthropic({ options, config }));
355
348
  }
@@ -1341,7 +1334,34 @@ class MixCustom {
1341
1334
  }
1342
1335
 
1343
1336
  static extractMessage(data) {
1344
- const message = data.choices[0].message?.content?.trim() || '';
1337
+ const choice = data?.choices?.[0] || {};
1338
+ const messageObj = choice.message || {};
1339
+ const finishReason = choice.finish_reason;
1340
+
1341
+ if (typeof messageObj.refusal === 'string' && messageObj.refusal.trim().length > 0) {
1342
+ throw new Error(`OpenAI model refused to process this request: ${messageObj.refusal}`);
1343
+ }
1344
+
1345
+ if (finishReason === 'content_filter') {
1346
+ throw new Error('OpenAI response was blocked by content_filter.');
1347
+ }
1348
+
1349
+ let message = '';
1350
+ if (typeof messageObj.content === 'string') {
1351
+ message = messageObj.content.trim();
1352
+ } else if (Array.isArray(messageObj.content)) {
1353
+ const refusalPart = messageObj.content.find(part => part?.type === 'refusal' || (typeof part?.refusal === 'string' && part.refusal.trim().length > 0));
1354
+ if (refusalPart) {
1355
+ const refusalText = typeof refusalPart.refusal === 'string' ? refusalPart.refusal : 'No refusal text provided.';
1356
+ throw new Error(`OpenAI model refused to process this request: ${refusalText}`);
1357
+ }
1358
+ message = messageObj.content
1359
+ .filter(part => typeof part?.text === 'string')
1360
+ .map(part => part.text)
1361
+ .join('')
1362
+ .trim();
1363
+ }
1364
+
1345
1365
  const endTagIndex = message.indexOf('</think>');
1346
1366
  if (message.startsWith('<think>') && endTagIndex !== -1) {
1347
1367
  return message.substring(endTagIndex + 8).trim();
@@ -2027,10 +2047,25 @@ class MixAnthropic extends MixCustom {
2027
2047
  }
2028
2048
 
2029
2049
  static extractMessage(data) {
2030
- if (data.content?.[1]?.text) {
2031
- return data.content[1].text;
2050
+ const content = Array.isArray(data?.content) ? data.content : [];
2051
+
2052
+ // Anthropic can return text in different positions depending on thinking/tool blocks.
2053
+ const textBlock = content.find(block => typeof block?.text === 'string' && block.text.trim().length > 0);
2054
+ if (textBlock) {
2055
+ return textBlock.text;
2056
+ }
2057
+
2058
+ // Empty/non-text content is often due to safety refusal or token limits.
2059
+ const stopReason = data?.stop_reason;
2060
+ const contentTypes = content.map(block => block?.type || 'unknown').join(', ') || 'none';
2061
+
2062
+ if (stopReason === 'refusal') {
2063
+ throw new Error('Anthropic refused to process this request (content policy). Try different wording or a fallback model.');
2064
+ }
2065
+ if (!content.length) {
2066
+ throw new Error(`Anthropic returned empty content (stop_reason: ${stopReason ?? 'unknown'}).`);
2032
2067
  }
2033
- return data.content[0].text;
2068
+ throw new Error(`Anthropic content blocks are missing .text (stop_reason: ${stopReason ?? 'unknown'}, content_types: ${contentTypes}).`);
2034
2069
  }
2035
2070
 
2036
2071
  static extractThink(data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "4.4.18",
3
+ "version": "4.4.22",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -96,9 +96,9 @@ If `sonnet46` fails, it automatically tries `gpt52`, then `gemini3flash`.
96
96
  `gpt52()` `gpt52chat()` `gpt51()` `gpt5()` `gpt5mini()` `gpt5nano()` `gpt45()` `gpt41()` `gpt41mini()` `gpt41nano()` `o3()` `o4mini()`
97
97
 
98
98
  ### Anthropic
99
- `opus46()` `opus45()` `opus41()` `sonnet46()` `sonnet45()` `sonnet4()` `sonnet37()` `haiku45()` `haiku35()`
99
+ `opus46()` `opus45()` `opus41()` `sonnet46()` `sonnet45()` `sonnet4()` `haiku45()` `haiku35()`
100
100
 
101
- Thinking variants: append `think` — e.g. `opus46think()` `sonnet46think()` `sonnet45think()` `sonnet4think()` `sonnet37think()` `opus45think()` `opus41think()` `haiku45think()`
101
+ Thinking variants: append `think` — e.g. `opus46think()` `sonnet46think()` `sonnet45think()` `sonnet4think()` `opus45think()` `opus41think()` `haiku45think()`
102
102
 
103
103
  ### Google
104
104
  `gemini3pro()` `gemini3flash()` `gemini25pro()` `gemini25flash()`
@@ -1,12 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(node:*)",
5
- "Bash(npm install:*)",
6
- "Bash(mkdir:*)",
7
- "Bash(npm test)",
8
- "Bash(npm test:*)"
9
- ],
10
- "deny": []
11
- }
12
- }