utilitas 1998.2.59 → 1998.2.60

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,25 +49,30 @@ const [
49
49
  OPENAI, GEMINI, CHATGPT, OPENAI_EMBEDDING, GEMINI_EMEDDING, OPENAI_TRAINING,
50
50
  OLLAMA, CLAUDE, 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_32B, DEEPSEEK_R1_70B, MD_CODE, CHATGPT_REASONING,
53
- TEXT_EMBEDDING_3_SMALL, TEXT_EMBEDDING_3_LARGE, CLAUDE_35_SONNET,
54
- CLAUDE_35_HAIKU, CLOUD_37_SONNET, AUDIO, WAV, CHATGPT_MINI, ATTACHMENTS,
55
- CHAT, OPENAI_VOICE, MEDIUM, LOW, HIGH, GPT_REASONING_EFFORT, THINK,
56
- THINK_STR, THINK_END, AZURE, TOOLS_STR, TOOLS_END, TOOLS, TEXT, THINKING,
57
- OK, FUNC, GPT_45, REDACTED_THINKING, GEMMA_3_27B,
52
+ DEEPSEEK_R1_70B, MD_CODE, CHATGPT_REASONING, TEXT_EMBEDDING_3_SMALL,
53
+ TEXT_EMBEDDING_3_LARGE, CLOUD_37_SONNET, AUDIO, WAV, CHATGPT_MINI,
54
+ ATTACHMENTS, CHAT, OPENAI_VOICE, MEDIUM, LOW, HIGH, GPT_REASONING_EFFORT,
55
+ THINK, THINK_STR, THINK_END, AZURE, TOOLS_STR, TOOLS_END, TOOLS, TEXT,
56
+ THINKING, OK, FUNC, GPT_45, REDACTED_THINKING, GEMMA_3_27B, AZURE_OPENAI,
57
+ ANTHROPIC, VERTEX_ANTHROPIC, GEMMA327B, size8k, ais, MAX_TOOL_RECURSION,
58
+ LOG, name, user, system, assistant, MODEL, JSON_OBJECT, TOOL, silent,
59
+ NOT_INIT, INVALID_FILE, tokenSafeRatio, GPT_QUERY_LIMIT, minsOfDay,
58
60
  ] = [
59
61
  'OPENAI', 'GEMINI', 'CHATGPT', 'OPENAI_EMBEDDING', 'GEMINI_EMEDDING',
60
62
  'OPENAI_TRAINING', 'OLLAMA', 'CLAUDE', 'gpt-4o-mini', 'gpt-4o', 'o1',
61
63
  'o3-mini', 'gemini-2.0-flash', 'gemini-2.0-flash-thinking-exp',
62
64
  'gemini-2.0-pro-exp', 'nova', 'embedding-001', 'deepseek-r1',
63
- 'deepseek-r1:32b', 'deepseek-r1:70b', '```', 'CHATGPT_REASONING',
64
- 'text-embedding-3-small', 'text-embedding-3-large',
65
- 'claude-3-5-sonnet-latest', 'claude-3-5-haiku-latest',
66
- 'claude-3-7-sonnet@20250219', 'audio', 'wav', 'CHATGPT_MINI',
67
- '[ATTACHMENTS]', 'CHAT', 'OPENAI_VOICE', 'medium', 'low', 'high',
68
- 'medium', 'think', '<think>', '</think>', 'AZURE', '<tools>',
69
- '</tools>', 'tools', 'text', 'thinking', 'OK', 'function',
65
+ 'deepseek-r1:70b', '```', 'CHATGPT_REASONING', 'text-embedding-3-small',
66
+ 'text-embedding-3-large', 'claude-3-7-sonnet@20250219', 'audio', 'wav',
67
+ 'CHATGPT_MINI', '[ATTACHMENTS]', 'CHAT', 'OPENAI_VOICE', 'medium',
68
+ 'low', 'high', 'medium', 'think', '<think>', '</think>', 'AZURE',
69
+ '<tools>', '</tools>', 'tools', 'text', 'thinking', 'OK', 'function',
70
70
  'gpt-4.5-preview', 'redacted_thinking', 'gemma-3-27b-it',
71
+ 'AZURE OPENAI', 'ANTHROPIC', 'VERTEX ANTHROPIC', 'gemma3:27b',
72
+ 7680 * 4320, {}, 10, { log: true }, 'Alan', 'user', 'system',
73
+ 'assistant', 'model', 'json_object', 'tool', true,
74
+ 'AI engine has not been initialized.', 'Invalid file data.', 1.1, 100,
75
+ 60 * 24,
71
76
  ];
72
77
 
73
78
  const [
@@ -88,45 +93,52 @@ const [tool, provider, messages, text] = [
88
93
  messages => ({ messages }), text => ({ text }),
89
94
  ];
90
95
 
91
- const [name, user, system, assistant, MODEL, JSON_OBJECT, TOOL, silent]
92
- = ['Alan', 'user', 'system', 'assistant', 'model', 'json_object', 'tool', true];
93
96
  const [CODE_INTERPRETER, RETRIEVAL, FUNCTION]
94
97
  = ['code_interpreter', 'retrieval', FUNC].map(tool);
95
- const [NOT_INIT, INVALID_FILE]
96
- = ['AI engine has not been initialized.', 'Invalid file data.'];
97
98
  const chatConfig
98
99
  = { sessions: new Map(), engines: {}, systemPrompt: INSTRUCTIONS };
99
- const [tokenSafeRatio, GPT_QUERY_LIMIT, minsOfDay] = [1.1, 100, 60 * 24];
100
+ const [sessionType, aiType]
101
+ = [`${name.toUpperCase()}-SESSION`, `${name.toUpperCase()}-AI`];
102
+ const [newSessionId, newAiId]
103
+ = [sessionType, aiType].map(type => () => createUoid({ type }));
100
104
  const tokenSafe = count => Math.ceil(count * tokenSafeRatio);
101
105
  const clients = {};
102
- const size8k = 7680 * 4320;
103
- const MAX_TOOL_RECURSION = 10;
104
- const LOG = { log: true };
105
- const sessionType = `${name.toUpperCase()}-SESSION`;
106
106
  const unifyProvider = options => unifyType(options?.provider, 'AI provider');
107
107
  const unifyEngine = options => unifyType(options?.engine, 'AI engine');
108
108
  const trimTailing = text => text.replace(/[\.\s]*$/, '');
109
- const newSessionId = () => createUoid({ type: sessionType });
110
109
  const renderText = (t, o) => _renderText(t, { extraCodeBlock: 0, ...o || {} });
111
110
  const log = (cnt, opt) => _log(cnt, import.meta.url, { time: 1, ...opt || {} });
112
111
  const CONTENT_IS_REQUIRED = 'Content is required.';
113
112
  const assertContent = content => assert(content.length, CONTENT_IS_REQUIRED);
114
113
  const countToolCalls = r => r?.split('\n').filter(x => x === TOOLS_STR).length;
114
+ const assertApiKey = (p, o) => assert(o?.apiKey, `${p} api key is required.`);
115
+ const libOpenAi = async opts => await need('openai', { ...opts, raw: true });
116
+ const OpenAI = async opts => new (await libOpenAi(opts)).OpenAI(opts);
117
+ const AzureOpenAI = async opts => new (await libOpenAi(opts)).AzureOpenAI(opts);
115
118
 
116
119
  const DEFAULT_MODELS = {
117
- [CHATGPT_MINI]: GPT_4O_MINI,
118
- [CHATGPT_REASONING]: GPT_O3_MINI,
119
- [CHATGPT]: GPT_4O,
120
- [CLAUDE]: CLOUD_37_SONNET,
121
- [GEMINI_EMEDDING]: EMBEDDING_001,
120
+ [OPENAI]: GPT_4O,
121
+ [AZURE_OPENAI]: GPT_4O,
122
122
  [GEMINI]: GEMINI_20_FLASH,
123
- [OLLAMA]: DEEPSEEK_R1,
124
- [AZURE]: DEEPSEEK_R1,
123
+ [ANTHROPIC]: CLOUD_37_SONNET,
124
+ [VERTEX_ANTHROPIC]: CLOUD_37_SONNET,
125
+ [OLLAMA]: GEMMA327B,
126
+ [OPENAI_VOICE]: NOVA,
125
127
  [OPENAI_EMBEDDING]: TEXT_EMBEDDING_3_SMALL,
128
+ [GEMINI_EMEDDING]: EMBEDDING_001,
126
129
  [OPENAI_TRAINING]: GPT_4O_MINI, // https://platform.openai.com/docs/guides/fine-tuning
127
- [OPENAI_VOICE]: NOVA,
130
+
131
+
132
+ [CHATGPT_MINI]: GPT_4O_MINI,
133
+ [CHATGPT_REASONING]: GPT_O3_MINI,
128
134
  };
129
135
 
136
+
137
+
138
+
139
+
140
+
141
+
130
142
  DEFAULT_MODELS[CHAT] = DEFAULT_MODELS[GEMINI];
131
143
 
132
144
  const tokenRatioByWords = Math.min(
@@ -194,13 +206,9 @@ const MODELS = {
194
206
  reasoning: true,
195
207
  vision: true,
196
208
  tools: true,
197
- // audio: 'gpt-4o-audio-preview', // fallback to GPT-4O to support audio
198
209
  supportedMimeTypes: [
199
210
  png, jpeg, gif, webp,
200
211
  ],
201
- // supportedAudioTypes: [ // fallback to GPT-4O to support audio
202
- // wav,
203
- // ],
204
212
  },
205
213
  [GPT_O3_MINI]: {
206
214
  contextWindow: 200000,
@@ -214,13 +222,9 @@ const MODELS = {
214
222
  reasoning: true,
215
223
  vision: true,
216
224
  tools: true,
217
- // audio: 'gpt-4o-mini-audio-preview', // fallback to GPT-4O-MINI to support audio
218
225
  supportedMimeTypes: [
219
226
  png, jpeg, gif, webp,
220
227
  ],
221
- // supportedAudioTypes: [ // fallback to GPT-4O-MINI to support audio
222
- // wav,
223
- // ],
224
228
  },
225
229
  [GPT_45]: {
226
230
  contextWindow: 128000,
@@ -342,24 +346,6 @@ const MODELS = {
342
346
  embedding: true,
343
347
  requestLimitsRPM: 1500,
344
348
  },
345
- [CLAUDE_35_SONNET]: { // https://docs.anthropic.com/en/docs/about-claude/models
346
- contextWindow: 200 * 1000,
347
- maxOutputTokens: 8192,
348
- imageCostTokens: size8k / 750,
349
- documentCostTokens: 3000 * 100, // 100 pages: https://docs.anthropic.com/en/docs/build-with-claude/pdf-support
350
- maxImagePerPrompt: 5, // https://docs.anthropic.com/en/docs/build-with-claude/vision
351
- maxImageSize: 1092, // by pixels
352
- maxDocumentPages: 100,
353
- maxDocumentFile: 1024 * 1024 * 32, // 32MB
354
- requestLimitsRPM: 50,
355
- tokenLimitsITPM: 40000,
356
- tokenLimitsOTPM: 8000,
357
- trainingData: 'Apr 2024',
358
- tools: true,
359
- supportedMimeTypes: [
360
- png, jpeg, gif, webp, pdf,
361
- ],
362
- },
363
349
  // https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet?authuser=5&inv=1&invt=Abqftg&project=backend-alpha-97077
364
350
  [CLOUD_37_SONNET]: {
365
351
  contextWindow: 200 * 1000,
@@ -382,9 +368,8 @@ const MODELS = {
382
368
  },
383
369
  };
384
370
 
385
- MODELS[CLAUDE_35_HAIKU] = MODELS[CLAUDE_35_SONNET];
386
- MODELS[DEEPSEEK_R1_32B] = MODELS[DEEPSEEK_R1];
387
371
  MODELS[DEEPSEEK_R1_70B] = MODELS[DEEPSEEK_R1];
372
+ MODELS[GEMMA327B] = MODELS[GEMMA_3_27B];
388
373
 
389
374
  for (const n in MODELS) {
390
375
  MODELS[n]['name'] = n;
@@ -514,56 +499,90 @@ const toolsGemini = async () => (await toolsOpenAI()).map(x => ({
514
499
  }
515
500
  }));
516
501
 
517
- const init = async (options) => {
502
+ const init = async (options = {}) => {
503
+ const id = newAiId();
518
504
  const provider = unifyProvider(options);
505
+ const modelName = options.model || DEFAULT_MODELS[provider];
506
+ assert(modelName, `Model is required for provider: ${provider}.`);
507
+ let model = options.modelConfig || MODELS[modelName];
508
+ assert(model, `The model has not been configured yet: ${modelName}.`);
509
+ model = { name: modelName, ...model };
519
510
  switch (provider) {
520
- case OPENAI: case AZURE:
521
- if (options?.apiKey) {
522
- provider === AZURE && assert(
523
- options?.baseURL, 'Azure api endpoint is required.'
524
- );
525
- const libOpenAI = await need('openai', { raw: true });
526
- const openai = new (options?.endpoint && options?.deployment
527
- ? libOpenAI.AzureOpenAI : libOpenAI.OpenAI)(options);
528
- clients[provider] = { client: openai, clientBeta: openai.beta };
529
- }
511
+ case OPENAI:
512
+ assertApiKey(provider, options);
513
+ ais[id] = {
514
+ id, provider, model, client: await OpenAI(options),
515
+ prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
516
+ };
517
+ break;
518
+ case AZURE_OPENAI:
519
+ assertApiKey(provider, options);
520
+ assert(options.endpoint,
521
+ `{provider} api endpoint and deployment are required.`);
522
+ ais[id] = {
523
+ id, provider, model, client: await AzureOpenAI({
524
+ apiVersion: '2025-01-01-preview',
525
+ deployment: model.name, ...options,
526
+ }),
527
+ prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
528
+ };
529
+ break;
530
+ case AZURE:
531
+ assertApiKey(provider, options);
532
+ assert(options.baseURL, `${provider} api endpoint is required.`);
533
+ ais[id] = {
534
+ id, provider, model, client: await OpenAI(options),
535
+ prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
536
+ };
530
537
  break;
531
538
  case GEMINI:
532
- if (options?.apiKey) {
533
- const { GoogleGenerativeAI } = await need('@google/generative-ai');
534
- const genAi = new GoogleGenerativeAI(options.apiKey);
535
- clients[provider] = { client: genAi };
536
- }
539
+ assertApiKey(provider, options);
540
+ const { GoogleGenerativeAI } = await need('@google/generative-ai');
541
+ ais[id] = {
542
+ id, provider, model,
543
+ client: new GoogleGenerativeAI(options.apiKey),
544
+ prompt: async (cnt, opts) => await promptGemini(id, cnt, opts),
545
+ };
537
546
  break;
538
- case CLAUDE:
539
- if (options?.apiKey || (options?.credentials && options?.projectId)) {
540
- // https://github.com/anthropics/anthropic-sdk-typescript/tree/main/packages/vertex-sdk
541
- const Anthropic = (await need(options?.credentials
542
- ? '@anthropic-ai/vertex-sdk' : '@anthropic-ai/sdk', { raw: true }))[
543
- options?.credentials ? 'AnthropicVertex' : 'Anthropic'
544
- ];
545
- if (options?.credentials) {
546
- process.env['GOOGLE_APPLICATION_CREDENTIALS'] = options.credentials;
547
- process.env['ANTHROPIC_VERTEX_PROJECT_ID'] = options.projectId;
548
- }
549
- const anthropic = new Anthropic({
550
- ...options?.apiKey ? { apiKey: options.apiKey } : {},
551
- ...options?.credentials ? { region: options?.region || 'us-east5' } : {},
552
- });
553
- clients[provider] = { client: anthropic };
554
- }
547
+ case ANTHROPIC:
548
+ assertApiKey(provider, options);
549
+ const Anthropic = (await need('@anthropic-ai/sdk')).Anthropic;
550
+ ais[id] = {
551
+ id, provider, model, client: new Anthropic(options),
552
+ prompt: async (cnt, opts) => await promptAnthropic(id, cnt, opts),
553
+ };
554
+ break;
555
+ case VERTEX_ANTHROPIC:
556
+ // https://github.com/anthropics/anthropic-sdk-typescript/tree/main/packages/vertex-sdk
557
+ assert(options?.credentials, `${provider} api credentials are required.`);
558
+ const AnthropicVertex = (await need('@anthropic-ai/vertex-sdk')).AnthropicVertex;
559
+ process.env['GOOGLE_APPLICATION_CREDENTIALS'] = options.credentials;
560
+ process.env['ANTHROPIC_VERTEX_PROJECT_ID'] = options.projectId;
561
+ ais[id] = {
562
+ id, provider, model,
563
+ client: new AnthropicVertex({ region: options?.region || 'us-east5' }),
564
+ prompt: async (cnt, opts) => await promptAnthropic(id, cnt, opts),
565
+ };
555
566
  break;
556
567
  case OLLAMA:
557
- clients[provider] || (clients[provider] = {
568
+ ais[id] = {
569
+ id, provider, model,
558
570
  client: new (await need('ollama', { raw: true })).Ollama(options),
559
- model: options?.model || DEFAULT_MODELS[OLLAMA],
560
- });
571
+ prompt: async (cnt, opts) => await promptOllama(id, cnt, opts),
572
+ };
561
573
  break;
562
574
  default:
563
- throwError(`Invalid AI provider: ${options?.provider || 'null'}`);
575
+ throwError(`Invalid AI provider: ${options.provider || 'null'}.`);
564
576
  }
565
- assert(clients[provider], NOT_INIT);
566
- return clients[provider];
577
+ return ais[id];
578
+ };
579
+
580
+ const getAi = async (id, options) => {
581
+ if (id) {
582
+ if (ais[id]) { return options?.client ? ais[id]?.client : ais[id]; }
583
+ else { throwError(`AI not found: ${id}.`); }
584
+ }
585
+ return ais;
567
586
  };
568
587
 
569
588
  const countTokens = async (input, options) => {
@@ -972,36 +991,32 @@ const mergeMsgs = (resp, calls) => [resp, ...calls.length ? [
972
991
  `⚠️ Tools recursion limit reached: ${MAX_TOOL_RECURSION}`
973
992
  ] : []].map(x => x.trim()).join('\n\n');
974
993
 
975
- const promptChatGPT = async (content, options = {}) => {
976
- if (options.model) { } else if (options.provider === AZURE) {
977
- options.model = DEFAULT_MODELS[AZURE];
978
- } else if (options.reasoning) {
979
- options.model = DEFAULT_MODELS[CHATGPT_REASONING];
980
- } else {
981
- options.model = DEFAULT_MODELS[CHATGPT];
982
- }
983
- let [_MODEL, result, resultAudio, event, resultTools, responded] = [
984
- MODELS[options.model], options?.result ?? '', Buffer.alloc(0), null, [],
985
- false
994
+ const promptOpenAI = async (aiId, content, options = {}) => {
995
+ let { provider, client, model } = await getAi(aiId);
996
+ let [result, resultAudio, event, resultTools, responded, azure] = [
997
+ options.result ?? '', Buffer.alloc(0), null, [], false,
998
+ provider === AZURE
986
999
  ];
987
- options.reasoning && !options.reasoning_effort
988
- && (options.reasoning_effort = GPT_REASONING_EFFORT);
989
- const { client } = await getOpenAIClient(options);
1000
+ options.flavor = CHATGPT;
1001
+ options.model = options.model || model.name;
990
1002
  const { history }
991
- = await buildPrompts(_MODEL, content, { ...options, flavor: CHATGPT });
1003
+ = await buildPrompts(MODELS[options.model], content, options);
1004
+ model = MODELS[options.model];
1005
+ model.reasoning && !azure && !options.reasoning_effort
1006
+ && (options.reasoning_effort = GPT_REASONING_EFFORT);
992
1007
  const modalities = options.modalities
993
1008
  || (options.audioMode ? [TEXT, AUDIO] : undefined);
994
1009
  [options.audioMimeType, options.suffix] = [pcm16, 'pcm.wav'];
995
1010
  const resp = await client.chat.completions.create({
1011
+ model: azure ? undefined : options.model, ...history,
1012
+ ...options.jsonMode ? { response_format: { type: JSON_OBJECT } } : {},
996
1013
  modalities, audio: options.audio || (
997
1014
  modalities?.find?.(x => x === AUDIO)
998
1015
  && { voice: DEFAULT_MODELS[OPENAI_VOICE], format: 'pcm16' }
999
- ), ...history, ..._MODEL?.tools ? {
1016
+ ), ...model.tools && !azure ? {
1000
1017
  tools: options.tools ?? (await toolsOpenAI()).map(x => x.def),
1001
- } : {}, ...options.jsonMode ? {
1002
- response_format: { type: JSON_OBJECT }
1003
- } : {}, model: options.model, stream: true,
1004
- store: true, tool_choice: 'auto',
1018
+ tool_choice: 'auto',
1019
+ } : {}, ...azure ? {} : { store: true }, stream: true,
1005
1020
  });
1006
1021
  for await (event of resp) {
1007
1022
  event = event?.choices?.[0] || {};
@@ -1016,9 +1031,11 @@ const promptChatGPT = async (content, options = {}) => {
1016
1031
  isSet(x.index, true) && (curFunc.index = x.index);
1017
1032
  x.id && (curFunc.id = x.id);
1018
1033
  x.type && (curFunc.type = x.type);
1019
- curFunc.function || (curFunc.function = { name: '', arguments: '' });
1034
+ curFunc.function
1035
+ || (curFunc.function = { name: '', arguments: '' });
1020
1036
  x?.function?.name && (curFunc.function.name += x.function.name);
1021
- x?.function?.arguments && (curFunc.function.arguments += x.function.arguments);
1037
+ x?.function?.arguments
1038
+ && (curFunc.function.arguments += x.function.arguments);
1022
1039
  }
1023
1040
  options.result && deltaText
1024
1041
  && (responded = responded || (deltaText = `\n\n${deltaText}`));
@@ -1036,25 +1053,27 @@ const promptChatGPT = async (content, options = {}) => {
1036
1053
  };
1037
1054
  const { toolsResult, toolsResponse }
1038
1055
  = await handleToolsCall(event, { ...options, result });
1039
- if (toolsResult.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
1040
- return promptChatGPT(content, { ...options, toolsResult, result: toolsResponse });
1056
+ if (toolsResult.length
1057
+ && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
1058
+ return promptOpenAI(aiId, content, {
1059
+ ...options, toolsResult, result: toolsResponse,
1060
+ });
1041
1061
  }
1042
1062
  event.text = mergeMsgs(toolsResponse, toolsResult);
1043
1063
  return await packResp(event, options);
1044
1064
  };
1045
1065
 
1046
- const promptAzure = async (content, options = {}) =>
1047
- await promptChatGPT(content, { ...options, provider: AZURE });
1048
-
1049
- const promptOllama = async (content, options = {}) => {
1050
- const { client, model } = await getOllamaClient(options);
1066
+ const promptOllama = async (aiId, content, options = {}) => {
1067
+ const { client, model } = await getAi(aiId);
1051
1068
  // https://github.com/ollama/ollama-js
1052
1069
  // https://github.com/jmorganca/ollama/blob/main/examples/typescript-simplechat/client.ts
1053
- options.model = options?.model || model;
1054
- let [_MODEL, chunk, result] = [MODELS[options.model], null, ''];
1055
- const { history: h }
1056
- = await buildPrompts(_MODEL, content, { ...options, flavor: OLLAMA });
1057
- const resp = await client.chat({ model: options.model, stream: true, ...h });
1070
+ options.model = options?.model || model.name;
1071
+ let [chunk, result] = [null, ''];
1072
+ const { history }
1073
+ = await buildPrompts(model, content, { ...options, flavor: OLLAMA });
1074
+ const resp = await client.chat({
1075
+ model: options.model, stream: true, ...history,
1076
+ });
1058
1077
  for await (chunk of resp) {
1059
1078
  const delta = chunk.message.content || '';
1060
1079
  result += delta;
@@ -1065,33 +1084,28 @@ const promptOllama = async (content, options = {}) => {
1065
1084
  return await packResp({ text: result }, options);
1066
1085
  };
1067
1086
 
1068
- const promptClaude = async (content, options = {}) => {
1069
- options.model = options.model || DEFAULT_MODELS[CLAUDE];
1087
+ const promptAnthropic = async (aiId, content, options = {}) => {
1088
+ const { client, model } = await getAi(aiId);
1070
1089
  let [
1071
- _MODEL, event, text, thinking, signature, result, thinkEnd, tool_use,
1072
- responded, redacted_thinking,
1073
- ] = [
1074
- MODELS[options.model], null, '', '', '', options.result ?? '', '',
1075
- [], false, [],
1076
- ];
1077
- // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking
1078
- options?.test_redacted_thinking && !options?.result && (
1090
+ event, text, thinking, signature, result, thinkEnd, tool_use,
1091
+ responded, redacted_thinking
1092
+ ] = [null, '', '', '', options.result ?? '', '', [], false, []];
1093
+ options.model = options.model || model.name;
1094
+ options.test_redacted_thinking && !result && (
1079
1095
  content += '\n\nANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_'
1080
1096
  + '46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB'
1081
- );
1082
- const { client } = await getClaudeClient(options);
1097
+ ); // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking
1083
1098
  const { systemPrompt: system, history }
1084
- = await buildPrompts(_MODEL, content, { ...options, flavor: CLAUDE });
1099
+ = await buildPrompts(model, content, { ...options, flavor: CLAUDE });
1085
1100
  const resp = await client.beta.messages.create({
1086
- model: options.model,
1087
- max_tokens: options?.extendedThinking ? 128000 : _MODEL.maxOutputTokens,
1088
- ...history, system, stream: true,
1089
- ...options.reasoning ?? _MODEL?.reasoning ? {
1101
+ model: options.model, ...history, system, stream: true,
1102
+ max_tokens: options.extendedThinking ? 128000 : model.maxOutputTokens,
1103
+ ...(options.reasoning ?? model.reasoning) ? {
1090
1104
  thinking: options.thinking || {
1091
1105
  type: 'enabled',
1092
1106
  budget_tokens: options?.extendedThinking ? 16000 : 1024,
1093
1107
  },
1094
- } : {}, ..._MODEL?.tools ? {
1108
+ } : {}, ...model?.tools ? {
1095
1109
  tools: options.tools ?? (await toolsClaude()).map(x => x.def),
1096
1110
  tool_choice: { type: 'auto' }, betas: [
1097
1111
  // https://docs.anthropic.com/en/docs/build-with-claude/tool-use/token-efficient-tool-use
@@ -1136,7 +1150,7 @@ const promptClaude = async (content, options = {}) => {
1136
1150
  event, { ...options, result, flavor: CLAUDE },
1137
1151
  );
1138
1152
  if (tool_use.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
1139
- return await promptClaude(content, {
1153
+ return await promptAnthropic(aiId, content, {
1140
1154
  ...options, toolsResult: [...options.toolsResult || [],
1141
1155
  ...toolsResult], result: toolsResponse,
1142
1156
  });
@@ -1193,31 +1207,29 @@ const packGeminiReferences = (chunks, supports) => {
1193
1207
  return references;
1194
1208
  };
1195
1209
 
1196
- const promptGemini = async (content, options = {}) => {
1197
- options.model || (options.model = DEFAULT_MODELS[GEMINI]);
1198
- let [result, references, functionCalls, responded, _MODEL]
1199
- = [options.result ?? '', null, null, false, MODELS[options.model]];
1200
- const { client: _client } = await getGeminiClient(options);
1210
+ const promptGemini = async (aiId, content, options = {}) => {
1211
+ const { client, model } = await getAi(aiId);
1212
+ let [result, references, functionCalls, responded]
1213
+ = [options.result ?? '', null, null, false];
1214
+ options.model = options.model || model.name;
1201
1215
  const { systemPrompt: systemInstruction, history, prompt }
1202
- = await buildPrompts(_MODEL, content, { ...options, flavor: GEMINI });
1203
- const client = _client.getGenerativeModel({
1216
+ = await buildPrompts(model, content, { ...options, flavor: GEMINI });
1217
+ const _client = client.getGenerativeModel({
1204
1218
  model: options.model, systemInstruction,
1205
- ...MODELS[options.model]?.tools && !options.jsonMode ? (
1206
- options.tools ?? {
1207
- tools: [
1208
- // @todo: Gemini will failed when using these tools together.
1209
- // https://ai.google.dev/gemini-api/docs/function-calling
1210
- // { codeExecution: {} },
1211
- // { googleSearch: {} },
1212
- { functionDeclarations: (await toolsGemini()).map(x => x.def) },
1213
- ],
1214
- toolConfig: { functionCallingConfig: { mode: 'AUTO' } },
1215
- }
1216
- ) : {},
1219
+ ...model?.tools && !options.jsonMode ? (options.tools ?? {
1220
+ tools: [
1221
+ // @todo: Gemini will failed when using these tools together.
1222
+ // https://ai.google.dev/gemini-api/docs/function-calling
1223
+ // { codeExecution: {} },
1224
+ // { googleSearch: {} },
1225
+ { functionDeclarations: (await toolsGemini()).map(x => x.def) },
1226
+ ],
1227
+ toolConfig: { functionCallingConfig: { mode: 'AUTO' } },
1228
+ }) : {},
1217
1229
  });
1218
1230
  // https://github.com/google/generative-ai-js/blob/main/samples/node/advanced-chat.js
1219
1231
  // Google's bug: history is not allowed while using inline_data?
1220
- const chat = client.startChat({ history, ...generationConfig(options) });
1232
+ const chat = _client.startChat({ history, ...generationConfig(options) });
1221
1233
  const resp = await chat.sendMessageStream(prompt);
1222
1234
  for await (const chunk of resp.stream) {
1223
1235
  functionCalls || (functionCalls = chunk.functionCalls);
@@ -1242,10 +1254,11 @@ const promptGemini = async (content, options = {}) => {
1242
1254
  { role: MODEL, parts: functionCalls },
1243
1255
  { ...options, result, flavor: GEMINI }
1244
1256
  );
1245
- if (toolsResult.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
1246
- return promptGemini(content, {
1247
- ...options || {}, toolsResult: [...options?.toolsResult || [],
1248
- ...toolsResult], result: toolsResponse,
1257
+ if (toolsResult.length
1258
+ && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
1259
+ return promptGemini(aiId, content, {
1260
+ ...options || {}, result: toolsResponse,
1261
+ toolsResult: [...options?.toolsResult || [], ...toolsResult],
1249
1262
  });
1250
1263
  }
1251
1264
  return await packResp({
@@ -1442,9 +1455,9 @@ const talk = async (input, options) => {
1442
1455
  ...options,
1443
1456
  };
1444
1457
  switch (engine) {
1445
- case CHATGPT: resp = await promptChatGPT(input, pmtOptions); break;
1458
+ case CHATGPT: resp = await promptOpenAI(input, pmtOptions); break;
1446
1459
  case GEMINI: resp = await promptGemini(input, pmtOptions); break;
1447
- case CLAUDE: resp = await promptClaude(input, pmtOptions); break;
1460
+ case CLAUDE: resp = await promptAnthropic(input, pmtOptions); break;
1448
1461
  case OLLAMA: resp = await promptOllama(input, pmtOptions); break;
1449
1462
  case AZURE: resp = await promptAzure(input, pmtOptions); break;
1450
1463
  default: throwError(`Invalid AI engine: '${engine}'.`);
@@ -1568,17 +1581,17 @@ const analyzeSessions = async (sessionIds, options) => {
1568
1581
  };
1569
1582
 
1570
1583
  const PREFERRED_ENGINES = [
1571
- { client: OPENAI, func: promptChatGPT, multimodal: 0 },
1584
+ { client: OPENAI, func: promptOpenAI, multimodal: 0 },
1572
1585
  { client: GEMINI, func: promptGemini, multimodal: 1 },
1573
- { client: CLAUDE, func: promptClaude, multimodal: 2 },
1574
- { client: AZURE, func: promptAzure, multimodal: 3 },
1586
+ { client: CLAUDE, func: promptAnthropic, multimodal: 2 },
1587
+ // { client: AZURE, func: promptAzure, multimodal: 3 },
1575
1588
  { client: OLLAMA, func: promptOllama, multimodal: 99 },
1576
1589
  ]; // keep gpt first to avoid gemini grounding by default
1577
1590
 
1578
1591
  export default init;
1579
1592
  export {
1580
1593
  ATTACHMENT_TOKEN_COST, CLOUD_37_SONNET, CODE_INTERPRETER, DEEPSEEK_R1,
1581
- DEEPSEEK_R1_32B, DEEPSEEK_R1_70B, DEFAULT_MODELS,
1594
+ DEEPSEEK_R1_70B, DEFAULT_MODELS,
1582
1595
  EMBEDDING_001,
1583
1596
  FUNCTION, GEMINI_20_FLASH, GEMINI_20_FLASH_THINKING, GPT_45, GPT_4O, GPT_4O_MINI, GPT_O1, GPT_O3_MINI, INSTRUCTIONS, MODELS,
1584
1597
  OPENAI_VOICE, RETRIEVAL,
@@ -1597,13 +1610,14 @@ export {
1597
1610
  init,
1598
1611
  initChat,
1599
1612
  jpeg,
1613
+ getAi,
1600
1614
  listFiles,
1601
1615
  listGptFineTuningEvents,
1602
1616
  listGptFineTuningJobs,
1603
1617
  listOpenAIModels,
1604
1618
  ogg,
1605
- prompt, promptAzure, promptChatGPT,
1606
- promptClaude,
1619
+ prompt, promptOpenAI,
1620
+ promptAnthropic,
1607
1621
  promptGemini,
1608
1622
  promptOllama,
1609
1623
  resetSession,
package/lib/manifest.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  const manifest = {
2
2
  "name": "utilitas",
3
3
  "description": "Just another common utility for JavaScript.",
4
- "version": "1998.2.59",
4
+ "version": "1998.2.60",
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": "1998.2.59",
4
+ "version": "1998.2.60",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",