utilitas 1999.1.17 → 1999.1.18
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/dist/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/lib/alan.mjs +119 -76
- package/lib/manifest.mjs +1 -1
- package/package.json +1 -1
package/lib/alan.mjs
CHANGED
|
@@ -65,8 +65,8 @@ const [
|
|
|
65
65
|
name, user, system, assistant, MODEL, JSON_OBJECT, TOOL, silent,
|
|
66
66
|
GEMINI_EMBEDDING_M, INVALID_FILE, tokenSafeRatio, GPT_QUERY_LIMIT,
|
|
67
67
|
CONTENT_IS_REQUIRED, OPENAI_HI_RES_SIZE, k, kT, m, minute, hour,
|
|
68
|
-
gb, trimTailing, EBD, GEMINI_20_FLASH_EXP, IMAGE, JINA_DEEPSEARCH,
|
|
69
|
-
|
|
68
|
+
gb, trimTailing, EBD, GEMINI_20_FLASH_EXP, IMAGE, JINA, JINA_DEEPSEARCH,
|
|
69
|
+
JINA_EMBEDDING, JINA_CLIP,
|
|
70
70
|
] = [
|
|
71
71
|
'OpenAI', 'Gemini', 'OPENAI_EMBEDDING', 'GEMINI_EMEDDING',
|
|
72
72
|
'OPENAI_TRAINING', 'Ollama', 'gpt-4o-mini', 'gpt-4o', 'o1', 'o3-mini',
|
|
@@ -84,7 +84,7 @@ const [
|
|
|
84
84
|
'Content is required.', 2000 * 768, x => 1024 * x, x => 1000 * x,
|
|
85
85
|
x => 1024 * 1024 * x, x => 60 * x, x => 60 * 60 * x,
|
|
86
86
|
x => 1024 * 1024 * 1024 * x, x => x.replace(/[\.\s]*$/, ''),
|
|
87
|
-
{ embedding: true }, 'gemini-2.0-flash-exp', 'image', 'Jina
|
|
87
|
+
{ embedding: true }, 'gemini-2.0-flash-exp', 'image', 'Jina',
|
|
88
88
|
'jina-deepsearch-v1', 'JINA_EMBEDDING', 'jina-clip-v2',
|
|
89
89
|
];
|
|
90
90
|
|
|
@@ -92,11 +92,9 @@ const [tool, messages, text]
|
|
|
92
92
|
= [type => ({ type }), messages => ({ messages }), text => ({ text })];
|
|
93
93
|
const [CODE_INTERPRETER, RETRIEVAL, FUNCTION]
|
|
94
94
|
= ['code_interpreter', 'retrieval', FUNC].map(tool);
|
|
95
|
-
const [sessionType, aiType]
|
|
96
|
-
= [`${name.toUpperCase()}-SESSION`, `${name.toUpperCase()}-AI`];
|
|
97
|
-
const [newSessionId, newAiId]
|
|
98
|
-
= [sessionType, aiType].map(type => () => createUoid({ type }));
|
|
99
95
|
const _NO_RENDER = ['INSTRUCTIONS', 'MODELS', 'DEFAULT_MODELS'];
|
|
96
|
+
const sessionType = `${name.toUpperCase()}-SESSION`;
|
|
97
|
+
const newSessionId = () => createUoid({ type: sessionType });
|
|
100
98
|
const chatConfig = { sessions: new Map(), systemPrompt: INSTRUCTIONS };
|
|
101
99
|
const tokenSafe = count => Math.ceil(count * tokenSafeRatio);
|
|
102
100
|
const renderText = (t, o) => _renderText(t, { extraCodeBlock: 0, ...o || {} });
|
|
@@ -117,7 +115,7 @@ const OPENAI_RULES = {
|
|
|
117
115
|
imageCostTokens: ~~(OPENAI_HI_RES_SIZE / (512 * 512) * 170 + 85),
|
|
118
116
|
maxFileSize: m(20), maxImageSize: OPENAI_HI_RES_SIZE,
|
|
119
117
|
supportedMimeTypes: [png, jpeg, gif, webp],
|
|
120
|
-
json: true, tools: true, vision: true,
|
|
118
|
+
json: true, tools: true, vision: true, defaultProvider: OPENAI,
|
|
121
119
|
};
|
|
122
120
|
|
|
123
121
|
const GEMINI_RULES = {
|
|
@@ -128,7 +126,7 @@ const GEMINI_RULES = {
|
|
|
128
126
|
maxVideoPerPrompt: 10, vision: true, supportedMimeTypes: [
|
|
129
127
|
png, jpeg, mov, mpeg, mp4, mpg, avi, wmv, mpegps, flv, pdf, aac,
|
|
130
128
|
flac, mp3, m4a, mpga, opus, pcm, wav, webm, tgpp,
|
|
131
|
-
],
|
|
129
|
+
], defaultProvider: GEMINI,
|
|
132
130
|
};
|
|
133
131
|
|
|
134
132
|
// https://platform.openai.com/docs/models
|
|
@@ -164,13 +162,13 @@ const MODELS = {
|
|
|
164
162
|
contextWindow: kT(128), maxOutputTokens: k(8),
|
|
165
163
|
imageCostTokens: 256, maxImageSize: 896 * 896,
|
|
166
164
|
supportedMimeTypes: [png, jpeg, gif],
|
|
167
|
-
fast: true, json: true, vision: true,
|
|
165
|
+
fast: true, json: true, vision: true, defaultProvider: GEMINI,
|
|
168
166
|
},
|
|
169
|
-
[
|
|
167
|
+
[JINA_DEEPSEARCH]: {
|
|
170
168
|
contextWindow: Infinity, maxInputTokens: Infinity,
|
|
171
169
|
maxOutputTokens: Infinity, imageCostTokens: 0, maxImageSize: Infinity,
|
|
172
170
|
supportedMimeTypes: [png, jpeg, MIME_TEXT, webp, pdf],
|
|
173
|
-
reasoning: true, json: true, vision: true,
|
|
171
|
+
reasoning: true, json: true, vision: true, defaultProvider: JINA,
|
|
174
172
|
},
|
|
175
173
|
[DEEPSEEK_R1]: {
|
|
176
174
|
contextWindow: kT(128), maxOutputTokens: k(32),
|
|
@@ -189,6 +187,7 @@ const MODELS = {
|
|
|
189
187
|
maxImagePerPrompt: 100, maxImageSize: 2000 * 2000,
|
|
190
188
|
supportedMimeTypes: [png, jpeg, gif, webp, pdf],
|
|
191
189
|
json: true, reasoning: true, tools: true, vision: true,
|
|
190
|
+
defaultProvider: ANTHROPIC,
|
|
192
191
|
}, // https://docs.anthropic.com/en/docs/build-with-claude/vision
|
|
193
192
|
};
|
|
194
193
|
|
|
@@ -207,12 +206,15 @@ for (const n in MODELS) {
|
|
|
207
206
|
) : MODELS[n].imageCostTokens;
|
|
208
207
|
}
|
|
209
208
|
}
|
|
210
|
-
|
|
209
|
+
|
|
211
210
|
MODELS[GEMINI_20_FLASH].image = GEMINI_20_FLASH_EXP;
|
|
212
211
|
MODELS[GEMINI_20_FLASH_EXP] = {
|
|
213
212
|
...MODELS[GEMINI_20_FLASH],
|
|
214
213
|
name: GEMINI_20_FLASH_EXP, image: true, tools: false,
|
|
215
214
|
};
|
|
215
|
+
MODELS[GEMMA327B] = { // Ollama Alias
|
|
216
|
+
...MODELS[GEMMA_3_27B], name: GEMMA327B, defaultProvider: OLLAMA
|
|
217
|
+
};
|
|
216
218
|
|
|
217
219
|
// Default models for each provider
|
|
218
220
|
const DEFAULT_MODELS = {
|
|
@@ -221,7 +223,7 @@ const DEFAULT_MODELS = {
|
|
|
221
223
|
[GEMINI]: GEMINI_20_FLASH,
|
|
222
224
|
[ANTHROPIC]: CLOUD_37_SONNET,
|
|
223
225
|
[VERTEX_ANTHROPIC]: CLOUD_37_SONNET,
|
|
224
|
-
[
|
|
226
|
+
[JINA]: JINA_DEEPSEARCH,
|
|
225
227
|
[OLLAMA]: GEMMA327B,
|
|
226
228
|
[OPENAI_VOICE]: NOVA,
|
|
227
229
|
[OPENAI_EMBEDDING]: TEXT_EMBEDDING_3_SMALL,
|
|
@@ -247,7 +249,7 @@ let tokeniser;
|
|
|
247
249
|
const unifyProvider = provider => {
|
|
248
250
|
assert(provider = (provider || '').trim(), 'AI provider is required.');
|
|
249
251
|
for (let type of [OPENAI, AZURE_OPENAI, AZURE, GEMINI, ANTHROPIC,
|
|
250
|
-
VERTEX_ANTHROPIC,
|
|
252
|
+
VERTEX_ANTHROPIC, JINA, OLLAMA]) {
|
|
251
253
|
if (insensitiveCompare(provider, type)) { return type; }
|
|
252
254
|
}
|
|
253
255
|
throwError(`Invalid AI provider: ${provider}.`);
|
|
@@ -347,112 +349,153 @@ const toolsGemini = async () => (await toolsOpenAI()).map(x => ({
|
|
|
347
349
|
}
|
|
348
350
|
}));
|
|
349
351
|
|
|
352
|
+
const buildAiId = (provider, model) => [provider, model].map(
|
|
353
|
+
x => ensureString(x, { case: 'SNAKE' })
|
|
354
|
+
).join('_');
|
|
355
|
+
|
|
350
356
|
const init = async (options = {}) => {
|
|
351
|
-
const id = options.id || newAiId();
|
|
352
357
|
const provider = unifyProvider(options?.provider);
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
+
let models;
|
|
359
|
+
if (options.model === '*') { // All models
|
|
360
|
+
models = Object.values(MODELS).filter(x => x.defaultProvider === provider);
|
|
361
|
+
} else if (options.model) { // Specific model
|
|
362
|
+
models = Object.values(MODELS).filter(x => ensureArray(options.model).includes(x.name));
|
|
363
|
+
} else if (DEFAULT_MODELS[provider]) { // Default model
|
|
364
|
+
models = [MODELS[DEFAULT_MODELS[provider]]];
|
|
365
|
+
} else if (options.modelConfig) {
|
|
366
|
+
models = ensureArray(options.modelConfig);
|
|
367
|
+
}
|
|
368
|
+
assert(models.length,
|
|
369
|
+
`Model name or description is required for provider: ${provider}.`);
|
|
358
370
|
switch (provider) {
|
|
359
371
|
case OPENAI:
|
|
360
372
|
assertApiKey(provider, options);
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
373
|
+
var client = await OpenAI(options);
|
|
374
|
+
for (let model of models) {
|
|
375
|
+
var id = buildAiId(provider, model.name);
|
|
376
|
+
ais.push({
|
|
377
|
+
id, provider, model, priority: 0, initOrder: ais.length, client,
|
|
378
|
+
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
379
|
+
embedding: async (i, o) => await createOpenAIEmbedding(id, i, o),
|
|
380
|
+
});
|
|
381
|
+
}
|
|
367
382
|
break;
|
|
368
383
|
case AZURE_OPENAI:
|
|
369
384
|
assertApiKey(provider, options);
|
|
370
385
|
assert(options.endpoint,
|
|
371
386
|
`${provider} api endpoint and deployment are required.`);
|
|
387
|
+
var model = models[0];
|
|
388
|
+
var client = await AzureOpenAI({
|
|
389
|
+
apiVersion: '2025-01-01-preview',
|
|
390
|
+
deployment: model.name, ...options,
|
|
391
|
+
});
|
|
392
|
+
var id = buildAiId(provider, model.name);
|
|
372
393
|
ais.push({
|
|
373
|
-
id, provider, model, priority: 0, initOrder: ais.length,
|
|
374
|
-
client: await AzureOpenAI({
|
|
375
|
-
apiVersion: '2025-01-01-preview',
|
|
376
|
-
deployment: model.name, ...options,
|
|
377
|
-
}),
|
|
394
|
+
id, provider, model, priority: 0, initOrder: ais.length, client,
|
|
378
395
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
379
396
|
});
|
|
380
397
|
break;
|
|
381
398
|
case AZURE:
|
|
382
399
|
assertApiKey(provider, options);
|
|
383
400
|
assert(options.baseURL, `${provider} api endpoint is required.`);
|
|
401
|
+
var model = models[0];
|
|
402
|
+
var client = await OpenAI(options);
|
|
403
|
+
var id = buildAiId(provider, model.name);
|
|
384
404
|
ais.push({
|
|
385
|
-
id, provider, model, priority: 0, initOrder: ais.length,
|
|
386
|
-
client: await OpenAI(options),
|
|
405
|
+
id, provider, model, priority: 0, initOrder: ais.length, client,
|
|
387
406
|
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
388
407
|
});
|
|
389
408
|
break;
|
|
390
409
|
case GEMINI:
|
|
391
410
|
assertApiKey(provider, options);
|
|
392
411
|
const { GoogleGenerativeAI } = await need('@google/generative-ai');
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
412
|
+
var client = new GoogleGenerativeAI(options.apiKey);
|
|
413
|
+
for (let model of models) {
|
|
414
|
+
var id = buildAiId(provider, model.name);
|
|
415
|
+
ais.push({
|
|
416
|
+
id, provider, model, priority: 0, initOrder: ais.length, client,
|
|
417
|
+
prompt: async (cnt, opts) => await promptGemini(id, cnt, opts),
|
|
418
|
+
embedding: async (i, o) => await createGeminiEmbedding(id, i, o),
|
|
419
|
+
});
|
|
420
|
+
}
|
|
399
421
|
break;
|
|
400
422
|
case ANTHROPIC:
|
|
401
423
|
assertApiKey(provider, options);
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
424
|
+
var client = new ((
|
|
425
|
+
await need('@anthropic-ai/sdk')
|
|
426
|
+
).Anthropic)(options)
|
|
427
|
+
for (let model of models) {
|
|
428
|
+
var id = buildAiId(provider, model.name);
|
|
429
|
+
ais.push({
|
|
430
|
+
id, provider, model, priority: 0, initOrder: ais.length, client,
|
|
431
|
+
prompt: async (cnt, opts) => await promptAnthropic(id, cnt, opts),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
408
434
|
break;
|
|
409
435
|
case VERTEX_ANTHROPIC:
|
|
410
436
|
// https://github.com/anthropics/anthropic-sdk-typescript/tree/main/packages/vertex-sdk
|
|
411
437
|
assert(options?.credentials, `${provider} api credentials are required.`);
|
|
412
|
-
const AnthropicVertex = (await need('@anthropic-ai/vertex-sdk')).AnthropicVertex;
|
|
413
438
|
process.env['GOOGLE_APPLICATION_CREDENTIALS'] = options.credentials;
|
|
414
439
|
process.env['ANTHROPIC_VERTEX_PROJECT_ID'] = options.projectId;
|
|
440
|
+
var model = models[0];
|
|
441
|
+
var client = new ((
|
|
442
|
+
await need('@anthropic-ai/vertex-sdk')
|
|
443
|
+
).AnthropicVertex)({ region: options?.region || 'us-east5' });
|
|
444
|
+
var id = buildAiId(provider, model.name);
|
|
415
445
|
ais.push({
|
|
416
|
-
id, provider, model, priority: 0, initOrder: ais.length,
|
|
417
|
-
client: new AnthropicVertex({ region: options?.region || 'us-east5' }),
|
|
446
|
+
id, provider, model, priority: 0, initOrder: ais.length, client: client,
|
|
418
447
|
prompt: async (cnt, opts) => await promptAnthropic(id, cnt, opts),
|
|
419
448
|
});
|
|
420
449
|
break;
|
|
421
|
-
case
|
|
450
|
+
case JINA:
|
|
422
451
|
assertApiKey(provider, options);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
452
|
+
var [client, ebd] = [await OpenAI({
|
|
453
|
+
baseURL: 'https://deepsearch.jina.ai/v1/', ...options,
|
|
454
|
+
}), await OpenAI({
|
|
455
|
+
baseURL: 'https://api.jina.ai/v1/', ...options,
|
|
456
|
+
})];
|
|
457
|
+
for (let model of models) {
|
|
458
|
+
var id = buildAiId(provider, model.name);
|
|
459
|
+
ais.push({
|
|
460
|
+
id, provider, model, priority: 0, initOrder: ais.length, client,
|
|
461
|
+
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
462
|
+
embedding: async (i, o) => await createJinaEmbedding(ebd, i, o),
|
|
463
|
+
});
|
|
464
|
+
}
|
|
433
465
|
break;
|
|
434
466
|
case OLLAMA:
|
|
435
467
|
// https://github.com/ollama/ollama/blob/main/docs/openai.md
|
|
436
468
|
const baseURL = 'http://localhost:11434/v1/';
|
|
437
|
-
ais.push({
|
|
438
|
-
id, provider, model, priority: 0, initOrder: ais.length,
|
|
439
|
-
client: await OpenAI({ baseURL, apiKey: 'ollama', ...options }),
|
|
440
|
-
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
441
|
-
});
|
|
442
469
|
const phLog = m => log(`Ollama preheat: ${m?.message || m}`);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
470
|
+
var client = await OpenAI({ baseURL, apiKey: 'ollama', ...options });
|
|
471
|
+
for (let model of models) {
|
|
472
|
+
var id = buildAiId(provider, model.name);
|
|
473
|
+
ais.push({
|
|
474
|
+
id, provider, model, priority: 0, initOrder: ais.length, client,
|
|
475
|
+
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
476
|
+
});
|
|
477
|
+
ignoreErrFunc(async () => {
|
|
478
|
+
phLog(await (await fetch(`${baseURL}completions`, {
|
|
479
|
+
method: 'POST', body: JSON.stringify({
|
|
480
|
+
model: model.name, prompt: '', keep_alive: -1
|
|
481
|
+
})
|
|
482
|
+
})).text());
|
|
483
|
+
}, { log: phLog });
|
|
484
|
+
}
|
|
450
485
|
break;
|
|
451
486
|
default:
|
|
452
487
|
throwError(`Invalid AI provider: ${options.provider || 'null'}.`);
|
|
453
488
|
}
|
|
454
489
|
ais.sort((a, b) => a.priority - b.priority || a.initOrder - b.initOrder);
|
|
455
|
-
return ais
|
|
490
|
+
return ais;
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const packAi = (ais, options = {}) => {
|
|
494
|
+
const res = options.basic ? ais.map(x => ({
|
|
495
|
+
id: x.id, provider: x.provider, model: x.model, priority: x.priority,
|
|
496
|
+
initOrder: x.initOrder, prompt: !!x.prompt, embedding: !!x.embedding,
|
|
497
|
+
})) : ais;
|
|
498
|
+
return options.all ? res : res[0];
|
|
456
499
|
};
|
|
457
500
|
|
|
458
501
|
const getAi = async (id, options = {}) => {
|
|
@@ -472,12 +515,12 @@ const getAi = async (id, options = {}) => {
|
|
|
472
515
|
select && (res.push(x));
|
|
473
516
|
}
|
|
474
517
|
const best = options.select?.fast ? res.filter(x => x.model.fast) : res;
|
|
475
|
-
if (best.length) { return
|
|
518
|
+
if (best.length) { return packAi(best, options); }
|
|
476
519
|
assert(res.length, 'AI not found.');
|
|
477
520
|
log(`Best match AI not found, fallbacked: ${JSON.stringify(options.select)}.`);
|
|
478
|
-
return
|
|
521
|
+
return packAi(res, options);
|
|
479
522
|
}
|
|
480
|
-
const result =
|
|
523
|
+
const result = packAi(ais, options);
|
|
481
524
|
assert(result?.length || result?.id, 'AI not found.');
|
|
482
525
|
return result;
|
|
483
526
|
};
|
package/lib/manifest.mjs
CHANGED