@promptbook/cli 0.89.0-5 โ 0.89.0-7
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/esm/index.es.js +1405 -1066
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/core.index.d.ts +8 -4
- package/esm/typings/src/_packages/remote-client.index.d.ts +0 -2
- package/esm/typings/src/cli/common/$addGlobalOptionsToCommand.d.ts +7 -0
- package/esm/typings/src/cli/common/$provideLlmToolsForCli.d.ts +15 -0
- package/esm/typings/src/config.d.ts +15 -8
- package/esm/typings/src/errors/0-index.d.ts +3 -0
- package/esm/typings/src/errors/AuthenticationError.d.ts +9 -0
- package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizzardOrCli.d.ts +34 -1
- package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionToolsOptions.d.ts +1 -1
- package/esm/typings/src/llm-providers/anthropic-claude/register-configuration.d.ts +1 -1
- package/esm/typings/src/remote-server/types/RemoteClientOptions.d.ts +2 -10
- package/esm/typings/src/remote-server/types/RemoteServerOptions.d.ts +30 -2
- package/package.json +3 -1
- package/umd/index.umd.js +1607 -1268
- package/umd/index.umd.js.map +1 -1
package/esm/index.es.js
CHANGED
|
@@ -2,11 +2,13 @@ import colors from 'colors';
|
|
|
2
2
|
import commander from 'commander';
|
|
3
3
|
import spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
|
|
4
4
|
import { forTime, forEver } from 'waitasecond';
|
|
5
|
+
import prompts from 'prompts';
|
|
5
6
|
import { basename, join, dirname, relative } from 'path';
|
|
6
7
|
import { stat, access, constants, readFile, writeFile, readdir, mkdir, unlink, rm, rename, rmdir } from 'fs/promises';
|
|
7
8
|
import hexEncoder from 'crypto-js/enc-hex';
|
|
8
9
|
import sha256 from 'crypto-js/sha256';
|
|
9
10
|
import { randomBytes } from 'crypto';
|
|
11
|
+
import { io } from 'socket.io-client';
|
|
10
12
|
import { Subject } from 'rxjs';
|
|
11
13
|
import * as dotenv from 'dotenv';
|
|
12
14
|
import { spawn } from 'child_process';
|
|
@@ -17,12 +19,12 @@ import { parse, unparse } from 'papaparse';
|
|
|
17
19
|
import { SHA256 } from 'crypto-js';
|
|
18
20
|
import { lookup, extension } from 'mime-types';
|
|
19
21
|
import glob from 'glob-promise';
|
|
20
|
-
import prompts from 'prompts';
|
|
21
22
|
import moment from 'moment';
|
|
22
23
|
import express from 'express';
|
|
23
24
|
import http from 'http';
|
|
24
25
|
import { Server } from 'socket.io';
|
|
25
|
-
import
|
|
26
|
+
import swaggerJsdoc from 'swagger-jsdoc';
|
|
27
|
+
import swaggerUi from 'swagger-ui-express';
|
|
26
28
|
import Anthropic from '@anthropic-ai/sdk';
|
|
27
29
|
import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
|
|
28
30
|
import OpenAI from 'openai';
|
|
@@ -44,7 +46,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
|
|
|
44
46
|
* @generated
|
|
45
47
|
* @see https://github.com/webgptorg/promptbook
|
|
46
48
|
*/
|
|
47
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.89.0-
|
|
49
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.89.0-7';
|
|
48
50
|
/**
|
|
49
51
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
50
52
|
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
@@ -213,6 +215,7 @@ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [๐คนโโ๏ธ]
|
|
|
213
215
|
*/
|
|
214
216
|
const DEFAULT_BOOKS_DIRNAME = './books';
|
|
215
217
|
// <- TODO: [๐] Make also `BOOKS_DIRNAME_ALTERNATIVES`
|
|
218
|
+
// TODO: !!!!!! Just .promptbook dir, hardocode others
|
|
216
219
|
/**
|
|
217
220
|
* Where to store the temporary downloads
|
|
218
221
|
*
|
|
@@ -237,6 +240,21 @@ const DEFAULT_EXECUTION_CACHE_DIRNAME = './.promptbook/execution-cache';
|
|
|
237
240
|
* @public exported from `@promptbook/core`
|
|
238
241
|
*/
|
|
239
242
|
const DEFAULT_SCRAPE_CACHE_DIRNAME = './.promptbook/scrape-cache';
|
|
243
|
+
/**
|
|
244
|
+
* Id of application for the CLI when using remote server
|
|
245
|
+
*
|
|
246
|
+
* @public exported from `@promptbook/core`
|
|
247
|
+
*/
|
|
248
|
+
const CLI_APP_ID = 'cli';
|
|
249
|
+
/*
|
|
250
|
+
TODO: [๐]
|
|
251
|
+
/**
|
|
252
|
+
* Id of application for the wizzard when using remote server
|
|
253
|
+
*
|
|
254
|
+
* @public exported from `@promptbook/core`
|
|
255
|
+
* /
|
|
256
|
+
ex-port const WIZZARD_APP_ID: string_app_id = 'wizzard';
|
|
257
|
+
*/
|
|
240
258
|
/**
|
|
241
259
|
* The name of the builded pipeline collection made by CLI `ptbk make` and for lookup in `createCollectionFromDirectory`
|
|
242
260
|
*
|
|
@@ -257,13 +275,7 @@ const MOMENT_ARG_THRESHOLDS = {
|
|
|
257
275
|
*
|
|
258
276
|
* @public exported from `@promptbook/core`
|
|
259
277
|
*/
|
|
260
|
-
const
|
|
261
|
-
/**
|
|
262
|
-
* @@@
|
|
263
|
-
*
|
|
264
|
-
* @public exported from `@promptbook/core`
|
|
265
|
-
*/
|
|
266
|
-
const DEFAULT_REMOTE_URL_PATH = '/promptbook/socket.io';
|
|
278
|
+
const DEFAULT_REMOTE_SERVER_URL = 'https://api.pavolhejny.com/promptbook';
|
|
267
279
|
// <- TODO: [๐งโโ๏ธ]
|
|
268
280
|
/**
|
|
269
281
|
* @@@
|
|
@@ -305,7 +317,7 @@ const IS_PIPELINE_LOGIC_VALIDATED = just(
|
|
|
305
317
|
true);
|
|
306
318
|
/**
|
|
307
319
|
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
308
|
-
* TODO: [๐ง ][๐งโโ๏ธ] Maybe join
|
|
320
|
+
* TODO: [๐ง ][๐งโโ๏ธ] Maybe join remoteServerUrl and path into single value
|
|
309
321
|
*/
|
|
310
322
|
|
|
311
323
|
/**
|
|
@@ -483,7 +495,8 @@ function $initializeHelloCommand(program) {
|
|
|
483
495
|
helloCommand.alias('hi');
|
|
484
496
|
helloCommand.argument('[name]', 'Your name', 'Paul');
|
|
485
497
|
helloCommand.option('-g, --greeting <greeting>', `Greeting`, 'Hello');
|
|
486
|
-
helloCommand.action(handleActionErrors(async (name,
|
|
498
|
+
helloCommand.action(handleActionErrors(async (name, cliOptions) => {
|
|
499
|
+
const { greeting } = cliOptions;
|
|
487
500
|
console.info(colors.cyan(`${greeting} ${name}`));
|
|
488
501
|
await forTime(1000);
|
|
489
502
|
console.info(colors.rainbow(`Nice to meet you!`));
|
|
@@ -497,40 +510,27 @@ function $initializeHelloCommand(program) {
|
|
|
497
510
|
*/
|
|
498
511
|
|
|
499
512
|
/**
|
|
500
|
-
*
|
|
501
|
-
* No side effects.
|
|
502
|
-
*
|
|
503
|
-
* Note: It can be usefull suppressing eslint errors of unused variables
|
|
513
|
+
* This error type indicates that some part of the code is not implemented yet
|
|
504
514
|
*
|
|
505
|
-
* @
|
|
506
|
-
* @returns void
|
|
507
|
-
* @private within the repository
|
|
515
|
+
* @public exported from `@promptbook/core`
|
|
508
516
|
*/
|
|
509
|
-
|
|
510
|
-
|
|
517
|
+
class NotYetImplementedError extends Error {
|
|
518
|
+
constructor(message) {
|
|
519
|
+
super(spaceTrim$1((block) => `
|
|
520
|
+
${block(message)}
|
|
511
521
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
522
|
+
Note: This feature is not implemented yet but it will be soon.
|
|
523
|
+
|
|
524
|
+
If you want speed up the implementation or just read more, look here:
|
|
525
|
+
https://github.com/webgptorg/promptbook
|
|
526
|
+
|
|
527
|
+
Or contact us on pavol@ptbk.io
|
|
528
|
+
|
|
529
|
+
`));
|
|
530
|
+
this.name = 'NotYetImplementedError';
|
|
531
|
+
Object.setPrototypeOf(this, NotYetImplementedError.prototype);
|
|
520
532
|
}
|
|
521
|
-
return {
|
|
522
|
-
stat,
|
|
523
|
-
access,
|
|
524
|
-
constants,
|
|
525
|
-
readFile,
|
|
526
|
-
writeFile,
|
|
527
|
-
readdir,
|
|
528
|
-
mkdir,
|
|
529
|
-
};
|
|
530
533
|
}
|
|
531
|
-
/**
|
|
532
|
-
* Note: [๐ข] Code in this file should never be never released in packages that could be imported into browser environment
|
|
533
|
-
*/
|
|
534
534
|
|
|
535
535
|
/**
|
|
536
536
|
* Make error report URL for the given error
|
|
@@ -601,120 +601,566 @@ class UnexpectedError extends Error {
|
|
|
601
601
|
}
|
|
602
602
|
|
|
603
603
|
/**
|
|
604
|
-
*
|
|
604
|
+
* @@@
|
|
605
605
|
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
606
|
+
* Note: `$` is used to indicate that this function is not a pure function - it access global scope
|
|
607
|
+
*
|
|
608
|
+
* @private internal function of `$Register`
|
|
608
609
|
*/
|
|
609
|
-
function
|
|
610
|
-
|
|
611
|
-
const orderedValue = {
|
|
612
|
-
...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
|
|
613
|
-
...value,
|
|
614
|
-
};
|
|
615
|
-
return orderedValue;
|
|
610
|
+
function $getGlobalScope() {
|
|
611
|
+
return Function('return this')();
|
|
616
612
|
}
|
|
617
613
|
|
|
618
614
|
/**
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
* Note: `$` is used to indicate that this function is not a pure function - it mutates given object
|
|
622
|
-
* Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
|
|
615
|
+
* @@@
|
|
623
616
|
*
|
|
624
|
-
* @
|
|
617
|
+
* @param text @@@
|
|
618
|
+
* @returns @@@
|
|
619
|
+
* @example 'HELLO_WORLD'
|
|
620
|
+
* @example 'I_LOVE_PROMPTBOOK'
|
|
625
621
|
* @public exported from `@promptbook/utils`
|
|
626
622
|
*/
|
|
627
|
-
function
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
623
|
+
function normalizeTo_SCREAMING_CASE(text) {
|
|
624
|
+
let charType;
|
|
625
|
+
let lastCharType = 'OTHER';
|
|
626
|
+
let normalizedName = '';
|
|
627
|
+
for (const char of text) {
|
|
628
|
+
let normalizedChar;
|
|
629
|
+
if (/^[a-z]$/.test(char)) {
|
|
630
|
+
charType = 'LOWERCASE';
|
|
631
|
+
normalizedChar = char.toUpperCase();
|
|
632
|
+
}
|
|
633
|
+
else if (/^[A-Z]$/.test(char)) {
|
|
634
|
+
charType = 'UPPERCASE';
|
|
635
|
+
normalizedChar = char;
|
|
636
|
+
}
|
|
637
|
+
else if (/^[0-9]$/.test(char)) {
|
|
638
|
+
charType = 'NUMBER';
|
|
639
|
+
normalizedChar = char;
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
charType = 'OTHER';
|
|
643
|
+
normalizedChar = '_';
|
|
644
|
+
}
|
|
645
|
+
if (charType !== lastCharType &&
|
|
646
|
+
!(lastCharType === 'UPPERCASE' && charType === 'LOWERCASE') &&
|
|
647
|
+
!(lastCharType === 'NUMBER') &&
|
|
648
|
+
!(charType === 'NUMBER')) {
|
|
649
|
+
normalizedName += '_';
|
|
636
650
|
}
|
|
651
|
+
normalizedName += normalizedChar;
|
|
652
|
+
lastCharType = charType;
|
|
637
653
|
}
|
|
638
|
-
|
|
639
|
-
|
|
654
|
+
normalizedName = normalizedName.replace(/_+/g, '_');
|
|
655
|
+
normalizedName = normalizedName.replace(/_?\/_?/g, '/');
|
|
656
|
+
normalizedName = normalizedName.replace(/^_/, '');
|
|
657
|
+
normalizedName = normalizedName.replace(/_$/, '');
|
|
658
|
+
return normalizedName;
|
|
640
659
|
}
|
|
641
660
|
/**
|
|
642
|
-
* TODO:
|
|
661
|
+
* TODO: Tests
|
|
662
|
+
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: 'Moje tabule' })).toEqual('/VtG7sR9rRJqwNEdM2/Moje tabule');
|
|
663
|
+
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: 'ฤลกฤลลพลพรฝรกรญรบลฏ' })).toEqual('/VtG7sR9rRJqwNEdM2/escrzyaieuu');
|
|
664
|
+
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: ' ahoj ' })).toEqual('/VtG7sR9rRJqwNEdM2/ahoj');
|
|
665
|
+
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: ' ahoj_ahojAhoj ahoj ' })).toEqual('/VtG7sR9rRJqwNEdM2/ahoj-ahoj-ahoj-ahoj');
|
|
666
|
+
* TODO: [๐บ] Use some intermediate util splitWords
|
|
643
667
|
*/
|
|
644
668
|
|
|
645
669
|
/**
|
|
646
|
-
*
|
|
647
|
-
* If not, throws an UnexpectedError with a rich error message and tracking
|
|
648
|
-
*
|
|
649
|
-
* - Almost all primitives are serializable BUT:
|
|
650
|
-
* - `undefined` is not serializable
|
|
651
|
-
* - `NaN` is not serializable
|
|
652
|
-
* - Objects and arrays are serializable if all their properties are serializable
|
|
653
|
-
* - Functions are not serializable
|
|
654
|
-
* - Circular references are not serializable
|
|
655
|
-
* - `Date` objects are not serializable
|
|
656
|
-
* - `Map` and `Set` objects are not serializable
|
|
657
|
-
* - `RegExp` objects are not serializable
|
|
658
|
-
* - `Error` objects are not serializable
|
|
659
|
-
* - `Symbol` objects are not serializable
|
|
660
|
-
* - And much more...
|
|
670
|
+
* @@@
|
|
661
671
|
*
|
|
662
|
-
* @
|
|
672
|
+
* @param text @@@
|
|
673
|
+
* @returns @@@
|
|
674
|
+
* @example 'hello_world'
|
|
675
|
+
* @example 'i_love_promptbook'
|
|
663
676
|
* @public exported from `@promptbook/utils`
|
|
664
677
|
*/
|
|
665
|
-
function
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
throw new UnexpectedError(`${name} is undefined`);
|
|
669
|
-
}
|
|
670
|
-
else if (value === null) {
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
else if (typeof value === 'boolean') {
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
else if (typeof value === 'number' && !isNaN(value)) {
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
else if (typeof value === 'string') {
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
else if (typeof value === 'symbol') {
|
|
683
|
-
throw new UnexpectedError(`${name} is symbol`);
|
|
684
|
-
}
|
|
685
|
-
else if (typeof value === 'function') {
|
|
686
|
-
throw new UnexpectedError(`${name} is function`);
|
|
687
|
-
}
|
|
688
|
-
else if (typeof value === 'object' && Array.isArray(value)) {
|
|
689
|
-
for (let i = 0; i < value.length; i++) {
|
|
690
|
-
checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
else if (typeof value === 'object') {
|
|
694
|
-
if (value instanceof Date) {
|
|
695
|
-
throw new UnexpectedError(spaceTrim((block) => `
|
|
696
|
-
\`${name}\` is Date
|
|
697
|
-
|
|
698
|
-
Use \`string_date_iso8601\` instead
|
|
678
|
+
function normalizeTo_snake_case(text) {
|
|
679
|
+
return normalizeTo_SCREAMING_CASE(text).toLowerCase();
|
|
680
|
+
}
|
|
699
681
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
682
|
+
/**
|
|
683
|
+
* Register is @@@
|
|
684
|
+
*
|
|
685
|
+
* Note: `$` is used to indicate that this function is not a pure function - it accesses and adds variables in global scope.
|
|
686
|
+
*
|
|
687
|
+
* @private internal utility, exported are only signleton instances of this class
|
|
688
|
+
*/
|
|
689
|
+
class $Register {
|
|
690
|
+
constructor(registerName) {
|
|
691
|
+
this.registerName = registerName;
|
|
692
|
+
const storageName = `_promptbook_${normalizeTo_snake_case(registerName)}`;
|
|
693
|
+
const globalScope = $getGlobalScope();
|
|
694
|
+
if (globalScope[storageName] === undefined) {
|
|
695
|
+
globalScope[storageName] = [];
|
|
703
696
|
}
|
|
704
|
-
else if (
|
|
705
|
-
throw new UnexpectedError(
|
|
697
|
+
else if (!Array.isArray(globalScope[storageName])) {
|
|
698
|
+
throw new UnexpectedError(`Expected (global) ${storageName} to be an array, but got ${typeof globalScope[storageName]}`);
|
|
706
699
|
}
|
|
707
|
-
|
|
708
|
-
|
|
700
|
+
this.storage = globalScope[storageName];
|
|
701
|
+
}
|
|
702
|
+
list() {
|
|
703
|
+
// <- TODO: ReadonlyDeep<ReadonlyArray<TRegistered>>
|
|
704
|
+
return this.storage;
|
|
705
|
+
}
|
|
706
|
+
register(registered) {
|
|
707
|
+
const { packageName, className } = registered;
|
|
708
|
+
const existingRegistrationIndex = this.storage.findIndex((item) => item.packageName === packageName && item.className === className);
|
|
709
|
+
const existingRegistration = this.storage[existingRegistrationIndex];
|
|
710
|
+
if (!existingRegistration) {
|
|
711
|
+
this.storage.push(registered);
|
|
709
712
|
}
|
|
710
|
-
else
|
|
711
|
-
|
|
713
|
+
else {
|
|
714
|
+
this.storage[existingRegistrationIndex] = registered;
|
|
712
715
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
716
|
+
return {
|
|
717
|
+
registerName: this.registerName,
|
|
718
|
+
packageName,
|
|
719
|
+
className,
|
|
720
|
+
get isDestroyed() {
|
|
721
|
+
return false;
|
|
722
|
+
},
|
|
723
|
+
destroy() {
|
|
724
|
+
throw new NotYetImplementedError(`Registration to ${this.registerName} is permanent in this version of Promptbook`);
|
|
725
|
+
},
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* @@@
|
|
732
|
+
*
|
|
733
|
+
* Note: `$` is used to indicate that this interacts with the global scope
|
|
734
|
+
* @singleton Only one instance of each register is created per build, but thare can be more @@@
|
|
735
|
+
* @public exported from `@promptbook/core`
|
|
736
|
+
*/
|
|
737
|
+
const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
|
|
738
|
+
/**
|
|
739
|
+
* TODO: [ยฎ] DRY Register logic
|
|
740
|
+
*/
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* @@@
|
|
744
|
+
*
|
|
745
|
+
* Note: `$` is used to indicate that this interacts with the global scope
|
|
746
|
+
* @singleton Only one instance of each register is created per build, but thare can be more @@@
|
|
747
|
+
* @public exported from `@promptbook/core`
|
|
748
|
+
*/
|
|
749
|
+
const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
|
|
750
|
+
/**
|
|
751
|
+
* TODO: [ยฎ] DRY Register logic
|
|
752
|
+
*/
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Path to the `.env` file which was used to configure LLM tools
|
|
756
|
+
*
|
|
757
|
+
* Note: `$` is used to indicate that this variable is changed by side effect in `$provideLlmToolsConfigurationFromEnv` through `$setUsedEnvFilename`
|
|
758
|
+
*/
|
|
759
|
+
let $usedEnvFilename = null;
|
|
760
|
+
/**
|
|
761
|
+
* Pass the `.env` file which was used to configure LLM tools
|
|
762
|
+
*
|
|
763
|
+
* Note: `$` is used to indicate that this variable is making side effect
|
|
764
|
+
*
|
|
765
|
+
* @private internal log of `$provideLlmToolsConfigurationFromEnv` and `$registeredLlmToolsMessage`
|
|
766
|
+
*/
|
|
767
|
+
function $setUsedEnvFilename(filepath) {
|
|
768
|
+
$usedEnvFilename = filepath;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Creates a message with all registered LLM tools
|
|
772
|
+
*
|
|
773
|
+
* Note: This function is used to create a (error) message when there is no constructor for some LLM provider
|
|
774
|
+
*
|
|
775
|
+
* @private internal function of `createLlmToolsFromConfiguration` and `$provideLlmToolsFromEnv`
|
|
776
|
+
*/
|
|
777
|
+
function $registeredLlmToolsMessage() {
|
|
778
|
+
let env;
|
|
779
|
+
if ($isRunningInNode()) {
|
|
780
|
+
env = process.env;
|
|
781
|
+
// <- TODO: [โ] Some DRY way how to get to `process.env` and pass it into functions - ACRY search for `env`
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
env = {};
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Mixes registered LLM tools from $llmToolsMetadataRegister and $llmToolsRegister
|
|
788
|
+
*/
|
|
789
|
+
const all = [];
|
|
790
|
+
for (const { title, packageName, className, envVariables } of $llmToolsMetadataRegister.list()) {
|
|
791
|
+
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
all.push({ title, packageName, className, envVariables });
|
|
795
|
+
}
|
|
796
|
+
for (const { packageName, className } of $llmToolsRegister.list()) {
|
|
797
|
+
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
all.push({ packageName, className });
|
|
801
|
+
}
|
|
802
|
+
const metadata = all.map((metadata) => {
|
|
803
|
+
var _a, _b;
|
|
804
|
+
const isMetadataAviailable = $llmToolsMetadataRegister
|
|
805
|
+
.list()
|
|
806
|
+
.find(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
|
|
807
|
+
const isInstalled = $llmToolsRegister
|
|
808
|
+
.list()
|
|
809
|
+
.find(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
|
|
810
|
+
const isFullyConfigured = ((_a = metadata.envVariables) === null || _a === void 0 ? void 0 : _a.every((envVariableName) => env[envVariableName] !== undefined)) || false;
|
|
811
|
+
const isPartiallyConfigured = ((_b = metadata.envVariables) === null || _b === void 0 ? void 0 : _b.some((envVariableName) => env[envVariableName] !== undefined)) || false;
|
|
812
|
+
// <- Note: [๐จ]
|
|
813
|
+
return { ...metadata, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured };
|
|
814
|
+
});
|
|
815
|
+
const usedEnvMessage = $usedEnvFilename === null ? `Unknown \`.env\` file` : `Used \`.env\` file:\n${$usedEnvFilename}`;
|
|
816
|
+
if (metadata.length === 0) {
|
|
817
|
+
return spaceTrim((block) => `
|
|
818
|
+
No LLM providers are available.
|
|
819
|
+
|
|
820
|
+
${block(usedEnvMessage)}
|
|
821
|
+
`);
|
|
822
|
+
}
|
|
823
|
+
return spaceTrim((block) => `
|
|
824
|
+
|
|
825
|
+
${block(usedEnvMessage)}
|
|
826
|
+
|
|
827
|
+
Relevant environment variables:
|
|
828
|
+
${block(Object.keys(env)
|
|
829
|
+
.filter((envVariableName) => metadata.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)))
|
|
830
|
+
.map((envVariableName) => `- \`${envVariableName}\``)
|
|
831
|
+
.join('\n'))}
|
|
832
|
+
|
|
833
|
+
Available LLM providers are:
|
|
834
|
+
${block(metadata
|
|
835
|
+
.map(({ title, packageName, className, envVariables, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured, }, i) => {
|
|
836
|
+
const morePieces = [];
|
|
837
|
+
if (just(false)) ;
|
|
838
|
+
else if (!isMetadataAviailable && !isInstalled) {
|
|
839
|
+
// TODO: [๏ฟฝ][๏ฟฝ] Maybe do allow to do auto-install if package not registered and not found
|
|
840
|
+
morePieces.push(`Not installed and no metadata, looks like a unexpected behavior`);
|
|
841
|
+
}
|
|
842
|
+
else if (isMetadataAviailable && !isInstalled) {
|
|
843
|
+
// TODO: [๏ฟฝ][๏ฟฝ]
|
|
844
|
+
morePieces.push(`Not installed`);
|
|
845
|
+
}
|
|
846
|
+
else if (!isMetadataAviailable && isInstalled) {
|
|
847
|
+
morePieces.push(`No metadata but installed, looks like a unexpected behavior`);
|
|
848
|
+
}
|
|
849
|
+
else if (isMetadataAviailable && isInstalled) {
|
|
850
|
+
morePieces.push(`Installed`);
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
morePieces.push(`unknown state, looks like a unexpected behavior`);
|
|
854
|
+
} /* not else */
|
|
855
|
+
if (isFullyConfigured) {
|
|
856
|
+
morePieces.push(`Configured`);
|
|
857
|
+
}
|
|
858
|
+
else if (isPartiallyConfigured) {
|
|
859
|
+
morePieces.push(`Partially confugured, missing ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined).join(' + ')}`);
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
if (envVariables !== null) {
|
|
863
|
+
morePieces.push(`Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`);
|
|
864
|
+
}
|
|
865
|
+
else {
|
|
866
|
+
morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
let providerMessage = spaceTrim(`
|
|
870
|
+
${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
871
|
+
${morePieces.join('; ')}
|
|
872
|
+
`);
|
|
873
|
+
if ($isRunningInNode) {
|
|
874
|
+
if (isInstalled && isFullyConfigured) {
|
|
875
|
+
providerMessage = colors.green(providerMessage);
|
|
876
|
+
}
|
|
877
|
+
else if (isInstalled && isPartiallyConfigured) {
|
|
878
|
+
providerMessage = colors.yellow(providerMessage);
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
providerMessage = colors.gray(providerMessage);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return providerMessage;
|
|
885
|
+
})
|
|
886
|
+
.join('\n'))}
|
|
887
|
+
`);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* TODO: [ยฎ] DRY Register logic
|
|
891
|
+
* TODO: [๐ง ][โ] Maybe pass env as argument
|
|
892
|
+
*/
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Just says that the variable is not used but should be kept
|
|
896
|
+
* No side effects.
|
|
897
|
+
*
|
|
898
|
+
* Note: It can be usefull for:
|
|
899
|
+
*
|
|
900
|
+
* 1) Suppressing eager optimization of unused imports
|
|
901
|
+
* 2) Suppressing eslint errors of unused variables in the tests
|
|
902
|
+
* 3) Keeping the type of the variable for type testing
|
|
903
|
+
*
|
|
904
|
+
* @param value any values
|
|
905
|
+
* @returns void
|
|
906
|
+
* @private within the repository
|
|
907
|
+
*/
|
|
908
|
+
function keepUnused(...valuesToKeep) {
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Just says that the variable is not used directlys but should be kept because the existence of the variable is important
|
|
913
|
+
*
|
|
914
|
+
* @param value any values
|
|
915
|
+
* @returns void
|
|
916
|
+
* @private within the repository
|
|
917
|
+
*/
|
|
918
|
+
function $sideEffect(...sideEffectSubjects) {
|
|
919
|
+
keepUnused(...sideEffectSubjects);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Checks if value is valid email
|
|
924
|
+
*
|
|
925
|
+
* @public exported from `@promptbook/utils`
|
|
926
|
+
*/
|
|
927
|
+
function isValidEmail(email) {
|
|
928
|
+
if (typeof email !== 'string') {
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
if (email.split('\n').length > 1) {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
return /^.+@.+\..+$/.test(email);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Tests if given string is valid URL.
|
|
939
|
+
*
|
|
940
|
+
* Note: Dataurl are considered perfectly valid.
|
|
941
|
+
* Note: There are two simmilar functions:
|
|
942
|
+
* - `isValidUrl` which tests any URL
|
|
943
|
+
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
944
|
+
*
|
|
945
|
+
* @public exported from `@promptbook/utils`
|
|
946
|
+
*/
|
|
947
|
+
function isValidUrl(url) {
|
|
948
|
+
if (typeof url !== 'string') {
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
951
|
+
try {
|
|
952
|
+
if (url.startsWith('blob:')) {
|
|
953
|
+
url = url.replace(/^blob:/, '');
|
|
954
|
+
}
|
|
955
|
+
const urlObject = new URL(url /* because fail is handled */);
|
|
956
|
+
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
961
|
+
catch (error) {
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Stores data in memory (HEAP)
|
|
968
|
+
*
|
|
969
|
+
* @public exported from `@promptbook/core`
|
|
970
|
+
*/
|
|
971
|
+
class MemoryStorage {
|
|
972
|
+
constructor() {
|
|
973
|
+
this.storage = {};
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Returns the number of key/value pairs currently present in the list associated with the object.
|
|
977
|
+
*/
|
|
978
|
+
get length() {
|
|
979
|
+
return Object.keys(this.storage).length;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Empties the list associated with the object of all key/value pairs, if there are any.
|
|
983
|
+
*/
|
|
984
|
+
clear() {
|
|
985
|
+
this.storage = {};
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Returns the current value associated with the given key, or null if the given key does not exist in the list associated with the object.
|
|
989
|
+
*/
|
|
990
|
+
getItem(key) {
|
|
991
|
+
return this.storage[key] || null;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Returns the name of the nth key in the list, or null if n is greater than or equal to the number of key/value pairs in the object.
|
|
995
|
+
*/
|
|
996
|
+
key(index) {
|
|
997
|
+
return Object.keys(this.storage)[index] || null;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
|
|
1001
|
+
*/
|
|
1002
|
+
setItem(key, value) {
|
|
1003
|
+
this.storage[key] = value;
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Removes the key/value pair with the given key from the list associated with the object, if a key/value pair with the given key exists.
|
|
1007
|
+
*/
|
|
1008
|
+
removeItem(key) {
|
|
1009
|
+
delete this.storage[key];
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Just marks a place of place where should be something implemented
|
|
1015
|
+
* No side effects.
|
|
1016
|
+
*
|
|
1017
|
+
* Note: It can be usefull suppressing eslint errors of unused variables
|
|
1018
|
+
*
|
|
1019
|
+
* @param value any values
|
|
1020
|
+
* @returns void
|
|
1021
|
+
* @private within the repository
|
|
1022
|
+
*/
|
|
1023
|
+
function TODO_USE(...value) {
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* @@@
|
|
1028
|
+
*
|
|
1029
|
+
* @public exported from `@promptbook/node`
|
|
1030
|
+
*/
|
|
1031
|
+
function $provideFilesystemForNode(options) {
|
|
1032
|
+
if (!$isRunningInNode()) {
|
|
1033
|
+
throw new EnvironmentMismatchError('Function `$provideFilesystemForNode` works only in Node.js environment');
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
stat,
|
|
1037
|
+
access,
|
|
1038
|
+
constants,
|
|
1039
|
+
readFile,
|
|
1040
|
+
writeFile,
|
|
1041
|
+
readdir,
|
|
1042
|
+
mkdir,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Note: [๐ข] Code in this file should never be never released in packages that could be imported into browser environment
|
|
1047
|
+
*/
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* Orders JSON object by keys
|
|
1051
|
+
*
|
|
1052
|
+
* @returns The same type of object as the input re-ordered
|
|
1053
|
+
* @public exported from `@promptbook/utils`
|
|
1054
|
+
*/
|
|
1055
|
+
function orderJson(options) {
|
|
1056
|
+
const { value, order } = options;
|
|
1057
|
+
const orderedValue = {
|
|
1058
|
+
...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
|
|
1059
|
+
...value,
|
|
1060
|
+
};
|
|
1061
|
+
return orderedValue;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Freezes the given object and all its nested objects recursively
|
|
1066
|
+
*
|
|
1067
|
+
* Note: `$` is used to indicate that this function is not a pure function - it mutates given object
|
|
1068
|
+
* Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
|
|
1069
|
+
*
|
|
1070
|
+
* @returns The same object as the input, but deeply frozen
|
|
1071
|
+
* @public exported from `@promptbook/utils`
|
|
1072
|
+
*/
|
|
1073
|
+
function $deepFreeze(objectValue) {
|
|
1074
|
+
if (Array.isArray(objectValue)) {
|
|
1075
|
+
return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
|
|
1076
|
+
}
|
|
1077
|
+
const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
1078
|
+
for (const propertyName of propertyNames) {
|
|
1079
|
+
const value = objectValue[propertyName];
|
|
1080
|
+
if (value && typeof value === 'object') {
|
|
1081
|
+
$deepFreeze(value);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
Object.freeze(objectValue);
|
|
1085
|
+
return objectValue;
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* TODO: [๐ง ] Is there a way how to meaningfully test this utility
|
|
1089
|
+
*/
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Checks if the value is [๐] serializable as JSON
|
|
1093
|
+
* If not, throws an UnexpectedError with a rich error message and tracking
|
|
1094
|
+
*
|
|
1095
|
+
* - Almost all primitives are serializable BUT:
|
|
1096
|
+
* - `undefined` is not serializable
|
|
1097
|
+
* - `NaN` is not serializable
|
|
1098
|
+
* - Objects and arrays are serializable if all their properties are serializable
|
|
1099
|
+
* - Functions are not serializable
|
|
1100
|
+
* - Circular references are not serializable
|
|
1101
|
+
* - `Date` objects are not serializable
|
|
1102
|
+
* - `Map` and `Set` objects are not serializable
|
|
1103
|
+
* - `RegExp` objects are not serializable
|
|
1104
|
+
* - `Error` objects are not serializable
|
|
1105
|
+
* - `Symbol` objects are not serializable
|
|
1106
|
+
* - And much more...
|
|
1107
|
+
*
|
|
1108
|
+
* @throws UnexpectedError if the value is not serializable as JSON
|
|
1109
|
+
* @public exported from `@promptbook/utils`
|
|
1110
|
+
*/
|
|
1111
|
+
function checkSerializableAsJson(options) {
|
|
1112
|
+
const { value, name, message } = options;
|
|
1113
|
+
if (value === undefined) {
|
|
1114
|
+
throw new UnexpectedError(`${name} is undefined`);
|
|
1115
|
+
}
|
|
1116
|
+
else if (value === null) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
else if (typeof value === 'boolean') {
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
else if (typeof value === 'number' && !isNaN(value)) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
else if (typeof value === 'string') {
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
else if (typeof value === 'symbol') {
|
|
1129
|
+
throw new UnexpectedError(`${name} is symbol`);
|
|
1130
|
+
}
|
|
1131
|
+
else if (typeof value === 'function') {
|
|
1132
|
+
throw new UnexpectedError(`${name} is function`);
|
|
1133
|
+
}
|
|
1134
|
+
else if (typeof value === 'object' && Array.isArray(value)) {
|
|
1135
|
+
for (let i = 0; i < value.length; i++) {
|
|
1136
|
+
checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
else if (typeof value === 'object') {
|
|
1140
|
+
if (value instanceof Date) {
|
|
1141
|
+
throw new UnexpectedError(spaceTrim((block) => `
|
|
1142
|
+
\`${name}\` is Date
|
|
1143
|
+
|
|
1144
|
+
Use \`string_date_iso8601\` instead
|
|
1145
|
+
|
|
1146
|
+
Additional message for \`${name}\`:
|
|
1147
|
+
${block(message || '(nothing)')}
|
|
1148
|
+
`));
|
|
1149
|
+
}
|
|
1150
|
+
else if (value instanceof Map) {
|
|
1151
|
+
throw new UnexpectedError(`${name} is Map`);
|
|
1152
|
+
}
|
|
1153
|
+
else if (value instanceof Set) {
|
|
1154
|
+
throw new UnexpectedError(`${name} is Set`);
|
|
1155
|
+
}
|
|
1156
|
+
else if (value instanceof RegExp) {
|
|
1157
|
+
throw new UnexpectedError(`${name} is RegExp`);
|
|
1158
|
+
}
|
|
1159
|
+
else if (value instanceof Error) {
|
|
1160
|
+
throw new UnexpectedError(spaceTrim((block) => `
|
|
1161
|
+
\`${name}\` is unserialized Error
|
|
1162
|
+
|
|
1163
|
+
Use function \`serializeError\`
|
|
718
1164
|
|
|
719
1165
|
Additional message for \`${name}\`:
|
|
720
1166
|
${block(message || '(nothing)')}
|
|
@@ -1058,35 +1504,6 @@ function isValidFilePath(filename) {
|
|
|
1058
1504
|
* TODO: [๐] Implement for MacOs
|
|
1059
1505
|
*/
|
|
1060
1506
|
|
|
1061
|
-
/**
|
|
1062
|
-
* Tests if given string is valid URL.
|
|
1063
|
-
*
|
|
1064
|
-
* Note: Dataurl are considered perfectly valid.
|
|
1065
|
-
* Note: There are two simmilar functions:
|
|
1066
|
-
* - `isValidUrl` which tests any URL
|
|
1067
|
-
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
1068
|
-
*
|
|
1069
|
-
* @public exported from `@promptbook/utils`
|
|
1070
|
-
*/
|
|
1071
|
-
function isValidUrl(url) {
|
|
1072
|
-
if (typeof url !== 'string') {
|
|
1073
|
-
return false;
|
|
1074
|
-
}
|
|
1075
|
-
try {
|
|
1076
|
-
if (url.startsWith('blob:')) {
|
|
1077
|
-
url = url.replace(/^blob:/, '');
|
|
1078
|
-
}
|
|
1079
|
-
const urlObject = new URL(url /* because fail is handled */);
|
|
1080
|
-
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
1081
|
-
return false;
|
|
1082
|
-
}
|
|
1083
|
-
return true;
|
|
1084
|
-
}
|
|
1085
|
-
catch (error) {
|
|
1086
|
-
return false;
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
1507
|
const defaultDiacriticsRemovalMap = [
|
|
1091
1508
|
{
|
|
1092
1509
|
base: 'A',
|
|
@@ -1470,32 +1887,177 @@ class FileCacheStorage {
|
|
|
1470
1887
|
// TODO: [๐]
|
|
1471
1888
|
return value;
|
|
1472
1889
|
}
|
|
1473
|
-
/**
|
|
1474
|
-
* @@@ Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
|
|
1475
|
-
*/
|
|
1476
|
-
async setItem(key, value) {
|
|
1477
|
-
const filename = this.getFilenameForKey(key);
|
|
1478
|
-
if (!isSerializableAsJson(value)) {
|
|
1479
|
-
throw new UnexpectedError(`The "${key}" you want to store in JSON file is not serializable as JSON`);
|
|
1480
|
-
}
|
|
1481
|
-
const fileContent = stringifyPipelineJson(value);
|
|
1482
|
-
await mkdir(dirname(filename), { recursive: true }); // <- [0]
|
|
1483
|
-
await writeFile(filename, fileContent, 'utf-8');
|
|
1890
|
+
/**
|
|
1891
|
+
* @@@ Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
|
|
1892
|
+
*/
|
|
1893
|
+
async setItem(key, value) {
|
|
1894
|
+
const filename = this.getFilenameForKey(key);
|
|
1895
|
+
if (!isSerializableAsJson(value)) {
|
|
1896
|
+
throw new UnexpectedError(`The "${key}" you want to store in JSON file is not serializable as JSON`);
|
|
1897
|
+
}
|
|
1898
|
+
const fileContent = stringifyPipelineJson(value);
|
|
1899
|
+
await mkdir(dirname(filename), { recursive: true }); // <- [0]
|
|
1900
|
+
await writeFile(filename, fileContent, 'utf-8');
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* @@@ Removes the key/value pair with the given key from the list associated with the object, if a key/value pair with the given key exists.
|
|
1904
|
+
*/
|
|
1905
|
+
async removeItem(key) {
|
|
1906
|
+
const filename = this.getFilenameForKey(key);
|
|
1907
|
+
// TODO: [๐ง ] What to use `unlink` or `rm`
|
|
1908
|
+
await unlink(filename);
|
|
1909
|
+
// <- TODO: [๐ฟ][๐ง ] Maybe remove empty folders
|
|
1910
|
+
// [0] When `setItem` and `removeItem` called, the state of the file system should be the same
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* TODO: [๐] Maybe some checkers, not all valid JSONs are desired and valid values
|
|
1915
|
+
* Note: [๐ข] Code in this file should never be never released in packages that could be imported into browser environment
|
|
1916
|
+
*/
|
|
1917
|
+
|
|
1918
|
+
/**
|
|
1919
|
+
* This error indicates problems parsing the format value
|
|
1920
|
+
*
|
|
1921
|
+
* For example, when the format value is not a valid JSON or CSV
|
|
1922
|
+
* This is not thrown directly but in extended classes
|
|
1923
|
+
*
|
|
1924
|
+
* @public exported from `@promptbook/core`
|
|
1925
|
+
*/
|
|
1926
|
+
class AbstractFormatError extends Error {
|
|
1927
|
+
// Note: To allow instanceof do not put here error `name`
|
|
1928
|
+
// public readonly name = 'AbstractFormatError';
|
|
1929
|
+
constructor(message) {
|
|
1930
|
+
super(message);
|
|
1931
|
+
Object.setPrototypeOf(this, AbstractFormatError.prototype);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
/**
|
|
1936
|
+
* This error indicates problem with parsing of CSV
|
|
1937
|
+
*
|
|
1938
|
+
* @public exported from `@promptbook/core`
|
|
1939
|
+
*/
|
|
1940
|
+
class CsvFormatError extends AbstractFormatError {
|
|
1941
|
+
constructor(message) {
|
|
1942
|
+
super(message);
|
|
1943
|
+
this.name = 'CsvFormatError';
|
|
1944
|
+
Object.setPrototypeOf(this, CsvFormatError.prototype);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
/**
|
|
1949
|
+
* AuthenticationError is thrown from login function which is dependency of remote server
|
|
1950
|
+
*
|
|
1951
|
+
* @public exported from `@promptbook/core`
|
|
1952
|
+
*/
|
|
1953
|
+
class AuthenticationError extends Error {
|
|
1954
|
+
constructor(message) {
|
|
1955
|
+
super(message);
|
|
1956
|
+
this.name = 'AuthenticationError';
|
|
1957
|
+
Object.setPrototypeOf(this, AuthenticationError.prototype);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
/**
|
|
1962
|
+
* This error indicates that the pipeline collection cannot be propperly loaded
|
|
1963
|
+
*
|
|
1964
|
+
* @public exported from `@promptbook/core`
|
|
1965
|
+
*/
|
|
1966
|
+
class CollectionError extends Error {
|
|
1967
|
+
constructor(message) {
|
|
1968
|
+
super(message);
|
|
1969
|
+
this.name = 'CollectionError';
|
|
1970
|
+
Object.setPrototypeOf(this, CollectionError.prototype);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
/**
|
|
1975
|
+
* This error occurs when some expectation is not met in the execution of the pipeline
|
|
1976
|
+
*
|
|
1977
|
+
* @public exported from `@promptbook/core`
|
|
1978
|
+
* Note: Do not throw this error, its reserved for `checkExpectations` and `createPipelineExecutor` and public ONLY to be serializable through remote server
|
|
1979
|
+
* Note: Always thrown in `checkExpectations` and catched in `createPipelineExecutor` and rethrown as `PipelineExecutionError`
|
|
1980
|
+
* Note: This is a kindof subtype of PipelineExecutionError
|
|
1981
|
+
*/
|
|
1982
|
+
class ExpectError extends Error {
|
|
1983
|
+
constructor(message) {
|
|
1984
|
+
super(message);
|
|
1985
|
+
this.name = 'ExpectError';
|
|
1986
|
+
Object.setPrototypeOf(this, ExpectError.prototype);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
/**
|
|
1991
|
+
* This error indicates that the promptbook can not retrieve knowledge from external sources
|
|
1992
|
+
*
|
|
1993
|
+
* @public exported from `@promptbook/core`
|
|
1994
|
+
*/
|
|
1995
|
+
class KnowledgeScrapeError extends Error {
|
|
1996
|
+
constructor(message) {
|
|
1997
|
+
super(message);
|
|
1998
|
+
this.name = 'KnowledgeScrapeError';
|
|
1999
|
+
Object.setPrototypeOf(this, KnowledgeScrapeError.prototype);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
/**
|
|
2004
|
+
* This error type indicates that some limit was reached
|
|
2005
|
+
*
|
|
2006
|
+
* @public exported from `@promptbook/core`
|
|
2007
|
+
*/
|
|
2008
|
+
class LimitReachedError extends Error {
|
|
2009
|
+
constructor(message) {
|
|
2010
|
+
super(message);
|
|
2011
|
+
this.name = 'LimitReachedError';
|
|
2012
|
+
Object.setPrototypeOf(this, LimitReachedError.prototype);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/**
|
|
2017
|
+
* This error type indicates that some tools are missing for pipeline execution or preparation
|
|
2018
|
+
*
|
|
2019
|
+
* @public exported from `@promptbook/core`
|
|
2020
|
+
*/
|
|
2021
|
+
class MissingToolsError extends Error {
|
|
2022
|
+
constructor(message) {
|
|
2023
|
+
super(spaceTrim$1((block) => `
|
|
2024
|
+
${block(message)}
|
|
2025
|
+
|
|
2026
|
+
Note: You have probbably forgot to provide some tools for pipeline execution or preparation
|
|
2027
|
+
|
|
2028
|
+
`));
|
|
2029
|
+
this.name = 'MissingToolsError';
|
|
2030
|
+
Object.setPrototypeOf(this, MissingToolsError.prototype);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
/**
|
|
2035
|
+
* This error indicates that promptbook not found in the collection
|
|
2036
|
+
*
|
|
2037
|
+
* @public exported from `@promptbook/core`
|
|
2038
|
+
*/
|
|
2039
|
+
class NotFoundError extends Error {
|
|
2040
|
+
constructor(message) {
|
|
2041
|
+
super(message);
|
|
2042
|
+
this.name = 'NotFoundError';
|
|
2043
|
+
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
1484
2044
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
/**
|
|
2048
|
+
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
2049
|
+
*
|
|
2050
|
+
* @public exported from `@promptbook/core`
|
|
2051
|
+
*/
|
|
2052
|
+
class ParseError extends Error {
|
|
2053
|
+
constructor(message) {
|
|
2054
|
+
super(message);
|
|
2055
|
+
this.name = 'ParseError';
|
|
2056
|
+
Object.setPrototypeOf(this, ParseError.prototype);
|
|
1494
2057
|
}
|
|
1495
2058
|
}
|
|
1496
2059
|
/**
|
|
1497
|
-
* TODO:
|
|
1498
|
-
* Note: [๐ข] Code in this file should never be never released in packages that could be imported into browser environment
|
|
2060
|
+
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
1499
2061
|
*/
|
|
1500
2062
|
|
|
1501
2063
|
/**
|
|
@@ -1533,51 +2095,260 @@ class PipelineExecutionError extends Error {
|
|
|
1533
2095
|
*/
|
|
1534
2096
|
|
|
1535
2097
|
/**
|
|
1536
|
-
*
|
|
2098
|
+
* This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
|
|
1537
2099
|
*
|
|
1538
2100
|
* @public exported from `@promptbook/core`
|
|
1539
2101
|
*/
|
|
1540
|
-
class
|
|
1541
|
-
constructor() {
|
|
1542
|
-
|
|
2102
|
+
class PipelineLogicError extends Error {
|
|
2103
|
+
constructor(message) {
|
|
2104
|
+
super(message);
|
|
2105
|
+
this.name = 'PipelineLogicError';
|
|
2106
|
+
Object.setPrototypeOf(this, PipelineLogicError.prototype);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
/**
|
|
2111
|
+
* This error indicates errors in referencing promptbooks between each other
|
|
2112
|
+
*
|
|
2113
|
+
* @public exported from `@promptbook/core`
|
|
2114
|
+
*/
|
|
2115
|
+
class PipelineUrlError extends Error {
|
|
2116
|
+
constructor(message) {
|
|
2117
|
+
super(message);
|
|
2118
|
+
this.name = 'PipelineUrlError';
|
|
2119
|
+
Object.setPrototypeOf(this, PipelineUrlError.prototype);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
/**
|
|
2124
|
+
* Index of all custom errors
|
|
2125
|
+
*
|
|
2126
|
+
* @public exported from `@promptbook/core`
|
|
2127
|
+
*/
|
|
2128
|
+
const PROMPTBOOK_ERRORS = {
|
|
2129
|
+
AbstractFormatError,
|
|
2130
|
+
CsvFormatError,
|
|
2131
|
+
CollectionError,
|
|
2132
|
+
EnvironmentMismatchError,
|
|
2133
|
+
ExpectError,
|
|
2134
|
+
KnowledgeScrapeError,
|
|
2135
|
+
LimitReachedError,
|
|
2136
|
+
MissingToolsError,
|
|
2137
|
+
NotFoundError,
|
|
2138
|
+
NotYetImplementedError,
|
|
2139
|
+
ParseError,
|
|
2140
|
+
PipelineExecutionError,
|
|
2141
|
+
PipelineLogicError,
|
|
2142
|
+
PipelineUrlError,
|
|
2143
|
+
UnexpectedError,
|
|
2144
|
+
// TODO: [๐ช]> VersionMismatchError,
|
|
2145
|
+
};
|
|
2146
|
+
/**
|
|
2147
|
+
* Index of all javascript errors
|
|
2148
|
+
*
|
|
2149
|
+
* @private for internal usage
|
|
2150
|
+
*/
|
|
2151
|
+
const COMMON_JAVASCRIPT_ERRORS = {
|
|
2152
|
+
Error,
|
|
2153
|
+
EvalError,
|
|
2154
|
+
RangeError,
|
|
2155
|
+
ReferenceError,
|
|
2156
|
+
SyntaxError,
|
|
2157
|
+
TypeError,
|
|
2158
|
+
URIError,
|
|
2159
|
+
AggregateError,
|
|
2160
|
+
AuthenticationError,
|
|
2161
|
+
/*
|
|
2162
|
+
Note: Not widely supported
|
|
2163
|
+
> InternalError,
|
|
2164
|
+
> ModuleError,
|
|
2165
|
+
> HeapError,
|
|
2166
|
+
> WebAssemblyCompileError,
|
|
2167
|
+
> WebAssemblyRuntimeError,
|
|
2168
|
+
*/
|
|
2169
|
+
};
|
|
2170
|
+
/**
|
|
2171
|
+
* Index of all errors
|
|
2172
|
+
*
|
|
2173
|
+
* @private for internal usage
|
|
2174
|
+
*/
|
|
2175
|
+
const ALL_ERRORS = {
|
|
2176
|
+
...PROMPTBOOK_ERRORS,
|
|
2177
|
+
...COMMON_JAVASCRIPT_ERRORS,
|
|
2178
|
+
};
|
|
2179
|
+
/**
|
|
2180
|
+
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
2181
|
+
*/
|
|
2182
|
+
|
|
2183
|
+
/**
|
|
2184
|
+
* Deserializes the error object
|
|
2185
|
+
*
|
|
2186
|
+
* @public exported from `@promptbook/utils`
|
|
2187
|
+
*/
|
|
2188
|
+
function deserializeError(error) {
|
|
2189
|
+
const { name, stack, id } = error; // Added id
|
|
2190
|
+
let { message } = error;
|
|
2191
|
+
let ErrorClass = ALL_ERRORS[error.name];
|
|
2192
|
+
if (ErrorClass === undefined) {
|
|
2193
|
+
ErrorClass = Error;
|
|
2194
|
+
message = `${name}: ${message}`;
|
|
2195
|
+
}
|
|
2196
|
+
if (stack !== undefined && stack !== '') {
|
|
2197
|
+
message = spaceTrim((block) => `
|
|
2198
|
+
${block(message)}
|
|
2199
|
+
|
|
2200
|
+
Original stack trace:
|
|
2201
|
+
${block(stack || '')}
|
|
2202
|
+
`);
|
|
2203
|
+
}
|
|
2204
|
+
const deserializedError = new ErrorClass(message);
|
|
2205
|
+
deserializedError.id = id; // Assign id to the error object
|
|
2206
|
+
return deserializedError;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
/**
|
|
2210
|
+
* Creates a connection to the remote proxy server.
|
|
2211
|
+
*
|
|
2212
|
+
* Note: This function creates a connection to the remote server and returns a socket but responsibility of closing the connection is on the caller
|
|
2213
|
+
*
|
|
2214
|
+
* @private internal utility function
|
|
2215
|
+
*/
|
|
2216
|
+
async function createRemoteClient(options) {
|
|
2217
|
+
const { remoteServerUrl } = options;
|
|
2218
|
+
let path = new URL(remoteServerUrl).pathname;
|
|
2219
|
+
if (path.endsWith('/')) {
|
|
2220
|
+
path = path.slice(0, -1);
|
|
2221
|
+
}
|
|
2222
|
+
path = `${path}/socket.io`;
|
|
2223
|
+
return new Promise((resolve, reject) => {
|
|
2224
|
+
const socket = io(remoteServerUrl, {
|
|
2225
|
+
retries: CONNECTION_RETRIES_LIMIT,
|
|
2226
|
+
timeout: CONNECTION_TIMEOUT_MS,
|
|
2227
|
+
path,
|
|
2228
|
+
transports: [/*'websocket', <- TODO: [๐ฌ] Make websocket transport work */ 'polling'],
|
|
2229
|
+
});
|
|
2230
|
+
// console.log('Connecting to', this.options.remoteServerUrl.href, { socket });
|
|
2231
|
+
socket.on('connect', () => {
|
|
2232
|
+
resolve(socket);
|
|
2233
|
+
});
|
|
2234
|
+
// TODO: [๐ฉ] Better timeout handling
|
|
2235
|
+
setTimeout(() => {
|
|
2236
|
+
reject(new Error(`Timeout while connecting to ${remoteServerUrl}`));
|
|
2237
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
/**
|
|
2242
|
+
* Remote server is a proxy server that uses its execution tools internally and exposes the executor interface externally.
|
|
2243
|
+
*
|
|
2244
|
+
* You can simply use `RemoteExecutionTools` on client-side javascript and connect to your remote server.
|
|
2245
|
+
* This is useful to make all logic on browser side but not expose your API keys or no need to use customer's GPU.
|
|
2246
|
+
*
|
|
2247
|
+
* @see https://github.com/webgptorg/promptbook#remote-server
|
|
2248
|
+
* @public exported from `@promptbook/remote-client`
|
|
2249
|
+
*/
|
|
2250
|
+
class RemoteLlmExecutionTools {
|
|
2251
|
+
/* <- TODO: [๐] `, Destroyable` */
|
|
2252
|
+
constructor(options) {
|
|
2253
|
+
this.options = options;
|
|
2254
|
+
}
|
|
2255
|
+
get title() {
|
|
2256
|
+
// TODO: [๐ง ] Maybe fetch title+description from the remote server (as well as if model methods are defined)
|
|
2257
|
+
return 'Remote server';
|
|
2258
|
+
}
|
|
2259
|
+
get description() {
|
|
2260
|
+
return 'Use all models by your remote server';
|
|
1543
2261
|
}
|
|
1544
2262
|
/**
|
|
1545
|
-
*
|
|
2263
|
+
* Check the configuration of all execution tools
|
|
1546
2264
|
*/
|
|
1547
|
-
|
|
1548
|
-
|
|
2265
|
+
async checkConfiguration() {
|
|
2266
|
+
const socket = await createRemoteClient(this.options);
|
|
2267
|
+
socket.disconnect();
|
|
2268
|
+
// TODO: [main] !!3 Check version of the remote server and compatibility
|
|
2269
|
+
// TODO: [๐] Send checkConfiguration
|
|
1549
2270
|
}
|
|
1550
2271
|
/**
|
|
1551
|
-
*
|
|
2272
|
+
* List all available models that can be used
|
|
1552
2273
|
*/
|
|
1553
|
-
|
|
1554
|
-
|
|
2274
|
+
async listModels() {
|
|
2275
|
+
// TODO: [๐] Listing models (and checking configuration) probbably should go through REST API not Socket.io
|
|
2276
|
+
const socket = await createRemoteClient(this.options);
|
|
2277
|
+
socket.emit('listModels-request', {
|
|
2278
|
+
identification: this.options.identification,
|
|
2279
|
+
} /* <- Note: [๐ค] */);
|
|
2280
|
+
const promptResult = await new Promise((resolve, reject) => {
|
|
2281
|
+
socket.on('listModels-response', (response) => {
|
|
2282
|
+
resolve(response.models);
|
|
2283
|
+
socket.disconnect();
|
|
2284
|
+
});
|
|
2285
|
+
socket.on('error', (error) => {
|
|
2286
|
+
reject(deserializeError(error));
|
|
2287
|
+
socket.disconnect();
|
|
2288
|
+
});
|
|
2289
|
+
});
|
|
2290
|
+
socket.disconnect();
|
|
2291
|
+
return promptResult;
|
|
1555
2292
|
}
|
|
1556
2293
|
/**
|
|
1557
|
-
*
|
|
2294
|
+
* Calls remote proxy server to use a chat model
|
|
1558
2295
|
*/
|
|
1559
|
-
|
|
1560
|
-
|
|
2296
|
+
callChatModel(prompt) {
|
|
2297
|
+
if (this.options.isVerbose) {
|
|
2298
|
+
console.info(`๐ Remote callChatModel call`);
|
|
2299
|
+
}
|
|
2300
|
+
return /* not await */ this.callCommonModel(prompt);
|
|
1561
2301
|
}
|
|
1562
2302
|
/**
|
|
1563
|
-
*
|
|
2303
|
+
* Calls remote proxy server to use a completion model
|
|
1564
2304
|
*/
|
|
1565
|
-
|
|
1566
|
-
|
|
2305
|
+
callCompletionModel(prompt) {
|
|
2306
|
+
if (this.options.isVerbose) {
|
|
2307
|
+
console.info(`๐ฌ Remote callCompletionModel call`);
|
|
2308
|
+
}
|
|
2309
|
+
return /* not await */ this.callCommonModel(prompt);
|
|
1567
2310
|
}
|
|
1568
2311
|
/**
|
|
1569
|
-
*
|
|
2312
|
+
* Calls remote proxy server to use a embedding model
|
|
1570
2313
|
*/
|
|
1571
|
-
|
|
1572
|
-
this.
|
|
2314
|
+
callEmbeddingModel(prompt) {
|
|
2315
|
+
if (this.options.isVerbose) {
|
|
2316
|
+
console.info(`๐ฌ Remote callEmbeddingModel call`);
|
|
2317
|
+
}
|
|
2318
|
+
return /* not await */ this.callCommonModel(prompt);
|
|
1573
2319
|
}
|
|
2320
|
+
// <- Note: [๐ค] callXxxModel
|
|
1574
2321
|
/**
|
|
1575
|
-
*
|
|
2322
|
+
* Calls remote proxy server to use both completion or chat model
|
|
1576
2323
|
*/
|
|
1577
|
-
|
|
1578
|
-
|
|
2324
|
+
async callCommonModel(prompt) {
|
|
2325
|
+
const socket = await createRemoteClient(this.options);
|
|
2326
|
+
socket.emit('prompt-request', {
|
|
2327
|
+
identification: this.options.identification,
|
|
2328
|
+
prompt,
|
|
2329
|
+
} /* <- Note: [๐ค] */);
|
|
2330
|
+
const promptResult = await new Promise((resolve, reject) => {
|
|
2331
|
+
socket.on('prompt-response', (response) => {
|
|
2332
|
+
resolve(response.promptResult);
|
|
2333
|
+
socket.disconnect();
|
|
2334
|
+
});
|
|
2335
|
+
socket.on('error', (error) => {
|
|
2336
|
+
reject(deserializeError(error));
|
|
2337
|
+
socket.disconnect();
|
|
2338
|
+
});
|
|
2339
|
+
});
|
|
2340
|
+
socket.disconnect();
|
|
2341
|
+
return promptResult;
|
|
1579
2342
|
}
|
|
1580
2343
|
}
|
|
2344
|
+
/**
|
|
2345
|
+
* TODO: Maybe use `$exportJson`
|
|
2346
|
+
* TODO: [๐ง ][๐] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`
|
|
2347
|
+
* TODO: [๐] Allow to list compatible models with each variant
|
|
2348
|
+
* TODO: [๐ฏ] RemoteLlmExecutionTools should extend Destroyable and implement IDestroyable
|
|
2349
|
+
* TODO: [๐ง ][๐ฐ] Allow to pass `title` for tracking purposes
|
|
2350
|
+
* TODO: [๐ง ] Maybe remove `@promptbook/remote-client` and just use `@promptbook/core`
|
|
2351
|
+
*/
|
|
1581
2352
|
|
|
1582
2353
|
/**
|
|
1583
2354
|
* Simple wrapper `new Date().toISOString()`
|
|
@@ -1863,339 +2634,25 @@ function countUsage(llmTools) {
|
|
|
1863
2634
|
* TODO: [๐ง ][๐ฏ] Maybe a way how to hide ability to `get totalUsage`
|
|
1864
2635
|
* > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
|
|
1865
2636
|
* TODO: [๐ทโโ๏ธ] @@@ Manual about construction of llmTools
|
|
1866
|
-
*/
|
|
1867
|
-
|
|
1868
|
-
/**
|
|
1869
|
-
* This error type indicates that some part of the code is not implemented yet
|
|
1870
|
-
*
|
|
1871
|
-
* @public exported from `@promptbook/core`
|
|
1872
|
-
*/
|
|
1873
|
-
class NotYetImplementedError extends Error {
|
|
1874
|
-
constructor(message) {
|
|
1875
|
-
super(spaceTrim$1((block) => `
|
|
1876
|
-
${block(message)}
|
|
1877
|
-
|
|
1878
|
-
Note: This feature is not implemented yet but it will be soon.
|
|
1879
|
-
|
|
1880
|
-
If you want speed up the implementation or just read more, look here:
|
|
1881
|
-
https://github.com/webgptorg/promptbook
|
|
1882
|
-
|
|
1883
|
-
Or contact us on pavol@ptbk.io
|
|
1884
|
-
|
|
1885
|
-
`));
|
|
1886
|
-
this.name = 'NotYetImplementedError';
|
|
1887
|
-
Object.setPrototypeOf(this, NotYetImplementedError.prototype);
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
/**
|
|
1892
|
-
* @@@
|
|
1893
|
-
*
|
|
1894
|
-
* Note: `$` is used to indicate that this function is not a pure function - it access global scope
|
|
1895
|
-
*
|
|
1896
|
-
* @private internal function of `$Register`
|
|
1897
|
-
*/
|
|
1898
|
-
function $getGlobalScope() {
|
|
1899
|
-
return Function('return this')();
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
/**
|
|
1903
|
-
* @@@
|
|
1904
|
-
*
|
|
1905
|
-
* @param text @@@
|
|
1906
|
-
* @returns @@@
|
|
1907
|
-
* @example 'HELLO_WORLD'
|
|
1908
|
-
* @example 'I_LOVE_PROMPTBOOK'
|
|
1909
|
-
* @public exported from `@promptbook/utils`
|
|
1910
|
-
*/
|
|
1911
|
-
function normalizeTo_SCREAMING_CASE(text) {
|
|
1912
|
-
let charType;
|
|
1913
|
-
let lastCharType = 'OTHER';
|
|
1914
|
-
let normalizedName = '';
|
|
1915
|
-
for (const char of text) {
|
|
1916
|
-
let normalizedChar;
|
|
1917
|
-
if (/^[a-z]$/.test(char)) {
|
|
1918
|
-
charType = 'LOWERCASE';
|
|
1919
|
-
normalizedChar = char.toUpperCase();
|
|
1920
|
-
}
|
|
1921
|
-
else if (/^[A-Z]$/.test(char)) {
|
|
1922
|
-
charType = 'UPPERCASE';
|
|
1923
|
-
normalizedChar = char;
|
|
1924
|
-
}
|
|
1925
|
-
else if (/^[0-9]$/.test(char)) {
|
|
1926
|
-
charType = 'NUMBER';
|
|
1927
|
-
normalizedChar = char;
|
|
1928
|
-
}
|
|
1929
|
-
else {
|
|
1930
|
-
charType = 'OTHER';
|
|
1931
|
-
normalizedChar = '_';
|
|
1932
|
-
}
|
|
1933
|
-
if (charType !== lastCharType &&
|
|
1934
|
-
!(lastCharType === 'UPPERCASE' && charType === 'LOWERCASE') &&
|
|
1935
|
-
!(lastCharType === 'NUMBER') &&
|
|
1936
|
-
!(charType === 'NUMBER')) {
|
|
1937
|
-
normalizedName += '_';
|
|
1938
|
-
}
|
|
1939
|
-
normalizedName += normalizedChar;
|
|
1940
|
-
lastCharType = charType;
|
|
1941
|
-
}
|
|
1942
|
-
normalizedName = normalizedName.replace(/_+/g, '_');
|
|
1943
|
-
normalizedName = normalizedName.replace(/_?\/_?/g, '/');
|
|
1944
|
-
normalizedName = normalizedName.replace(/^_/, '');
|
|
1945
|
-
normalizedName = normalizedName.replace(/_$/, '');
|
|
1946
|
-
return normalizedName;
|
|
1947
|
-
}
|
|
1948
|
-
/**
|
|
1949
|
-
* TODO: Tests
|
|
1950
|
-
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: 'Moje tabule' })).toEqual('/VtG7sR9rRJqwNEdM2/Moje tabule');
|
|
1951
|
-
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: 'ฤลกฤลลพลพรฝรกรญรบลฏ' })).toEqual('/VtG7sR9rRJqwNEdM2/escrzyaieuu');
|
|
1952
|
-
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: ' ahoj ' })).toEqual('/VtG7sR9rRJqwNEdM2/ahoj');
|
|
1953
|
-
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: ' ahoj_ahojAhoj ahoj ' })).toEqual('/VtG7sR9rRJqwNEdM2/ahoj-ahoj-ahoj-ahoj');
|
|
1954
|
-
* TODO: [๐บ] Use some intermediate util splitWords
|
|
1955
|
-
*/
|
|
1956
|
-
|
|
1957
|
-
/**
|
|
1958
|
-
* @@@
|
|
1959
|
-
*
|
|
1960
|
-
* @param text @@@
|
|
1961
|
-
* @returns @@@
|
|
1962
|
-
* @example 'hello_world'
|
|
1963
|
-
* @example 'i_love_promptbook'
|
|
1964
|
-
* @public exported from `@promptbook/utils`
|
|
1965
|
-
*/
|
|
1966
|
-
function normalizeTo_snake_case(text) {
|
|
1967
|
-
return normalizeTo_SCREAMING_CASE(text).toLowerCase();
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
/**
|
|
1971
|
-
* Register is @@@
|
|
1972
|
-
*
|
|
1973
|
-
* Note: `$` is used to indicate that this function is not a pure function - it accesses and adds variables in global scope.
|
|
1974
|
-
*
|
|
1975
|
-
* @private internal utility, exported are only signleton instances of this class
|
|
1976
|
-
*/
|
|
1977
|
-
class $Register {
|
|
1978
|
-
constructor(registerName) {
|
|
1979
|
-
this.registerName = registerName;
|
|
1980
|
-
const storageName = `_promptbook_${normalizeTo_snake_case(registerName)}`;
|
|
1981
|
-
const globalScope = $getGlobalScope();
|
|
1982
|
-
if (globalScope[storageName] === undefined) {
|
|
1983
|
-
globalScope[storageName] = [];
|
|
1984
|
-
}
|
|
1985
|
-
else if (!Array.isArray(globalScope[storageName])) {
|
|
1986
|
-
throw new UnexpectedError(`Expected (global) ${storageName} to be an array, but got ${typeof globalScope[storageName]}`);
|
|
1987
|
-
}
|
|
1988
|
-
this.storage = globalScope[storageName];
|
|
1989
|
-
}
|
|
1990
|
-
list() {
|
|
1991
|
-
// <- TODO: ReadonlyDeep<ReadonlyArray<TRegistered>>
|
|
1992
|
-
return this.storage;
|
|
1993
|
-
}
|
|
1994
|
-
register(registered) {
|
|
1995
|
-
const { packageName, className } = registered;
|
|
1996
|
-
const existingRegistrationIndex = this.storage.findIndex((item) => item.packageName === packageName && item.className === className);
|
|
1997
|
-
const existingRegistration = this.storage[existingRegistrationIndex];
|
|
1998
|
-
if (!existingRegistration) {
|
|
1999
|
-
this.storage.push(registered);
|
|
2000
|
-
}
|
|
2001
|
-
else {
|
|
2002
|
-
this.storage[existingRegistrationIndex] = registered;
|
|
2003
|
-
}
|
|
2004
|
-
return {
|
|
2005
|
-
registerName: this.registerName,
|
|
2006
|
-
packageName,
|
|
2007
|
-
className,
|
|
2008
|
-
get isDestroyed() {
|
|
2009
|
-
return false;
|
|
2010
|
-
},
|
|
2011
|
-
destroy() {
|
|
2012
|
-
throw new NotYetImplementedError(`Registration to ${this.registerName} is permanent in this version of Promptbook`);
|
|
2013
|
-
},
|
|
2014
|
-
};
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
|
|
2018
|
-
/**
|
|
2019
|
-
* @@@
|
|
2020
|
-
*
|
|
2021
|
-
* Note: `$` is used to indicate that this interacts with the global scope
|
|
2022
|
-
* @singleton Only one instance of each register is created per build, but thare can be more @@@
|
|
2023
|
-
* @public exported from `@promptbook/core`
|
|
2024
|
-
*/
|
|
2025
|
-
const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
|
|
2026
|
-
/**
|
|
2027
|
-
* TODO: [ยฎ] DRY Register logic
|
|
2028
|
-
*/
|
|
2029
|
-
|
|
2030
|
-
/**
|
|
2031
|
-
* Determines if the given path is a root path.
|
|
2032
|
-
*
|
|
2033
|
-
* Note: This does not check if the file exists only if the path is valid
|
|
2034
|
-
* @public exported from `@promptbook/utils`
|
|
2035
|
-
*/
|
|
2036
|
-
function isRootPath(value) {
|
|
2037
|
-
if (value === '/') {
|
|
2038
|
-
return true;
|
|
2039
|
-
}
|
|
2040
|
-
if (/^[A-Z]:\\$/i.test(value)) {
|
|
2041
|
-
return true;
|
|
2042
|
-
}
|
|
2043
|
-
return false;
|
|
2044
|
-
}
|
|
2045
|
-
/**
|
|
2046
|
-
* TODO: [๐] Make for MacOS paths
|
|
2047
|
-
*/
|
|
2048
|
-
|
|
2049
|
-
/**
|
|
2050
|
-
* @@@
|
|
2051
|
-
*
|
|
2052
|
-
* Note: `$` is used to indicate that this interacts with the global scope
|
|
2053
|
-
* @singleton Only one instance of each register is created per build, but thare can be more @@@
|
|
2054
|
-
* @public exported from `@promptbook/core`
|
|
2055
|
-
*/
|
|
2056
|
-
const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
|
|
2057
|
-
/**
|
|
2058
|
-
* TODO: [ยฎ] DRY Register logic
|
|
2059
|
-
*/
|
|
2060
|
-
|
|
2061
|
-
/**
|
|
2062
|
-
* Path to the `.env` file which was used to configure LLM tools
|
|
2063
|
-
*
|
|
2064
|
-
* Note: `$` is used to indicate that this variable is changed by side effect in `$provideLlmToolsConfigurationFromEnv` through `$setUsedEnvFilename`
|
|
2065
|
-
*/
|
|
2066
|
-
let $usedEnvFilename = null;
|
|
2067
|
-
/**
|
|
2068
|
-
* Pass the `.env` file which was used to configure LLM tools
|
|
2069
|
-
*
|
|
2070
|
-
* Note: `$` is used to indicate that this variable is making side effect
|
|
2071
|
-
*
|
|
2072
|
-
* @private internal log of `$provideLlmToolsConfigurationFromEnv` and `$registeredLlmToolsMessage`
|
|
2073
|
-
*/
|
|
2074
|
-
function $setUsedEnvFilename(filepath) {
|
|
2075
|
-
$usedEnvFilename = filepath;
|
|
2076
|
-
}
|
|
2637
|
+
*/
|
|
2638
|
+
|
|
2077
2639
|
/**
|
|
2078
|
-
*
|
|
2079
|
-
*
|
|
2080
|
-
* Note: This function is used to create a (error) message when there is no constructor for some LLM provider
|
|
2640
|
+
* Determines if the given path is a root path.
|
|
2081
2641
|
*
|
|
2082
|
-
*
|
|
2642
|
+
* Note: This does not check if the file exists only if the path is valid
|
|
2643
|
+
* @public exported from `@promptbook/utils`
|
|
2083
2644
|
*/
|
|
2084
|
-
function
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
env = process.env;
|
|
2088
|
-
// <- TODO: [โ] Some DRY way how to get to `process.env` and pass it into functions - ACRY search for `env`
|
|
2089
|
-
}
|
|
2090
|
-
else {
|
|
2091
|
-
env = {};
|
|
2092
|
-
}
|
|
2093
|
-
/**
|
|
2094
|
-
* Mixes registered LLM tools from $llmToolsMetadataRegister and $llmToolsRegister
|
|
2095
|
-
*/
|
|
2096
|
-
const all = [];
|
|
2097
|
-
for (const { title, packageName, className, envVariables } of $llmToolsMetadataRegister.list()) {
|
|
2098
|
-
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
2099
|
-
continue;
|
|
2100
|
-
}
|
|
2101
|
-
all.push({ title, packageName, className, envVariables });
|
|
2102
|
-
}
|
|
2103
|
-
for (const { packageName, className } of $llmToolsRegister.list()) {
|
|
2104
|
-
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
2105
|
-
continue;
|
|
2106
|
-
}
|
|
2107
|
-
all.push({ packageName, className });
|
|
2645
|
+
function isRootPath(value) {
|
|
2646
|
+
if (value === '/') {
|
|
2647
|
+
return true;
|
|
2108
2648
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
const isMetadataAviailable = $llmToolsMetadataRegister
|
|
2112
|
-
.list()
|
|
2113
|
-
.find(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
|
|
2114
|
-
const isInstalled = $llmToolsRegister
|
|
2115
|
-
.list()
|
|
2116
|
-
.find(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
|
|
2117
|
-
const isFullyConfigured = ((_a = metadata.envVariables) === null || _a === void 0 ? void 0 : _a.every((envVariableName) => env[envVariableName] !== undefined)) || false;
|
|
2118
|
-
const isPartiallyConfigured = ((_b = metadata.envVariables) === null || _b === void 0 ? void 0 : _b.some((envVariableName) => env[envVariableName] !== undefined)) || false;
|
|
2119
|
-
// <- Note: [๐จ]
|
|
2120
|
-
return { ...metadata, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured };
|
|
2121
|
-
});
|
|
2122
|
-
const usedEnvMessage = $usedEnvFilename === null ? `Unknown \`.env\` file` : `Used \`.env\` file:\n${$usedEnvFilename}`;
|
|
2123
|
-
if (metadata.length === 0) {
|
|
2124
|
-
return spaceTrim((block) => `
|
|
2125
|
-
No LLM providers are available.
|
|
2126
|
-
|
|
2127
|
-
${block(usedEnvMessage)}
|
|
2128
|
-
`);
|
|
2649
|
+
if (/^[A-Z]:\\$/i.test(value)) {
|
|
2650
|
+
return true;
|
|
2129
2651
|
}
|
|
2130
|
-
return
|
|
2131
|
-
|
|
2132
|
-
${block(usedEnvMessage)}
|
|
2133
|
-
|
|
2134
|
-
Relevant environment variables:
|
|
2135
|
-
${block(Object.keys(env)
|
|
2136
|
-
.filter((envVariableName) => metadata.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)))
|
|
2137
|
-
.map((envVariableName) => `- \`${envVariableName}\``)
|
|
2138
|
-
.join('\n'))}
|
|
2139
|
-
|
|
2140
|
-
Available LLM providers are:
|
|
2141
|
-
${block(metadata
|
|
2142
|
-
.map(({ title, packageName, className, envVariables, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured, }, i) => {
|
|
2143
|
-
const morePieces = [];
|
|
2144
|
-
if (just(false)) ;
|
|
2145
|
-
else if (!isMetadataAviailable && !isInstalled) {
|
|
2146
|
-
// TODO: [๏ฟฝ][๏ฟฝ] Maybe do allow to do auto-install if package not registered and not found
|
|
2147
|
-
morePieces.push(`Not installed and no metadata, looks like a unexpected behavior`);
|
|
2148
|
-
}
|
|
2149
|
-
else if (isMetadataAviailable && !isInstalled) {
|
|
2150
|
-
// TODO: [๏ฟฝ][๏ฟฝ]
|
|
2151
|
-
morePieces.push(`Not installed`);
|
|
2152
|
-
}
|
|
2153
|
-
else if (!isMetadataAviailable && isInstalled) {
|
|
2154
|
-
morePieces.push(`No metadata but installed, looks like a unexpected behavior`);
|
|
2155
|
-
}
|
|
2156
|
-
else if (isMetadataAviailable && isInstalled) {
|
|
2157
|
-
morePieces.push(`Installed`);
|
|
2158
|
-
}
|
|
2159
|
-
else {
|
|
2160
|
-
morePieces.push(`unknown state, looks like a unexpected behavior`);
|
|
2161
|
-
} /* not else */
|
|
2162
|
-
if (isFullyConfigured) {
|
|
2163
|
-
morePieces.push(`Configured`);
|
|
2164
|
-
}
|
|
2165
|
-
else if (isPartiallyConfigured) {
|
|
2166
|
-
morePieces.push(`Partially confugured, missing ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined).join(' + ')}`);
|
|
2167
|
-
}
|
|
2168
|
-
else {
|
|
2169
|
-
if (envVariables !== null) {
|
|
2170
|
-
morePieces.push(`Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`);
|
|
2171
|
-
}
|
|
2172
|
-
else {
|
|
2173
|
-
morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
let providerMessage = spaceTrim(`
|
|
2177
|
-
${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
2178
|
-
${morePieces.join('; ')}
|
|
2179
|
-
`);
|
|
2180
|
-
if ($isRunningInNode) {
|
|
2181
|
-
if (isInstalled && isFullyConfigured) {
|
|
2182
|
-
providerMessage = colors.green(providerMessage);
|
|
2183
|
-
}
|
|
2184
|
-
else if (isInstalled && isPartiallyConfigured) {
|
|
2185
|
-
providerMessage = colors.yellow(providerMessage);
|
|
2186
|
-
}
|
|
2187
|
-
else {
|
|
2188
|
-
providerMessage = colors.gray(providerMessage);
|
|
2189
|
-
}
|
|
2190
|
-
}
|
|
2191
|
-
return providerMessage;
|
|
2192
|
-
})
|
|
2193
|
-
.join('\n'))}
|
|
2194
|
-
`);
|
|
2652
|
+
return false;
|
|
2195
2653
|
}
|
|
2196
2654
|
/**
|
|
2197
|
-
* TODO: [
|
|
2198
|
-
* TODO: [๐ง ][โ] Maybe pass env as argument
|
|
2655
|
+
* TODO: [๐] Make for MacOS paths
|
|
2199
2656
|
*/
|
|
2200
2657
|
|
|
2201
2658
|
/**
|
|
@@ -2558,10 +3015,33 @@ async function $provideLlmToolsForWizzardOrCli(options) {
|
|
|
2558
3015
|
if (!$isRunningInNode()) {
|
|
2559
3016
|
throw new EnvironmentMismatchError('Function `$provideLlmToolsForWizzardOrCli` works only in Node.js environment');
|
|
2560
3017
|
}
|
|
2561
|
-
|
|
3018
|
+
options = options !== null && options !== void 0 ? options : { strategy: 'BRING_YOUR_OWN_KEYS' };
|
|
3019
|
+
const { strategy, isCacheReloaded } = options;
|
|
3020
|
+
let llmExecutionTools;
|
|
3021
|
+
if (strategy === 'REMOTE_SERVER') {
|
|
3022
|
+
const { remoteServerUrl = DEFAULT_REMOTE_SERVER_URL, loginPrompt } = options;
|
|
3023
|
+
const storage = new MemoryStorage(); // <- TODO: !!!!!! Save to `.promptbook` folder
|
|
3024
|
+
const key = `${remoteServerUrl}-identification`;
|
|
3025
|
+
let identification = await storage.getItem(key);
|
|
3026
|
+
if (identification === null) {
|
|
3027
|
+
identification = await loginPrompt();
|
|
3028
|
+
await storage.setItem(key, identification);
|
|
3029
|
+
}
|
|
3030
|
+
llmExecutionTools = new RemoteLlmExecutionTools({
|
|
3031
|
+
remoteServerUrl,
|
|
3032
|
+
identification,
|
|
3033
|
+
});
|
|
3034
|
+
}
|
|
3035
|
+
else if (strategy === 'BRING_YOUR_OWN_KEYS') {
|
|
3036
|
+
llmExecutionTools = await $provideLlmToolsFromEnv();
|
|
3037
|
+
}
|
|
3038
|
+
else {
|
|
3039
|
+
throw new UnexpectedError(`\`$provideLlmToolsForWizzardOrCli\` wrong strategy "${strategy}"`);
|
|
3040
|
+
}
|
|
2562
3041
|
return cacheLlmTools(countUsage(
|
|
3042
|
+
// <- TODO: [๐ฏ] We dont use countUsage at all, maybe just unwrap it
|
|
2563
3043
|
// <- Note: for example here we don`t want the [๐ฏ]
|
|
2564
|
-
|
|
3044
|
+
llmExecutionTools), {
|
|
2565
3045
|
storage: new FileCacheStorage({ fs: $provideFilesystemForNode() }, {
|
|
2566
3046
|
rootFolderPath: join(process.cwd(), DEFAULT_EXECUTION_CACHE_DIRNAME),
|
|
2567
3047
|
}),
|
|
@@ -2577,31 +3057,88 @@ async function $provideLlmToolsForWizzardOrCli(options) {
|
|
|
2577
3057
|
*/
|
|
2578
3058
|
|
|
2579
3059
|
/**
|
|
2580
|
-
*
|
|
2581
|
-
* No side effects.
|
|
2582
|
-
*
|
|
2583
|
-
* Note: It can be usefull for:
|
|
2584
|
-
*
|
|
2585
|
-
* 1) Suppressing eager optimization of unused imports
|
|
2586
|
-
* 2) Suppressing eslint errors of unused variables in the tests
|
|
2587
|
-
* 3) Keeping the type of the variable for type testing
|
|
2588
|
-
*
|
|
2589
|
-
* @param value any values
|
|
2590
|
-
* @returns void
|
|
2591
|
-
* @private within the repository
|
|
2592
|
-
*/
|
|
2593
|
-
function keepUnused(...valuesToKeep) {
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
/**
|
|
2597
|
-
* Just says that the variable is not used directlys but should be kept because the existence of the variable is important
|
|
2598
|
-
*
|
|
2599
|
-
* @param value any values
|
|
2600
|
-
* @returns void
|
|
2601
|
-
* @private within the repository
|
|
3060
|
+
* @private utility of CLI
|
|
2602
3061
|
*/
|
|
2603
|
-
function $
|
|
2604
|
-
|
|
3062
|
+
function $provideLlmToolsForCli(options) {
|
|
3063
|
+
const { cliOptions: {
|
|
3064
|
+
/* TODO: Use verbose: isVerbose, */ interactive: isInteractive, provider, remoteServerUrl: remoteServerUrlRaw, }, } = options;
|
|
3065
|
+
let strategy;
|
|
3066
|
+
if (/^b/i.test(provider)) {
|
|
3067
|
+
strategy = 'BRING_YOUR_OWN_KEYS';
|
|
3068
|
+
}
|
|
3069
|
+
else if (/^r/i.test(provider)) {
|
|
3070
|
+
strategy = 'REMOTE_SERVER';
|
|
3071
|
+
}
|
|
3072
|
+
else {
|
|
3073
|
+
console.log(colors.red(`Unknown provider: "${provider}", please use "BRING_YOUR_OWN_KEYS" or "REMOTE_SERVER"`));
|
|
3074
|
+
process.exit(1);
|
|
3075
|
+
}
|
|
3076
|
+
if (strategy === 'BRING_YOUR_OWN_KEYS') {
|
|
3077
|
+
return /* not await */ $provideLlmToolsForWizzardOrCli({ strategy, ...options });
|
|
3078
|
+
}
|
|
3079
|
+
else if (strategy === 'REMOTE_SERVER') {
|
|
3080
|
+
if (!isValidUrl(remoteServerUrlRaw)) {
|
|
3081
|
+
console.log(colors.red(`Invalid URL of remote server: "${remoteServerUrlRaw}"`));
|
|
3082
|
+
process.exit(1);
|
|
3083
|
+
}
|
|
3084
|
+
const remoteServerUrl = remoteServerUrlRaw.endsWith('/') ? remoteServerUrlRaw.slice(0, -1) : remoteServerUrlRaw;
|
|
3085
|
+
return /* not await */ $provideLlmToolsForWizzardOrCli({
|
|
3086
|
+
strategy,
|
|
3087
|
+
appId: CLI_APP_ID,
|
|
3088
|
+
remoteServerUrl,
|
|
3089
|
+
...options,
|
|
3090
|
+
async loginPrompt() {
|
|
3091
|
+
if (!isInteractive) {
|
|
3092
|
+
console.log(colors.red(`You can not login to remote server in non-interactive mode`));
|
|
3093
|
+
process.exit(1);
|
|
3094
|
+
}
|
|
3095
|
+
const { username, password } = await prompts([
|
|
3096
|
+
{
|
|
3097
|
+
type: 'text',
|
|
3098
|
+
name: 'username',
|
|
3099
|
+
message: 'Enter your email:',
|
|
3100
|
+
validate: (value) => (isValidEmail(value) ? true : 'Valid email is required'),
|
|
3101
|
+
},
|
|
3102
|
+
{
|
|
3103
|
+
type: 'password',
|
|
3104
|
+
name: 'password',
|
|
3105
|
+
message: 'Enter your password:',
|
|
3106
|
+
validate: (value) => value.length /* <- TODO: [๐ง ] Better password validation */ > 0
|
|
3107
|
+
? true
|
|
3108
|
+
: 'Password is required',
|
|
3109
|
+
},
|
|
3110
|
+
]);
|
|
3111
|
+
const loginUrl = `${remoteServerUrl}/login`;
|
|
3112
|
+
const response = await fetch(loginUrl, {
|
|
3113
|
+
method: 'POST',
|
|
3114
|
+
headers: {
|
|
3115
|
+
'Content-Type': 'application/json',
|
|
3116
|
+
},
|
|
3117
|
+
body: JSON.stringify({
|
|
3118
|
+
appId: CLI_APP_ID,
|
|
3119
|
+
username,
|
|
3120
|
+
password,
|
|
3121
|
+
}),
|
|
3122
|
+
});
|
|
3123
|
+
console.log('!!!', {
|
|
3124
|
+
loginUrl,
|
|
3125
|
+
username,
|
|
3126
|
+
password,
|
|
3127
|
+
// type: response.type,
|
|
3128
|
+
// text: await response.text(),
|
|
3129
|
+
});
|
|
3130
|
+
const body = (await response.json());
|
|
3131
|
+
if ('error' in body) {
|
|
3132
|
+
console.log(colors.red(body.error.message));
|
|
3133
|
+
process.exit(1);
|
|
3134
|
+
}
|
|
3135
|
+
return body.identification;
|
|
3136
|
+
},
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
else {
|
|
3140
|
+
throw new UnexpectedError(`\`$provideLlmToolsForCli\` wrong strategy "${strategy}"`);
|
|
3141
|
+
}
|
|
2605
3142
|
}
|
|
2606
3143
|
|
|
2607
3144
|
/**
|
|
@@ -2618,8 +3155,10 @@ function $initializeListModelsCommand(program) {
|
|
|
2618
3155
|
`));
|
|
2619
3156
|
listModelsCommand.alias('models');
|
|
2620
3157
|
listModelsCommand.alias('llm');
|
|
2621
|
-
listModelsCommand.action(handleActionErrors(async () => {
|
|
2622
|
-
|
|
3158
|
+
listModelsCommand.action(handleActionErrors(async (cliOptions) => {
|
|
3159
|
+
console.log('!!!', cliOptions);
|
|
3160
|
+
// TODO: !!!!!! Not relevant for remote server and also for `about` command
|
|
3161
|
+
const llm = await $provideLlmToolsForCli({ cliOptions });
|
|
2623
3162
|
$sideEffect(llm);
|
|
2624
3163
|
// <- Note: Providing LLM tools will make a side effect of registering all available LLM tools to show the message
|
|
2625
3164
|
console.info($registeredLlmToolsMessage());
|
|
@@ -3179,49 +3718,73 @@ function $initializeListScrapersCommand(program) {
|
|
|
3179
3718
|
*/
|
|
3180
3719
|
|
|
3181
3720
|
/**
|
|
3182
|
-
*
|
|
3721
|
+
* Initializes `login` command for Promptbook CLI utilities
|
|
3183
3722
|
*
|
|
3184
|
-
* Note:
|
|
3723
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
3185
3724
|
*
|
|
3186
|
-
* @
|
|
3187
|
-
*/
|
|
3188
|
-
async function collectionToJson(collection) {
|
|
3189
|
-
const pipelineUrls = await collection.listPipelines();
|
|
3190
|
-
const promptbooks = await Promise.all(pipelineUrls.map((url) => collection.getPipelineByUrl(url)));
|
|
3191
|
-
return promptbooks;
|
|
3192
|
-
}
|
|
3193
|
-
/**
|
|
3194
|
-
* TODO: [๐ง ] Maybe clear `sourceFile` or clear when exposing through API or remote server
|
|
3725
|
+
* @private internal function of `promptbookCli`
|
|
3195
3726
|
*/
|
|
3727
|
+
function $initializeLoginCommand(program) {
|
|
3728
|
+
const loginCommand = program.command('login');
|
|
3729
|
+
loginCommand.description(spaceTrim(`
|
|
3730
|
+
Login to the remote Promptbook server
|
|
3731
|
+
`));
|
|
3732
|
+
loginCommand.action(handleActionErrors(async () => {
|
|
3733
|
+
// @@@
|
|
3734
|
+
console.error(colors.green(spaceTrim(`
|
|
3735
|
+
You will be logged in to https://promptbook.studio server.
|
|
3736
|
+
If you don't have an account, it will be created automatically.
|
|
3737
|
+
`)));
|
|
3738
|
+
// !!!!!!!!! Remove from here and use $provideLlmToolsForCli
|
|
3739
|
+
const { email, password } = await prompts([
|
|
3740
|
+
{
|
|
3741
|
+
type: 'text',
|
|
3742
|
+
name: 'email',
|
|
3743
|
+
message: 'Enter your email:',
|
|
3744
|
+
validate: (value) => (isValidEmail(value) ? true : 'Valid email is required'),
|
|
3745
|
+
},
|
|
3746
|
+
{
|
|
3747
|
+
type: 'password',
|
|
3748
|
+
name: 'password',
|
|
3749
|
+
message: 'Enter your password:',
|
|
3750
|
+
validate: (value) => value.length /* <- TODO: [๐ง ] Better password validation */ > 0 ? true : 'Password is required',
|
|
3751
|
+
},
|
|
3752
|
+
]);
|
|
3753
|
+
TODO_USE(email, password);
|
|
3754
|
+
await forTime(1000);
|
|
3755
|
+
console.error(colors.green(spaceTrim(`
|
|
3756
|
+
Your account ${email} was successfully created.
|
|
3196
3757
|
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
super(message);
|
|
3205
|
-
this.name = 'ParseError';
|
|
3206
|
-
Object.setPrototypeOf(this, ParseError.prototype);
|
|
3207
|
-
}
|
|
3758
|
+
Please verify your email:
|
|
3759
|
+
https://brj.app/api/v1/customer/register-account?apiKey=PRODdh003eNKaec7PoO1AzU244tsL4WO
|
|
3760
|
+
|
|
3761
|
+
After verification, you will receive 500 000 credits for free ๐
|
|
3762
|
+
`)));
|
|
3763
|
+
return process.exit(0);
|
|
3764
|
+
}));
|
|
3208
3765
|
}
|
|
3209
3766
|
/**
|
|
3210
|
-
* TODO:
|
|
3767
|
+
* TODO: Pass remote server URL (and path)
|
|
3768
|
+
* TODO: Implement non-interactive login
|
|
3769
|
+
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
3770
|
+
* Note: [๐ก] Code in this file should never be published outside of `@promptbook/cli`
|
|
3211
3771
|
*/
|
|
3212
3772
|
|
|
3213
3773
|
/**
|
|
3214
|
-
*
|
|
3774
|
+
* Converts PipelineCollection to serialized JSON
|
|
3775
|
+
*
|
|
3776
|
+
* Note: Functions `collectionToJson` and `createCollectionFromJson` are complementary
|
|
3215
3777
|
*
|
|
3216
3778
|
* @public exported from `@promptbook/core`
|
|
3217
3779
|
*/
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
Object.setPrototypeOf(this, PipelineLogicError.prototype);
|
|
3223
|
-
}
|
|
3780
|
+
async function collectionToJson(collection) {
|
|
3781
|
+
const pipelineUrls = await collection.listPipelines();
|
|
3782
|
+
const promptbooks = await Promise.all(pipelineUrls.map((url) => collection.getPipelineByUrl(url)));
|
|
3783
|
+
return promptbooks;
|
|
3224
3784
|
}
|
|
3785
|
+
/**
|
|
3786
|
+
* TODO: [๐ง ] Maybe clear `sourceFile` or clear when exposing through API or remote server
|
|
3787
|
+
*/
|
|
3225
3788
|
|
|
3226
3789
|
/**
|
|
3227
3790
|
* Tests if given string is valid semantic version
|
|
@@ -3619,21 +4182,6 @@ async function loadArchive(filePath, fs) {
|
|
|
3619
4182
|
|
|
3620
4183
|
var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [๐] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [๐] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModelNames",description:"List of available model names separated by comma (,)",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are experienced AI engineer, you need to create virtual assistant.\nWrite\n\n## Example\n\n```json\n{\n\"modelName\": \"gpt-4o\",\n\"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n\"temperature\": 0.7\n}\n```\n\n## Instructions\n\n- Your output format is JSON object\n- Write just the JSON object, no other text should be present\n- It contains the following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nPick from the following models:\n\n- {availableModelNames}\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelRequirements",format:"JSON",dependentParameterNames:["availableModelNames","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModelNames}` List of available model names separated by comma (,)\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are experienced AI engineer, you need to create virtual assistant.\nWrite\n\n## Example\n\n\\`\\`\\`json\n{\n\"modelName\": \"gpt-4o\",\n\"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n\"temperature\": 0.7\n}\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON object\n- Write just the JSON object, no other text should be present\n- It contains the following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nPick from the following models:\n\n- {availableModelNames}\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"โ Convert Knowledge-piece to title\" but \"โ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"โ Convert Knowledge-piece to title\" but \"โ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
|
|
3621
4184
|
|
|
3622
|
-
/**
|
|
3623
|
-
* Checks if value is valid email
|
|
3624
|
-
*
|
|
3625
|
-
* @public exported from `@promptbook/utils`
|
|
3626
|
-
*/
|
|
3627
|
-
function isValidEmail(email) {
|
|
3628
|
-
if (typeof email !== 'string') {
|
|
3629
|
-
return false;
|
|
3630
|
-
}
|
|
3631
|
-
if (email.split('\n').length > 1) {
|
|
3632
|
-
return false;
|
|
3633
|
-
}
|
|
3634
|
-
return /^.+@.+\..+$/.test(email);
|
|
3635
|
-
}
|
|
3636
|
-
|
|
3637
4185
|
/**
|
|
3638
4186
|
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
3639
4187
|
*
|
|
@@ -3867,32 +4415,6 @@ function taskParameterJsonToString(taskParameterJson) {
|
|
|
3867
4415
|
* TODO: [๐ง ] Should be in generated .book.md file GENERATOR_WARNING
|
|
3868
4416
|
*/
|
|
3869
4417
|
|
|
3870
|
-
/**
|
|
3871
|
-
* This error indicates that promptbook not found in the collection
|
|
3872
|
-
*
|
|
3873
|
-
* @public exported from `@promptbook/core`
|
|
3874
|
-
*/
|
|
3875
|
-
class NotFoundError extends Error {
|
|
3876
|
-
constructor(message) {
|
|
3877
|
-
super(message);
|
|
3878
|
-
this.name = 'NotFoundError';
|
|
3879
|
-
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
3882
|
-
|
|
3883
|
-
/**
|
|
3884
|
-
* This error indicates errors in referencing promptbooks between each other
|
|
3885
|
-
*
|
|
3886
|
-
* @public exported from `@promptbook/core`
|
|
3887
|
-
*/
|
|
3888
|
-
class PipelineUrlError extends Error {
|
|
3889
|
-
constructor(message) {
|
|
3890
|
-
super(message);
|
|
3891
|
-
this.name = 'PipelineUrlError';
|
|
3892
|
-
Object.setPrototypeOf(this, PipelineUrlError.prototype);
|
|
3893
|
-
}
|
|
3894
|
-
}
|
|
3895
|
-
|
|
3896
4418
|
/**
|
|
3897
4419
|
* Parses the task and returns the list of all parameter names
|
|
3898
4420
|
*
|
|
@@ -4063,24 +4585,6 @@ function createCollectionFromJson(...promptbooks) {
|
|
|
4063
4585
|
return new SimplePipelineCollection(...promptbooks);
|
|
4064
4586
|
}
|
|
4065
4587
|
|
|
4066
|
-
/**
|
|
4067
|
-
* This error type indicates that some tools are missing for pipeline execution or preparation
|
|
4068
|
-
*
|
|
4069
|
-
* @public exported from `@promptbook/core`
|
|
4070
|
-
*/
|
|
4071
|
-
class MissingToolsError extends Error {
|
|
4072
|
-
constructor(message) {
|
|
4073
|
-
super(spaceTrim$1((block) => `
|
|
4074
|
-
${block(message)}
|
|
4075
|
-
|
|
4076
|
-
Note: You have probbably forgot to provide some tools for pipeline execution or preparation
|
|
4077
|
-
|
|
4078
|
-
`));
|
|
4079
|
-
this.name = 'MissingToolsError';
|
|
4080
|
-
Object.setPrototypeOf(this, MissingToolsError.prototype);
|
|
4081
|
-
}
|
|
4082
|
-
}
|
|
4083
|
-
|
|
4084
4588
|
/**
|
|
4085
4589
|
* Determine if the pipeline is fully prepared
|
|
4086
4590
|
*
|
|
@@ -4113,210 +4617,40 @@ function isPipelinePrepared(pipeline) {
|
|
|
4113
4617
|
* TODO: [๐ ] Maybe base this on `makeValidator`
|
|
4114
4618
|
* TODO: [๐ง] Pipeline can be partially prepared, this should return true ONLY if fully prepared
|
|
4115
4619
|
* TODO: [๐งฟ] Maybe do same process with same granularity and subfinctions as `preparePipeline`
|
|
4116
|
-
* - [๐] ? Is context in each task
|
|
4117
|
-
* - [โจ] Are examples prepared
|
|
4118
|
-
* - [โจ] Are tasks prepared
|
|
4119
|
-
*/
|
|
4120
|
-
|
|
4121
|
-
/**
|
|
4122
|
-
* Recursively converts JSON strings to JSON objects
|
|
4123
|
-
|
|
4124
|
-
* @public exported from `@promptbook/utils`
|
|
4125
|
-
*/
|
|
4126
|
-
function jsonStringsToJsons(object) {
|
|
4127
|
-
if (object === null) {
|
|
4128
|
-
return object;
|
|
4129
|
-
}
|
|
4130
|
-
if (Array.isArray(object)) {
|
|
4131
|
-
return object.map(jsonStringsToJsons);
|
|
4132
|
-
}
|
|
4133
|
-
if (typeof object !== 'object') {
|
|
4134
|
-
return object;
|
|
4135
|
-
}
|
|
4136
|
-
const newObject = { ...object };
|
|
4137
|
-
for (const [key, value] of Object.entries(object)) {
|
|
4138
|
-
if (typeof value === 'string' && isValidJsonString(value)) {
|
|
4139
|
-
newObject[key] = JSON.parse(value);
|
|
4140
|
-
}
|
|
4141
|
-
else {
|
|
4142
|
-
newObject[key] = jsonStringsToJsons(value);
|
|
4143
|
-
}
|
|
4144
|
-
}
|
|
4145
|
-
return newObject;
|
|
4146
|
-
}
|
|
4147
|
-
/**
|
|
4148
|
-
* TODO: Type the return type correctly
|
|
4149
|
-
*/
|
|
4150
|
-
|
|
4151
|
-
/**
|
|
4152
|
-
* This error indicates problems parsing the format value
|
|
4153
|
-
*
|
|
4154
|
-
* For example, when the format value is not a valid JSON or CSV
|
|
4155
|
-
* This is not thrown directly but in extended classes
|
|
4156
|
-
*
|
|
4157
|
-
* @public exported from `@promptbook/core`
|
|
4158
|
-
*/
|
|
4159
|
-
class AbstractFormatError extends Error {
|
|
4160
|
-
// Note: To allow instanceof do not put here error `name`
|
|
4161
|
-
// public readonly name = 'AbstractFormatError';
|
|
4162
|
-
constructor(message) {
|
|
4163
|
-
super(message);
|
|
4164
|
-
Object.setPrototypeOf(this, AbstractFormatError.prototype);
|
|
4165
|
-
}
|
|
4166
|
-
}
|
|
4167
|
-
|
|
4168
|
-
/**
|
|
4169
|
-
* This error indicates problem with parsing of CSV
|
|
4170
|
-
*
|
|
4171
|
-
* @public exported from `@promptbook/core`
|
|
4172
|
-
*/
|
|
4173
|
-
class CsvFormatError extends AbstractFormatError {
|
|
4174
|
-
constructor(message) {
|
|
4175
|
-
super(message);
|
|
4176
|
-
this.name = 'CsvFormatError';
|
|
4177
|
-
Object.setPrototypeOf(this, CsvFormatError.prototype);
|
|
4178
|
-
}
|
|
4179
|
-
}
|
|
4180
|
-
|
|
4181
|
-
/**
|
|
4182
|
-
* This error indicates that the pipeline collection cannot be propperly loaded
|
|
4183
|
-
*
|
|
4184
|
-
* @public exported from `@promptbook/core`
|
|
4185
|
-
*/
|
|
4186
|
-
class CollectionError extends Error {
|
|
4187
|
-
constructor(message) {
|
|
4188
|
-
super(message);
|
|
4189
|
-
this.name = 'CollectionError';
|
|
4190
|
-
Object.setPrototypeOf(this, CollectionError.prototype);
|
|
4191
|
-
}
|
|
4192
|
-
}
|
|
4193
|
-
|
|
4194
|
-
/**
|
|
4195
|
-
* This error occurs when some expectation is not met in the execution of the pipeline
|
|
4196
|
-
*
|
|
4197
|
-
* @public exported from `@promptbook/core`
|
|
4198
|
-
* Note: Do not throw this error, its reserved for `checkExpectations` and `createPipelineExecutor` and public ONLY to be serializable through remote server
|
|
4199
|
-
* Note: Always thrown in `checkExpectations` and catched in `createPipelineExecutor` and rethrown as `PipelineExecutionError`
|
|
4200
|
-
* Note: This is a kindof subtype of PipelineExecutionError
|
|
4201
|
-
*/
|
|
4202
|
-
class ExpectError extends Error {
|
|
4203
|
-
constructor(message) {
|
|
4204
|
-
super(message);
|
|
4205
|
-
this.name = 'ExpectError';
|
|
4206
|
-
Object.setPrototypeOf(this, ExpectError.prototype);
|
|
4207
|
-
}
|
|
4208
|
-
}
|
|
4209
|
-
|
|
4210
|
-
/**
|
|
4211
|
-
* This error indicates that the promptbook can not retrieve knowledge from external sources
|
|
4212
|
-
*
|
|
4213
|
-
* @public exported from `@promptbook/core`
|
|
4214
|
-
*/
|
|
4215
|
-
class KnowledgeScrapeError extends Error {
|
|
4216
|
-
constructor(message) {
|
|
4217
|
-
super(message);
|
|
4218
|
-
this.name = 'KnowledgeScrapeError';
|
|
4219
|
-
Object.setPrototypeOf(this, KnowledgeScrapeError.prototype);
|
|
4220
|
-
}
|
|
4221
|
-
}
|
|
4222
|
-
|
|
4223
|
-
/**
|
|
4224
|
-
* This error type indicates that some limit was reached
|
|
4225
|
-
*
|
|
4226
|
-
* @public exported from `@promptbook/core`
|
|
4227
|
-
*/
|
|
4228
|
-
class LimitReachedError extends Error {
|
|
4229
|
-
constructor(message) {
|
|
4230
|
-
super(message);
|
|
4231
|
-
this.name = 'LimitReachedError';
|
|
4232
|
-
Object.setPrototypeOf(this, LimitReachedError.prototype);
|
|
4233
|
-
}
|
|
4234
|
-
}
|
|
4235
|
-
|
|
4236
|
-
/**
|
|
4237
|
-
* Index of all custom errors
|
|
4238
|
-
*
|
|
4239
|
-
* @public exported from `@promptbook/core`
|
|
4240
|
-
*/
|
|
4241
|
-
const PROMPTBOOK_ERRORS = {
|
|
4242
|
-
AbstractFormatError,
|
|
4243
|
-
CsvFormatError,
|
|
4244
|
-
CollectionError,
|
|
4245
|
-
EnvironmentMismatchError,
|
|
4246
|
-
ExpectError,
|
|
4247
|
-
KnowledgeScrapeError,
|
|
4248
|
-
LimitReachedError,
|
|
4249
|
-
MissingToolsError,
|
|
4250
|
-
NotFoundError,
|
|
4251
|
-
NotYetImplementedError,
|
|
4252
|
-
ParseError,
|
|
4253
|
-
PipelineExecutionError,
|
|
4254
|
-
PipelineLogicError,
|
|
4255
|
-
PipelineUrlError,
|
|
4256
|
-
UnexpectedError,
|
|
4257
|
-
// TODO: [๐ช]> VersionMismatchError,
|
|
4258
|
-
};
|
|
4259
|
-
/**
|
|
4260
|
-
* Index of all javascript errors
|
|
4261
|
-
*
|
|
4262
|
-
* @private for internal usage
|
|
4263
|
-
*/
|
|
4264
|
-
const COMMON_JAVASCRIPT_ERRORS = {
|
|
4265
|
-
Error,
|
|
4266
|
-
EvalError,
|
|
4267
|
-
RangeError,
|
|
4268
|
-
ReferenceError,
|
|
4269
|
-
SyntaxError,
|
|
4270
|
-
TypeError,
|
|
4271
|
-
URIError,
|
|
4272
|
-
AggregateError,
|
|
4273
|
-
/*
|
|
4274
|
-
Note: Not widely supported
|
|
4275
|
-
> InternalError,
|
|
4276
|
-
> ModuleError,
|
|
4277
|
-
> HeapError,
|
|
4278
|
-
> WebAssemblyCompileError,
|
|
4279
|
-
> WebAssemblyRuntimeError,
|
|
4280
|
-
*/
|
|
4281
|
-
};
|
|
4282
|
-
/**
|
|
4283
|
-
* Index of all errors
|
|
4284
|
-
*
|
|
4285
|
-
* @private for internal usage
|
|
4286
|
-
*/
|
|
4287
|
-
const ALL_ERRORS = {
|
|
4288
|
-
...PROMPTBOOK_ERRORS,
|
|
4289
|
-
...COMMON_JAVASCRIPT_ERRORS,
|
|
4290
|
-
};
|
|
4291
|
-
/**
|
|
4292
|
-
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
4293
|
-
*/
|
|
4294
|
-
|
|
4295
|
-
/**
|
|
4296
|
-
* Deserializes the error object
|
|
4297
|
-
*
|
|
4298
|
-
* @public exported from `@promptbook/utils`
|
|
4299
|
-
*/
|
|
4300
|
-
function deserializeError(error) {
|
|
4301
|
-
const { name, stack, id } = error; // Added id
|
|
4302
|
-
let { message } = error;
|
|
4303
|
-
let ErrorClass = ALL_ERRORS[error.name];
|
|
4304
|
-
if (ErrorClass === undefined) {
|
|
4305
|
-
ErrorClass = Error;
|
|
4306
|
-
message = `${name}: ${message}`;
|
|
4307
|
-
}
|
|
4308
|
-
if (stack !== undefined && stack !== '') {
|
|
4309
|
-
message = spaceTrim((block) => `
|
|
4310
|
-
${block(message)}
|
|
4620
|
+
* - [๐] ? Is context in each task
|
|
4621
|
+
* - [โจ] Are examples prepared
|
|
4622
|
+
* - [โจ] Are tasks prepared
|
|
4623
|
+
*/
|
|
4311
4624
|
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4625
|
+
/**
|
|
4626
|
+
* Recursively converts JSON strings to JSON objects
|
|
4627
|
+
|
|
4628
|
+
* @public exported from `@promptbook/utils`
|
|
4629
|
+
*/
|
|
4630
|
+
function jsonStringsToJsons(object) {
|
|
4631
|
+
if (object === null) {
|
|
4632
|
+
return object;
|
|
4315
4633
|
}
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4634
|
+
if (Array.isArray(object)) {
|
|
4635
|
+
return object.map(jsonStringsToJsons);
|
|
4636
|
+
}
|
|
4637
|
+
if (typeof object !== 'object') {
|
|
4638
|
+
return object;
|
|
4639
|
+
}
|
|
4640
|
+
const newObject = { ...object };
|
|
4641
|
+
for (const [key, value] of Object.entries(object)) {
|
|
4642
|
+
if (typeof value === 'string' && isValidJsonString(value)) {
|
|
4643
|
+
newObject[key] = JSON.parse(value);
|
|
4644
|
+
}
|
|
4645
|
+
else {
|
|
4646
|
+
newObject[key] = jsonStringsToJsons(value);
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
return newObject;
|
|
4319
4650
|
}
|
|
4651
|
+
/**
|
|
4652
|
+
* TODO: Type the return type correctly
|
|
4653
|
+
*/
|
|
4320
4654
|
|
|
4321
4655
|
/**
|
|
4322
4656
|
* Asserts that the execution of a Promptbook is successful
|
|
@@ -4469,6 +4803,10 @@ function serializeError(error) {
|
|
|
4469
4803
|
|
|
4470
4804
|
Cannot serialize error with name "${name}"
|
|
4471
4805
|
|
|
4806
|
+
Authors of Promptbook probably forgot to add this error into the list of errors:
|
|
4807
|
+
https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
|
|
4808
|
+
|
|
4809
|
+
|
|
4472
4810
|
${block(stack || message)}
|
|
4473
4811
|
|
|
4474
4812
|
`));
|
|
@@ -11379,7 +11717,6 @@ function $initializeMakeCommand(program) {
|
|
|
11379
11717
|
makeCommand.option('--no-validation', `Do not validate logic of pipelines in collection`, true);
|
|
11380
11718
|
makeCommand.option('--validation', `Types of validations separated by comma (options "logic","imports")`, 'logic,imports');
|
|
11381
11719
|
makeCommand.option('-r, --reload', `Call LLM models even if same prompt with result is in the cache`, false);
|
|
11382
|
-
makeCommand.option('-v, --verbose', `Is output verbose`, false);
|
|
11383
11720
|
makeCommand.option('-o, --output <path>', spaceTrim(`
|
|
11384
11721
|
Where to save the builded collection
|
|
11385
11722
|
|
|
@@ -11393,7 +11730,8 @@ function $initializeMakeCommand(program) {
|
|
|
11393
11730
|
Note: This can be used only with "javascript" or "typescript" format
|
|
11394
11731
|
|
|
11395
11732
|
`), DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME);
|
|
11396
|
-
makeCommand.action(handleActionErrors(async (path,
|
|
11733
|
+
makeCommand.action(handleActionErrors(async (path, cliOptions) => {
|
|
11734
|
+
const { projectName, rootUrl, format, functionName, validation, reload: isCacheReloaded, verbose: isVerbose, output, } = cliOptions;
|
|
11397
11735
|
if (!isValidJavascriptName(functionName)) {
|
|
11398
11736
|
console.error(colors.red(`Function name "${functionName}" is not valid javascript name`));
|
|
11399
11737
|
return process.exit(1);
|
|
@@ -11421,7 +11759,10 @@ function $initializeMakeCommand(program) {
|
|
|
11421
11759
|
isCacheReloaded,
|
|
11422
11760
|
}; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
|
|
11423
11761
|
const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
|
|
11424
|
-
const llm = await $
|
|
11762
|
+
const llm = await $provideLlmToolsForCli({
|
|
11763
|
+
cliOptions,
|
|
11764
|
+
...prepareAndScrapeOptions,
|
|
11765
|
+
});
|
|
11425
11766
|
const executables = await $provideExecutablesForNode(prepareAndScrapeOptions);
|
|
11426
11767
|
const tools = {
|
|
11427
11768
|
llm,
|
|
@@ -11689,8 +12030,8 @@ function $initializePrettifyCommand(program) {
|
|
|
11689
12030
|
// <- TODO: [๐งโโ๏ธ] Unite path to promptbook collection argument
|
|
11690
12031
|
'Pipelines to prettify as glob pattern');
|
|
11691
12032
|
prettifyCommand.option('-i, --ignore <glob>', `Ignore as glob pattern`);
|
|
11692
|
-
prettifyCommand.
|
|
11693
|
-
|
|
12033
|
+
prettifyCommand.action(handleActionErrors(async (filesGlob, cliOptions) => {
|
|
12034
|
+
const { ignore, verbose: isVerbose } = cliOptions;
|
|
11694
12035
|
const filenames = await glob(filesGlob, { ignore });
|
|
11695
12036
|
// <- TODO: [๐ถ]
|
|
11696
12037
|
for (const filename of filenames) {
|
|
@@ -12291,13 +12632,12 @@ function $initializeRunCommand(program) {
|
|
|
12291
12632
|
// TODO: [๐ง
] DRY command arguments
|
|
12292
12633
|
runCommand.argument('[pipelineSource]', 'Path to book file OR URL to book file, if not provided it will be asked');
|
|
12293
12634
|
runCommand.option('-r, --reload', `Call LLM models even if same prompt with result is in the cache`, false);
|
|
12294
|
-
runCommand.option('-v, --verbose', `Is output verbose`, false);
|
|
12295
|
-
runCommand.option('--no-interactive', `Input is not interactive, if true you need to pass all the input parameters through --json`);
|
|
12296
12635
|
runCommand.option('--no-formfactor', `When set, behavior of the interactive mode is not changed by the formfactor of the pipeline`);
|
|
12297
12636
|
runCommand.option('-j, --json <json>', `Pass all or some input parameters as JSON record, if used the output is also returned as JSON`);
|
|
12298
12637
|
runCommand.option('-s, --save-report <path>', `Save report to file`);
|
|
12299
|
-
runCommand.action(handleActionErrors(async (pipelineSource,
|
|
12300
|
-
|
|
12638
|
+
runCommand.action(handleActionErrors(async (pipelineSource, cliOptions) => {
|
|
12639
|
+
console.log('!!!', cliOptions);
|
|
12640
|
+
const { reload: isCacheReloaded, interactive: isInteractive, formfactor: isFormfactorUsed, json, verbose: isVerbose, saveReport, } = cliOptions;
|
|
12301
12641
|
if (pipelineSource.includes('-') && normalizeToKebabCase(pipelineSource) === pipelineSource) {
|
|
12302
12642
|
console.error(colors.red(`""${pipelineSource}" is not a valid command or book. See 'ptbk --help'.`));
|
|
12303
12643
|
return process.exit(1);
|
|
@@ -12322,7 +12662,7 @@ function $initializeRunCommand(program) {
|
|
|
12322
12662
|
const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
|
|
12323
12663
|
let llm;
|
|
12324
12664
|
try {
|
|
12325
|
-
llm = await $
|
|
12665
|
+
llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
|
|
12326
12666
|
}
|
|
12327
12667
|
catch (error) {
|
|
12328
12668
|
if (!(error instanceof Error)) {
|
|
@@ -12379,7 +12719,7 @@ function $initializeRunCommand(program) {
|
|
|
12379
12719
|
fs,
|
|
12380
12720
|
fetch: scraperFetch,
|
|
12381
12721
|
scrapers: await $provideScrapersForNode({ fs, llm, executables }, prepareAndScrapeOptions),
|
|
12382
|
-
script: [new JavascriptExecutionTools(
|
|
12722
|
+
script: [new JavascriptExecutionTools(cliOptions)],
|
|
12383
12723
|
};
|
|
12384
12724
|
if (isVerbose) {
|
|
12385
12725
|
console.info(colors.gray('--- Getting the book ---'));
|
|
@@ -12543,11 +12883,12 @@ function $initializeRunCommand(program) {
|
|
|
12543
12883
|
* @public exported from `@promptbook/remote-server`
|
|
12544
12884
|
*/
|
|
12545
12885
|
function startRemoteServer(options) {
|
|
12546
|
-
const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, } = {
|
|
12886
|
+
const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
|
|
12547
12887
|
isAnonymousModeAllowed: false,
|
|
12548
12888
|
isApplicationModeAllowed: false,
|
|
12549
12889
|
collection: null,
|
|
12550
12890
|
createLlmExecutionTools: null,
|
|
12891
|
+
login: null,
|
|
12551
12892
|
...options,
|
|
12552
12893
|
};
|
|
12553
12894
|
// <- TODO: [๐ฆช] Some helper type to be able to use discriminant union types with destructuring
|
|
@@ -12614,9 +12955,38 @@ function startRemoteServer(options) {
|
|
|
12614
12955
|
response.setHeader('X-Powered-By', 'Promptbook engine');
|
|
12615
12956
|
next();
|
|
12616
12957
|
});
|
|
12958
|
+
const swaggerOptions = {
|
|
12959
|
+
definition: {
|
|
12960
|
+
openapi: '3.0.0',
|
|
12961
|
+
info: {
|
|
12962
|
+
title: 'Promptbook Remote Server API',
|
|
12963
|
+
version: '1.0.0',
|
|
12964
|
+
description: 'API documentation for the Promptbook Remote Server',
|
|
12965
|
+
},
|
|
12966
|
+
servers: [
|
|
12967
|
+
{
|
|
12968
|
+
url: `http://localhost:${port}${rootPath}`,
|
|
12969
|
+
// <- TODO: !!!!! Probbably: Pass `remoteServerUrl` instead of `port` and `rootPath`
|
|
12970
|
+
},
|
|
12971
|
+
],
|
|
12972
|
+
},
|
|
12973
|
+
apis: ['./src/remote-server/**/*.ts'], // Adjust path as needed
|
|
12974
|
+
};
|
|
12975
|
+
const swaggerSpec = swaggerJsdoc(swaggerOptions);
|
|
12976
|
+
app.use([`/api-docs`, `${rootPath}/api-docs`], swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
12617
12977
|
const runningExecutionTasks = [];
|
|
12618
12978
|
// <- TODO: [๐คฌ] Identify the users
|
|
12619
12979
|
// TODO: [๐ง ] Do here some garbage collection of finished tasks
|
|
12980
|
+
/**
|
|
12981
|
+
* @swagger
|
|
12982
|
+
* /:
|
|
12983
|
+
* get:
|
|
12984
|
+
* summary: Get server details
|
|
12985
|
+
* description: Returns details about the Promptbook server.
|
|
12986
|
+
* responses:
|
|
12987
|
+
* 200:
|
|
12988
|
+
* description: Server details in markdown format.
|
|
12989
|
+
*/
|
|
12620
12990
|
app.get(['/', rootPath], async (request, response) => {
|
|
12621
12991
|
var _a;
|
|
12622
12992
|
if ((_a = request.url) === null || _a === void 0 ? void 0 : _a.includes('socket.io')) {
|
|
@@ -12653,9 +13023,12 @@ function startRemoteServer(options) {
|
|
|
12653
13023
|
|
|
12654
13024
|
## Paths
|
|
12655
13025
|
|
|
12656
|
-
${block(
|
|
12657
|
-
.
|
|
12658
|
-
|
|
13026
|
+
${block([
|
|
13027
|
+
...app._router.stack
|
|
13028
|
+
.map(({ route }) => (route === null || route === void 0 ? void 0 : route.path) || null)
|
|
13029
|
+
.filter((path) => path !== null),
|
|
13030
|
+
'/api-docs',
|
|
13031
|
+
]
|
|
12659
13032
|
.map((path) => `- ${path}`)
|
|
12660
13033
|
.join('\n'))}
|
|
12661
13034
|
|
|
@@ -12673,8 +13046,81 @@ function startRemoteServer(options) {
|
|
|
12673
13046
|
https://github.com/webgptorg/promptbook
|
|
12674
13047
|
`));
|
|
12675
13048
|
});
|
|
12676
|
-
|
|
12677
|
-
|
|
13049
|
+
/**
|
|
13050
|
+
* @swagger
|
|
13051
|
+
*
|
|
13052
|
+
* /login:
|
|
13053
|
+
* post:
|
|
13054
|
+
* summary: Login to the server
|
|
13055
|
+
* description: Login to the server and get identification.
|
|
13056
|
+
* requestBody:
|
|
13057
|
+
* required: true
|
|
13058
|
+
* content:
|
|
13059
|
+
* application/json:
|
|
13060
|
+
* schema:
|
|
13061
|
+
* type: object
|
|
13062
|
+
* properties:
|
|
13063
|
+
* username:
|
|
13064
|
+
* type: string
|
|
13065
|
+
* password:
|
|
13066
|
+
* type: string
|
|
13067
|
+
* appId:
|
|
13068
|
+
* type: string
|
|
13069
|
+
* responses:
|
|
13070
|
+
* 200:
|
|
13071
|
+
* description: Successful login
|
|
13072
|
+
* content:
|
|
13073
|
+
* application/json:
|
|
13074
|
+
* schema:
|
|
13075
|
+
* type: object
|
|
13076
|
+
* properties:
|
|
13077
|
+
* identification:
|
|
13078
|
+
* type: object
|
|
13079
|
+
*/
|
|
13080
|
+
app.post([`/login`, `${rootPath}/login`], async (request, response) => {
|
|
13081
|
+
if (!isApplicationModeAllowed || login === null) {
|
|
13082
|
+
response.status(400).send('Application mode is not allowed');
|
|
13083
|
+
return;
|
|
13084
|
+
}
|
|
13085
|
+
try {
|
|
13086
|
+
const username = request.body.username;
|
|
13087
|
+
const password = request.body.password;
|
|
13088
|
+
const appId = request.body.appId;
|
|
13089
|
+
const identification = await login({ username, password, appId });
|
|
13090
|
+
response.status(201).send({ identification });
|
|
13091
|
+
return;
|
|
13092
|
+
}
|
|
13093
|
+
catch (error) {
|
|
13094
|
+
if (!(error instanceof Error)) {
|
|
13095
|
+
throw error;
|
|
13096
|
+
}
|
|
13097
|
+
if (error instanceof AuthenticationError) {
|
|
13098
|
+
response.status(401).send({ error: serializeError(error) });
|
|
13099
|
+
}
|
|
13100
|
+
console.warn(`Login function thrown different error than AuthenticationError`, {
|
|
13101
|
+
error,
|
|
13102
|
+
serializedError: serializeError(error),
|
|
13103
|
+
});
|
|
13104
|
+
response.status(400).send({ error: serializeError(error) });
|
|
13105
|
+
}
|
|
13106
|
+
});
|
|
13107
|
+
/**
|
|
13108
|
+
* @swagger
|
|
13109
|
+
* /books:
|
|
13110
|
+
* get:
|
|
13111
|
+
* summary: List all books
|
|
13112
|
+
* description: Returns a list of all available books in the collection.
|
|
13113
|
+
* responses:
|
|
13114
|
+
* 200:
|
|
13115
|
+
* description: A list of books.
|
|
13116
|
+
* content:
|
|
13117
|
+
* application/json:
|
|
13118
|
+
* schema:
|
|
13119
|
+
* type: array
|
|
13120
|
+
* items:
|
|
13121
|
+
* type: string
|
|
13122
|
+
*/
|
|
13123
|
+
app.get([`/books`, `${rootPath}/books`], async (request, response) => {
|
|
12678
13124
|
if (collection === null) {
|
|
12679
13125
|
response.status(500).send('No collection available');
|
|
12680
13126
|
return;
|
|
@@ -12684,7 +13130,30 @@ function startRemoteServer(options) {
|
|
|
12684
13130
|
response.send(pipelines);
|
|
12685
13131
|
});
|
|
12686
13132
|
// TODO: [๐ง ] Is it secure / good idea to expose source codes of hosted books
|
|
12687
|
-
|
|
13133
|
+
/**
|
|
13134
|
+
* @swagger
|
|
13135
|
+
* /books/{bookId}:
|
|
13136
|
+
* get:
|
|
13137
|
+
* summary: Get book content
|
|
13138
|
+
* description: Returns the content of a specific book.
|
|
13139
|
+
* parameters:
|
|
13140
|
+
* - in: path
|
|
13141
|
+
* name: bookId
|
|
13142
|
+
* required: true
|
|
13143
|
+
* schema:
|
|
13144
|
+
* type: string
|
|
13145
|
+
* description: The ID of the book to retrieve.
|
|
13146
|
+
* responses:
|
|
13147
|
+
* 200:
|
|
13148
|
+
* description: The content of the book.
|
|
13149
|
+
* content:
|
|
13150
|
+
* text/markdown:
|
|
13151
|
+
* schema:
|
|
13152
|
+
* type: string
|
|
13153
|
+
* 404:
|
|
13154
|
+
* description: Book not found.
|
|
13155
|
+
*/
|
|
13156
|
+
app.get([`/books/*`, `${rootPath}/books/*`], async (request, response) => {
|
|
12688
13157
|
try {
|
|
12689
13158
|
if (collection === null) {
|
|
12690
13159
|
response.status(500).send('No collection nor books available');
|
|
@@ -12738,10 +13207,26 @@ function startRemoteServer(options) {
|
|
|
12738
13207
|
};
|
|
12739
13208
|
}
|
|
12740
13209
|
}
|
|
12741
|
-
|
|
13210
|
+
/**
|
|
13211
|
+
* @swagger
|
|
13212
|
+
* /executions:
|
|
13213
|
+
* get:
|
|
13214
|
+
* summary: List all executions
|
|
13215
|
+
* description: Returns a list of all running execution tasks.
|
|
13216
|
+
* responses:
|
|
13217
|
+
* 200:
|
|
13218
|
+
* description: A list of execution tasks.
|
|
13219
|
+
* content:
|
|
13220
|
+
* application/json:
|
|
13221
|
+
* schema:
|
|
13222
|
+
* type: array
|
|
13223
|
+
* items:
|
|
13224
|
+
* type: object
|
|
13225
|
+
*/
|
|
13226
|
+
app.get([`/executions`, `${rootPath}/executions`], async (request, response) => {
|
|
12742
13227
|
response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)));
|
|
12743
13228
|
});
|
|
12744
|
-
app.get(`${rootPath}/executions/last
|
|
13229
|
+
app.get([`/executions/last`, `${rootPath}/executions/last`], async (request, response) => {
|
|
12745
13230
|
// TODO: [๐คฌ] Filter only for user
|
|
12746
13231
|
if (runningExecutionTasks.length === 0) {
|
|
12747
13232
|
response.status(404).send('No execution tasks found');
|
|
@@ -12750,7 +13235,7 @@ function startRemoteServer(options) {
|
|
|
12750
13235
|
const lastExecutionTask = runningExecutionTasks[runningExecutionTasks.length - 1];
|
|
12751
13236
|
response.send(exportExecutionTask(lastExecutionTask, true));
|
|
12752
13237
|
});
|
|
12753
|
-
app.get(`${rootPath}/executions/:taskId
|
|
13238
|
+
app.get([`/executions/:taskId`, `${rootPath}/executions/:taskId`], async (request, response) => {
|
|
12754
13239
|
const { taskId } = request.params;
|
|
12755
13240
|
// TODO: [๐คฌ] Filter only for user
|
|
12756
13241
|
const executionTask = runningExecutionTasks.find((executionTask) => executionTask.taskId === taskId);
|
|
@@ -12762,7 +13247,36 @@ function startRemoteServer(options) {
|
|
|
12762
13247
|
}
|
|
12763
13248
|
response.send(exportExecutionTask(executionTask, true));
|
|
12764
13249
|
});
|
|
12765
|
-
|
|
13250
|
+
/**
|
|
13251
|
+
* @swagger
|
|
13252
|
+
* /executions/new:
|
|
13253
|
+
* post:
|
|
13254
|
+
* summary: Start a new execution
|
|
13255
|
+
* description: Starts a new execution task for a given pipeline.
|
|
13256
|
+
* requestBody:
|
|
13257
|
+
* required: true
|
|
13258
|
+
* content:
|
|
13259
|
+
* application/json:
|
|
13260
|
+
* schema:
|
|
13261
|
+
* type: object
|
|
13262
|
+
* properties:
|
|
13263
|
+
* pipelineUrl:
|
|
13264
|
+
* type: string
|
|
13265
|
+
* inputParameters:
|
|
13266
|
+
* type: object
|
|
13267
|
+
* identification:
|
|
13268
|
+
* type: object
|
|
13269
|
+
* responses:
|
|
13270
|
+
* 200:
|
|
13271
|
+
* description: The newly created execution task.
|
|
13272
|
+
* content:
|
|
13273
|
+
* application/json:
|
|
13274
|
+
* schema:
|
|
13275
|
+
* type: object
|
|
13276
|
+
* 400:
|
|
13277
|
+
* description: Invalid input.
|
|
13278
|
+
*/
|
|
13279
|
+
app.post([`/executions/new`, `${rootPath}/executions/new`], async (request, response) => {
|
|
12766
13280
|
try {
|
|
12767
13281
|
const { inputParameters, identification /* <- [๐คฌ] */ } = request.body;
|
|
12768
13282
|
const pipelineUrl = request.body.pipelineUrl || request.body.book;
|
|
@@ -12998,12 +13512,12 @@ function $initializeStartServerCommand(program) {
|
|
|
12998
13512
|
`));
|
|
12999
13513
|
startServerCommand.option('--allow-anonymous', `Is anonymous mode allowed`, false);
|
|
13000
13514
|
startServerCommand.option('-r, --reload', `Call LLM models even if same prompt with result is in the cache`, false);
|
|
13001
|
-
startServerCommand.option('-v, --verbose', `Is output verbose`, false);
|
|
13002
13515
|
startServerCommand.description(spaceTrim(`
|
|
13003
13516
|
Starts a remote server to execute books
|
|
13004
13517
|
`));
|
|
13005
13518
|
startServerCommand.alias('server');
|
|
13006
|
-
startServerCommand.action(handleActionErrors(async (path,
|
|
13519
|
+
startServerCommand.action(handleActionErrors(async (path, cliOptions) => {
|
|
13520
|
+
const { port: portRaw, url: rawUrl, allowAnonymous: isAnonymousModeAllowed, reload: isCacheReloaded, verbose: isVerbose, } = cliOptions;
|
|
13007
13521
|
if (rawUrl && !isValidUrl(rawUrl)) {
|
|
13008
13522
|
console.error(colors.red(`Invalid URL: ${rawUrl}`));
|
|
13009
13523
|
return process.exit(1);
|
|
@@ -13032,7 +13546,7 @@ function $initializeStartServerCommand(program) {
|
|
|
13032
13546
|
isCacheReloaded,
|
|
13033
13547
|
}; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
|
|
13034
13548
|
const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
|
|
13035
|
-
const llm = await $
|
|
13549
|
+
const llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
|
|
13036
13550
|
const executables = await $provideExecutablesForNode(prepareAndScrapeOptions);
|
|
13037
13551
|
const tools = {
|
|
13038
13552
|
llm,
|
|
@@ -13056,6 +13570,9 @@ function $initializeStartServerCommand(program) {
|
|
|
13056
13570
|
isAnonymousModeAllowed,
|
|
13057
13571
|
isApplicationModeAllowed: true,
|
|
13058
13572
|
collection,
|
|
13573
|
+
async login() {
|
|
13574
|
+
throw new AuthenticationError('You can not login to the server started by `ptbk start-server` in cli, use `startRemoteServer` function instead.');
|
|
13575
|
+
},
|
|
13059
13576
|
createLlmExecutionTools(options) {
|
|
13060
13577
|
const { appId, userId } = options;
|
|
13061
13578
|
TODO_USE({ appId, userId });
|
|
@@ -13092,8 +13609,8 @@ function $initializeTestCommand(program) {
|
|
|
13092
13609
|
testCommand.option('--no-validation', `Do not validate logic of pipelines in collection`, true);
|
|
13093
13610
|
testCommand.option('--no-prepare', `Do not prepare the pipelines, ideal when no LLM tools or scrapers available`, true);
|
|
13094
13611
|
testCommand.option('-r, --reload', `Call LLM models even if same prompt with result is in the cache `, false);
|
|
13095
|
-
testCommand.
|
|
13096
|
-
|
|
13612
|
+
testCommand.action(handleActionErrors(async (filesGlob, cliOptions) => {
|
|
13613
|
+
const { ignore: ignoreRaw = '', validation: isValidated, prepare: isPrepared, reload: isCacheReloaded, verbose: isVerbose, } = cliOptions;
|
|
13097
13614
|
let tools = undefined;
|
|
13098
13615
|
if (isPrepared) {
|
|
13099
13616
|
// TODO: DRY [โฝ]
|
|
@@ -13102,7 +13619,7 @@ function $initializeTestCommand(program) {
|
|
|
13102
13619
|
isCacheReloaded,
|
|
13103
13620
|
}; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
|
|
13104
13621
|
const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
|
|
13105
|
-
const llm = await $
|
|
13622
|
+
const llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
|
|
13106
13623
|
const executables = await $provideExecutablesForNode(prepareAndScrapeOptions);
|
|
13107
13624
|
tools = {
|
|
13108
13625
|
llm,
|
|
@@ -13163,56 +13680,16 @@ function $initializeTestCommand(program) {
|
|
|
13163
13680
|
*/
|
|
13164
13681
|
|
|
13165
13682
|
/**
|
|
13166
|
-
*
|
|
13167
|
-
*
|
|
13168
|
-
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
13683
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers an option in the CLI
|
|
13169
13684
|
*
|
|
13170
|
-
* @private
|
|
13685
|
+
* @private utility of CLI
|
|
13171
13686
|
*/
|
|
13172
|
-
function $
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
loginCommand.action(handleActionErrors(async () => {
|
|
13178
|
-
// @@@
|
|
13179
|
-
console.error(colors.green(spaceTrim(`
|
|
13180
|
-
You will be logged in to https://promptbook.studio server.
|
|
13181
|
-
If you don't have an account, it will be created automatically.
|
|
13182
|
-
`)));
|
|
13183
|
-
const { email, password } = await prompts([
|
|
13184
|
-
{
|
|
13185
|
-
type: 'text',
|
|
13186
|
-
name: 'email',
|
|
13187
|
-
message: 'Enter your email:',
|
|
13188
|
-
validate: (value) => (isValidEmail(value) ? true : 'Valid email is required'),
|
|
13189
|
-
},
|
|
13190
|
-
{
|
|
13191
|
-
type: 'password',
|
|
13192
|
-
name: 'password',
|
|
13193
|
-
message: 'Enter your password:',
|
|
13194
|
-
validate: (value) => value.length /* <- TODO: [๐ง ] Better password validation */ > 0 ? true : 'Password is required',
|
|
13195
|
-
},
|
|
13196
|
-
]);
|
|
13197
|
-
TODO_USE(email, password);
|
|
13198
|
-
await forTime(1000);
|
|
13199
|
-
console.error(colors.green(spaceTrim(`
|
|
13200
|
-
Your account ${email} was successfully created.
|
|
13201
|
-
|
|
13202
|
-
Please verify your email:
|
|
13203
|
-
https://brj.app/api/v1/customer/register-account?apiKey=PRODdh003eNKaec7PoO1AzU244tsL4WO
|
|
13204
|
-
|
|
13205
|
-
After verification, you will receive 500 000 credits for free ๐
|
|
13206
|
-
`)));
|
|
13207
|
-
return process.exit(0);
|
|
13208
|
-
}));
|
|
13687
|
+
function $addGlobalOptionsToCommand(command) {
|
|
13688
|
+
command.option('-v, --verbose', `Log more details`, false);
|
|
13689
|
+
command.option('--no-interactive', `Input is not interactive, if true, no CLI input is prompted if required, so either defaults are used or the command fails with exit code 1`);
|
|
13690
|
+
command.option('-p, --provider <provider>', `Which LLM provider to use: "BYOK" / "BRING_YOUR_OWN_KEYS" or "REMOTE_SERVER" / "RS"`, 'REMOTE_SERVER');
|
|
13691
|
+
command.option('--remote-server-url <url>', `URL of remote server to use when `, DEFAULT_REMOTE_SERVER_URL);
|
|
13209
13692
|
}
|
|
13210
|
-
/**
|
|
13211
|
-
* TODO: Pass remote server URL (and path)
|
|
13212
|
-
* TODO: Implement non-interactive login
|
|
13213
|
-
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
13214
|
-
* Note: [๐ก] Code in this file should never be published outside of `@promptbook/cli`
|
|
13215
|
-
*/
|
|
13216
13693
|
|
|
13217
13694
|
/**
|
|
13218
13695
|
* Runs CLI utilities of Promptbook package
|
|
@@ -13238,6 +13715,7 @@ async function promptbookCli() {
|
|
|
13238
13715
|
program.alias('ptbk');
|
|
13239
13716
|
program.version(PROMPTBOOK_ENGINE_VERSION);
|
|
13240
13717
|
program.description(CLAIM);
|
|
13718
|
+
// Note: Theese options are valid for all commands
|
|
13241
13719
|
$initializeAboutCommand(program);
|
|
13242
13720
|
$initializeRunCommand(program);
|
|
13243
13721
|
$initializeLoginCommand(program);
|
|
@@ -13248,6 +13726,8 @@ async function promptbookCli() {
|
|
|
13248
13726
|
$initializeListModelsCommand(program);
|
|
13249
13727
|
$initializeListScrapersCommand(program);
|
|
13250
13728
|
$initializeStartServerCommand(program);
|
|
13729
|
+
// TODO: [๐ง ] Should it be here or not> $addGlobalOptionsToCommand(program);
|
|
13730
|
+
program.commands.forEach($addGlobalOptionsToCommand);
|
|
13251
13731
|
program.parse(process.argv);
|
|
13252
13732
|
}
|
|
13253
13733
|
/**
|
|
@@ -13297,8 +13777,7 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
|
|
|
13297
13777
|
options: {
|
|
13298
13778
|
apiKey: 'sk-ant-api03-',
|
|
13299
13779
|
isProxied: true,
|
|
13300
|
-
|
|
13301
|
-
path: DEFAULT_REMOTE_URL_PATH,
|
|
13780
|
+
remoteServerUrl: DEFAULT_REMOTE_SERVER_URL,
|
|
13302
13781
|
},
|
|
13303
13782
|
};
|
|
13304
13783
|
},
|
|
@@ -13321,146 +13800,6 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
|
|
|
13321
13800
|
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
13322
13801
|
*/
|
|
13323
13802
|
|
|
13324
|
-
/**
|
|
13325
|
-
* Creates a connection to the remote proxy server.
|
|
13326
|
-
*
|
|
13327
|
-
* Note: This function creates a connection to the remote server and returns a socket but responsibility of closing the connection is on the caller
|
|
13328
|
-
*
|
|
13329
|
-
* @private internal utility function
|
|
13330
|
-
*/
|
|
13331
|
-
async function createRemoteClient(options) {
|
|
13332
|
-
const { remoteUrl, path } = options;
|
|
13333
|
-
return new Promise((resolve, reject) => {
|
|
13334
|
-
const socket = io(remoteUrl, {
|
|
13335
|
-
retries: CONNECTION_RETRIES_LIMIT,
|
|
13336
|
-
timeout: CONNECTION_TIMEOUT_MS,
|
|
13337
|
-
path,
|
|
13338
|
-
// path: `${this.remoteUrl.pathname}/socket.io`,
|
|
13339
|
-
transports: [/*'websocket', <- TODO: [๐ฌ] Make websocket transport work */ 'polling'],
|
|
13340
|
-
});
|
|
13341
|
-
// console.log('Connecting to', this.options.remoteUrl.href, { socket });
|
|
13342
|
-
socket.on('connect', () => {
|
|
13343
|
-
resolve(socket);
|
|
13344
|
-
});
|
|
13345
|
-
// TODO: [๐ฉ] Better timeout handling
|
|
13346
|
-
setTimeout(() => {
|
|
13347
|
-
reject(new Error(`Timeout while connecting to ${remoteUrl}`));
|
|
13348
|
-
}, CONNECTION_TIMEOUT_MS);
|
|
13349
|
-
});
|
|
13350
|
-
}
|
|
13351
|
-
|
|
13352
|
-
/**
|
|
13353
|
-
* Remote server is a proxy server that uses its execution tools internally and exposes the executor interface externally.
|
|
13354
|
-
*
|
|
13355
|
-
* You can simply use `RemoteExecutionTools` on client-side javascript and connect to your remote server.
|
|
13356
|
-
* This is useful to make all logic on browser side but not expose your API keys or no need to use customer's GPU.
|
|
13357
|
-
*
|
|
13358
|
-
* @see https://github.com/webgptorg/promptbook#remote-server
|
|
13359
|
-
* @public exported from `@promptbook/remote-client`
|
|
13360
|
-
*/
|
|
13361
|
-
class RemoteLlmExecutionTools {
|
|
13362
|
-
/* <- TODO: [๐] `, Destroyable` */
|
|
13363
|
-
constructor(options) {
|
|
13364
|
-
this.options = options;
|
|
13365
|
-
}
|
|
13366
|
-
get title() {
|
|
13367
|
-
// TODO: [๐ง ] Maybe fetch title+description from the remote server (as well as if model methods are defined)
|
|
13368
|
-
return 'Remote server';
|
|
13369
|
-
}
|
|
13370
|
-
get description() {
|
|
13371
|
-
return 'Use all models by your remote server';
|
|
13372
|
-
}
|
|
13373
|
-
/**
|
|
13374
|
-
* Check the configuration of all execution tools
|
|
13375
|
-
*/
|
|
13376
|
-
async checkConfiguration() {
|
|
13377
|
-
const socket = await createRemoteClient(this.options);
|
|
13378
|
-
socket.disconnect();
|
|
13379
|
-
// TODO: [main] !!3 Check version of the remote server and compatibility
|
|
13380
|
-
// TODO: [๐] Send checkConfiguration
|
|
13381
|
-
}
|
|
13382
|
-
/**
|
|
13383
|
-
* List all available models that can be used
|
|
13384
|
-
*/
|
|
13385
|
-
async listModels() {
|
|
13386
|
-
// TODO: [๐] Listing models (and checking configuration) probbably should go through REST API not Socket.io
|
|
13387
|
-
const socket = await createRemoteClient(this.options);
|
|
13388
|
-
socket.emit('listModels-request', {
|
|
13389
|
-
identification: this.options.identification,
|
|
13390
|
-
} /* <- Note: [๐ค] */);
|
|
13391
|
-
const promptResult = await new Promise((resolve, reject) => {
|
|
13392
|
-
socket.on('listModels-response', (response) => {
|
|
13393
|
-
resolve(response.models);
|
|
13394
|
-
socket.disconnect();
|
|
13395
|
-
});
|
|
13396
|
-
socket.on('error', (error) => {
|
|
13397
|
-
reject(deserializeError(error));
|
|
13398
|
-
socket.disconnect();
|
|
13399
|
-
});
|
|
13400
|
-
});
|
|
13401
|
-
socket.disconnect();
|
|
13402
|
-
return promptResult;
|
|
13403
|
-
}
|
|
13404
|
-
/**
|
|
13405
|
-
* Calls remote proxy server to use a chat model
|
|
13406
|
-
*/
|
|
13407
|
-
callChatModel(prompt) {
|
|
13408
|
-
if (this.options.isVerbose) {
|
|
13409
|
-
console.info(`๐ Remote callChatModel call`);
|
|
13410
|
-
}
|
|
13411
|
-
return /* not await */ this.callCommonModel(prompt);
|
|
13412
|
-
}
|
|
13413
|
-
/**
|
|
13414
|
-
* Calls remote proxy server to use a completion model
|
|
13415
|
-
*/
|
|
13416
|
-
callCompletionModel(prompt) {
|
|
13417
|
-
if (this.options.isVerbose) {
|
|
13418
|
-
console.info(`๐ฌ Remote callCompletionModel call`);
|
|
13419
|
-
}
|
|
13420
|
-
return /* not await */ this.callCommonModel(prompt);
|
|
13421
|
-
}
|
|
13422
|
-
/**
|
|
13423
|
-
* Calls remote proxy server to use a embedding model
|
|
13424
|
-
*/
|
|
13425
|
-
callEmbeddingModel(prompt) {
|
|
13426
|
-
if (this.options.isVerbose) {
|
|
13427
|
-
console.info(`๐ฌ Remote callEmbeddingModel call`);
|
|
13428
|
-
}
|
|
13429
|
-
return /* not await */ this.callCommonModel(prompt);
|
|
13430
|
-
}
|
|
13431
|
-
// <- Note: [๐ค] callXxxModel
|
|
13432
|
-
/**
|
|
13433
|
-
* Calls remote proxy server to use both completion or chat model
|
|
13434
|
-
*/
|
|
13435
|
-
async callCommonModel(prompt) {
|
|
13436
|
-
const socket = await createRemoteClient(this.options);
|
|
13437
|
-
socket.emit('prompt-request', {
|
|
13438
|
-
identification: this.options.identification,
|
|
13439
|
-
prompt,
|
|
13440
|
-
} /* <- Note: [๐ค] */);
|
|
13441
|
-
const promptResult = await new Promise((resolve, reject) => {
|
|
13442
|
-
socket.on('prompt-response', (response) => {
|
|
13443
|
-
resolve(response.promptResult);
|
|
13444
|
-
socket.disconnect();
|
|
13445
|
-
});
|
|
13446
|
-
socket.on('error', (error) => {
|
|
13447
|
-
reject(deserializeError(error));
|
|
13448
|
-
socket.disconnect();
|
|
13449
|
-
});
|
|
13450
|
-
});
|
|
13451
|
-
socket.disconnect();
|
|
13452
|
-
return promptResult;
|
|
13453
|
-
}
|
|
13454
|
-
}
|
|
13455
|
-
/**
|
|
13456
|
-
* TODO: Maybe use `$exportJson`
|
|
13457
|
-
* TODO: [๐ง ][๐] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`
|
|
13458
|
-
* TODO: [๐] Allow to list compatible models with each variant
|
|
13459
|
-
* TODO: [๐ฏ] RemoteLlmExecutionTools should extend Destroyable and implement IDestroyable
|
|
13460
|
-
* TODO: [๐ง ][๐ฐ] Allow to pass `title` for tracking purposes
|
|
13461
|
-
* TODO: [๐ง ] Maybe remove `@promptbook/remote-client` and just use `@promptbook/core`
|
|
13462
|
-
*/
|
|
13463
|
-
|
|
13464
13803
|
/**
|
|
13465
13804
|
* Function computeUsage will create price per one token based on the string value found on openai page
|
|
13466
13805
|
*
|