utilitas 2000.3.31 → 2000.3.33

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
@@ -1,20 +1,19 @@
1
1
  import { checkSearch, distill, search } from './web.mjs';
2
2
  import { create as createUoid } from './uoid.mjs';
3
- import { fileTypeFromBuffer } from 'file-type';
4
3
  import { packPcmToWav } from './media.mjs';
5
4
  import { v4 as uuidv4 } from 'uuid';
6
5
 
7
6
  import {
8
- STREAM, FILE, BASE64, BUFFER, DATAURL, MIME_BINARY, MIME_TEXT, MIME_PNG,
9
- MIME_JPEG, MIME_MOV, MIME_MPEG, MIME_MP4, MIME_MPG, MIME_AVI, MIME_WMV,
10
- MIME_MPEGPS, MIME_FLV, MIME_GIF, MIME_WEBP, MIME_PDF, MIME_AAC, MIME_FLAC,
11
- MIME_MP3, MIME_MPEGA, MIME_M4A, MIME_MPGA, MIME_OPUS, MIME_PCM, MIME_WAV,
12
- MIME_WEBM, MIME_TGPP, MIME_PCM16, MIME_OGG, convert, formatDataURL,
13
- getTempPath, decodeBase64DataURL,
7
+ STREAM, FILE, BASE64, BUFFER, DATAURL, MIME_TEXT, MIME_PNG, MIME_JPEG,
8
+ MIME_MOV, MIME_MPEG, MIME_MP4, MIME_MPG, MIME_AVI, MIME_WMV, MIME_MPEGPS,
9
+ MIME_FLV, MIME_GIF, MIME_WEBP, MIME_PDF, MIME_AAC, MIME_FLAC, MIME_MP3,
10
+ MIME_MPEGA, MIME_M4A, MIME_MPGA, MIME_OPUS, MIME_PCM, MIME_WAV, MIME_WEBM,
11
+ MIME_TGPP, MIME_PCM16, MIME_OGG, convert, getTempPath, decodeBase64DataURL,
12
+ getMime,
14
13
  } from './storage.mjs';
15
14
 
16
15
  import {
17
- log as _log, renderText as _renderText, ensureArray, ensureString, extract,
16
+ log as _log, renderText as _renderText, ensureArray, ensureString,
18
17
  ignoreErrFunc, insensitiveCompare, isSet, need, parseJson, throwError,
19
18
  tryUntil, timeout, mergeAtoB,
20
19
  } from './utilitas.mjs';
@@ -225,8 +224,6 @@ let ATTACHMENT_TOKEN_COST = 0;
225
224
  for (const n in MODELS) {
226
225
  MODELS[n]['name'] = n;
227
226
  MODELS[n].supportedMimeTypes = MODELS[n].supportedMimeTypes || [];
228
- MODELS[n].supportedDocTypes = MODELS[n].supportedDocTypes || [];
229
- MODELS[n].supportedAudioTypes = MODELS[n].supportedAudioTypes || [];
230
227
  MODELS[n].maxOutputTokens = MODELS[n].maxOutputTokens
231
228
  || Math.ceil(MODELS[n].contextWindow * 0.4);
232
229
  MODELS[n].maxInputTokens = MODELS[n].maxInputTokens
@@ -258,9 +255,7 @@ for (const n in MODELS) {
258
255
  // );
259
256
  // }
260
257
  // // combine supported types
261
- // for (const key of [
262
- // 'supportedAudioTypes', 'supportedDocTypes', 'supportedMimeTypes',
263
- // ]) {
258
+ // for (const key of ['supportedMimeTypes']) {
264
259
  // MODELS[AUTO][key] = [...new Set(
265
260
  // [...MODELS[AUTO][key] || [], ...MODELS[n][key] || []]
266
261
  // )];
@@ -588,55 +583,51 @@ const isOpenrouter = (provider, model) => insensitiveCompare(
588
583
  provider, OPENROUTER
589
584
  ) && (model ? model?.source : true);
590
585
 
591
- const selectVisionModel = options => {
592
- assert(
593
- MODELS[options.model]?.vision,
594
- `Vision modality is not supported by model: ${options.model}`
595
- );
596
- return String.isString(MODELS[options.model].vision)
597
- ? MODELS[options.model].vision : null;
598
- };
599
-
600
- const selectAudioModel = options => {
601
- assert(
602
- MODELS[options.model]?.audio,
603
- `Audio modality is not supported by model: ${options.model}`
604
- );
605
- return String.isString(MODELS[options.model]?.audio)
606
- ? MODELS[options.model]?.audio : null;
607
- };
608
-
609
- const buildMessage = (content, options) => {
586
+ const buildMessage = async (content, options) => {
610
587
  content = content || '';
611
- let alterModel = options?.audioMode && selectAudioModel(options);
612
- const attachments = (options?.attachments || []).map(x => {
613
- assert(MODELS[options?.model], 'Model is required.');
614
- if (MODELS[options.model]?.supportedMimeTypes?.includes?.(x.mime_type)) {
615
- alterModel = selectVisionModel(options); // URL or Base64URL
588
+ const attachments = await Promise.all((options?.attachments || []).map(async x => {
589
+ assert(
590
+ MODELS[options?.model]?.supportedMimeTypes?.includes?.(x.mime_type),
591
+ `Unsupported mime type: '${x.mime_type}'.`
592
+ );
593
+ if ([
594
+ MIME_PNG, MIME_JPEG, MIME_GIF, MIME_WEBP
595
+ ].includes?.(x.mime_type)) {
616
596
  return {
617
597
  type: 'image_url',
618
- image_url: { url: x.url, detail: 'high' },
619
- };
620
- } else if (MODELS[options.model]?.supportedDocTypes?.includes?.(x.mime_type)) {
621
- alterModel = selectVisionModel(options);
622
- return {
623
- type: 'file',
624
- file: {
625
- file_data: formatDataURL(x.mime_type, x.data),
626
- filename: x.file_name
627
- || `${uuidv4()}.${x.mime_type.split('/')[1]}`,
598
+ image_url: {
599
+ url: x.url || await convert(x.data, {
600
+ input: BUFFER, expected: DATAURL,
601
+ }), detail: 'high',
628
602
  },
629
603
  };
630
- } else if (MODELS[options.model]?.supportedAudioTypes?.includes?.(x.mime_type)) {
631
- alterModel = selectAudioModel(options);
604
+ } else if ([
605
+ MIME_AAC, MIME_FLAC, MIME_MP3, MIME_MPEGA, MIME_M4A, MIME_MPGA,
606
+ MIME_OPUS, MIME_PCM, MIME_WAV, MIME_TGPP, MIME_PCM16, MIME_OGG,
607
+ ].includes?.(x.mime_type)) {
632
608
  return {
633
609
  type: 'input_audio',
634
- input_audio: { data: x.data, format: WAV },
610
+ input_audio: {
611
+ data: x.url || await convert(x.data, {
612
+ input: BUFFER, expected: BASE64,
613
+ }), format: WAV,
614
+ },
635
615
  };
636
616
  }
637
- throwError(`Unsupported mime type: '${x.mime_type}'.`);
638
- });
639
- alterModel && (options.model = alterModel);
617
+ [
618
+ MIME_TEXT, MIME_MOV, MIME_MPEG, MIME_WEBM, MIME_MP4, MIME_MPG,
619
+ MIME_AVI, MIME_WMV, MIME_MPEGPS, MIME_FLV, MIME_PDF,
620
+ ].includes?.(x.mime_type)
621
+ || log(`Unknown mime type: '${x.mime_type}', fallbacked.`);
622
+ return {
623
+ type: 'file', file: {
624
+ file_data: await convert(x.data, {
625
+ input: BUFFER, expected: DATAURL,
626
+ }), filename: x.file_name
627
+ || `${uuidv4()}.${x.mime_type.split('/')[1]}`,
628
+ },
629
+ };
630
+ }));
640
631
  const message = String.isString(content) ? {
641
632
  role: options?.role || user,
642
633
  content: content.length ? [{ type: TEXT, text: content }] : [],
@@ -786,10 +777,10 @@ const packModelLabel = (model_reference) => {
786
777
  const buildPrompts = async (model, input, options = {}) => {
787
778
  assert(!(
788
779
  options.jsonMode && !model?.json
789
- ), `This model does not support JSON output: ${options.model}`);
780
+ ), `This model does not support JSON output: ${model.name}`);
790
781
  assert(!(
791
782
  options.reasoning && !model?.reasoning
792
- ), `This model does not support reasoning: ${options.model}`);
783
+ ), `This model does not support reasoning: ${model.name}`);
793
784
  let [history, content, prompt, _model, _assistant, _history]
794
785
  = [null, input, null, { role: MODEL }, { role: assistant }, null];
795
786
  options.systemPrompt = options.systemPrompt || INSTRUCTIONS;
@@ -797,42 +788,31 @@ const buildPrompts = async (model, input, options = {}) => {
797
788
  options.attachments?.length ? options.attachments : []
798
789
  ).map(async x => {
799
790
  if (String.isString(x)) {
800
- var convResp = await convert(x, { input: FILE, expected: DATAURL, meta: true });
801
- return {
802
- url: convResp.content,
803
- mime_type: convResp.mime,
804
- }
791
+ var convResp = await convert(x, { input: FILE, expected: BUFFER, meta: true });
792
+ return { data: convResp.content, mime_type: convResp.mime };
805
793
  } else if (Buffer.isBuffer(x)) {
806
- var convResp = await convert(x, { input: BUFFER, expected: DATAURL, meta: true });
807
- return {
808
- url: convResp.content,
809
- mime_type: convResp.mime,
810
- }
794
+ return { data: x, mime_type: (await getMime(x))?.mime }
811
795
  } else if (Object.isObject(x)) { return x; } else { return null; }
812
- }))).filter(x => x && [
813
- ...model?.supportedMimeTypes,
814
- ...model?.supportedDocTypes,
815
- ...model?.supportedAudioTypes,
816
- ].includes(x.mime_type));
817
- const systemPrompt = buildMessage(options.systemPrompt, system);
818
- const msgBuilder = () => {
796
+ }))).filter(x => (model?.supportedMimeTypes || []).includes(x.mime_type));
797
+ const systemPrompt = await buildMessage(options.systemPrompt, system);
798
+ const msgBuilder = async () => {
819
799
  [history, _history] = [[], []];
820
- (options.messages?.length ? options.messages : []).map((x, i) => {
821
- _history.push(buildMessage(x.request));
822
- _history.push(buildMessage(x.response, _assistant));
823
- });
800
+ await Promise.all((options.messages?.length ? options.messages : []).map(async (x, i) => {
801
+ _history.push(await buildMessage(x.request));
802
+ _history.push(await buildMessage(x.response, _assistant));
803
+ }));
824
804
  history = messages([
825
- systemPrompt, ..._history, buildMessage(content, options),
805
+ systemPrompt, ..._history, await buildMessage(content, options),
826
806
  ...options.toolsResult?.length ? options.toolsResult : []
827
807
  ]);
828
808
  };
829
- msgBuilder();
809
+ await msgBuilder();
830
810
  await trimPrompt(() => [
831
811
  systemPrompt, _history, content, options.toolsResult
832
- ], () => {
812
+ ], async () => {
833
813
  if (options.messages?.length) {
834
814
  options.messages?.shift();
835
- msgBuilder();
815
+ await msgBuilder();
836
816
  } else if (options.trimBeginning) {
837
817
  content = '...' + trimBeginning(trimBeginning(content).slice(1));
838
818
  } else {
@@ -1251,7 +1231,7 @@ const initChat = async (options = {}) => {
1251
1231
  }
1252
1232
  options.instructions && (chatConfig.systemPrompt = options.instructions);
1253
1233
  // Use Gemini instead of ChatGPT because of the longer package.
1254
- const [spTokens, ais] = await Promise.all([countTokens([buildMessage(
1234
+ const [spTokens, ais] = await Promise.all([countTokens([await buildMessage(
1255
1235
  chatConfig.systemPrompt, system
1256
1236
  )]), getAi(null, { all: true })]);
1257
1237
  for (const ai of ais.filter(x => ![
@@ -1348,15 +1328,9 @@ const distillFile = async (attachments, o) => {
1348
1328
  o?.keepPaging ? '' : '- If the document has multiple pages, merge them into one page. Please do not return any paging information.',
1349
1329
  o?.keepDecoration ? '' : '- If the document has side notes, headers, footers, or watermarks, please ignore them.',
1350
1330
  ].filter(x => x).join('\n');
1351
- attachments = await Promise.all(ensureArray(attachments).map(async x => {
1352
- const convResp = await convert(
1353
- x, { expected: DATAURL, ...o || {}, meta: true }
1354
- );
1355
- return { url: convResp.content, mime_type: convResp.mime };
1356
- }));
1357
1331
  return await prompt(strPmt, {
1358
1332
  select: { vision: true, hearing: true, fast: true },
1359
- simple: true, ...o, attachments,
1333
+ simple: true, ...o, attachments: ensureArray(attachments),
1360
1334
  });
1361
1335
  };
1362
1336
 
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": "2000.3.31",
4
+ "version": "2000.3.33",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
package/lib/storage.mjs CHANGED
@@ -538,6 +538,7 @@ export {
538
538
  getConfigFilename,
539
539
  getGcUrlByBucket,
540
540
  getIdByGs,
541
+ getMime,
541
542
  getTempPath,
542
543
  handleError,
543
544
  init,
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": "2000.3.31",
4
+ "version": "2000.3.33",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",