utilitas 1995.3.21 → 1995.3.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
@@ -53,7 +53,7 @@ const trimTailing = text => text.replace(/[\.\s]*$/, '');
53
53
  const newSessionId = () => createUoid({ type: sessionType });
54
54
  const renderText = (t, o) => _renderText(t, { extraCodeBlock: 0, ...o || {} });
55
55
  const log = (cnt, opt) => _log(cnt, import.meta.url, { time: 1, ...opt || {} });
56
- const buildGeminiParts = (text, atcmt) => [{ text }, ...atcmt ? [atcmt] : []];
56
+ const buildGeminiParts = (text, atcmts) => [{ text }, ...atcmts || []];
57
57
 
58
58
  const [
59
59
  png, jpeg, mov, mpeg, mp4, mpg, avi, wmv, mpegps, flv, gif, webp, pdf, aac,
@@ -108,6 +108,7 @@ const MODELS = {
108
108
  },
109
109
  [GPT_4O]: {
110
110
  contextWindow: 128000,
111
+ imageCostTokens: 1105,
111
112
  maxOutputTokens: 4096,
112
113
  requestLimitsRPM: 5000,
113
114
  tokenLimitsTPD: 40000000,
@@ -127,7 +128,7 @@ const MODELS = {
127
128
  // https://ai.google.dev/gemini-api/docs/models/gemini
128
129
  // https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/send-multimodal-prompts?hl=en#gemini-send-multimodal-samples-pdf-nodejs
129
130
  contextWindow: 1048576,
130
- imageUseTokens: 258,
131
+ imageCostTokens: 258,
131
132
  maxAudioLength: 60 * 60 * 9.5, // 9.5 hours
132
133
  maxAudioPerPrompt: 1,
133
134
  maxFileSize: 20 * 1024 * 1024, // 20 MB
@@ -201,6 +202,9 @@ for (const n in MODELS) {
201
202
  }
202
203
  }
203
204
 
205
+ const MAX_INPUT_TOKENS = MODELS[GPT_35_TURBO].maxInputTokens;
206
+ const ATTACHMENT_TOKEN_COST = MODELS[GPT_4O].imageCostTokens;
207
+
204
208
  const unifyType = (type, name) => {
205
209
  const TYPE = ensureString(type, { case: 'UP' });
206
210
  assert(TYPE, `${name} is required.`);
@@ -313,23 +317,20 @@ const buildGptMessage = (content, options) => {
313
317
 
314
318
  const buildVertexMessage = (text, options) => {
315
319
  assert(text, 'Text is required.');
316
- // only 1 attachment is allowed while using inline_data:
317
- const attachment = (options?.attachments || []).map(x => ({
320
+ const attachments = (options?.attachments || []).map(x => ({
318
321
  inline_data: { mime_type: x.mime_type, data: x.data }
319
- }))?.[0];
322
+ }));
320
323
  return String.isString(text) ? {
321
- role: options?.role || user, parts: buildGeminiParts(text, attachment),
324
+ role: options?.role || user, parts: buildGeminiParts(text, attachments),
322
325
  } : text;
323
326
  };
324
327
 
325
328
  const buildGeminiMessage = (text, options) => {
326
329
  assert(text, 'Text is required.');
327
- // @todo: check this issue similar to Vertex AI:
328
- // only 1 attachment is allowed while using inline_data?
329
- const attachment = (options?.attachments || []).map(x => ({
330
+ const attachments = (options?.attachments || []).map(x => ({
330
331
  inlineData: { mimeType: x.mime_type, data: x.data }
331
332
  }))?.[0];
332
- return String.isString(text) ? buildGeminiParts(text, attachment) : text;
333
+ return String.isString(text) ? buildGeminiParts(text, attachments) : text;
333
334
  };
334
335
 
335
336
  const [getOpenAIClient, getVertexClient, getGeminiClient, getOllamaClient]
@@ -947,12 +948,13 @@ const getMaxChatPromptLimit = (options) => {
947
948
  resp = resp ? Math.min(resp, maxInputTokens) : maxInputTokens;
948
949
  }
949
950
  assert(resp > 0, 'Chat engine has not been initialized.');
950
- return resp;
951
+ return options?.raw ? resp : Math.min(resp, MAX_INPUT_TOKENS);
951
952
  };
952
953
 
953
954
  export default init;
954
955
  export {
955
956
  _NEED,
957
+ ATTACHMENT_TOKEN_COST,
956
958
  CODE_INTERPRETER,
957
959
  DEFAULT_MODELS,
958
960
  EMBEDDING_001,
@@ -963,6 +965,8 @@ export {
963
965
  GEMINI_15_FLASH_VERTEX,
964
966
  GEMINI_15_FLASH,
965
967
  GPT_35_TURBO,
968
+ GPT_4O,
969
+ MAX_INPUT_TOKENS,
966
970
  MISTRAL,
967
971
  MODELS,
968
972
  RETRIEVAL,
package/lib/bot.mjs CHANGED
@@ -612,67 +612,59 @@ const subconscious = [{
612
612
  },
613
613
  }, {
614
614
  run: true, priority: -8860, name: 'vision', func: async (ctx, next) => {
615
- let fileId, type, file_name, mime_type, ocrFunc, asPrompt = false;
616
- if ('application/pdf' === ctx.m.document?.mime_type) {
617
- ocrFunc = ctx._.vision?.read;
618
- fileId = ctx.m.document.file_id;
619
- file_name = ctx.m.document.file_name;
620
- mime_type = ctx.m.document.mime_type;
621
- type = 'DOCUMENT';
622
- } else if (/^image\/.*$/ig.test(ctx.m.document?.mime_type)) {
623
- asPrompt = bot._.supportedMimeTypes.has(ctx.m.document.mime_type);
624
- ocrFunc = ctx._.vision?.see;
625
- fileId = ctx.m.document.file_id;
626
- file_name = ctx.m.document.file_name;
627
- mime_type = ctx.m.document.mime_type;
628
- type = 'IMAGE';
629
- } else if (/^.*\.(docx|xlsx|pptx)$/.test(ctx.m.document?.file_name)) {
630
- ocrFunc = officeParser;
631
- fileId = ctx.m.document.file_id;
632
- file_name = ctx.m.document.file_name;
633
- mime_type = ctx.m.document.mime_type;
634
- type = 'DOCUMENT';
635
- } else if (ctx.m.document) {
636
- ocrFunc = async file => (await isTextFile(file)) && file.toString();
637
- fileId = ctx.m.document.file_id;
638
- file_name = ctx.m.document.file_name;
639
- mime_type = ctx.m.document.mime_type;
640
- type = 'FILE';
641
- } else if (ctx.m.photo) {
642
- asPrompt = true;
643
- ocrFunc = ctx._.vision?.see;
644
- fileId = ctx.m.photo[ctx.m.photo.length - 1]?.file_id;
645
- mime_type = 'image';
646
- type = 'PHOTO';
615
+ const files = [];
616
+ if (ctx.m.document) {
617
+ let file = {
618
+ asPrompt: bot._.supportedMimeTypes.has(ctx.m.document.mime_type),
619
+ file_name: ctx.m.document.file_name, fileId: ctx.m.document.file_id,
620
+ mime_type: ctx.m.document.mime_type, type: 'FILE',
621
+ ocrFunc: async f => (await isTextFile(f)) && f.toString(),
622
+ };
623
+ if ('application/pdf' === ctx.m.document?.mime_type) {
624
+ file = { ...file, ocrFunc: ctx._.vision?.read, type: 'DOCUMENT' };
625
+ } else if (/^image\/.*$/ig.test(ctx.m.document?.mime_type)) {
626
+ file = { ...file, ocrFunc: ctx._.vision?.see, type: 'IMAGE' };
627
+ } else if (/^.*\.(docx|xlsx|pptx)$/.test(ctx.m.document?.file_name)) {
628
+ file = { ...file, ocrFunc: officeParser, type: 'DOCUMENT' };
629
+ }
630
+ files.push(file);
647
631
  }
648
- if (fileId && (asPrompt || ocrFunc)) {
632
+ ctx.m.photo?.map(p => files.push({
633
+ asPrompt: true, file_name: `${p.file_id}.jpg`, fileId: p.file_id,
634
+ mime_type: 'image/jpeg', type: 'PHOTO', ocrFunc: ctx._.vision?.see,
635
+ }));
636
+ if (files.length) {
649
637
  await ctx.ok(EMOJI_LOOK);
650
- try {
651
- const image_url = await getFileUrl(fileId);
652
- const file = (await get(image_url, BUFFER_ENCODE)).content;
653
- if (asPrompt) {
654
- ctx.collect(ctx.m.caption || '');
655
- ctx.collect({
656
- mime_type: mime_type === 'image' ? 'image/jpeg' : mime_type,
657
- image_url, data: base64Encode(file, true),
658
- }, 'PROMPT');
659
- }
660
- if (ocrFunc) {
661
- const content = trim(ensureArray(
662
- await ignoreErrFunc(async () => await ocrFunc(
663
- file, BUFFER_ENCODE
664
- ), logOptions)
665
- ).filter(x => x).join('\n'));
666
- if (content) {
638
+ for (const f of files) {
639
+ if (!f.asPrompt && !f.ocrFunc) { continue; }
640
+ try {
641
+ const image_url = await getFileUrl(f.fileId);
642
+ const file = (await get(image_url, BUFFER_ENCODE)).content;
643
+ if (f.asPrompt) {
667
644
  ctx.collect(ctx.m.caption || '');
668
- ctx.collect(lines([
669
- '---', ...file_name ? [`file_name: ${file_name}`] : [],
670
- `mime_type: ${mime_type}`, `type: ${type}`, '---',
671
- content
672
- ]), 'VISION');
645
+ ctx.collect({
646
+ mime_type: f.mime_type, image_url,
647
+ data: base64Encode(file, true),
648
+ }, 'PROMPT');
673
649
  }
674
- }
675
- } catch (err) { return await ctx.er(err); }
650
+ if (f.ocrFunc) {
651
+ const content = trim(ensureArray(
652
+ await ignoreErrFunc(async () => await f.ocrFunc(
653
+ file, BUFFER_ENCODE
654
+ ), logOptions)
655
+ ).filter(x => x).join('\n'));
656
+ if (content) {
657
+ ctx.collect(ctx.m.caption || '');
658
+ ctx.collect(lines([
659
+ '---', `file_name: ${f.file_name}`,
660
+ `mime_type: ${f.mime_type}`, `type: ${f.type}`,
661
+ '---',
662
+ content
663
+ ]), 'VISION');
664
+ }
665
+ }
666
+ } catch (err) { return await ctx.er(err); }
667
+ }
676
668
  }
677
669
  await next();
678
670
  },
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": "1995.3.21",
4
+ "version": "1995.3.23",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
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": "1995.3.21",
4
+ "version": "1995.3.23",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",