wingbot 3.75.9-alpha.4 → 3.75.9-alpha.5
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/.claude/settings.local.json +11 -0
- package/package.json +1 -1
- package/src/ChatGpt.js +15 -11
- package/src/LLM.js +22 -0
- package/src/LLMSession.js +62 -11
- package/src/Responder.js +21 -6
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(npx mocha:*)",
|
|
5
|
+
"Bash(npx tsc *)",
|
|
6
|
+
"Bash(npm test *)",
|
|
7
|
+
"Bash(node -e \"const p = require\\('./bot/plugins/BTMLLM'\\); console.log\\('factory type:', typeof p\\); console.log\\('factory name:', p.name\\);\")",
|
|
8
|
+
"Bash(grep -n \"prompt\\\\`\\\\|tagged\\\\|render\\\\|compile\\\\|hbs\" /Users/ondrejveres/Wingbot/wingbot-llm/src/prompt.js)"
|
|
9
|
+
]
|
|
10
|
+
}
|
|
11
|
+
}
|
package/package.json
CHANGED
package/src/ChatGpt.js
CHANGED
|
@@ -341,6 +341,10 @@ class ChatGpt {
|
|
|
341
341
|
});
|
|
342
342
|
|
|
343
343
|
let body;
|
|
344
|
+
let responseData;
|
|
345
|
+
let responseText;
|
|
346
|
+
let status;
|
|
347
|
+
let statusText;
|
|
344
348
|
try {
|
|
345
349
|
let lastUserIndex = 0;
|
|
346
350
|
let totalTokens = messages
|
|
@@ -445,26 +449,26 @@ class ChatGpt {
|
|
|
445
449
|
body: JSON.stringify(body)
|
|
446
450
|
});
|
|
447
451
|
|
|
448
|
-
|
|
449
|
-
const data = await response.json();
|
|
452
|
+
({ status, statusText } = response);
|
|
450
453
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
+
responseText = await response.text();
|
|
455
|
+
/** @type {ChatGPTResponse} */
|
|
456
|
+
responseData = JSON.parse(responseText);
|
|
454
457
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
});
|
|
458
|
+
if (status !== 200
|
|
459
|
+
|| !Array.isArray(responseData.choices)) {
|
|
458
460
|
throw new Error(`Chat GPT ${status}`);
|
|
459
461
|
}
|
|
460
462
|
|
|
461
|
-
const [choice] =
|
|
463
|
+
const [choice] = responseData.choices;
|
|
462
464
|
|
|
463
|
-
this._log('#GPT response', { choice,
|
|
465
|
+
this._log('#GPT response', { choice, responseData });
|
|
464
466
|
|
|
465
467
|
return choice;
|
|
466
468
|
} catch (e) {
|
|
467
|
-
this._logger.
|
|
469
|
+
this._logger.log('#GPT failed', {
|
|
470
|
+
message: e.message, body, responseText, responseData, status, statusText
|
|
471
|
+
});
|
|
468
472
|
throw e;
|
|
469
473
|
}
|
|
470
474
|
}
|
package/src/LLM.js
CHANGED
|
@@ -294,12 +294,34 @@ class LLM {
|
|
|
294
294
|
/** @type {LLMMessage} */
|
|
295
295
|
this._lastResult = null;
|
|
296
296
|
|
|
297
|
+
/** @type {Error|null} */
|
|
298
|
+
this._delegatedError = null;
|
|
299
|
+
|
|
297
300
|
this.log = log;
|
|
298
301
|
|
|
299
302
|
this.req = null;
|
|
300
303
|
this.res = null;
|
|
301
304
|
}
|
|
302
305
|
|
|
306
|
+
/**
|
|
307
|
+
* @param {Error|null} [error]
|
|
308
|
+
*/
|
|
309
|
+
setDelegatedError (error = null) {
|
|
310
|
+
this._delegatedError = error;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* @param {boolean} [reset=false]
|
|
315
|
+
* @returns {boolean}
|
|
316
|
+
*/
|
|
317
|
+
hasDelegatedError (reset = false) {
|
|
318
|
+
const has = this._delegatedError !== null;
|
|
319
|
+
if (reset && has) {
|
|
320
|
+
this._delegatedError = null;
|
|
321
|
+
}
|
|
322
|
+
return has;
|
|
323
|
+
}
|
|
324
|
+
|
|
303
325
|
setReqRes (req, res) {
|
|
304
326
|
this.req = req;
|
|
305
327
|
this.res = res;
|
package/src/LLMSession.js
CHANGED
|
@@ -791,6 +791,49 @@ class LLMSession {
|
|
|
791
791
|
return this;
|
|
792
792
|
}
|
|
793
793
|
|
|
794
|
+
/**
|
|
795
|
+
* If there is a delegated error on the LLM instance, appends (or replaces)
|
|
796
|
+
* a system message with `prompt` and resets the delegated error.
|
|
797
|
+
* If `prompt` resolves to an empty string, nothing happens.
|
|
798
|
+
*
|
|
799
|
+
* @param {PossiblyAsyncContent} prompt
|
|
800
|
+
* @param {boolean} [replaceCurrent=false]
|
|
801
|
+
* @returns {this}
|
|
802
|
+
*/
|
|
803
|
+
onDelegatedError (prompt, replaceCurrent = false) {
|
|
804
|
+
this._job(async () => {
|
|
805
|
+
if (!this._llm.hasDelegatedError()) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const content = typeof prompt === 'function'
|
|
810
|
+
? await Promise.resolve(prompt())
|
|
811
|
+
: await Promise.resolve(prompt);
|
|
812
|
+
|
|
813
|
+
if (!content) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (replaceCurrent) {
|
|
818
|
+
const lastSystemIdx = this._chat.reduceRight((found, msg, i) => {
|
|
819
|
+
if (found !== -1) return found;
|
|
820
|
+
if ('role' in msg && msg.role === ROLE_SYSTEM) return i;
|
|
821
|
+
return found;
|
|
822
|
+
}, -1);
|
|
823
|
+
if (lastSystemIdx !== -1) {
|
|
824
|
+
this._chat[lastSystemIdx] = { role: ROLE_SYSTEM, content };
|
|
825
|
+
} else {
|
|
826
|
+
this._chat.push({ role: ROLE_SYSTEM, content });
|
|
827
|
+
}
|
|
828
|
+
} else {
|
|
829
|
+
this._chat.push({ role: ROLE_SYSTEM, content });
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
this._llm.setDelegatedError(null);
|
|
833
|
+
});
|
|
834
|
+
return this;
|
|
835
|
+
}
|
|
836
|
+
|
|
794
837
|
/**
|
|
795
838
|
*
|
|
796
839
|
* @param {LLMFilter|LLMFilter[]} filter
|
|
@@ -836,19 +879,27 @@ class LLMSession {
|
|
|
836
879
|
}
|
|
837
880
|
|
|
838
881
|
this._job(async () => {
|
|
839
|
-
const result = await this._generate({
|
|
840
|
-
...(typeof providerOptions === 'object' ? providerOptions : {}),
|
|
841
|
-
...(typeof providerOptions === 'string' ? { preset: providerOptions } : {}),
|
|
842
|
-
responseFormat
|
|
843
|
-
}, logOptions);
|
|
844
|
-
|
|
845
882
|
try {
|
|
846
|
-
|
|
883
|
+
const result = await this._generate({
|
|
884
|
+
...(typeof providerOptions === 'object' ? providerOptions : {}),
|
|
885
|
+
...(typeof providerOptions === 'string' ? { preset: providerOptions } : {}),
|
|
886
|
+
responseFormat
|
|
887
|
+
}, logOptions);
|
|
888
|
+
|
|
889
|
+
try {
|
|
890
|
+
return JSON.parse(result.content);
|
|
891
|
+
} catch (e) {
|
|
892
|
+
this._llm.log.error(`LLM structured output parse error (${responseFormat.name}): ${e.message}`, e, {
|
|
893
|
+
rawContent: result.content
|
|
894
|
+
});
|
|
895
|
+
throw new Error(
|
|
896
|
+
`LLM structured output is not valid JSON: ${e.message}. Raw content: ${JSON.stringify(result.content)}`,
|
|
897
|
+
{ cause: e }
|
|
898
|
+
);
|
|
899
|
+
}
|
|
847
900
|
} catch (e) {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
{ cause: e }
|
|
851
|
-
);
|
|
901
|
+
this._llm.setDelegatedError(e);
|
|
902
|
+
throw e;
|
|
852
903
|
}
|
|
853
904
|
});
|
|
854
905
|
return this;
|
package/src/Responder.js
CHANGED
|
@@ -248,12 +248,14 @@ class Responder {
|
|
|
248
248
|
this.llm = llm;
|
|
249
249
|
|
|
250
250
|
this.LLM_CTX_DEFAULT = 'default';
|
|
251
|
+
this.LLM_ERROR = 'error';
|
|
251
252
|
|
|
252
253
|
/** @typedef {PromptSource|Promise<string>} PromptContextItem */
|
|
253
254
|
|
|
254
255
|
/** @type {Map<string,PromptContextItem[]>} */
|
|
255
256
|
this._llmContext = new Map([
|
|
256
|
-
[this.LLM_CTX_DEFAULT, []]
|
|
257
|
+
[this.LLM_CTX_DEFAULT, []],
|
|
258
|
+
[this.LLM_ERROR, []]
|
|
257
259
|
]);
|
|
258
260
|
|
|
259
261
|
/** @type {Map<string,PreprocessedRule[]>} */
|
|
@@ -373,18 +375,24 @@ class Responder {
|
|
|
373
375
|
*
|
|
374
376
|
* @param {string} contextType
|
|
375
377
|
* @param {LLMCallPreset} [callPreset]
|
|
378
|
+
* @param {PossiblyAsyncContent} [errorPrompt]
|
|
376
379
|
* @returns {LLMSession}
|
|
377
380
|
*/
|
|
378
|
-
llmSession (contextType = this.LLM_CTX_DEFAULT, callPreset = undefined) {
|
|
381
|
+
llmSession (contextType = this.LLM_CTX_DEFAULT, callPreset = undefined, errorPrompt = undefined) { // eslint-disable-line max-len
|
|
379
382
|
const system = this._getSystemMessagesForType(contextType);
|
|
380
383
|
|
|
381
384
|
const filters = this._filtersForContext(contextType);
|
|
382
|
-
|
|
385
|
+
const session = new LLMSession(this.llm, [system], {
|
|
383
386
|
onSend: this._llmSend.bind(this),
|
|
384
387
|
filters,
|
|
385
388
|
preset: callPreset,
|
|
386
389
|
res: this
|
|
387
390
|
});
|
|
391
|
+
session.onDelegatedError(
|
|
392
|
+
errorPrompt ?? (() => this._getSystemContentForType(this.LLM_ERROR)
|
|
393
|
+
.then((parts) => parts.join('\n\n')))
|
|
394
|
+
);
|
|
395
|
+
return session;
|
|
388
396
|
}
|
|
389
397
|
|
|
390
398
|
/**
|
|
@@ -488,12 +496,14 @@ class Responder {
|
|
|
488
496
|
/**
|
|
489
497
|
*
|
|
490
498
|
* @param {'default'|string} contextType
|
|
491
|
-
* @param {LLMCallPreset} callPreset
|
|
499
|
+
* @param {LLMCallPreset} [callPreset]
|
|
500
|
+
* @param {import('./LLMSession').PossiblyAsyncContent} [errorPrompt]
|
|
492
501
|
* @returns {LLMSession}
|
|
493
502
|
*/
|
|
494
503
|
llmSessionWithHistory (
|
|
495
504
|
contextType = this.LLM_CTX_DEFAULT,
|
|
496
|
-
callPreset = undefined
|
|
505
|
+
callPreset = undefined,
|
|
506
|
+
errorPrompt = undefined
|
|
497
507
|
) {
|
|
498
508
|
const {
|
|
499
509
|
transcriptAnonymize,
|
|
@@ -502,7 +512,7 @@ class Responder {
|
|
|
502
512
|
} = this.llm.llmOptions(callPreset);
|
|
503
513
|
|
|
504
514
|
const filters = this._filtersForContext(contextType);
|
|
505
|
-
|
|
515
|
+
const session = new LLMSession(this.llm, [
|
|
506
516
|
this._getSystemMessagesForType(contextType),
|
|
507
517
|
this._getTranscriptMessages(transcriptLength, transcriptFlag, transcriptAnonymize)
|
|
508
518
|
], {
|
|
@@ -511,6 +521,11 @@ class Responder {
|
|
|
511
521
|
preset: callPreset,
|
|
512
522
|
res: this
|
|
513
523
|
});
|
|
524
|
+
session.onDelegatedError(
|
|
525
|
+
errorPrompt ?? (() => this._getSystemContentForType(this.LLM_ERROR)
|
|
526
|
+
.then((parts) => parts.join('\n\n')))
|
|
527
|
+
);
|
|
528
|
+
return session;
|
|
514
529
|
}
|
|
515
530
|
|
|
516
531
|
async _getTranscriptMessages (transcriptLength, transcriptFlag, transcriptAnonymize) {
|