modelmix 4.3.2 → 4.3.6
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 +159 -69
- package/demo/custom.js +1 -1
- package/demo/demo.js +2 -2
- package/demo/free.js +1 -1
- package/demo/gpt51.js +1 -1
- package/demo/grok.js +1 -1
- package/demo/images.js +1 -1
- package/demo/json.js +16 -4
- package/demo/mcp-simple.js +1 -1
- package/demo/minimax.js +1 -1
- package/demo/parallel.js +1 -1
- package/demo/repl-powers.js +1 -1
- package/demo/short.js +1 -1
- package/index.js +107 -7
- package/package.json +3 -2
- package/skills/modelmix/SKILL.md +303 -0
package/README.md
CHANGED
|
@@ -23,6 +23,11 @@ Recommended: install dotenv to manage environment variables
|
|
|
23
23
|
npm install modelmix dotenv
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
> **AI Skill**: You can also add ModelMix as a skill for AI agentic development:
|
|
27
|
+
> ```bash
|
|
28
|
+
> npx skills add https://github.com/clasen/ModelMix --skill modelmix
|
|
29
|
+
> ```
|
|
30
|
+
|
|
26
31
|
2. **Setup your environment variables (.env file)**:
|
|
27
32
|
Only the API keys you plan to use are required.
|
|
28
33
|
```plaintext
|
|
@@ -55,7 +60,7 @@ console.log(await model.json(outputExample));
|
|
|
55
60
|
const setup = {
|
|
56
61
|
config: {
|
|
57
62
|
system: "You are ALF, if they ask your name, respond with 'ALF'.",
|
|
58
|
-
debug:
|
|
63
|
+
debug: 2
|
|
59
64
|
}
|
|
60
65
|
};
|
|
61
66
|
|
|
@@ -139,6 +144,7 @@ Here's a comprehensive list of available methods:
|
|
|
139
144
|
| `gpt41mini()` | OpenAI | gpt-4.1-mini | [\$0.40 / \$1.60][1] |
|
|
140
145
|
| `gpt41nano()` | OpenAI | gpt-4.1-nano | [\$0.10 / \$0.40][1] |
|
|
141
146
|
| `gptOss()` | Together | gpt-oss-120B | [\$0.15 / \$0.60][7] |
|
|
147
|
+
| `opus46[think]()` | Anthropic | claude-opus-4-6 | [\$5.00 / \$25.00][2] |
|
|
142
148
|
| `opus45[think]()` | Anthropic | claude-opus-4-5-20251101 | [\$5.00 / \$25.00][2] |
|
|
143
149
|
| `opus41[think]()` | Anthropic | claude-opus-4-1-20250805 | [\$15.00 / \$75.00][2] |
|
|
144
150
|
| `sonnet45[think]()`| Anthropic | claude-sonnet-4-5-20250929 | [\$3.00 / \$15.00][2] |
|
|
@@ -186,107 +192,175 @@ const result = await ModelMix.new({
|
|
|
186
192
|
.message();
|
|
187
193
|
```
|
|
188
194
|
|
|
189
|
-
## 🔄
|
|
195
|
+
## 🔄 Templates
|
|
196
|
+
|
|
197
|
+
ModelMix includes a simple but powerful templating system. You can write your system prompts and user messages in external `.md` files with placeholders, then use `replace` to fill them in at runtime.
|
|
190
198
|
|
|
191
|
-
###
|
|
199
|
+
### Core methods
|
|
192
200
|
|
|
193
|
-
|
|
201
|
+
| Method | Description |
|
|
202
|
+
| --- | --- |
|
|
203
|
+
| `setSystemFromFile(path)` | Load the system prompt from a file |
|
|
204
|
+
| `addTextFromFile(path)` | Load a user message from a file |
|
|
205
|
+
| `replace({ key: value })` | Replace placeholders in all messages and the system prompt |
|
|
206
|
+
| `replaceKeyFromFile(key, path)` | Replace a placeholder with the contents of a file |
|
|
207
|
+
|
|
208
|
+
### Basic example with `replace`
|
|
194
209
|
|
|
195
|
-
#### Usage:
|
|
196
210
|
```javascript
|
|
197
|
-
|
|
211
|
+
const gpt = ModelMix.new().gpt5mini();
|
|
212
|
+
|
|
213
|
+
gpt.addText('Write a short story about a {animal} that lives in {place}.');
|
|
214
|
+
gpt.replace({ '{animal}': 'cat', '{place}': 'a haunted castle' });
|
|
215
|
+
|
|
216
|
+
console.log(await gpt.message());
|
|
198
217
|
```
|
|
199
218
|
|
|
200
|
-
|
|
201
|
-
1. It updates the `config.replace` object with the provided key-value pairs.
|
|
202
|
-
2. In the template, placeholders like `{{key1}}` will be replaced with 'value1'.
|
|
219
|
+
### Loading prompts from `.md` files
|
|
203
220
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
221
|
+
Instead of writing long prompts inline, keep them in separate Markdown files. This makes them easier to read, edit, and version control.
|
|
222
|
+
|
|
223
|
+
**`prompts/system.md`**
|
|
224
|
+
```markdown
|
|
225
|
+
You are {role}, an expert in {topic}.
|
|
226
|
+
Always respond in {language}.
|
|
209
227
|
```
|
|
210
|
-
This would result in the message: "Hello Alice, are you 30 years old?"
|
|
211
228
|
|
|
212
|
-
|
|
229
|
+
**`prompts/task.md`**
|
|
230
|
+
```markdown
|
|
231
|
+
Analyze the following and provide 3 key insights:
|
|
213
232
|
|
|
214
|
-
|
|
233
|
+
{content}
|
|
234
|
+
```
|
|
215
235
|
|
|
216
|
-
|
|
236
|
+
**`app.js`**
|
|
217
237
|
```javascript
|
|
218
|
-
|
|
238
|
+
const gpt = ModelMix.new().gpt5mini();
|
|
239
|
+
|
|
240
|
+
gpt.setSystemFromFile('./prompts/system.md');
|
|
241
|
+
gpt.addTextFromFile('./prompts/task.md');
|
|
242
|
+
|
|
243
|
+
gpt.replace({
|
|
244
|
+
'{role}': 'a senior analyst',
|
|
245
|
+
'{topic}': 'market trends',
|
|
246
|
+
'{language}': 'Spanish',
|
|
247
|
+
'{content}': 'Bitcoin surpassed $100,000 in December 2024...'
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
console.log(await gpt.message());
|
|
219
251
|
```
|
|
220
252
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
253
|
+
### Injecting file contents into a placeholder
|
|
254
|
+
|
|
255
|
+
Use `replaceKeyFromFile` when the replacement value itself is a large text stored in a file.
|
|
256
|
+
|
|
257
|
+
**`prompts/summarize.md`**
|
|
258
|
+
```markdown
|
|
259
|
+
Summarize the following article in 3 bullet points:
|
|
224
260
|
|
|
225
|
-
|
|
261
|
+
{article}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**`app.js`**
|
|
226
265
|
```javascript
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
266
|
+
const gpt = ModelMix.new().gpt5mini();
|
|
267
|
+
|
|
268
|
+
gpt.addTextFromFile('./prompts/summarize.md');
|
|
269
|
+
gpt.replaceKeyFromFile('{article}', './data/article.md');
|
|
270
|
+
|
|
271
|
+
console.log(await gpt.message());
|
|
230
272
|
```
|
|
231
|
-
This would replace `article_file_contents` with the entire content of 'article.txt'.
|
|
232
273
|
|
|
233
|
-
###
|
|
234
|
-
|
|
235
|
-
|
|
274
|
+
### Full template workflow
|
|
275
|
+
|
|
276
|
+
Combine all methods to build reusable, file-based prompt pipelines:
|
|
236
277
|
|
|
237
|
-
|
|
278
|
+
**`prompts/system.md`**
|
|
279
|
+
```markdown
|
|
280
|
+
You are {role}. Follow these rules:
|
|
281
|
+
- Be concise
|
|
282
|
+
- Use examples when possible
|
|
283
|
+
- Respond in {language}
|
|
284
|
+
```
|
|
238
285
|
|
|
239
|
-
|
|
286
|
+
**`prompts/review.md`**
|
|
287
|
+
```markdown
|
|
288
|
+
Review the following code and suggest improvements:
|
|
240
289
|
|
|
241
|
-
|
|
290
|
+
{code}
|
|
291
|
+
```
|
|
242
292
|
|
|
293
|
+
**`app.js`**
|
|
243
294
|
```javascript
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
295
|
+
const gpt = ModelMix.new().gpt5mini();
|
|
296
|
+
|
|
297
|
+
gpt.setSystemFromFile('./prompts/system.md');
|
|
298
|
+
gpt.addTextFromFile('./prompts/review.md');
|
|
299
|
+
|
|
300
|
+
gpt.replace({ '{role}': 'a senior code reviewer', '{language}': 'English' });
|
|
301
|
+
gpt.replaceKeyFromFile('{code}', './src/utils.js');
|
|
302
|
+
|
|
303
|
+
console.log(await gpt.message());
|
|
250
304
|
```
|
|
251
305
|
|
|
252
|
-
|
|
306
|
+
## 🧩 JSON Structured Output
|
|
253
307
|
|
|
254
|
-
|
|
255
|
-
- When set to `true`, includes the generated JSON schema in the system prompt
|
|
308
|
+
The `json` method forces the model to return a structured JSON response. You define the shape with an example object and optionally describe each field.
|
|
256
309
|
|
|
257
|
-
|
|
258
|
-
|
|
310
|
+
```javascript
|
|
311
|
+
await model.json(schemaExample, schemaDescription, options)
|
|
312
|
+
```
|
|
259
313
|
|
|
260
|
-
|
|
261
|
-
- When set to `true`, adds a technical note about JSON formatting requirements
|
|
262
|
-
- Specifically adds this instruction to the system prompt:
|
|
263
|
-
```
|
|
264
|
-
Output JSON Note: Escape all unescaped double quotes, backslashes, and ASCII control characters inside JSON strings, and ensure the output contains no comments.
|
|
265
|
-
```
|
|
266
|
-
- Helps prevent common JSON parsing errors
|
|
314
|
+
### Basic usage
|
|
267
315
|
|
|
268
|
-
|
|
316
|
+
```javascript
|
|
317
|
+
const model = ModelMix.new()
|
|
318
|
+
.gpt5mini()
|
|
319
|
+
.addText('Name and capital of 3 South American countries.');
|
|
320
|
+
|
|
321
|
+
const result = await model.json({ countries: [{ name: "", capital: "" }] });
|
|
322
|
+
console.log(result);
|
|
323
|
+
// { countries: [{ name: "Argentina", capital: "Buenos Aires" }, ...] }
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Adding field descriptions
|
|
327
|
+
|
|
328
|
+
The second argument lets you describe each field so the model understands exactly what you expect:
|
|
269
329
|
|
|
270
330
|
```javascript
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
{ name: "Person's full name", age: "Age in years" },
|
|
275
|
-
{ addExample: true, addNote: true }
|
|
276
|
-
);
|
|
331
|
+
const model = ModelMix.new()
|
|
332
|
+
.gpt5mini()
|
|
333
|
+
.addText('Name and capital of 3 South American countries.');
|
|
277
334
|
|
|
278
|
-
// Only add the example, skip the technical note
|
|
279
335
|
const result = await model.json(
|
|
280
|
-
{
|
|
281
|
-
{},
|
|
282
|
-
{
|
|
336
|
+
{ countries: [{ name: "Argentina", capital: "BUENOS AIRES" }] },
|
|
337
|
+
{ countries: [{ name: "name of the country", capital: "capital of the country in uppercase" }] },
|
|
338
|
+
{ addNote: true }
|
|
283
339
|
);
|
|
340
|
+
console.log(result);
|
|
341
|
+
// { countries: [
|
|
342
|
+
// { name: "Brazil", capital: "BRASILIA" },
|
|
343
|
+
// { name: "Colombia", capital: "BOGOTA" },
|
|
344
|
+
// { name: "Chile", capital: "SANTIAGO" }
|
|
345
|
+
// ]}
|
|
346
|
+
```
|
|
284
347
|
|
|
285
|
-
|
|
348
|
+
The example values (like `"Argentina"` and `"BUENOS AIRES"`) show the model the expected format, while the descriptions clarify what each field should contain.
|
|
349
|
+
|
|
350
|
+
### Options
|
|
351
|
+
|
|
352
|
+
| Option | Default | Description |
|
|
353
|
+
| --- | --- | --- |
|
|
354
|
+
| `addSchema` | `true` | Include the generated JSON schema in the system prompt |
|
|
355
|
+
| `addExample` | `false` | Include the example object in the system prompt |
|
|
356
|
+
| `addNote` | `false` | Add a note about JSON escaping to prevent parsing errors |
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
// Include the example and the escaping note
|
|
286
360
|
const result = await model.json(
|
|
287
|
-
{
|
|
288
|
-
{},
|
|
289
|
-
{ addNote: true }
|
|
361
|
+
{ name: "John", age: 30, skills: ["JavaScript"] },
|
|
362
|
+
{ name: "Full name", age: "Age in years", skills: "List of programming languages" },
|
|
363
|
+
{ addExample: true, addNote: true }
|
|
290
364
|
);
|
|
291
365
|
```
|
|
292
366
|
|
|
@@ -310,16 +384,28 @@ Every response from `raw()` now includes a `tokens` object with the following st
|
|
|
310
384
|
}
|
|
311
385
|
```
|
|
312
386
|
|
|
387
|
+
### `lastRaw` — Access full response after `message()` or `json()`
|
|
388
|
+
|
|
389
|
+
After calling `message()` or `json()`, use `lastRaw` to access the complete response (tokens, thinking, tool calls, etc.). It has the same structure as `raw()`.
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
const text = await model.message();
|
|
393
|
+
console.log(model.lastRaw.tokens);
|
|
394
|
+
// { input: 122, output: 86, total: 541, cost: 0.000319 }
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
The `cost` field is the estimated cost in USD based on the model's pricing per 1M tokens (input/output). If the model is not found in the pricing table, `cost` will be `null`.
|
|
398
|
+
|
|
313
399
|
## 🐛 Enabling Debug Mode
|
|
314
400
|
|
|
315
401
|
To activate debug mode in ModelMix and view detailed request information, follow these two steps:
|
|
316
402
|
|
|
317
|
-
1. In the ModelMix constructor, include `debug:
|
|
403
|
+
1. In the ModelMix constructor, include `debug: 3` in the configuration:
|
|
318
404
|
|
|
319
405
|
```javascript
|
|
320
406
|
const mix = ModelMix.new({
|
|
321
407
|
config: {
|
|
322
|
-
debug:
|
|
408
|
+
debug: 3
|
|
323
409
|
// ... other configuration options ...
|
|
324
410
|
}
|
|
325
411
|
});
|
|
@@ -389,10 +475,14 @@ new ModelMix(args = { options: {}, config: {} })
|
|
|
389
475
|
- `new()`: `static` Creates a new `ModelMix`.
|
|
390
476
|
- `new()`: Creates a new `ModelMix` using instance setup.
|
|
391
477
|
|
|
478
|
+
- `setSystem(text)`: Sets the system prompt.
|
|
479
|
+
- `setSystemFromFile(filePath)`: Sets the system prompt from a file.
|
|
392
480
|
- `addText(text, config = { role: "user" })`: Adds a text message.
|
|
393
|
-
- `addTextFromFile(filePath, config = { role: "user" })`: Adds a text message from a file
|
|
481
|
+
- `addTextFromFile(filePath, config = { role: "user" })`: Adds a text message from a file.
|
|
394
482
|
- `addImage(filePath, config = { role: "user" })`: Adds an image message from a file path.
|
|
395
483
|
- `addImageFromUrl(url, config = { role: "user" })`: Adds an image message from URL.
|
|
484
|
+
- `replace(keyValues)`: Defines placeholder replacements for messages and system prompt.
|
|
485
|
+
- `replaceKeyFromFile(key, filePath)`: Defines a placeholder replacement with file contents as value.
|
|
396
486
|
- `message()`: Sends the message and returns the response.
|
|
397
487
|
- `raw()`: Sends the message and returns the complete response data including:
|
|
398
488
|
- `message`: The text response from the model
|
package/demo/custom.js
CHANGED
package/demo/demo.js
CHANGED
|
@@ -10,7 +10,7 @@ const mmix = new ModelMix({
|
|
|
10
10
|
system: 'You are {name} from Melmac.',
|
|
11
11
|
max_history: 2,
|
|
12
12
|
bottleneck: { maxConcurrent: 1 },
|
|
13
|
-
debug:
|
|
13
|
+
debug: 3,
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
16
|
|
|
@@ -33,7 +33,7 @@ gpt.replace({ '{animal}': 'cat' });
|
|
|
33
33
|
await gpt.json({ time: '24:00:00', message: 'Hello' }, { time: 'Time in format HH:MM:SS' });
|
|
34
34
|
|
|
35
35
|
console.log("\n" + '--------| sonnet45() |--------');
|
|
36
|
-
const claude = mmix.new({ config: { debug:
|
|
36
|
+
const claude = mmix.new({ config: { debug: 2 } }).sonnet45();
|
|
37
37
|
claude.addImageFromUrl('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mP8z8BQz0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC');
|
|
38
38
|
claude.addText('in one word, which is the main color of the image?');
|
|
39
39
|
const imageDescription = await claude.message();
|
package/demo/free.js
CHANGED
package/demo/gpt51.js
CHANGED
package/demo/grok.js
CHANGED
package/demo/images.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
process.loadEnvFile();
|
|
2
2
|
import { ModelMix } from '../index.js';
|
|
3
3
|
|
|
4
|
-
const model = ModelMix.new({ config: { max_history: 2, debug:
|
|
4
|
+
const model = ModelMix.new({ config: { max_history: 2, debug: 2 } }).maverick()
|
|
5
5
|
// model.addImageFromUrl('https://pbs.twimg.com/media/F6-GsjraAAADDGy?format=jpg');
|
|
6
6
|
model.addImage('./img.png');
|
|
7
7
|
model.addText('in one word, which is the main color of the image?');
|
package/demo/json.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
process.loadEnvFile();
|
|
2
2
|
import { ModelMix } from '../index.js';
|
|
3
3
|
|
|
4
|
-
const model = await ModelMix.new({ options: { max_tokens: 10000 }, config: { debug:
|
|
5
|
-
.
|
|
4
|
+
const model = await ModelMix.new({ options: { max_tokens: 10000 }, config: { debug: 3 } })
|
|
5
|
+
.gemini3flash()
|
|
6
6
|
// .gptOss()
|
|
7
7
|
// .scout({ config: { temperature: 0 } })
|
|
8
8
|
// .o4mini()
|
|
@@ -11,5 +11,17 @@ const model = await ModelMix.new({ options: { max_tokens: 10000 }, config: { deb
|
|
|
11
11
|
// .gemini25flash()
|
|
12
12
|
.addText("Name and capital of 3 South American countries.")
|
|
13
13
|
|
|
14
|
-
const jsonResult = await model.json({
|
|
15
|
-
|
|
14
|
+
const jsonResult = await model.json({
|
|
15
|
+
countries: [{
|
|
16
|
+
name: "Argentina",
|
|
17
|
+
capital: "BUENOS AIRES"
|
|
18
|
+
}]
|
|
19
|
+
}, {
|
|
20
|
+
countries: [{
|
|
21
|
+
name: "name of the country",
|
|
22
|
+
capital: "capital of the country in uppercase"
|
|
23
|
+
}]
|
|
24
|
+
}, { addNote: true });
|
|
25
|
+
|
|
26
|
+
console.log(jsonResult);
|
|
27
|
+
console.log(model.lastRaw.tokens);
|
package/demo/mcp-simple.js
CHANGED
|
@@ -92,7 +92,7 @@ async function simpleCalculator() {
|
|
|
92
92
|
async function contentGenerator() {
|
|
93
93
|
console.log('\n=== Content Generator ===');
|
|
94
94
|
|
|
95
|
-
const mmix = ModelMix.new({ config: { debug:
|
|
95
|
+
const mmix = ModelMix.new({ config: { debug: 2, max_history: 1 } })
|
|
96
96
|
.gemini3flash()
|
|
97
97
|
.setSystem('You are a creative assistant that can generate different types of content.');
|
|
98
98
|
|
package/demo/minimax.js
CHANGED
package/demo/parallel.js
CHANGED
package/demo/repl-powers.js
CHANGED
|
@@ -11,7 +11,7 @@ const isolate = new ivm.Isolate({ memoryLimit: 128 }); // 128MB máximo
|
|
|
11
11
|
async function replPowersExample() {
|
|
12
12
|
console.log('\n=== JavaScript REPL - Potencias de 2 ===\n');
|
|
13
13
|
const gptArgs = { options: { reasoning_effort: "none", verbosity: null } };
|
|
14
|
-
const mmix = ModelMix.new({ config: { debug:
|
|
14
|
+
const mmix = ModelMix.new({ config: { debug: 2, max_history: 10 } })
|
|
15
15
|
.gpt41nano()
|
|
16
16
|
.gpt52(gptArgs)
|
|
17
17
|
.gemini3flash()
|
package/demo/short.js
CHANGED
package/index.js
CHANGED
|
@@ -10,6 +10,81 @@ const { Client } = require("@modelcontextprotocol/sdk/client/index.js");
|
|
|
10
10
|
const { StdioClientTransport } = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
11
11
|
const { MCPToolsManager } = require('./mcp-tools');
|
|
12
12
|
|
|
13
|
+
// Pricing per 1M tokens: [input, output] in USD
|
|
14
|
+
// Based on provider pricing pages linked in README
|
|
15
|
+
const MODEL_PRICING = {
|
|
16
|
+
// OpenAI
|
|
17
|
+
'gpt-5.2': [1.75, 14.00],
|
|
18
|
+
'gpt-5.2-chat-latest': [1.75, 14.00],
|
|
19
|
+
'gpt-5.1': [1.25, 10.00],
|
|
20
|
+
'gpt-5': [1.25, 10.00],
|
|
21
|
+
'gpt-5-mini': [0.25, 2.00],
|
|
22
|
+
'gpt-5-nano': [0.05, 0.40],
|
|
23
|
+
'gpt-4.1': [2.00, 8.00],
|
|
24
|
+
'gpt-4.1-mini': [0.40, 1.60],
|
|
25
|
+
'gpt-4.1-nano': [0.10, 0.40],
|
|
26
|
+
// gptOss (Together/Groq/Cerebras/OpenRouter)
|
|
27
|
+
'openai/gpt-oss-120b': [0.15, 0.60],
|
|
28
|
+
'gpt-oss-120b': [0.15, 0.60],
|
|
29
|
+
'openai/gpt-oss-120b:free': [0, 0],
|
|
30
|
+
// Anthropic
|
|
31
|
+
'claude-opus-4-6': [5.00, 25.00],
|
|
32
|
+
'claude-opus-4-5-20251101': [5.00, 25.00],
|
|
33
|
+
'claude-opus-4-1-20250805': [15.00, 75.00],
|
|
34
|
+
'claude-sonnet-4-5-20250929': [3.00, 15.00],
|
|
35
|
+
'claude-sonnet-4-20250514': [3.00, 15.00],
|
|
36
|
+
'claude-3-5-haiku-20241022': [0.80, 4.00],
|
|
37
|
+
'claude-haiku-4-5-20251001': [1.00, 5.00],
|
|
38
|
+
// Google
|
|
39
|
+
'gemini-3-pro-preview': [2.00, 12.00],
|
|
40
|
+
'gemini-3-flash-preview': [0.50, 3.00],
|
|
41
|
+
'gemini-2.5-pro': [1.25, 10.00],
|
|
42
|
+
'gemini-2.5-flash': [0.30, 2.50],
|
|
43
|
+
// Grok
|
|
44
|
+
'grok-4-0709': [3.00, 15.00],
|
|
45
|
+
'grok-4-1-fast-reasoning': [0.20, 0.50],
|
|
46
|
+
'grok-4-1-fast-non-reasoning': [0.20, 0.50],
|
|
47
|
+
// Fireworks
|
|
48
|
+
'accounts/fireworks/models/deepseek-v3p2': [0.56, 1.68],
|
|
49
|
+
'accounts/fireworks/models/glm-4p7': [0.55, 2.19],
|
|
50
|
+
'accounts/fireworks/models/kimi-k2p5': [0.50, 2.80],
|
|
51
|
+
// MiniMax
|
|
52
|
+
'MiniMax-M2.1': [0.30, 1.20],
|
|
53
|
+
// Perplexity
|
|
54
|
+
'sonar': [1.00, 1.00],
|
|
55
|
+
'sonar-pro': [3.00, 15.00],
|
|
56
|
+
// Scout (Groq/Together/Cerebras)
|
|
57
|
+
'meta-llama/llama-4-scout-17b-16e-instruct': [0.11, 0.34],
|
|
58
|
+
'meta-llama/Llama-4-Scout-17B-16E-Instruct': [0.11, 0.34],
|
|
59
|
+
'llama-4-scout-17b-16e-instruct': [0.11, 0.34],
|
|
60
|
+
// Maverick (Groq/Together/Lambda)
|
|
61
|
+
'meta-llama/llama-4-maverick-17b-128e-instruct': [0.20, 0.60],
|
|
62
|
+
'meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8': [0.20, 0.60],
|
|
63
|
+
'llama-4-maverick-17b-128e-instruct-fp8': [0.20, 0.60],
|
|
64
|
+
// Hermes3 (Lambda/OpenRouter)
|
|
65
|
+
'Hermes-3-Llama-3.1-405B-FP8': [0.80, 0.80],
|
|
66
|
+
'nousresearch/hermes-3-llama-3.1-405b:free': [0, 0],
|
|
67
|
+
// Qwen3 (Together/Cerebras)
|
|
68
|
+
'Qwen/Qwen3-235B-A22B-fp8-tput': [0.20, 0.60],
|
|
69
|
+
'qwen-3-32b': [0.20, 0.60],
|
|
70
|
+
// Kimi K2 (Together/Groq/OpenRouter)
|
|
71
|
+
'moonshotai/Kimi-K2-Instruct-0905': [1.00, 3.00],
|
|
72
|
+
'moonshotai/kimi-k2-instruct-0905': [1.00, 3.00],
|
|
73
|
+
'moonshotai/kimi-k2:free': [0, 0],
|
|
74
|
+
'moonshotai/Kimi-K2-Thinking': [1.00, 3.00],
|
|
75
|
+
'moonshotai/kimi-k2-thinking': [1.00, 3.00],
|
|
76
|
+
// Kimi K2.5 (Together/Fireworks/OpenRouter)
|
|
77
|
+
'moonshotai/Kimi-K2.5': [0.50, 2.80],
|
|
78
|
+
'moonshotai/kimi-k2.5': [0.50, 2.80],
|
|
79
|
+
// DeepSeek V3.2 (OpenRouter)
|
|
80
|
+
'deepseek/deepseek-v3.2': [0.56, 1.68],
|
|
81
|
+
// GLM 4.7 (OpenRouter/Cerebras)
|
|
82
|
+
'z-ai/glm-4.7': [0.55, 2.19],
|
|
83
|
+
'zai-glm-4.7': [0.55, 2.19],
|
|
84
|
+
// DeepSeek R1 (OpenRouter free)
|
|
85
|
+
'deepseek/deepseek-r1-0528:free': [0, 0],
|
|
86
|
+
};
|
|
87
|
+
|
|
13
88
|
class ModelMix {
|
|
14
89
|
|
|
15
90
|
constructor({ options = {}, config = {}, mix = {} } = {}) {
|
|
@@ -19,6 +94,7 @@ class ModelMix {
|
|
|
19
94
|
this.toolClient = {};
|
|
20
95
|
this.mcp = {};
|
|
21
96
|
this.mcpToolsManager = new MCPToolsManager();
|
|
97
|
+
this.lastRaw = null;
|
|
22
98
|
this.options = {
|
|
23
99
|
max_tokens: 8192,
|
|
24
100
|
temperature: 1, // 1 --> More creative, 0 --> More deterministic.
|
|
@@ -82,11 +158,18 @@ class ModelMix {
|
|
|
82
158
|
}
|
|
83
159
|
|
|
84
160
|
// debug logging helpers
|
|
85
|
-
static truncate(str, maxLen =
|
|
161
|
+
static truncate(str, maxLen = 1000) {
|
|
86
162
|
if (!str || typeof str !== 'string') return str;
|
|
87
163
|
return str.length > maxLen ? str.substring(0, maxLen) + '...' : str;
|
|
88
164
|
}
|
|
89
165
|
|
|
166
|
+
static calculateCost(modelKey, tokens) {
|
|
167
|
+
const pricing = MODEL_PRICING[modelKey];
|
|
168
|
+
if (!pricing) return null;
|
|
169
|
+
const [inputPerMillion, outputPerMillion] = pricing;
|
|
170
|
+
return (tokens.input * inputPerMillion / 1_000_000) + (tokens.output * outputPerMillion / 1_000_000);
|
|
171
|
+
}
|
|
172
|
+
|
|
90
173
|
static formatInputSummary(messages, system) {
|
|
91
174
|
const lastMessage = messages[messages.length - 1];
|
|
92
175
|
let inputText = '';
|
|
@@ -98,8 +181,8 @@ class ModelMix {
|
|
|
98
181
|
inputText = lastMessage.content;
|
|
99
182
|
}
|
|
100
183
|
|
|
101
|
-
const systemStr = `System: ${ModelMix.truncate(system,
|
|
102
|
-
const inputStr = `Input: ${ModelMix.truncate(inputText,
|
|
184
|
+
const systemStr = `System: ${ModelMix.truncate(system, 500)}`;
|
|
185
|
+
const inputStr = `Input: ${ModelMix.truncate(inputText, 1200)}`;
|
|
103
186
|
const msgCount = `(${messages.length} msg${messages.length !== 1 ? 's' : ''})`;
|
|
104
187
|
|
|
105
188
|
return `${systemStr} \n| ${inputStr} ${msgCount}`;
|
|
@@ -115,15 +198,15 @@ class ModelMix {
|
|
|
115
198
|
if (debug >= 2) {
|
|
116
199
|
parts.push(`Output (JSON):\n${ModelMix.formatJSON(parsed)}`);
|
|
117
200
|
} else {
|
|
118
|
-
parts.push(`Output: ${ModelMix.truncate(result.message,
|
|
201
|
+
parts.push(`Output: ${ModelMix.truncate(result.message, 1500)}`);
|
|
119
202
|
}
|
|
120
203
|
} catch (e) {
|
|
121
204
|
// Not JSON, show truncated as before
|
|
122
|
-
parts.push(`Output: ${ModelMix.truncate(result.message,
|
|
205
|
+
parts.push(`Output: ${ModelMix.truncate(result.message, 1500)}`);
|
|
123
206
|
}
|
|
124
207
|
}
|
|
125
208
|
if (result.think) {
|
|
126
|
-
parts.push(`Think: ${ModelMix.truncate(result.think,
|
|
209
|
+
parts.push(`Think: ${ModelMix.truncate(result.think, 800)}`);
|
|
127
210
|
}
|
|
128
211
|
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
129
212
|
const toolNames = result.toolCalls.map(t => t.function?.name || t.name).join(', ');
|
|
@@ -190,6 +273,17 @@ class ModelMix {
|
|
|
190
273
|
if (mix.openrouter) this.attach('openai/gpt-oss-120b:free', new MixOpenRouter({ options, config }));
|
|
191
274
|
return this;
|
|
192
275
|
}
|
|
276
|
+
opus46think({ options = {}, config = {} } = {}) {
|
|
277
|
+
options = { ...MixAnthropic.thinkingOptions, ...options };
|
|
278
|
+
return this.attach('claude-opus-4-6', new MixAnthropic({ options, config }));
|
|
279
|
+
}
|
|
280
|
+
opus45think({ options = {}, config = {} } = {}) {
|
|
281
|
+
options = { ...MixAnthropic.thinkingOptions, ...options };
|
|
282
|
+
return this.attach('claude-opus-4-5-20251101', new MixAnthropic({ options, config }));
|
|
283
|
+
}
|
|
284
|
+
opus46({ options = {}, config = {} } = {}) {
|
|
285
|
+
return this.attach('claude-opus-4-6', new MixAnthropic({ options, config }));
|
|
286
|
+
}
|
|
193
287
|
opus45({ options = {}, config = {} } = {}) {
|
|
194
288
|
return this.attach('claude-opus-4-5-20251101', new MixAnthropic({ options, config }));
|
|
195
289
|
}
|
|
@@ -761,6 +855,11 @@ class ModelMix {
|
|
|
761
855
|
|
|
762
856
|
const result = await providerInstance.create({ options: currentOptions, config: currentConfig });
|
|
763
857
|
|
|
858
|
+
// Calculate cost based on model pricing
|
|
859
|
+
if (result.tokens) {
|
|
860
|
+
result.tokens.cost = ModelMix.calculateCost(currentModelKey, result.tokens);
|
|
861
|
+
}
|
|
862
|
+
|
|
764
863
|
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
765
864
|
|
|
766
865
|
if (result.message) {
|
|
@@ -821,6 +920,7 @@ class ModelMix {
|
|
|
821
920
|
|
|
822
921
|
if (currentConfig.debug >= 1) console.log('');
|
|
823
922
|
|
|
923
|
+
this.lastRaw = result;
|
|
824
924
|
return result;
|
|
825
925
|
|
|
826
926
|
} catch (error) {
|
|
@@ -1349,7 +1449,7 @@ class MixAnthropic extends MixCustom {
|
|
|
1349
1449
|
static thinkingOptions = {
|
|
1350
1450
|
thinking: {
|
|
1351
1451
|
"type": "enabled",
|
|
1352
|
-
"budget_tokens":
|
|
1452
|
+
"budget_tokens": 1638
|
|
1353
1453
|
},
|
|
1354
1454
|
temperature: 1
|
|
1355
1455
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modelmix",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.6",
|
|
4
4
|
"description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
11
|
"mcp",
|
|
12
|
+
"skill",
|
|
12
13
|
"llm",
|
|
13
14
|
"ai",
|
|
14
15
|
"model",
|
|
@@ -72,4 +73,4 @@
|
|
|
72
73
|
"test:live.mcp": "mocha test/live.mcp.js --timeout 60000 --require dotenv/config --require test/setup.js",
|
|
73
74
|
"test:tokens": "mocha test/tokens.test.js --timeout 10000 --require dotenv/config --require test/setup.js"
|
|
74
75
|
}
|
|
75
|
-
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: modelmix
|
|
3
|
+
description: Instructions for using the ModelMix Node.js library to interact with multiple AI LLM providers through a unified interface. Use when integrating AI models (OpenAI, Anthropic, Google, Groq, Perplexity, Grok, etc.), chaining models with fallback, getting structured JSON from LLMs, adding MCP tools, streaming responses, or managing multi-provider AI workflows in Node.js.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ModelMix Library Skill
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
ModelMix is a Node.js library that provides a unified fluent API to interact with multiple AI LLM providers. It handles automatic fallback between models, round-robin load balancing, structured JSON output, streaming, MCP tool integration, rate limiting, and token tracking.
|
|
11
|
+
|
|
12
|
+
Use this skill when:
|
|
13
|
+
- Integrating one or more AI models into a Node.js project
|
|
14
|
+
- Chaining models with automatic fallback
|
|
15
|
+
- Extracting structured JSON from LLMs
|
|
16
|
+
- Adding MCP tools or custom tools to models
|
|
17
|
+
- Working with templates and file-based prompts
|
|
18
|
+
|
|
19
|
+
Do NOT use this skill for:
|
|
20
|
+
- Python or non-Node.js projects
|
|
21
|
+
- Direct HTTP calls to LLM APIs (use ModelMix instead)
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install modelmix
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Core Concepts
|
|
30
|
+
|
|
31
|
+
### Import
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
import { ModelMix } from 'modelmix';
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Creating an Instance
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
// Static factory (preferred)
|
|
41
|
+
const model = ModelMix.new();
|
|
42
|
+
|
|
43
|
+
// With global options
|
|
44
|
+
const model = ModelMix.new({
|
|
45
|
+
options: { max_tokens: 4096, temperature: 0.7 },
|
|
46
|
+
config: {
|
|
47
|
+
system: "You are a helpful assistant.",
|
|
48
|
+
max_history: 5,
|
|
49
|
+
debug: 0, // 0=silent, 1=minimal, 2=summary, 3=full
|
|
50
|
+
roundRobin: false // false=fallback, true=rotate models
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Attaching Models (Fluent Chain)
|
|
56
|
+
|
|
57
|
+
Chain shorthand methods to attach providers. First model is primary; others are fallbacks:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
const model = ModelMix.new()
|
|
61
|
+
.sonnet45() // primary
|
|
62
|
+
.gpt5mini() // fallback 1
|
|
63
|
+
.gemini3flash() // fallback 2
|
|
64
|
+
.addText("Hello!")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If `sonnet45` fails, it automatically tries `gpt5mini`, then `gemini3flash`.
|
|
68
|
+
|
|
69
|
+
## Available Model Shorthands
|
|
70
|
+
|
|
71
|
+
- **OpenAI**: `gpt52` `gpt51` `gpt5` `gpt5mini` `gpt5nano` `gpt41` `gpt41mini` `gpt41nano`
|
|
72
|
+
- **Anthropic**: `opus46` `opus45` `sonnet45` `sonnet4` `haiku45` `haiku35` (thinking variants: add `think` suffix)
|
|
73
|
+
- **Google**: `gemini3pro` `gemini3flash` `gemini25pro` `gemini25flash`
|
|
74
|
+
- **Grok**: `grok4` `grok41` (thinking variant available)
|
|
75
|
+
- **Perplexity**: `sonar` `sonarPro`
|
|
76
|
+
- **Groq**: `scout` `maverick`
|
|
77
|
+
- **Together**: `qwen3` `kimiK2`
|
|
78
|
+
- **Multi-provider**: `deepseekR1` `gptOss`
|
|
79
|
+
- **MiniMax**: `minimaxM21`
|
|
80
|
+
- **Fireworks**: `deepseekV32` `GLM47`
|
|
81
|
+
|
|
82
|
+
Each method is called as `mix.methodName()` and accepts optional `{ options, config }` to override per-model settings.
|
|
83
|
+
|
|
84
|
+
## Common Tasks
|
|
85
|
+
|
|
86
|
+
### Get a text response
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
const answer = await ModelMix.new()
|
|
90
|
+
.gpt5mini()
|
|
91
|
+
.addText("What is the capital of France?")
|
|
92
|
+
.message();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Get structured JSON
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const result = await ModelMix.new()
|
|
99
|
+
.gpt5mini()
|
|
100
|
+
.addText("Name and capital of 3 South American countries.")
|
|
101
|
+
.json(
|
|
102
|
+
{ countries: [{ name: "", capital: "" }] }, // schema example
|
|
103
|
+
{ countries: [{ name: "country name", capital: "in uppercase" }] }, // descriptions
|
|
104
|
+
{ addNote: true } // options
|
|
105
|
+
);
|
|
106
|
+
// result.countries → [{ name: "Brazil", capital: "BRASILIA" }, ...]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`json()` signature: `json(schemaExample, schemaDescription?, { addSchema, addExample, addNote }?)`
|
|
110
|
+
|
|
111
|
+
### Stream a response
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
await ModelMix.new()
|
|
115
|
+
.gpt5mini()
|
|
116
|
+
.addText("Tell me a story.")
|
|
117
|
+
.stream(({ delta, message }) => {
|
|
118
|
+
process.stdout.write(delta);
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Get raw response (tokens, thinking, tool calls)
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
const raw = await ModelMix.new()
|
|
126
|
+
.sonnet45think()
|
|
127
|
+
.addText("Solve this step by step: 2+2*3")
|
|
128
|
+
.raw();
|
|
129
|
+
// raw.message, raw.think, raw.tokens, raw.toolCalls, raw.response
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Access full response after `message()` or `json()` with `lastRaw`
|
|
133
|
+
|
|
134
|
+
After calling `message()`, `json()`, `block()`, or `stream()`, use `lastRaw` to access the complete response (tokens, thinking, tool calls, etc.). It has the same structure as `raw()`.
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
const model = ModelMix.new().gpt5mini().addText("Hello!");
|
|
138
|
+
const text = await model.message();
|
|
139
|
+
console.log(model.lastRaw.tokens);
|
|
140
|
+
// { input: 122, output: 86, total: 541, cost: 0.000319 }
|
|
141
|
+
console.log(model.lastRaw.think); // reasoning content (if available)
|
|
142
|
+
console.log(model.lastRaw.response); // raw API response
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Add images
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
const model = ModelMix.new().sonnet45();
|
|
149
|
+
model.addImage('./photo.jpg'); // from file
|
|
150
|
+
model.addImageFromUrl('https://example.com/img.png'); // from URL
|
|
151
|
+
model.addText('Describe this image.');
|
|
152
|
+
const description = await model.message();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Use templates with placeholders
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
const model = ModelMix.new().gpt5mini();
|
|
159
|
+
model.setSystemFromFile('./prompts/system.md');
|
|
160
|
+
model.addTextFromFile('./prompts/task.md');
|
|
161
|
+
model.replace({
|
|
162
|
+
'{role}': 'data analyst',
|
|
163
|
+
'{language}': 'Spanish'
|
|
164
|
+
});
|
|
165
|
+
model.replaceKeyFromFile('{code}', './src/utils.js');
|
|
166
|
+
console.log(await model.message());
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Round-robin load balancing
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
const pool = ModelMix.new({ config: { roundRobin: true } })
|
|
173
|
+
.gpt5mini()
|
|
174
|
+
.sonnet45()
|
|
175
|
+
.gemini3flash();
|
|
176
|
+
|
|
177
|
+
// Each call rotates to the next model
|
|
178
|
+
const r1 = await pool.new().addText("Request 1").message();
|
|
179
|
+
const r2 = await pool.new().addText("Request 2").message();
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### MCP integration (external tools)
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
const model = ModelMix.new({ config: { max_history: 10 } }).gpt5nano();
|
|
186
|
+
model.setSystem('You are an assistant. Today is ' + new Date().toISOString());
|
|
187
|
+
await model.addMCP('@modelcontextprotocol/server-brave-search');
|
|
188
|
+
model.addText('Use Internet: What is the latest news about AI?');
|
|
189
|
+
console.log(await model.message());
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Requires `BRAVE_API_KEY` in `.env` for Brave Search MCP.
|
|
193
|
+
|
|
194
|
+
### Custom local tools (addTool)
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
const model = ModelMix.new({ config: { max_history: 10 } }).gpt5mini();
|
|
198
|
+
|
|
199
|
+
model.addTool({
|
|
200
|
+
name: "get_weather",
|
|
201
|
+
description: "Get weather for a city",
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: { city: { type: "string" } },
|
|
205
|
+
required: ["city"]
|
|
206
|
+
}
|
|
207
|
+
}, async ({ city }) => {
|
|
208
|
+
return `The weather in ${city} is sunny, 25C`;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
model.addText("What's the weather in Tokyo?");
|
|
212
|
+
console.log(await model.message());
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Rate limiting (Bottleneck)
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
const model = ModelMix.new({
|
|
219
|
+
config: {
|
|
220
|
+
bottleneck: {
|
|
221
|
+
maxConcurrent: 4,
|
|
222
|
+
minTime: 1000
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}).gpt5mini();
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Debug mode
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
const model = ModelMix.new({
|
|
232
|
+
config: { debug: 2 } // 0=silent, 1=minimal, 2=summary, 3=full
|
|
233
|
+
}).gpt5mini();
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
For full debug output, also set the env: `DEBUG=ModelMix* node script.js`
|
|
237
|
+
|
|
238
|
+
### Use free-tier models
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
// These use providers with free quotas (OpenRouter, Groq, Cerebras)
|
|
242
|
+
const model = ModelMix.new()
|
|
243
|
+
.gptOss()
|
|
244
|
+
.kimiK2()
|
|
245
|
+
.deepseekR1()
|
|
246
|
+
.hermes3()
|
|
247
|
+
.addText("What is the capital of France?");
|
|
248
|
+
console.log(await model.message());
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Conversation history
|
|
252
|
+
|
|
253
|
+
```javascript
|
|
254
|
+
const chat = ModelMix.new({ config: { max_history: 10 } }).gpt5mini();
|
|
255
|
+
chat.addText("My name is Martin.");
|
|
256
|
+
await chat.message();
|
|
257
|
+
chat.addText("What's my name?");
|
|
258
|
+
const reply = await chat.message(); // "Martin"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Agent Usage Rules
|
|
262
|
+
|
|
263
|
+
- Always check `package.json` for `modelmix` before running `npm install`.
|
|
264
|
+
- Use `ModelMix.new()` static factory to create instances (not `new ModelMix()`).
|
|
265
|
+
- Store API keys in `.env` and load with `dotenv/config` or `process.loadEnvFile()`. Never hardcode keys.
|
|
266
|
+
- Chain models for resilience: primary model first, fallbacks after.
|
|
267
|
+
- When using MCP tools or `addTool()`, set `max_history` to at least 3.
|
|
268
|
+
- Use `.json()` for structured output instead of parsing text manually.
|
|
269
|
+
- Use `.message()` for simple text, `.raw()` when you need tokens/thinking/toolCalls.
|
|
270
|
+
- For thinking models, append `think` to the method name (e.g. `sonnet45think()`).
|
|
271
|
+
- Template placeholders use `{key}` syntax in both system prompts and user messages.
|
|
272
|
+
- The library uses CommonJS internally (`require`) but supports ESM import via `{ ModelMix }`.
|
|
273
|
+
- Available provider Mix classes for custom setups: `MixOpenAI`, `MixAnthropic`, `MixGoogle`, `MixPerplexity`, `MixGroq`, `MixTogether`, `MixGrok`, `MixOpenRouter`, `MixOllama`, `MixLMStudio`, `MixCustom`, `MixCerebras`, `MixFireworks`, `MixMiniMax`.
|
|
274
|
+
|
|
275
|
+
## API Quick Reference
|
|
276
|
+
|
|
277
|
+
| Method | Returns | Description |
|
|
278
|
+
| --- | --- | --- |
|
|
279
|
+
| `.addText(text)` | `this` | Add user message |
|
|
280
|
+
| `.addTextFromFile(path)` | `this` | Add user message from file |
|
|
281
|
+
| `.setSystem(text)` | `this` | Set system prompt |
|
|
282
|
+
| `.setSystemFromFile(path)` | `this` | Set system prompt from file |
|
|
283
|
+
| `.addImage(path)` | `this` | Add image from file |
|
|
284
|
+
| `.addImageFromUrl(url)` | `this` | Add image from URL or data URI |
|
|
285
|
+
| `.replace({})` | `this` | Set placeholder replacements |
|
|
286
|
+
| `.replaceKeyFromFile(key, path)` | `this` | Replace placeholder with file content |
|
|
287
|
+
| `.message()` | `Promise<string>` | Get text response |
|
|
288
|
+
| `.json(example, desc?, opts?)` | `Promise<object>` | Get structured JSON |
|
|
289
|
+
| `.raw()` | `Promise<{message, think, toolCalls, tokens, response}>` | Full response |
|
|
290
|
+
| `.lastRaw` | `object \| null` | Full response from last `message()`/`json()`/`block()`/`stream()` call |
|
|
291
|
+
| `.stream(callback)` | `Promise` | Stream response |
|
|
292
|
+
| `.block()` | `Promise<string>` | Extract code block from response |
|
|
293
|
+
| `.addMCP(package)` | `Promise` | Add MCP server tools |
|
|
294
|
+
| `.addTool(def, callback)` | `this` | Register custom local tool |
|
|
295
|
+
| `.addTools([{tool, callback}])` | `this` | Register multiple tools |
|
|
296
|
+
| `.removeTool(name)` | `this` | Remove a tool |
|
|
297
|
+
| `.listTools()` | `{local, mcp}` | List registered tools |
|
|
298
|
+
| `.new()` | `ModelMix` | Clone instance sharing models |
|
|
299
|
+
| `.attach(key, provider)` | `this` | Attach custom provider |
|
|
300
|
+
|
|
301
|
+
## References
|
|
302
|
+
|
|
303
|
+
- [GitHub Repository](https://github.com/clasen/ModelMix)
|