utilitas 1998.2.65 → 1999.1.2
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 +170 -225
- 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,53 @@ const init = async (options = {}) => {
|
|
|
512
487
|
switch (provider) {
|
|
513
488
|
case OPENAI:
|
|
514
489
|
assertApiKey(provider, options);
|
|
515
|
-
ais
|
|
516
|
-
id, provider, model,
|
|
490
|
+
ais.push({
|
|
491
|
+
id, provider, model, priority: 0, initOrder: ais.length,
|
|
492
|
+
client: await OpenAI(options),
|
|
517
493
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
518
494
|
embedding: async (i, o) => await createOpenAIEmbedding(id, i, o),
|
|
519
|
-
};
|
|
495
|
+
});
|
|
520
496
|
break;
|
|
521
497
|
case AZURE_OPENAI:
|
|
522
498
|
assertApiKey(provider, options);
|
|
523
499
|
assert(options.endpoint,
|
|
524
|
-
|
|
525
|
-
ais
|
|
526
|
-
id, provider, model,
|
|
500
|
+
`${provider} api endpoint and deployment are required.`);
|
|
501
|
+
ais.push({
|
|
502
|
+
id, provider, model, priority: 0, initOrder: ais.length,
|
|
503
|
+
client: await AzureOpenAI({
|
|
527
504
|
apiVersion: '2025-01-01-preview',
|
|
528
505
|
deployment: model.name, ...options,
|
|
529
506
|
}),
|
|
530
507
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
531
|
-
};
|
|
508
|
+
});
|
|
532
509
|
break;
|
|
533
510
|
case AZURE:
|
|
534
511
|
assertApiKey(provider, options);
|
|
535
512
|
assert(options.baseURL, `${provider} api endpoint is required.`);
|
|
536
|
-
ais
|
|
537
|
-
id, provider, model,
|
|
513
|
+
ais.push({
|
|
514
|
+
id, provider, model, priority: 0, initOrder: ais.length,
|
|
515
|
+
client: await OpenAI(options),
|
|
538
516
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
539
|
-
};
|
|
517
|
+
});
|
|
540
518
|
break;
|
|
541
519
|
case GEMINI:
|
|
542
520
|
assertApiKey(provider, options);
|
|
543
521
|
const { GoogleGenerativeAI } = await need('@google/generative-ai');
|
|
544
|
-
ais
|
|
545
|
-
id, provider, model,
|
|
522
|
+
ais.push({
|
|
523
|
+
id, provider, model, priority: 0, initOrder: ais.length,
|
|
546
524
|
client: new GoogleGenerativeAI(options.apiKey),
|
|
547
525
|
prompt: async (cnt, opts) => await promptGemini(id, cnt, opts),
|
|
548
526
|
embedding: async (i, o) => await createGeminiEmbedding(id, i, o),
|
|
549
|
-
};
|
|
527
|
+
});
|
|
550
528
|
break;
|
|
551
529
|
case ANTHROPIC:
|
|
552
530
|
assertApiKey(provider, options);
|
|
553
531
|
const Anthropic = (await need('@anthropic-ai/sdk')).Anthropic;
|
|
554
|
-
ais
|
|
555
|
-
id, provider, model,
|
|
532
|
+
ais.push({
|
|
533
|
+
id, provider, model, priority: 0, initOrder: ais.length,
|
|
534
|
+
client: new Anthropic(options),
|
|
556
535
|
prompt: async (cnt, opts) => await promptAnthropic(id, cnt, opts),
|
|
557
|
-
};
|
|
536
|
+
});
|
|
558
537
|
break;
|
|
559
538
|
case VERTEX_ANTHROPIC:
|
|
560
539
|
// https://github.com/anthropics/anthropic-sdk-typescript/tree/main/packages/vertex-sdk
|
|
@@ -562,21 +541,20 @@ const init = async (options = {}) => {
|
|
|
562
541
|
const AnthropicVertex = (await need('@anthropic-ai/vertex-sdk')).AnthropicVertex;
|
|
563
542
|
process.env['GOOGLE_APPLICATION_CREDENTIALS'] = options.credentials;
|
|
564
543
|
process.env['ANTHROPIC_VERTEX_PROJECT_ID'] = options.projectId;
|
|
565
|
-
ais
|
|
566
|
-
id, provider, model,
|
|
544
|
+
ais.push({
|
|
545
|
+
id, provider, model, priority: 0, initOrder: ais.length,
|
|
567
546
|
client: new AnthropicVertex({ region: options?.region || 'us-east5' }),
|
|
568
547
|
prompt: async (cnt, opts) => await promptAnthropic(id, cnt, opts),
|
|
569
|
-
};
|
|
548
|
+
});
|
|
570
549
|
break;
|
|
571
550
|
case OLLAMA:
|
|
572
551
|
// https://github.com/ollama/ollama/blob/main/docs/openai.md
|
|
573
552
|
const baseURL = 'http://localhost:11434/v1/';
|
|
574
|
-
ais
|
|
575
|
-
id, provider, model,
|
|
576
|
-
|
|
577
|
-
}),
|
|
553
|
+
ais.push({
|
|
554
|
+
id, provider, model, priority: 0, initOrder: ais.length,
|
|
555
|
+
client: await OpenAI({ baseURL, apiKey: 'ollama', ...options }),
|
|
578
556
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
579
|
-
};
|
|
557
|
+
});
|
|
580
558
|
const phLog = m => log(`Ollama preheat: ${m?.message || m}`);
|
|
581
559
|
ignoreErrFunc(async () => {
|
|
582
560
|
phLog(await (await fetch(`${baseURL}completions`, {
|
|
@@ -589,15 +567,33 @@ const init = async (options = {}) => {
|
|
|
589
567
|
default:
|
|
590
568
|
throwError(`Invalid AI provider: ${options.provider || 'null'}.`);
|
|
591
569
|
}
|
|
592
|
-
|
|
570
|
+
ais.sort((a, b) => a.priority - b.priority || a.initOrder - b.initOrder);
|
|
571
|
+
return ais.find(x => x.id === id);
|
|
593
572
|
};
|
|
594
573
|
|
|
595
|
-
const getAi = async (id, options) => {
|
|
574
|
+
const getAi = async (id, options = {}) => {
|
|
596
575
|
if (id) {
|
|
597
|
-
|
|
598
|
-
|
|
576
|
+
const ai = ais.find(x => x.id === id);
|
|
577
|
+
assert(ais, `AI not found: ${id}.`);
|
|
578
|
+
return options?.client ? ai?.client : ai;
|
|
579
|
+
} else if (options?.select) {
|
|
580
|
+
const res = [];
|
|
581
|
+
for (let x of ais) {
|
|
582
|
+
let select = true;
|
|
583
|
+
for (let i in options.select) {
|
|
584
|
+
if (options.select[i] && i !== 'fast' && !x.model[i]) {
|
|
585
|
+
select = false; break;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
select && (res.push(x));
|
|
589
|
+
}
|
|
590
|
+
const best = options.select?.fast ? res.filter(x => x.model.fast) : res;
|
|
591
|
+
if (best.length) { return options.all ? best : best[0]; }
|
|
592
|
+
assert(res.length, 'AI not found.');
|
|
593
|
+
log(`Best match AI not found, fallbacked: ${JSON.stringify(options.select)}.`);
|
|
594
|
+
return options.all ? res : res[0];
|
|
599
595
|
}
|
|
600
|
-
return ais;
|
|
596
|
+
return options.all ? ais : ais[0];
|
|
601
597
|
};
|
|
602
598
|
|
|
603
599
|
const countTokens = async (input, options) => {
|
|
@@ -822,11 +818,11 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
822
818
|
...model?.supportedMimeTypes || [], ...model.supportedAudioTypes || []
|
|
823
819
|
].includes(x.mime_type));
|
|
824
820
|
switch (options.flavor) {
|
|
825
|
-
case
|
|
821
|
+
case OPENAI:
|
|
826
822
|
systemPrompt = buildGptMessage(options.systemPrompt, _system);
|
|
827
823
|
prompt = buildGptMessage(content, options);
|
|
828
824
|
break;
|
|
829
|
-
case
|
|
825
|
+
case ANTHROPIC:
|
|
830
826
|
systemPrompt = options.systemPrompt;
|
|
831
827
|
prompt = buildClaudeMessage(content, { ...options, cache_control: true });
|
|
832
828
|
break;
|
|
@@ -841,11 +837,11 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
841
837
|
history = [];
|
|
842
838
|
(options.messages?.length ? options.messages : []).map((x, i) => {
|
|
843
839
|
switch (options.flavor) {
|
|
844
|
-
case
|
|
840
|
+
case OPENAI:
|
|
845
841
|
history.push(buildGptMessage(x.request, _user));
|
|
846
842
|
history.push(buildGptMessage(x.response, _assistant));
|
|
847
843
|
break;
|
|
848
|
-
case
|
|
844
|
+
case ANTHROPIC:
|
|
849
845
|
history.push(buildClaudeMessage(x.request, _user));
|
|
850
846
|
history.push(buildClaudeMessage(x.response, _assistant));
|
|
851
847
|
break;
|
|
@@ -857,13 +853,13 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
857
853
|
}
|
|
858
854
|
});
|
|
859
855
|
switch (options.flavor) {
|
|
860
|
-
case
|
|
856
|
+
case OPENAI:
|
|
861
857
|
history = messages([
|
|
862
858
|
systemPrompt, ...history, prompt,
|
|
863
859
|
...options.toolsResult?.length ? options.toolsResult : []
|
|
864
860
|
]);
|
|
865
861
|
break;
|
|
866
|
-
case
|
|
862
|
+
case ANTHROPIC:
|
|
867
863
|
history = messages([
|
|
868
864
|
...history, prompt,
|
|
869
865
|
...options.toolsResult?.length ? options.toolsResult : []
|
|
@@ -889,7 +885,7 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
889
885
|
content = trimTailing(trimTailing(content).slice(0, -1)) + '...';
|
|
890
886
|
}
|
|
891
887
|
}, model.maxInputTokens - options.attachments?.length * ATTACHMENT_TOKEN_COST);
|
|
892
|
-
if ([
|
|
888
|
+
if ([OPENAI].includes(options.flavor) || options.model === GEMMA_3_27B) {
|
|
893
889
|
systemPrompt = null;
|
|
894
890
|
}
|
|
895
891
|
return { systemPrompt, history, prompt };
|
|
@@ -910,13 +906,13 @@ const handleToolsCall = async (msg, options) => {
|
|
|
910
906
|
const calls = msg.tool_calls || msg.content || msg.parts || [];
|
|
911
907
|
if (calls.length) {
|
|
912
908
|
switch (options?.flavor) {
|
|
913
|
-
case
|
|
909
|
+
case ANTHROPIC: preRes.push(msg); break;
|
|
914
910
|
case GEMINI: preRes.push(msg); break;
|
|
915
|
-
case
|
|
911
|
+
case OPENAI: default: preRes.push(msg); break;
|
|
916
912
|
}
|
|
917
913
|
for (const fn of calls) {
|
|
918
914
|
switch (options?.flavor) {
|
|
919
|
-
case
|
|
915
|
+
case ANTHROPIC:
|
|
920
916
|
input = fn.input = String.isString(fn?.input)
|
|
921
917
|
? parseJson(fn.input) : fn?.input;
|
|
922
918
|
packMsg = (content, is_error) => ({
|
|
@@ -935,7 +931,7 @@ const handleToolsCall = async (msg, options) => {
|
|
|
935
931
|
}
|
|
936
932
|
});
|
|
937
933
|
break;
|
|
938
|
-
case
|
|
934
|
+
case OPENAI: default:
|
|
939
935
|
input = parseJson(fn?.function?.arguments);
|
|
940
936
|
packMsg = (content = '', e = false) => ({
|
|
941
937
|
role: TOOL, tool_call_id: fn.id,
|
|
@@ -972,7 +968,7 @@ const handleToolsCall = async (msg, options) => {
|
|
|
972
968
|
}
|
|
973
969
|
if (content.length) {
|
|
974
970
|
switch (options?.flavor) {
|
|
975
|
-
case
|
|
971
|
+
case ANTHROPIC: content = [{ role: user, content }]; break;
|
|
976
972
|
case GEMINI: content = [{ role: FUNC, parts: content }]; break;
|
|
977
973
|
}
|
|
978
974
|
}
|
|
@@ -994,7 +990,7 @@ const promptOpenAI = async (aiId, content, options = {}) => {
|
|
|
994
990
|
options.result ?? '', Buffer.alloc(0), null, [], false,
|
|
995
991
|
provider === AZURE
|
|
996
992
|
];
|
|
997
|
-
options.flavor =
|
|
993
|
+
options.flavor = OPENAI;
|
|
998
994
|
options.model = options.model || model.name;
|
|
999
995
|
const { history }
|
|
1000
996
|
= await buildPrompts(MODELS[options.model], content, options);
|
|
@@ -1073,7 +1069,7 @@ const promptAnthropic = async (aiId, content, options = {}) => {
|
|
|
1073
1069
|
+ '46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB'
|
|
1074
1070
|
); // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking
|
|
1075
1071
|
const { systemPrompt: system, history }
|
|
1076
|
-
= await buildPrompts(model, content, { ...options, flavor:
|
|
1072
|
+
= await buildPrompts(model, content, { ...options, flavor: ANTHROPIC });
|
|
1077
1073
|
const resp = await client.beta.messages.create({
|
|
1078
1074
|
model: options.model, ...history, system, stream: true,
|
|
1079
1075
|
max_tokens: options.extendedThinking ? 128000 : model.maxOutputTokens,
|
|
@@ -1124,7 +1120,7 @@ const promptAnthropic = async (aiId, content, options = {}) => {
|
|
|
1124
1120
|
]
|
|
1125
1121
|
};
|
|
1126
1122
|
const { toolsResult, toolsResponse } = await handleToolsCall(
|
|
1127
|
-
event, { ...options, result, flavor:
|
|
1123
|
+
event, { ...options, result, flavor: ANTHROPIC },
|
|
1128
1124
|
);
|
|
1129
1125
|
if (tool_use.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
|
|
1130
1126
|
return await promptAnthropic(aiId, content, {
|
|
@@ -1326,7 +1322,7 @@ const listGptFineTuningEvents = async (aiId, job_id, options) => {
|
|
|
1326
1322
|
|
|
1327
1323
|
const tailGptFineTuningEvents = async (aiId, job_id, options) => {
|
|
1328
1324
|
assert(job_id, 'Job ID is required.');
|
|
1329
|
-
const [loopName, listOpts] = [`GPT
|
|
1325
|
+
const [loopName, listOpts] = [`GPT-${job_id}`, {
|
|
1330
1326
|
...options, params: { ...options?.params, order: 'ascending' }
|
|
1331
1327
|
}];
|
|
1332
1328
|
let lastEvent;
|
|
@@ -1344,33 +1340,23 @@ const tailGptFineTuningEvents = async (aiId, job_id, options) => {
|
|
|
1344
1340
|
}, 3, 2, 1, loopName, { silent, ...options });
|
|
1345
1341
|
};
|
|
1346
1342
|
|
|
1347
|
-
const initChat = async (options) => {
|
|
1348
|
-
options
|
|
1349
|
-
engines: options?.engines || { [DEFAULT_MODELS[CHAT]]: {} },
|
|
1350
|
-
...options || {},
|
|
1351
|
-
};
|
|
1352
|
-
if (options?.sessions) {
|
|
1343
|
+
const initChat = async (options = {}) => {
|
|
1344
|
+
if (options.sessions) {
|
|
1353
1345
|
assert(
|
|
1354
1346
|
options.sessions?.get && options.sessions?.set,
|
|
1355
1347
|
'Invalid session storage provider.'
|
|
1356
1348
|
);
|
|
1357
1349
|
chatConfig.sessions = options.sessions;
|
|
1358
1350
|
}
|
|
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
|
-
);
|
|
1351
|
+
options.instructions && (chatConfig.systemPrompt = options.instructions);
|
|
1352
|
+
// Use Gemini instead of ChatGPT because of the longer package.
|
|
1353
|
+
const [spTokens, ais] = await Promise.all([countTokens([buildGeminiHistory(
|
|
1354
|
+
chatConfig.systemPrompt, { role: system }
|
|
1355
|
+
)]), getAi(null, { all: true })]);
|
|
1356
|
+
for (const ai of ais) {
|
|
1357
|
+
const mxPmpt = ai.model.maxInputTokens / 2;
|
|
1358
|
+
assert(spTokens < mxPmpt,
|
|
1359
|
+
`System prompt is too long: ${spTokens} / ${mxPmpt} tokens.`);
|
|
1374
1360
|
}
|
|
1375
1361
|
return chatConfig;
|
|
1376
1362
|
};
|
|
@@ -1406,53 +1392,30 @@ const resetSession = async (sessionId, options) => {
|
|
|
1406
1392
|
return await setSession(sessionId, session);
|
|
1407
1393
|
};
|
|
1408
1394
|
|
|
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
|
-
}
|
|
1395
|
+
const talk = async (input, options = {}) => {
|
|
1396
|
+
let [chat, sessionId] =
|
|
1397
|
+
[{ request: input || ATTACHMENTS }, options.sessionId || newSessionId()];
|
|
1398
|
+
const session = await getSession(sessionId, options);
|
|
1399
|
+
const resp = await prompt(input, {
|
|
1400
|
+
messages: session.messages, log: true, ...options,
|
|
1401
|
+
});
|
|
1443
1402
|
chat.response = resp.text;
|
|
1444
1403
|
chat.request && chat.response && session.messages.push(chat);
|
|
1445
1404
|
await setSession(sessionId, session, options);
|
|
1446
|
-
return {
|
|
1405
|
+
return {
|
|
1406
|
+
sessionId, ...resp, spoken: renderText(
|
|
1407
|
+
resp.text, { noCode: true, noLink: true }
|
|
1408
|
+
).replace(/\[\^\d\^\]/ig, ''),
|
|
1409
|
+
};
|
|
1447
1410
|
};
|
|
1448
1411
|
|
|
1449
|
-
const getMaxChatPromptLimit = (options) => {
|
|
1412
|
+
const getMaxChatPromptLimit = async (options) => {
|
|
1450
1413
|
let resp = 0;
|
|
1451
|
-
|
|
1452
|
-
if (options?.
|
|
1453
|
-
const maxInputTokens =
|
|
1414
|
+
(await getAi(null, { all: true })).map(x => {
|
|
1415
|
+
if (options?.aiId && options?.aiId !== x.id) { return; }
|
|
1416
|
+
const maxInputTokens = x.model.maxInputTokens;
|
|
1454
1417
|
resp = resp ? Math.min(resp, maxInputTokens) : maxInputTokens;
|
|
1455
|
-
}
|
|
1418
|
+
});
|
|
1456
1419
|
assert(resp > 0, 'Chat engine has not been initialized.');
|
|
1457
1420
|
return options?.raw ? resp : Math.min(resp, MAX_INPUT_TOKENS);
|
|
1458
1421
|
};
|
|
@@ -1486,28 +1449,17 @@ const distillFile = async (attachments, o) => {
|
|
|
1486
1449
|
attachments = await Promise.all(attachments);
|
|
1487
1450
|
// print(attachments);
|
|
1488
1451
|
return await prompt(strPmt, {
|
|
1489
|
-
|
|
1452
|
+
simple: true, select: { vision: true, fast: true }, ...o, attachments,
|
|
1490
1453
|
});
|
|
1491
1454
|
};
|
|
1492
1455
|
|
|
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.');
|
|
1456
|
+
const prompt = async (input, options = {}) => {
|
|
1457
|
+
const ai = await getAi(options?.aiId, options);
|
|
1458
|
+
const tag = `${ai.provider} (${ai.model.name})`;
|
|
1459
|
+
options.log && log(`Prompt ${tag}: ${JSON.stringify(input)}`);
|
|
1460
|
+
const resp = await ai.prompt(input, options);
|
|
1461
|
+
options.log && log(`Response ${tag}: ${JSON.stringify(resp.text)}`);
|
|
1462
|
+
return resp;
|
|
1511
1463
|
};
|
|
1512
1464
|
|
|
1513
1465
|
const trimPrompt = async (getPrompt, trimFunc, contextWindow, options) => {
|
|
@@ -1549,23 +1501,16 @@ const analyzeSessions = async (sessionIds, options) => {
|
|
|
1549
1501
|
x, JSON.stringify(sses[x]).length,
|
|
1550
1502
|
]).sort((x, y) => y[1] - x[1])[0][0]];
|
|
1551
1503
|
}
|
|
1552
|
-
}, getMaxChatPromptLimit(options));
|
|
1504
|
+
}, await getMaxChatPromptLimit(options));
|
|
1553
1505
|
const aiResp = Object.keys(sses) ? (await prompt(getInput(), {
|
|
1554
|
-
jsonMode: true,
|
|
1506
|
+
jsonMode: true, simple: true, select: { json: true, fase: true },
|
|
1507
|
+
...options || {}
|
|
1555
1508
|
})) : {};
|
|
1556
1509
|
assert(aiResp, 'Unable to analyze sessions.');
|
|
1557
1510
|
ids.map(x => resp[x] = aiResp[x] || null);
|
|
1558
1511
|
return Array.isArray(sessionIds) ? resp : resp[sessionIds[0]];
|
|
1559
1512
|
};
|
|
1560
1513
|
|
|
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
1514
|
export default init;
|
|
1570
1515
|
export {
|
|
1571
1516
|
ATTACHMENT_TOKEN_COST, CLOUD_37_SONNET, CODE_INTERPRETER, DEEPSEEK_R1,
|