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 +1 -1
- package/demo/fallback.js +1 -1
- package/demo/json.js +0 -1
- package/index.js +46 -11
- package/package.json +1 -1
- package/skills/modelmix/SKILL.md +2 -2
- package/.claude/settings.local.json +0 -12
package/README.md
CHANGED
package/demo/fallback.js
CHANGED
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
|
|
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
|
-
|
|
2031
|
-
|
|
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
|
-
|
|
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
package/skills/modelmix/SKILL.md
CHANGED
|
@@ -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()` `
|
|
99
|
+
`opus46()` `opus45()` `opus41()` `sonnet46()` `sonnet45()` `sonnet4()` `haiku45()` `haiku35()`
|
|
100
100
|
|
|
101
|
-
Thinking variants: append `think` — e.g. `opus46think()` `sonnet46think()` `sonnet45think()` `sonnet4think()` `
|
|
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()`
|