utilitas 1998.2.65 → 1998.2.66
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/README.md +3 -3
- package/dist/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/lib/alan.mjs +157 -216
- package/lib/manifest.mjs +1 -1
- package/package.json +1 -1
package/lib/alan.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fileTypeFromBuffer } from 'file-type';
|
|
2
2
|
import { end, loop } from './event.mjs';
|
|
3
3
|
import { createWavHeader } from './media.mjs';
|
|
4
|
-
import
|
|
4
|
+
import { checkSearch, search } from './shot.mjs';
|
|
5
5
|
import { BASE64, BUFFER, DATAURL, MIME_BINARY, STREAM, convert } from './storage.mjs';
|
|
6
6
|
import { create as createUoid } from './uoid.mjs';
|
|
7
7
|
import { distill } from './web.mjs';
|
|
@@ -46,32 +46,31 @@ const _NEED = [
|
|
|
46
46
|
];
|
|
47
47
|
|
|
48
48
|
const [
|
|
49
|
-
OPENAI, GEMINI,
|
|
50
|
-
OLLAMA,
|
|
49
|
+
OPENAI, GEMINI, OPENAI_EMBEDDING, GEMINI_EMEDDING, OPENAI_TRAINING,
|
|
50
|
+
OLLAMA, GPT_4O_MINI, GPT_4O, GPT_O1, GPT_O3_MINI, GEMINI_20_FLASH,
|
|
51
51
|
GEMINI_20_FLASH_THINKING, GEMINI_20_PRO, NOVA, EMBEDDING_001, DEEPSEEK_R1,
|
|
52
|
-
DEEPSEEK_R1_70B, DEEPSEEK_R1_32B, MD_CODE,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
DEEPSEEK_R1_70B, DEEPSEEK_R1_32B, MD_CODE, TEXT_EMBEDDING_3_SMALL,
|
|
53
|
+
TEXT_EMBEDDING_3_LARGE, CLOUD_37_SONNET, AUDIO, WAV, ATTACHMENTS, CHAT,
|
|
54
|
+
OPENAI_VOICE, MEDIUM, LOW, HIGH, GPT_REASONING_EFFORT, THINK, THINK_STR,
|
|
55
|
+
THINK_END, AZURE, TOOLS_STR, TOOLS_END, TOOLS, TEXT, THINKING, OK, FUNC,
|
|
56
|
+
GPT_45, REDACTED_THINKING, GEMMA_3_27B, AZURE_OPENAI, ANTHROPIC,
|
|
57
|
+
VERTEX_ANTHROPIC, GEMMA327B, size8k, ais, MAX_TOOL_RECURSION, LOG, name,
|
|
58
|
+
user, system, assistant, MODEL, JSON_OBJECT, TOOL, silent, NOT_INIT,
|
|
59
|
+
INVALID_FILE, tokenSafeRatio, GPT_QUERY_LIMIT, minsOfDay,
|
|
60
|
+
CONTENT_IS_REQUIRED,
|
|
61
61
|
] = [
|
|
62
|
-
'
|
|
63
|
-
'OPENAI_TRAINING', '
|
|
64
|
-
'
|
|
62
|
+
'OpenAI', 'Gemini', 'OPENAI_EMBEDDING', 'GEMINI_EMEDDING',
|
|
63
|
+
'OPENAI_TRAINING', 'Ollama', 'gpt-4o-mini', 'gpt-4o', 'o1', 'o3-mini',
|
|
64
|
+
'gemini-2.0-flash', 'gemini-2.0-flash-thinking-exp',
|
|
65
65
|
'gemini-2.0-pro-exp', 'nova', 'embedding-001', 'deepseek-r1',
|
|
66
|
-
'deepseek-r1:70b', 'deepseek-r1:32b', '```', '
|
|
67
|
-
'text-embedding-3-
|
|
68
|
-
'claude-3-7-sonnet@20250219', 'audio', 'wav', 'CHATGPT_MINI',
|
|
66
|
+
'deepseek-r1:70b', 'deepseek-r1:32b', '```', 'text-embedding-3-small',
|
|
67
|
+
'text-embedding-3-large', 'claude-3-7-sonnet@20250219', 'audio', 'wav',
|
|
69
68
|
'[ATTACHMENTS]', 'CHAT', 'OPENAI_VOICE', 'medium', 'low', 'high',
|
|
70
69
|
'medium', 'think', '<think>', '</think>', 'AZURE', '<tools>',
|
|
71
70
|
'</tools>', 'tools', 'text', 'thinking', 'OK', 'function',
|
|
72
71
|
'gpt-4.5-preview', 'redacted_thinking', 'gemma-3-27b-it',
|
|
73
72
|
'AZURE OPENAI', 'ANTHROPIC', 'VERTEX ANTHROPIC', 'gemma3:27b',
|
|
74
|
-
7680 * 4320,
|
|
73
|
+
7680 * 4320, [], 10, { log: true }, 'Alan', 'user', 'system',
|
|
75
74
|
'assistant', 'model', 'json_object', 'tool', true,
|
|
76
75
|
'AI engine has not been initialized.', 'Invalid file data.', 1.1, 100,
|
|
77
76
|
60 * 24, 'Content is required.',
|
|
@@ -90,23 +89,16 @@ const [
|
|
|
90
89
|
'text/plain', 'audio/x-wav', 'audio/ogg',
|
|
91
90
|
];
|
|
92
91
|
|
|
93
|
-
const [tool,
|
|
94
|
-
type => ({ type }),
|
|
95
|
-
messages => ({ messages }), text => ({ text }),
|
|
96
|
-
];
|
|
97
|
-
|
|
92
|
+
const [tool, messages, text]
|
|
93
|
+
= [type => ({ type }), messages => ({ messages }), text => ({ text })];
|
|
98
94
|
const [CODE_INTERPRETER, RETRIEVAL, FUNCTION]
|
|
99
95
|
= ['code_interpreter', 'retrieval', FUNC].map(tool);
|
|
100
|
-
const chatConfig
|
|
101
|
-
= { sessions: new Map(), engines: {}, systemPrompt: INSTRUCTIONS };
|
|
102
96
|
const [sessionType, aiType]
|
|
103
97
|
= [`${name.toUpperCase()}-SESSION`, `${name.toUpperCase()}-AI`];
|
|
104
98
|
const [newSessionId, newAiId]
|
|
105
99
|
= [sessionType, aiType].map(type => () => createUoid({ type }));
|
|
100
|
+
const chatConfig = { sessions: new Map(), systemPrompt: INSTRUCTIONS };
|
|
106
101
|
const tokenSafe = count => Math.ceil(count * tokenSafeRatio);
|
|
107
|
-
const clients = {};
|
|
108
|
-
const unifyProvider = options => unifyType(options?.provider, 'AI provider');
|
|
109
|
-
const unifyEngine = options => unifyType(options?.engine, 'AI engine');
|
|
110
102
|
const trimTailing = text => text.replace(/[\.\s]*$/, '');
|
|
111
103
|
const renderText = (t, o) => _renderText(t, { extraCodeBlock: 0, ...o || {} });
|
|
112
104
|
const log = (cnt, opt) => _log(cnt, import.meta.url, { time: 1, ...opt || {} });
|
|
@@ -128,18 +120,8 @@ const DEFAULT_MODELS = {
|
|
|
128
120
|
[OPENAI_EMBEDDING]: TEXT_EMBEDDING_3_SMALL,
|
|
129
121
|
[GEMINI_EMEDDING]: EMBEDDING_001,
|
|
130
122
|
[OPENAI_TRAINING]: GPT_4O_MINI, // https://platform.openai.com/docs/guides/fine-tuning
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
[CHATGPT_MINI]: GPT_4O_MINI,
|
|
134
|
-
[CHATGPT_REASONING]: GPT_O3_MINI,
|
|
135
123
|
};
|
|
136
124
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
125
|
DEFAULT_MODELS[CHAT] = DEFAULT_MODELS[GEMINI];
|
|
144
126
|
|
|
145
127
|
const tokenRatioByWords = Math.min(
|
|
@@ -164,17 +146,14 @@ const MODELS = {
|
|
|
164
146
|
requestLimitsRPM: 10000,
|
|
165
147
|
tokenLimitsTPD: 1000000000,
|
|
166
148
|
tokenLimitsTPM: 10000000,
|
|
167
|
-
|
|
149
|
+
audio: 'gpt-4o-mini-audio-preview',
|
|
150
|
+
fast: true,
|
|
168
151
|
json: true,
|
|
169
|
-
vision: true,
|
|
170
152
|
tools: true,
|
|
171
|
-
|
|
172
|
-
supportedMimeTypes: [
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
supportedAudioTypes: [
|
|
176
|
-
wav,
|
|
177
|
-
],
|
|
153
|
+
vision: true,
|
|
154
|
+
supportedMimeTypes: [png, jpeg, gif, webp],
|
|
155
|
+
supportedAudioTypes: [wav],
|
|
156
|
+
trainingData: 'Oct 2023',
|
|
178
157
|
},
|
|
179
158
|
[GPT_4O]: {
|
|
180
159
|
contextWindow: 128000,
|
|
@@ -183,17 +162,13 @@ const MODELS = {
|
|
|
183
162
|
requestLimitsRPM: 10000,
|
|
184
163
|
tokenLimitsTPD: 20000000,
|
|
185
164
|
tokenLimitsTPM: 2000000,
|
|
186
|
-
|
|
165
|
+
audio: 'gpt-4o-audio-preview',
|
|
187
166
|
json: true,
|
|
188
|
-
vision: true,
|
|
189
167
|
tools: true,
|
|
190
|
-
|
|
191
|
-
supportedMimeTypes: [
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
supportedAudioTypes: [
|
|
195
|
-
wav,
|
|
196
|
-
],
|
|
168
|
+
vision: true,
|
|
169
|
+
supportedMimeTypes: [png, jpeg, gif, webp],
|
|
170
|
+
supportedAudioTypes: [wav],
|
|
171
|
+
trainingData: 'Oct 2023',
|
|
197
172
|
},
|
|
198
173
|
[GPT_O1]: {
|
|
199
174
|
contextWindow: 200000,
|
|
@@ -202,14 +177,14 @@ const MODELS = {
|
|
|
202
177
|
requestLimitsRPM: 10000,
|
|
203
178
|
tokenLimitsTPD: 200000000,
|
|
204
179
|
tokenLimitsTPM: 2000000,
|
|
205
|
-
trainingData: 'Oct 2023',
|
|
206
180
|
json: true,
|
|
207
181
|
reasoning: true,
|
|
208
|
-
vision: true,
|
|
209
182
|
tools: true,
|
|
183
|
+
vision: true,
|
|
210
184
|
supportedMimeTypes: [
|
|
211
185
|
png, jpeg, gif, webp,
|
|
212
186
|
],
|
|
187
|
+
trainingData: 'Oct 2023',
|
|
213
188
|
},
|
|
214
189
|
[GPT_O3_MINI]: {
|
|
215
190
|
contextWindow: 200000,
|
|
@@ -218,14 +193,13 @@ const MODELS = {
|
|
|
218
193
|
requestLimitsRPM: 10000,
|
|
219
194
|
tokenLimitsTPD: 1000000000,
|
|
220
195
|
tokenLimitsTPM: 10000000,
|
|
221
|
-
|
|
196
|
+
fast: true,
|
|
222
197
|
json: true,
|
|
223
198
|
reasoning: true,
|
|
224
|
-
vision: true,
|
|
225
199
|
tools: true,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
200
|
+
vision: true,
|
|
201
|
+
supportedMimeTypes: [png, jpeg, gif, webp],
|
|
202
|
+
trainingData: 'Oct 2023',
|
|
229
203
|
},
|
|
230
204
|
[GPT_45]: {
|
|
231
205
|
contextWindow: 128000,
|
|
@@ -235,20 +209,18 @@ const MODELS = {
|
|
|
235
209
|
tokenLimitsTPD: 100000000,
|
|
236
210
|
tokenLimitsTPM: 1000000,
|
|
237
211
|
json: true,
|
|
238
|
-
vision: true,
|
|
239
212
|
tools: true,
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
],
|
|
213
|
+
vision: true,
|
|
214
|
+
supportedMimeTypes: [png, jpeg, gif, webp],
|
|
243
215
|
trainingData: 'Oct 2023',
|
|
244
216
|
},
|
|
245
217
|
[GEMINI_20_FLASH]: {
|
|
246
218
|
// https://ai.google.dev/gemini-api/docs/models/gemini
|
|
247
219
|
// https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/send-multimodal-prompts?hl=en#gemini-send-multimodal-samples-pdf-nodejs
|
|
248
220
|
// Audio / Video Comming Soon: https://ai.google.dev/gemini-api/docs/models/gemini#gemini-2.0-flash
|
|
221
|
+
audioCostTokens: 1000000, // 8.4 hours => 1 million tokens
|
|
249
222
|
contextWindow: 1048576,
|
|
250
223
|
imageCostTokens: size8k / (768 * 768) * 258,
|
|
251
|
-
audioCostTokens: 1000000, // 8.4 hours => 1 million tokens
|
|
252
224
|
maxAudioLength: 60 * 60 * 8.4, // 9.5 hours
|
|
253
225
|
maxAudioPerPrompt: 1,
|
|
254
226
|
maxFileSize: 20 * 1024 * 1024, // 20 MB
|
|
@@ -260,17 +232,18 @@ const MODELS = {
|
|
|
260
232
|
maxVideoLengthWithAudio: 60 * 50, // 50 minutes
|
|
261
233
|
maxVideoLengthWithoutAudio: 60 * 60, // 1 hour
|
|
262
234
|
maxVideoPerPrompt: 10,
|
|
263
|
-
requestLimitsRPM: 2000,
|
|
264
235
|
requestLimitsRPD: 1500,
|
|
236
|
+
requestLimitsRPM: 2000,
|
|
265
237
|
tokenLimitsTPM: 4 * 1000000,
|
|
266
|
-
|
|
267
|
-
vision: true,
|
|
238
|
+
fast: true,
|
|
268
239
|
json: true,
|
|
269
240
|
tools: true,
|
|
241
|
+
vision: true,
|
|
270
242
|
supportedMimeTypes: [
|
|
271
243
|
png, jpeg, mov, mpeg, mp4, mpg, avi, wmv, mpegps, flv, pdf, aac,
|
|
272
244
|
flac, mp3, m4a, mpga, opus, pcm, wav, webm, tgpp,
|
|
273
245
|
],
|
|
246
|
+
trainingData: 'August 2024',
|
|
274
247
|
},
|
|
275
248
|
[GEMINI_20_FLASH_THINKING]: {
|
|
276
249
|
// https://cloud.google.com/vertex-ai/generative-ai/docs/thinking-mode?hl=en
|
|
@@ -284,12 +257,10 @@ const MODELS = {
|
|
|
284
257
|
requestLimitsRPM: 1000,
|
|
285
258
|
requestLimitsRPD: 1500,
|
|
286
259
|
tokenLimitsTPM: 4 * 1000000,
|
|
287
|
-
trainingData: 'August 2024',
|
|
288
|
-
vision: true,
|
|
289
260
|
reasoning: true,
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
261
|
+
vision: true,
|
|
262
|
+
supportedMimeTypes: [png, jpeg],
|
|
263
|
+
trainingData: 'August 2024',
|
|
293
264
|
},
|
|
294
265
|
[GEMINI_20_PRO]: {
|
|
295
266
|
contextWindow: 2097152,
|
|
@@ -302,21 +273,22 @@ const MODELS = {
|
|
|
302
273
|
requestLimitsRPM: 1000,
|
|
303
274
|
requestLimitsRPD: 1500,
|
|
304
275
|
tokenLimitsTPM: 4 * 1000000,
|
|
305
|
-
trainingData: 'August 2024',
|
|
306
|
-
vision: true,
|
|
307
276
|
json: true,
|
|
277
|
+
vision: true,
|
|
308
278
|
supportedMimeTypes: [
|
|
309
279
|
png, jpeg, mov, mpeg, mp4, mpg, avi, wmv, mpegps, flv, pdf, aac,
|
|
310
280
|
flac, mp3, m4a, mpga, opus, pcm, wav, webm, tgpp,
|
|
311
281
|
],
|
|
282
|
+
trainingData: 'August 2024',
|
|
312
283
|
},
|
|
313
284
|
[GEMMA_3_27B]: {
|
|
314
285
|
contextWindow: 128 * 1000,
|
|
315
286
|
imageCostTokens: 256,
|
|
316
287
|
maxImageSize: 896 * 896,
|
|
317
288
|
maxOutputTokens: 1024 * 8,
|
|
318
|
-
|
|
289
|
+
fast: true,
|
|
319
290
|
json: true,
|
|
291
|
+
vision: true,
|
|
320
292
|
supportedMimeTypes: [png, jpeg],
|
|
321
293
|
},
|
|
322
294
|
[DEEPSEEK_R1]: {
|
|
@@ -360,12 +332,12 @@ const MODELS = {
|
|
|
360
332
|
requestLimitsRPM: 50,
|
|
361
333
|
tokenLimitsITPM: 40000,
|
|
362
334
|
tokenLimitsOTPM: 8000,
|
|
363
|
-
|
|
335
|
+
json: true,
|
|
364
336
|
reasoning: true,
|
|
365
337
|
tools: true,
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
338
|
+
vision: true,
|
|
339
|
+
supportedMimeTypes: [png, jpeg, gif, webp, pdf],
|
|
340
|
+
trainingData: 'Apr 2024',
|
|
369
341
|
},
|
|
370
342
|
};
|
|
371
343
|
|
|
@@ -401,10 +373,13 @@ const MAX_TRIM_TRY = MAX_INPUT_TOKENS / 1000;
|
|
|
401
373
|
|
|
402
374
|
let tokeniser;
|
|
403
375
|
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
376
|
+
const unifyProvider = provider => {
|
|
377
|
+
assert(provider = (provider || '').trim(), 'AI provider is required.');
|
|
378
|
+
for (let type of [OPENAI, AZURE_OPENAI, AZURE, GEMINI, ANTHROPIC,
|
|
379
|
+
VERTEX_ANTHROPIC, OLLAMA]) {
|
|
380
|
+
if (insensitiveCompare(provider, type)) { return type; }
|
|
381
|
+
}
|
|
382
|
+
throwError(`Invalid AI provider: ${provider}.`);
|
|
408
383
|
};
|
|
409
384
|
|
|
410
385
|
const tools = [
|
|
@@ -502,8 +477,8 @@ const toolsGemini = async () => (await toolsOpenAI()).map(x => ({
|
|
|
502
477
|
}));
|
|
503
478
|
|
|
504
479
|
const init = async (options = {}) => {
|
|
505
|
-
const id = newAiId();
|
|
506
|
-
const provider = unifyProvider(options);
|
|
480
|
+
const id = options.id || newAiId();
|
|
481
|
+
const provider = unifyProvider(options?.provider);
|
|
507
482
|
const modelName = options.model || DEFAULT_MODELS[provider];
|
|
508
483
|
assert(modelName, `Model is required for provider: ${provider}.`);
|
|
509
484
|
let model = options.modelConfig || MODELS[modelName];
|
|
@@ -512,49 +487,49 @@ const init = async (options = {}) => {
|
|
|
512
487
|
switch (provider) {
|
|
513
488
|
case OPENAI:
|
|
514
489
|
assertApiKey(provider, options);
|
|
515
|
-
ais
|
|
490
|
+
ais.push({
|
|
516
491
|
id, provider, model, client: await OpenAI(options),
|
|
517
492
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
518
493
|
embedding: async (i, o) => await createOpenAIEmbedding(id, i, o),
|
|
519
|
-
};
|
|
494
|
+
});
|
|
520
495
|
break;
|
|
521
496
|
case AZURE_OPENAI:
|
|
522
497
|
assertApiKey(provider, options);
|
|
523
498
|
assert(options.endpoint,
|
|
524
|
-
|
|
525
|
-
ais
|
|
499
|
+
`${provider} api endpoint and deployment are required.`);
|
|
500
|
+
ais.push({
|
|
526
501
|
id, provider, model, client: await AzureOpenAI({
|
|
527
502
|
apiVersion: '2025-01-01-preview',
|
|
528
503
|
deployment: model.name, ...options,
|
|
529
504
|
}),
|
|
530
505
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
531
|
-
};
|
|
506
|
+
});
|
|
532
507
|
break;
|
|
533
508
|
case AZURE:
|
|
534
509
|
assertApiKey(provider, options);
|
|
535
510
|
assert(options.baseURL, `${provider} api endpoint is required.`);
|
|
536
|
-
ais
|
|
511
|
+
ais.push({
|
|
537
512
|
id, provider, model, client: await OpenAI(options),
|
|
538
513
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
539
|
-
};
|
|
514
|
+
});
|
|
540
515
|
break;
|
|
541
516
|
case GEMINI:
|
|
542
517
|
assertApiKey(provider, options);
|
|
543
518
|
const { GoogleGenerativeAI } = await need('@google/generative-ai');
|
|
544
|
-
ais
|
|
519
|
+
ais.push({
|
|
545
520
|
id, provider, model,
|
|
546
521
|
client: new GoogleGenerativeAI(options.apiKey),
|
|
547
522
|
prompt: async (cnt, opts) => await promptGemini(id, cnt, opts),
|
|
548
523
|
embedding: async (i, o) => await createGeminiEmbedding(id, i, o),
|
|
549
|
-
};
|
|
524
|
+
});
|
|
550
525
|
break;
|
|
551
526
|
case ANTHROPIC:
|
|
552
527
|
assertApiKey(provider, options);
|
|
553
528
|
const Anthropic = (await need('@anthropic-ai/sdk')).Anthropic;
|
|
554
|
-
ais
|
|
529
|
+
ais.push({
|
|
555
530
|
id, provider, model, client: new Anthropic(options),
|
|
556
531
|
prompt: async (cnt, opts) => await promptAnthropic(id, cnt, opts),
|
|
557
|
-
};
|
|
532
|
+
});
|
|
558
533
|
break;
|
|
559
534
|
case VERTEX_ANTHROPIC:
|
|
560
535
|
// https://github.com/anthropics/anthropic-sdk-typescript/tree/main/packages/vertex-sdk
|
|
@@ -562,21 +537,21 @@ const init = async (options = {}) => {
|
|
|
562
537
|
const AnthropicVertex = (await need('@anthropic-ai/vertex-sdk')).AnthropicVertex;
|
|
563
538
|
process.env['GOOGLE_APPLICATION_CREDENTIALS'] = options.credentials;
|
|
564
539
|
process.env['ANTHROPIC_VERTEX_PROJECT_ID'] = options.projectId;
|
|
565
|
-
ais
|
|
540
|
+
ais.push({
|
|
566
541
|
id, provider, model,
|
|
567
542
|
client: new AnthropicVertex({ region: options?.region || 'us-east5' }),
|
|
568
543
|
prompt: async (cnt, opts) => await promptAnthropic(id, cnt, opts),
|
|
569
|
-
};
|
|
544
|
+
});
|
|
570
545
|
break;
|
|
571
546
|
case OLLAMA:
|
|
572
547
|
// https://github.com/ollama/ollama/blob/main/docs/openai.md
|
|
573
548
|
const baseURL = 'http://localhost:11434/v1/';
|
|
574
|
-
ais
|
|
549
|
+
ais.push({
|
|
575
550
|
id, provider, model, client: await OpenAI({
|
|
576
551
|
baseURL, apiKey: 'ollama', ...options
|
|
577
552
|
}),
|
|
578
553
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
579
|
-
};
|
|
554
|
+
});
|
|
580
555
|
const phLog = m => log(`Ollama preheat: ${m?.message || m}`);
|
|
581
556
|
ignoreErrFunc(async () => {
|
|
582
557
|
phLog(await (await fetch(`${baseURL}completions`, {
|
|
@@ -589,15 +564,32 @@ const init = async (options = {}) => {
|
|
|
589
564
|
default:
|
|
590
565
|
throwError(`Invalid AI provider: ${options.provider || 'null'}.`);
|
|
591
566
|
}
|
|
592
|
-
return ais
|
|
567
|
+
return ais.find(x => x.id === id);
|
|
593
568
|
};
|
|
594
569
|
|
|
595
|
-
const getAi = async (id, options) => {
|
|
570
|
+
const getAi = async (id, options = {}) => {
|
|
596
571
|
if (id) {
|
|
597
|
-
|
|
598
|
-
|
|
572
|
+
const ai = ais.find(x => x.id === id);
|
|
573
|
+
assert(ais, `AI not found: ${id}.`);
|
|
574
|
+
return options?.client ? ai?.client : ai;
|
|
575
|
+
} else if (options?.select) {
|
|
576
|
+
const res = [];
|
|
577
|
+
for (let x of ais) {
|
|
578
|
+
let select = true;
|
|
579
|
+
for (let i in options.select) {
|
|
580
|
+
if (options.select[i] && i !== 'fast' && !x.model[i]) {
|
|
581
|
+
select = false; break;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
select && (res.push(x));
|
|
585
|
+
}
|
|
586
|
+
const best = options.select?.fast ? res.filter(x => x.model.fast) : res;
|
|
587
|
+
if (best.length) { return options.all ? best : best[0]; }
|
|
588
|
+
assert(res.length, 'AI not found.');
|
|
589
|
+
log(`Best match AI not found, fallbacked: ${JSON.stringify(options.select)}.`);
|
|
590
|
+
return options.all ? res : res[0];
|
|
599
591
|
}
|
|
600
|
-
return ais;
|
|
592
|
+
return options.all ? ais : ais[0];
|
|
601
593
|
};
|
|
602
594
|
|
|
603
595
|
const countTokens = async (input, options) => {
|
|
@@ -822,11 +814,11 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
822
814
|
...model?.supportedMimeTypes || [], ...model.supportedAudioTypes || []
|
|
823
815
|
].includes(x.mime_type));
|
|
824
816
|
switch (options.flavor) {
|
|
825
|
-
case
|
|
817
|
+
case OPENAI:
|
|
826
818
|
systemPrompt = buildGptMessage(options.systemPrompt, _system);
|
|
827
819
|
prompt = buildGptMessage(content, options);
|
|
828
820
|
break;
|
|
829
|
-
case
|
|
821
|
+
case ANTHROPIC:
|
|
830
822
|
systemPrompt = options.systemPrompt;
|
|
831
823
|
prompt = buildClaudeMessage(content, { ...options, cache_control: true });
|
|
832
824
|
break;
|
|
@@ -841,11 +833,11 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
841
833
|
history = [];
|
|
842
834
|
(options.messages?.length ? options.messages : []).map((x, i) => {
|
|
843
835
|
switch (options.flavor) {
|
|
844
|
-
case
|
|
836
|
+
case OPENAI:
|
|
845
837
|
history.push(buildGptMessage(x.request, _user));
|
|
846
838
|
history.push(buildGptMessage(x.response, _assistant));
|
|
847
839
|
break;
|
|
848
|
-
case
|
|
840
|
+
case ANTHROPIC:
|
|
849
841
|
history.push(buildClaudeMessage(x.request, _user));
|
|
850
842
|
history.push(buildClaudeMessage(x.response, _assistant));
|
|
851
843
|
break;
|
|
@@ -857,13 +849,13 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
857
849
|
}
|
|
858
850
|
});
|
|
859
851
|
switch (options.flavor) {
|
|
860
|
-
case
|
|
852
|
+
case OPENAI:
|
|
861
853
|
history = messages([
|
|
862
854
|
systemPrompt, ...history, prompt,
|
|
863
855
|
...options.toolsResult?.length ? options.toolsResult : []
|
|
864
856
|
]);
|
|
865
857
|
break;
|
|
866
|
-
case
|
|
858
|
+
case ANTHROPIC:
|
|
867
859
|
history = messages([
|
|
868
860
|
...history, prompt,
|
|
869
861
|
...options.toolsResult?.length ? options.toolsResult : []
|
|
@@ -889,7 +881,7 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
889
881
|
content = trimTailing(trimTailing(content).slice(0, -1)) + '...';
|
|
890
882
|
}
|
|
891
883
|
}, model.maxInputTokens - options.attachments?.length * ATTACHMENT_TOKEN_COST);
|
|
892
|
-
if ([
|
|
884
|
+
if ([OPENAI].includes(options.flavor) || options.model === GEMMA_3_27B) {
|
|
893
885
|
systemPrompt = null;
|
|
894
886
|
}
|
|
895
887
|
return { systemPrompt, history, prompt };
|
|
@@ -910,13 +902,13 @@ const handleToolsCall = async (msg, options) => {
|
|
|
910
902
|
const calls = msg.tool_calls || msg.content || msg.parts || [];
|
|
911
903
|
if (calls.length) {
|
|
912
904
|
switch (options?.flavor) {
|
|
913
|
-
case
|
|
905
|
+
case ANTHROPIC: preRes.push(msg); break;
|
|
914
906
|
case GEMINI: preRes.push(msg); break;
|
|
915
|
-
case
|
|
907
|
+
case OPENAI: default: preRes.push(msg); break;
|
|
916
908
|
}
|
|
917
909
|
for (const fn of calls) {
|
|
918
910
|
switch (options?.flavor) {
|
|
919
|
-
case
|
|
911
|
+
case ANTHROPIC:
|
|
920
912
|
input = fn.input = String.isString(fn?.input)
|
|
921
913
|
? parseJson(fn.input) : fn?.input;
|
|
922
914
|
packMsg = (content, is_error) => ({
|
|
@@ -935,7 +927,7 @@ const handleToolsCall = async (msg, options) => {
|
|
|
935
927
|
}
|
|
936
928
|
});
|
|
937
929
|
break;
|
|
938
|
-
case
|
|
930
|
+
case OPENAI: default:
|
|
939
931
|
input = parseJson(fn?.function?.arguments);
|
|
940
932
|
packMsg = (content = '', e = false) => ({
|
|
941
933
|
role: TOOL, tool_call_id: fn.id,
|
|
@@ -972,7 +964,7 @@ const handleToolsCall = async (msg, options) => {
|
|
|
972
964
|
}
|
|
973
965
|
if (content.length) {
|
|
974
966
|
switch (options?.flavor) {
|
|
975
|
-
case
|
|
967
|
+
case ANTHROPIC: content = [{ role: user, content }]; break;
|
|
976
968
|
case GEMINI: content = [{ role: FUNC, parts: content }]; break;
|
|
977
969
|
}
|
|
978
970
|
}
|
|
@@ -994,7 +986,7 @@ const promptOpenAI = async (aiId, content, options = {}) => {
|
|
|
994
986
|
options.result ?? '', Buffer.alloc(0), null, [], false,
|
|
995
987
|
provider === AZURE
|
|
996
988
|
];
|
|
997
|
-
options.flavor =
|
|
989
|
+
options.flavor = OPENAI;
|
|
998
990
|
options.model = options.model || model.name;
|
|
999
991
|
const { history }
|
|
1000
992
|
= await buildPrompts(MODELS[options.model], content, options);
|
|
@@ -1073,7 +1065,7 @@ const promptAnthropic = async (aiId, content, options = {}) => {
|
|
|
1073
1065
|
+ '46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB'
|
|
1074
1066
|
); // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking
|
|
1075
1067
|
const { systemPrompt: system, history }
|
|
1076
|
-
= await buildPrompts(model, content, { ...options, flavor:
|
|
1068
|
+
= await buildPrompts(model, content, { ...options, flavor: ANTHROPIC });
|
|
1077
1069
|
const resp = await client.beta.messages.create({
|
|
1078
1070
|
model: options.model, ...history, system, stream: true,
|
|
1079
1071
|
max_tokens: options.extendedThinking ? 128000 : model.maxOutputTokens,
|
|
@@ -1124,7 +1116,7 @@ const promptAnthropic = async (aiId, content, options = {}) => {
|
|
|
1124
1116
|
]
|
|
1125
1117
|
};
|
|
1126
1118
|
const { toolsResult, toolsResponse } = await handleToolsCall(
|
|
1127
|
-
event, { ...options, result, flavor:
|
|
1119
|
+
event, { ...options, result, flavor: ANTHROPIC },
|
|
1128
1120
|
);
|
|
1129
1121
|
if (tool_use.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
|
|
1130
1122
|
return await promptAnthropic(aiId, content, {
|
|
@@ -1326,7 +1318,7 @@ const listGptFineTuningEvents = async (aiId, job_id, options) => {
|
|
|
1326
1318
|
|
|
1327
1319
|
const tailGptFineTuningEvents = async (aiId, job_id, options) => {
|
|
1328
1320
|
assert(job_id, 'Job ID is required.');
|
|
1329
|
-
const [loopName, listOpts] = [`GPT
|
|
1321
|
+
const [loopName, listOpts] = [`GPT-${job_id}`, {
|
|
1330
1322
|
...options, params: { ...options?.params, order: 'ascending' }
|
|
1331
1323
|
}];
|
|
1332
1324
|
let lastEvent;
|
|
@@ -1344,33 +1336,23 @@ const tailGptFineTuningEvents = async (aiId, job_id, options) => {
|
|
|
1344
1336
|
}, 3, 2, 1, loopName, { silent, ...options });
|
|
1345
1337
|
};
|
|
1346
1338
|
|
|
1347
|
-
const initChat = async (options) => {
|
|
1348
|
-
options
|
|
1349
|
-
engines: options?.engines || { [DEFAULT_MODELS[CHAT]]: {} },
|
|
1350
|
-
...options || {},
|
|
1351
|
-
};
|
|
1352
|
-
if (options?.sessions) {
|
|
1339
|
+
const initChat = async (options = {}) => {
|
|
1340
|
+
if (options.sessions) {
|
|
1353
1341
|
assert(
|
|
1354
1342
|
options.sessions?.get && options.sessions?.set,
|
|
1355
1343
|
'Invalid session storage provider.'
|
|
1356
1344
|
);
|
|
1357
1345
|
chatConfig.sessions = options.sessions;
|
|
1358
1346
|
}
|
|
1359
|
-
options
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
chatConfig.systemPrompt, { role: system }
|
|
1369
|
-
)]); // Use Gemini instead of ChatGPT because of the longer pack
|
|
1370
|
-
assert(
|
|
1371
|
-
pmptTokens < mxPmpt,
|
|
1372
|
-
`System prompt is too long: ${pmptTokens} / ${mxPmpt} tokens.`
|
|
1373
|
-
);
|
|
1347
|
+
options.instructions && (chatConfig.systemPrompt = options.instructions);
|
|
1348
|
+
// Use Gemini instead of ChatGPT because of the longer package.
|
|
1349
|
+
const [spTokens, ais] = await Promise.all([countTokens([buildGeminiHistory(
|
|
1350
|
+
chatConfig.systemPrompt, { role: system }
|
|
1351
|
+
)]), getAi(null, { all: true })]);
|
|
1352
|
+
for (const ai of ais) {
|
|
1353
|
+
const mxPmpt = ai.model.maxInputTokens / 2;
|
|
1354
|
+
assert(spTokens < mxPmpt,
|
|
1355
|
+
`System prompt is too long: ${spTokens} / ${mxPmpt} tokens.`);
|
|
1374
1356
|
}
|
|
1375
1357
|
return chatConfig;
|
|
1376
1358
|
};
|
|
@@ -1406,53 +1388,30 @@ const resetSession = async (sessionId, options) => {
|
|
|
1406
1388
|
return await setSession(sessionId, session);
|
|
1407
1389
|
};
|
|
1408
1390
|
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
// log(result);
|
|
1417
|
-
return result;
|
|
1418
|
-
};
|
|
1419
|
-
|
|
1420
|
-
const talk = async (input, options) => {
|
|
1421
|
-
let [engine, chat, resp, sessionId] = [
|
|
1422
|
-
unifyEngine({
|
|
1423
|
-
engine: Object.keys(chatConfig.engines)?.[0] || DEFAULT_MODELS[CHAT],
|
|
1424
|
-
...options,
|
|
1425
|
-
}), { request: input || ATTACHMENTS }, null,
|
|
1426
|
-
options?.sessionId || newSessionId(),
|
|
1427
|
-
];
|
|
1428
|
-
assert(chatConfig.engines[engine], NOT_INIT);
|
|
1429
|
-
const session = await getSession(sessionId, { engine, ...options });
|
|
1430
|
-
log(`Prompt (${engine}): ${JSON.stringify(input)}`);
|
|
1431
|
-
const pmtOptions = {
|
|
1432
|
-
messages: session.messages, model: chatConfig.engines[engine].model,
|
|
1433
|
-
...options,
|
|
1434
|
-
};
|
|
1435
|
-
switch (engine) {
|
|
1436
|
-
case CHATGPT: resp = await promptOpenAI(input, pmtOptions); break;
|
|
1437
|
-
case GEMINI: resp = await promptGemini(input, pmtOptions); break;
|
|
1438
|
-
case CLAUDE: resp = await promptAnthropic(input, pmtOptions); break;
|
|
1439
|
-
// case OLLAMA: resp = await promptOllama(input, pmtOptions); break;
|
|
1440
|
-
case AZURE: resp = await promptAzure(input, pmtOptions); break;
|
|
1441
|
-
default: throwError(`Invalid AI engine: '${engine}'.`);
|
|
1442
|
-
}
|
|
1391
|
+
const talk = async (input, options = {}) => {
|
|
1392
|
+
let [chat, sessionId] =
|
|
1393
|
+
[{ request: input || ATTACHMENTS }, options.sessionId || newSessionId()];
|
|
1394
|
+
const session = await getSession(sessionId, options);
|
|
1395
|
+
const resp = await prompt(input, {
|
|
1396
|
+
messages: session.messages, log: true, ...options,
|
|
1397
|
+
});
|
|
1443
1398
|
chat.response = resp.text;
|
|
1444
1399
|
chat.request && chat.response && session.messages.push(chat);
|
|
1445
1400
|
await setSession(sessionId, session, options);
|
|
1446
|
-
return {
|
|
1401
|
+
return {
|
|
1402
|
+
sessionId, ...resp, spoken: renderText(
|
|
1403
|
+
resp.text, { noCode: true, noLink: true }
|
|
1404
|
+
).replace(/\[\^\d\^\]/ig, ''),
|
|
1405
|
+
};
|
|
1447
1406
|
};
|
|
1448
1407
|
|
|
1449
|
-
const getMaxChatPromptLimit = (options) => {
|
|
1408
|
+
const getMaxChatPromptLimit = async (options) => {
|
|
1450
1409
|
let resp = 0;
|
|
1451
|
-
|
|
1452
|
-
if (options?.
|
|
1453
|
-
const maxInputTokens =
|
|
1410
|
+
(await getAi(null, { all: true })).map(x => {
|
|
1411
|
+
if (options?.aiId && options?.aiId !== x.id) { return; }
|
|
1412
|
+
const maxInputTokens = x.model.maxInputTokens;
|
|
1454
1413
|
resp = resp ? Math.min(resp, maxInputTokens) : maxInputTokens;
|
|
1455
|
-
}
|
|
1414
|
+
});
|
|
1456
1415
|
assert(resp > 0, 'Chat engine has not been initialized.');
|
|
1457
1416
|
return options?.raw ? resp : Math.min(resp, MAX_INPUT_TOKENS);
|
|
1458
1417
|
};
|
|
@@ -1486,28 +1445,17 @@ const distillFile = async (attachments, o) => {
|
|
|
1486
1445
|
attachments = await Promise.all(attachments);
|
|
1487
1446
|
// print(attachments);
|
|
1488
1447
|
return await prompt(strPmt, {
|
|
1489
|
-
|
|
1448
|
+
simple: true, select: { vision: true, fast: true }, ...o, attachments,
|
|
1490
1449
|
});
|
|
1491
1450
|
};
|
|
1492
1451
|
|
|
1493
|
-
const prompt = async (input, options) => {
|
|
1494
|
-
|
|
1495
|
-
const
|
|
1496
|
-
options
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
if (engine.client === OPENAI) {
|
|
1501
|
-
if (options?.fast) {
|
|
1502
|
-
extra.model = DEFAULT_MODELS[CHATGPT_MINI];
|
|
1503
|
-
} else if (options?.reasoning) {
|
|
1504
|
-
extra.model = DEFAULT_MODELS[CHATGPT_REASONING];
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
return await engine.func(input, { ...extra, ...options || {} });
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
throwError('No AI provider is available.');
|
|
1452
|
+
const prompt = async (input, options = {}) => {
|
|
1453
|
+
const ai = await getAi(options?.aiId, options);
|
|
1454
|
+
const tag = `${ai.provider} (${ai.model.name})`;
|
|
1455
|
+
options.log && log(`Prompt ${tag}: ${JSON.stringify(input)}`);
|
|
1456
|
+
const resp = await ai.prompt(input, options);
|
|
1457
|
+
options.log && log(`Response ${tag}: ${JSON.stringify(resp.text)}`);
|
|
1458
|
+
return resp;
|
|
1511
1459
|
};
|
|
1512
1460
|
|
|
1513
1461
|
const trimPrompt = async (getPrompt, trimFunc, contextWindow, options) => {
|
|
@@ -1549,23 +1497,16 @@ const analyzeSessions = async (sessionIds, options) => {
|
|
|
1549
1497
|
x, JSON.stringify(sses[x]).length,
|
|
1550
1498
|
]).sort((x, y) => y[1] - x[1])[0][0]];
|
|
1551
1499
|
}
|
|
1552
|
-
}, getMaxChatPromptLimit(options));
|
|
1500
|
+
}, await getMaxChatPromptLimit(options));
|
|
1553
1501
|
const aiResp = Object.keys(sses) ? (await prompt(getInput(), {
|
|
1554
|
-
jsonMode: true,
|
|
1502
|
+
jsonMode: true, simple: true, select: { json: true, fase: true },
|
|
1503
|
+
...options || {}
|
|
1555
1504
|
})) : {};
|
|
1556
1505
|
assert(aiResp, 'Unable to analyze sessions.');
|
|
1557
1506
|
ids.map(x => resp[x] = aiResp[x] || null);
|
|
1558
1507
|
return Array.isArray(sessionIds) ? resp : resp[sessionIds[0]];
|
|
1559
1508
|
};
|
|
1560
1509
|
|
|
1561
|
-
const PREFERRED_ENGINES = [
|
|
1562
|
-
{ client: OPENAI, func: promptOpenAI, multimodal: 0 },
|
|
1563
|
-
{ client: GEMINI, func: promptGemini, multimodal: 1 },
|
|
1564
|
-
{ client: CLAUDE, func: promptAnthropic, multimodal: 2 },
|
|
1565
|
-
// { client: AZURE, func: promptAzure, multimodal: 3 },
|
|
1566
|
-
// { client: OLLAMA, func: promptOllama, multimodal: 99 },
|
|
1567
|
-
]; // keep gpt first to avoid gemini grounding by default
|
|
1568
|
-
|
|
1569
1510
|
export default init;
|
|
1570
1511
|
export {
|
|
1571
1512
|
ATTACHMENT_TOKEN_COST, CLOUD_37_SONNET, CODE_INTERPRETER, DEEPSEEK_R1,
|