utilitas 1998.2.59 → 1998.2.61
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 +7 -9
- package/dist/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/lib/alan.mjs +206 -224
- package/lib/manifest.mjs +1 -2
- package/package.json +1 -2
package/lib/alan.mjs
CHANGED
|
@@ -42,32 +42,37 @@ You may be provided with some tools(functions) to help you gather information an
|
|
|
42
42
|
|
|
43
43
|
const _NEED = [
|
|
44
44
|
'@anthropic-ai/sdk', '@anthropic-ai/vertex-sdk', '@google/generative-ai',
|
|
45
|
-
'js-tiktoken', '
|
|
45
|
+
'js-tiktoken', 'OpenAI',
|
|
46
46
|
];
|
|
47
47
|
|
|
48
48
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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:
|
|
64
|
-
'text-embedding-3-
|
|
65
|
-
'
|
|
66
|
-
'
|
|
67
|
-
'
|
|
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 [
|
|
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
|
-
[
|
|
118
|
-
[
|
|
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
|
-
[
|
|
124
|
-
[
|
|
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
|
-
|
|
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,93 @@ 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:
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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
|
-
|
|
558
|
-
|
|
559
|
-
model:
|
|
560
|
-
|
|
568
|
+
// https://github.com/ollama/ollama/blob/main/docs/openai.md
|
|
569
|
+
ais[id] = {
|
|
570
|
+
id, provider, model, client: await OpenAI({
|
|
571
|
+
baseURL: 'http://localhost:11434/v1/', apiKey: 'ollama',
|
|
572
|
+
...options
|
|
573
|
+
}),
|
|
574
|
+
prompt: async (cnt, opts) => await promptOpenAI(id, cnt, opts),
|
|
575
|
+
};
|
|
561
576
|
break;
|
|
562
577
|
default:
|
|
563
|
-
throwError(`Invalid AI provider: ${options
|
|
578
|
+
throwError(`Invalid AI provider: ${options.provider || 'null'}.`);
|
|
579
|
+
}
|
|
580
|
+
return ais[id];
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const getAi = async (id, options) => {
|
|
584
|
+
if (id) {
|
|
585
|
+
if (ais[id]) { return options?.client ? ais[id]?.client : ais[id]; }
|
|
586
|
+
else { throwError(`AI not found: ${id}.`); }
|
|
564
587
|
}
|
|
565
|
-
|
|
566
|
-
return clients[provider];
|
|
588
|
+
return ais;
|
|
567
589
|
};
|
|
568
590
|
|
|
569
591
|
const countTokens = async (input, options) => {
|
|
@@ -619,14 +641,6 @@ const buildGptMessage = (content, options) => {
|
|
|
619
641
|
return message;
|
|
620
642
|
};
|
|
621
643
|
|
|
622
|
-
const buildOllamaMessage = (content, options) => {
|
|
623
|
-
const message = String.isString(content) ? {
|
|
624
|
-
role: options?.role || user, content,
|
|
625
|
-
} : content;
|
|
626
|
-
assertContent(message.content);
|
|
627
|
-
return message;
|
|
628
|
-
};
|
|
629
|
-
|
|
630
644
|
const buildGeminiParts = (text, attachments) => {
|
|
631
645
|
// Gemini API does not allow empty text, even you prompt with attachments.
|
|
632
646
|
const message = [...text?.length || attachments?.length ? [{
|
|
@@ -676,8 +690,8 @@ const buildGeminiHistory = (text, options) => buildGeminiMessage(
|
|
|
676
690
|
text, { ...options || {}, history: true }
|
|
677
691
|
);
|
|
678
692
|
|
|
679
|
-
const [getOpenAIClient, getGeminiClient,
|
|
680
|
-
= [OPENAI, GEMINI,
|
|
693
|
+
const [getOpenAIClient, getGeminiClient, getClaudeClient]
|
|
694
|
+
= [OPENAI, GEMINI, CLAUDE].map(
|
|
681
695
|
x => async options => await init({ ...provider(x), ...options })
|
|
682
696
|
);
|
|
683
697
|
|
|
@@ -723,8 +737,7 @@ const packResp = async (resp, options) => {
|
|
|
723
737
|
])) && (audio = await convert(audio, {
|
|
724
738
|
input: BUFFER, expected: BUFFER, ...options || {},
|
|
725
739
|
}));
|
|
726
|
-
options?.jsonMode && !options?.delta &&
|
|
727
|
-
&& (json = parseJson(simpleText));
|
|
740
|
+
options?.jsonMode && !options?.delta && (json = parseJson(simpleText, null));
|
|
728
741
|
if (options?.simple && options?.audioMode) { return audio; }
|
|
729
742
|
else if (options?.simple && options?.jsonMode) { return json; }
|
|
730
743
|
else if (options?.simple) { return simpleText; }
|
|
@@ -810,10 +823,6 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
810
823
|
systemPrompt = options.systemPrompt;
|
|
811
824
|
prompt = buildClaudeMessage(content, { ...options, cache_control: true });
|
|
812
825
|
break;
|
|
813
|
-
case OLLAMA:
|
|
814
|
-
systemPrompt = buildOllamaMessage(options.systemPrompt, _system);
|
|
815
|
-
prompt = buildOllamaMessage(content, options);
|
|
816
|
-
break;
|
|
817
826
|
case GEMINI:
|
|
818
827
|
const _role = { role: options.model === GEMMA_3_27B ? user : system };
|
|
819
828
|
systemPrompt = buildGeminiHistory(options.systemPrompt, _role);
|
|
@@ -833,10 +842,6 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
833
842
|
history.push(buildClaudeMessage(x.request, _user));
|
|
834
843
|
history.push(buildClaudeMessage(x.response, _assistant));
|
|
835
844
|
break;
|
|
836
|
-
case OLLAMA:
|
|
837
|
-
history.push(buildOllamaMessage(x.request, _user));
|
|
838
|
-
history.push(buildOllamaMessage(x.response, _assistant));
|
|
839
|
-
break;
|
|
840
845
|
case GEMINI:
|
|
841
846
|
if (options.attachments?.length) { return; }
|
|
842
847
|
history.push(buildGeminiHistory(x.request, _user));
|
|
@@ -845,9 +850,14 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
845
850
|
}
|
|
846
851
|
});
|
|
847
852
|
switch (options.flavor) {
|
|
848
|
-
case CHATGPT:
|
|
853
|
+
case CHATGPT:
|
|
854
|
+
history = messages([
|
|
855
|
+
systemPrompt, ...history, prompt,
|
|
856
|
+
...options.toolsResult?.length ? options.toolsResult : []
|
|
857
|
+
]);
|
|
858
|
+
break;
|
|
859
|
+
case CLAUDE:
|
|
849
860
|
history = messages([
|
|
850
|
-
...options.flavor === CLAUDE ? [] : [systemPrompt],
|
|
851
861
|
...history, prompt,
|
|
852
862
|
...options.toolsResult?.length ? options.toolsResult : []
|
|
853
863
|
]);
|
|
@@ -872,8 +882,7 @@ const buildPrompts = async (model, input, options = {}) => {
|
|
|
872
882
|
content = trimTailing(trimTailing(content).slice(0, -1)) + '...';
|
|
873
883
|
}
|
|
874
884
|
}, model.maxInputTokens - options.attachments?.length * ATTACHMENT_TOKEN_COST);
|
|
875
|
-
if ([CHATGPT
|
|
876
|
-
|| options.model === GEMMA_3_27B) {
|
|
885
|
+
if ([CHATGPT].includes(options.flavor) || options.model === GEMMA_3_27B) {
|
|
877
886
|
systemPrompt = null;
|
|
878
887
|
}
|
|
879
888
|
return { systemPrompt, history, prompt };
|
|
@@ -972,36 +981,32 @@ const mergeMsgs = (resp, calls) => [resp, ...calls.length ? [
|
|
|
972
981
|
`⚠️ Tools recursion limit reached: ${MAX_TOOL_RECURSION}`
|
|
973
982
|
] : []].map(x => x.trim()).join('\n\n');
|
|
974
983
|
|
|
975
|
-
const
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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
|
|
984
|
+
const promptOpenAI = async (aiId, content, options = {}) => {
|
|
985
|
+
let { provider, client, model } = await getAi(aiId);
|
|
986
|
+
let [result, resultAudio, event, resultTools, responded, azure] = [
|
|
987
|
+
options.result ?? '', Buffer.alloc(0), null, [], false,
|
|
988
|
+
provider === AZURE
|
|
986
989
|
];
|
|
987
|
-
options.
|
|
988
|
-
|
|
989
|
-
const { client } = await getOpenAIClient(options);
|
|
990
|
+
options.flavor = CHATGPT;
|
|
991
|
+
options.model = options.model || model.name;
|
|
990
992
|
const { history }
|
|
991
|
-
= await buildPrompts(
|
|
993
|
+
= await buildPrompts(MODELS[options.model], content, options);
|
|
994
|
+
model = MODELS[options.model];
|
|
995
|
+
model.reasoning && !azure && !options.reasoning_effort
|
|
996
|
+
&& (options.reasoning_effort = GPT_REASONING_EFFORT);
|
|
992
997
|
const modalities = options.modalities
|
|
993
998
|
|| (options.audioMode ? [TEXT, AUDIO] : undefined);
|
|
994
999
|
[options.audioMimeType, options.suffix] = [pcm16, 'pcm.wav'];
|
|
995
1000
|
const resp = await client.chat.completions.create({
|
|
1001
|
+
model: azure ? undefined : options.model, ...history,
|
|
1002
|
+
...options.jsonMode ? { response_format: { type: JSON_OBJECT } } : {},
|
|
996
1003
|
modalities, audio: options.audio || (
|
|
997
1004
|
modalities?.find?.(x => x === AUDIO)
|
|
998
1005
|
&& { voice: DEFAULT_MODELS[OPENAI_VOICE], format: 'pcm16' }
|
|
999
|
-
), ...
|
|
1006
|
+
), ...model.tools && !azure ? {
|
|
1000
1007
|
tools: options.tools ?? (await toolsOpenAI()).map(x => x.def),
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
} : {}, model: options.model, stream: true,
|
|
1004
|
-
store: true, tool_choice: 'auto',
|
|
1008
|
+
tool_choice: 'auto',
|
|
1009
|
+
} : {}, ...azure ? {} : { store: true }, stream: true,
|
|
1005
1010
|
});
|
|
1006
1011
|
for await (event of resp) {
|
|
1007
1012
|
event = event?.choices?.[0] || {};
|
|
@@ -1016,9 +1021,11 @@ const promptChatGPT = async (content, options = {}) => {
|
|
|
1016
1021
|
isSet(x.index, true) && (curFunc.index = x.index);
|
|
1017
1022
|
x.id && (curFunc.id = x.id);
|
|
1018
1023
|
x.type && (curFunc.type = x.type);
|
|
1019
|
-
curFunc.function
|
|
1024
|
+
curFunc.function
|
|
1025
|
+
|| (curFunc.function = { name: '', arguments: '' });
|
|
1020
1026
|
x?.function?.name && (curFunc.function.name += x.function.name);
|
|
1021
|
-
x?.function?.arguments
|
|
1027
|
+
x?.function?.arguments
|
|
1028
|
+
&& (curFunc.function.arguments += x.function.arguments);
|
|
1022
1029
|
}
|
|
1023
1030
|
options.result && deltaText
|
|
1024
1031
|
&& (responded = responded || (deltaText = `\n\n${deltaText}`));
|
|
@@ -1036,62 +1043,38 @@ const promptChatGPT = async (content, options = {}) => {
|
|
|
1036
1043
|
};
|
|
1037
1044
|
const { toolsResult, toolsResponse }
|
|
1038
1045
|
= await handleToolsCall(event, { ...options, result });
|
|
1039
|
-
if (toolsResult.length
|
|
1040
|
-
|
|
1046
|
+
if (toolsResult.length
|
|
1047
|
+
&& countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
|
|
1048
|
+
return promptOpenAI(aiId, content, {
|
|
1049
|
+
...options, toolsResult, result: toolsResponse,
|
|
1050
|
+
});
|
|
1041
1051
|
}
|
|
1042
1052
|
event.text = mergeMsgs(toolsResponse, toolsResult);
|
|
1043
1053
|
return await packResp(event, options);
|
|
1044
1054
|
};
|
|
1045
1055
|
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
const promptOllama = async (content, options = {}) => {
|
|
1050
|
-
const { client, model } = await getOllamaClient(options);
|
|
1051
|
-
// https://github.com/ollama/ollama-js
|
|
1052
|
-
// 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 });
|
|
1058
|
-
for await (chunk of resp) {
|
|
1059
|
-
const delta = chunk.message.content || '';
|
|
1060
|
-
result += delta;
|
|
1061
|
-
delta && await streamResp({
|
|
1062
|
-
text: options.delta ? delta : result,
|
|
1063
|
-
}, options);
|
|
1064
|
-
}
|
|
1065
|
-
return await packResp({ text: result }, options);
|
|
1066
|
-
};
|
|
1067
|
-
|
|
1068
|
-
const promptClaude = async (content, options = {}) => {
|
|
1069
|
-
options.model = options.model || DEFAULT_MODELS[CLAUDE];
|
|
1056
|
+
const promptAnthropic = async (aiId, content, options = {}) => {
|
|
1057
|
+
const { client, model } = await getAi(aiId);
|
|
1070
1058
|
let [
|
|
1071
|
-
|
|
1072
|
-
responded, redacted_thinking
|
|
1073
|
-
] = [
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
];
|
|
1077
|
-
// https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking
|
|
1078
|
-
options?.test_redacted_thinking && !options?.result && (
|
|
1059
|
+
event, text, thinking, signature, result, thinkEnd, tool_use,
|
|
1060
|
+
responded, redacted_thinking
|
|
1061
|
+
] = [null, '', '', '', options.result ?? '', '', [], false, []];
|
|
1062
|
+
options.model = options.model || model.name;
|
|
1063
|
+
options.test_redacted_thinking && !result && (
|
|
1079
1064
|
content += '\n\nANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_'
|
|
1080
1065
|
+ '46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB'
|
|
1081
|
-
);
|
|
1082
|
-
const { client } = await getClaudeClient(options);
|
|
1066
|
+
); // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking
|
|
1083
1067
|
const { systemPrompt: system, history }
|
|
1084
|
-
= await buildPrompts(
|
|
1068
|
+
= await buildPrompts(model, content, { ...options, flavor: CLAUDE });
|
|
1085
1069
|
const resp = await client.beta.messages.create({
|
|
1086
|
-
model: options.model,
|
|
1087
|
-
max_tokens: options
|
|
1088
|
-
...
|
|
1089
|
-
...options.reasoning ?? _MODEL?.reasoning ? {
|
|
1070
|
+
model: options.model, ...history, system, stream: true,
|
|
1071
|
+
max_tokens: options.extendedThinking ? 128000 : model.maxOutputTokens,
|
|
1072
|
+
...(options.reasoning ?? model.reasoning) ? {
|
|
1090
1073
|
thinking: options.thinking || {
|
|
1091
1074
|
type: 'enabled',
|
|
1092
1075
|
budget_tokens: options?.extendedThinking ? 16000 : 1024,
|
|
1093
1076
|
},
|
|
1094
|
-
} : {}, ...
|
|
1077
|
+
} : {}, ...model?.tools ? {
|
|
1095
1078
|
tools: options.tools ?? (await toolsClaude()).map(x => x.def),
|
|
1096
1079
|
tool_choice: { type: 'auto' }, betas: [
|
|
1097
1080
|
// https://docs.anthropic.com/en/docs/build-with-claude/tool-use/token-efficient-tool-use
|
|
@@ -1136,7 +1119,7 @@ const promptClaude = async (content, options = {}) => {
|
|
|
1136
1119
|
event, { ...options, result, flavor: CLAUDE },
|
|
1137
1120
|
);
|
|
1138
1121
|
if (tool_use.length && countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
|
|
1139
|
-
return await
|
|
1122
|
+
return await promptAnthropic(aiId, content, {
|
|
1140
1123
|
...options, toolsResult: [...options.toolsResult || [],
|
|
1141
1124
|
...toolsResult], result: toolsResponse,
|
|
1142
1125
|
});
|
|
@@ -1193,31 +1176,29 @@ const packGeminiReferences = (chunks, supports) => {
|
|
|
1193
1176
|
return references;
|
|
1194
1177
|
};
|
|
1195
1178
|
|
|
1196
|
-
const promptGemini = async (content, options = {}) => {
|
|
1197
|
-
|
|
1198
|
-
let [result, references, functionCalls, responded
|
|
1199
|
-
= [options.result ?? '', null, null, false
|
|
1200
|
-
|
|
1179
|
+
const promptGemini = async (aiId, content, options = {}) => {
|
|
1180
|
+
const { client, model } = await getAi(aiId);
|
|
1181
|
+
let [result, references, functionCalls, responded]
|
|
1182
|
+
= [options.result ?? '', null, null, false];
|
|
1183
|
+
options.model = options.model || model.name;
|
|
1201
1184
|
const { systemPrompt: systemInstruction, history, prompt }
|
|
1202
|
-
= await buildPrompts(
|
|
1203
|
-
const
|
|
1185
|
+
= await buildPrompts(model, content, { ...options, flavor: GEMINI });
|
|
1186
|
+
const _client = client.getGenerativeModel({
|
|
1204
1187
|
model: options.model, systemInstruction,
|
|
1205
|
-
...
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
}
|
|
1216
|
-
) : {},
|
|
1188
|
+
...model?.tools && !options.jsonMode ? (options.tools ?? {
|
|
1189
|
+
tools: [
|
|
1190
|
+
// @todo: Gemini will failed when using these tools together.
|
|
1191
|
+
// https://ai.google.dev/gemini-api/docs/function-calling
|
|
1192
|
+
// { codeExecution: {} },
|
|
1193
|
+
// { googleSearch: {} },
|
|
1194
|
+
{ functionDeclarations: (await toolsGemini()).map(x => x.def) },
|
|
1195
|
+
],
|
|
1196
|
+
toolConfig: { functionCallingConfig: { mode: 'AUTO' } },
|
|
1197
|
+
}) : {},
|
|
1217
1198
|
});
|
|
1218
1199
|
// https://github.com/google/generative-ai-js/blob/main/samples/node/advanced-chat.js
|
|
1219
1200
|
// Google's bug: history is not allowed while using inline_data?
|
|
1220
|
-
const chat =
|
|
1201
|
+
const chat = _client.startChat({ history, ...generationConfig(options) });
|
|
1221
1202
|
const resp = await chat.sendMessageStream(prompt);
|
|
1222
1203
|
for await (const chunk of resp.stream) {
|
|
1223
1204
|
functionCalls || (functionCalls = chunk.functionCalls);
|
|
@@ -1242,10 +1223,11 @@ const promptGemini = async (content, options = {}) => {
|
|
|
1242
1223
|
{ role: MODEL, parts: functionCalls },
|
|
1243
1224
|
{ ...options, result, flavor: GEMINI }
|
|
1244
1225
|
);
|
|
1245
|
-
if (toolsResult.length
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
...
|
|
1226
|
+
if (toolsResult.length
|
|
1227
|
+
&& countToolCalls(toolsResponse) < MAX_TOOL_RECURSION) {
|
|
1228
|
+
return promptGemini(aiId, content, {
|
|
1229
|
+
...options || {}, result: toolsResponse,
|
|
1230
|
+
toolsResult: [...options?.toolsResult || [], ...toolsResult],
|
|
1249
1231
|
});
|
|
1250
1232
|
}
|
|
1251
1233
|
return await packResp({
|
|
@@ -1442,10 +1424,10 @@ const talk = async (input, options) => {
|
|
|
1442
1424
|
...options,
|
|
1443
1425
|
};
|
|
1444
1426
|
switch (engine) {
|
|
1445
|
-
case CHATGPT: resp = await
|
|
1427
|
+
case CHATGPT: resp = await promptOpenAI(input, pmtOptions); break;
|
|
1446
1428
|
case GEMINI: resp = await promptGemini(input, pmtOptions); break;
|
|
1447
|
-
case CLAUDE: resp = await
|
|
1448
|
-
case OLLAMA: resp = await promptOllama(input, pmtOptions); break;
|
|
1429
|
+
case CLAUDE: resp = await promptAnthropic(input, pmtOptions); break;
|
|
1430
|
+
// case OLLAMA: resp = await promptOllama(input, pmtOptions); break;
|
|
1449
1431
|
case AZURE: resp = await promptAzure(input, pmtOptions); break;
|
|
1450
1432
|
default: throwError(`Invalid AI engine: '${engine}'.`);
|
|
1451
1433
|
}
|
|
@@ -1568,17 +1550,17 @@ const analyzeSessions = async (sessionIds, options) => {
|
|
|
1568
1550
|
};
|
|
1569
1551
|
|
|
1570
1552
|
const PREFERRED_ENGINES = [
|
|
1571
|
-
{ client: OPENAI, func:
|
|
1553
|
+
{ client: OPENAI, func: promptOpenAI, multimodal: 0 },
|
|
1572
1554
|
{ client: GEMINI, func: promptGemini, multimodal: 1 },
|
|
1573
|
-
{ client: CLAUDE, func:
|
|
1574
|
-
{ client: AZURE, func: promptAzure, multimodal: 3 },
|
|
1575
|
-
{ client: OLLAMA, func: promptOllama, multimodal: 99 },
|
|
1555
|
+
{ client: CLAUDE, func: promptAnthropic, multimodal: 2 },
|
|
1556
|
+
// { client: AZURE, func: promptAzure, multimodal: 3 },
|
|
1557
|
+
// { client: OLLAMA, func: promptOllama, multimodal: 99 },
|
|
1576
1558
|
]; // keep gpt first to avoid gemini grounding by default
|
|
1577
1559
|
|
|
1578
1560
|
export default init;
|
|
1579
1561
|
export {
|
|
1580
1562
|
ATTACHMENT_TOKEN_COST, CLOUD_37_SONNET, CODE_INTERPRETER, DEEPSEEK_R1,
|
|
1581
|
-
|
|
1563
|
+
DEEPSEEK_R1_70B, DEFAULT_MODELS,
|
|
1582
1564
|
EMBEDDING_001,
|
|
1583
1565
|
FUNCTION, GEMINI_20_FLASH, GEMINI_20_FLASH_THINKING, GPT_45, GPT_4O, GPT_4O_MINI, GPT_O1, GPT_O3_MINI, INSTRUCTIONS, MODELS,
|
|
1584
1566
|
OPENAI_VOICE, RETRIEVAL,
|
|
@@ -1597,15 +1579,15 @@ export {
|
|
|
1597
1579
|
init,
|
|
1598
1580
|
initChat,
|
|
1599
1581
|
jpeg,
|
|
1582
|
+
getAi,
|
|
1600
1583
|
listFiles,
|
|
1601
1584
|
listGptFineTuningEvents,
|
|
1602
1585
|
listGptFineTuningJobs,
|
|
1603
1586
|
listOpenAIModels,
|
|
1604
1587
|
ogg,
|
|
1605
|
-
prompt,
|
|
1606
|
-
|
|
1588
|
+
prompt, promptOpenAI,
|
|
1589
|
+
promptAnthropic,
|
|
1607
1590
|
promptGemini,
|
|
1608
|
-
promptOllama,
|
|
1609
1591
|
resetSession,
|
|
1610
1592
|
tailGptFineTuningEvents,
|
|
1611
1593
|
talk,
|