utilitas 1999.1.78 → 1999.1.80
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/alan.mjs +4 -2
- package/lib/gen.mjs +78 -145
- package/lib/manifest.mjs +1 -1
- package/package.json +1 -1
package/lib/alan.mjs
CHANGED
|
@@ -1197,8 +1197,10 @@ const promptGemini = async (aiId, content, options = {}) => {
|
|
|
1197
1197
|
const chat = client.chats.create({
|
|
1198
1198
|
model: options.model, history, config: {
|
|
1199
1199
|
responseMimeType: options.jsonMode ? MIME_JSON : MIME_TEXT,
|
|
1200
|
-
|
|
1201
|
-
|
|
1200
|
+
...model.reasoning ? {
|
|
1201
|
+
thinkingConfig: { includeThoughts: true },
|
|
1202
|
+
} : {}, systemInstruction,
|
|
1203
|
+
responseModalities: options.modalities || (
|
|
1202
1204
|
options.imageMode ? [TEXT, IMAGE] : undefined
|
|
1203
1205
|
), ...options?.config || {}, ...model?.tools && !options.jsonMode
|
|
1204
1206
|
&& ![GEMINI_20_FLASH].includes(options.model)
|
package/lib/gen.mjs
CHANGED
|
@@ -1,42 +1,40 @@
|
|
|
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';
|
|
8
|
+
import { hash } from './encryption.mjs';
|
|
9
9
|
|
|
10
|
-
const _NEED = ['OpenAI'];
|
|
10
|
+
const _NEED = ['OpenAI', '@google/genai'];
|
|
11
11
|
const log = (cnt, opt) => _log(cnt, import.meta.url, { time: 1, ...opt || {} });
|
|
12
12
|
const [
|
|
13
|
-
clients, OPENAI, GEMINI, BASE64, BUFFER, ERROR_GENERATING,
|
|
14
|
-
OPENAI_MODEL, VEO_MODEL,
|
|
13
|
+
clients, OPENAI, GEMINI, BASE64, FILE, BUFFER, ERROR_GENERATING,
|
|
14
|
+
IMAGEN_MODEL, OPENAI_MODEL, VEO_MODEL,
|
|
15
15
|
] = [
|
|
16
|
-
{}, 'OPENAI', 'GEMINI', 'BASE64', '
|
|
17
|
-
'imagen-3.0-generate-002', 'gpt-image-1',
|
|
16
|
+
{}, 'OPENAI', 'GEMINI', 'BASE64', 'FILE', 'BUFFER',
|
|
17
|
+
'Error generating media.', 'imagen-3.0-generate-002', 'gpt-image-1',
|
|
18
|
+
'veo-2.0-generate-001',
|
|
18
19
|
];
|
|
19
20
|
|
|
20
21
|
const init = async (options) => {
|
|
21
|
-
assert(
|
|
22
|
-
options?.apiKey || (options?.credentials && options?.projectId),
|
|
23
|
-
'API key or credentials are required.'
|
|
24
|
-
);
|
|
22
|
+
assert(options?.apiKey, 'API key is required.');
|
|
25
23
|
const provider = ensureString(options?.provider, { case: 'UP' });
|
|
26
24
|
switch (provider) {
|
|
27
25
|
case OPENAI:
|
|
28
26
|
const OpenAI = await need('openai');
|
|
29
|
-
|
|
27
|
+
var client = new OpenAI(options);
|
|
30
28
|
clients[provider] = {
|
|
31
|
-
image:
|
|
29
|
+
image: client.images,
|
|
32
30
|
toFile: OpenAI.toFile,
|
|
33
31
|
};
|
|
34
32
|
break;
|
|
35
33
|
case GEMINI:
|
|
34
|
+
const { GoogleGenAI } = await need('@google/genai');
|
|
35
|
+
var client = new GoogleGenAI({ vertexai: false, ...options });
|
|
36
36
|
clients[provider] = {
|
|
37
|
-
|
|
38
|
-
projectId: options.projectId,
|
|
39
|
-
credentials: options.credentials,
|
|
37
|
+
gen: client,
|
|
40
38
|
};
|
|
41
39
|
break;
|
|
42
40
|
default:
|
|
@@ -50,7 +48,7 @@ const extractImage = async (data, options) => await convert(
|
|
|
50
48
|
);
|
|
51
49
|
|
|
52
50
|
const extractVideo = async (data, options) => await convert(
|
|
53
|
-
data, { input:
|
|
51
|
+
data, { input: FILE, suffix: 'mp4', ...options || {} }
|
|
54
52
|
);
|
|
55
53
|
|
|
56
54
|
const prepareImage = async (files, repack, options) => {
|
|
@@ -68,8 +66,8 @@ const prepareImage = async (files, repack, options) => {
|
|
|
68
66
|
|
|
69
67
|
const image = async (prompt, options) => {
|
|
70
68
|
let provider = ensureString(options?.provider, { case: 'UP' });
|
|
71
|
-
if (!provider && clients?.[GEMINI]
|
|
72
|
-
if (!provider && clients?.[OPENAI]) { provider = OPENAI; }
|
|
69
|
+
if (!provider && clients?.[GEMINI]) { provider = GEMINI; }
|
|
70
|
+
else if (!provider && clients?.[OPENAI]) { provider = OPENAI; }
|
|
73
71
|
const client = clients?.[provider];
|
|
74
72
|
const n = options?.n || 4;
|
|
75
73
|
assert(client, 'No available image generation provider.');
|
|
@@ -110,66 +108,25 @@ const image = async (prompt, options) => {
|
|
|
110
108
|
}
|
|
111
109
|
return resp?.data;
|
|
112
110
|
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);
|
|
111
|
+
var resp = await client.gen.models.generateImages({
|
|
112
|
+
model: IMAGEN_MODEL, prompt, config: {
|
|
113
|
+
numberOfImages: n, includeRaiReason: true,
|
|
114
|
+
// "1:1" (default), "3:4", "4:3", "9:16", and "16:9"
|
|
115
|
+
aspectRatio: '16:9', personGeneration: 'allow_adult',
|
|
116
|
+
...options?.config || {},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
const generated = resp?.generatedImages;
|
|
120
|
+
assert(!resp?.error && generated?.filter(
|
|
121
|
+
x => !x.raiFilteredReason
|
|
122
|
+
).length, resp?.error?.message || generated?.find(
|
|
123
|
+
x => x.raiFilteredReason
|
|
124
|
+
)?.raiFilteredReason || ERROR_GENERATING);
|
|
168
125
|
if (!options?.raw) {
|
|
169
|
-
resp = await Promise.all((resp?.
|
|
126
|
+
resp = await Promise.all((resp?.generatedImages || []).map(
|
|
170
127
|
async x => ({
|
|
171
128
|
caption: `🎨 by ${IMAGEN_MODEL}`,
|
|
172
|
-
data: await extractImage(x.
|
|
129
|
+
data: await extractImage(x.image.imageBytes, options),
|
|
173
130
|
mimeType: x.mimeType,
|
|
174
131
|
})
|
|
175
132
|
));
|
|
@@ -180,46 +137,11 @@ const image = async (prompt, options) => {
|
|
|
180
137
|
}
|
|
181
138
|
};
|
|
182
139
|
|
|
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
140
|
const video = async (prompt, options) => {
|
|
216
141
|
let provider = ensureString(options?.provider, { case: 'UP' });
|
|
217
|
-
if (!provider
|
|
218
|
-
&& clients?.[GEMINI]?.credentials
|
|
219
|
-
&& clients?.[GEMINI]?.projectId) { provider = GEMINI; }
|
|
142
|
+
if (!provider && clients?.[GEMINI]) { provider = GEMINI; }
|
|
220
143
|
const client = clients?.[provider];
|
|
221
144
|
assert(client, 'No available video generation provider.');
|
|
222
|
-
const accessToken = await getGeminiAccessToken(client.credentials);
|
|
223
145
|
prompt = ensureString(prompt);
|
|
224
146
|
assert(prompt.length <= 4000,
|
|
225
147
|
'Prompt must be less than 4000 characters.', 400);
|
|
@@ -229,39 +151,50 @@ const video = async (prompt, options) => {
|
|
|
229
151
|
};
|
|
230
152
|
switch (provider) {
|
|
231
153
|
case GEMINI:
|
|
232
|
-
var resp = await (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
'Authorization': `Bearer ${accessToken}`,
|
|
154
|
+
var resp = await client.gen.models.generateVideos({
|
|
155
|
+
model: VEO_MODEL, prompt, config: {
|
|
156
|
+
aspectRatio: '16:9', numberOfVideos: 1,
|
|
157
|
+
personGeneration: 'allow_adult',
|
|
158
|
+
enablePromptRewriting: true, addWatermark: false,
|
|
159
|
+
includeRaiReason: true, ...options?.config || {},
|
|
239
160
|
},
|
|
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
|
-
);
|
|
161
|
+
});
|
|
162
|
+
assert(!resp?.error, resp?.error?.message || ERROR_GENERATING);
|
|
254
163
|
if (options?.generateRaw) { return resp; }
|
|
255
|
-
|
|
256
|
-
resp.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
164
|
+
await tryUntil(async () => {
|
|
165
|
+
resp = await client.gen.operations.getVideosOperation({
|
|
166
|
+
operation: resp,
|
|
167
|
+
});
|
|
168
|
+
assert(
|
|
169
|
+
resp?.done,
|
|
170
|
+
`Waiting for Gemini video generation: ${resp.name}`,
|
|
171
|
+
);
|
|
172
|
+
}, { maxTry: 60 * 10, log });
|
|
173
|
+
let generated = resp?.response?.generatedVideos;
|
|
174
|
+
assert(!resp?.error && generated?.filter(
|
|
175
|
+
x => !x.raiFilteredReason
|
|
176
|
+
).length, resp?.error?.message || generated?.find(
|
|
177
|
+
x => x.raiFilteredReason
|
|
178
|
+
)?.raiFilteredReason || ERROR_GENERATING);
|
|
179
|
+
if (!options?.videoRaw) {
|
|
180
|
+
generated = await Promise.all(generated?.filter(
|
|
181
|
+
x => x?.video?.uri
|
|
182
|
+
).map(async (x, i) => {
|
|
183
|
+
const downloadPath = `${getTempPath({
|
|
184
|
+
seed: x?.video?.uri
|
|
185
|
+
})}.mp4`;
|
|
186
|
+
// @todo: fix this
|
|
187
|
+
// https://github.com/googleapis/js-genai/compare/main...Leask:js-genai:main
|
|
188
|
+
await client.gen.files.download({ file: x, downloadPath });
|
|
189
|
+
await timeout(5000);
|
|
190
|
+
return {
|
|
191
|
+
caption: `🎥 by ${VEO_MODEL}`,
|
|
192
|
+
data: await extractVideo(downloadPath, options),
|
|
193
|
+
mimeType: MIME_MP4, jobId: resp.name,
|
|
194
|
+
};
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
return generated;
|
|
265
198
|
default:
|
|
266
199
|
throw new Error('Invalid provider.');
|
|
267
200
|
}
|
package/lib/manifest.mjs
CHANGED