utilitas 1995.2.37 → 1995.2.39
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 +4 -13
- package/dist/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/index.mjs +2 -3
- package/lib/alan.mjs +171 -74
- package/lib/manifest.mjs +1 -2
- package/package.json +1 -2
- package/lib/hal.mjs +0 -139
package/index.mjs
CHANGED
|
@@ -13,7 +13,6 @@ import * as dbio from './lib/dbio.mjs';
|
|
|
13
13
|
import * as email from './lib/email.mjs';
|
|
14
14
|
import * as encryption from './lib/encryption.mjs';
|
|
15
15
|
import * as event from './lib/event.mjs';
|
|
16
|
-
import * as hal from './lib/hal.mjs';
|
|
17
16
|
import * as image from './lib/image.mjs';
|
|
18
17
|
import * as media from './lib/media.mjs';
|
|
19
18
|
import * as memory from './lib/memory.mjs';
|
|
@@ -41,8 +40,8 @@ export {
|
|
|
41
40
|
fileType, math, uuid,
|
|
42
41
|
// features
|
|
43
42
|
alan, bot, boxes, cache, callosum, color, dbio, email, encryption, event,
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
image, manifest, media, memory, network, sentinel, shekel, shell, shot, sms,
|
|
44
|
+
speech, ssl, storage, tape, uoid, utilitas, vision, web
|
|
46
45
|
};
|
|
47
46
|
|
|
48
47
|
if (utilitas.inBrowser() && !globalThis.utilitas) {
|
package/lib/alan.mjs
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { convert } from './storage.mjs';
|
|
2
2
|
import { create as createUoid } from './uoid.mjs';
|
|
3
|
-
import { ensureString, ignoreErrFunc, need, throwError } from './utilitas.mjs';
|
|
4
3
|
import { loop, end } from './event.mjs';
|
|
5
4
|
|
|
5
|
+
import {
|
|
6
|
+
ensureString, ignoreErrFunc, log as _log, need, renderText as _renderText,
|
|
7
|
+
throwError,
|
|
8
|
+
} from './utilitas.mjs';
|
|
9
|
+
|
|
6
10
|
const _NEED = [
|
|
7
11
|
'@google-cloud/aiplatform', '@google-cloud/vertexai',
|
|
8
12
|
'@google/generative-ai', 'js-tiktoken', 'OpenAI',
|
|
@@ -24,7 +28,7 @@ const [tool, provider, messages, text] = [
|
|
|
24
28
|
messages => ({ messages }), text => ({ text }),
|
|
25
29
|
];
|
|
26
30
|
|
|
27
|
-
const [name, user, system, assistant,
|
|
31
|
+
const [name, user, system, assistant, MODEL]
|
|
28
32
|
= ['Alan', 'user', 'system', 'assistant', 'model'];
|
|
29
33
|
const [CODE_INTERPRETER, RETRIEVAL, FUNCTION]
|
|
30
34
|
= ['code_interpreter', 'retrieval', 'function'].map(tool);
|
|
@@ -33,15 +37,19 @@ const [NOT_INIT, INVALID_FILE]
|
|
|
33
37
|
const [silent, STREAM, instructions]
|
|
34
38
|
= [true, 'STREAM', 'You are a helpful assistant.'];
|
|
35
39
|
const GOOGLE_APPLICATION_CREDENTIALS = 'GOOGLE_APPLICATION_CREDENTIALS';
|
|
36
|
-
const [tokenRatio, tokenSafeRatio, GPT_QUERY_LIMIT
|
|
40
|
+
const [tokenRatio, tokenSafeRatio, GPT_QUERY_LIMIT, minsOfDay]
|
|
41
|
+
= [100 / 75, 1.1, 100, 60 * 24]; // https://platform.openai.com/tokenizer
|
|
37
42
|
const tokenSafe = count => Math.ceil(count * tokenSafeRatio);
|
|
38
|
-
const countTokens = t => tokenSafe(t.split(/[^a-z0-9]/i).length * tokenRatio);
|
|
39
43
|
const clients = {};
|
|
40
|
-
const
|
|
44
|
+
const LOG = { log: true };
|
|
41
45
|
const sessionType = `${name.toUpperCase()}-SESSION`;
|
|
42
46
|
const unifyProvider = options => unifyType(options?.provider, 'AI provider');
|
|
43
47
|
const unifyEngine = options => unifyType(options?.engine, 'AI engine');
|
|
44
48
|
const packResp = _text => [text(_text)];
|
|
49
|
+
const trimTailing = text => text.replace(/[\.\s]*$/, '');
|
|
50
|
+
const newSessionId = () => createUoid({ type: sessionType });
|
|
51
|
+
const renderText = (t, o) => _renderText(t, { extraCodeBlock: 0, ...o || {} });
|
|
52
|
+
const log = (cnt, opt) => _log(cnt, import.meta.url, { time: 1, ...opt || {} });
|
|
45
53
|
|
|
46
54
|
const [
|
|
47
55
|
OPENAI, VERTEX, GEMINI, CHATGPT, ASSISTANT, OPENAI_EMBEDDING,
|
|
@@ -70,6 +78,8 @@ const chatConfig = {
|
|
|
70
78
|
|
|
71
79
|
// https://platform.openai.com/docs/models/continuous-model-upgrades
|
|
72
80
|
// https://platform.openai.com/account/limits // Tier 2
|
|
81
|
+
// https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini
|
|
82
|
+
// https://cloud.google.com/vertex-ai/docs/generative-ai/learn/models
|
|
73
83
|
const MODELS = {
|
|
74
84
|
[GPT_35_TURBO]: {
|
|
75
85
|
contextWindow: 4096,
|
|
@@ -79,7 +89,7 @@ const MODELS = {
|
|
|
79
89
|
},
|
|
80
90
|
[GPT_35_TURBO_1106]: {
|
|
81
91
|
contextWindow: 16385,
|
|
82
|
-
|
|
92
|
+
maxOutputTokens: 4096,
|
|
83
93
|
tokenLimitsTPM: 160000,
|
|
84
94
|
requestLimitsRPM: 5000,
|
|
85
95
|
trainingData: 'Sep 2021',
|
|
@@ -92,7 +102,7 @@ const MODELS = {
|
|
|
92
102
|
},
|
|
93
103
|
[GPT_4_1106]: {
|
|
94
104
|
contextWindow: 128000,
|
|
95
|
-
|
|
105
|
+
maxOutputTokens: 4096,
|
|
96
106
|
tokenLimitsTPM: 300000,
|
|
97
107
|
tokenLimitsTPD: 5000000,
|
|
98
108
|
requestLimitsRPM: 5000,
|
|
@@ -100,12 +110,30 @@ const MODELS = {
|
|
|
100
110
|
},
|
|
101
111
|
[GPT_4_VISION]: {
|
|
102
112
|
contextWindow: 128000,
|
|
103
|
-
|
|
113
|
+
maxOutputTokens: 4096,
|
|
104
114
|
tokenLimitsTPM: 40000,
|
|
105
115
|
requestLimitsRPM: 120,
|
|
106
116
|
requestLimitsRPD: 1500,
|
|
107
117
|
trainingData: 'Apr 2023',
|
|
108
118
|
},
|
|
119
|
+
[GEMINI_PRO]: {
|
|
120
|
+
contextWindow: 32760,
|
|
121
|
+
maxOutputTokens: 8192,
|
|
122
|
+
trainingData: 'Feb 2023',
|
|
123
|
+
},
|
|
124
|
+
[GEMINI_PRO_VISION]: {
|
|
125
|
+
contextWindow: 16384,
|
|
126
|
+
maxOutputTokens: 2048,
|
|
127
|
+
maxImageSize: Infinity,
|
|
128
|
+
maxFileSize: 20 * 1024 * 1024, // 20 MB
|
|
129
|
+
maxImagePerPrompt: 16,
|
|
130
|
+
maxVideoLength: 60 * 2,
|
|
131
|
+
trainingData: 'Feb 2023',
|
|
132
|
+
supportedMimeTypes: [
|
|
133
|
+
'image/png', 'image/jpeg', 'video/mov', 'video/mpeg', 'video/mp4',
|
|
134
|
+
'video/mpg', 'video/avi', 'video/wmv', 'video/mpegps', 'video/flv',
|
|
135
|
+
],
|
|
136
|
+
},
|
|
109
137
|
[TEXT_EMBEDDING_ADA_002]: {
|
|
110
138
|
contextWindow: 5000000,
|
|
111
139
|
requestLimitsRPM: 5000,
|
|
@@ -116,19 +144,17 @@ const MODELS = {
|
|
|
116
144
|
for (const n in MODELS) {
|
|
117
145
|
MODELS[n]['name'] = n;
|
|
118
146
|
if ([TEXT_EMBEDDING_ADA_002].includes(n)) { continue; }
|
|
119
|
-
MODELS[n].
|
|
120
|
-
= MODELS[n].maximumOutputTokens
|
|
147
|
+
MODELS[n].maxOutputTokens = MODELS[n].maxOutputTokens
|
|
121
148
|
|| Math.ceil(MODELS[n].contextWindow * 0.4);
|
|
122
|
-
MODELS[n].
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
= MODELS[n].requestLimitsRPD || (MODELS[n].requestLimitsRPM * 60 * 24);
|
|
149
|
+
MODELS[n].maxInputTokens = MODELS[n].maxInputTokens
|
|
150
|
+
|| (MODELS[n].contextWindow - MODELS[n].maxOutputTokens);
|
|
151
|
+
MODELS[n].tokenLimitsTPD = MODELS[n].tokenLimitsTPD
|
|
152
|
+
|| (MODELS[n].tokenLimitsTPM * minsOfDay);
|
|
153
|
+
MODELS[n].requestLimitsRPD = MODELS[n].requestLimitsRPD
|
|
154
|
+
|| (MODELS[n].requestLimitsRPM * minsOfDay);
|
|
129
155
|
MODELS[n].requestCapacityRPM = Math.ceil(Math.min(
|
|
130
|
-
MODELS[n].tokenLimitsTPM / MODELS[n].
|
|
131
|
-
MODELS[n].requestLimitsRPM, MODELS[n].requestLimitsRPD /
|
|
156
|
+
MODELS[n].tokenLimitsTPM / MODELS[n].maxInputTokens,
|
|
157
|
+
MODELS[n].requestLimitsRPM, MODELS[n].requestLimitsRPD / minsOfDay
|
|
132
158
|
));
|
|
133
159
|
}
|
|
134
160
|
|
|
@@ -144,14 +170,14 @@ const init = async (options) => {
|
|
|
144
170
|
case OPENAI:
|
|
145
171
|
if (options?.apiKey) {
|
|
146
172
|
const OpenAI = await need('openai');
|
|
147
|
-
const { getEncoding } = await need('js-tiktoken');
|
|
173
|
+
// const { getEncoding } = await need('js-tiktoken');
|
|
148
174
|
const openai = new OpenAI(options);
|
|
149
175
|
clients[provider] = {
|
|
150
176
|
client: openai, clientBeta: openai.beta,
|
|
151
|
-
tokeniser: getEncoding(options?.tokenModel || 'cl100k_base'),
|
|
152
|
-
countTokens: text => tokenSafe(
|
|
153
|
-
|
|
154
|
-
),
|
|
177
|
+
// tokeniser: getEncoding(options?.tokenModel || 'cl100k_base'),
|
|
178
|
+
// countTokens: text => tokenSafe(
|
|
179
|
+
// clients[provider].tokeniser.encode(text).length
|
|
180
|
+
// ),
|
|
155
181
|
};
|
|
156
182
|
}
|
|
157
183
|
break;
|
|
@@ -160,17 +186,21 @@ const init = async (options) => {
|
|
|
160
186
|
process.env[GOOGLE_APPLICATION_CREDENTIALS] = options.credentials;
|
|
161
187
|
const { VertexAI } = await need('@google-cloud/vertexai');
|
|
162
188
|
const aiplatform = await need('@google-cloud/aiplatform');
|
|
163
|
-
const [project, location]
|
|
164
|
-
|
|
189
|
+
const [project, location] = [
|
|
190
|
+
options?.project,
|
|
191
|
+
options?.location || 'northamerica-northeast1', // 'us-east4'
|
|
192
|
+
];
|
|
165
193
|
clients[provider] = {
|
|
166
|
-
project, location, helpers: aiplatform.helpers, countTokens,
|
|
194
|
+
project, location, helpers: aiplatform.helpers, // countTokens,
|
|
167
195
|
generative: (new VertexAI({
|
|
168
196
|
project, location,
|
|
169
197
|
})).preview.getGenerativeModel({
|
|
170
198
|
model: options?.model || DEFAULT_MODELS[VERTEX],
|
|
171
199
|
generation_config: {
|
|
172
|
-
|
|
173
|
-
|
|
200
|
+
// https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini
|
|
201
|
+
// max_output_tokens: 2048, temperature: 0.4,
|
|
202
|
+
// top_p: 1, top_k: 32,
|
|
203
|
+
...options?.generation_config || {},
|
|
174
204
|
},
|
|
175
205
|
}),
|
|
176
206
|
prediction: new aiplatform.v1.PredictionServiceClient({
|
|
@@ -190,7 +220,7 @@ const init = async (options) => {
|
|
|
190
220
|
embedding: genAi.getGenerativeModel({
|
|
191
221
|
model: DEFAULT_MODELS[GEMINI_EMEDDING],
|
|
192
222
|
}),
|
|
193
|
-
countTokens,
|
|
223
|
+
// countTokens,
|
|
194
224
|
};
|
|
195
225
|
}
|
|
196
226
|
break;
|
|
@@ -201,6 +231,10 @@ const init = async (options) => {
|
|
|
201
231
|
return clients[provider];
|
|
202
232
|
};
|
|
203
233
|
|
|
234
|
+
const countTokens = input => tokenSafe((
|
|
235
|
+
String.isString(input) ? input : JSON.stringify(input)
|
|
236
|
+
).split(/[^a-z0-9]/i).length * tokenRatio);
|
|
237
|
+
|
|
204
238
|
const buildGptMessage = (content, options) => {
|
|
205
239
|
assert(content, 'Content is required.');
|
|
206
240
|
return String.isString(content) ? {
|
|
@@ -250,7 +284,7 @@ const promptChatGPT = async (content, options) => {
|
|
|
250
284
|
};
|
|
251
285
|
await ignoreErrFunc(async () => await options?.stream?.(
|
|
252
286
|
packGptResp(chunk, options)
|
|
253
|
-
),
|
|
287
|
+
), LOG);
|
|
254
288
|
}
|
|
255
289
|
return { response: packGptResp(chunk, options) };
|
|
256
290
|
};
|
|
@@ -360,7 +394,7 @@ const promptAssistant = async (content, options) => {
|
|
|
360
394
|
const resp = await getRun(thread.id, objRun.id);
|
|
361
395
|
await ignoreErrFunc(async () => await options?.stream?.(
|
|
362
396
|
options?.raw ? resp : packResp('')
|
|
363
|
-
),
|
|
397
|
+
), LOG);
|
|
364
398
|
if (resp?.status !== 'completed') { return; }
|
|
365
399
|
resolve(resp);
|
|
366
400
|
} catch (err) {
|
|
@@ -454,7 +488,7 @@ const handleGeminiResponse = async (resp, options) => {
|
|
|
454
488
|
for await (const chunk of _resp.stream) {
|
|
455
489
|
await ignoreErrFunc(async () => await options.stream(
|
|
456
490
|
options?.raw ? chunk : chunk.candidates[0].content.parts
|
|
457
|
-
),
|
|
491
|
+
), LOG);
|
|
458
492
|
}
|
|
459
493
|
}
|
|
460
494
|
const result = await _resp.response;
|
|
@@ -616,6 +650,8 @@ const initChat = async (options) => {
|
|
|
616
650
|
assert(model, `Invalid chat model: '${i}'.`);
|
|
617
651
|
chatConfig.engines[key] = options.engines[i];
|
|
618
652
|
chatConfig.engines[key].model = chatConfig.engines[key].model || model;
|
|
653
|
+
const mxPmpt = MODELS[chatConfig.engines[key].model].maxInputTokens / 2;
|
|
654
|
+
let pmptTokens = 0;
|
|
619
655
|
switch (key) {
|
|
620
656
|
case ASSISTANT:
|
|
621
657
|
chatConfig.engines[key].assistantId
|
|
@@ -628,62 +664,108 @@ const initChat = async (options) => {
|
|
|
628
664
|
}
|
|
629
665
|
})).id;
|
|
630
666
|
break;
|
|
667
|
+
case CHATGPT: case GEMINI: case VERTEX:
|
|
668
|
+
pmptTokens = countTokens([buildVertexMessage(
|
|
669
|
+
chatConfig.systemPrompt, { role: system }
|
|
670
|
+
)]); // Use vertex instead of chatgpt because of the longer pack
|
|
671
|
+
assert(
|
|
672
|
+
pmptTokens < mxPmpt,
|
|
673
|
+
`System prompt is too long: ${pmptTokens} / ${mxPmpt} tokens.`
|
|
674
|
+
);
|
|
675
|
+
break;
|
|
631
676
|
}
|
|
632
677
|
}
|
|
633
678
|
return chatConfig;
|
|
634
679
|
};
|
|
635
680
|
|
|
681
|
+
const defaultSession = session => ({
|
|
682
|
+
messages: [], systemPrompt: chatConfig.systemPrompt,
|
|
683
|
+
threadId: null, ...session || {},
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
const assertSessionId = sessionId => {
|
|
687
|
+
sessionId = ensureString(sessionId, { case: 'UP' });
|
|
688
|
+
assert(sessionId, 'Session ID is required.');
|
|
689
|
+
return sessionId;
|
|
690
|
+
};
|
|
691
|
+
|
|
636
692
|
const getSession = async (sessionId, options) => {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
)) || {},
|
|
642
|
-
}, []];
|
|
643
|
-
switch (options?.engine) {
|
|
644
|
-
case CHATGPT:
|
|
645
|
-
session.messages.map(x => {
|
|
646
|
-
messages.push(buildGptMessage(x.request, { role: user }));
|
|
647
|
-
messages.push(buildGptMessage(x.response, { role: assistant }));
|
|
648
|
-
});
|
|
649
|
-
break;
|
|
650
|
-
case GEMINI:
|
|
651
|
-
case VERTEX:
|
|
652
|
-
session.messages.map(x => {
|
|
653
|
-
messages.push(buildVertexMessage(x.request, { role: user }));
|
|
654
|
-
messages.push(buildVertexMessage(x.response, { role: model }));
|
|
655
|
-
});
|
|
656
|
-
break;
|
|
657
|
-
}
|
|
658
|
-
messages.length && (session.messages = messages);
|
|
659
|
-
return session;
|
|
693
|
+
sessionId = assertSessionId(sessionId);
|
|
694
|
+
return defaultSession(await chatConfig.sessions.get(
|
|
695
|
+
sessionId, options?.prompt, options
|
|
696
|
+
));
|
|
660
697
|
};
|
|
661
698
|
|
|
662
|
-
const
|
|
699
|
+
const setSession = async (sessionId, session, options) => {
|
|
700
|
+
sessionId = assertSessionId(sessionId);
|
|
663
701
|
return await chatConfig.sessions.set(sessionId, session, options);
|
|
664
702
|
};
|
|
665
703
|
|
|
704
|
+
const resetSession = async (sessionId, options) => {
|
|
705
|
+
const session = {
|
|
706
|
+
...defaultSession(),
|
|
707
|
+
...options?.systemPrompt ? { systemPrompt: options.systemPrompt } : {},
|
|
708
|
+
};
|
|
709
|
+
return await setSession(sessionId, session);
|
|
710
|
+
};
|
|
711
|
+
|
|
666
712
|
const talk = async (input, options) => {
|
|
667
713
|
const engine = unifyEngine({ engine: CHATGPT, ...options });
|
|
668
714
|
assert(chatConfig.engines[engine], NOT_INIT);
|
|
669
|
-
const
|
|
670
|
-
const
|
|
671
|
-
const
|
|
672
|
-
let [_provider, _model, resp
|
|
715
|
+
const model = MODELS[chatConfig.engines[engine].model];
|
|
716
|
+
const sessionId = options?.sessionId || newSessionId();
|
|
717
|
+
const session = await getSession(sessionId, { engine, ...options });
|
|
718
|
+
let [_provider, _model, resp, sys, messages, msgBuilder] =
|
|
719
|
+
[engine, null, null, [], [], null];
|
|
673
720
|
switch (engine) {
|
|
674
721
|
case CHATGPT:
|
|
675
|
-
_provider = OPENAI;
|
|
722
|
+
_provider = OPENAI;
|
|
723
|
+
_model = DEFAULT_MODELS[CHATGPT];
|
|
724
|
+
sys.push(buildGptMessage(session.systemPrompt, { role: system }));
|
|
725
|
+
msgBuilder = () => {
|
|
726
|
+
messages = [];
|
|
727
|
+
session.messages.map(x => {
|
|
728
|
+
messages.push(buildGptMessage(x.request, { role: user }));
|
|
729
|
+
messages.push(buildGptMessage(x.response, { role: assistant }));
|
|
730
|
+
});
|
|
731
|
+
};
|
|
732
|
+
msgBuilder()
|
|
733
|
+
break;
|
|
676
734
|
case ASSISTANT:
|
|
677
|
-
_provider = OPENAI;
|
|
735
|
+
_provider = OPENAI;
|
|
736
|
+
_model = DEFAULT_MODELS[ASSISTANT];
|
|
737
|
+
break;
|
|
678
738
|
case GEMINI: case VERTEX:
|
|
679
|
-
_model = DEFAULT_MODELS[engine];
|
|
739
|
+
_model = DEFAULT_MODELS[engine];
|
|
740
|
+
sys.push(buildVertexMessage(session.systemPrompt, { role: user }));
|
|
741
|
+
msgBuilder = () => {
|
|
742
|
+
messages = [];
|
|
743
|
+
session.messages.map(x => {
|
|
744
|
+
messages.push(buildVertexMessage(x.request, { role: user }));
|
|
745
|
+
messages.push(buildVertexMessage(x.response, { role: MODEL }));
|
|
746
|
+
});
|
|
747
|
+
};
|
|
748
|
+
msgBuilder()
|
|
749
|
+
break;
|
|
680
750
|
default:
|
|
681
751
|
throwError(`Invalid AI engine: '${engine}'.`);
|
|
682
752
|
}
|
|
753
|
+
while (countTokens([
|
|
754
|
+
...sys, ...messages, buildVertexMessage(input, { role: user })
|
|
755
|
+
]) >= model.maxInputTokens) {
|
|
756
|
+
if (messages.length) {
|
|
757
|
+
session.messages.shift();
|
|
758
|
+
msgBuilder && msgBuilder();
|
|
759
|
+
} else {
|
|
760
|
+
input = trimTailing(trimTailing(input).slice(0, -1)) + '...';
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
const chat = { request: input };
|
|
764
|
+
log(`Prompt: ${JSON.stringify(input)}`);
|
|
683
765
|
switch (engine) {
|
|
684
766
|
case CHATGPT:
|
|
685
767
|
resp = await promptChatGPT(input, {
|
|
686
|
-
messages
|
|
768
|
+
messages, model: _model, ...options,
|
|
687
769
|
});
|
|
688
770
|
break;
|
|
689
771
|
case ASSISTANT:
|
|
@@ -694,14 +776,10 @@ const talk = async (input, options) => {
|
|
|
694
776
|
session.threadId = resp.thread.id;
|
|
695
777
|
break;
|
|
696
778
|
case GEMINI:
|
|
697
|
-
resp = await promptGemini(input, {
|
|
698
|
-
messages: session.messages, ...options,
|
|
699
|
-
});
|
|
779
|
+
resp = await promptGemini(input, { messages, ...options });
|
|
700
780
|
break;
|
|
701
781
|
case VERTEX:
|
|
702
|
-
resp = await promptVertex(input, {
|
|
703
|
-
messages: session.messages, ...options,
|
|
704
|
-
});
|
|
782
|
+
resp = await promptVertex(input, { messages, ...options });
|
|
705
783
|
break;
|
|
706
784
|
}
|
|
707
785
|
for (const _resp of resp.response) {
|
|
@@ -711,8 +789,24 @@ const talk = async (input, options) => {
|
|
|
711
789
|
}
|
|
712
790
|
}
|
|
713
791
|
session.messages.push(chat);
|
|
714
|
-
await
|
|
715
|
-
|
|
792
|
+
await setSession(sessionId, session, options);
|
|
793
|
+
const text = resp.response.filter(x => x.text).map(x => x.text).join('\n\n');
|
|
794
|
+
log(`Response: ${JSON.stringify(resp.response)}`);
|
|
795
|
+
return {
|
|
796
|
+
sessionId, response: resp.response, text, rendered: renderText(text),
|
|
797
|
+
spoken: renderText(text, { noCode: true }).replace(/\[\^\d\^\]/ig, ''),
|
|
798
|
+
};
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
const getMaxChatPromptLimit = (options) => {
|
|
802
|
+
let resp = 0;
|
|
803
|
+
for (const i in chatConfig.engines) {
|
|
804
|
+
if (options?.engine && i !== options.engine) { continue; }
|
|
805
|
+
const maxInputTokens = MODELS[chatConfig.engines[i].model].maxInputTokens;
|
|
806
|
+
resp = resp ? Math.min(resp, maxInputTokens) : maxInputTokens;
|
|
807
|
+
}
|
|
808
|
+
assert(resp > 0, 'Chat engine has not been initialized.');
|
|
809
|
+
return resp;
|
|
716
810
|
};
|
|
717
811
|
|
|
718
812
|
export default init;
|
|
@@ -738,11 +832,11 @@ export {
|
|
|
738
832
|
buildGptTrainingCase,
|
|
739
833
|
buildGptTrainingCases,
|
|
740
834
|
cancelGptFineTuningJob,
|
|
835
|
+
countTokens,
|
|
741
836
|
createAssistant,
|
|
742
837
|
createGeminiEmbedding,
|
|
743
838
|
createGptFineTuningJob,
|
|
744
839
|
createMessage,
|
|
745
|
-
initChat,
|
|
746
840
|
createOpenAIEmbedding,
|
|
747
841
|
createVertexEmbedding,
|
|
748
842
|
deleteAllFilesFromAssistant,
|
|
@@ -756,9 +850,11 @@ export {
|
|
|
756
850
|
getAssistant,
|
|
757
851
|
getGptFineTuningJob,
|
|
758
852
|
getLatestMessage,
|
|
853
|
+
getMaxChatPromptLimit,
|
|
759
854
|
getRun,
|
|
760
855
|
getThread,
|
|
761
856
|
init,
|
|
857
|
+
initChat,
|
|
762
858
|
listAssistant,
|
|
763
859
|
listAssistantFiles,
|
|
764
860
|
listFiles,
|
|
@@ -771,6 +867,7 @@ export {
|
|
|
771
867
|
promptChatGPT,
|
|
772
868
|
promptGemini,
|
|
773
869
|
promptVertex,
|
|
870
|
+
resetSession,
|
|
774
871
|
run,
|
|
775
872
|
tailGptFineTuningEvents,
|
|
776
873
|
talk,
|
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": "1995.2.
|
|
4
|
+
"version": "1995.2.39",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/Leask/utilitas",
|
|
7
7
|
"main": "index.mjs",
|
|
@@ -36,7 +36,6 @@ const manifest = {
|
|
|
36
36
|
"@mozilla/readability": "^0.4.4",
|
|
37
37
|
"@ngrok/ngrok": "^0.9.1",
|
|
38
38
|
"@sentry/node": "^7.88.0",
|
|
39
|
-
"@waylaidwanderer/chatgpt-api": "^1.37.3",
|
|
40
39
|
"acme-client": "^5.0.0",
|
|
41
40
|
"browserify-fs": "^1.0.0",
|
|
42
41
|
"buffer": "^6.0.3",
|
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": "1995.2.
|
|
4
|
+
"version": "1995.2.39",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/Leask/utilitas",
|
|
7
7
|
"main": "index.mjs",
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
"@mozilla/readability": "^0.4.4",
|
|
48
48
|
"@ngrok/ngrok": "^0.9.1",
|
|
49
49
|
"@sentry/node": "^7.88.0",
|
|
50
|
-
"@waylaidwanderer/chatgpt-api": "^1.37.3",
|
|
51
50
|
"acme-client": "^5.0.0",
|
|
52
51
|
"browserify-fs": "^1.0.0",
|
|
53
52
|
"buffer": "^6.0.3",
|
package/lib/hal.mjs
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ensureString, insensitiveCompare, log as _log, need,
|
|
3
|
-
renderText as _renderText, throwError, verifyUrl,
|
|
4
|
-
} from './utilitas.mjs';
|
|
5
|
-
|
|
6
|
-
const _NEED = ['@waylaidwanderer/chatgpt-api'];
|
|
7
|
-
const [BING, CHATGPT] = ['BING', 'CHATGPT'];
|
|
8
|
-
const renderText = (t, o) => _renderText(t, { extraCodeBlock: 1, ...o || {} });
|
|
9
|
-
const log = content => _log(content, import.meta.url);
|
|
10
|
-
const iCmp = (strA, strB) => insensitiveCompare(strA, strB, { w: true });
|
|
11
|
-
const link = (text, url) => `[${text}](${url})`;
|
|
12
|
-
const li = (id, text, url) => `\n${id}. ` + (url ? link(text, url) : text);
|
|
13
|
-
const cardReg = /^\[\d*\]:\ ([^\ ]*)\ "(.*)"$/ig;
|
|
14
|
-
// https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them
|
|
15
|
-
// Adding 10% to the token count to account for the model's error margin.
|
|
16
|
-
const countTokens = t => Math.ceil(t.split(/[^a-z0-9]/i).length * 100 / 75 * 1.1);
|
|
17
|
-
// Keep this for GPT4 {
|
|
18
|
-
// const MAX_CONTEXT_TOKENS = 8192;
|
|
19
|
-
// }
|
|
20
|
-
const MAX_CONTEXT_TOKENS = 4096;
|
|
21
|
-
const MAX_PROMPT_TOKENS = Math.floor(MAX_CONTEXT_TOKENS * 0.6);
|
|
22
|
-
const MAX_RESPONSE_TOKENS = MAX_CONTEXT_TOKENS - MAX_PROMPT_TOKENS;
|
|
23
|
-
|
|
24
|
-
const init = async options => {
|
|
25
|
-
const clear = key => key ? (delete sessions[key]) : (sessions = {});
|
|
26
|
-
const get = (k, s) => k ? (s ? sessions[k]?.[s] : sessions[k]) : sessions;
|
|
27
|
-
const set = (k, s, v) => sessions[k][s] = v;
|
|
28
|
-
let [sessions, provider, engine, client] = [{}];
|
|
29
|
-
switch ((provider = ensureString(options?.provider, { case: 'UP' }))) {
|
|
30
|
-
case BING:
|
|
31
|
-
// https://github.com/waylaidwanderer/node-chatgpt-api/blob/main/demos/use-bing-client.js
|
|
32
|
-
engine = (await need('@waylaidwanderer/chatgpt-api')).BingAIClient;
|
|
33
|
-
client = new engine(options?.clientOptions);
|
|
34
|
-
break;
|
|
35
|
-
case CHATGPT:
|
|
36
|
-
// https://github.com/waylaidwanderer/node-chatgpt-api/blob/main/demos/use-client.js
|
|
37
|
-
// Throttled: Request is throttled.
|
|
38
|
-
// https://github.com/waylaidwanderer/node-chatgpt-api/issues/96
|
|
39
|
-
engine = (await need('@waylaidwanderer/chatgpt-api')).ChatGPTClient;
|
|
40
|
-
client = new engine(options?.clientOptions?.apiKey, {
|
|
41
|
-
keepNecessaryMessagesOnly: true,
|
|
42
|
-
// Keep this for GPT4 {
|
|
43
|
-
// maxContextTokens: MAX_CONTEXT_TOKENS,
|
|
44
|
-
// }
|
|
45
|
-
modelOptions: {
|
|
46
|
-
model: options?.model || 'gpt-3.5-turbo',
|
|
47
|
-
// Keep this for GPT4 {
|
|
48
|
-
// model: options?.model || 'gpt-4',
|
|
49
|
-
// max_tokens: MAX_RESPONSE_TOKENS,
|
|
50
|
-
// }
|
|
51
|
-
...options?.clientOptions?.modelOptions || {}
|
|
52
|
-
}, ...options?.clientOptions || {},
|
|
53
|
-
}, options?.cacheOptions);
|
|
54
|
-
break;
|
|
55
|
-
default: throwError('Invalid AI provider.', 500);
|
|
56
|
-
}
|
|
57
|
-
client.provider = provider;
|
|
58
|
-
const send = async (message, options, onProgress) => {
|
|
59
|
-
const [sessionId, cur, upd, apd] = [
|
|
60
|
-
options?.sessionId || '_',
|
|
61
|
-
key => get(sessionId, key),
|
|
62
|
-
(key, val) => set(sessionId, key, val),
|
|
63
|
-
(key, val) => set(sessionId, key, cur(key) + val),
|
|
64
|
-
];
|
|
65
|
-
options?.session && (sessions[sessionId] = options.session);
|
|
66
|
-
const objSession = { parentMessageId: cur('messageId') };
|
|
67
|
-
switch (client.provider) {
|
|
68
|
-
case BING:
|
|
69
|
-
Object.assign(objSession, {
|
|
70
|
-
toneStyle: options?.toneStyle || 'balanced', // or creative, precise
|
|
71
|
-
jailbreakConversationId: cur('jailbreakConversationId') || true,
|
|
72
|
-
});
|
|
73
|
-
break;
|
|
74
|
-
case CHATGPT:
|
|
75
|
-
Object.assign(objSession, { conversationId: cur('conversationId') });
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
log(`Prompt: ${message}`);
|
|
79
|
-
try {
|
|
80
|
-
sessions[sessionId] = await client.sendMessage(
|
|
81
|
-
message, { ...objSession, ...options || {}, onProgress }
|
|
82
|
-
);
|
|
83
|
-
} catch (err) {
|
|
84
|
-
// @todo: when this happens, just reset hal? not all the project?
|
|
85
|
-
log(err);
|
|
86
|
-
clear(sessionId);
|
|
87
|
-
throwError(err?.message || err, 500);
|
|
88
|
-
}
|
|
89
|
-
upd('responseRendered', renderText(cur('response')));
|
|
90
|
-
// We should use `cur('details')?.spokenText` but it's too short and infoless for now.
|
|
91
|
-
upd('spokenText', renderText(cur('response'), { noCode: true }).replace(/\[\^\d\^\]/ig, ''));
|
|
92
|
-
const sources = cur('details')?.sourceAttributions || [];
|
|
93
|
-
if (sources.length) {
|
|
94
|
-
apd('responseRendered', '\n\nSource:');
|
|
95
|
-
for (let i in sources) {
|
|
96
|
-
const idx = ~~i + 1;
|
|
97
|
-
upd('responseRendered', cur('responseRendered').replaceAll(
|
|
98
|
-
`[^${idx}^]`, link(`(${idx})`, sources[i].seeMoreUrl)
|
|
99
|
-
) + li(idx, sources[i].providerDisplayName, sources[i].seeMoreUrl));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const cards = (
|
|
103
|
-
cur('details')?.adaptiveCards || []
|
|
104
|
-
)[0]?.body[0].text.split('\n\n')[0].split('\n').map(line => ({
|
|
105
|
-
providerDisplayName: line.replace(cardReg, '$2'),
|
|
106
|
-
seeMoreUrl: line.replace(cardReg, '$1'),
|
|
107
|
-
})).filter(card => {
|
|
108
|
-
for (let src of sources) {
|
|
109
|
-
if (iCmp(src.seeMoreUrl, card.seeMoreUrl)) { return false; }
|
|
110
|
-
}
|
|
111
|
-
return card.providerDisplayName && verifyUrl(card.seeMoreUrl);
|
|
112
|
-
});
|
|
113
|
-
if (cards?.length) {
|
|
114
|
-
apd('responseRendered', '\n\nLearn more:');
|
|
115
|
-
for (let i in cards) {
|
|
116
|
-
apd('responseRendered', li(
|
|
117
|
-
~~i + 1, cards[i].providerDisplayName, cards[i].seeMoreUrl
|
|
118
|
-
));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
upd(
|
|
122
|
-
'suggestedResponses',
|
|
123
|
-
cur('details')?.suggestedResponses?.map?.(s => s.text) || []
|
|
124
|
-
);
|
|
125
|
-
// console.log(JSON.stringify(cur()));
|
|
126
|
-
return cur();
|
|
127
|
-
};
|
|
128
|
-
return { clear, client, engine, get, send };
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
export default init;
|
|
132
|
-
export {
|
|
133
|
-
_NEED,
|
|
134
|
-
MAX_CONTEXT_TOKENS,
|
|
135
|
-
MAX_PROMPT_TOKENS,
|
|
136
|
-
MAX_RESPONSE_TOKENS,
|
|
137
|
-
countTokens,
|
|
138
|
-
init,
|
|
139
|
-
};
|