halbot 1990.1.106 → 1990.1.108

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/bin/halbot.mjs CHANGED
@@ -17,8 +17,7 @@ let storage = {
17
17
  try {
18
18
  const { filename, config } = await _getConfig();
19
19
  assert(utilitas.countKeys(config), `Error loading config from ${filename}.`);
20
- const sessionType = utilitas.trim(config.storage?.type, { case: 'UP' });
21
- if (config.storage?.type) { delete config.storage.type; }
20
+ const sessionType = utilitas.trim(config.storage?.provider, { case: 'UP' });
22
21
  switch (sessionType) {
23
22
  case 'MARIADB': case 'MYSQL':
24
23
  await dbio.init(config.storage);
package/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { bot, hal, image, shot, speech, utilitas, vision } from 'utilitas';
1
+ import { alan, bot, image, shot, speech, utilitas, vision } from 'utilitas';
2
2
  import { parse } from 'csv-parse/sync';
3
+ import { end } from 'utilitas/lib/event.mjs';
3
4
 
4
5
  await utilitas.locate(utilitas.__(import.meta.url, 'package.json'));
5
6
  const log = content => utilitas.log(content, 'halbot');
@@ -29,19 +30,18 @@ const fetchPrompts = async () => {
29
30
 
30
31
  const init = async (options) => {
31
32
  assert(options?.telegramToken, 'Telegram Bot API Token is required.');
32
- const [pkg, ai, _speech, speechOptions]
33
- = [await utilitas.which(), {}, {}, { tts: true, stt: true }];
33
+ const [pkg, ai, _speech, speechOptions, engines]
34
+ = [await utilitas.which(), {}, {}, { tts: true, stt: true }, {}];
34
35
  const info = bot.lines([
35
36
  `[${bot.EMOJI_BOT} ${pkg.title}](${pkg.homepage})`, pkg.description
36
37
  ]);
37
- const cacheOptions = options?.storage ? { store: options.storage } : null;
38
38
  if (options?.openaiApiKey) {
39
39
  const apiKey = { apiKey: options.openaiApiKey };
40
- ai['ChatGPT'] = await hal.init({
41
- provider: 'CHATGPT', clientOptions: apiKey, cacheOptions,
42
- });
40
+ await alan.init({ provider: 'openai', ...apiKey });
43
41
  await speech.init({ ...apiKey, provider: 'OPENAI', ...speechOptions });
44
42
  await image.init(apiKey);
43
+ ai['ChatGPT'] = { engine: 'CHATGPT' };
44
+ engines['CHATGPT'] = {};
45
45
  }
46
46
  if (options?.googleApiKey) {
47
47
  const apiKey = { apiKey: options.googleApiKey };
@@ -50,12 +50,25 @@ const init = async (options) => {
50
50
  ...apiKey, provider: 'GOOGLE', ...speechOptions,
51
51
  });
52
52
  }
53
- if (options?.bingToken) {
54
- ai['Bing'] = await hal.init({
55
- provider: 'BING', clientOptions: { userToken: options.bingToken },
56
- cacheOptions,
53
+ if (options?.googleCredentials && options?.googleProject) {
54
+ await alan.init({
55
+ provider: 'VERTEX',
56
+ credentials: options.googleCredentials,
57
+ project: options.googleProject,
58
+ });
59
+ ai['Gemini'] = { engine: 'VERTEX' };
60
+ engines['VERTEX'] = {};
61
+ }
62
+ if (options?.mistralEnabled || options?.mistralEndpoint) {
63
+ await alan.init({
64
+ provider: 'OLLAMA',
65
+ endpoint: options?.mistralEndpoint,
66
+ model: options?.mistralModel,
57
67
  });
68
+ ai['Mistral'] = { engine: 'OLLAMA' };
69
+ engines['OLLAMA'] = {};
58
70
  }
71
+ await alan.initChat({ engines, sessions: options?.storage });
59
72
  assert(utilitas.countKeys(ai), 'No AI provider is configured.');
60
73
  const _bot = await bot.init({
61
74
  args: options?.args,
@@ -83,4 +96,4 @@ const init = async (options) => {
83
96
  };
84
97
 
85
98
  export default init;
86
- export { bot, hal, init, speech, utilitas };
99
+ export { alan, bot, init, speech, utilitas };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "halbot",
3
3
  "description": "Just another ChatGPT/Bing Chat Telegram bob, which is simple design, easy to use, extendable and fun.",
4
- "version": "1990.1.106",
4
+ "version": "1990.1.108",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/halbot",
7
7
  "type": "module",
@@ -31,21 +31,22 @@
31
31
  "dependencies": {
32
32
  "@ffmpeg-installer/ffmpeg": "^1.1.0",
33
33
  "@ffprobe-installer/ffprobe": "^2.1.2",
34
+ "@google-cloud/aiplatform": "^3.9.0",
34
35
  "@google-cloud/speech": "^6.1.0",
35
36
  "@google-cloud/text-to-speech": "^5.0.1",
37
+ "@google-cloud/vertexai": "^0.1.3",
36
38
  "@google-cloud/vision": "^4.0.2",
37
- "@mozilla/readability": "^0.4.4",
38
- "@waylaidwanderer/chatgpt-api": "^1.37.3",
39
+ "@mozilla/readability": "^0.5.0",
39
40
  "csv-parse": "^5.5.3",
40
41
  "fluent-ffmpeg": "^2.1.2",
41
42
  "ioredis": "^5.3.2",
42
43
  "jsdom": "^23.0.1",
43
44
  "mysql2": "^3.6.5",
44
45
  "office-text-extractor": "^3.0.2",
45
- "openai": "^4.20.1",
46
+ "openai": "^4.24.1",
46
47
  "telegraf": "^4.15.3",
47
- "tesseract.js": "^5.0.3",
48
- "utilitas": "^1995.2.20",
48
+ "tesseract.js": "^5.0.4",
49
+ "utilitas": "^1995.2.42",
49
50
  "youtube-transcript": "^1.0.6"
50
51
  }
51
52
  }
@@ -0,0 +1,33 @@
1
+ import { alan } from 'utilitas';
2
+
3
+ const action = async (ctx, next) => {
4
+ const resetContext = context => ctx.session.context = context || {}
5
+ ctx.session.context || resetContext();
6
+ ctx.carry = { sessionId: `ALAN_SESSION_${ctx.chatId}` };
7
+ ctx.clear = async context => {
8
+ await alan.resetSession(
9
+ ctx.carry.sessionId, // { systemPrompt: context?.prompt } @todo! // switch to real system prompt
10
+ );
11
+ resetContext(context);
12
+ ctx.hello(context?.prompt);
13
+ };
14
+ switch (ctx.cmd?.cmd) {
15
+ case 'clear':
16
+ await ctx.clear();
17
+ break;
18
+ case 'factory':
19
+ case 'reset':
20
+ await alan.resetSession(ctx.carry.sessionId);
21
+ break;
22
+ }
23
+ await next();
24
+ };
25
+
26
+ export const { name, run, priority, func, help, cmds, cmdx } = {
27
+ name: 'session',
28
+ run: true,
29
+ priority: -8845,
30
+ func: action,
31
+ help: '',
32
+ cmdx: {},
33
+ };
@@ -1,20 +1,9 @@
1
1
  import { bot, utilitas } from 'utilitas';
2
2
 
3
- const balanced = 'balanced';
4
- const bingTones = [balanced, 'creative', 'precise'];
5
-
6
3
  let configuredAi;
7
4
 
8
5
  const action = async (ctx, next) => {
9
- ctx.session.context || (ctx.session.context = {});
10
- ctx.session.latest || (ctx.session.latest = {});
11
6
  ctx.isDefaultAi = name => name === ctx.firstAi;
12
- ctx.clear = context => (ctx.selectedAi || []).map(n => {
13
- ctx._.ai[n].clear(ctx.chatId);
14
- delete ctx.session.context[n];
15
- delete ctx.session.latest[n];
16
- context && (ctx.session.context[n] = context);
17
- }) && ctx.hello(context?.prompt);
18
7
  ctx.firstAi = (configuredAi = Object.keys(ctx._.ai))[0];
19
8
  switch (ctx.session.config?.ai) {
20
9
  case '': ctx.selectedAi = [ctx.firstAi]; break;
@@ -34,12 +23,6 @@ const validateAi = val => {
34
23
  utilitas.throwError('No AI engine matched.');
35
24
  };
36
25
 
37
- const validateTone = val => {
38
- val = utilitas.trim(val, { case: 'LOW' });
39
- assert([...bingTones, ''].includes(val), 'Unsupported tone-style.');
40
- return val;
41
- };
42
-
43
26
  export const { name, run, priority, func, help, args } = {
44
27
  name: 'Engine',
45
28
  run: true,
@@ -54,26 +37,15 @@ export const { name, run, priority, func, help, args } = {
54
37
  '¶ Tweak enhanced output rendering.',
55
38
  'Example 1: /set --render on',
56
39
  'Example 2: /set --render off',
57
- // '¶ Select between [OpenAI models](https://platform.openai.com/docs/models).',
58
- // "Tip !!!4!!!: Set `gptmodel=''` to use default OpenAI model.",
59
- // 'Popular models:',
60
- // '- [gpt-4](https://platform.openai.com/docs/models/gpt-4): 8192 tokens, trained Sep 2021.',
61
- // '- [gpt-4-32k](https://platform.openai.com/docs/models/gpt-4): 32768 tokens, trained Sep 2021.',
62
- // '- [gpt-3.5-turbo](https://platform.openai.com/docs/models/gpt-3-5): 4096 tokens, trained Sep 2021.',
63
- // '- [text-davinci-003](https://platform.openai.com/docs/models/gpt-3-5): 4097 tokens, trained Sep 2021.',
64
- // '- [text-davinci-002](https://platform.openai.com/docs/models/gpt-3-5): 4097 tokens, trained Sep 2021.',
65
- // '- [code-davinci-002](https://platform.openai.com/docs/models/gpt-3-5): 8001 tokens, trained Sep 2021 (Coding Optimized).',
66
- '¶ Set tone-style for Bing.',
67
- "Tip 4: Set `tone=''` to use default tone-style.",
68
40
  ]),
69
41
  args: {
70
42
  hello: {
71
- type: 'string', short: 's', default: 'You are ChatGPT, a large...',
43
+ type: 'string', short: 's', default: 'You are a helpful assistant.',
72
44
  desc: "Change initial prompt: /set --hello 'Bonjour!'",
73
45
  },
74
46
  ai: {
75
47
  type: 'string', short: 'a', default: '',
76
- desc: "`(ChatGPT, Bing, '', @)` Select AI engine.",
48
+ desc: "`(ChatGPT, Gemini, Mistral, @)` Select AI engine.",
77
49
  validate: validateAi,
78
50
  },
79
51
  render: {
@@ -81,14 +53,5 @@ export const { name, run, priority, func, help, args } = {
81
53
  desc: `\`(${bot.BINARY_STRINGS.join(', ')})\` Enable/Disable enhanced output rendering.`,
82
54
  validate: utilitas.humanReadableBoolean,
83
55
  },
84
- // gptmodel: {
85
- // type: 'string', short: 'g', default: 'gpt-3.5-turbo',
86
- // desc: 'Set OpenAI model: /set --gptmodel=`MODEL`.',
87
- // },
88
- tone: {
89
- type: 'string', short: 't', default: balanced,
90
- desc: `\`(${bingTones.join(', ')})\` Set tone-style for Bing.`,
91
- validate: validateTone,
92
- },
93
56
  },
94
57
  };
@@ -8,16 +8,27 @@ const action = async (ctx, next) => {
8
8
  ctx.multiAi = ctx.selectedAi.length > 1;
9
9
  ctx.hello(ctx.cmd.args);
10
10
  break;
11
- case 'bing':
12
- assert(utilitas.insensitiveHas(allAi, 'bing'), 'Bing is not available.');
13
- ctx.selectedAi = ['Bing'];
14
- ctx.hello(ctx.cmd.args);
15
- break;
16
11
  case 'chatgpt':
17
- assert(utilitas.insensitiveHas(allAi, 'chatgpt'), 'ChatGPT is not available.');
12
+ if (!utilitas.insensitiveHas(allAi, 'chatgpt')) {
13
+ return await ctx.er('ChatGPT is not available.');
14
+ }
18
15
  ctx.selectedAi = ['ChatGPT'];
19
16
  ctx.hello(ctx.cmd.args);
20
17
  break;
18
+ case 'gemini':
19
+ if (!utilitas.insensitiveHas(allAi, 'gemini')) {
20
+ return await ctx.er('Gemini is not available.');
21
+ }
22
+ ctx.selectedAi = ['Gemini'];
23
+ ctx.hello(ctx.cmd.args);
24
+ break;
25
+ case 'mistral':
26
+ if (!utilitas.insensitiveHas(allAi, 'mistral')) {
27
+ return await ctx.er('Mistral is not available.');
28
+ }
29
+ ctx.selectedAi = ['Mistral'];
30
+ ctx.hello(ctx.cmd.args);
31
+ break;
21
32
  }
22
33
  await next();
23
34
  };
@@ -32,7 +43,8 @@ export const { name, run, priority, func, help, cmds } = {
32
43
  ]),
33
44
  cmds: {
34
45
  all: 'Use all AI engines simultaneously: /all Say hello to all AIs!',
35
- chatgpt: 'Use ChatGPT temporary: /chatgpt Say hello to ChatGPT!',
36
- bing: 'Use Bing temporary: /bing Say hello to Bing!',
46
+ chatgpt: 'Use ⚛️ ChatGPT temporary: /chatgpt Say hello to ChatGPT!',
47
+ gemini: 'Use ♊️ Gemini temporary: /gemini Say hello to Gemini!',
48
+ mistral: 'Use Ⓜ️ Mistral temporary: /mistral Say hello to Mistral!',
37
49
  },
38
50
  };
@@ -48,13 +48,10 @@ const action = async (ctx, next) => {
48
48
  ])
49
49
  ));
50
50
  return await ctx.ok(details || 'Data not found.');
51
- case 'clear':
52
- ctx.clear();
53
- break;
54
51
  default:
55
52
  const prompt = ctx.session.prompts?.[cmd] || ctx._.prompts?.[cmd]?.prompt;
56
53
  !ctx.context && prompt && (ctx.context = { cmd, prompt });
57
- ctx.context && ctx.clear(ctx.context);
54
+ ctx.context && await ctx.clear(ctx.context);
58
55
  }
59
56
  await next();
60
57
  };
@@ -1,4 +1,4 @@
1
- import { bot, hal, utilitas } from 'utilitas';
1
+ import { alan, bot, utilitas } from 'utilitas';
2
2
 
3
3
  const action = async (ctx, next) => {
4
4
  // avatar
@@ -16,21 +16,18 @@ const action = async (ctx, next) => {
16
16
  ctx.avatar = '😸';
17
17
  }
18
18
  // prompt
19
+ const maxInputTokens = alan.getMaxChatPromptLimit();
19
20
  const additionInfo = ctx.collected.length ? ctx.collected.map(
20
21
  x => x.content
21
22
  ).join('\n').split(' ') : [];
22
23
  ctx.prompt = (ctx.text || '') + '\n\n';
23
- while (hal.countTokens(ctx.prompt) < hal.MAX_PROMPT_TOKENS
24
+ while (alan.countTokens(ctx.prompt) < maxInputTokens
24
25
  && additionInfo.length) {
25
26
  ctx.prompt += ` ${additionInfo.shift()}`;
26
27
  }
27
28
  ctx.prompt = utilitas.trim(ctx.prompt);
28
29
  additionInfo.filter(x => x).length && (ctx.prompt += '...');
29
30
  // next
30
- ctx.carry = {
31
- sessionId: ctx.chatId,
32
- toneStyle: ctx.session.config?.tone,
33
- };
34
31
  await next();
35
32
  };
36
33
 
@@ -1,10 +1,11 @@
1
- import { bot, utilitas } from 'utilitas';
1
+ import { alan, bot, utilitas } from 'utilitas';
2
2
 
3
3
  const onProgress = { onProgress: true };
4
- const [BOT, LN2] = [`${bot.EMOJI_BOT} `, '\n\n'];
5
4
  const [joinL1, joinL2] = [a => a.join(LN2), a => a.join(LN2)];
6
- const enrich = name => name === 'Bing' ? `${name} (Sydney)` : name;
5
+ const enrich = name => name === 'VERTEX' ? 'Gemini' : name;
7
6
  const log = content => utilitas.log(content, import.meta.url);
7
+ const [BOT, BOTS, LN2]
8
+ = [`${bot.EMOJI_BOT} `, { ChatGPT: '⚛️', Gemini: '♊️', Mistral: 'Ⓜ️' }, '\n\n'];
8
9
 
9
10
  const action = async (ctx, next) => {
10
11
  if (!ctx.prompt) { return await next(); }
@@ -17,12 +18,12 @@ const action = async (ctx, next) => {
17
18
  const pure = [];
18
19
  ctx.selectedAi.map(n => {
19
20
  const content = source[n] || '';
20
- const cmd = ctx.session.context[n]?.cmd;
21
+ const cmd = ctx.session.context?.cmd;
21
22
  const context = cmd && ` > \`${cmd}\` (exit by /clear)` || '';
22
23
  pure.push(content);
23
24
  packed.push(joinL2([
24
25
  ...ctx.multiAi || !ctx.isDefaultAi(n) || said || context
25
- ? [`${BOT}${enrich(n)}${context}:`] : [], content,
26
+ ? [`${BOTS[n]} ${enrich(n)}${context}:`] : [], content,
26
27
  ]));
27
28
  });
28
29
  return options?.tts && !pure.join('').trim().length ? '' : joinL1(packed);
@@ -39,19 +40,20 @@ const action = async (ctx, next) => {
39
40
  for (let n of ctx.selectedAi) {
40
41
  pms.push((async () => {
41
42
  try {
42
- const response = await ctx._.ai[n].send(ctx.prompt, {
43
- ...ctx.carry, session: ctx.session.latest[n], // promptPrefix: '',
44
- }, token => {
45
- msgs[n] = `${(msgs[n] || '')}${token}`;
46
- ok(onProgress);
43
+ const resp = await alan.talk(ctx.prompt, {
44
+ engine: ctx._.ai[n].engine, ...ctx.carry,
45
+ stream: async r => {
46
+ msgs[n] = r[0].text;
47
+ await ok(onProgress);
48
+ },
47
49
  });
48
50
  msgs[n] = ctx.session.config?.render === false
49
- ? response.response : response.responseRendered;
50
- tts[n] = msgs[n].split('\n').some(x => /^```/.test(x)) ? '' : response.spokenText;
51
- extra.buttons = response?.suggestedResponses?.map?.(label => ({
52
- label, text: `/bing@${ctx.botInfo.username} ${label}`,
53
- }));
54
- return ctx.session.latest[n] = response;
51
+ ? resp.text : resp.rendered;
52
+ tts[n] = msgs[n].split('\n').some(x => /^```/.test(x)) ? '' : resp.spoken;
53
+ // extra.buttons = resp?.suggestedResponses?.map?.(label => ({
54
+ // label, text: `/bing@${ctx.botInfo.username} ${label}`,
55
+ // }));
56
+ return resp;
55
57
  } catch (err) {
56
58
  msgs[n] = err?.message || err;
57
59
  tts[n] = msgs[n];