utilitas 1998.2.37 → 1998.2.38

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
@@ -54,7 +54,7 @@ const [
54
54
  CLAUDE_35_HAIKU, CLOUD_37_SONNET, AUDIO, WAV, CHATGPT_MINI, ATTACHMENTS,
55
55
  CHAT, OPENAI_VOICE, MEDIUM, LOW, HIGH, GPT_REASONING_EFFORT, THINK,
56
56
  THINK_STR, THINK_END, AZURE, TOOLS_STR, TOOLS_END, TOOLS, TEXT, THINKING,
57
- OK,
57
+ OK, FUNC,
58
58
  ] = [
59
59
  'OPENAI', 'GEMINI', 'CHATGPT', 'OPENAI_EMBEDDING', 'GEMINI_EMEDDING',
60
60
  'OPENAI_TRAINING', 'OLLAMA', 'CLAUDE', 'gpt-4o-mini', 'gpt-4o', 'o1',
@@ -66,7 +66,7 @@ const [
66
66
  'claude-3-7-sonnet@20250219', 'audio', 'wav', 'CHATGPT_MINI',
67
67
  '[ATTACHMENTS]', 'CHAT', 'OPENAI_VOICE', 'medium', 'low', 'high',
68
68
  'medium', 'think', '<think>', '</think>', 'AZURE', '<tools>',
69
- '</tools>', 'tools', 'text', 'thinking', 'OK',
69
+ '</tools>', 'tools', 'text', 'thinking', 'OK', 'function',
70
70
  ];
71
71
 
72
72
  const [
@@ -90,7 +90,7 @@ const [tool, provider, messages, text] = [
90
90
  const [name, user, system, assistant, MODEL, JSON_OBJECT, TOOL, silent]
91
91
  = ['Alan', 'user', 'system', 'assistant', 'model', 'json_object', 'tool', true];
92
92
  const [CODE_INTERPRETER, RETRIEVAL, FUNCTION]
93
- = ['code_interpreter', 'retrieval', 'function'].map(tool);
93
+ = ['code_interpreter', 'retrieval', FUNC].map(tool);
94
94
  const [NOT_INIT, INVALID_FILE]
95
95
  = ['AI engine has not been initialized.', 'Invalid file data.'];
96
96
  const chatConfig
@@ -442,7 +442,10 @@ const tools = [
442
442
  parameters: {
443
443
  type: 'object',
444
444
  properties: {
445
- keyword: { type: 'string', description: 'The keyword you need to search for.' }
445
+ keyword: { type: 'string', description: 'The keyword you need to search for.' },
446
+ num: { type: 'integer', description: 'The number of search results you need, default `10`.' },
447
+ start: { type: 'integer', description: 'The start index of the search results, default `1`.' },
448
+ image: { type: 'boolean', description: 'Whether to search for images, default `false`.' },
446
449
  },
447
450
  required: ['keyword'],
448
451
  additionalProperties: false
@@ -792,9 +795,11 @@ const handleToolsCall = async (msg, options) => {
792
795
  for (const fn of calls) {
793
796
  switch (options?.flavor) {
794
797
  case CLAUDE:
795
- input = fn.input = String.isString(fn?.input) ? parseJson(fn.input) : fn?.input;
798
+ input = fn.input = String.isString(fn?.input)
799
+ ? parseJson(fn.input) : fn?.input;
796
800
  packMsg = (content, is_error) => ({
797
- type: 'tool_result', tool_use_id: fn.id, content, is_error,
801
+ type: 'tool_result', tool_use_id: fn.id,
802
+ content, is_error,
798
803
  });
799
804
  break;
800
805
  case GEMINI:
@@ -802,7 +807,8 @@ const handleToolsCall = async (msg, options) => {
802
807
  packMsg = (t, e) => ({
803
808
  functionResponse: {
804
809
  name: fn?.functionCall?.name, response: {
805
- name: fn?.functionCall?.name, content: e ? `[Error] ${t}` : t,
810
+ name: fn?.functionCall?.name,
811
+ content: e ? `[Error] ${t}` : t,
806
812
  }
807
813
  }
808
814
  });
@@ -842,13 +848,18 @@ const handleToolsCall = async (msg, options) => {
842
848
  log(rt);
843
849
  }
844
850
  }
845
- switch (options?.flavor) {
846
- case CLAUDE: content = content.length ? [{ role: user, content }] : []; break;
847
- case GEMINI: content = [{ role: user, parts: content }]; break;
851
+ if (content.length) {
852
+ switch (options?.flavor) {
853
+ case CLAUDE: content = [{ role: user, content }]; break;
854
+ case GEMINI: content = [{ role: FUNC, parts: content }]; break;
855
+ }
848
856
  }
849
857
  responded && await resp(`\n${TOOLS_END}`);
850
858
  }
851
- return { toolsResult: [...content.length ? preRes : [], ...content], toolsResponse };
859
+ return {
860
+ toolsResult: [...content.length ? preRes : [], ...content],
861
+ toolsResponse,
862
+ };
852
863
  };
853
864
 
854
865
  const mergeMsgs = (resp, calls) => [resp, ...calls.length ? [
@@ -1089,18 +1100,18 @@ const promptGemini = async (content, options = {}) => {
1089
1100
  options?.toolsResult?.[options?.toolsResult?.length]?.parts
1090
1101
  || buildGeminiMessage(content, options)
1091
1102
  );
1092
- let [result, references, functionCalls]
1103
+ let [result, references, functionCalls, responded]
1093
1104
  = [options?.result ?? '', null, null];
1094
1105
  for await (const chunk of resp.stream) {
1095
1106
  functionCalls || (functionCalls = chunk.functionCalls);
1096
- const delta = chunk?.text?.() || '';
1097
1107
  const rfc = packGeminiReferences(
1098
1108
  chunk.candidates[0]?.groundingMetadata?.groundingChunks,
1099
1109
  chunk.candidates[0]?.groundingMetadata?.groundingSupports
1100
1110
  );
1101
- if (delta === '' && !rfc) { continue; }
1111
+ rfc && (references = rfc);
1112
+ let delta = chunk?.text?.() || '';
1113
+ delta && (responded = responded || (delta = `\n\n${delta}`));
1102
1114
  result += delta;
1103
- references = rfc;
1104
1115
  await streamResp({ text: options?.delta ? delta : result }, options);
1105
1116
  }
1106
1117
  const _resp = await resp.response;
@@ -1109,14 +1120,14 @@ const promptGemini = async (content, options = {}) => {
1109
1120
  { role: MODEL, parts: functionCalls },
1110
1121
  { ...options, result, flavor: GEMINI }
1111
1122
  );
1112
- if (functionCalls.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
1123
+ if (toolsResult.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
1113
1124
  return promptGemini(content, {
1114
1125
  ...options || {}, toolsResult: [...options?.toolsResult || [],
1115
1126
  ...toolsResult], result: toolsResponse,
1116
1127
  });
1117
1128
  }
1118
1129
  return await packGptResp({
1119
- text: mergeMsgs(toolsResponse, functionCalls), references,
1130
+ text: mergeMsgs(toolsResponse, toolsResult), references,
1120
1131
  }, options);
1121
1132
  };
1122
1133
 
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.37",
4
+ "version": "1998.2.38",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
package/lib/shot.mjs CHANGED
@@ -5,6 +5,7 @@ import { sha256 } from './encryption.mjs';
5
5
  import { distillHtml } from './web.mjs';
6
6
 
7
7
  import {
8
+ ensureInt,
8
9
  ensureString, extract, ignoreErrFunc, inBrowser, parseJson, parseVersion,
9
10
  throwError, which
10
11
  } from './utilitas.mjs';
@@ -160,66 +161,28 @@ const initSearch = (options) => {
160
161
  };
161
162
 
162
163
  const search = async (query, options) => {
164
+ const [key, cx, min, max]
165
+ = [options?.apiKey || googleApiKey, options?.cx || googleCx, 1, 10];
163
166
  assert(query, 'Query is required.');
164
- const [GOOGLE, DUCKDUCKGO] = ['GOOGLE', 'DUCKDUCKGO'];
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;
170
- switch (provider) {
171
- case GOOGLE:
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)}`;
178
- break;
179
- case DUCKDUCKGO:
180
- url = 'https://api.duckduckgo.com/'
181
- + `?q=${encodeURIComponent(query)}&format=json&skip_disambig=1`;
182
- parser = x => x.FirstURL ? {
183
- title: x.FirstURL.replace(/^.*\/([^\/]*)$/, '$1').replace(/_/g, ' '),
184
- link: x.FirstURL, snippet: x.Text,
185
- image: `https://duckduckgo.com${x.Icon.URL}`, source: null,
186
- } : null;
187
- break;
188
- default:
189
- throwError(`Invalid provider: ${provider}.`);
190
- }
167
+ assert(key, 'API key is required.');
168
+ assert(cx, 'CX is required.');
169
+ const num = ensureInt(options?.num || max, { min, max });
170
+ const start = ensureInt(options?.start || min, { min });
171
+ assert(start + num <= 100, 'Reached maximum search limit.');
172
+ const url = 'https://www.googleapis.com/customsearch/v1'
173
+ + `?key=${encodeURIComponent(key)}&cx=${encodeURIComponent(cx)}`
174
+ + `&q=${encodeURIComponent(query)}&num=${num}&start=${start}`
175
+ + (options?.image ? `&searchType=image` : '');
191
176
  const resp = await get(url, { encode: _JSON, ...options || {} });
192
- let result = [];
193
- if (options?.raw) {
194
- result = resp.content;
195
- } else if (provider === GOOGLE) {
196
- result.push(...resp?.content?.items.map(x => ({
197
- title: x.title, link: x.link, snippet: x.snippet,
198
- image: x.pagemap?.cse_image?.[0]?.src || null,
199
- })));
200
- } else if (provider === DUCKDUCKGO) {
201
- const cnt = resp?.content;
202
- if (cnt?.Abstract) {
203
- result.push({
204
- title: cnt?.Heading + (cnt?.Entity ? ` (${cnt.Entity})` : ''),
205
- link: cnt?.AbstractURL, snippet: cnt?.AbstractText,
206
- image: cnt?.Image ? `https://duckduckgo.com${cnt.Image}` : null,
207
- source: cnt?.AbstractSource,
208
- });
209
- if (cnt?.Results) {
210
- result.push(...cnt.Results.map(x => ({
211
- title: x.Text, link: x.FirstURL, snippet: null,
212
- image: x.Icon ? `https://duckduckgo.com${x.Icon.URL}` : null,
213
- source: null,
214
- })));
215
- }
216
- } else if (cnt?.RelatedTopics?.[0] && !options?.no_recurse) {
217
- result = await search(cnt.RelatedTopics[0].FirstURL.replace(
218
- /^.*\/([^\/]*)$/, '$1'
219
- ).replace(/_/g, ' '), { ...options, no_recurse: true });
220
- }
221
- }
222
- return result;
177
+ return options?.raw ? resp.content : {
178
+ totalResults: resp?.content?.searchInformation?.totalResults || 0,
179
+ startIndex: resp?.content?.queries?.request?.[0]?.startIndex || 1,
180
+ items: resp?.content?.items.map(x => ({
181
+ title: x.title, link: options?.image ? null : x.link,
182
+ snippet: x.snippet,
183
+ image: (options?.image ? x.link : x.pagemap?.cse_image?.[0]?.src) || null,
184
+ })),
185
+ };
223
186
  };
224
187
 
225
188
  export default get;
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.37",
4
+ "version": "1998.2.38",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",