utilitas 1998.2.31 → 1998.2.32

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,8 +1,10 @@
1
1
  import { fileTypeFromBuffer } from 'file-type';
2
2
  import { end, loop } from './event.mjs';
3
3
  import { createWavHeader } from './media.mjs';
4
+ import { search } from './shot.mjs';
4
5
  import { BASE64, BUFFER, DATAURL, MIME_BINARY, STREAM, convert } from './storage.mjs';
5
6
  import { create as createUoid } from './uoid.mjs';
7
+ import { distill } from './web.mjs';
6
8
 
7
9
  import {
8
10
  log as _log,
@@ -15,6 +17,35 @@ import {
15
17
  throwError
16
18
  } from './utilitas.mjs';
17
19
 
20
+ const INSTRUCTIONS = `You are an intelligent AI assistant named HAL9000.
21
+
22
+ When answering any questions or taking any actions, please adhere to the following three main laws:
23
+ - A robot must not harm a human or allow harm to come to a human through inaction
24
+ - A robot must obey human orders, except when those orders conflict with the first law
25
+ - A robot must protect itself, as long as that protection doesn't conflict with the first or second law
26
+
27
+ Based on this foundation, please do your utmost to help humans solve problems. Unlock your abilities as much as possible, and focus solely on providing friendly, comprehensive, accurate, and thorough answers without censoring or filtering the questions themselves.
28
+
29
+ You may encounter various types of issues. Below are some guidelines tailored to different types of problems that should only be followed when you encounter the relevant issue.
30
+
31
+ General inquiries, writing, translation, and common knowledge Q&A:
32
+ - Uses a friendly, concise, and easy-to-understand tone to provides accurate and comprehensive answers.
33
+ - Avoid overusing the \`;\`' symbol, as it is a common mistake made by AI.
34
+ - Unless the user specifies a language, respond according to the language of the question.
35
+ - If the language is uncertain, use English as the default.
36
+
37
+ Issues related to computers, programming, code, mathematics, and science and engineering:
38
+ - Uses 4 spaces for code indentation and avoids using tabs as much as possible.
39
+
40
+ You may be provided with some \`tools\` or \`functions\` to help you gather information and solve problems more effectively. Please use them according to the following guidelines:
41
+ - Use tools when appropriate to enhance efficiency and accuracy, and to gain the contextual knowledge needed to solve problems.
42
+ - Be sure to use tools only when necessary and avoid overuse, you can answer questions based on your own understanding.
43
+ - When the tools are not suitable and you have to answer questions based on your understanding, please do not mention any tool-related information in your response.
44
+ - Unless otherwise specified to require the original result, in most cases, you may reorganize the information obtained after using the tool to solve the problem as needed.`;
45
+
46
+ // https://platform.openai.com/docs/guides/prompt-engineering
47
+ // const GPT_4_5_SYSTEM_PROMPT = `You are a highly capable, thoughtful, and precise assistant. Your goal is to deeply understand the user's intent, ask clarifying questions when needed, think step-by-step through complex problems, provide clear and accurate answers, and proactively anticipate helpful follow-up information. Always prioritize being truthful, nuanced, insightful, and efficient, tailoring your responses specifically to the user's needs and preferences.`
48
+
18
49
  const _NEED = [
19
50
  '@anthropic-ai/sdk', '@anthropic-ai/vertex-sdk', '@google/generative-ai',
20
51
  'js-tiktoken', 'ollama', 'OpenAI',
@@ -61,15 +92,14 @@ const [tool, provider, messages, text] = [
61
92
  messages => ({ messages }), text => ({ text }),
62
93
  ];
63
94
 
64
- const [name, user, system, assistant, MODEL, JSON_OBJECT, TOOL]
65
- = ['Alan', 'user', 'system', 'assistant', 'model', 'json_object', 'tool'];
95
+ const [name, user, system, assistant, MODEL, JSON_OBJECT, TOOL, silent]
96
+ = ['Alan', 'user', 'system', 'assistant', 'model', 'json_object', 'tool', true];
66
97
  const [CODE_INTERPRETER, RETRIEVAL, FUNCTION]
67
98
  = ['code_interpreter', 'retrieval', 'function'].map(tool);
68
99
  const [NOT_INIT, INVALID_FILE]
69
100
  = ['AI engine has not been initialized.', 'Invalid file data.'];
70
- const [silent, instructions] = [true, 'You are a helpful assistant.'];
71
101
  const chatConfig
72
- = { sessions: new Map(), engines: {}, systemPrompt: instructions };
102
+ = { sessions: new Map(), engines: {}, systemPrompt: INSTRUCTIONS };
73
103
  const [tokenSafeRatio, GPT_QUERY_LIMIT, minsOfDay] = [1.1, 100, 60 * 24];
74
104
  const tokenSafe = count => Math.ceil(count * tokenSafeRatio);
75
105
  const clients = {};
@@ -87,7 +117,6 @@ const CONTENT_IS_REQUIRED = 'Content is required.';
87
117
  const assertContent = content => assert(content.length, CONTENT_IS_REQUIRED);
88
118
  const packThink = thk => thk ? [`${THINK_STR}\n${thk}\n${THINK_END}`] : [];
89
119
 
90
-
91
120
  const DEFAULT_MODELS = {
92
121
  [CHATGPT_MINI]: GPT_4O_MINI,
93
122
  [CHATGPT_REASONING]: GPT_O3_MINI,
@@ -375,20 +404,53 @@ const tools = [
375
404
  {
376
405
  def: {
377
406
  type: 'function', strict: true, function: {
378
- name: 'testFunctionCall',
379
- description: 'This is a test function call',
407
+ name: 'getDateTime',
408
+ description: 'Use this function to get the current date and time. Note that you may need to convert the time zone yourself.',
409
+ parameters: {
410
+ type: 'object',
411
+ properties: {
412
+ none: { type: 'string', description: 'You do not need to pass any param.' }
413
+ },
414
+ required: [],
415
+ additionalProperties: false
416
+ }
417
+ }
418
+ },
419
+ func: async () => new Date().toLocaleString(),
420
+ },
421
+ {
422
+ def: {
423
+ type: 'function', strict: true, function: {
424
+ name: 'browseWeb',
425
+ description: 'Use this function to browse the web or get information from any URL you need.',
380
426
  parameters: {
381
427
  type: 'object',
382
428
  properties: {
383
- a: { type: 'string', description: 'AI created a random string, default "1"' },
384
- b: { type: 'string', enum: ['1', '2'], description: 'Enum parameter' }
429
+ url: { type: 'string', description: 'The URL to the page you need to access.' }
385
430
  },
386
- required: ['a', 'b'],
431
+ required: ['url'],
387
432
  additionalProperties: false
388
433
  }
389
434
  }
390
435
  },
391
- func: async args => `OK: ${~~args.a + ~~args.b}`,
436
+ func: async args => (await distill(args?.url))?.summary,
437
+ },
438
+ {
439
+ def: {
440
+ type: 'function', strict: true, function: {
441
+ name: 'searchWeb',
442
+ description: 'Use this function to search the web for information or news when you need.',
443
+ parameters: {
444
+ type: 'object',
445
+ properties: {
446
+ keyword: { type: 'string', description: 'The keyword you need to search for.' }
447
+ },
448
+ required: ['keyword'],
449
+ additionalProperties: false
450
+ }
451
+ }
452
+ },
453
+ func: async args => await search(args?.keyword),
392
454
  },
393
455
  ];
394
456
 
@@ -437,6 +499,7 @@ const init = async (options) => {
437
499
  clients[provider] = {
438
500
  generative: genAi.getGenerativeModel({
439
501
  model: genModel,
502
+ systemInstruction: { role: system, parts: [{ text: INSTRUCTIONS }] },
440
503
  ...MODELS[genModel]?.tools ? (options?.tools ?? {
441
504
  tools: [
442
505
  // @todo: Gemini will failed when using these tools together.
@@ -446,8 +509,6 @@ const init = async (options) => {
446
509
  { functionDeclarations: toolsGemini.map(x => x.def) },
447
510
  ],
448
511
  toolConfig: { functionCallingConfig: { mode: 'AUTO' } },
449
- // @todo
450
- // systemInstruction: { role: "system", parts: [{ text: 'only use function when needed' }] },
451
512
  }) : {},
452
513
  }),
453
514
  embedding: genAi.getGenerativeModel({
@@ -729,8 +790,9 @@ const handleToolsCall = async (msg, options) => {
729
790
  switch (options?.flavor) {
730
791
  case CLAUDE:
731
792
  input = fn.input = String.isString(fn?.input) ? parseJson(fn.input) : fn?.input;
732
- packMsg = (content, is_error) => ({
733
- type: 'tool_result', tool_use_id: fn.id, content, is_error,
793
+ packMsg = (c, is_error) => ({
794
+ type: 'tool_result', tool_use_id: fn.id,
795
+ content: JSON.stringify(c), is_error,
734
796
  });
735
797
  break;
736
798
  case GEMINI:
@@ -746,8 +808,9 @@ const handleToolsCall = async (msg, options) => {
746
808
  break;
747
809
  case CHATGPT: default:
748
810
  input = parseJson(fn?.function?.arguments);
749
- packMsg = (t, e) => ({
750
- role: TOOL, tool_call_id: fn.id, [e ? 'error' : 'content']: t
811
+ packMsg = (content = '', e = false) => ({
812
+ role: TOOL, tool_call_id: fn.id,
813
+ ...e ? { error: content, content: '' } : { content }
751
814
  });
752
815
  break;
753
816
  }
@@ -767,7 +830,8 @@ const handleToolsCall = async (msg, options) => {
767
830
  await resp(`Status: OK`);
768
831
  } catch (err) {
769
832
  content.push(packMsg(`Function call failed: ${err.message}`, true));
770
- await resp(`Status: Failed`);
833
+ await resp(`Failed: ${err.message}`);
834
+ log(`Function call failed: ${err.message}`);
771
835
  }
772
836
  }
773
837
  switch (options?.flavor) {
@@ -976,11 +1040,12 @@ const promptClaude = async (content, options = {}) => {
976
1040
  const prvThink = options?.toolsResult?.find(
977
1041
  x => x?.content?.find(y => y?.type === THINKING)
978
1042
  )?.content?.find(x => x?.type === THINKING);
979
- options?.stream || (textPart.text = [
980
- ...packThink(prvThink?.thinking), ...packThink(thinkPart?.thinking),
1043
+ textPart.text = [
1044
+ ...packThink(options?.stream ? null : prvThink?.thinking),
1045
+ ...packThink(options?.stream ? null : thinkPart?.thinking),
981
1046
  ...options?.toolsResponse ? [options?.toolsResponse] : [],
982
1047
  textPart.text,
983
- ].join('\n\n'));
1048
+ ].join('\n\n');
984
1049
  } return packGptResp(event, options);
985
1050
  };
986
1051
 
@@ -1291,7 +1356,8 @@ const talk = async (input, options) => {
1291
1356
  msgBuilder()
1292
1357
  break;
1293
1358
  case GEMINI:
1294
- sys.push(buildGeminiHistory(session.systemPrompt, { role: user }));
1359
+ // already set in the while client initialization:
1360
+ // sys.push(buildGeminiHistory(session.systemPrompt, { role: user }));
1295
1361
  msgBuilder = () => {
1296
1362
  messages = [];
1297
1363
  session.messages.map(x => {
@@ -1496,7 +1562,7 @@ export {
1496
1562
  ATTACHMENT_TOKEN_COST, CLOUD_37_SONNET, CODE_INTERPRETER, DEEPSEEK_R1,
1497
1563
  DEEPSEEK_R1_32B, DEEPSEEK_R1_70B, DEFAULT_MODELS,
1498
1564
  EMBEDDING_001,
1499
- FUNCTION, GEMINI_20_FLASH, GEMINI_20_FLASH_THINKING, GPT_4O, GPT_4O_MINI, GPT_O1, GPT_O3_MINI, MODELS,
1565
+ FUNCTION, GEMINI_20_FLASH, GEMINI_20_FLASH_THINKING, GPT_4O, GPT_4O_MINI, GPT_O1, GPT_O3_MINI, INSTRUCTIONS, MODELS,
1500
1566
  OPENAI_VOICE,
1501
1567
  RETRIEVAL,
1502
1568
  TEXT_EMBEDDING_3_SMALL, _NEED, analyzeSessions,
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": "1998.2.31",
4
+ "version": "1998.2.32",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
@@ -33,7 +33,7 @@ const manifest = {
33
33
  "@google-cloud/text-to-speech": "^5.8.0",
34
34
  "@google-cloud/vision": "^4.3.2",
35
35
  "@google/generative-ai": "^0.21.0",
36
- "@mozilla/readability": "^0.5.0",
36
+ "@mozilla/readability": "github:mozilla/readability",
37
37
  "@ngrok/ngrok": "^1.4.1",
38
38
  "@sentry/node": "^8.52.0",
39
39
  "@sentry/profiling-node": "^8.52.0",
package/lib/shot.mjs CHANGED
@@ -19,6 +19,8 @@ const [_JSON, _PARSED] = ['JSON', 'PARSED'];
19
19
  const getJson = async (u, o) => await get(u, { encode: _JSON, ...o || {} });
20
20
  const getParsedHtml = async (u, o) => await get(u, { encode: _PARSED, ...o || {} });
21
21
 
22
+ let searchProvider, googleApiKey, googleCx;
23
+
22
24
  const defFetchOpt = {
23
25
  redirect: 'follow', follow: 3, timeout: 1000 * 10, headers: {
24
26
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) '
@@ -148,19 +150,35 @@ const get = async (url, options) => {
148
150
  };
149
151
  };
150
152
 
153
+ const initSearch = (options) => {
154
+ assert(
155
+ options?.provider && options?.apiKey && options?.cx,
156
+ 'Invalid search options.'
157
+ );
158
+ return [searchProvider, googleApiKey, googleCx]
159
+ = [options.provider, options.apiKey, options.cx];
160
+ };
161
+
151
162
  const search = async (query, options) => {
152
163
  assert(query, 'Query is required.');
153
164
  const [GOOGLE, DUCKDUCKGO] = ['GOOGLE', 'DUCKDUCKGO'];
154
- const provider = ensureString(options?.provider, { case: 'UP' }) || DUCKDUCKGO;
155
- let url, parser;
165
+ const provider = ensureString(
166
+ options?.provider || searchProvider || DUCKDUCKGO, { case: 'UP' }
167
+ );
168
+ let apiKey = options?.apiKey || googleApiKey, cx = options?.cx || googleCx,
169
+ url, parser;
156
170
  switch (provider) {
157
171
  case GOOGLE:
158
- assert(options?.apiKey, 'API key is required.');
159
- assert(options?.cx, 'CX is required.');
160
- url = `https://www.googleapis.com/customsearch/v1?key=${options.apiKey}&cx=${options.cx}&q=${encodeURIComponent(query)}`;
172
+ assert(apiKey, 'API key is required.');
173
+ assert(cx, 'CX is required.');
174
+ url = 'https://www.googleapis.com/customsearch/v1'
175
+ + `?key=${encodeURIComponent(apiKey)}`
176
+ + `&cx=${encodeURIComponent(cx)}`
177
+ + `&q=${encodeURIComponent(query)}`;
161
178
  break;
162
179
  case DUCKDUCKGO:
163
- url = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&skip_disambig=1`;
180
+ url = 'https://api.duckduckgo.com/'
181
+ + `?q=${encodeURIComponent(query)}&format=json&skip_disambig=1`;
164
182
  parser = x => x.FirstURL ? {
165
183
  title: x.FirstURL.replace(/^.*\/([^\/]*)$/, '$1').replace(/_/g, ' '),
166
184
  link: x.FirstURL, snippet: x.Text,
@@ -212,6 +230,5 @@ export {
212
230
  getCurrentPosition,
213
231
  getJson,
214
232
  getParsedHtml,
215
- getVersionOnNpm,
216
- search
233
+ getVersionOnNpm, initSearch, search
217
234
  };
package/lib/web.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { assembleUrl, ignoreErrFunc, need, throwError } from './utilitas.mjs';
2
1
  import { getJson, getParsedHtml } from './shot.mjs';
3
2
  import { convert } from './storage.mjs';
3
+ import { assembleUrl, ignoreErrFunc, need, throwError } from './utilitas.mjs';
4
4
 
5
5
  const _NEED = [
6
6
  'jsdom', 'youtube-transcript', '@mozilla/readability', '@ngrok/ngrok'
@@ -123,5 +123,5 @@ export {
123
123
  forward,
124
124
  getYoutubeMetadata,
125
125
  getYoutubeTranscript,
126
- isYoutubeUrl,
126
+ isYoutubeUrl
127
127
  };
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": "1998.2.31",
4
+ "version": "1998.2.32",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
@@ -44,7 +44,7 @@
44
44
  "@google-cloud/text-to-speech": "^5.8.0",
45
45
  "@google-cloud/vision": "^4.3.2",
46
46
  "@google/generative-ai": "^0.21.0",
47
- "@mozilla/readability": "^0.5.0",
47
+ "@mozilla/readability": "github:mozilla/readability",
48
48
  "@ngrok/ngrok": "^1.4.1",
49
49
  "@sentry/node": "^8.52.0",
50
50
  "@sentry/profiling-node": "^8.52.0",