utilitas 1997.1.21 → 1997.1.23

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/lib/alan.mjs CHANGED
@@ -18,17 +18,18 @@ const _NEED = [
18
18
  ];
19
19
 
20
20
  const [
21
- OPENAI, GEMINI, CHATGPT, ASSISTANT, OPENAI_EMBEDDING, GEMINI_EMEDDING,
22
- OPENAI_TRAINING, OLLAMA, CLAUDE, GPT_4O_MINI, GPT_4O, GPT_O1, GPT_O1_MINI,
23
- GEMINI_20_FLASH, NOVA, EMBEDDING_001, MISTRAL, TEXT_EMBEDDING_3_SMALL,
24
- TEXT_EMBEDDING_3_LARGE, CLAUDE_35_SONNET, CLAUDE_35_HAIKU, AUDIO, WAV,
21
+ OPENAI, GEMINI, CHATGPT, OPENAI_EMBEDDING, GEMINI_EMEDDING, OPENAI_TRAINING,
22
+ OLLAMA, CLAUDE, GPT_4O_MINI, GPT_4O, GPT_O1, GPT_O1_MINI, GEMINI_20_FLASH,
23
+ GEMINI_20_FLASH_THINKING, NOVA, EMBEDDING_001, MISTRAL,
24
+ TEXT_EMBEDDING_3_SMALL, TEXT_EMBEDDING_3_LARGE, CLAUDE_35_SONNET,
25
+ CLAUDE_35_HAIKU, AUDIO, WAV,
25
26
  ] = [
26
- 'OPENAI', 'GEMINI', 'CHATGPT', 'ASSISTANT', 'OPENAI_EMBEDDING',
27
- 'GEMINI_EMEDDING', 'OPENAI_TRAINING', 'OLLAMA', 'CLAUDE', 'gpt-4o-mini',
28
- 'gpt-4o', 'o1-preview', 'o1-mini', 'gemini-2.0-flash-exp', 'nova',
29
- 'embedding-001', 'mistral', 'text-embedding-3-small',
30
- 'text-embedding-3-large', 'claude-3-5-sonnet-latest',
31
- 'claude-3-5-haiku-latest', 'audio', 'wav',
27
+ 'OPENAI', 'GEMINI', 'CHATGPT', 'OPENAI_EMBEDDING', 'GEMINI_EMEDDING',
28
+ 'OPENAI_TRAINING', 'OLLAMA', 'CLAUDE', 'gpt-4o-mini', 'gpt-4o',
29
+ 'o1-preview', 'o1-mini', 'gemini-2.0-flash-exp',
30
+ 'gemini-2.0-flash-thinking-exp', 'nova', 'embedding-001',
31
+ 'mistral', 'text-embedding-3-small', 'text-embedding-3-large',
32
+ 'claude-3-5-sonnet-latest', 'claude-3-5-haiku-latest', 'audio', 'wav',
32
33
  ];
33
34
 
34
35
  const [
@@ -74,7 +75,6 @@ const buildGeminiParts = (text, atcmts) => [{ text }, ...atcmts || []];
74
75
  const [DEFAULT_CHAT_ENGINE, OPENAI_DEFAULT_VOICE] = [CHATGPT, NOVA];
75
76
 
76
77
  const DEFAULT_MODELS = {
77
- [ASSISTANT]: GPT_4O,
78
78
  [CHATGPT]: GPT_4O,
79
79
  [GEMINI_EMEDDING]: EMBEDDING_001,
80
80
  [GEMINI]: GEMINI_20_FLASH,
@@ -176,8 +176,8 @@ const MODELS = {
176
176
  maxFileSize: 20 * 1024 * 1024, // 20 MB
177
177
  maxImagePerPrompt: 3000,
178
178
  maxImageSize: Infinity,
179
- maxOutputTokens: 8192,
180
- maxUrlSize: 2 * 1024 * 1024 * 1024, // 2 GB
179
+ maxOutputTokens: 1024 * 8,
180
+ maxUrlSize: 1024 * 1024 * 1024 * 2, // 2 GB
181
181
  maxVideoLength: 60 * 50, // 50 minutes
182
182
  maxVideoLengthWithAudio: 60 * 50, // 50 minutes
183
183
  maxVideoLengthWithoutAudio: 60 * 60, // 1 hour
@@ -193,6 +193,25 @@ const MODELS = {
193
193
  flac, mp3, m4a, mpga, opus, pcm, wav, webm, tgpp,
194
194
  ],
195
195
  },
196
+ [GEMINI_20_FLASH_THINKING]: {
197
+ // https://cloud.google.com/vertex-ai/generative-ai/docs/thinking-mode?hl=en
198
+ contextWindow: 1024 * (8 + 32),
199
+ imageCostTokens: size8k / (768 * 768) * 258,
200
+ maxFileSize: 20 * 1024 * 1024, // 20 MB
201
+ maxImagePerPrompt: 3000,
202
+ maxImageSize: Infinity,
203
+ maxOutputTokens: 1024 * 8,
204
+ maxUrlSize: 1024 * 1024 * 1024 * 2, // 2 GB
205
+ requestLimitsRPM: 15,
206
+ requestLimitsRPD: 1500,
207
+ tokenLimitsTPM: 4 * 1000000,
208
+ trainingData: 'August 2024',
209
+ vision: true,
210
+ json: false,
211
+ supportedMimeTypes: [
212
+ png, jpeg,
213
+ ],
214
+ },
196
215
  [MISTRAL]: {
197
216
  contextWindow: 128000,
198
217
  requestLimitsRPM: Infinity,
@@ -295,13 +314,12 @@ const init = async (options) => {
295
314
  if (options?.apiKey) {
296
315
  const { GoogleGenerativeAI } = await need('@google/generative-ai');
297
316
  const genAi = new GoogleGenerativeAI(options.apiKey);
317
+ const genModel = options?.model || DEFAULT_MODELS[GEMINI];
298
318
  clients[provider] = {
299
- generative: genAi.getGenerativeModel({
300
- model: options?.model || DEFAULT_MODELS[GEMINI],
301
- }),
319
+ generative: genAi.getGenerativeModel({ model: genModel }),
302
320
  embedding: genAi.getGenerativeModel({
303
321
  model: DEFAULT_MODELS[GEMINI_EMEDDING],
304
- }),
322
+ }), genModel,
305
323
  };
306
324
  }
307
325
  break;
@@ -350,25 +368,18 @@ const selectGptAudioModel = options => {
350
368
  MODELS[options.model]?.audio,
351
369
  `Audio modality is not supported by model: ${options.model}`
352
370
  );
353
- options.model = MODELS[options.model].audio;
371
+ return MODELS[options.model]?.audio;
354
372
  };
355
373
 
356
374
  const buildGptMessage = (content, options) => {
357
375
  content = content || '';
358
- let audioModelSelected = false;
359
- if (options?.audioMode) {
360
- selectGptAudioModel(options);
361
- audioModelSelected = true;
362
- }
376
+ let alterModel = options?.audioMode && selectGptAudioModel(options);
363
377
  const attachments = (options?.attachments || []).map(x => {
364
378
  assert(MODELS[options?.model], 'Model is required.');
365
379
  if (MODELS[options.model]?.supportedMimeTypes?.includes?.(x.mime_type)) {
366
380
  return { type: 'image_url', image_url: { url: x.url } };
367
381
  } else if (MODELS[options.model]?.supportedAudioTypes?.includes?.(x.mime_type)) {
368
- if (!audioModelSelected) {
369
- selectGptAudioModel(options);
370
- audioModelSelected = true;
371
- }
382
+ alterModel = selectGptAudioModel(options);
372
383
  return {
373
384
  type: 'input_audio',
374
385
  input_audio: { data: x.data, format: WAV },
@@ -376,6 +387,7 @@ const buildGptMessage = (content, options) => {
376
387
  }
377
388
  throwError(`Unsupported mime type: '${x.mime_type}'.`);
378
389
  });
390
+ alterModel && (options.model = alterModel);
379
391
  const message = String.isString(content) ? {
380
392
  role: options?.role || user,
381
393
  content: content.length ? [{ type: 'text', text: content }] : [],
@@ -475,20 +487,26 @@ const promptChatGPT = async (content, options = {}) => {
475
487
  options.model = options?.model || DEFAULT_MODELS[CHATGPT];
476
488
  const message = buildGptMessage(content, options);
477
489
  const modalities = options?.modalities || (
478
- options?.audioMode ? ['text', AUDIO] : null
490
+ options?.audioMode ? ['text', AUDIO] : undefined
479
491
  );
492
+ assert(!(
493
+ options?.jsonMode && MODELS[options.model]?.json == false
494
+ ), `This model does not support JSON output: ${options.model}`);
480
495
  let format;
481
496
  [format, options.audioMimeType, options.suffix]
482
497
  = options?.stream ? ['pcm16', pcm16, 'pcm.wav'] : [WAV, wav, WAV];
483
- let [resp, resultText, resultAudio, chunk] = [await chatGptClient.chat.completions.create({
484
- modalities, audio: options?.audio || (
485
- modalities?.find?.(x => x === AUDIO) && {
486
- voice: OPENAI_DEFAULT_VOICE, format
487
- }
488
- ), ...messages([...options?.messages || [], message]),
489
- ...options?.jsonMode ? { response_format: { type: JSON_OBJECT } } : {},
490
- model: options.model, stream: !!options?.stream,
491
- }), '', Buffer.alloc(0), null];
498
+ let [resp, resultText, resultAudio, chunk] = [
499
+ await chatGptClient.chat.completions.create({
500
+ modalities, audio: options?.audio || (
501
+ modalities?.find?.(x => x === AUDIO) && {
502
+ voice: OPENAI_DEFAULT_VOICE, format
503
+ }
504
+ ), ...messages([...options?.messages || [], message]),
505
+ ...options?.jsonMode ? {
506
+ response_format: { type: JSON_OBJECT }
507
+ } : {}, model: options.model, stream: !!options?.stream,
508
+ }), '', Buffer.alloc(0), null
509
+ ];
492
510
  if (!options?.stream) {
493
511
  return { response: await packGptResp(resp, options) };
494
512
  }
@@ -563,132 +581,6 @@ const promptClaude = async (content, options) => {
563
581
  return { response: await packGptResp(options?.stream ? event : resp, options) };
564
582
  };
565
583
 
566
- const createAssistant = async (options) => {
567
- const { clientBeta } = await getOpenAIClient(options);
568
- // https://platform.openai.com/docs/api-reference/assistants/createAssistant
569
- return await clientBeta.assistants.create({
570
- model: DEFAULT_MODELS[ASSISTANT], name, instructions,
571
- tools: [CODE_INTERPRETER, RETRIEVAL], // 'FUNCTION'
572
- ...options?.params || {},
573
- // description: null, file_ids: [], metadata: {},
574
- });
575
- };
576
-
577
- const getAssistant = async (assistantId, options) => {
578
- const { clientBeta } = await getOpenAIClient(options);
579
- return await clientBeta.assistants.retrieve(assistantId);
580
- };
581
-
582
- const modifyAssistant = async (assistantId, assistant, options) => {
583
- const { clientBeta } = await getOpenAIClient(options);
584
- return await clientBeta.assistants.update(assistantId, assistant);
585
- };
586
-
587
- const deleteAssistant = async (assistantId, options) => {
588
- const { clientBeta } = await getOpenAIClient(options);
589
- const cleanup = await deleteAllFilesFromAssistant(assistantId, options);
590
- const respDel = await clientBeta.assistants.del(assistantId);
591
- return { cleanup, delete: respDel };
592
- };
593
-
594
- const listAssistant = async (options) => {
595
- const { clientBeta } = await getOpenAIClient(options);
596
- return (await clientBeta.assistants.list({
597
- order: 'asc', limit: `${GPT_QUERY_LIMIT}`, ...options?.params || {},
598
- })).data;
599
- };
600
-
601
- const ensureAssistant = async opts => {
602
- if (opts?.assistantId) { return await getAssistant(opts?.assistantId); }
603
- const list = await listAssistant(opts);
604
- return list.find(x => x.name === name) || await createAssistant(opts);
605
- };
606
-
607
- const createThread = async (options) => {
608
- const { clientBeta } = await getOpenAIClient(options);
609
- // https://platform.openai.com/docs/api-reference/threads/createThread
610
- return await clientBeta.threads.create({ ...options?.params || {} });
611
- };
612
-
613
- const getThread = async (threadId, options) => {
614
- const { clientBeta } = await getOpenAIClient(options);
615
- // https://platform.openai.com/docs/api-reference/threads/getThread
616
- return await clientBeta.threads.retrieve(threadId);
617
- };
618
-
619
- const deleteThread = async (threadId, options) => {
620
- const { clientBeta } = await getOpenAIClient(options);
621
- return await clientBeta.threads.del(threadId);
622
- };
623
-
624
- const ensureThread = async options => await (
625
- options?.threadId ? getThread(options?.threadId) : createThread(options)
626
- );
627
-
628
- const createMessage = async (threadId, content, options) => {
629
- const { clientBeta } = await getOpenAIClient(options);
630
- // https://platform.openai.com/docs/api-reference/messages/createMessage
631
- return await clientBeta.threads.messages.create(
632
- threadId, buildGptMessage(content, options),
633
- );
634
- };
635
-
636
- const listMessages = async (threadId, options) => {
637
- const { clientBeta } = await getOpenAIClient(options);
638
- // https://platform.openai.com/docs/api-reference/messages/listMessages
639
- return await clientBeta.threads.messages.list(
640
- threadId, { ...options?.params || {} }
641
- );
642
- };
643
-
644
- const getLatestMessage = async (threadId, options) => (await listMessages(
645
- threadId, { ...options, params: { ...options?.params || {}, limit: 1 } }
646
- ))?.data?.[0];
647
-
648
- const run = async (assistantId, threadId, options) => {
649
- const { clientBeta } = await getOpenAIClient(options);
650
- let content = '';
651
- const stream = async (delta, snapshot) => {
652
- content += delta.value;
653
- await ignoreErrFunc(async () => await options?.stream?.(
654
- options?.raw ? delta : await packResp(content), snapshot
655
- ), LOG);
656
- };
657
- let resp = clientBeta.threads.runs[options?.runOnly ? 'create' : 'stream'](
658
- threadId, { assistant_id: assistantId }
659
- );
660
- if (!options?.runOnly) {
661
- resp = await new Promise((resolve, reject) => {
662
- // https://github.com/openai/openai-node/blob/master/helpers.md#assistant-events
663
- resp.on('textCreated', stream)
664
- .on('textDelta', stream)
665
- .on('textDone', async (delta, snapshot) => resolve(
666
- options?.raw ? delta : await packResp(delta.value), snapshot
667
- ))
668
- .on('error', reject)
669
- });
670
- }
671
- return await resp;
672
- };
673
-
674
- const getRun = async (threadId, runId, options) => {
675
- const { clientBeta } = await getOpenAIClient(options);
676
- return await clientBeta.threads.runs.retrieve(threadId, runId);
677
- };
678
-
679
- const promptAssistant = async (content, options) => {
680
- const assistant = await ensureAssistant(options);
681
- const thread = await ensureThread(options);
682
- const messageSent = await createMessage(thread.id, content, options);
683
- const runResp = await run(assistant.id, thread.id, options);
684
- const threadDeleted = options?.deleteThread
685
- ? await deleteThread(thread.id) : null;
686
- return {
687
- assistant, thread, messageSent, run: options?.runOnly ? runResp : null,
688
- threadDeleted, response: options?.runOnly ? null : runResp,
689
- };
690
- };
691
-
692
584
  const uploadFile = async (input, options) => {
693
585
  const { client } = await getOpenAIClient(options);
694
586
  const { content: file, cleanup } = await convert(input, {
@@ -701,44 +593,10 @@ const uploadFile = async (input, options) => {
701
593
  return resp;
702
594
  };
703
595
 
704
- const uploadFileForAssistants = async (content, options) => await uploadFile(
705
- content, { ...options, params: { purpose: 'assistants' } }
706
- );
707
-
708
596
  const uploadFileForFineTuning = async (content, options) => await uploadFile(
709
597
  content, { suffix: 'jsonl', ...options, params: { purpose: 'fine-tune' } }
710
598
  );
711
599
 
712
- const attachFileToAssistant = async (assistantId, file_id, options) => {
713
- const { clientBeta } = await getOpenAIClient(options);
714
- return await clientBeta.assistants.files.create(assistantId, { file_id });
715
- };
716
-
717
- const detachFileFromAssistant = async (assistantId, file_id, options) => {
718
- const { clientBeta } = await getOpenAIClient(options);
719
- return await clientBeta.assistants.files.del(assistantId, file_id);
720
- };
721
-
722
- const deleteFileFromAssistant = async (assistantId, file_id, options) => {
723
- const detach = await detachFileFromAssistant(assistantId, file_id, options);
724
- const respDel = await deleteFile(file_id, options);
725
- return { detach, delete: respDel };
726
- };
727
-
728
- const deleteAllFilesFromAssistant = async (assistantId, options) => {
729
- const files = await listAssistantFiles(assistantId, options);
730
- const delPms = [];
731
- for (const file of files) {
732
- delPms.push(deleteFileFromAssistant(assistantId, file.id, options));
733
- }
734
- return await Promise.all(delPms);
735
- };
736
-
737
- const uploadFileForRetrieval = async (assistantId, content, options) => {
738
- const file = await uploadFileForAssistants(content, options);
739
- return await attachFileToAssistant(assistantId, file.id, options);
740
- };
741
-
742
600
  const listFiles = async (options) => {
743
601
  const { client } = await getOpenAIClient(options);
744
602
  const files = [];
@@ -752,14 +610,6 @@ const deleteFile = async (file_id, options) => {
752
610
  return await client.files.del(file_id);
753
611
  };
754
612
 
755
- const listAssistantFiles = async (assistant_id, options) => {
756
- const { clientBeta } = await getOpenAIClient(options);
757
- const resp = await clientBeta.assistants.files.list(
758
- assistant_id, { limit: GPT_QUERY_LIMIT, ...options?.params }
759
- );
760
- return options?.raw ? resp : resp.data;
761
- };
762
-
763
613
  const generationConfig = options => ({
764
614
  generationConfig: {
765
615
  ...options?.generationConfig || {},
@@ -787,10 +637,14 @@ const handleGeminiResponse = async (resp, options) => {
787
637
  };
788
638
 
789
639
  const promptGemini = async (content, options) => {
790
- const { generative } = await getGeminiClient(options);
640
+ const { generative, genModel } = await getGeminiClient(options);
791
641
  // https://github.com/google/generative-ai-js/blob/main/samples/node/advanced-chat.js
792
642
  // @todo: check this issue similar to Vertex AI:
793
643
  // Google's bug: history is not allowed while using inline_data?
644
+ console.log(genModel);
645
+ assert(!(
646
+ options?.jsonMode && MODELS[genModel]?.json == false
647
+ ), `This model does not support JSON output: ${genModel}`);
794
648
  const chat = generative.startChat({
795
649
  history: options?.messages && !options?.attachments?.length
796
650
  ? options.messages : [],
@@ -922,29 +776,13 @@ const initChat = async (options) => {
922
776
  chatConfig.engines[key] = options.engines[i];
923
777
  chatConfig.engines[key].model = chatConfig.engines[key].model || model;
924
778
  const mxPmpt = MODELS[chatConfig.engines[key].model].maxInputTokens / 2;
925
- let pmptTokens = 0;
926
- switch (key) {
927
- case ASSISTANT:
928
- chatConfig.engines[key].assistantId
929
- = chatConfig.engines[key].assistantId
930
- || (await ensureAssistant({
931
- ...options, params: {
932
- name, model: chatConfig.engines[key].model,
933
- instructions: chatConfig.systemPrompt,
934
- ...chatConfig.engines[key].params,
935
- }
936
- })).id;
937
- break;
938
- case CHATGPT: case GEMINI: case OLLAMA: case CLAUDE:
939
- pmptTokens = await countTokens([buildGeminiHistory(
940
- chatConfig.systemPrompt, { role: system }
941
- )]); // Use Gemini instead of ChatGPT because of the longer pack
942
- assert(
943
- pmptTokens < mxPmpt,
944
- `System prompt is too long: ${pmptTokens} / ${mxPmpt} tokens.`
945
- );
946
- break;
947
- }
779
+ const pmptTokens = await countTokens([buildGeminiHistory(
780
+ chatConfig.systemPrompt, { role: system }
781
+ )]); // Use Gemini instead of ChatGPT because of the longer pack
782
+ assert(
783
+ pmptTokens < mxPmpt,
784
+ `System prompt is too long: ${pmptTokens} / ${mxPmpt} tokens.`
785
+ );
948
786
  }
949
787
  return chatConfig;
950
788
  };
@@ -1012,8 +850,6 @@ const talk = async (input, options) => {
1012
850
  };
1013
851
  msgBuilder()
1014
852
  break;
1015
- case ASSISTANT:
1016
- break;
1017
853
  case GEMINI:
1018
854
  sys.push(buildGeminiHistory(session.systemPrompt, { role: user }));
1019
855
  msgBuilder = () => {
@@ -1061,13 +897,6 @@ const talk = async (input, options) => {
1061
897
  messages, model, ...options, attachments,
1062
898
  });
1063
899
  break;
1064
- case ASSISTANT:
1065
- resp = await promptAssistant(input, {
1066
- assistantId: chatConfig.engines[engine].assistantId,
1067
- threadId: session.threadId, ...options, attachments,
1068
- });
1069
- session.threadId = resp.thread.id;
1070
- break;
1071
900
  case GEMINI:
1072
901
  resp = await promptGemini(input, {
1073
902
  messages, ...options, attachments,
@@ -1233,50 +1062,29 @@ export {
1233
1062
  buildGptTrainingCases,
1234
1063
  cancelGptFineTuningJob,
1235
1064
  countTokens,
1236
- createAssistant,
1237
1065
  createGeminiEmbedding,
1238
1066
  createGptFineTuningJob,
1239
- createMessage,
1240
1067
  createOpenAIEmbedding,
1241
- deleteAllFilesFromAssistant,
1242
- deleteAssistant,
1243
1068
  deleteFile,
1244
- deleteFileFromAssistant,
1245
- deleteThread,
1246
- detachFileFromAssistant,
1247
1069
  distillFile,
1248
- ensureAssistant,
1249
- ensureThread,
1250
- getAssistant,
1251
1070
  getGptFineTuningJob,
1252
- getLatestMessage,
1253
1071
  getMaxChatPromptLimit,
1254
- getRun,
1255
1072
  getSession,
1256
- getThread,
1257
1073
  init,
1258
1074
  initChat,
1259
- listAssistant,
1260
- listAssistantFiles,
1261
1075
  listFiles,
1262
1076
  listGptFineTuningEvents,
1263
1077
  listGptFineTuningJobs,
1264
- listMessages,
1265
1078
  listOpenAIModels,
1266
- modifyAssistant,
1267
1079
  prompt,
1268
- promptAssistant,
1269
1080
  promptChatGPT,
1270
1081
  promptClaude,
1271
1082
  promptGemini,
1272
1083
  promptOllama,
1273
1084
  resetSession,
1274
- run,
1275
1085
  tailGptFineTuningEvents,
1276
1086
  talk,
1277
1087
  trimPrompt,
1278
1088
  uploadFile,
1279
- uploadFileForAssistants,
1280
1089
  uploadFileForFineTuning,
1281
- uploadFileForRetrieval,
1282
1090
  };
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": "1997.1.21",
4
+ "version": "1997.1.23",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
@@ -46,10 +46,10 @@ const manifest = {
46
46
  "js-tiktoken": "^1.0.16",
47
47
  "jsdom": "^25.0.1",
48
48
  "lorem-ipsum": "^2.0.8",
49
- "mailgun.js": "^10.2.4",
49
+ "mailgun.js": "^10.3.0",
50
50
  "mailparser": "^3.7.2",
51
51
  "mime": "^4.0.6",
52
- "mysql2": "^3.11.5",
52
+ "mysql2": "^3.12.0",
53
53
  "node-mailjet": "^6.0.6",
54
54
  "node-polyfill-webpack-plugin": "^4.1.0",
55
55
  "office-text-extractor": "^3.0.3",
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": "1997.1.21",
4
+ "version": "1997.1.23",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
@@ -57,10 +57,10 @@
57
57
  "js-tiktoken": "^1.0.16",
58
58
  "jsdom": "^25.0.1",
59
59
  "lorem-ipsum": "^2.0.8",
60
- "mailgun.js": "^10.2.4",
60
+ "mailgun.js": "^10.3.0",
61
61
  "mailparser": "^3.7.2",
62
62
  "mime": "^4.0.6",
63
- "mysql2": "^3.11.5",
63
+ "mysql2": "^3.12.0",
64
64
  "node-mailjet": "^6.0.6",
65
65
  "node-polyfill-webpack-plugin": "^4.1.0",
66
66
  "office-text-extractor": "^3.0.3",