wingbot 3.73.15 → 3.74.1
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/.github/workflows/deploy.yml +74 -49
- package/index.js +7 -1
- package/package.json +6 -1
- package/src/BuildRouter.js +24 -3
- package/src/ChatGpt.js +112 -35
- package/src/LLM.js +140 -48
- package/src/LLMConsts.js +42 -0
- package/src/LLMMockProvider.js +62 -1
- package/src/LLMSession.js +733 -90
- package/src/LLMTool.js +56 -0
- package/src/LLMType.js +331 -0
- package/src/Processor.js +6 -6
- package/src/Responder.js +56 -34
- package/src/Router.js +7 -2
- package/src/graphApi/validateBotApi.js +6 -1
- package/src/resolvers/expectedInput.js +11 -2
- package/src/resolvers/message.js +2 -3
- package/.claude/settings.local.json +0 -9
package/src/LLM.js
CHANGED
|
@@ -8,6 +8,16 @@ const { PHONE_REGEX, EMAIL_REGEX } = require('./systemEntities/regexps');
|
|
|
8
8
|
const getCondition = require('./utils/getCondition');
|
|
9
9
|
const stateData = require('./utils/stateData');
|
|
10
10
|
const Ai = require('./Ai');
|
|
11
|
+
const {
|
|
12
|
+
PRESET_DEFAULT,
|
|
13
|
+
PRESET_ROUTING,
|
|
14
|
+
PRESET_EMBEDDINGS,
|
|
15
|
+
ROLE_USER,
|
|
16
|
+
ROLE_ASSISTANT,
|
|
17
|
+
ROLE_SYSTEM,
|
|
18
|
+
FILTER_SCOPE_CONVERSATION
|
|
19
|
+
} = require('./LLMConsts');
|
|
20
|
+
const LLMSession = require('./LLMSession');
|
|
11
21
|
// const getCondition = require('./utils/getCondition');
|
|
12
22
|
|
|
13
23
|
/** @typedef {import('./Responder')} Responder */
|
|
@@ -19,7 +29,10 @@ const Ai = require('./Ai');
|
|
|
19
29
|
/** @typedef {import('./LLMSession').ToolCall} ToolCall */
|
|
20
30
|
/** @typedef {import('./LLMSession').LLMRole} LLMRole */
|
|
21
31
|
/** @typedef {import('./LLMSession').FilterScope} FilterScope */
|
|
22
|
-
/** @typedef {import('./LLMSession')}
|
|
32
|
+
/** @typedef {import('./LLMSession').JsonSchemaProp} JsonSchemaProp */
|
|
33
|
+
/** @typedef {import('./LLMSession').JsonSchemaProp} SimpleJsonSchema */
|
|
34
|
+
/** @typedef {import('./LLMSession').ToolFunction} ToolFunction */
|
|
35
|
+
|
|
23
36
|
/** @typedef {import('./transcript/transcriptFromHistory').Transcript} Transcript */
|
|
24
37
|
/** @typedef {import('./utils/getCondition').ConditionDefinition} ConditionDefinition */
|
|
25
38
|
/** @typedef {import('./utils/getCondition').ConditionContext} ConditionContext */
|
|
@@ -74,12 +87,24 @@ const Ai = require('./Ai');
|
|
|
74
87
|
* @callback LLMChatProviderPrompt
|
|
75
88
|
* @param {LLMMessage[]} prompt
|
|
76
89
|
* @param {LLMProviderOptions} [options]
|
|
90
|
+
* @param {ToolFunction[]} [tools]
|
|
77
91
|
* @returns {Promise<LLMMessage>}
|
|
78
92
|
*/
|
|
79
93
|
|
|
94
|
+
/**
|
|
95
|
+
* @typedef {object} ForcedFn
|
|
96
|
+
* @prop {'function'|string} [type]
|
|
97
|
+
* @prop {string} name
|
|
98
|
+
*/
|
|
99
|
+
|
|
80
100
|
/**
|
|
81
101
|
* @typedef {object} LLMProviderOptions
|
|
82
102
|
* @prop {string} [model]
|
|
103
|
+
* @prop {boolean} [parallelToolCalls]
|
|
104
|
+
* @prop {'auto'|'required'|'none'|ForcedFn|string} [toolChoice]
|
|
105
|
+
* @prop {'none'|'low'|'medium'|'high'|string} [reasoningEffort]
|
|
106
|
+
* @prop {'low'|'medium'|'high'|string} [verbosity]
|
|
107
|
+
* @prop {'text'|SimpleJsonSchema} [responseFormat]
|
|
83
108
|
*/
|
|
84
109
|
|
|
85
110
|
/**
|
|
@@ -95,15 +120,29 @@ const Ai = require('./Ai');
|
|
|
95
120
|
/** @typedef {import('node-fetch').default} Fetch */
|
|
96
121
|
|
|
97
122
|
/**
|
|
98
|
-
* @typedef {object}
|
|
99
|
-
* @prop {LLMChatProvider} provider
|
|
100
|
-
* @prop {string} [model]
|
|
123
|
+
* @typedef {object} LLMOptionsExt
|
|
101
124
|
* @prop {number} [transcriptLength=-5]
|
|
102
125
|
* @prop {'gpt'|string} [transcriptFlag]
|
|
103
126
|
* @prop {boolean} [transcriptAnonymize]
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
/** @typedef {LLMOptionsExt & LLMProviderOptions} LLMOptions */
|
|
130
|
+
/** @typedef {LLMOptions & { preset?: LLMPresetName}} LLMCallOptions */
|
|
131
|
+
/** @typedef {'default'|'routing'|'embeddings'|string} LLMPresetName */
|
|
132
|
+
|
|
133
|
+
/** @typedef {LLMCallOptions|LLMPresetName} LLMCallPreset */
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @typedef {object} LLMGlobalConfigExt
|
|
137
|
+
* @prop {LLMChatProvider} provider
|
|
104
138
|
* @prop {Persona|string|null} [persona]
|
|
105
139
|
* @prop {LLMLogger} [logger]
|
|
106
140
|
* @prop {boolean} [disableLLM]
|
|
141
|
+
* @prop {{ [key: LLMPresetName]: LLMOptions }} [presets]
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @typedef {LLMGlobalConfigExt & LLMOptions} LLMGlobalConfig
|
|
107
146
|
*/
|
|
108
147
|
|
|
109
148
|
/**
|
|
@@ -129,6 +168,12 @@ const Ai = require('./Ai');
|
|
|
129
168
|
* @prop {LogPrompt} logPrompt
|
|
130
169
|
*/
|
|
131
170
|
|
|
171
|
+
/**
|
|
172
|
+
* @typedef {object} Logger
|
|
173
|
+
* @prop {Function} log
|
|
174
|
+
* @prop {Function} error
|
|
175
|
+
*/
|
|
176
|
+
|
|
132
177
|
/**
|
|
133
178
|
* @typedef {object} VectorSearchDocument
|
|
134
179
|
* @property {string} id
|
|
@@ -148,19 +193,28 @@ const Ai = require('./Ai');
|
|
|
148
193
|
*/
|
|
149
194
|
class LLM {
|
|
150
195
|
|
|
196
|
+
/** @type {LLMPresetName} */
|
|
197
|
+
static PRESET_DEFAULT = PRESET_DEFAULT;
|
|
198
|
+
|
|
199
|
+
/** @type {LLMPresetName} */
|
|
200
|
+
static PRESET_ROUTING = PRESET_ROUTING;
|
|
201
|
+
|
|
202
|
+
/** @type {LLMPresetName} */
|
|
203
|
+
static PRESET_EMBEDDINGS = PRESET_EMBEDDINGS;
|
|
204
|
+
|
|
151
205
|
/** @type {LLMRole} */
|
|
152
|
-
static ROLE_USER =
|
|
206
|
+
static ROLE_USER = ROLE_USER;
|
|
153
207
|
|
|
154
208
|
/** @type {LLMRole} */
|
|
155
|
-
static ROLE_ASSISTANT =
|
|
209
|
+
static ROLE_ASSISTANT = ROLE_ASSISTANT;
|
|
156
210
|
|
|
157
211
|
/** @type {LLMRole} */
|
|
158
|
-
static ROLE_SYSTEM =
|
|
212
|
+
static ROLE_SYSTEM = ROLE_SYSTEM;
|
|
159
213
|
|
|
160
214
|
static GPT_FLAG = 'gpt';
|
|
161
215
|
|
|
162
216
|
/** @type {FilterScope} */
|
|
163
|
-
static FILTER_SCOPE_CONVERSATION =
|
|
217
|
+
static FILTER_SCOPE_CONVERSATION = FILTER_SCOPE_CONVERSATION;
|
|
164
218
|
|
|
165
219
|
static EVALUATION_ACTIONS = {
|
|
166
220
|
DISCARD: '_DISCARD'
|
|
@@ -174,11 +228,20 @@ class LLM {
|
|
|
174
228
|
|
|
175
229
|
/**
|
|
176
230
|
*
|
|
177
|
-
* @param {
|
|
231
|
+
* @param {LLMGlobalConfig} configuration
|
|
178
232
|
* @param {Ai} ai
|
|
233
|
+
* @param {Logger} [log=console]
|
|
179
234
|
*/
|
|
180
|
-
constructor (configuration, ai) {
|
|
181
|
-
const {
|
|
235
|
+
constructor (configuration, ai, log = console) {
|
|
236
|
+
const {
|
|
237
|
+
provider,
|
|
238
|
+
presets = {},
|
|
239
|
+
transcriptFlag = null,
|
|
240
|
+
transcriptLength = 5,
|
|
241
|
+
transcriptAnonymize = false,
|
|
242
|
+
model = null,
|
|
243
|
+
...rest
|
|
244
|
+
} = configuration;
|
|
182
245
|
|
|
183
246
|
this._configuration = {
|
|
184
247
|
transcriptFlag: null,
|
|
@@ -190,6 +253,30 @@ class LLM {
|
|
|
190
253
|
...rest
|
|
191
254
|
};
|
|
192
255
|
|
|
256
|
+
/** @type {LLMOptions} */
|
|
257
|
+
const defaultPreset = {
|
|
258
|
+
model,
|
|
259
|
+
transcriptFlag,
|
|
260
|
+
transcriptLength,
|
|
261
|
+
transcriptAnonymize
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
this._presets = new Map(
|
|
265
|
+
Object.entries(presets)
|
|
266
|
+
.map(([k, v]) => [k, {
|
|
267
|
+
...defaultPreset,
|
|
268
|
+
...v
|
|
269
|
+
}])
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
if (!this._presets.has(LLM.PRESET_DEFAULT)) {
|
|
273
|
+
this._presets.set(LLM.PRESET_DEFAULT, defaultPreset);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!this._presets.has(LLM.PRESET_ROUTING)) {
|
|
277
|
+
this._presets.set(LLM.PRESET_ROUTING, defaultPreset);
|
|
278
|
+
}
|
|
279
|
+
|
|
193
280
|
/** @type {LLMChatProvider} */
|
|
194
281
|
this._provider = provider;
|
|
195
282
|
|
|
@@ -197,6 +284,8 @@ class LLM {
|
|
|
197
284
|
|
|
198
285
|
/** @type {LLMMessage} */
|
|
199
286
|
this._lastResult = null;
|
|
287
|
+
|
|
288
|
+
this.log = log;
|
|
200
289
|
}
|
|
201
290
|
|
|
202
291
|
/**
|
|
@@ -214,20 +303,51 @@ class LLM {
|
|
|
214
303
|
}
|
|
215
304
|
|
|
216
305
|
/**
|
|
217
|
-
* @
|
|
306
|
+
* @deprecated
|
|
307
|
+
* @returns {Omit<LLMGlobalConfig, 'provider'>}
|
|
218
308
|
*/
|
|
219
309
|
get configuration () {
|
|
220
310
|
return this._configuration;
|
|
221
311
|
}
|
|
222
312
|
|
|
313
|
+
/**
|
|
314
|
+
* @returns {LLMSession}
|
|
315
|
+
*/
|
|
316
|
+
session () {
|
|
317
|
+
return new LLMSession(this);
|
|
318
|
+
}
|
|
319
|
+
|
|
223
320
|
/**
|
|
224
321
|
*
|
|
225
|
-
* @param {
|
|
322
|
+
* @param {LLMCallPreset} [requestedPreset]
|
|
226
323
|
*/
|
|
227
|
-
|
|
228
|
-
|
|
324
|
+
llmOptions (requestedPreset = LLM.PRESET_DEFAULT) {
|
|
325
|
+
let preset;
|
|
326
|
+
let override;
|
|
327
|
+
|
|
328
|
+
if (typeof requestedPreset === 'string') {
|
|
329
|
+
preset = requestedPreset;
|
|
330
|
+
} else {
|
|
331
|
+
({ preset = LLM.PRESET_DEFAULT, ...override } = requestedPreset);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (!this._presets.has(preset)) {
|
|
335
|
+
throw new Error(`LLM Preset '${preset}' does not exist.`);
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
...this._presets.get(preset),
|
|
339
|
+
...override
|
|
340
|
+
};
|
|
229
341
|
}
|
|
230
342
|
|
|
343
|
+
// /**
|
|
344
|
+
// *
|
|
345
|
+
// * @param {Partial<LLMConfiguration>} override
|
|
346
|
+
// */
|
|
347
|
+
// setSessionConfig (override) {
|
|
348
|
+
// Object.assign(this._configuration, override);
|
|
349
|
+
// }
|
|
350
|
+
|
|
231
351
|
/**
|
|
232
352
|
*
|
|
233
353
|
* @param {Transcript[]} chat
|
|
@@ -250,19 +370,14 @@ class LLM {
|
|
|
250
370
|
/**
|
|
251
371
|
*
|
|
252
372
|
* @param {LLMSession} session
|
|
253
|
-
* @param {
|
|
373
|
+
* @param {LLMCallPreset} [preset={}]
|
|
254
374
|
* @param {LLMLogOptions} [logOptions]
|
|
255
375
|
* @returns {Promise<LLMMessage>}
|
|
256
376
|
*/
|
|
257
|
-
async generate (session,
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
...options
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const prompt = session.toArray(true);
|
|
265
|
-
const result = await this._provider.requestChat(prompt, opts);
|
|
377
|
+
async generate (session, preset = {}, logOptions = {}) {
|
|
378
|
+
const opts = this.llmOptions(preset);
|
|
379
|
+
const prompt = await session.toArray(true);
|
|
380
|
+
const result = await this._provider.requestChat(prompt, opts, session.tools);
|
|
266
381
|
this.logPrompt(prompt, result, logOptions.vectorSearchResult);
|
|
267
382
|
return result;
|
|
268
383
|
}
|
|
@@ -280,29 +395,6 @@ class LLM {
|
|
|
280
395
|
});
|
|
281
396
|
}
|
|
282
397
|
|
|
283
|
-
/**
|
|
284
|
-
*
|
|
285
|
-
* @param {LLMMessage} result
|
|
286
|
-
* @returns {LLMMessage[]}
|
|
287
|
-
*/
|
|
288
|
-
static toMessages (result) {
|
|
289
|
-
let filtered = result.content
|
|
290
|
-
.replace(/\n\n\n+/g, '\n\n')
|
|
291
|
-
.split(/\n\n+(?!\s*-)/g)
|
|
292
|
-
.map((t) => t.replace(/\s*\n\s+/g, '\n')
|
|
293
|
-
.trim())
|
|
294
|
-
.filter((t) => !!t);
|
|
295
|
-
|
|
296
|
-
if (result.finishReason === 'length' && filtered.length <= 0) {
|
|
297
|
-
filtered = filtered.slice(0, filtered.length - 1);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return filtered.map((content) => ({
|
|
301
|
-
content,
|
|
302
|
-
role: result.role
|
|
303
|
-
}));
|
|
304
|
-
}
|
|
305
|
-
|
|
306
398
|
/**
|
|
307
399
|
*
|
|
308
400
|
* @param {EvaluationRule[]} rules
|
package/src/LLMConsts.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
/** @typedef {import('./LLMSession').FilterScope} FilterScope */
|
|
7
|
+
/** @typedef {import('./LLM').LLMPresetName} LLMPresetName */
|
|
8
|
+
/** @typedef {import('./LLMSession').LLMRole} LLMRole */
|
|
9
|
+
|
|
10
|
+
/** @type {LLMPresetName} */
|
|
11
|
+
const PRESET_DEFAULT = 'default';
|
|
12
|
+
|
|
13
|
+
/** @type {LLMPresetName} */
|
|
14
|
+
const PRESET_ROUTING = 'routing';
|
|
15
|
+
|
|
16
|
+
/** @type {LLMPresetName} */
|
|
17
|
+
const PRESET_EMBEDDINGS = 'embeddings';
|
|
18
|
+
|
|
19
|
+
/** @type {LLMRole} */
|
|
20
|
+
const ROLE_USER = 'user';
|
|
21
|
+
|
|
22
|
+
/** @type {LLMRole} */
|
|
23
|
+
const ROLE_ASSISTANT = 'assistant';
|
|
24
|
+
|
|
25
|
+
/** @type {LLMRole} */
|
|
26
|
+
const ROLE_SYSTEM = 'system';
|
|
27
|
+
|
|
28
|
+
const GPT_FLAG = 'gpt';
|
|
29
|
+
|
|
30
|
+
/** @type {FilterScope} */
|
|
31
|
+
const FILTER_SCOPE_CONVERSATION = 'conversation';
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
PRESET_DEFAULT,
|
|
35
|
+
PRESET_ROUTING,
|
|
36
|
+
PRESET_EMBEDDINGS,
|
|
37
|
+
ROLE_USER,
|
|
38
|
+
ROLE_ASSISTANT,
|
|
39
|
+
ROLE_SYSTEM,
|
|
40
|
+
GPT_FLAG,
|
|
41
|
+
FILTER_SCOPE_CONVERSATION
|
|
42
|
+
};
|
package/src/LLMMockProvider.js
CHANGED
|
@@ -35,9 +35,36 @@ class LLMMockProvider {
|
|
|
35
35
|
*/
|
|
36
36
|
// eslint-disable-next-line no-unused-vars
|
|
37
37
|
async requestChat (prompt, options) {
|
|
38
|
+
|
|
38
39
|
if (prompt.length === 0) {
|
|
39
40
|
throw new Error('Empty prompt');
|
|
40
41
|
}
|
|
42
|
+
|
|
43
|
+
if (prompt.some((p) => p?.content?.includes('THROW EXCEPTION'))) {
|
|
44
|
+
throw new Error('THROW EXCEPTION');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (prompt.some((p) => p.toolCallId)) {
|
|
48
|
+
return {
|
|
49
|
+
role: LLM.ROLE_ASSISTANT,
|
|
50
|
+
content: 'got tool call id'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (prompt.some((p) => p?.content?.includes('CALL MOCK TOOL'))) {
|
|
55
|
+
return {
|
|
56
|
+
role: LLM.ROLE_ASSISTANT,
|
|
57
|
+
toolCalls: [
|
|
58
|
+
{
|
|
59
|
+
id: `${Date.now()}${Math.floor(Math.random() * 10)}`,
|
|
60
|
+
type: 'function',
|
|
61
|
+
name: 'mock',
|
|
62
|
+
args: JSON.stringify({ mock: true })
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
41
68
|
// const stats = prompt.reduce((o, m) => Object.assign(o, {
|
|
42
69
|
// [m.role]: (o[m.role] || 0) + 1
|
|
43
70
|
// }), { system: 0, assistant: 0, user: 0 });
|
|
@@ -55,13 +82,47 @@ class LLMMockProvider {
|
|
|
55
82
|
// content: `${statsText} > ${LLMMockProvider.DEFAULT_MODEL}: ${prompt.map((m) => m.content).join(' ')}`
|
|
56
83
|
// };
|
|
57
84
|
|
|
85
|
+
const text = `${options.model || LLMMockProvider.DEFAULT_MODEL}:${prompt.filter((m) => m.content).map((m) => `[${m.role}] ${m.content}`).join(' | ')}`;
|
|
86
|
+
|
|
58
87
|
return {
|
|
59
88
|
role: LLM.ROLE_ASSISTANT,
|
|
60
89
|
finishReason: 'length',
|
|
61
|
-
content:
|
|
90
|
+
content: options.responseFormat && options.responseFormat !== 'text'
|
|
91
|
+
? JSON.stringify(this._mockFromSchema(options.responseFormat))
|
|
92
|
+
: text
|
|
62
93
|
};
|
|
63
94
|
}
|
|
64
95
|
|
|
96
|
+
_mockFromSchema (schema) {
|
|
97
|
+
if (schema.properties) {
|
|
98
|
+
const obj = {};
|
|
99
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
100
|
+
obj[key] = this._mockFromProp(prop);
|
|
101
|
+
}
|
|
102
|
+
return obj;
|
|
103
|
+
}
|
|
104
|
+
return {};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
_mockFromProp (prop) {
|
|
108
|
+
if (prop.enum) {
|
|
109
|
+
return prop.enum[0];
|
|
110
|
+
}
|
|
111
|
+
switch (prop.type) {
|
|
112
|
+
case 'number':
|
|
113
|
+
return prop.minimum || 0;
|
|
114
|
+
case 'boolean':
|
|
115
|
+
return false;
|
|
116
|
+
case 'array':
|
|
117
|
+
return [];
|
|
118
|
+
case 'object':
|
|
119
|
+
return this._mockFromSchema(prop);
|
|
120
|
+
case 'string':
|
|
121
|
+
default:
|
|
122
|
+
return 'mock';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
65
126
|
}
|
|
66
127
|
|
|
67
128
|
module.exports = LLMMockProvider;
|