utilitas 2001.1.87 โ†’ 2001.1.89

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
@@ -49,12 +49,13 @@ const [
49
49
  TOOLS_STR, TOOLS_END, TOOLS, TEXT, OK, FUNC, GPT_52, GPT_51_CODEX,
50
50
  GPT_IMAGE_15, GEMMA_3_27B, ANTHROPIC, ais, MAX_TOOL_RECURSION, LOG, name,
51
51
  user, system, assistant, JSON_OBJECT, PROMPT_IS_REQUIRED, k, trimTailing,
52
- trimBeginning, GEMINI_30_PRO_IMAGE, IMAGE, JINA, JINA_DEEPSEARCH,
53
- SILICONFLOW, SF_DEEPSEEK_32, OPENROUTER_API, OPENROUTER, AUTO, TOOL, ONLINE,
52
+ trimBeginning, GEMINI_30_PRO_IMAGE, IMAGE, JINA, SILICONFLOW,
53
+ SF_DEEPSEEK_32, OPENROUTER_API, OPENROUTER, AUTO, TOOL, ONLINE,
54
54
  GEMINI_30_PRO, GEMINI_30_FLASH, IMAGEN_4_ULTRA, VEO_31, IMAGEN_4_UPSCALE,
55
55
  ERROR_GENERATING, GEMINI_25_FLASH_TTS, GEMINI_25_PRO_TTS, wav, PNG_EXT,
56
56
  GPT_4O_MIMI_TTS, GPT_4O_TRANSCRIBE, INVALID_AUDIO, OGG_EXT, ELLIPSIS,
57
- TOP_LIMIT, ATTACHMENT, PROCESSING, CURSOR, LN1, LN2, TOP, DEEPSEEK
57
+ TOP_LIMIT, ATTACHMENT, PROCESSING, CURSOR, LN1, LN2, TOP, DEEPSEEK,
58
+ DEEP_RESEARCH_PRO,
58
59
  ] = [
59
60
  'OpenAI', 'Google', 'Ollama', 'nova', 'deepseek-3.2-speciale', '```',
60
61
  'claude-opus-4.5', 'audio', 'wav', 'OPENAI_VOICE', 'medium', 'think',
@@ -64,16 +65,15 @@ const [
64
65
  { role: 'assistant' }, 'json_object', 'Prompt is required.',
65
66
  x => 1000 * x, x => x.replace(/[\.\s]*$/, ''),
66
67
  x => x.replace(/^[\.\s]*/, ''), 'gemini-3-pro-image-preview', 'image',
67
- 'Jina', 'jina-deepsearch-v1', 'SiliconFlow',
68
- 'deepseek-ai/DeepSeek-V3.2-exp', 'https://openrouter.ai/api/v1',
69
- 'OpenRouter', 'openrouter/auto', 'tool', ':online',
70
- 'gemini-3-pro-preview', 'gemini-3-flash-preview',
68
+ 'Jina', 'SiliconFlow', 'deepseek-ai/DeepSeek-V3.2-exp',
69
+ 'https://openrouter.ai/api/v1', 'OpenRouter', 'openrouter/auto', 'tool',
70
+ ':online', 'gemini-3-pro-preview', 'gemini-3-flash-preview',
71
71
  'imagen-4.0-ultra-generate-001', 'veo-3.1-generate-preview',
72
72
  'imagen-4.0-upscale-preview', 'Error generating content.',
73
73
  'gemini-2.5-flash-preview-tts', 'gemini-2.5-pro-preview-tts', 'wav',
74
74
  'png', 'gpt-4o-mini-tts', 'gpt-4o-transcribe', 'Invalid audio data.',
75
75
  'ogg', '...', 3, 'ATTACHMENT', { processing: true }, ' โ–ˆ', '\n\n',
76
- '\n\n\n', 'top', 'DeepSeek',
76
+ '\n\n\n', 'top', 'DeepSeek', 'deep-research-pro-preview-12-2025',
77
77
  ];
78
78
 
79
79
  const [joinL1, joinL2]
@@ -97,12 +97,12 @@ const m = x => k(k(x));
97
97
  const [MAX_TOKENS, ATTACHMENT_TOKEN_COST] = [m(1), k(10)];
98
98
 
99
99
  const MODEL_ICONS = {
100
- [OPENROUTER]: '๐Ÿ”€', [OPENAI]: 'โš›๏ธ', [JINA]: 'โœด๏ธ', [GOOGLE]: 'โ™Š๏ธ',
100
+ [OPENROUTER]: '๐Ÿ”€', [OPENAI]: 'โš›๏ธ', [GOOGLE]: 'โ™Š๏ธ', // [JINA]: 'โœด๏ธ',
101
101
  [OLLAMA]: '๐Ÿฆ™', [ANTHROPIC]: 'โœณ๏ธ', [SILICONFLOW]: '๐Ÿงฌ', [DEEPSEEK]: '๐Ÿฌ',
102
102
  };
103
103
 
104
104
  const FEATURE_ICONS = {
105
- audio: '๐Ÿ”Š', deepsearch: '๐Ÿ”', fast: 'โšก๏ธ', hearing: '๐Ÿ‘‚', hidden: '๐Ÿ™ˆ',
105
+ audio: '๐Ÿ”Š', 'deep-research': '๐Ÿ”', fast: 'โšก๏ธ', hearing: '๐Ÿ‘‚', hidden: '๐Ÿ™ˆ',
106
106
  image: '๐ŸŽจ', reasoning: '๐Ÿง ', structured: '๐Ÿ“Š', tools: '๐Ÿงฐ', video: '๐ŸŽฌ',
107
107
  vision: '๐Ÿ‘๏ธ', // finetune: '๐Ÿ”ง',
108
108
  };
@@ -191,12 +191,10 @@ const MODELS = {
191
191
  source: OPENAI, maxInputTokens: 0,
192
192
  hearing: true, fast: true, hidden: true, defaultProvider: OPENAI,
193
193
  },
194
- // models with deepsearch capabilities
195
- [JINA_DEEPSEARCH]: { // @todo: parse more details from results, eg: "reed urls".
196
- maxInputTokens: Infinity, attachmentTokenCost: 0,
197
- deepsearch: true, reasoning: true, structured: true, vision: true,
198
- supportedMimeTypes: [MIME_PNG, MIME_JPEG, MIME_TEXT, MIME_WEBP, MIME_PDF],
199
- defaultProvider: JINA,
194
+ // agents with deep-research capabilities
195
+ [DEEP_RESEARCH_PRO]: {
196
+ source: GOOGLE, contextWindow: m(1.05), maxOutputTokens: k(65.5),
197
+ 'deep-research': true, reasoning: true, defaultProvider: GOOGLE,
200
198
  },
201
199
  // best Chinese models
202
200
  [DEEPSEEK_32]: DEEPSEEK_32_RULES,
@@ -252,7 +250,7 @@ for (const n in MODELS) {
252
250
  // }
253
251
  // // for other features, if any model supports it, then AUTO supports it
254
252
  // for (const key of [
255
- // 'structured', 'reasoning', 'tools', 'vision', 'fast', 'deepsearch', 'image',
253
+ // 'structured', 'reasoning', 'tools', 'vision', 'fast', 'deep-research', 'image',
256
254
  // ]) {
257
255
  // MODELS[AUTO][key] = MODELS[AUTO][key] || MODELS[n][key];
258
256
  // }
@@ -266,7 +264,6 @@ for (const n in MODELS) {
266
264
  const DEFAULT_MODELS = {
267
265
  [OPENROUTER]: GEMINI_30_FLASH,
268
266
  [SILICONFLOW]: SF_DEEPSEEK_32,
269
- [JINA]: JINA_DEEPSEARCH,
270
267
  [OLLAMA]: GEMMA_3_27B,
271
268
  [OPENAI_VOICE]: NOVA,
272
269
  };
@@ -275,7 +272,7 @@ let _tools;
275
272
 
276
273
  const unifyProvider = provider => {
277
274
  assert(provider = (provider || '').trim(), 'AI provider is required.');
278
- for (let type of [OPENROUTER, GOOGLE, OPENAI, JINA, OLLAMA, SILICONFLOW]) {
275
+ for (let type of [OPENROUTER, GOOGLE, OPENAI, OLLAMA, SILICONFLOW]) { // JINA,
279
276
  if (insensitiveCompare(provider, type)) { return type; }
280
277
  }
281
278
  throwError(`Invalid AI provider: ${provider}.`);
@@ -443,17 +440,6 @@ const init = async (options = {}) => {
443
440
  });
444
441
  }
445
442
  break;
446
- case JINA:
447
- assertApiKey(provider, options);
448
- var { client } = await OpenAI({
449
- baseURL: 'https://deepsearch.jina.ai/v1/', ...options,
450
- });
451
- for (let model of models) {
452
- setupAi({
453
- provider, model, client, prompt: promptOpenRouter, priority,
454
- });
455
- }
456
- break;
457
443
  case OLLAMA:
458
444
  // https://github.com/ollama/ollama/blob/main/docs/openai.md
459
445
  const baseURL = 'http://localhost:11434/v1/';
@@ -687,7 +673,7 @@ const packResp = async (resp, options) => {
687
673
  // "title": "ๅœจ็ทšๆ™‚้˜- ็›ฎๅ‰ๆ™‚้–“- ็ทšไธŠๆ™‚้˜- ๆ™‚้˜็ทšไธŠ - ้ฌง้˜",
688
674
  // "url": "https://naozhong.tw/shijian/",
689
675
  // "content": "- [้ฌง้˜](https://naozhong.tw/)\n- [่จˆๆ™‚ๅ™จ](https://naozhong.tw/jishiqi/)\n- [็ขผ้Œถ](https://naozhong.tw/miaobiao/)\n- [ๆ™‚้–“](https://naozhong.tw/shijian/)\n\n# ็พๅœจๆ™‚้–“\n\nๅŠ ๅ…ฅ\n\n- [็ทจ่ผฏ](javascript:;)\n- [็งป่‡ณ้ ‚็ซฏ](javascript:;)\n- [ไธŠ็งป](javascript:;)\n- [ไธ‹็งป](javascript:;)\n- [ๅˆช้™ค](javascript:;)\n\n# ๆœ€ๅธธ็”จ\n\n| | |\n| --- | --- |\n| [ๅฐๅŒ—](https://naozhong.tw/shijian/%E5%8F%B0%E5%8C%97/) | 10:09:14 |\n| [ๅŒ—ไบฌ๏ผŒไธญๅœ‹](https://naozhong.tw/shijian/%E5%8C%97%E4%BA%AC-%E4%B8%AD%E5%9C%8B/) | 10:09:14 |\n| [ไธŠๆตท๏ผŒไธญๅœ‹](https://naozhong.tw/shijian/%E4%B8%8A%E6%B5%B7-%E4%B8%AD%E5%9C%8B/) | 10:09:14 |\n| [็ƒ้ญฏๆœจ้ฝŠ๏ผŒไธญๅœ‹](https://naozhong.tw/shijian/%E7%83%8F%E9%AD%AF%",
690
- // "dateTime": "2025-03-13 06:48:01" // jina deepsearch only
676
+ // "dateTime": "2025-03-13 06:48:01" // jina deep-research only
691
677
  // }
692
678
  // },
693
679
  // ];
@@ -700,21 +686,21 @@ const packResp = async (resp, options) => {
700
686
  ).join('\n');
701
687
  }
702
688
  txt = txt.split('\n');
703
- const [reJinaStr, reJinaEnd]
704
- = [`^\s*(${THINK_STR})`, `(${THINK_END})\s*$`].map(x => new RegExp(x));
705
- const fixJina = [];
706
- for (let l of txt) {
707
- let catched = false;
708
- if (reJinaStr.test(l)) {
709
- fixJina.push(THINK_STR);
710
- l = l.replace(reJinaStr, '');
711
- }
712
- if (reJinaEnd.test(l)) {
713
- l = l.replace(reJinaEnd, '');
714
- catched = true;
715
- }
716
- fixJina.push(l, ...catched ? [THINK_END, ''] : []);
717
- }
689
+ // const [reJinaStr, reJinaEnd]
690
+ // = [`^\s*(${THINK_STR})`, `(${THINK_END})\s*$`].map(x => new RegExp(x));
691
+ // const fixJina = [];
692
+ // for (let l of txt) {
693
+ // let catched = false;
694
+ // if (reJinaStr.test(l)) {
695
+ // fixJina.push(THINK_STR);
696
+ // l = l.replace(reJinaStr, '');
697
+ // }
698
+ // if (reJinaEnd.test(l)) {
699
+ // l = l.replace(reJinaEnd, '');
700
+ // catched = true;
701
+ // }
702
+ // fixJina.push(l, ...catched ? [THINK_END, ''] : []);
703
+ // }
718
704
  txt = fixJina;
719
705
  for (let i in txt) {
720
706
  switch (txt[i]) {
@@ -732,7 +718,7 @@ const packResp = async (resp, options) => {
732
718
  ...audio ? { audio } : {}, ...images?.length ? { images } : {},
733
719
  processing: !!options?.processing,
734
720
  model: packModelId([
735
- options.provider, options?.router?.provider,
721
+ options?.provider, options?.router?.provider,
736
722
  options?.router?.model || options?.model,
737
723
  ]),
738
724
  };
@@ -1117,6 +1103,60 @@ const promptGoogle = async (aiId, prompt, options = {}) => {
1117
1103
  }, model: packModelId([provider, M.source, M.name]),
1118
1104
  };
1119
1105
  }
1106
+ } else if (M?.['deep-research']) {
1107
+ const pkgOptions = { ...options || {}, provider, model: M.name };
1108
+ let interactionId, last_event_id, isComplete = false;
1109
+ let [thought, text, result] = ['', '', ''];
1110
+ var resp;
1111
+ // Helper to handle the event logic
1112
+ const handleStream = async (stream) => {
1113
+ for await (const chunk of stream) {
1114
+ chunk.event_type === 'interaction.start'
1115
+ && (interactionId = chunk.interaction.id);
1116
+ chunk.event_id && (last_event_id = chunk.event_id);
1117
+ let deltaThought = '', deltaText = '', delta = '';
1118
+ if (chunk.event_type === 'content.delta') {
1119
+ if (chunk.delta.type === 'text') {
1120
+ thought && (deltaThought = `${THINK_END}\n\n`);
1121
+ text += (deltaText = chunk.delta.text);
1122
+ } else if (chunk.delta.type === 'thought_summary') {
1123
+ deltaThought = chunk.delta.content.text;
1124
+ thought || (deltaThought = `${THINK_STR}\n${deltaThought}`);
1125
+ thought += deltaThought;
1126
+ }
1127
+ result += (delta = deltaThought + deltaText);
1128
+ await streamResp({
1129
+ text: options.delta ? delta : result,
1130
+ }, pkgOptions);
1131
+ } else if (chunk.event_type === 'interaction.complete') {
1132
+ isComplete = true;
1133
+ }
1134
+ }
1135
+ };
1136
+ // 1. Start the task with streaming
1137
+ resp = await client.interactions.create({
1138
+ input: prompt, agent: M.name, background: true, store: true,
1139
+ stream: true, // tools: [],
1140
+ agent_config: { type: 'deep-research', thinking_summaries: 'auto' },
1141
+ previous_interaction_id: options?.previous_interaction_id,
1142
+ });
1143
+ await handleStream(resp);
1144
+ // 2. Reconnect Loop
1145
+ while (!isComplete && interactionId) {
1146
+ log(`[DRS] Reconnecting to interaction ${interactionId} from event ${last_event_id}...`);
1147
+ try {
1148
+ resp = await client.interactions.get(interactionId, {
1149
+ stream: true, last_event_id,
1150
+ });
1151
+ await handleStream(resp);
1152
+ } catch (e) {
1153
+ log('[DRS] Reconnection failed, retrying in 2s...');
1154
+ await timeout(2000);
1155
+ }
1156
+ }
1157
+ // 3. Return response
1158
+ options?.raw || (resp = await packResp({ text: result }, pkgOptions));
1159
+ return resp;
1120
1160
  } else {
1121
1161
  throwError('Unsupported model.');
1122
1162
  }
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": "2001.1.87",
4
+ "version": "2001.1.89",
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": "2001.1.87",
4
+ "version": "2001.1.89",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",