@heyputer/puter.js 2.1.6 → 2.1.8
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/dist/puter.cjs +2 -2
- package/index.d.ts +103 -626
- package/package.json +1 -1
- package/src/index.js +91 -91
- package/src/lib/APICallLogger.js +20 -21
- package/src/lib/EventListener.js +10 -10
- package/src/lib/filesystem/APIFS.js +11 -19
- package/src/lib/filesystem/CacheFS.js +25 -25
- package/src/lib/filesystem/PostMessageFS.js +11 -11
- package/src/lib/filesystem/definitions.js +11 -10
- package/src/lib/path.js +505 -446
- package/src/lib/polyfills/fileReaderPoly.js +40 -0
- package/src/lib/polyfills/localStorage.js +30 -33
- package/src/lib/polyfills/xhrshim.js +206 -207
- package/src/lib/utils.js +160 -151
- package/src/lib/xdrpc.js +9 -9
- package/src/modules/AI.js +416 -290
- package/src/modules/Apps.js +56 -56
- package/src/modules/Auth.js +17 -17
- package/src/modules/Debug.js +1 -1
- package/src/modules/Drivers.js +41 -41
- package/src/modules/FSItem.js +64 -62
- package/src/modules/FileSystem/index.js +22 -23
- package/src/modules/FileSystem/operations/copy.js +7 -7
- package/src/modules/FileSystem/operations/deleteFSEntry.js +14 -12
- package/src/modules/FileSystem/operations/getReadUrl.js +16 -14
- package/src/modules/FileSystem/operations/mkdir.js +11 -11
- package/src/modules/FileSystem/operations/move.js +12 -12
- package/src/modules/FileSystem/operations/read.js +10 -10
- package/src/modules/FileSystem/operations/readdir.js +28 -28
- package/src/modules/FileSystem/operations/rename.js +11 -11
- package/src/modules/FileSystem/operations/sign.js +33 -30
- package/src/modules/FileSystem/operations/space.js +7 -7
- package/src/modules/FileSystem/operations/stat.js +25 -25
- package/src/modules/FileSystem/operations/symlink.js +15 -17
- package/src/modules/FileSystem/operations/upload.js +151 -122
- package/src/modules/FileSystem/operations/write.js +16 -12
- package/src/modules/FileSystem/utils/getAbsolutePathForApp.js +10 -6
- package/src/modules/Hosting.js +29 -29
- package/src/modules/KV.js +23 -23
- package/src/modules/OS.js +15 -15
- package/src/modules/Perms.js +19 -21
- package/src/modules/PuterDialog.js +46 -48
- package/src/modules/Threads.js +17 -20
- package/src/modules/UI.js +156 -156
- package/src/modules/Util.js +3 -3
- package/src/modules/Workers.js +52 -49
- package/src/modules/networking/PSocket.js +38 -38
- package/src/modules/networking/PTLS.js +54 -47
- package/src/modules/networking/PWispHandler.js +49 -47
- package/src/modules/networking/parsers.js +110 -108
- package/src/modules/networking/requests.js +67 -78
- package/src/services/APIAccess.js +9 -9
- package/src/services/FSRelay.js +6 -6
- package/src/services/Filesystem.js +8 -8
- package/src/services/NoPuterYet.js +2 -2
- package/src/services/XDIncoming.js +1 -1
package/src/modules/AI.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as utils from '../lib/utils.js';
|
|
2
2
|
|
|
3
3
|
const normalizeTTSProvider = (value) => {
|
|
4
|
-
if (typeof value !== 'string') {
|
|
4
|
+
if ( typeof value !== 'string' ) {
|
|
5
5
|
return 'aws-polly';
|
|
6
6
|
}
|
|
7
7
|
const lower = value.toLowerCase();
|
|
8
|
-
if (lower === 'openai') return 'openai';
|
|
9
|
-
if (
|
|
8
|
+
if ( lower === 'openai' ) return 'openai';
|
|
9
|
+
if ( ['elevenlabs', 'eleven', '11labs', '11-labs', 'eleven-labs', 'elevenlabs-tts'].includes(lower) ) return 'elevenlabs';
|
|
10
|
+
if ( lower === 'aws' || lower === 'polly' || lower === 'aws-polly' ) return 'aws-polly';
|
|
10
11
|
return value;
|
|
11
12
|
};
|
|
12
13
|
|
|
@@ -20,6 +21,13 @@ const TOGETHER_IMAGE_MODEL_PREFIXES = [
|
|
|
20
21
|
'sg161222/',
|
|
21
22
|
'wavymulder/',
|
|
22
23
|
'prompthero/',
|
|
24
|
+
'bytedance-seed/',
|
|
25
|
+
'hidream-ai/',
|
|
26
|
+
'lykon/',
|
|
27
|
+
'qwen/',
|
|
28
|
+
'rundiffusion/',
|
|
29
|
+
'google/',
|
|
30
|
+
'ideogram/',
|
|
23
31
|
];
|
|
24
32
|
|
|
25
33
|
const TOGETHER_IMAGE_MODEL_KEYWORDS = [
|
|
@@ -40,7 +48,7 @@ const TOGETHER_VIDEO_MODEL_PREFIXES = [
|
|
|
40
48
|
'wan-ai/',
|
|
41
49
|
];
|
|
42
50
|
|
|
43
|
-
class AI{
|
|
51
|
+
class AI {
|
|
44
52
|
/**
|
|
45
53
|
* Creates a new instance with the given authentication token, API origin, and app ID,
|
|
46
54
|
*
|
|
@@ -68,7 +76,7 @@ class AI{
|
|
|
68
76
|
|
|
69
77
|
/**
|
|
70
78
|
* Sets the API origin.
|
|
71
|
-
*
|
|
79
|
+
*
|
|
72
80
|
* @param {string} APIOrigin - The new API origin.
|
|
73
81
|
* @memberof [AI]
|
|
74
82
|
* @returns {void}
|
|
@@ -77,83 +85,96 @@ class AI{
|
|
|
77
85
|
this.APIOrigin = APIOrigin;
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
|
|
88
|
+
/**
|
|
81
89
|
* Returns a list of available AI models.
|
|
82
90
|
* @param {string} provider - The provider to filter the models returned.
|
|
83
|
-
* @returns {
|
|
91
|
+
* @returns {Array} Array containing available model objects
|
|
84
92
|
*/
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
async listModels (provider) {
|
|
94
|
+
// Prefer the public API endpoint and fall back to the legacy driver call if needed.
|
|
95
|
+
const headers = this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {};
|
|
96
|
+
|
|
97
|
+
const tryFetchModels = async () => {
|
|
98
|
+
const resp = await fetch(`${this.APIOrigin }/puterai/chat/models/details`, { headers });
|
|
99
|
+
if ( !resp.ok ) return null;
|
|
100
|
+
const data = await resp.json();
|
|
101
|
+
const models = Array.isArray(data?.models) ? data.models : [];
|
|
102
|
+
return provider ? models.filter(model => model.provider === provider) : models;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const tryDriverModels = async () => {
|
|
106
|
+
const models = await puter.drivers.call('puter-chat-completion', 'ai-chat', 'models');
|
|
107
|
+
const result = Array.isArray(models?.result) ? models.result : [];
|
|
108
|
+
return provider ? result.filter(model => model.provider === provider) : result;
|
|
109
|
+
};
|
|
87
110
|
|
|
88
|
-
const models = await
|
|
111
|
+
const models = await (async () => {
|
|
112
|
+
try {
|
|
113
|
+
const apiModels = await tryFetchModels();
|
|
114
|
+
if ( apiModels !== null ) return apiModels;
|
|
115
|
+
} catch (e) {
|
|
116
|
+
// Ignore and fall back to the driver call below.
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
return await tryDriverModels();
|
|
120
|
+
} catch (e) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
})();
|
|
89
124
|
|
|
90
|
-
|
|
91
|
-
return modelsByProvider;
|
|
92
|
-
}
|
|
93
|
-
models.result.forEach(item => {
|
|
94
|
-
if (!item.provider || !item.id) return;
|
|
95
|
-
if (provider && item.provider !== provider) return;
|
|
96
|
-
if (!modelsByProvider[item.provider]) modelsByProvider[item.provider] = [];
|
|
97
|
-
modelsByProvider[item.provider].push(item.id);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
return modelsByProvider;
|
|
125
|
+
return models;
|
|
101
126
|
}
|
|
102
127
|
|
|
103
128
|
/**
|
|
104
129
|
* Returns a list of all available AI providers
|
|
105
130
|
* @returns {Array} Array containing providers
|
|
106
131
|
*/
|
|
107
|
-
async listModelProviders() {
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
providers = new Set(); // Use a Set to store unique providers
|
|
113
|
-
models.result.forEach(item => {
|
|
114
|
-
if (item.provider) providers.add(item.provider);
|
|
132
|
+
async listModelProviders () {
|
|
133
|
+
const models = await this.listModels();
|
|
134
|
+
const providers = new Set();
|
|
135
|
+
(models ?? []).forEach(item => {
|
|
136
|
+
if ( item?.provider ) providers.add(item.provider);
|
|
115
137
|
});
|
|
116
|
-
|
|
117
|
-
return providers;
|
|
138
|
+
return Array.from(providers);
|
|
118
139
|
}
|
|
119
|
-
|
|
140
|
+
|
|
120
141
|
img2txt = async (...args) => {
|
|
121
142
|
const MAX_INPUT_SIZE = 10 * 1024 * 1024;
|
|
122
|
-
if (!args || args.length === 0) {
|
|
143
|
+
if ( !args || args.length === 0 ) {
|
|
123
144
|
throw { message: 'Arguments are required', code: 'arguments_required' };
|
|
124
145
|
}
|
|
125
146
|
|
|
126
147
|
const isBlobLike = (value) => {
|
|
127
|
-
if (typeof Blob === 'undefined') return false;
|
|
148
|
+
if ( typeof Blob === 'undefined' ) return false;
|
|
128
149
|
return value instanceof Blob || (typeof File !== 'undefined' && value instanceof File);
|
|
129
150
|
};
|
|
130
151
|
const isPlainObject = (value) => value && typeof value === 'object' && !Array.isArray(value) && !isBlobLike(value);
|
|
131
152
|
const normalizeProvider = (value) => {
|
|
132
|
-
if (!value) return 'aws-textract';
|
|
153
|
+
if ( ! value ) return 'aws-textract';
|
|
133
154
|
const normalized = String(value).toLowerCase();
|
|
134
|
-
if (['aws', 'textract', 'aws-textract'].includes(normalized)) return 'aws-textract';
|
|
135
|
-
if (['mistral', 'mistral-ocr'].includes(normalized)) return 'mistral';
|
|
155
|
+
if ( ['aws', 'textract', 'aws-textract'].includes(normalized) ) return 'aws-textract';
|
|
156
|
+
if ( ['mistral', 'mistral-ocr'].includes(normalized) ) return 'mistral';
|
|
136
157
|
return 'aws-textract';
|
|
137
158
|
};
|
|
138
159
|
|
|
139
160
|
let options = {};
|
|
140
|
-
if (isPlainObject(args[0])) {
|
|
161
|
+
if ( isPlainObject(args[0]) ) {
|
|
141
162
|
options = { ...args[0] };
|
|
142
163
|
} else {
|
|
143
164
|
options.source = args[0];
|
|
144
165
|
}
|
|
145
166
|
|
|
146
167
|
let testMode = false;
|
|
147
|
-
for (let i = 1; i < args.length; i++) {
|
|
168
|
+
for ( let i = 1; i < args.length; i++ ) {
|
|
148
169
|
const value = args[i];
|
|
149
|
-
if (typeof value === 'boolean') {
|
|
170
|
+
if ( typeof value === 'boolean' ) {
|
|
150
171
|
testMode = testMode || value;
|
|
151
|
-
} else if (isPlainObject(value)) {
|
|
172
|
+
} else if ( isPlainObject(value) ) {
|
|
152
173
|
options = { ...options, ...value };
|
|
153
174
|
}
|
|
154
175
|
}
|
|
155
176
|
|
|
156
|
-
if (typeof options.testMode === 'boolean') {
|
|
177
|
+
if ( typeof options.testMode === 'boolean' ) {
|
|
157
178
|
testMode = options.testMode;
|
|
158
179
|
}
|
|
159
180
|
|
|
@@ -161,46 +182,46 @@ class AI{
|
|
|
161
182
|
delete options.provider;
|
|
162
183
|
delete options.testMode;
|
|
163
184
|
|
|
164
|
-
if (!options.source) {
|
|
185
|
+
if ( ! options.source ) {
|
|
165
186
|
throw { message: 'Source is required', code: 'source_required' };
|
|
166
187
|
}
|
|
167
188
|
|
|
168
|
-
if (isBlobLike(options.source)) {
|
|
189
|
+
if ( isBlobLike(options.source) ) {
|
|
169
190
|
options.source = await utils.blobToDataUri(options.source);
|
|
170
|
-
} else if (options.source?.source && isBlobLike(options.source.source)) {
|
|
191
|
+
} else if ( options.source?.source && isBlobLike(options.source.source) ) {
|
|
171
192
|
// Support shape { source: Blob }
|
|
172
193
|
options.source = await utils.blobToDataUri(options.source.source);
|
|
173
194
|
}
|
|
174
195
|
|
|
175
|
-
if (typeof options.source === 'string' &&
|
|
196
|
+
if ( typeof options.source === 'string' &&
|
|
176
197
|
options.source.startsWith('data:') &&
|
|
177
|
-
options.source.length > MAX_INPUT_SIZE) {
|
|
178
|
-
throw { message:
|
|
198
|
+
options.source.length > MAX_INPUT_SIZE ) {
|
|
199
|
+
throw { message: `Input size cannot be larger than ${ MAX_INPUT_SIZE}`, code: 'input_too_large' };
|
|
179
200
|
}
|
|
180
201
|
|
|
181
202
|
const toText = (result) => {
|
|
182
|
-
if (!result) return '';
|
|
183
|
-
if (Array.isArray(result.blocks) && result.blocks.length) {
|
|
203
|
+
if ( ! result ) return '';
|
|
204
|
+
if ( Array.isArray(result.blocks) && result.blocks.length ) {
|
|
184
205
|
let str = '';
|
|
185
|
-
for (const block of result.blocks) {
|
|
186
|
-
if (typeof block?.text !== 'string') continue;
|
|
187
|
-
if (!block.type || block.type === 'text/textract:LINE' || block.type.startsWith('text/')) {
|
|
188
|
-
str += block.text
|
|
206
|
+
for ( const block of result.blocks ) {
|
|
207
|
+
if ( typeof block?.text !== 'string' ) continue;
|
|
208
|
+
if ( !block.type || block.type === 'text/textract:LINE' || block.type.startsWith('text/') ) {
|
|
209
|
+
str += `${block.text }\n`;
|
|
189
210
|
}
|
|
190
211
|
}
|
|
191
|
-
if (str.trim()) return str;
|
|
212
|
+
if ( str.trim() ) return str;
|
|
192
213
|
}
|
|
193
|
-
if (Array.isArray(result.pages) && result.pages.length) {
|
|
214
|
+
if ( Array.isArray(result.pages) && result.pages.length ) {
|
|
194
215
|
const markdown = result.pages
|
|
195
216
|
.map(page => (page?.markdown || '').trim())
|
|
196
217
|
.filter(Boolean)
|
|
197
218
|
.join('\n\n');
|
|
198
|
-
if (markdown.trim()) return markdown;
|
|
219
|
+
if ( markdown.trim() ) return markdown;
|
|
199
220
|
}
|
|
200
|
-
if (typeof result.document_annotation === 'string') {
|
|
221
|
+
if ( typeof result.document_annotation === 'string' ) {
|
|
201
222
|
return result.document_annotation;
|
|
202
223
|
}
|
|
203
|
-
if (typeof result.text === 'string') {
|
|
224
|
+
if ( typeof result.text === 'string' ) {
|
|
204
225
|
return result.text;
|
|
205
226
|
}
|
|
206
227
|
return '';
|
|
@@ -212,17 +233,17 @@ class AI{
|
|
|
212
233
|
});
|
|
213
234
|
|
|
214
235
|
return await driverCall.call(this, options);
|
|
215
|
-
}
|
|
236
|
+
};
|
|
216
237
|
|
|
217
238
|
txt2speech = async (...args) => {
|
|
218
239
|
let MAX_INPUT_SIZE = 3000;
|
|
219
240
|
let options = {};
|
|
220
241
|
let testMode = false;
|
|
221
242
|
|
|
222
|
-
if(!args){
|
|
223
|
-
throw({message: 'Arguments are required', code: 'arguments_required'});
|
|
243
|
+
if ( ! args ) {
|
|
244
|
+
throw ({ message: 'Arguments are required', code: 'arguments_required' });
|
|
224
245
|
}
|
|
225
|
-
|
|
246
|
+
|
|
226
247
|
// Accept arguments in the following formats:
|
|
227
248
|
// 1. Shorthand API
|
|
228
249
|
// puter.ai.txt2speech("Hello world")
|
|
@@ -238,123 +259,258 @@ class AI{
|
|
|
238
259
|
// puter.ai.txt2speech("Hello world", "en-US")
|
|
239
260
|
// puter.ai.txt2speech("Hello world", "en-US", "Joanna")
|
|
240
261
|
// puter.ai.txt2speech("Hello world", "en-US", "Joanna", "neural")
|
|
241
|
-
//
|
|
262
|
+
//
|
|
242
263
|
// Undefined parameters will be set to default values:
|
|
243
264
|
// - voice: "Joanna"
|
|
244
265
|
// - engine: "standard"
|
|
245
266
|
// - language: "en-US"
|
|
246
267
|
|
|
247
|
-
|
|
248
|
-
if (typeof args[0] === 'string') {
|
|
268
|
+
if ( typeof args[0] === 'string' ) {
|
|
249
269
|
options = { text: args[0] };
|
|
250
270
|
}
|
|
251
271
|
|
|
252
|
-
if (args[1] && typeof args[1] === 'object' && !Array.isArray(args[1])) {
|
|
272
|
+
if ( args[1] && typeof args[1] === 'object' && !Array.isArray(args[1]) ) {
|
|
253
273
|
// for verbose object API
|
|
254
274
|
Object.assign(options, args[1]);
|
|
255
|
-
} else if (args[1] && typeof args[1] === 'string') {
|
|
275
|
+
} else if ( args[1] && typeof args[1] === 'string' ) {
|
|
256
276
|
// for legacy positional-arguments API
|
|
257
|
-
//
|
|
277
|
+
//
|
|
258
278
|
// puter.ai.txt2speech(<text>, <language>, <voice>, <engine>)
|
|
259
279
|
options.language = args[1];
|
|
260
|
-
|
|
261
|
-
if (args[2] && typeof args[2] === 'string') {
|
|
280
|
+
|
|
281
|
+
if ( args[2] && typeof args[2] === 'string' ) {
|
|
262
282
|
options.voice = args[2];
|
|
263
283
|
}
|
|
264
|
-
|
|
265
|
-
if (args[3] && typeof args[3] === 'string') {
|
|
284
|
+
|
|
285
|
+
if ( args[3] && typeof args[3] === 'string' ) {
|
|
266
286
|
options.engine = args[3];
|
|
267
287
|
}
|
|
268
|
-
} else if (args[1] && typeof args[1] !== 'boolean') {
|
|
288
|
+
} else if ( args[1] && typeof args[1] !== 'boolean' ) {
|
|
269
289
|
// If second argument is not an object, string, or boolean, throw an error
|
|
270
290
|
throw { message: 'Second argument must be an options object or language string. Use: txt2speech("text", { voice: "name", engine: "type", language: "code" }) or txt2speech("text", "language", "voice", "engine")', code: 'invalid_arguments' };
|
|
271
291
|
}
|
|
272
292
|
|
|
273
293
|
// Validate required text parameter
|
|
274
|
-
if (!options.text) {
|
|
294
|
+
if ( ! options.text ) {
|
|
275
295
|
throw { message: 'Text parameter is required', code: 'text_required' };
|
|
276
296
|
}
|
|
277
297
|
|
|
278
298
|
const validEngines = ['standard', 'neural', 'long-form', 'generative'];
|
|
279
299
|
let provider = normalizeTTSProvider(options.provider);
|
|
280
300
|
|
|
281
|
-
if (options.engine && normalizeTTSProvider(options.engine) === 'openai' && !options.provider) {
|
|
301
|
+
if ( options.engine && normalizeTTSProvider(options.engine) === 'openai' && !options.provider ) {
|
|
282
302
|
provider = 'openai';
|
|
283
303
|
}
|
|
284
304
|
|
|
285
|
-
if (
|
|
286
|
-
|
|
305
|
+
if ( options.engine && normalizeTTSProvider(options.engine) === 'elevenlabs' && !options.provider ) {
|
|
306
|
+
provider = 'elevenlabs';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if ( provider === 'openai' ) {
|
|
310
|
+
if ( !options.model && typeof options.engine === 'string' ) {
|
|
287
311
|
options.model = options.engine;
|
|
288
312
|
}
|
|
289
|
-
if (!options.voice) {
|
|
313
|
+
if ( ! options.voice ) {
|
|
290
314
|
options.voice = 'alloy';
|
|
291
315
|
}
|
|
292
|
-
if (!options.model) {
|
|
316
|
+
if ( ! options.model ) {
|
|
293
317
|
options.model = 'gpt-4o-mini-tts';
|
|
294
318
|
}
|
|
295
|
-
if (!options.response_format) {
|
|
319
|
+
if ( ! options.response_format ) {
|
|
296
320
|
options.response_format = 'mp3';
|
|
297
321
|
}
|
|
298
322
|
delete options.engine;
|
|
323
|
+
} else if ( provider === 'elevenlabs' ) {
|
|
324
|
+
if ( ! options.voice ) {
|
|
325
|
+
options.voice = '21m00Tcm4TlvDq8ikWAM';
|
|
326
|
+
}
|
|
327
|
+
if ( ! options.model && typeof options.engine === 'string' ) {
|
|
328
|
+
options.model = options.engine;
|
|
329
|
+
}
|
|
330
|
+
if ( ! options.model ) {
|
|
331
|
+
options.model = 'eleven_multilingual_v2';
|
|
332
|
+
}
|
|
333
|
+
if ( ! options.output_format && !options.response_format ) {
|
|
334
|
+
options.output_format = 'mp3_44100_128';
|
|
335
|
+
}
|
|
336
|
+
if ( options.response_format && !options.output_format ) {
|
|
337
|
+
options.output_format = options.response_format;
|
|
338
|
+
}
|
|
339
|
+
delete options.engine;
|
|
299
340
|
} else {
|
|
300
341
|
provider = 'aws-polly';
|
|
301
342
|
|
|
302
|
-
if (options.engine && !validEngines.includes(options.engine)) {
|
|
303
|
-
throw { message:
|
|
343
|
+
if ( options.engine && !validEngines.includes(options.engine) ) {
|
|
344
|
+
throw { message: `Invalid engine. Must be one of: ${ validEngines.join(', ')}`, code: 'invalid_engine' };
|
|
304
345
|
}
|
|
305
346
|
|
|
306
|
-
if (!options.voice) {
|
|
347
|
+
if ( ! options.voice ) {
|
|
307
348
|
options.voice = 'Joanna';
|
|
308
349
|
}
|
|
309
|
-
if (!options.engine) {
|
|
350
|
+
if ( ! options.engine ) {
|
|
310
351
|
options.engine = 'standard';
|
|
311
352
|
}
|
|
312
|
-
if (!options.language) {
|
|
353
|
+
if ( ! options.language ) {
|
|
313
354
|
options.language = 'en-US';
|
|
314
355
|
}
|
|
315
356
|
}
|
|
316
357
|
|
|
317
358
|
// check input size
|
|
318
|
-
if (options.text.length > MAX_INPUT_SIZE) {
|
|
319
|
-
throw { message:
|
|
359
|
+
if ( options.text.length > MAX_INPUT_SIZE ) {
|
|
360
|
+
throw { message: `Input size cannot be larger than ${ MAX_INPUT_SIZE}`, code: 'input_too_large' };
|
|
320
361
|
}
|
|
321
362
|
|
|
322
363
|
// determine if test mode is enabled (check all arguments for boolean true)
|
|
323
|
-
for (let i = 0; i < args.length; i++) {
|
|
324
|
-
if (typeof args[i] === 'boolean' && args[i] === true) {
|
|
364
|
+
for ( let i = 0; i < args.length; i++ ) {
|
|
365
|
+
if ( typeof args[i] === 'boolean' && args[i] === true ) {
|
|
325
366
|
testMode = true;
|
|
326
367
|
break;
|
|
327
368
|
}
|
|
328
369
|
}
|
|
329
370
|
|
|
330
|
-
const driverName = provider === 'openai'
|
|
371
|
+
const driverName = provider === 'openai'
|
|
372
|
+
? 'openai-tts'
|
|
373
|
+
: (provider === 'elevenlabs' ? 'elevenlabs-tts' : 'aws-polly');
|
|
331
374
|
|
|
332
375
|
return await utils.make_driver_method(['source'], 'puter-tts', driverName, 'synthesize', {
|
|
333
376
|
responseType: 'blob',
|
|
334
377
|
test_mode: testMode ?? false,
|
|
335
378
|
transform: async (result) => {
|
|
336
379
|
let url;
|
|
337
|
-
if (typeof result === 'string') {
|
|
380
|
+
if ( typeof result === 'string' ) {
|
|
338
381
|
url = result;
|
|
339
|
-
} else if (result instanceof Blob) {
|
|
382
|
+
} else if ( result instanceof Blob ) {
|
|
340
383
|
url = await utils.blob_to_url(result);
|
|
341
|
-
} else if (result instanceof ArrayBuffer) {
|
|
384
|
+
} else if ( result instanceof ArrayBuffer ) {
|
|
342
385
|
const blob = new Blob([result]);
|
|
343
386
|
url = await utils.blob_to_url(blob);
|
|
344
|
-
} else if (result && typeof result === 'object' && typeof result.arrayBuffer === 'function') {
|
|
387
|
+
} else if ( result && typeof result === 'object' && typeof result.arrayBuffer === 'function' ) {
|
|
345
388
|
const arrayBuffer = await result.arrayBuffer();
|
|
346
389
|
const blob = new Blob([arrayBuffer], { type: result.type || undefined });
|
|
347
390
|
url = await utils.blob_to_url(blob);
|
|
348
391
|
} else {
|
|
349
392
|
throw { message: 'Unexpected audio response format', code: 'invalid_audio_response' };
|
|
350
393
|
}
|
|
351
|
-
const audio = new Audio(
|
|
394
|
+
const audio = new (globalThis.Audio || Object)();
|
|
395
|
+
audio.src = url;
|
|
352
396
|
audio.toString = () => url;
|
|
353
397
|
audio.valueOf = () => url;
|
|
354
398
|
return audio;
|
|
355
|
-
}
|
|
399
|
+
},
|
|
356
400
|
}).call(this, options);
|
|
357
|
-
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
speech2speech = async (...args) => {
|
|
404
|
+
const MAX_INPUT_SIZE = 25 * 1024 * 1024;
|
|
405
|
+
if ( !args || !args.length ) {
|
|
406
|
+
throw ({ message: 'Arguments are required', code: 'arguments_required' });
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const normalizeSource = async (value) => {
|
|
410
|
+
if ( value instanceof Blob ) {
|
|
411
|
+
return await utils.blobToDataUri(value);
|
|
412
|
+
}
|
|
413
|
+
return value;
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const normalizeOptions = (opts = {}) => {
|
|
417
|
+
const normalized = { ...opts };
|
|
418
|
+
if ( normalized.voiceId && !normalized.voice && !normalized.voice_id ) normalized.voice = normalized.voiceId;
|
|
419
|
+
if ( normalized.modelId && !normalized.model && !normalized.model_id ) normalized.model = normalized.modelId;
|
|
420
|
+
if ( normalized.outputFormat && !normalized.output_format ) normalized.output_format = normalized.outputFormat;
|
|
421
|
+
if ( normalized.voiceSettings && !normalized.voice_settings ) normalized.voice_settings = normalized.voiceSettings;
|
|
422
|
+
if ( normalized.fileFormat && !normalized.file_format ) normalized.file_format = normalized.fileFormat;
|
|
423
|
+
if ( normalized.removeBackgroundNoise !== undefined && normalized.remove_background_noise === undefined ) {
|
|
424
|
+
normalized.remove_background_noise = normalized.removeBackgroundNoise;
|
|
425
|
+
}
|
|
426
|
+
if ( normalized.optimizeStreamingLatency !== undefined && normalized.optimize_streaming_latency === undefined ) {
|
|
427
|
+
normalized.optimize_streaming_latency = normalized.optimizeStreamingLatency;
|
|
428
|
+
}
|
|
429
|
+
if ( normalized.enableLogging !== undefined && normalized.enable_logging === undefined ) {
|
|
430
|
+
normalized.enable_logging = normalized.enableLogging;
|
|
431
|
+
}
|
|
432
|
+
delete normalized.voiceId;
|
|
433
|
+
delete normalized.modelId;
|
|
434
|
+
delete normalized.outputFormat;
|
|
435
|
+
delete normalized.voiceSettings;
|
|
436
|
+
delete normalized.fileFormat;
|
|
437
|
+
delete normalized.removeBackgroundNoise;
|
|
438
|
+
delete normalized.optimizeStreamingLatency;
|
|
439
|
+
delete normalized.enableLogging;
|
|
440
|
+
return normalized;
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
let options = {};
|
|
444
|
+
let testMode = false;
|
|
445
|
+
|
|
446
|
+
const primary = args[0];
|
|
447
|
+
if ( primary && typeof primary === 'object' && !Array.isArray(primary) && !(primary instanceof Blob) ) {
|
|
448
|
+
options = { ...primary };
|
|
449
|
+
} else {
|
|
450
|
+
options.audio = await normalizeSource(primary);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if ( args[1] && typeof args[1] === 'object' && !Array.isArray(args[1]) && !(args[1] instanceof Blob) ) {
|
|
454
|
+
options = { ...options, ...args[1] };
|
|
455
|
+
} else if ( typeof args[1] === 'boolean' ) {
|
|
456
|
+
testMode = args[1];
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if ( typeof args[2] === 'boolean' ) {
|
|
460
|
+
testMode = args[2];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if ( options.file ) {
|
|
464
|
+
options.audio = await normalizeSource(options.file);
|
|
465
|
+
delete options.file;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if ( options.audio instanceof Blob ) {
|
|
469
|
+
options.audio = await normalizeSource(options.audio);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if ( ! options.audio ) {
|
|
473
|
+
throw { message: 'Audio input is required', code: 'audio_required' };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if ( typeof options.audio === 'string' && options.audio.startsWith('data:') ) {
|
|
477
|
+
const base64 = options.audio.split(',')[1] || '';
|
|
478
|
+
const padding = base64.endsWith('==') ? 2 : (base64.endsWith('=') ? 1 : 0);
|
|
479
|
+
const byteLength = Math.floor((base64.length * 3) / 4) - padding;
|
|
480
|
+
if ( byteLength > MAX_INPUT_SIZE ) {
|
|
481
|
+
throw { message: 'Input size cannot be larger than 25 MB', code: 'input_too_large' };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const driverArgs = normalizeOptions({ ...options });
|
|
486
|
+
delete driverArgs.provider;
|
|
487
|
+
|
|
488
|
+
return await utils.make_driver_method(['audio'], 'puter-speech2speech', 'elevenlabs-voice-changer', 'convert', {
|
|
489
|
+
responseType: 'blob',
|
|
490
|
+
test_mode: testMode,
|
|
491
|
+
transform: async (result) => {
|
|
492
|
+
let url;
|
|
493
|
+
if ( typeof result === 'string' ) {
|
|
494
|
+
url = result;
|
|
495
|
+
} else if ( result instanceof Blob ) {
|
|
496
|
+
url = await utils.blob_to_url(result);
|
|
497
|
+
} else if ( result instanceof ArrayBuffer ) {
|
|
498
|
+
const blob = new Blob([result]);
|
|
499
|
+
url = await utils.blob_to_url(blob);
|
|
500
|
+
} else if ( result && typeof result === 'object' && typeof result.arrayBuffer === 'function' ) {
|
|
501
|
+
const arrayBuffer = await result.arrayBuffer();
|
|
502
|
+
const blob = new Blob([arrayBuffer], { type: result.type || undefined });
|
|
503
|
+
url = await utils.blob_to_url(blob);
|
|
504
|
+
} else {
|
|
505
|
+
throw { message: 'Unexpected audio response format', code: 'invalid_audio_response' };
|
|
506
|
+
}
|
|
507
|
+
const audio = new Audio(url);
|
|
508
|
+
audio.toString = () => url;
|
|
509
|
+
audio.valueOf = () => url;
|
|
510
|
+
return audio;
|
|
511
|
+
},
|
|
512
|
+
}).call(this, driverArgs);
|
|
513
|
+
};
|
|
358
514
|
|
|
359
515
|
speech2txt = async (...args) => {
|
|
360
516
|
const MAX_INPUT_SIZE = 25 * 1024 * 1024;
|
|
@@ -398,7 +554,7 @@ class AI{
|
|
|
398
554
|
options.file = await normalizeSource(options.file);
|
|
399
555
|
}
|
|
400
556
|
|
|
401
|
-
if ( !options.file ) {
|
|
557
|
+
if ( ! options.file ) {
|
|
402
558
|
throw { message: 'Audio input is required', code: 'audio_required' };
|
|
403
559
|
}
|
|
404
560
|
|
|
@@ -426,7 +582,7 @@ class AI{
|
|
|
426
582
|
return result;
|
|
427
583
|
},
|
|
428
584
|
}).call(this, driverArgs);
|
|
429
|
-
}
|
|
585
|
+
};
|
|
430
586
|
|
|
431
587
|
// Add new methods for TTS engine management
|
|
432
588
|
txt2speech = Object.assign(this.txt2speech, {
|
|
@@ -438,19 +594,25 @@ class AI{
|
|
|
438
594
|
let provider = 'aws-polly';
|
|
439
595
|
let params = {};
|
|
440
596
|
|
|
441
|
-
if (typeof options === 'string') {
|
|
597
|
+
if ( typeof options === 'string' ) {
|
|
442
598
|
provider = normalizeTTSProvider(options);
|
|
443
|
-
} else if (options && typeof options === 'object') {
|
|
599
|
+
} else if ( options && typeof options === 'object' ) {
|
|
444
600
|
provider = normalizeTTSProvider(options.provider) || provider;
|
|
445
601
|
params = { ...options };
|
|
446
602
|
delete params.provider;
|
|
447
603
|
}
|
|
448
604
|
|
|
449
|
-
if (provider === 'openai') {
|
|
605
|
+
if ( provider === 'openai' ) {
|
|
450
606
|
params.provider = 'openai';
|
|
451
607
|
}
|
|
452
608
|
|
|
453
|
-
|
|
609
|
+
if ( provider === 'elevenlabs' ) {
|
|
610
|
+
params.provider = 'elevenlabs';
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const driverName = provider === 'openai'
|
|
614
|
+
? 'openai-tts'
|
|
615
|
+
: (provider === 'elevenlabs' ? 'elevenlabs-tts' : 'aws-polly');
|
|
454
616
|
|
|
455
617
|
return await utils.make_driver_method(['source'], 'puter-tts', driverName, 'list_engines', {
|
|
456
618
|
responseType: 'text',
|
|
@@ -466,28 +628,33 @@ class AI{
|
|
|
466
628
|
let provider = 'aws-polly';
|
|
467
629
|
let params = {};
|
|
468
630
|
|
|
469
|
-
if (typeof options === 'string') {
|
|
631
|
+
if ( typeof options === 'string' ) {
|
|
470
632
|
params.engine = options;
|
|
471
|
-
} else if (options && typeof options === 'object') {
|
|
633
|
+
} else if ( options && typeof options === 'object' ) {
|
|
472
634
|
provider = normalizeTTSProvider(options.provider) || provider;
|
|
473
635
|
params = { ...options };
|
|
474
636
|
delete params.provider;
|
|
475
637
|
}
|
|
476
638
|
|
|
477
|
-
if (provider === 'openai') {
|
|
639
|
+
if ( provider === 'openai' ) {
|
|
478
640
|
params.provider = 'openai';
|
|
479
641
|
delete params.engine;
|
|
480
642
|
}
|
|
481
643
|
|
|
482
|
-
|
|
644
|
+
if ( provider === 'elevenlabs' ) {
|
|
645
|
+
params.provider = 'elevenlabs';
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const driverName = provider === 'openai'
|
|
649
|
+
? 'openai-tts'
|
|
650
|
+
: (provider === 'elevenlabs' ? 'elevenlabs-tts' : 'aws-polly');
|
|
483
651
|
|
|
484
652
|
return utils.make_driver_method(['source'], 'puter-tts', driverName, 'list_voices', {
|
|
485
653
|
responseType: 'text',
|
|
486
654
|
}).call(this, params);
|
|
487
|
-
}
|
|
655
|
+
},
|
|
488
656
|
});
|
|
489
657
|
|
|
490
|
-
|
|
491
658
|
// accepts either a string or an array of message objects
|
|
492
659
|
// if string, it's treated as the prompt which is a shorthand for { messages: [{ content: prompt }] }
|
|
493
660
|
// if object, it's treated as the full argument object that the API expects
|
|
@@ -502,100 +669,99 @@ class AI{
|
|
|
502
669
|
let driver = 'openai-completion';
|
|
503
670
|
|
|
504
671
|
// Check that the argument is not undefined or null
|
|
505
|
-
if(!args){
|
|
506
|
-
throw({message: 'Arguments are required', code: 'arguments_required'});
|
|
672
|
+
if ( ! args ) {
|
|
673
|
+
throw ({ message: 'Arguments are required', code: 'arguments_required' });
|
|
507
674
|
}
|
|
508
675
|
|
|
509
676
|
// ai.chat(prompt)
|
|
510
|
-
if(typeof args[0] === 'string'){
|
|
677
|
+
if ( typeof args[0] === 'string' ) {
|
|
511
678
|
requestParams = { messages: [{ content: args[0] }] };
|
|
512
679
|
}
|
|
513
680
|
|
|
514
681
|
// ai.chat(prompt, testMode)
|
|
515
|
-
if (typeof args[0] === 'string' && (!args[1] || typeof args[1] === 'boolean')) {
|
|
682
|
+
if ( typeof args[0] === 'string' && (!args[1] || typeof args[1] === 'boolean') ) {
|
|
516
683
|
requestParams = { messages: [{ content: args[0] }] };
|
|
517
684
|
}
|
|
518
685
|
|
|
519
686
|
// ai.chat(prompt, imageURL/File)
|
|
520
687
|
// ai.chat(prompt, imageURL/File, testMode)
|
|
521
|
-
else if (typeof args[0] === 'string' && (typeof args[1] === 'string' || args[1] instanceof File)) {
|
|
688
|
+
else if ( typeof args[0] === 'string' && (typeof args[1] === 'string' || args[1] instanceof File) ) {
|
|
522
689
|
// if imageURL is a File, transform it to a data URI
|
|
523
|
-
if(args[1] instanceof File){
|
|
690
|
+
if ( args[1] instanceof File ) {
|
|
524
691
|
args[1] = await utils.blobToDataUri(args[1]);
|
|
525
692
|
}
|
|
526
693
|
|
|
527
694
|
// parse args[1] as an image_url object
|
|
528
|
-
requestParams = {
|
|
695
|
+
requestParams = {
|
|
529
696
|
vision: true,
|
|
530
697
|
messages: [
|
|
531
|
-
{
|
|
698
|
+
{
|
|
532
699
|
content: [
|
|
533
700
|
args[0],
|
|
534
701
|
{
|
|
535
702
|
image_url: {
|
|
536
|
-
url: args[1]
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
],
|
|
540
|
-
}
|
|
541
|
-
]
|
|
703
|
+
url: args[1],
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
],
|
|
707
|
+
},
|
|
708
|
+
],
|
|
542
709
|
};
|
|
543
710
|
}
|
|
544
711
|
// chat(prompt, [imageURLs])
|
|
545
|
-
else if (typeof args[0] === 'string' && Array.isArray(args[1])) {
|
|
712
|
+
else if ( typeof args[0] === 'string' && Array.isArray(args[1]) ) {
|
|
546
713
|
// parse args[1] as an array of image_url objects
|
|
547
|
-
for (let i = 0; i < args[1].length; i++) {
|
|
714
|
+
for ( let i = 0; i < args[1].length; i++ ) {
|
|
548
715
|
args[1][i] = { image_url: { url: args[1][i] } };
|
|
549
716
|
}
|
|
550
|
-
requestParams = {
|
|
717
|
+
requestParams = {
|
|
551
718
|
vision: true,
|
|
552
719
|
messages: [
|
|
553
|
-
{
|
|
720
|
+
{
|
|
554
721
|
content: [
|
|
555
722
|
args[0],
|
|
556
|
-
...args[1]
|
|
557
|
-
],
|
|
558
|
-
}
|
|
559
|
-
]
|
|
723
|
+
...args[1],
|
|
724
|
+
],
|
|
725
|
+
},
|
|
726
|
+
],
|
|
560
727
|
};
|
|
561
728
|
}
|
|
562
729
|
// chat([messages])
|
|
563
|
-
else if (Array.isArray(args[0])) {
|
|
730
|
+
else if ( Array.isArray(args[0]) ) {
|
|
564
731
|
requestParams = { messages: args[0] };
|
|
565
732
|
}
|
|
566
733
|
|
|
567
734
|
// determine if testMode is enabled
|
|
568
|
-
if (typeof args[1] === 'boolean' && args[1] === true ||
|
|
735
|
+
if ( typeof args[1] === 'boolean' && args[1] === true ||
|
|
569
736
|
typeof args[2] === 'boolean' && args[2] === true ||
|
|
570
|
-
typeof args[3] === 'boolean' && args[3] === true) {
|
|
737
|
+
typeof args[3] === 'boolean' && args[3] === true ) {
|
|
571
738
|
testMode = true;
|
|
572
739
|
}
|
|
573
|
-
|
|
740
|
+
|
|
574
741
|
// if any of the args is an object, assume it's the user parameters object
|
|
575
742
|
const is_object = v => {
|
|
576
743
|
return typeof v === 'object' &&
|
|
577
744
|
!Array.isArray(v) &&
|
|
578
745
|
v !== null;
|
|
579
746
|
};
|
|
580
|
-
for (let i = 0; i < args.length; i++) {
|
|
581
|
-
if (is_object(args[i])) {
|
|
747
|
+
for ( let i = 0; i < args.length; i++ ) {
|
|
748
|
+
if ( is_object(args[i]) ) {
|
|
582
749
|
userParams = args[i];
|
|
583
750
|
break;
|
|
584
751
|
}
|
|
585
752
|
}
|
|
586
753
|
|
|
587
|
-
|
|
588
754
|
// Copy relevant parameters from userParams to requestParams
|
|
589
|
-
if (userParams.model) {
|
|
755
|
+
if ( userParams.model ) {
|
|
590
756
|
requestParams.model = userParams.model;
|
|
591
757
|
}
|
|
592
|
-
if (userParams.temperature) {
|
|
758
|
+
if ( userParams.temperature ) {
|
|
593
759
|
requestParams.temperature = userParams.temperature;
|
|
594
760
|
}
|
|
595
|
-
if (userParams.max_tokens) {
|
|
761
|
+
if ( userParams.max_tokens ) {
|
|
596
762
|
requestParams.max_tokens = userParams.max_tokens;
|
|
597
763
|
}
|
|
598
|
-
|
|
764
|
+
|
|
599
765
|
// convert undefined to empty string so that .startsWith works
|
|
600
766
|
requestParams.model = requestParams.model ?? '';
|
|
601
767
|
|
|
@@ -604,21 +770,21 @@ class AI{
|
|
|
604
770
|
// for example: "claude-3-5-sonnet" should become "anthropic/claude-3-5-sonnet"
|
|
605
771
|
// but for now, we want to keep the old behavior
|
|
606
772
|
// so we remove the "anthropic/" prefix if it exists
|
|
607
|
-
if (requestParams.model && requestParams.model.startsWith('anthropic/')) {
|
|
773
|
+
if ( requestParams.model && requestParams.model.startsWith('anthropic/') ) {
|
|
608
774
|
requestParams.model = requestParams.model.replace('anthropic/', '');
|
|
609
775
|
}
|
|
610
776
|
|
|
611
777
|
// convert to the correct model name if necessary
|
|
612
|
-
if( requestParams.model === 'claude-3-5-sonnet'){
|
|
778
|
+
if ( requestParams.model === 'claude-3-5-sonnet' ) {
|
|
613
779
|
requestParams.model = 'claude-3-5-sonnet-latest';
|
|
614
780
|
}
|
|
615
|
-
if( requestParams.model === 'claude-3-7-sonnet' || requestParams.model === 'claude'){
|
|
781
|
+
if ( requestParams.model === 'claude-3-7-sonnet' || requestParams.model === 'claude' ) {
|
|
616
782
|
requestParams.model = 'claude-3-7-sonnet-latest';
|
|
617
783
|
}
|
|
618
|
-
if( requestParams.model === 'claude-sonnet-4' || requestParams.model === 'claude-sonnet-4-latest'){
|
|
784
|
+
if ( requestParams.model === 'claude-sonnet-4' || requestParams.model === 'claude-sonnet-4-latest' ) {
|
|
619
785
|
requestParams.model = 'claude-sonnet-4-20250514';
|
|
620
786
|
}
|
|
621
|
-
if( requestParams.model === 'claude-opus-4' || requestParams.model === 'claude-opus-4-latest') {
|
|
787
|
+
if ( requestParams.model === 'claude-opus-4' || requestParams.model === 'claude-opus-4-latest' ) {
|
|
622
788
|
requestParams.model = 'claude-opus-4-20250514';
|
|
623
789
|
}
|
|
624
790
|
if ( requestParams.model === 'mistral' ) {
|
|
@@ -632,73 +798,22 @@ class AI{
|
|
|
632
798
|
}
|
|
633
799
|
|
|
634
800
|
// o1-mini to openrouter:openai/o1-mini
|
|
635
|
-
if ( requestParams.model === 'o1-mini') {
|
|
801
|
+
if ( requestParams.model === 'o1-mini' ) {
|
|
636
802
|
requestParams.model = 'openrouter:openai/o1-mini';
|
|
637
803
|
}
|
|
638
804
|
|
|
639
805
|
// if a model is prepended with "openai/", remove it
|
|
640
|
-
if (requestParams.model && requestParams.model.startsWith('openai/')) {
|
|
806
|
+
if ( requestParams.model && requestParams.model.startsWith('openai/') ) {
|
|
641
807
|
requestParams.model = requestParams.model.replace('openai/', '');
|
|
642
808
|
driver = 'openai-completion';
|
|
643
809
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
// agentica-org/
|
|
647
|
-
// ai21/
|
|
648
|
-
// aion-labs/
|
|
649
|
-
// alfredpros/
|
|
650
|
-
// alpindale/
|
|
651
|
-
// amazon/
|
|
652
|
-
// anthracite-org/
|
|
653
|
-
// arcee-ai/
|
|
654
|
-
// arliai/
|
|
655
|
-
// baidu/
|
|
656
|
-
// bytedance/
|
|
657
|
-
// cognitivecomputations/
|
|
658
|
-
// cohere/
|
|
659
|
-
// deepseek/
|
|
660
|
-
// eleutherai/
|
|
661
|
-
// google/
|
|
662
|
-
// gryphe/
|
|
663
|
-
// inception/
|
|
664
|
-
// infermatic/
|
|
665
|
-
// liquid/
|
|
666
|
-
// mancer/
|
|
667
|
-
// meta-llama/
|
|
668
|
-
// microsoft/
|
|
669
|
-
// minimax/
|
|
670
|
-
// mistralai/
|
|
671
|
-
// moonshotai/
|
|
672
|
-
// morph/
|
|
673
|
-
// neversleep/
|
|
674
|
-
// nousresearch/
|
|
675
|
-
// nvidia/
|
|
676
|
-
// openrouter/
|
|
677
|
-
// perplexity/
|
|
678
|
-
// pygmalionai/
|
|
679
|
-
// qwen/
|
|
680
|
-
// raifle/
|
|
681
|
-
// rekaai/
|
|
682
|
-
// sao10k/
|
|
683
|
-
// sarvamai/
|
|
684
|
-
// scb10x/
|
|
685
|
-
// shisa-ai/
|
|
686
|
-
// sophosympatheia/
|
|
687
|
-
// switchpoint/
|
|
688
|
-
// tencent/
|
|
689
|
-
// thedrummer/
|
|
690
|
-
// thudm/
|
|
691
|
-
// tngtech/
|
|
692
|
-
// undi95/
|
|
693
|
-
// x-ai/
|
|
694
|
-
// z-ai/
|
|
695
|
-
|
|
696
|
-
// prepend it with openrouter:
|
|
697
|
-
if (
|
|
810
|
+
// For the following providers, we need to prepend "openrouter:" to the model name so that the backend driver can handle it
|
|
811
|
+
if (
|
|
698
812
|
requestParams.model.startsWith('agentica-org/') ||
|
|
699
813
|
requestParams.model.startsWith('ai21/') ||
|
|
700
814
|
requestParams.model.startsWith('aion-labs/') ||
|
|
701
815
|
requestParams.model.startsWith('alfredpros/') ||
|
|
816
|
+
requestParams.model.startsWith('allenai/') ||
|
|
702
817
|
requestParams.model.startsWith('alpindale/') ||
|
|
703
818
|
requestParams.model.startsWith('amazon/') ||
|
|
704
819
|
requestParams.model.startsWith('anthracite-org/') ||
|
|
@@ -708,9 +823,9 @@ class AI{
|
|
|
708
823
|
requestParams.model.startsWith('bytedance/') ||
|
|
709
824
|
requestParams.model.startsWith('cognitivecomputations/') ||
|
|
710
825
|
requestParams.model.startsWith('cohere/') ||
|
|
711
|
-
requestParams.model.startsWith('deepseek/') ||
|
|
826
|
+
requestParams.model.startsWith('deepseek/') ||
|
|
712
827
|
requestParams.model.startsWith('eleutherai/') ||
|
|
713
|
-
requestParams.model.startsWith('google/') ||
|
|
828
|
+
requestParams.model.startsWith('google/') ||
|
|
714
829
|
requestParams.model.startsWith('gryphe/') ||
|
|
715
830
|
requestParams.model.startsWith('inception/') ||
|
|
716
831
|
requestParams.model.startsWith('infermatic/') ||
|
|
@@ -728,7 +843,7 @@ class AI{
|
|
|
728
843
|
requestParams.model.startsWith('openrouter/') ||
|
|
729
844
|
requestParams.model.startsWith('perplexity/') ||
|
|
730
845
|
requestParams.model.startsWith('pygmalionai/') ||
|
|
731
|
-
requestParams.model.startsWith('qwen/') ||
|
|
846
|
+
requestParams.model.startsWith('qwen/') ||
|
|
732
847
|
requestParams.model.startsWith('raifle/') ||
|
|
733
848
|
requestParams.model.startsWith('rekaai/') ||
|
|
734
849
|
requestParams.model.startsWith('sao10k/') ||
|
|
@@ -742,54 +857,59 @@ class AI{
|
|
|
742
857
|
requestParams.model.startsWith('thudm/') ||
|
|
743
858
|
requestParams.model.startsWith('tngtech/') ||
|
|
744
859
|
requestParams.model.startsWith('undi95/') ||
|
|
745
|
-
requestParams.model.startsWith('x-ai/') ||
|
|
860
|
+
requestParams.model.startsWith('x-ai/') ||
|
|
746
861
|
requestParams.model.startsWith('z-ai/')
|
|
747
862
|
) {
|
|
748
|
-
requestParams.model =
|
|
863
|
+
requestParams.model = `openrouter:${ requestParams.model}`;
|
|
749
864
|
}
|
|
750
865
|
|
|
751
866
|
// map model to the appropriate driver
|
|
752
|
-
if (!requestParams.model || requestParams.model.startsWith('gpt-')) {
|
|
867
|
+
if ( !requestParams.model || requestParams.model.startsWith('gpt-') ) {
|
|
753
868
|
driver = 'openai-completion';
|
|
754
|
-
}else if(
|
|
869
|
+
} else if (
|
|
755
870
|
requestParams.model.startsWith('claude-')
|
|
756
|
-
){
|
|
871
|
+
) {
|
|
757
872
|
driver = 'claude';
|
|
758
|
-
}else if(requestParams.model === 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo' || requestParams.model === 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo' || requestParams.model === 'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo' || requestParams.model ===
|
|
873
|
+
} else if ( requestParams.model === 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo' || requestParams.model === 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo' || requestParams.model === 'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo' || requestParams.model === 'google/gemma-2-27b-it' ) {
|
|
759
874
|
driver = 'together-ai';
|
|
760
|
-
}else if(requestParams.model.startsWith('mistral-') || requestParams.model.startsWith('codestral-') || requestParams.model.startsWith('pixtral-') || requestParams.model.startsWith('magistral-') || requestParams.model.startsWith('devstral-') || requestParams.model.startsWith('mistral-ocr-') || requestParams.model.startsWith('open-mistral-')){
|
|
875
|
+
} else if ( requestParams.model.startsWith('mistral-') || requestParams.model.startsWith('codestral-') || requestParams.model.startsWith('pixtral-') || requestParams.model.startsWith('magistral-') || requestParams.model.startsWith('devstral-') || requestParams.model.startsWith('mistral-ocr-') || requestParams.model.startsWith('open-mistral-') ) {
|
|
761
876
|
driver = 'mistral';
|
|
762
|
-
}else if([
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
].includes(requestParams.model)) {
|
|
877
|
+
} else if ( [
|
|
878
|
+
'distil-whisper-large-v3-en',
|
|
879
|
+
'gemma2-9b-it',
|
|
880
|
+
'gemma-7b-it',
|
|
881
|
+
'llama-3.1-70b-versatile',
|
|
882
|
+
'llama-3.1-8b-instant',
|
|
883
|
+
'llama3-70b-8192',
|
|
884
|
+
'llama3-8b-8192',
|
|
885
|
+
'llama3-groq-70b-8192-tool-use-preview',
|
|
886
|
+
'llama3-groq-8b-8192-tool-use-preview',
|
|
887
|
+
'llama-guard-3-8b',
|
|
888
|
+
'mixtral-8x7b-32768',
|
|
889
|
+
'whisper-large-v3',
|
|
890
|
+
].includes(requestParams.model) ) {
|
|
776
891
|
driver = 'groq';
|
|
777
|
-
}else if(requestParams.model === 'grok-beta') {
|
|
892
|
+
} else if ( requestParams.model === 'grok-beta' ) {
|
|
778
893
|
driver = 'xai';
|
|
779
894
|
}
|
|
780
|
-
else if(requestParams.model.startsWith('grok-')){
|
|
895
|
+
else if ( requestParams.model.startsWith('grok-') ) {
|
|
781
896
|
driver = 'openrouter';
|
|
782
897
|
}
|
|
783
|
-
else if(
|
|
898
|
+
else if (
|
|
784
899
|
requestParams.model === 'deepseek-chat' ||
|
|
785
900
|
requestParams.model === 'deepseek-reasoner'
|
|
786
|
-
){
|
|
901
|
+
) {
|
|
787
902
|
driver = 'deepseek';
|
|
788
903
|
}
|
|
789
|
-
else if(
|
|
904
|
+
else if (
|
|
790
905
|
requestParams.model === 'gemini-1.5-flash' ||
|
|
791
|
-
requestParams.model === 'gemini-2.0-flash'
|
|
792
|
-
|
|
906
|
+
requestParams.model === 'gemini-2.0-flash' ||
|
|
907
|
+
requestParams.model === 'gemini-2.5-flash' ||
|
|
908
|
+
requestParams.model === 'gemini-2.5-flash-lite' ||
|
|
909
|
+
requestParams.model === 'gemini-2.0-flash-lite' ||
|
|
910
|
+
requestParams.model === 'gemini-3-pro-preview' ||
|
|
911
|
+
requestParams.model === 'gemini-2.5-pro'
|
|
912
|
+
) {
|
|
793
913
|
driver = 'gemini';
|
|
794
914
|
}
|
|
795
915
|
else if ( requestParams.model.startsWith('openrouter:') ) {
|
|
@@ -800,10 +920,10 @@ class AI{
|
|
|
800
920
|
}
|
|
801
921
|
|
|
802
922
|
// stream flag from userParams
|
|
803
|
-
if(userParams.stream !== undefined && typeof userParams.stream === 'boolean'){
|
|
923
|
+
if ( userParams.stream !== undefined && typeof userParams.stream === 'boolean' ) {
|
|
804
924
|
requestParams.stream = userParams.stream;
|
|
805
925
|
}
|
|
806
|
-
|
|
926
|
+
|
|
807
927
|
if ( userParams.driver ) {
|
|
808
928
|
driver = userParams.driver;
|
|
809
929
|
}
|
|
@@ -815,7 +935,7 @@ class AI{
|
|
|
815
935
|
requestParams[name] = userParams[name];
|
|
816
936
|
}
|
|
817
937
|
}
|
|
818
|
-
|
|
938
|
+
|
|
819
939
|
if ( requestParams.model === '' ) {
|
|
820
940
|
delete requestParams.model;
|
|
821
941
|
}
|
|
@@ -830,16 +950,16 @@ class AI{
|
|
|
830
950
|
|
|
831
951
|
result.valueOf = () => {
|
|
832
952
|
return result.message?.content;
|
|
833
|
-
}
|
|
953
|
+
};
|
|
834
954
|
|
|
835
955
|
return result;
|
|
836
|
-
}
|
|
956
|
+
},
|
|
837
957
|
}).call(this, requestParams);
|
|
838
|
-
}
|
|
958
|
+
};
|
|
839
959
|
|
|
840
960
|
/**
|
|
841
961
|
* Generate images from text prompts or perform image-to-image generation
|
|
842
|
-
*
|
|
962
|
+
*
|
|
843
963
|
* @param {string|object} prompt - Text prompt or options object
|
|
844
964
|
* @param {object|boolean} [options] - Generation options or test mode flag
|
|
845
965
|
* @param {string} [options.prompt] - Text description of the image to generate
|
|
@@ -848,11 +968,11 @@ class AI{
|
|
|
848
968
|
* @param {string} [options.input_image] - Base64 encoded input image for image-to-image generation
|
|
849
969
|
* @param {string} [options.input_image_mime_type] - MIME type of input image (e.g., "image/png")
|
|
850
970
|
* @returns {Promise<Image>} Generated image object with src property
|
|
851
|
-
*
|
|
971
|
+
*
|
|
852
972
|
* @example
|
|
853
973
|
* // Text-to-image
|
|
854
974
|
* const img = await puter.ai.txt2img("A beautiful sunset");
|
|
855
|
-
*
|
|
975
|
+
*
|
|
856
976
|
* @example
|
|
857
977
|
* // Image-to-image
|
|
858
978
|
* const img = await puter.ai.txt2img({
|
|
@@ -866,32 +986,38 @@ class AI{
|
|
|
866
986
|
let options = {};
|
|
867
987
|
let testMode = false;
|
|
868
988
|
|
|
869
|
-
if(!args){
|
|
870
|
-
throw({message: 'Arguments are required', code: 'arguments_required'});
|
|
989
|
+
if ( ! args ) {
|
|
990
|
+
throw ({ message: 'Arguments are required', code: 'arguments_required' });
|
|
871
991
|
}
|
|
872
992
|
|
|
873
993
|
// if argument is string transform it to the object that the API expects
|
|
874
|
-
if (typeof args[0] === 'string') {
|
|
994
|
+
if ( typeof args[0] === 'string' ) {
|
|
875
995
|
options = { prompt: args[0] };
|
|
876
996
|
}
|
|
877
997
|
|
|
878
998
|
// if second argument is string, it's the `testMode`
|
|
879
|
-
if (typeof args[1] === 'boolean' && args[1] === true) {
|
|
999
|
+
if ( typeof args[1] === 'boolean' && args[1] === true ) {
|
|
880
1000
|
testMode = true;
|
|
881
1001
|
}
|
|
882
1002
|
|
|
883
|
-
if (typeof args[0] === 'string' && typeof args[1] ===
|
|
1003
|
+
if ( typeof args[0] === 'string' && typeof args[1] === 'object' ) {
|
|
884
1004
|
options = args[1];
|
|
885
1005
|
options.prompt = args[0];
|
|
886
1006
|
}
|
|
887
1007
|
|
|
888
|
-
if (typeof args[0] === 'object') {
|
|
889
|
-
options = args[0]
|
|
1008
|
+
if ( typeof args[0] === 'object' ) {
|
|
1009
|
+
options = args[0];
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
let AIService = 'openai-image-generation';
|
|
1013
|
+
if ( options.model === 'nano-banana' )
|
|
1014
|
+
{
|
|
1015
|
+
options.model = 'gemini-2.5-flash-image-preview';
|
|
890
1016
|
}
|
|
891
1017
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1018
|
+
if (options.model === "nano-banana-pro") {
|
|
1019
|
+
options.model = "gemini-3-pro-image-preview";
|
|
1020
|
+
}
|
|
895
1021
|
|
|
896
1022
|
const driverHint = typeof options.driver === 'string' ? options.driver : undefined;
|
|
897
1023
|
const providerRaw = typeof options.provider === 'string'
|
|
@@ -905,16 +1031,16 @@ class AI{
|
|
|
905
1031
|
(TOGETHER_IMAGE_MODEL_PREFIXES.some(prefix => modelLower.startsWith(prefix)) ||
|
|
906
1032
|
TOGETHER_IMAGE_MODEL_KEYWORDS.some(keyword => modelLower.includes(keyword)));
|
|
907
1033
|
|
|
908
|
-
if (driverHint) {
|
|
1034
|
+
if ( driverHint ) {
|
|
909
1035
|
AIService = driverHint;
|
|
910
|
-
} else if (providerHint === 'gemini') {
|
|
911
|
-
AIService =
|
|
912
|
-
} else if (providerHint === 'together' || providerHint === 'together-ai') {
|
|
913
|
-
AIService =
|
|
914
|
-
} else if (options.model ===
|
|
915
|
-
AIService =
|
|
916
|
-
} else if (looksLikeTogetherModel) {
|
|
917
|
-
AIService =
|
|
1036
|
+
} else if ( providerHint === 'gemini' ) {
|
|
1037
|
+
AIService = 'gemini-image-generation';
|
|
1038
|
+
} else if ( providerHint === 'together' || providerHint === 'together-ai' ) {
|
|
1039
|
+
AIService = 'together-image-generation';
|
|
1040
|
+
} else if (options.model === 'gemini-2.5-flash-image-preview' || options.model === "gemini-3-pro-image-preview" ) {
|
|
1041
|
+
AIService = 'gemini-image-generation';
|
|
1042
|
+
} else if ( looksLikeTogetherModel ) {
|
|
1043
|
+
AIService = 'together-image-generation';
|
|
918
1044
|
}
|
|
919
1045
|
// Call the original chat.complete method
|
|
920
1046
|
return await utils.make_driver_method(['prompt'], 'puter-image-generation', AIService, 'generate', {
|
|
@@ -936,49 +1062,49 @@ class AI{
|
|
|
936
1062
|
} else {
|
|
937
1063
|
throw { message: 'Unexpected image response format', code: 'invalid_image_response' };
|
|
938
1064
|
}
|
|
939
|
-
let img = new Image();
|
|
1065
|
+
let img = new (globalThis.Image || Object)();
|
|
940
1066
|
img.src = url;
|
|
941
1067
|
img.toString = () => img.src;
|
|
942
1068
|
img.valueOf = () => img.src;
|
|
943
1069
|
return img;
|
|
944
|
-
}
|
|
1070
|
+
},
|
|
945
1071
|
}).call(this, options);
|
|
946
|
-
}
|
|
1072
|
+
};
|
|
947
1073
|
|
|
948
1074
|
txt2vid = async (...args) => {
|
|
949
1075
|
let options = {};
|
|
950
1076
|
let testMode = false;
|
|
951
1077
|
|
|
952
|
-
if(!args){
|
|
953
|
-
throw({message: 'Arguments are required', code: 'arguments_required'});
|
|
1078
|
+
if ( ! args ) {
|
|
1079
|
+
throw ({ message: 'Arguments are required', code: 'arguments_required' });
|
|
954
1080
|
}
|
|
955
1081
|
|
|
956
|
-
if (typeof args[0] === 'string') {
|
|
1082
|
+
if ( typeof args[0] === 'string' ) {
|
|
957
1083
|
options = { prompt: args[0] };
|
|
958
1084
|
}
|
|
959
1085
|
|
|
960
|
-
if (typeof args[1] === 'boolean' && args[1] === true) {
|
|
1086
|
+
if ( typeof args[1] === 'boolean' && args[1] === true ) {
|
|
961
1087
|
testMode = true;
|
|
962
1088
|
}
|
|
963
1089
|
|
|
964
|
-
if (typeof args[0] === 'string' && typeof args[1] ===
|
|
1090
|
+
if ( typeof args[0] === 'string' && typeof args[1] === 'object' ) {
|
|
965
1091
|
options = args[1];
|
|
966
1092
|
options.prompt = args[0];
|
|
967
1093
|
}
|
|
968
1094
|
|
|
969
|
-
if (typeof args[0] === 'object') {
|
|
1095
|
+
if ( typeof args[0] === 'object' ) {
|
|
970
1096
|
options = args[0];
|
|
971
1097
|
}
|
|
972
1098
|
|
|
973
|
-
if (!options.prompt) {
|
|
974
|
-
throw({message: 'Prompt parameter is required', code: 'prompt_required'});
|
|
1099
|
+
if ( ! options.prompt ) {
|
|
1100
|
+
throw ({ message: 'Prompt parameter is required', code: 'prompt_required' });
|
|
975
1101
|
}
|
|
976
1102
|
|
|
977
|
-
if (!options.model) {
|
|
1103
|
+
if ( ! options.model ) {
|
|
978
1104
|
options.model = 'sora-2';
|
|
979
1105
|
}
|
|
980
1106
|
|
|
981
|
-
if (options.duration !== undefined && options.seconds === undefined) {
|
|
1107
|
+
if ( options.duration !== undefined && options.seconds === undefined ) {
|
|
982
1108
|
options.seconds = options.duration;
|
|
983
1109
|
}
|
|
984
1110
|
|
|
@@ -994,17 +1120,17 @@ class AI{
|
|
|
994
1120
|
const looksLikeTogetherVideoModel = typeof options.model === 'string' &&
|
|
995
1121
|
TOGETHER_VIDEO_MODEL_PREFIXES.some(prefix => modelLower.startsWith(prefix));
|
|
996
1122
|
|
|
997
|
-
if (driverHintLower === 'together' || driverHintLower === 'together-ai') {
|
|
1123
|
+
if ( driverHintLower === 'together' || driverHintLower === 'together-ai' ) {
|
|
998
1124
|
videoService = 'together-video-generation';
|
|
999
|
-
} else if (driverHintLower === 'together-video-generation') {
|
|
1125
|
+
} else if ( driverHintLower === 'together-video-generation' ) {
|
|
1000
1126
|
videoService = 'together-video-generation';
|
|
1001
|
-
} else if (driverHintLower === 'openai') {
|
|
1127
|
+
} else if ( driverHintLower === 'openai' ) {
|
|
1002
1128
|
videoService = 'openai-video-generation';
|
|
1003
|
-
} else if (driverHint) {
|
|
1129
|
+
} else if ( driverHint ) {
|
|
1004
1130
|
videoService = driverHint;
|
|
1005
|
-
} else if (providerHint === 'together' || providerHint === 'together-ai') {
|
|
1131
|
+
} else if ( providerHint === 'together' || providerHint === 'together-ai' ) {
|
|
1006
1132
|
videoService = 'together-video-generation';
|
|
1007
|
-
} else if (looksLikeTogetherVideoModel) {
|
|
1133
|
+
} else if ( looksLikeTogetherVideoModel ) {
|
|
1008
1134
|
videoService = 'together-video-generation';
|
|
1009
1135
|
}
|
|
1010
1136
|
|
|
@@ -1014,34 +1140,34 @@ class AI{
|
|
|
1014
1140
|
transform: async result => {
|
|
1015
1141
|
let sourceUrl = null;
|
|
1016
1142
|
let mimeType = null;
|
|
1017
|
-
if (result instanceof Blob) {
|
|
1143
|
+
if ( result instanceof Blob ) {
|
|
1018
1144
|
sourceUrl = await utils.blob_to_url(result);
|
|
1019
1145
|
mimeType = result.type || 'video/mp4';
|
|
1020
|
-
} else if (typeof result === 'string') {
|
|
1146
|
+
} else if ( typeof result === 'string' ) {
|
|
1021
1147
|
sourceUrl = result;
|
|
1022
|
-
} else if (result && typeof result === 'object') {
|
|
1148
|
+
} else if ( result && typeof result === 'object' ) {
|
|
1023
1149
|
sourceUrl = result.asset_url || result.url || result.href || null;
|
|
1024
1150
|
mimeType = result.mime_type || result.content_type || null;
|
|
1025
1151
|
}
|
|
1026
1152
|
|
|
1027
|
-
if (!sourceUrl) {
|
|
1153
|
+
if ( ! sourceUrl ) {
|
|
1028
1154
|
return result;
|
|
1029
1155
|
}
|
|
1030
1156
|
|
|
1031
|
-
const video = document
|
|
1157
|
+
const video = (globalThis.document?.createElement('video') || {setAttribute: ()=>{}});
|
|
1032
1158
|
video.src = sourceUrl;
|
|
1033
1159
|
video.controls = true;
|
|
1034
1160
|
video.preload = 'metadata';
|
|
1035
|
-
if (mimeType) {
|
|
1161
|
+
if ( mimeType ) {
|
|
1036
1162
|
video.setAttribute('data-mime-type', mimeType);
|
|
1037
1163
|
}
|
|
1038
1164
|
video.setAttribute('data-source', sourceUrl);
|
|
1039
1165
|
video.toString = () => video.src;
|
|
1040
1166
|
video.valueOf = () => video.src;
|
|
1041
1167
|
return video;
|
|
1042
|
-
}
|
|
1168
|
+
},
|
|
1043
1169
|
}).call(this, options);
|
|
1044
|
-
}
|
|
1170
|
+
};
|
|
1045
1171
|
}
|
|
1046
1172
|
|
|
1047
1173
|
export default AI;
|