utilitas 1999.1.79 → 1999.1.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/lib/gen.mjs +77 -145
- package/lib/manifest.mjs +1 -1
- package/package.json +1 -1
package/lib/gen.mjs
CHANGED
|
@@ -1,42 +1,39 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ensureArray, ensureString,
|
|
3
|
-
tryUntil,
|
|
2
|
+
ensureArray, ensureString, log as _log, need, throwError,
|
|
3
|
+
tryUntil, timeout,
|
|
4
4
|
} from './utilitas.mjs';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { convert, MIME_PNG } from './storage.mjs';
|
|
6
|
+
import { convert, MIME_PNG, MIME_MP4, getTempPath } from './storage.mjs';
|
|
8
7
|
import { createReadStream } from 'fs';
|
|
9
8
|
|
|
10
|
-
const _NEED = ['OpenAI'];
|
|
9
|
+
const _NEED = ['OpenAI', '@google/genai'];
|
|
11
10
|
const log = (cnt, opt) => _log(cnt, import.meta.url, { time: 1, ...opt || {} });
|
|
12
11
|
const [
|
|
13
|
-
clients, OPENAI, GEMINI, BASE64, BUFFER, ERROR_GENERATING,
|
|
14
|
-
OPENAI_MODEL, VEO_MODEL,
|
|
12
|
+
clients, OPENAI, GEMINI, BASE64, FILE, BUFFER, ERROR_GENERATING,
|
|
13
|
+
IMAGEN_MODEL, OPENAI_MODEL, VEO_MODEL,
|
|
15
14
|
] = [
|
|
16
|
-
{}, 'OPENAI', 'GEMINI', 'BASE64', '
|
|
17
|
-
'imagen-3.0-generate-002', 'gpt-image-1',
|
|
15
|
+
{}, 'OPENAI', 'GEMINI', 'BASE64', 'FILE', 'BUFFER',
|
|
16
|
+
'Error generating media.', 'imagen-3.0-generate-002', 'gpt-image-1',
|
|
17
|
+
'veo-2.0-generate-001',
|
|
18
18
|
];
|
|
19
19
|
|
|
20
20
|
const init = async (options) => {
|
|
21
|
-
assert(
|
|
22
|
-
options?.apiKey || (options?.credentials && options?.projectId),
|
|
23
|
-
'API key or credentials are required.'
|
|
24
|
-
);
|
|
21
|
+
assert(options?.apiKey, 'API key is required.');
|
|
25
22
|
const provider = ensureString(options?.provider, { case: 'UP' });
|
|
26
23
|
switch (provider) {
|
|
27
24
|
case OPENAI:
|
|
28
25
|
const OpenAI = await need('openai');
|
|
29
|
-
|
|
26
|
+
var client = new OpenAI(options);
|
|
30
27
|
clients[provider] = {
|
|
31
|
-
image:
|
|
28
|
+
image: client.images,
|
|
32
29
|
toFile: OpenAI.toFile,
|
|
33
30
|
};
|
|
34
31
|
break;
|
|
35
32
|
case GEMINI:
|
|
33
|
+
const { GoogleGenAI } = await need('@google/genai');
|
|
34
|
+
var client = new GoogleGenAI({ vertexai: false, ...options });
|
|
36
35
|
clients[provider] = {
|
|
37
|
-
|
|
38
|
-
projectId: options.projectId,
|
|
39
|
-
credentials: options.credentials,
|
|
36
|
+
gen: client,
|
|
40
37
|
};
|
|
41
38
|
break;
|
|
42
39
|
default:
|
|
@@ -50,7 +47,7 @@ const extractImage = async (data, options) => await convert(
|
|
|
50
47
|
);
|
|
51
48
|
|
|
52
49
|
const extractVideo = async (data, options) => await convert(
|
|
53
|
-
data, { input:
|
|
50
|
+
data, { input: FILE, suffix: 'mp4', ...options || {} }
|
|
54
51
|
);
|
|
55
52
|
|
|
56
53
|
const prepareImage = async (files, repack, options) => {
|
|
@@ -68,8 +65,8 @@ const prepareImage = async (files, repack, options) => {
|
|
|
68
65
|
|
|
69
66
|
const image = async (prompt, options) => {
|
|
70
67
|
let provider = ensureString(options?.provider, { case: 'UP' });
|
|
71
|
-
if (!provider && clients?.[GEMINI]
|
|
72
|
-
if (!provider && clients?.[OPENAI]) { provider = OPENAI; }
|
|
68
|
+
if (!provider && clients?.[GEMINI]) { provider = GEMINI; }
|
|
69
|
+
else if (!provider && clients?.[OPENAI]) { provider = OPENAI; }
|
|
73
70
|
const client = clients?.[provider];
|
|
74
71
|
const n = options?.n || 4;
|
|
75
72
|
assert(client, 'No available image generation provider.');
|
|
@@ -110,66 +107,25 @@ const image = async (prompt, options) => {
|
|
|
110
107
|
}
|
|
111
108
|
return resp?.data;
|
|
112
109
|
case GEMINI:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
// },
|
|
128
|
-
// "subjectImageConfig" {
|
|
129
|
-
// "subjectDescription": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
130
|
-
// "subjectType": "SUBJECT_TYPE_DEFAULT"
|
|
131
|
-
// }
|
|
132
|
-
// }
|
|
133
|
-
// ],
|
|
134
|
-
// }
|
|
135
|
-
// ],
|
|
136
|
-
// "parameters": {
|
|
137
|
-
// "aspectRatio": "1:1",
|
|
138
|
-
// "sampleCount": 4,
|
|
139
|
-
// "negativePrompt": "",
|
|
140
|
-
// "enhancePrompt": false,
|
|
141
|
-
// "personGeneration": "",
|
|
142
|
-
// "safetySetting": "",
|
|
143
|
-
// "addWatermark": true,
|
|
144
|
-
// "includeRaiReason": true,
|
|
145
|
-
// "language": "auto",
|
|
146
|
-
// }
|
|
147
|
-
// }
|
|
148
|
-
// curl \
|
|
149
|
-
// -X POST \
|
|
150
|
-
// -H "Content-Type: application/json" \
|
|
151
|
-
// -H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
|
152
|
-
// "https://${API_ENDPOINT}/v1/projects/${PROJECT_ID}/locations/${LOCATION_ID}/publishers/google/models/${MODEL_ID}:predict" -d '@request.json'
|
|
153
|
-
// ARGs: https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/imagen-api?authuser=4#rest_1
|
|
154
|
-
var resp = await (await fetch(
|
|
155
|
-
'https://generativelanguage.googleapis.com/v1beta/models/'
|
|
156
|
-
+ `${IMAGEN_MODEL}:predict?key=${client.apiKey}`, {
|
|
157
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
158
|
-
body: JSON.stringify({
|
|
159
|
-
instances: [{ prompt }], parameters: {
|
|
160
|
-
// "1:1" (default), "3:4", "4:3", "9:16", and "16:9"
|
|
161
|
-
aspectRatio: '16:9', includeRaiReason: true,
|
|
162
|
-
personGeneration: 'allow_adult', sampleCount: n,
|
|
163
|
-
...options?.params || {},
|
|
164
|
-
},
|
|
165
|
-
})
|
|
166
|
-
})).json();
|
|
167
|
-
assert(!resp?.error, resp?.error?.message || ERROR_GENERATING);
|
|
110
|
+
var resp = await client.gen.models.generateImages({
|
|
111
|
+
model: IMAGEN_MODEL, prompt, config: {
|
|
112
|
+
numberOfImages: n, includeRaiReason: true,
|
|
113
|
+
// "1:1" (default), "3:4", "4:3", "9:16", and "16:9"
|
|
114
|
+
aspectRatio: '16:9', personGeneration: 'allow_adult',
|
|
115
|
+
...options?.config || {},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
const generated = resp?.generatedImages;
|
|
119
|
+
assert(!resp?.error && generated?.filter(
|
|
120
|
+
x => !x.raiFilteredReason
|
|
121
|
+
).length, resp?.error?.message || generated?.find(
|
|
122
|
+
x => x.raiFilteredReason
|
|
123
|
+
)?.raiFilteredReason || ERROR_GENERATING);
|
|
168
124
|
if (!options?.raw) {
|
|
169
|
-
resp = await Promise.all((resp?.
|
|
125
|
+
resp = await Promise.all((resp?.generatedImages || []).map(
|
|
170
126
|
async x => ({
|
|
171
127
|
caption: `🎨 by ${IMAGEN_MODEL}`,
|
|
172
|
-
data: await extractImage(x.
|
|
128
|
+
data: await extractImage(x.image.imageBytes, options),
|
|
173
129
|
mimeType: x.mimeType,
|
|
174
130
|
})
|
|
175
131
|
));
|
|
@@ -180,46 +136,11 @@ const image = async (prompt, options) => {
|
|
|
180
136
|
}
|
|
181
137
|
};
|
|
182
138
|
|
|
183
|
-
const getGeminiAccessToken = async (credentials) => {
|
|
184
|
-
const bin = 'gcloud';
|
|
185
|
-
await assertExist(bin);
|
|
186
|
-
const actResp = await ignoreErrFunc(async () => await exec(
|
|
187
|
-
`${bin} auth activate-service-account --key-file=${credentials}`,
|
|
188
|
-
{ acceptError: true }
|
|
189
|
-
), { log: true });
|
|
190
|
-
assert(actResp?.includes?.('Activated service account credentials'),
|
|
191
|
-
'Failed to activate service account credentials.', 500);
|
|
192
|
-
const tokResp = (await exec(`gcloud auth print-access-token`)).trim();
|
|
193
|
-
assert(tokResp, 'Failed to get access token.', 500);
|
|
194
|
-
return tokResp;
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const getGeminiVideo = async (jobId, accessToken) => {
|
|
198
|
-
const client = clients?.[GEMINI];
|
|
199
|
-
assert(client, 'No available video generation provider.');
|
|
200
|
-
const resp = await (await fetch(
|
|
201
|
-
'https://us-central1-aiplatform.googleapis.com/v1/projects/'
|
|
202
|
-
+ `${client.projectId}/locations/us-central1/publishers/google/models/`
|
|
203
|
-
+ `${VEO_MODEL}:fetchPredictOperation`, {
|
|
204
|
-
method: 'POST', headers: {
|
|
205
|
-
'Content-Type': 'application/json',
|
|
206
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
207
|
-
}, body: JSON.stringify({ operationName: jobId })
|
|
208
|
-
})).json();
|
|
209
|
-
assert(resp?.response?.videos?.length,
|
|
210
|
-
'Waiting for Gemini video generation: '
|
|
211
|
-
+ jobId.replace(/^.*\/([^/]+)$/, '$1'));
|
|
212
|
-
return resp?.response?.videos;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
139
|
const video = async (prompt, options) => {
|
|
216
140
|
let provider = ensureString(options?.provider, { case: 'UP' });
|
|
217
|
-
if (!provider
|
|
218
|
-
&& clients?.[GEMINI]?.credentials
|
|
219
|
-
&& clients?.[GEMINI]?.projectId) { provider = GEMINI; }
|
|
141
|
+
if (!provider && clients?.[GEMINI]) { provider = GEMINI; }
|
|
220
142
|
const client = clients?.[provider];
|
|
221
143
|
assert(client, 'No available video generation provider.');
|
|
222
|
-
const accessToken = await getGeminiAccessToken(client.credentials);
|
|
223
144
|
prompt = ensureString(prompt);
|
|
224
145
|
assert(prompt.length <= 4000,
|
|
225
146
|
'Prompt must be less than 4000 characters.', 400);
|
|
@@ -229,39 +150,50 @@ const video = async (prompt, options) => {
|
|
|
229
150
|
};
|
|
230
151
|
switch (provider) {
|
|
231
152
|
case GEMINI:
|
|
232
|
-
var resp = await (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
153
|
+
var resp = await client.gen.models.generateVideos({
|
|
154
|
+
model: VEO_MODEL, prompt, config: {
|
|
155
|
+
aspectRatio: '16:9', numberOfVideos: 1,
|
|
156
|
+
personGeneration: 'allow_adult',
|
|
157
|
+
enablePromptRewriting: true, addWatermark: false,
|
|
158
|
+
includeRaiReason: true, ...options?.config || {},
|
|
239
159
|
},
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
aspectRatio: '16:9', sampleCount: 4,
|
|
243
|
-
durationSeconds: '8', fps: '24',
|
|
244
|
-
personGeneration: 'allow_adult',
|
|
245
|
-
enablePromptRewriting: true, addWatermark: false,
|
|
246
|
-
includeRaiReason: true, ...options?.params || {},
|
|
247
|
-
},
|
|
248
|
-
})
|
|
249
|
-
})).json();
|
|
250
|
-
assert(
|
|
251
|
-
!resp?.error && resp?.name,
|
|
252
|
-
resp?.error?.message || ERROR_GENERATING
|
|
253
|
-
);
|
|
160
|
+
});
|
|
161
|
+
assert(!resp?.error, resp?.error?.message || ERROR_GENERATING);
|
|
254
162
|
if (options?.generateRaw) { return resp; }
|
|
255
|
-
|
|
256
|
-
resp.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
163
|
+
await tryUntil(async () => {
|
|
164
|
+
resp = await client.gen.operations.getVideosOperation({
|
|
165
|
+
operation: resp,
|
|
166
|
+
});
|
|
167
|
+
assert(
|
|
168
|
+
resp?.done,
|
|
169
|
+
`Waiting for Gemini video generation: ${resp.name}`,
|
|
170
|
+
);
|
|
171
|
+
}, { maxTry: 60 * 10, log });
|
|
172
|
+
let generated = resp?.response?.generatedVideos;
|
|
173
|
+
assert(!resp?.error && generated?.filter(
|
|
174
|
+
x => !x.raiFilteredReason
|
|
175
|
+
).length, resp?.error?.message || generated?.find(
|
|
176
|
+
x => x.raiFilteredReason
|
|
177
|
+
)?.raiFilteredReason || ERROR_GENERATING);
|
|
178
|
+
if (!options?.videoRaw) {
|
|
179
|
+
generated = await Promise.all(generated?.filter(
|
|
180
|
+
x => x?.video?.uri
|
|
181
|
+
).map(async (x, i) => {
|
|
182
|
+
const downloadPath = `${getTempPath({
|
|
183
|
+
seed: x?.video?.uri
|
|
184
|
+
})}.mp4`;
|
|
185
|
+
// @todo: fix this
|
|
186
|
+
// https://github.com/googleapis/js-genai/compare/main...Leask:js-genai:main
|
|
187
|
+
await client.gen.files.download({ file: x, downloadPath });
|
|
188
|
+
await timeout(1000 * 10); // hack to wait for file to be downloaded
|
|
189
|
+
return {
|
|
190
|
+
caption: `🎥 by ${VEO_MODEL}`,
|
|
191
|
+
data: await extractVideo(downloadPath, options),
|
|
192
|
+
mimeType: MIME_MP4, jobId: resp.name,
|
|
193
|
+
};
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
return generated;
|
|
265
197
|
default:
|
|
266
198
|
throw new Error('Invalid provider.');
|
|
267
199
|
}
|
package/lib/manifest.mjs
CHANGED