utilitas 1999.1.69 → 1999.1.71
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 +5 -3
- package/dist/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/lib/alan.mjs +17 -11
- package/lib/manifest.mjs +16 -17
- package/lib/media.mjs +12 -0
- package/lib/speech.mjs +52 -21
- package/package.json +16 -17
package/lib/alan.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { checkSearch, distill, search } from './web.mjs';
|
|
2
2
|
import { create as createUoid } from './uoid.mjs';
|
|
3
|
-
import { createWavHeader } from './media.mjs';
|
|
4
3
|
import { end, loop } from './event.mjs';
|
|
5
4
|
import { fileTypeFromBuffer } from 'file-type';
|
|
5
|
+
import { packPcmToWav } from './media.mjs';
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid';
|
|
7
7
|
|
|
8
8
|
import {
|
|
@@ -62,7 +62,7 @@ const [
|
|
|
62
62
|
JINA_CLIP, VERTEX, GEMINI_25_PRO, SILICONFLOW, SF_DEEPSEEK_V3,
|
|
63
63
|
] = [
|
|
64
64
|
'OpenAI', 'Gemini', 'OPENAI_TRAINING', 'Ollama', 'gpt-4o', 'o3',
|
|
65
|
-
'gemini-2.5-flash-preview-
|
|
65
|
+
'gemini-2.5-flash-preview-05-20', 'nova', 'deepseek-r1', '```',
|
|
66
66
|
'text-embedding-3-small', 'text-embedding-3-large',
|
|
67
67
|
'claude-3-7-sonnet@20250219', 'audio', 'wav', '[ATTACHMENTS]', 'CHAT',
|
|
68
68
|
'OPENAI_VOICE', 'medium', 'low', 'high', 'medium', 'think', '<think>',
|
|
@@ -465,7 +465,7 @@ const init = async (options = {}) => {
|
|
|
465
465
|
process.env['ANTHROPIC_VERTEX_PROJECT_ID'] = options.projectId;
|
|
466
466
|
var model = models[0];
|
|
467
467
|
var client = new ((
|
|
468
|
-
await need('@anthropic-ai/vertex-sdk')
|
|
468
|
+
await need('@anthropic-ai/vertex-sdk', { raw: true })
|
|
469
469
|
).AnthropicVertex)({ region: options?.region || 'us-east5' });
|
|
470
470
|
setupAi({ provider, model, client, prompt: promptAnthropic });
|
|
471
471
|
break;
|
|
@@ -707,12 +707,8 @@ const packResp = async (resp, options) => {
|
|
|
707
707
|
const str = simpleText.indexOf(x);
|
|
708
708
|
str >= 0 && (simpleText = simpleText.slice(0, str).trim());
|
|
709
709
|
});
|
|
710
|
-
audio
|
|
711
|
-
input: BASE64, expected: BUFFER,
|
|
712
|
-
})) && audio.length && (audio = Buffer.concat([
|
|
713
|
-
createWavHeader(audio.length), audio
|
|
714
|
-
])) && (audio = await convert(audio, {
|
|
715
|
-
input: BUFFER, expected: BUFFER, ...options || {},
|
|
710
|
+
audio = await ignoreErrFunc(async () => await packPcmToWav(audio, {
|
|
711
|
+
input: Buffer.isBuffer(audio) ? BUFFER : BASE64, expected: BUFFER,
|
|
716
712
|
}));
|
|
717
713
|
if (images?.length) {
|
|
718
714
|
for (let i in images) {
|
|
@@ -1205,12 +1201,17 @@ const promptGemini = async (aiId, content, options = {}) => {
|
|
|
1205
1201
|
systemInstruction, responseModalities: options.modalities || (
|
|
1206
1202
|
options.imageMode ? [TEXT, IMAGE] : undefined
|
|
1207
1203
|
), ...options?.config || {}, ...model?.tools && !options.jsonMode
|
|
1208
|
-
|
|
1204
|
+
// @todo: Gemini 2.5 flash 05-20 refusing to use certain tool calls
|
|
1205
|
+
// https://discuss.ai.google.dev/t/gemini-2-5-flash-05-20-refusing-to-use-certain-tool-calls/84086
|
|
1206
|
+
&& ![GEMINI_20_FLASH, GEMINI_25_FLASH].includes(options.model)
|
|
1207
|
+
? (options.tools ?? {
|
|
1209
1208
|
tools: [
|
|
1210
1209
|
// @todo: Gemini will failed when using these tools together.
|
|
1211
1210
|
// https://ai.google.dev/gemini-api/docs/function-calling
|
|
1212
1211
|
// { codeExecution: {} },
|
|
1213
1212
|
// { googleSearch: {} },
|
|
1213
|
+
// { urlContext: {} },
|
|
1214
|
+
// @todo: test these tools in next version 👆
|
|
1214
1215
|
{
|
|
1215
1216
|
functionDeclarations: (
|
|
1216
1217
|
await toolsGemini({ provider })
|
|
@@ -1222,7 +1223,11 @@ const promptGemini = async (aiId, content, options = {}) => {
|
|
|
1222
1223
|
});
|
|
1223
1224
|
const resp = await chat.sendMessageStream({ message: prompt });
|
|
1224
1225
|
for await (const chunk of resp) {
|
|
1225
|
-
|
|
1226
|
+
assert(
|
|
1227
|
+
!chunk?.promptFeedback?.blockReason,
|
|
1228
|
+
chunk?.promptFeedback?.blockReason
|
|
1229
|
+
);
|
|
1230
|
+
event = chunk?.candidates?.[0];
|
|
1226
1231
|
let [deltaText, deltaImages] = ['', []];
|
|
1227
1232
|
event?.content?.parts?.map(x => {
|
|
1228
1233
|
if (x.text) { deltaText = x.text; }
|
|
@@ -1583,6 +1588,7 @@ export {
|
|
|
1583
1588
|
getSession,
|
|
1584
1589
|
init,
|
|
1585
1590
|
initChat,
|
|
1591
|
+
k,
|
|
1586
1592
|
listFiles,
|
|
1587
1593
|
listGptFineTuningEvents,
|
|
1588
1594
|
listGptFineTuningJobs,
|
package/lib/manifest.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const manifest = {
|
|
2
2
|
"name": "utilitas",
|
|
3
3
|
"description": "Just another common utility for JavaScript.",
|
|
4
|
-
"version": "1999.1.
|
|
4
|
+
"version": "1999.1.71",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/Leask/utilitas",
|
|
7
7
|
"main": "index.mjs",
|
|
@@ -20,22 +20,21 @@ const manifest = {
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"file-type": "^20.5.0",
|
|
23
|
-
"mathjs": "^14.
|
|
23
|
+
"mathjs": "^14.5.0",
|
|
24
24
|
"uuid": "^11.1.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@anthropic-ai/sdk": "^0.
|
|
28
|
-
"@anthropic-ai/vertex-sdk": "^0.
|
|
27
|
+
"@anthropic-ai/sdk": "^0.51.0",
|
|
28
|
+
"@anthropic-ai/vertex-sdk": "^0.11.3",
|
|
29
29
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
30
30
|
"@ffprobe-installer/ffprobe": "^2.1.2",
|
|
31
|
-
"@google-cloud/speech": "^7.0
|
|
31
|
+
"@google-cloud/speech": "^7.1.0",
|
|
32
32
|
"@google-cloud/storage": "^7.16.0",
|
|
33
|
-
"@google-cloud/text-to-speech": "^6.0.1",
|
|
34
33
|
"@google-cloud/vision": "^5.1.0",
|
|
35
|
-
"@google/genai": "^0.
|
|
34
|
+
"@google/genai": "^1.0.0",
|
|
36
35
|
"@mozilla/readability": "github:mozilla/readability",
|
|
37
|
-
"@sentry/node": "^9.
|
|
38
|
-
"@sentry/profiling-node": "^9.
|
|
36
|
+
"@sentry/node": "^9.22.0",
|
|
37
|
+
"@sentry/profiling-node": "^9.22.0",
|
|
39
38
|
"acme-client": "^5.4.0",
|
|
40
39
|
"browserify-fs": "^1.0.0",
|
|
41
40
|
"buffer": "^6.0.3",
|
|
@@ -47,28 +46,28 @@ const manifest = {
|
|
|
47
46
|
"jsdom": "^26.1.0",
|
|
48
47
|
"lorem-ipsum": "^2.0.8",
|
|
49
48
|
"mailgun.js": "^12.0.1",
|
|
50
|
-
"mailparser": "^3.7.
|
|
49
|
+
"mailparser": "^3.7.3",
|
|
51
50
|
"mime": "^4.0.7",
|
|
52
51
|
"mysql2": "^3.14.1",
|
|
53
52
|
"node-mailjet": "^6.0.8",
|
|
54
53
|
"node-polyfill-webpack-plugin": "^4.1.0",
|
|
55
54
|
"office-text-extractor": "^3.0.3",
|
|
56
|
-
"openai": "^4.
|
|
55
|
+
"openai": "^4.100.0",
|
|
57
56
|
"pdfjs-dist": "^5.2.133",
|
|
58
|
-
"pg": "^8.
|
|
59
|
-
"pgvector": "^0.2.
|
|
57
|
+
"pg": "^8.16.0",
|
|
58
|
+
"pgvector": "^0.2.1",
|
|
60
59
|
"ping": "^0.4.4",
|
|
61
60
|
"process": "^0.11.10",
|
|
62
|
-
"puppeteer": "^24.
|
|
61
|
+
"puppeteer": "^24.9.0",
|
|
63
62
|
"say": "^0.16.0",
|
|
64
63
|
"telegraf": "^4.16.3",
|
|
65
|
-
"telesignsdk": "^3.0.
|
|
64
|
+
"telesignsdk": "^3.0.3",
|
|
66
65
|
"tesseract.js": "^6.0.1",
|
|
67
|
-
"twilio": "^5.
|
|
66
|
+
"twilio": "^5.6.1",
|
|
68
67
|
"url": "github:Leask/node-url",
|
|
69
68
|
"webpack-cli": "^6.0.1",
|
|
70
69
|
"whisper-node": "^1.1.1",
|
|
71
|
-
"wrangler": "^4.
|
|
70
|
+
"wrangler": "^4.16.0",
|
|
72
71
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz",
|
|
73
72
|
"youtube-transcript": "^1.2.1"
|
|
74
73
|
}
|
package/lib/media.mjs
CHANGED
|
@@ -38,6 +38,17 @@ const createWavHeader = (
|
|
|
38
38
|
return header;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
const packPcmToWav = async (audio, options) => {
|
|
42
|
+
(audio = await convert(audio, { ...options || {}, expected: BUFFER })) // DON'T override expected
|
|
43
|
+
&& audio.length
|
|
44
|
+
&& (audio = Buffer.concat([createWavHeader(audio.length), audio]))
|
|
45
|
+
&& (audio = await convert(audio, {
|
|
46
|
+
expected: BUFFER, ...options || {}, input: BUFFER, // DON'T override input
|
|
47
|
+
}));
|
|
48
|
+
assert(audio, 'Failed to pack PCM to WAV.', 500);
|
|
49
|
+
return audio;
|
|
50
|
+
};
|
|
51
|
+
|
|
41
52
|
// https://codex.so/ffmpeg-node-js
|
|
42
53
|
const getFfmpeg = async (options) => {
|
|
43
54
|
const ffmpeg = await need('fluent-ffmpeg');
|
|
@@ -92,4 +103,5 @@ export {
|
|
|
92
103
|
convertAudioTo16kNanoPcmWave,
|
|
93
104
|
createWavHeader,
|
|
94
105
|
getFfmpeg,
|
|
106
|
+
packPcmToWav,
|
|
95
107
|
};
|
package/lib/speech.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DEFAULT_MODELS, OPENAI_VOICE } from './alan.mjs';
|
|
1
|
+
import { DEFAULT_MODELS, OPENAI_VOICE, countTokens, k } from './alan.mjs';
|
|
2
2
|
import { getApiKeyCredentials, hash } from './encryption.mjs';
|
|
3
|
-
import { getFfmpeg } from './media.mjs';
|
|
3
|
+
import { getFfmpeg, packPcmToWav } from './media.mjs';
|
|
4
4
|
import { get } from './web.mjs';
|
|
5
5
|
import { convert, getTempPath } from './storage.mjs';
|
|
6
6
|
import { ensureString } from './utilitas.mjs';
|
|
@@ -17,19 +17,32 @@ import {
|
|
|
17
17
|
|
|
18
18
|
const _NEED = [
|
|
19
19
|
'@google-cloud/speech',
|
|
20
|
-
'@google
|
|
20
|
+
'@google/genai',
|
|
21
21
|
'OpenAI',
|
|
22
22
|
'whisper-node',
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
const WHISPER_DEFAULT_MODEL = 'base';
|
|
26
26
|
const errorMessage = 'Invalid audio data.';
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
|
|
28
|
+
const [
|
|
29
|
+
BUFFER, STREAM, BASE64, FILE, clients, languageCode, audioEncoding, suffix,
|
|
30
|
+
SPEAKER, cleanup, wav,
|
|
31
|
+
] = [
|
|
32
|
+
'BUFFER', 'STREAM', 'BASE64', 'FILE', {}, 'en-US', 'OGG_OPUS', 'ogg',
|
|
33
|
+
'SPEAKER', true, 'wav'
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const [
|
|
37
|
+
GPT_4O_MIMI_TTS, GPT_4O_TRANSCRIBE, GEMINI_25_PRO_TTS, GEMINI_25_FLASH_TTS,
|
|
38
|
+
OPENAI_TTS_MAX_LENGTH,
|
|
39
|
+
] = [
|
|
40
|
+
'gpt-4o-mini-tts', 'gpt-4o-transcribe', 'gemini-2.5-pro-preview-tts',
|
|
41
|
+
'gemini-2.5-flash-preview-tts', 4096
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const [defaultOpenAITtsModel, defaultOpenAISttModel, defaultGeminiTtsModel]
|
|
45
|
+
= [GPT_4O_MIMI_TTS, GPT_4O_TRANSCRIBE, GEMINI_25_PRO_TTS];
|
|
33
46
|
|
|
34
47
|
const WHISPER_MODELS = [
|
|
35
48
|
// npx whisper-node download tiny.en
|
|
@@ -104,13 +117,14 @@ const init = async (options) => {
|
|
|
104
117
|
break;
|
|
105
118
|
case 'GOOGLE':
|
|
106
119
|
clients._provider = provider;
|
|
107
|
-
const sslCreds = await getApiKeyCredentials(options);
|
|
108
120
|
if (options?.tts) {
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
let { GoogleGenAI } = await need('@google/genai');
|
|
122
|
+
let client = new GoogleGenAI(options);
|
|
123
|
+
clients.tts = client.models.generateContent;
|
|
111
124
|
}
|
|
112
125
|
if (options?.stt) {
|
|
113
126
|
const stt = (await need('@google-cloud/speech')).default;
|
|
127
|
+
const sslCreds = await getApiKeyCredentials(options);
|
|
114
128
|
clients.stt = new stt.SpeechClient({ sslCreds });
|
|
115
129
|
}
|
|
116
130
|
break;
|
|
@@ -159,15 +173,29 @@ const ttsOpenAI = async (input, options) => {
|
|
|
159
173
|
return await convert(buffer, { suffix, ...options || {} });
|
|
160
174
|
};
|
|
161
175
|
|
|
162
|
-
|
|
176
|
+
// https://ai.google.dev/gemini-api/docs/speech-generation#voices
|
|
177
|
+
const ttsGoogle = async (contents, options) => {
|
|
163
178
|
assert(clients.tts, 'Google TTS API has not been initialized.', 500);
|
|
164
|
-
assert(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
179
|
+
assert(contents, 'Text is required.', 400);
|
|
180
|
+
assert(await countTokens(contents) <= k(32), 'Text is too long.', 400);
|
|
181
|
+
const resp = await clients.tts({
|
|
182
|
+
model: options?.model || defaultGeminiTtsModel, contents,
|
|
183
|
+
config: {
|
|
184
|
+
responseModalities: ['AUDIO'],
|
|
185
|
+
speechConfig: {
|
|
186
|
+
voiceConfig: {
|
|
187
|
+
prebuiltVoiceConfig: {
|
|
188
|
+
voiceName: options?.voice || 'Leda',
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
const rawAudio = resp?.candidates?.[0]?.content?.parts?.[0]?.inlineData;
|
|
195
|
+
assert(rawAudio, 'Failed to generate audio.', 500);
|
|
196
|
+
return options?.raw ? rawAudio : await packPcmToWav(rawAudio?.data, {
|
|
197
|
+
input: BASE64, expected: 'FILE', suffix: wav, ...options || {},
|
|
169
198
|
});
|
|
170
|
-
return await convert(response.audioContent, { suffix, ...options || {} });
|
|
171
199
|
};
|
|
172
200
|
|
|
173
201
|
const ttsSay = async (text, options) => {
|
|
@@ -275,13 +303,16 @@ const stt = async (audio, options) => {
|
|
|
275
303
|
export default init;
|
|
276
304
|
export {
|
|
277
305
|
_NEED,
|
|
306
|
+
OPENAI_TTS_MAX_LENGTH,
|
|
278
307
|
checkSay,
|
|
279
308
|
checkWhisper,
|
|
280
|
-
init,
|
|
309
|
+
init,
|
|
310
|
+
stt,
|
|
311
|
+
sttGoogle,
|
|
281
312
|
sttOpenAI,
|
|
282
313
|
sttWhisper,
|
|
283
314
|
tts,
|
|
284
315
|
ttsGoogle,
|
|
285
316
|
ttsOpenAI,
|
|
286
|
-
ttsSay
|
|
317
|
+
ttsSay,
|
|
287
318
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "utilitas",
|
|
3
3
|
"description": "Just another common utility for JavaScript.",
|
|
4
|
-
"version": "1999.1.
|
|
4
|
+
"version": "1999.1.71",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/Leask/utilitas",
|
|
7
7
|
"main": "index.mjs",
|
|
@@ -31,22 +31,21 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"file-type": "^20.5.0",
|
|
34
|
-
"mathjs": "^14.
|
|
34
|
+
"mathjs": "^14.5.0",
|
|
35
35
|
"uuid": "^11.1.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@anthropic-ai/sdk": "^0.
|
|
39
|
-
"@anthropic-ai/vertex-sdk": "^0.
|
|
38
|
+
"@anthropic-ai/sdk": "^0.51.0",
|
|
39
|
+
"@anthropic-ai/vertex-sdk": "^0.11.3",
|
|
40
40
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
41
41
|
"@ffprobe-installer/ffprobe": "^2.1.2",
|
|
42
|
-
"@google-cloud/speech": "^7.0
|
|
42
|
+
"@google-cloud/speech": "^7.1.0",
|
|
43
43
|
"@google-cloud/storage": "^7.16.0",
|
|
44
|
-
"@google-cloud/text-to-speech": "^6.0.1",
|
|
45
44
|
"@google-cloud/vision": "^5.1.0",
|
|
46
|
-
"@google/genai": "^0.
|
|
45
|
+
"@google/genai": "^1.0.0",
|
|
47
46
|
"@mozilla/readability": "github:mozilla/readability",
|
|
48
|
-
"@sentry/node": "^9.
|
|
49
|
-
"@sentry/profiling-node": "^9.
|
|
47
|
+
"@sentry/node": "^9.22.0",
|
|
48
|
+
"@sentry/profiling-node": "^9.22.0",
|
|
50
49
|
"acme-client": "^5.4.0",
|
|
51
50
|
"browserify-fs": "^1.0.0",
|
|
52
51
|
"buffer": "^6.0.3",
|
|
@@ -58,28 +57,28 @@
|
|
|
58
57
|
"jsdom": "^26.1.0",
|
|
59
58
|
"lorem-ipsum": "^2.0.8",
|
|
60
59
|
"mailgun.js": "^12.0.1",
|
|
61
|
-
"mailparser": "^3.7.
|
|
60
|
+
"mailparser": "^3.7.3",
|
|
62
61
|
"mime": "^4.0.7",
|
|
63
62
|
"mysql2": "^3.14.1",
|
|
64
63
|
"node-mailjet": "^6.0.8",
|
|
65
64
|
"node-polyfill-webpack-plugin": "^4.1.0",
|
|
66
65
|
"office-text-extractor": "^3.0.3",
|
|
67
|
-
"openai": "^4.
|
|
66
|
+
"openai": "^4.100.0",
|
|
68
67
|
"pdfjs-dist": "^5.2.133",
|
|
69
|
-
"pg": "^8.
|
|
70
|
-
"pgvector": "^0.2.
|
|
68
|
+
"pg": "^8.16.0",
|
|
69
|
+
"pgvector": "^0.2.1",
|
|
71
70
|
"ping": "^0.4.4",
|
|
72
71
|
"process": "^0.11.10",
|
|
73
|
-
"puppeteer": "^24.
|
|
72
|
+
"puppeteer": "^24.9.0",
|
|
74
73
|
"say": "^0.16.0",
|
|
75
74
|
"telegraf": "^4.16.3",
|
|
76
|
-
"telesignsdk": "^3.0.
|
|
75
|
+
"telesignsdk": "^3.0.3",
|
|
77
76
|
"tesseract.js": "^6.0.1",
|
|
78
|
-
"twilio": "^5.
|
|
77
|
+
"twilio": "^5.6.1",
|
|
79
78
|
"url": "github:Leask/node-url",
|
|
80
79
|
"webpack-cli": "^6.0.1",
|
|
81
80
|
"whisper-node": "^1.1.1",
|
|
82
|
-
"wrangler": "^4.
|
|
81
|
+
"wrangler": "^4.16.0",
|
|
83
82
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz",
|
|
84
83
|
"youtube-transcript": "^1.2.1"
|
|
85
84
|
}
|