halbot 1993.2.90 → 1993.2.92

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/index.mjs CHANGED
@@ -6,8 +6,10 @@ const skillPath = utilitas.__(import.meta.url, 'skills');
6
6
 
7
7
  const init = async (options = {}) => {
8
8
  assert(options.telegramToken, 'Telegram Bot API Token is required.');
9
- const [pkg, _speech, speechOptions, vision]
10
- = [await utilitas.which(), {}, { tts: true, stt: true }, {}];
9
+ const [pkg, _speech, speechOptions, vision] = [
10
+ await utilitas.which(), options?.speech || {}, { tts: true, stt: true },
11
+ {},
12
+ ];
11
13
  const info = bot.lines([
12
14
  `[${hal.EMOJI_BOT} ${pkg.title}](${pkg.homepage})`, pkg.description
13
15
  ]);
@@ -16,7 +18,7 @@ const init = async (options = {}) => {
16
18
  if (options.openaiApiKey || options.googleApiKey) {
17
19
  vision.read = alan.distillFile;
18
20
  vision.see = alan.distillFile;
19
- _speech.stt = alan.distillFile;
21
+ _speech?.stt || (_speech.stt = alan.distillFile);
20
22
  }
21
23
  // use openai embedding, dall-e, tts if openai is enabled
22
24
  if (options.openaiApiKey) {
@@ -26,8 +28,10 @@ const init = async (options = {}) => {
26
28
  priority: options.openaiPriority, ...options
27
29
  });
28
30
  await gen.init(apiKey);
29
- await speech.init({ ...apiKey, ...speechOptions });
30
- _speech.tts = speech.tts;
31
+ if (!_speech.tts) {
32
+ await speech.init({ ...apiKey, ...speechOptions });
33
+ _speech.tts = speech.tts;
34
+ }
31
35
  }
32
36
  // use gemini embedding if gemini is enabled and chatgpt is not enabled
33
37
  // use google tts if google api key is ready
package/lib/hal.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import {
2
- alan, bot, callosum, dbio, storage, uoid, utilitas, web,
2
+ bot, callosum, dbio, media, speech, storage, uoid, utilitas, web,
3
3
  } from 'utilitas';
4
4
 
5
5
  import { basename, join } from 'path';
@@ -612,7 +612,11 @@ const subconscious = [{
612
612
  const analyze = async () => {
613
613
  const resp = await utilitas.ignoreErrFunc(async () => {
614
614
  [
615
- alan.mp3, alan.mpega, alan.mp4, alan.mpeg, alan.mpga, alan.m4a, alan.wav, alan.webm, alan.ogg
615
+ storage.MIME_MP3, storage.MIME_MPEGA,
616
+ storage.MIME_MP4, storage.MIME_MPEG,
617
+ storage.MIME_MPGA, storage.MIME_M4A,
618
+ storage.MIME_WAV, storage.MIME_WEBM,
619
+ storage.MIME_OGG,
616
620
  ].includes(audio.mime_type) || (
617
621
  file = await media.convertAudioTo16kNanoPcmWave(
618
622
  file, { input: storage.BUFFER, expected: storage.BUFFER }
@@ -623,9 +627,9 @@ const subconscious = [{
623
627
  log(`STT: '${resp}'`);
624
628
  ctx.collect(resp);
625
629
  };
626
- if (hal._.supportedMimeTypes.has(alan.wav)) {
630
+ if (hal._.supportedMimeTypes.has(storage.MIME_WAV)) {
627
631
  ctx.collect({
628
- mime_type: alan.wav, url, analyze,
632
+ mime_type: storage.MIME_WAV, url, analyze,
629
633
  data: await media.convertAudioTo16kNanoPcmWave(file, {
630
634
  input: storage.BUFFER, expected: storage.BASE64,
631
635
  }),
@@ -736,9 +740,10 @@ const subconscious = [{
736
740
  if (m.photo?.[m.photo?.length - 1]) {
737
741
  const p = m.photo[m.photo.length - 1];
738
742
  files.push({
739
- asPrompt: hal._.supportedMimeTypes.has(alan.jpeg),
743
+ asPrompt: hal._.supportedMimeTypes.has(storage.MIME_JPEG),
740
744
  file_name: `${p.file_id}.jpg`, fileId: p.file_id,
741
- mime_type: alan.jpeg, type: 'PHOTO', ocrFunc: ctx._.vision?.see,
745
+ mime_type: storage.MIME_JPEG, type: 'PHOTO',
746
+ ocrFunc: ctx._.vision?.see,
742
747
  });
743
748
  }
744
749
  if (m.video_note) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "halbot",
3
3
  "description": "Just another `ChatGPT` / `Gemini` / `Claude` / `Azure` / `Jina` / `Ollama` Telegram bob, which is simple design, easy to use, extendable and fun.",
4
- "version": "1993.2.90",
4
+ "version": "1993.2.92",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/halbot",
7
7
  "type": "module",
@@ -46,12 +46,12 @@
46
46
  "mime": "^4.0.7",
47
47
  "mysql2": "^3.14.1",
48
48
  "office-text-extractor": "^3.0.3",
49
- "openai": "^4.96.2",
49
+ "openai": "^4.97.0",
50
50
  "pg": "^8.15.6",
51
51
  "pgvector": "^0.2.0",
52
52
  "telegraf": "^4.16.3",
53
53
  "tesseract.js": "^6.0.1",
54
- "utilitas": "^1999.1.60",
54
+ "utilitas": "^1999.1.63",
55
55
  "youtube-transcript": "^1.2.1"
56
56
  }
57
57
  }
@@ -1,6 +1,6 @@
1
1
  import { alan, hal, uoid, utilitas } from '../index.mjs';
2
2
 
3
- const [EMIJI_FINISH, END, NEW, THREAD] = ['☑️', '❎', '✨', '🧵'];
3
+ const [EMIJI_FINISH, END, NEW, THREAD, CLR] = ['☑️', '❎', '✨', '🧵', '🆑'];
4
4
 
5
5
  const [CREATED, SWITCHED] = [
6
6
  `${NEW} Thread created: `, `${EMIJI_FINISH} Thread switched: `
@@ -1,13 +1,19 @@
1
- import { bot } from '../index.mjs';
1
+ import { bot, storage } from '../index.mjs';
2
2
 
3
3
  const GEMINI = 'GEMINI';
4
4
  const types = { image: 'photo', video: 'video' };
5
5
 
6
6
  const action = async (ctx, next) => {
7
- let [provider, func] = [GEMINI, 'image'];
7
+ let [provider, func, reference] = [GEMINI, 'image', null];
8
8
  switch (ctx.cmd.cmd) {
9
- case 'gptimage': provider = 'OPENAI'; break;
10
- case 'fantasy': func = 'video';
9
+ case 'fantasy': func = 'video'; break;
10
+ case 'gptimage':
11
+ provider = 'OPENAI';
12
+ reference = ctx.collected.filter(x => [
13
+ storage.MIME_JPEG, storage.MIME_PNG, storage.MIME_WEBP
14
+ ].includes(x?.content?.mime_type)).slice(0, 16).map(
15
+ x => x?.content?.data
16
+ );
11
17
  }
12
18
  if (!ctx.cmd.args) {
13
19
  return await ctx.ok('Please input your prompt.');
@@ -15,7 +21,8 @@ const action = async (ctx, next) => {
15
21
  let [objMsg, output] = [(await ctx.ok('💭'))[0], null]; //tts = null
16
22
  try {
17
23
  output = (await ctx._.gen[func](ctx.cmd.args, {
18
- provider, expected: 'FILE'
24
+ provider, expected: 'FILE',
25
+ ...reference?.length ? { reference, input: 'BASE64' } : {},
19
26
  })) || [];
20
27
  } catch (err) {
21
28
  return await ctx.er(err.message || `Error generating ${func}.`,
@@ -8,25 +8,31 @@ const log = content => utilitas.log(content, import.meta.url);
8
8
  const action = async (ctx, next) => {
9
9
  if (!ctx.prompt && !ctx.carry.attachments.length) { return await next(); }
10
10
  let [
11
- ais, YOU, msgs, tts, rsm, pms, extra, lock, sResp, lastMsg, lastSent,
12
- references, audio
11
+ ais, YOU, msgs, pms, extra, lock, sResp, lastMsg, lastSent, references,
12
+ audio,
13
13
  ] = [
14
- await alan.getAi(null, { all: true }), `${ctx.avatar} You:`, {}, {},
15
- {}, [], { buttons: [] }, 1000 * 5, null, null, 0, null, null,
14
+ await alan.getAi(null, { all: true }), `${ctx.avatar} You:`, {}, [],
15
+ { buttons: [] }, 1000 * 5, null, null, 0, null, null,
16
16
  ];
17
17
  const packMsg = options => {
18
18
  const said = !options?.tts && ctx.result ? ctx.result : '';
19
19
  const packed = [
20
20
  ...ctx.carry?.threadInfo, ...said ? [joinL2([YOU, said])] : [],
21
21
  ];
22
- const source = options?.tts ? tts : msgs;
23
22
  const pure = [];
24
23
  ctx.selectedAi.map(n => {
25
- const content = source[n] || '';
24
+ const content = msgs[n]?.[options?.tts ? 'spoken' : 'text'] || '';
26
25
  pure.push(content);
27
- packed.push(joinL2([...options?.tts ? [] : [
28
- `${ais.find(x => x.id === n).name}:`
29
- ], content]));
26
+ const ai = ais.find(x => x.id === n);
27
+ let aiName = ai.name;
28
+ const defModel = aiName.replace(/^.*\(.*\)$/, '$1');
29
+ const curModel = msgs[n]?.model;
30
+ if (defModel && curModel && defModel !== curModel) {
31
+ aiName = aiName.replace(/^(.*\().*(\))$/, `$1${curModel}$2`);
32
+ }
33
+ packed.push(joinL2([
34
+ ...options?.tts ? [] : [`${aiName}:`], content
35
+ ]));
30
36
  });
31
37
  return pure.join('').trim().length ? joinL1(packed) : '';
32
38
  };
@@ -56,34 +62,35 @@ const action = async (ctx, next) => {
56
62
  for (const n of ctx.selectedAi) {
57
63
  pms.push((async ai => {
58
64
  try {
59
- const resp = await alan.talk(ctx.prompt || alen.ATTACHMENTS, {
65
+ const resp = await alan.talk(ctx.prompt || alan.ATTACHMENTS, {
60
66
  aiId: ai, ...ctx.carry, stream: async r => {
61
- msgs[ai] = r.text;
67
+ msgs[ai] = r;
62
68
  ctx.carry.threadInfo.length || ok(onProgress);
63
69
  },
64
70
  });
65
71
  references = resp.references;
66
72
  audio = resp.audio;
67
- msgs[ai] = resp.text;
68
- tts[ai] = ctx.selectedAi.length === 1
69
- && !msgs[ai].split('\n').some(x => /^\s*```/.test(x))
70
- ? resp.spoken : '';
73
+ msgs[ai] = resp;
74
+ msgs[ai].spoken = ctx.selectedAi.length === 1
75
+ && !resp.text.split('\n').some(x => /^\s*```/.test(x))
76
+ ? resp.spoken : null;
71
77
  for (let img of resp?.images || []) {
72
78
  await ctx.image(img.data, { caption: `🎨 by ${resp.model}` });
73
79
  await ctx.timeout();
74
80
  }
75
81
  return resp;
76
82
  } catch (err) {
77
- msgs[ai] = `⚠️ ${err?.message || err}`;
78
- tts[ai] = null;
79
- rsm[ai] = null;
83
+ msgs[ai] = {
84
+ ...msgs[ai], text: `⚠️ ${err?.message || err}`,
85
+ spoken: null,
86
+ };
80
87
  log(err);
81
88
  }
82
89
  })(n));
83
90
  }
84
91
  await Promise.all(pms);
85
- if (Object.values(msgs).join('').trim()) { await ok({ final: true }); }
86
- else { await ctx.deleteMessage(sResp[0].message_id); }
92
+ await (Object.values(msgs).map(x => x.text).join('').trim()
93
+ ? ok({ final: true }) : ctx.deleteMessage(sResp[0].message_id));
87
94
  ctx.tts = audio || packMsg({ tts: true });
88
95
  await next();
89
96
  };