modelmix 2.3.2 → 2.4.2
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 +93 -5
- package/demo/demo.mjs +17 -6
- package/demo/prompt.md +25 -0
- package/demo/title.md +1 -0
- package/index.js +74 -1
- package/package.json +4 -3
- package/demo/watson.jpg +0 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## ✨ Features
|
|
6
6
|
|
|
7
7
|
- **Unified Interface**: Interact with multiple AI models through a single, coherent API.
|
|
8
|
-
- **Request Control**: Manage the number of parallel requests to adhere to provider limitations
|
|
8
|
+
- **Request Control**: Manage the number of parallel requests to adhere to provider limitations with `max_request`.
|
|
9
9
|
- **Flexible Integration**: Easily integrate popular models like OpenAI, Anthropic, Perplexity, Groq, Ollama, LM Studio or custom models.
|
|
10
10
|
- **History Tracking**: Automatically logs the conversation history with model responses, allowing you to limit the number of historical messages with `max_history`.
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ First, install the ModelMix package:
|
|
|
17
17
|
npm install modelmix
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Optional: install dotenv to manage environment variables:
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
npm install dotenv
|
|
@@ -51,6 +51,7 @@ Here's a quick example to get you started:
|
|
|
51
51
|
system: "You are {name} from Melmac.",
|
|
52
52
|
max_history: 2,
|
|
53
53
|
max_request: 1,
|
|
54
|
+
debug: true
|
|
54
55
|
}
|
|
55
56
|
});
|
|
56
57
|
|
|
@@ -85,9 +86,19 @@ Here's a quick example to get you started:
|
|
|
85
86
|
gpt.replace({ '{animal}': 'cat' });
|
|
86
87
|
console.log(await gpt.message());
|
|
87
88
|
|
|
88
|
-
console.log("\n" + '--------| claude-3-sonnet-
|
|
89
|
-
const
|
|
90
|
-
|
|
89
|
+
console.log("\n" + '--------| [writer] claude-3-5-sonnet-20240620 |--------');
|
|
90
|
+
const setup = {
|
|
91
|
+
config: { system: "You are a writer like Stephen King" },
|
|
92
|
+
options: { temperature: 0.5 }
|
|
93
|
+
}
|
|
94
|
+
const writer = mmix.create('claude-3-5-sonnet-20240620', setup);
|
|
95
|
+
writer.replace({ '{story_title}': 'The Mysterious Package' }) // or claude.replaceKeyFromFile(key, filePath)
|
|
96
|
+
const story = await writer.addTextFromFile('./prompt.md').message();
|
|
97
|
+
console.log(story);
|
|
98
|
+
|
|
99
|
+
console.log("\n" + '--------| [image] claude-3-5-sonnet-20240620 |--------');
|
|
100
|
+
const claude = mmix.create('claude-3-5-sonnet-20240620', { temperature: 0.5 });
|
|
101
|
+
claude.addImage("./watson.jpg"); // or claude.addImageFromUrl(url)
|
|
91
102
|
const imageDescription = await claude.addText("Describe the image").message();
|
|
92
103
|
console.log(imageDescription);
|
|
93
104
|
|
|
@@ -134,7 +145,9 @@ new ModelMix(args = { options: {}, config: {} })
|
|
|
134
145
|
|
|
135
146
|
- `new()`: Initializes a new message handler instance.
|
|
136
147
|
- `addText(text, config = { role: "user" })`: Adds a text message.
|
|
148
|
+
- `addTextFromFile(filePath, config = { role: "user" })`: Adds a text message from a file path.
|
|
137
149
|
- `addImage(filePath, config = { role: "user" })`: Adds an image message from a file path.
|
|
150
|
+
- `addImageFromUrl(url, config = { role: "user" })`: Adds an image message from URL.
|
|
138
151
|
- `message()`: Sends the message and returns the response.
|
|
139
152
|
- `raw()`: Sends the message and returns the raw response data.
|
|
140
153
|
- `stream(callback)`: Sends the message and streams the response, invoking the callback with each streamed part.
|
|
@@ -221,6 +234,81 @@ new MixLMStudio(args = { config: {}, options: {} })
|
|
|
221
234
|
- `url`: The endpoint URL to which the model sends requests.
|
|
222
235
|
- **options**: Default options for Ollama model instances.
|
|
223
236
|
|
|
237
|
+
## Explanation of `replace` and `replaceKeyFromFile` Methods
|
|
238
|
+
|
|
239
|
+
### `replace` Method
|
|
240
|
+
|
|
241
|
+
The `replace` method is used to define key-value pairs for text replacement in the messages and system prompt.
|
|
242
|
+
|
|
243
|
+
#### Usage:
|
|
244
|
+
```javascript
|
|
245
|
+
gpt.replace({ '{{key1}}': 'value1', '{{key2}}': 'value2' });
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### How it works:
|
|
249
|
+
- It updates the `config.replace` object with the provided key-value pairs.
|
|
250
|
+
- In the template, placeholders like `{{key1}}` will be replaced with 'value1'.
|
|
251
|
+
|
|
252
|
+
#### Example:
|
|
253
|
+
```javascript
|
|
254
|
+
gpt
|
|
255
|
+
.replace({ '{{name}}': 'Alice', '{{age}}': '30' })
|
|
256
|
+
.addText('Hello {{name}}, are you {{age}} years old?');
|
|
257
|
+
```
|
|
258
|
+
This would result in the message: "Hello Alice, are you 30 years old?"
|
|
259
|
+
|
|
260
|
+
### `replaceKeyFromFile` Method
|
|
261
|
+
|
|
262
|
+
The `replaceKeyFromFile` method is similar to `replace`, but it reads the replacement value from a file.
|
|
263
|
+
|
|
264
|
+
#### Usage:
|
|
265
|
+
```javascript
|
|
266
|
+
messageHandler.replaceKeyFromFile('longText', './path/to/file.txt');
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### How it works:
|
|
270
|
+
1. It reads the content of the specified file synchronously.
|
|
271
|
+
2. It then calls the `replace` method, using the provided key and the file content as the value.
|
|
272
|
+
|
|
273
|
+
#### Example:
|
|
274
|
+
```javascript
|
|
275
|
+
messageHandler
|
|
276
|
+
.replaceKeyFromFile('article_file_contents', './article.txt')
|
|
277
|
+
.addText('Please summarize this article: article_file_contents');
|
|
278
|
+
```
|
|
279
|
+
This would replace `article_file_contents` with the entire content of 'article.txt'.
|
|
280
|
+
|
|
281
|
+
### When to use each method:
|
|
282
|
+
- Use `replace` for short, inline replacements or dynamically generated content.
|
|
283
|
+
- Use `replaceKeyFromFile` for longer texts or content that's stored externally.
|
|
284
|
+
|
|
285
|
+
Both methods allow for flexible content insertion, enabling you to create dynamic and customizable prompts for your AI model interactions.
|
|
286
|
+
|
|
287
|
+
## Enabling Debug Mode in ModelMix
|
|
288
|
+
|
|
289
|
+
To activate debug mode in ModelMix and view detailed request information, follow these two steps:
|
|
290
|
+
|
|
291
|
+
1. In the ModelMix constructor, include `debug: true` in the configuration:
|
|
292
|
+
|
|
293
|
+
```javascript
|
|
294
|
+
const mix = new ModelMix({
|
|
295
|
+
config: {
|
|
296
|
+
debug: true
|
|
297
|
+
// ... other configuration options ...
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
2. When running your script from the command line, use the `DEBUG=ModelMix*` prefix:
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
DEBUG=ModelMix* node your_script.js
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
When you run your script this way, you'll see detailed information about the requests in the console, including the configuration and options used for each AI model request.
|
|
309
|
+
|
|
310
|
+
This information is valuable for debugging and understanding how ModelMix is processing your requests.
|
|
311
|
+
|
|
224
312
|
## 🤝 Contributing
|
|
225
313
|
|
|
226
314
|
Contributions are welcome! If you find any issues or have suggestions for improvement, please open an issue or submit a pull request on the [GitHub repository](https://github.com/clasen/ModelMix).
|
package/demo/demo.mjs
CHANGED
|
@@ -9,6 +9,7 @@ const mmix = new ModelMix({
|
|
|
9
9
|
system: 'You are {name} from Melmac.',
|
|
10
10
|
max_history: 2,
|
|
11
11
|
max_request: 1,
|
|
12
|
+
debug: true,
|
|
12
13
|
}
|
|
13
14
|
});
|
|
14
15
|
|
|
@@ -38,18 +39,28 @@ console.log(await gpt.message());
|
|
|
38
39
|
|
|
39
40
|
console.log("\n" + '--------| claude-3-5-sonnet-20240620 |--------');
|
|
40
41
|
const claude = mmix.create('claude-3-5-sonnet-20240620', { temperature: 0.5 });
|
|
41
|
-
claude.
|
|
42
|
+
claude.addImageFromUrl('https://pbs.twimg.com/media/F6-GsjraAAADDGy?format=jpg');
|
|
42
43
|
const imageDescription = await claude.addText('describe the image').message();
|
|
43
44
|
console.log(imageDescription);
|
|
44
45
|
|
|
46
|
+
console.log("\n" + '--------| claude-3-5-sonnet-20240620 |--------');
|
|
47
|
+
const setup = {
|
|
48
|
+
config: { system: "You are a writer like Stephen King" },
|
|
49
|
+
options: { temperature: 0.5 }
|
|
50
|
+
}
|
|
51
|
+
const writer = mmix.create('claude-3-5-sonnet-20240620', setup);
|
|
52
|
+
writer.replaceKeyFromFile('{story_title}', './title.md');
|
|
53
|
+
const story = await writer.addTextFromFile('./prompt.md').message();
|
|
54
|
+
console.log(story);
|
|
55
|
+
|
|
45
56
|
console.log("\n" + '--------| llama-3-sonar-large-32k-online |--------');
|
|
46
57
|
const pplx = mmix.create('llama-3-sonar-large-32k-online', { max_tokens: 500 });
|
|
47
58
|
pplx.addText('How much is ETH trading in USD?');
|
|
48
59
|
const news = await pplx.addText('What are the 3 most recent Ethereum news?').message();
|
|
49
60
|
console.log(news);
|
|
50
61
|
|
|
51
|
-
console.log("\n" + '--------| ollama (llava:latest) |--------');
|
|
52
|
-
await mmix.create('llava:latest')
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
// console.log("\n" + '--------| ollama (llava:latest) |--------');
|
|
63
|
+
// await mmix.create('llava:latest')
|
|
64
|
+
// .addImage('./watson.jpg')
|
|
65
|
+
// .addText('what is the predominant color?')
|
|
66
|
+
// .stream((data) => { console.log(data.message); });
|
package/demo/prompt.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Writing Prompt: {story_title}
|
|
2
|
+
|
|
3
|
+
## Setting
|
|
4
|
+
A small, quiet town where nothing unusual ever happens.
|
|
5
|
+
|
|
6
|
+
## Main Character
|
|
7
|
+
You are a postal worker who has worked in this town for over 20 years.
|
|
8
|
+
|
|
9
|
+
## Situation
|
|
10
|
+
One day, you come across a package with these unusual characteristics:
|
|
11
|
+
- No return address
|
|
12
|
+
- Addressed to a person who doesn't exist in town
|
|
13
|
+
- Emits a faint, pulsing glow
|
|
14
|
+
- Feels unusually warm to the touch
|
|
15
|
+
|
|
16
|
+
## Writing Task
|
|
17
|
+
Write a short story (500-1000 words) that explores:
|
|
18
|
+
1. Your character's decision on what to do with the package
|
|
19
|
+
2. The consequences of that decision
|
|
20
|
+
3. The ultimate revelation of what's inside the package and its significance
|
|
21
|
+
|
|
22
|
+
### Additional Elements to Consider
|
|
23
|
+
- The reactions of other townspeople
|
|
24
|
+
- Any changes in the town's atmosphere after the package's arrival
|
|
25
|
+
- Your character's internal conflict between curiosity and duty
|
package/demo/title.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The Mysterious Package
|
package/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const axios = require('axios');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const mime = require('mime-types');
|
|
4
|
+
const log = require('lemonlog')('ModelMix');
|
|
4
5
|
|
|
5
6
|
class ModelMix {
|
|
6
7
|
constructor(args = { options: {}, config: {} }) {
|
|
@@ -16,6 +17,7 @@ class ModelMix {
|
|
|
16
17
|
system: 'You are an assistant.',
|
|
17
18
|
max_request: 1,
|
|
18
19
|
max_history: 5, // Default max history
|
|
20
|
+
debug: false,
|
|
19
21
|
...args.config
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -89,6 +91,8 @@ class MessageHandler {
|
|
|
89
91
|
this.options = options;
|
|
90
92
|
this.config = config;
|
|
91
93
|
this.messages = [];
|
|
94
|
+
|
|
95
|
+
this.imagesToProcess = [];
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
new() {
|
|
@@ -106,6 +110,16 @@ class MessageHandler {
|
|
|
106
110
|
return this;
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
addTextFromFile(filePath, config = { role: "user" }) {
|
|
114
|
+
try {
|
|
115
|
+
const content = fs.readFileSync(filePath, { encoding: 'utf8' });
|
|
116
|
+
this.addText(content, config);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error(`Error reading file ${filePath}:`, error);
|
|
119
|
+
}
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
|
|
109
123
|
addImage(filePath, config = { role: "user" }) {
|
|
110
124
|
try {
|
|
111
125
|
const imageBuffer = fs.readFileSync(filePath);
|
|
@@ -139,6 +153,46 @@ class MessageHandler {
|
|
|
139
153
|
return this;
|
|
140
154
|
}
|
|
141
155
|
|
|
156
|
+
addImageFromUrl(url, config = { role: "user" }) {
|
|
157
|
+
this.imagesToProcess.push({ url, config });
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async processImageUrls() {
|
|
162
|
+
const imageContents = await Promise.all(
|
|
163
|
+
this.imagesToProcess.map(async (image) => {
|
|
164
|
+
try {
|
|
165
|
+
const response = await axios.get(image.url, { responseType: 'arraybuffer' });
|
|
166
|
+
const base64 = Buffer.from(response.data, 'binary').toString('base64');
|
|
167
|
+
const mimeType = response.headers['content-type'];
|
|
168
|
+
return { base64, mimeType, config: image.config };
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error(`Error descargando imagen desde ${image.url}:`, error);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
imageContents.forEach((image) => {
|
|
177
|
+
if (image) {
|
|
178
|
+
const imageMessage = {
|
|
179
|
+
...image.config,
|
|
180
|
+
content: [
|
|
181
|
+
{
|
|
182
|
+
type: "image",
|
|
183
|
+
"source": {
|
|
184
|
+
type: "base64",
|
|
185
|
+
media_type: image.mimeType,
|
|
186
|
+
data: image.base64
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
};
|
|
191
|
+
this.messages.push(imageMessage);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
142
196
|
async message() {
|
|
143
197
|
this.options.stream = false;
|
|
144
198
|
const response = await this.execute();
|
|
@@ -161,6 +215,16 @@ class MessageHandler {
|
|
|
161
215
|
return this;
|
|
162
216
|
}
|
|
163
217
|
|
|
218
|
+
replaceKeyFromFile(key, filePath) {
|
|
219
|
+
try {
|
|
220
|
+
const content = fs.readFileSync(filePath, { encoding: 'utf8' });
|
|
221
|
+
this.replace({ [key]: content });
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error(`Error reading file ${filePath}:`, error);
|
|
224
|
+
}
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
|
|
164
228
|
template(input, replace) {
|
|
165
229
|
return input.split(/([¿?¡!,"';:\.\s])/).map(x => x in replace ? replace[x] : x).join("");
|
|
166
230
|
}
|
|
@@ -197,12 +261,14 @@ class MessageHandler {
|
|
|
197
261
|
|
|
198
262
|
async execute() {
|
|
199
263
|
|
|
264
|
+
await this.processImageUrls();
|
|
265
|
+
|
|
200
266
|
this.applyTemplate();
|
|
201
267
|
this.messages = this.messages.slice(-this.config.max_history);
|
|
202
268
|
this.messages = this.groupByRoles(this.messages);
|
|
203
269
|
|
|
204
270
|
if (this.messages.length === 0) {
|
|
205
|
-
throw new Error("No user messages have been added. Use
|
|
271
|
+
throw new Error("No user messages have been added. Use addText(prompt), addTextFromFile(filePath), addImage(filePath), or addImageFromUrl(url) to add a prompt.");
|
|
206
272
|
}
|
|
207
273
|
|
|
208
274
|
this.options.messages = this.messages;
|
|
@@ -255,6 +321,13 @@ class MixCustom {
|
|
|
255
321
|
|
|
256
322
|
async create(args = { config: {}, options: {} }) {
|
|
257
323
|
|
|
324
|
+
if (args.config.debug) {
|
|
325
|
+
log.info("config");
|
|
326
|
+
log.info(args.config);
|
|
327
|
+
log.inspect("options");
|
|
328
|
+
log.inspect(args.options);
|
|
329
|
+
}
|
|
330
|
+
|
|
258
331
|
if (args.options.stream) {
|
|
259
332
|
return this.processStream(await axios.post(this.config.url, args.options, {
|
|
260
333
|
headers: this.headers,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modelmix",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "🧬 ModelMix - Unified API for Diverse AI Language Models.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://github.com/clasen/ModelMix#readme",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"axios": "^1.6.8"
|
|
39
|
+
"axios": "^1.6.8",
|
|
40
|
+
"lemonlog": "^1.1.2"
|
|
40
41
|
}
|
|
41
|
-
}
|
|
42
|
+
}
|
package/demo/watson.jpg
DELETED
|
Binary file
|