@promptbook/cli 0.89.0-6 โ 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 +1298 -1073
- 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 +1 -1
- package/umd/index.umd.js +1303 -1078
- 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,14 +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
26
|
import swaggerJsdoc from 'swagger-jsdoc';
|
|
26
27
|
import swaggerUi from 'swagger-ui-express';
|
|
27
|
-
import { io } from 'socket.io-client';
|
|
28
28
|
import Anthropic from '@anthropic-ai/sdk';
|
|
29
29
|
import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
|
|
30
30
|
import OpenAI from 'openai';
|
|
@@ -46,7 +46,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
|
|
|
46
46
|
* @generated
|
|
47
47
|
* @see https://github.com/webgptorg/promptbook
|
|
48
48
|
*/
|
|
49
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.89.0-
|
|
49
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.89.0-7';
|
|
50
50
|
/**
|
|
51
51
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
52
52
|
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
@@ -215,6 +215,7 @@ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [๐คนโโ๏ธ]
|
|
|
215
215
|
*/
|
|
216
216
|
const DEFAULT_BOOKS_DIRNAME = './books';
|
|
217
217
|
// <- TODO: [๐] Make also `BOOKS_DIRNAME_ALTERNATIVES`
|
|
218
|
+
// TODO: !!!!!! Just .promptbook dir, hardocode others
|
|
218
219
|
/**
|
|
219
220
|
* Where to store the temporary downloads
|
|
220
221
|
*
|
|
@@ -239,6 +240,21 @@ const DEFAULT_EXECUTION_CACHE_DIRNAME = './.promptbook/execution-cache';
|
|
|
239
240
|
* @public exported from `@promptbook/core`
|
|
240
241
|
*/
|
|
241
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
|
+
*/
|
|
242
258
|
/**
|
|
243
259
|
* The name of the builded pipeline collection made by CLI `ptbk make` and for lookup in `createCollectionFromDirectory`
|
|
244
260
|
*
|
|
@@ -259,13 +275,7 @@ const MOMENT_ARG_THRESHOLDS = {
|
|
|
259
275
|
*
|
|
260
276
|
* @public exported from `@promptbook/core`
|
|
261
277
|
*/
|
|
262
|
-
const
|
|
263
|
-
/**
|
|
264
|
-
* @@@
|
|
265
|
-
*
|
|
266
|
-
* @public exported from `@promptbook/core`
|
|
267
|
-
*/
|
|
268
|
-
const DEFAULT_REMOTE_URL_PATH = '/promptbook/socket.io';
|
|
278
|
+
const DEFAULT_REMOTE_SERVER_URL = 'https://api.pavolhejny.com/promptbook';
|
|
269
279
|
// <- TODO: [๐งโโ๏ธ]
|
|
270
280
|
/**
|
|
271
281
|
* @@@
|
|
@@ -307,7 +317,7 @@ const IS_PIPELINE_LOGIC_VALIDATED = just(
|
|
|
307
317
|
true);
|
|
308
318
|
/**
|
|
309
319
|
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
310
|
-
* TODO: [๐ง ][๐งโโ๏ธ] Maybe join
|
|
320
|
+
* TODO: [๐ง ][๐งโโ๏ธ] Maybe join remoteServerUrl and path into single value
|
|
311
321
|
*/
|
|
312
322
|
|
|
313
323
|
/**
|
|
@@ -485,7 +495,8 @@ function $initializeHelloCommand(program) {
|
|
|
485
495
|
helloCommand.alias('hi');
|
|
486
496
|
helloCommand.argument('[name]', 'Your name', 'Paul');
|
|
487
497
|
helloCommand.option('-g, --greeting <greeting>', `Greeting`, 'Hello');
|
|
488
|
-
helloCommand.action(handleActionErrors(async (name,
|
|
498
|
+
helloCommand.action(handleActionErrors(async (name, cliOptions) => {
|
|
499
|
+
const { greeting } = cliOptions;
|
|
489
500
|
console.info(colors.cyan(`${greeting} ${name}`));
|
|
490
501
|
await forTime(1000);
|
|
491
502
|
console.info(colors.rainbow(`Nice to meet you!`));
|
|
@@ -499,40 +510,27 @@ function $initializeHelloCommand(program) {
|
|
|
499
510
|
*/
|
|
500
511
|
|
|
501
512
|
/**
|
|
502
|
-
*
|
|
503
|
-
* No side effects.
|
|
504
|
-
*
|
|
505
|
-
* 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
|
|
506
514
|
*
|
|
507
|
-
* @
|
|
508
|
-
* @returns void
|
|
509
|
-
* @private within the repository
|
|
515
|
+
* @public exported from `@promptbook/core`
|
|
510
516
|
*/
|
|
511
|
-
|
|
512
|
-
|
|
517
|
+
class NotYetImplementedError extends Error {
|
|
518
|
+
constructor(message) {
|
|
519
|
+
super(spaceTrim$1((block) => `
|
|
520
|
+
${block(message)}
|
|
513
521
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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);
|
|
522
532
|
}
|
|
523
|
-
return {
|
|
524
|
-
stat,
|
|
525
|
-
access,
|
|
526
|
-
constants,
|
|
527
|
-
readFile,
|
|
528
|
-
writeFile,
|
|
529
|
-
readdir,
|
|
530
|
-
mkdir,
|
|
531
|
-
};
|
|
532
533
|
}
|
|
533
|
-
/**
|
|
534
|
-
* Note: [๐ข] Code in this file should never be never released in packages that could be imported into browser environment
|
|
535
|
-
*/
|
|
536
534
|
|
|
537
535
|
/**
|
|
538
536
|
* Make error report URL for the given error
|
|
@@ -603,120 +601,566 @@ class UnexpectedError extends Error {
|
|
|
603
601
|
}
|
|
604
602
|
|
|
605
603
|
/**
|
|
606
|
-
*
|
|
604
|
+
* @@@
|
|
607
605
|
*
|
|
608
|
-
*
|
|
609
|
-
*
|
|
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`
|
|
610
609
|
*/
|
|
611
|
-
function
|
|
612
|
-
|
|
613
|
-
const orderedValue = {
|
|
614
|
-
...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
|
|
615
|
-
...value,
|
|
616
|
-
};
|
|
617
|
-
return orderedValue;
|
|
610
|
+
function $getGlobalScope() {
|
|
611
|
+
return Function('return this')();
|
|
618
612
|
}
|
|
619
613
|
|
|
620
614
|
/**
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
* Note: `$` is used to indicate that this function is not a pure function - it mutates given object
|
|
624
|
-
* Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
|
|
615
|
+
* @@@
|
|
625
616
|
*
|
|
626
|
-
* @
|
|
617
|
+
* @param text @@@
|
|
618
|
+
* @returns @@@
|
|
619
|
+
* @example 'HELLO_WORLD'
|
|
620
|
+
* @example 'I_LOVE_PROMPTBOOK'
|
|
627
621
|
* @public exported from `@promptbook/utils`
|
|
628
622
|
*/
|
|
629
|
-
function
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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 += '_';
|
|
638
650
|
}
|
|
651
|
+
normalizedName += normalizedChar;
|
|
652
|
+
lastCharType = charType;
|
|
639
653
|
}
|
|
640
|
-
|
|
641
|
-
|
|
654
|
+
normalizedName = normalizedName.replace(/_+/g, '_');
|
|
655
|
+
normalizedName = normalizedName.replace(/_?\/_?/g, '/');
|
|
656
|
+
normalizedName = normalizedName.replace(/^_/, '');
|
|
657
|
+
normalizedName = normalizedName.replace(/_$/, '');
|
|
658
|
+
return normalizedName;
|
|
642
659
|
}
|
|
643
660
|
/**
|
|
644
|
-
* 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
|
|
645
667
|
*/
|
|
646
668
|
|
|
647
669
|
/**
|
|
648
|
-
*
|
|
649
|
-
* If not, throws an UnexpectedError with a rich error message and tracking
|
|
650
|
-
*
|
|
651
|
-
* - Almost all primitives are serializable BUT:
|
|
652
|
-
* - `undefined` is not serializable
|
|
653
|
-
* - `NaN` is not serializable
|
|
654
|
-
* - Objects and arrays are serializable if all their properties are serializable
|
|
655
|
-
* - Functions are not serializable
|
|
656
|
-
* - Circular references are not serializable
|
|
657
|
-
* - `Date` objects are not serializable
|
|
658
|
-
* - `Map` and `Set` objects are not serializable
|
|
659
|
-
* - `RegExp` objects are not serializable
|
|
660
|
-
* - `Error` objects are not serializable
|
|
661
|
-
* - `Symbol` objects are not serializable
|
|
662
|
-
* - And much more...
|
|
670
|
+
* @@@
|
|
663
671
|
*
|
|
664
|
-
* @
|
|
672
|
+
* @param text @@@
|
|
673
|
+
* @returns @@@
|
|
674
|
+
* @example 'hello_world'
|
|
675
|
+
* @example 'i_love_promptbook'
|
|
665
676
|
* @public exported from `@promptbook/utils`
|
|
666
677
|
*/
|
|
667
|
-
function
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
throw new UnexpectedError(`${name} is undefined`);
|
|
671
|
-
}
|
|
672
|
-
else if (value === null) {
|
|
673
|
-
return;
|
|
674
|
-
}
|
|
675
|
-
else if (typeof value === 'boolean') {
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
else if (typeof value === 'number' && !isNaN(value)) {
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
else if (typeof value === 'string') {
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
684
|
-
else if (typeof value === 'symbol') {
|
|
685
|
-
throw new UnexpectedError(`${name} is symbol`);
|
|
686
|
-
}
|
|
687
|
-
else if (typeof value === 'function') {
|
|
688
|
-
throw new UnexpectedError(`${name} is function`);
|
|
689
|
-
}
|
|
690
|
-
else if (typeof value === 'object' && Array.isArray(value)) {
|
|
691
|
-
for (let i = 0; i < value.length; i++) {
|
|
692
|
-
checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
else if (typeof value === 'object') {
|
|
696
|
-
if (value instanceof Date) {
|
|
697
|
-
throw new UnexpectedError(spaceTrim((block) => `
|
|
698
|
-
\`${name}\` is Date
|
|
699
|
-
|
|
700
|
-
Use \`string_date_iso8601\` instead
|
|
678
|
+
function normalizeTo_snake_case(text) {
|
|
679
|
+
return normalizeTo_SCREAMING_CASE(text).toLowerCase();
|
|
680
|
+
}
|
|
701
681
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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] = [];
|
|
705
696
|
}
|
|
706
|
-
else if (
|
|
707
|
-
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]}`);
|
|
708
699
|
}
|
|
709
|
-
|
|
710
|
-
|
|
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);
|
|
711
712
|
}
|
|
712
|
-
else
|
|
713
|
-
|
|
713
|
+
else {
|
|
714
|
+
this.storage[existingRegistrationIndex] = registered;
|
|
714
715
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
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\`
|
|
720
1164
|
|
|
721
1165
|
Additional message for \`${name}\`:
|
|
722
1166
|
${block(message || '(nothing)')}
|
|
@@ -1060,35 +1504,6 @@ function isValidFilePath(filename) {
|
|
|
1060
1504
|
* TODO: [๐] Implement for MacOs
|
|
1061
1505
|
*/
|
|
1062
1506
|
|
|
1063
|
-
/**
|
|
1064
|
-
* Tests if given string is valid URL.
|
|
1065
|
-
*
|
|
1066
|
-
* Note: Dataurl are considered perfectly valid.
|
|
1067
|
-
* Note: There are two simmilar functions:
|
|
1068
|
-
* - `isValidUrl` which tests any URL
|
|
1069
|
-
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
1070
|
-
*
|
|
1071
|
-
* @public exported from `@promptbook/utils`
|
|
1072
|
-
*/
|
|
1073
|
-
function isValidUrl(url) {
|
|
1074
|
-
if (typeof url !== 'string') {
|
|
1075
|
-
return false;
|
|
1076
|
-
}
|
|
1077
|
-
try {
|
|
1078
|
-
if (url.startsWith('blob:')) {
|
|
1079
|
-
url = url.replace(/^blob:/, '');
|
|
1080
|
-
}
|
|
1081
|
-
const urlObject = new URL(url /* because fail is handled */);
|
|
1082
|
-
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
1083
|
-
return false;
|
|
1084
|
-
}
|
|
1085
|
-
return true;
|
|
1086
|
-
}
|
|
1087
|
-
catch (error) {
|
|
1088
|
-
return false;
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
1507
|
const defaultDiacriticsRemovalMap = [
|
|
1093
1508
|
{
|
|
1094
1509
|
base: 'A',
|
|
@@ -1472,32 +1887,177 @@ class FileCacheStorage {
|
|
|
1472
1887
|
// TODO: [๐]
|
|
1473
1888
|
return value;
|
|
1474
1889
|
}
|
|
1475
|
-
/**
|
|
1476
|
-
* @@@ Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
|
|
1477
|
-
*/
|
|
1478
|
-
async setItem(key, value) {
|
|
1479
|
-
const filename = this.getFilenameForKey(key);
|
|
1480
|
-
if (!isSerializableAsJson(value)) {
|
|
1481
|
-
throw new UnexpectedError(`The "${key}" you want to store in JSON file is not serializable as JSON`);
|
|
1482
|
-
}
|
|
1483
|
-
const fileContent = stringifyPipelineJson(value);
|
|
1484
|
-
await mkdir(dirname(filename), { recursive: true }); // <- [0]
|
|
1485
|
-
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);
|
|
1486
2044
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
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);
|
|
1496
2057
|
}
|
|
1497
2058
|
}
|
|
1498
2059
|
/**
|
|
1499
|
-
* TODO:
|
|
1500
|
-
* 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`
|
|
1501
2061
|
*/
|
|
1502
2062
|
|
|
1503
2063
|
/**
|
|
@@ -1535,51 +2095,260 @@ class PipelineExecutionError extends Error {
|
|
|
1535
2095
|
*/
|
|
1536
2096
|
|
|
1537
2097
|
/**
|
|
1538
|
-
*
|
|
2098
|
+
* This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
|
|
1539
2099
|
*
|
|
1540
2100
|
* @public exported from `@promptbook/core`
|
|
1541
2101
|
*/
|
|
1542
|
-
class
|
|
1543
|
-
constructor() {
|
|
1544
|
-
|
|
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';
|
|
1545
2261
|
}
|
|
1546
2262
|
/**
|
|
1547
|
-
*
|
|
2263
|
+
* Check the configuration of all execution tools
|
|
1548
2264
|
*/
|
|
1549
|
-
|
|
1550
|
-
|
|
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
|
|
1551
2270
|
}
|
|
1552
2271
|
/**
|
|
1553
|
-
*
|
|
2272
|
+
* List all available models that can be used
|
|
1554
2273
|
*/
|
|
1555
|
-
|
|
1556
|
-
|
|
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;
|
|
1557
2292
|
}
|
|
1558
2293
|
/**
|
|
1559
|
-
*
|
|
2294
|
+
* Calls remote proxy server to use a chat model
|
|
1560
2295
|
*/
|
|
1561
|
-
|
|
1562
|
-
|
|
2296
|
+
callChatModel(prompt) {
|
|
2297
|
+
if (this.options.isVerbose) {
|
|
2298
|
+
console.info(`๐ Remote callChatModel call`);
|
|
2299
|
+
}
|
|
2300
|
+
return /* not await */ this.callCommonModel(prompt);
|
|
1563
2301
|
}
|
|
1564
2302
|
/**
|
|
1565
|
-
*
|
|
2303
|
+
* Calls remote proxy server to use a completion model
|
|
1566
2304
|
*/
|
|
1567
|
-
|
|
1568
|
-
|
|
2305
|
+
callCompletionModel(prompt) {
|
|
2306
|
+
if (this.options.isVerbose) {
|
|
2307
|
+
console.info(`๐ฌ Remote callCompletionModel call`);
|
|
2308
|
+
}
|
|
2309
|
+
return /* not await */ this.callCommonModel(prompt);
|
|
1569
2310
|
}
|
|
1570
2311
|
/**
|
|
1571
|
-
*
|
|
2312
|
+
* Calls remote proxy server to use a embedding model
|
|
1572
2313
|
*/
|
|
1573
|
-
|
|
1574
|
-
this.
|
|
2314
|
+
callEmbeddingModel(prompt) {
|
|
2315
|
+
if (this.options.isVerbose) {
|
|
2316
|
+
console.info(`๐ฌ Remote callEmbeddingModel call`);
|
|
2317
|
+
}
|
|
2318
|
+
return /* not await */ this.callCommonModel(prompt);
|
|
1575
2319
|
}
|
|
2320
|
+
// <- Note: [๐ค] callXxxModel
|
|
1576
2321
|
/**
|
|
1577
|
-
*
|
|
2322
|
+
* Calls remote proxy server to use both completion or chat model
|
|
1578
2323
|
*/
|
|
1579
|
-
|
|
1580
|
-
|
|
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;
|
|
1581
2342
|
}
|
|
1582
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
|
+
*/
|
|
1583
2352
|
|
|
1584
2353
|
/**
|
|
1585
2354
|
* Simple wrapper `new Date().toISOString()`
|
|
@@ -1858,346 +2627,32 @@ function countUsage(llmTools) {
|
|
|
1858
2627
|
}
|
|
1859
2628
|
// <- Note: [๐ค]
|
|
1860
2629
|
return proxyTools;
|
|
1861
|
-
}
|
|
1862
|
-
/**
|
|
1863
|
-
* TODO: [๐ง ][๐ธ] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
|
|
1864
|
-
* TODO: [๐ง ] Is there some meaningfull way how to test this util
|
|
1865
|
-
* TODO: [๐ง ][๐ฏ] Maybe a way how to hide ability to `get totalUsage`
|
|
1866
|
-
* > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
|
|
1867
|
-
* TODO: [๐ทโโ๏ธ] @@@ Manual about construction of llmTools
|
|
1868
|
-
*/
|
|
1869
|
-
|
|
1870
|
-
/**
|
|
1871
|
-
* This error type indicates that some part of the code is not implemented yet
|
|
1872
|
-
*
|
|
1873
|
-
* @public exported from `@promptbook/core`
|
|
1874
|
-
*/
|
|
1875
|
-
class NotYetImplementedError extends Error {
|
|
1876
|
-
constructor(message) {
|
|
1877
|
-
super(spaceTrim$1((block) => `
|
|
1878
|
-
${block(message)}
|
|
1879
|
-
|
|
1880
|
-
Note: This feature is not implemented yet but it will be soon.
|
|
1881
|
-
|
|
1882
|
-
If you want speed up the implementation or just read more, look here:
|
|
1883
|
-
https://github.com/webgptorg/promptbook
|
|
1884
|
-
|
|
1885
|
-
Or contact us on pavol@ptbk.io
|
|
1886
|
-
|
|
1887
|
-
`));
|
|
1888
|
-
this.name = 'NotYetImplementedError';
|
|
1889
|
-
Object.setPrototypeOf(this, NotYetImplementedError.prototype);
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
|
-
/**
|
|
1894
|
-
* @@@
|
|
1895
|
-
*
|
|
1896
|
-
* Note: `$` is used to indicate that this function is not a pure function - it access global scope
|
|
1897
|
-
*
|
|
1898
|
-
* @private internal function of `$Register`
|
|
1899
|
-
*/
|
|
1900
|
-
function $getGlobalScope() {
|
|
1901
|
-
return Function('return this')();
|
|
1902
|
-
}
|
|
1903
|
-
|
|
1904
|
-
/**
|
|
1905
|
-
* @@@
|
|
1906
|
-
*
|
|
1907
|
-
* @param text @@@
|
|
1908
|
-
* @returns @@@
|
|
1909
|
-
* @example 'HELLO_WORLD'
|
|
1910
|
-
* @example 'I_LOVE_PROMPTBOOK'
|
|
1911
|
-
* @public exported from `@promptbook/utils`
|
|
1912
|
-
*/
|
|
1913
|
-
function normalizeTo_SCREAMING_CASE(text) {
|
|
1914
|
-
let charType;
|
|
1915
|
-
let lastCharType = 'OTHER';
|
|
1916
|
-
let normalizedName = '';
|
|
1917
|
-
for (const char of text) {
|
|
1918
|
-
let normalizedChar;
|
|
1919
|
-
if (/^[a-z]$/.test(char)) {
|
|
1920
|
-
charType = 'LOWERCASE';
|
|
1921
|
-
normalizedChar = char.toUpperCase();
|
|
1922
|
-
}
|
|
1923
|
-
else if (/^[A-Z]$/.test(char)) {
|
|
1924
|
-
charType = 'UPPERCASE';
|
|
1925
|
-
normalizedChar = char;
|
|
1926
|
-
}
|
|
1927
|
-
else if (/^[0-9]$/.test(char)) {
|
|
1928
|
-
charType = 'NUMBER';
|
|
1929
|
-
normalizedChar = char;
|
|
1930
|
-
}
|
|
1931
|
-
else {
|
|
1932
|
-
charType = 'OTHER';
|
|
1933
|
-
normalizedChar = '_';
|
|
1934
|
-
}
|
|
1935
|
-
if (charType !== lastCharType &&
|
|
1936
|
-
!(lastCharType === 'UPPERCASE' && charType === 'LOWERCASE') &&
|
|
1937
|
-
!(lastCharType === 'NUMBER') &&
|
|
1938
|
-
!(charType === 'NUMBER')) {
|
|
1939
|
-
normalizedName += '_';
|
|
1940
|
-
}
|
|
1941
|
-
normalizedName += normalizedChar;
|
|
1942
|
-
lastCharType = charType;
|
|
1943
|
-
}
|
|
1944
|
-
normalizedName = normalizedName.replace(/_+/g, '_');
|
|
1945
|
-
normalizedName = normalizedName.replace(/_?\/_?/g, '/');
|
|
1946
|
-
normalizedName = normalizedName.replace(/^_/, '');
|
|
1947
|
-
normalizedName = normalizedName.replace(/_$/, '');
|
|
1948
|
-
return normalizedName;
|
|
1949
|
-
}
|
|
1950
|
-
/**
|
|
1951
|
-
* TODO: Tests
|
|
1952
|
-
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: 'Moje tabule' })).toEqual('/VtG7sR9rRJqwNEdM2/Moje tabule');
|
|
1953
|
-
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: 'ฤลกฤลลพลพรฝรกรญรบลฏ' })).toEqual('/VtG7sR9rRJqwNEdM2/escrzyaieuu');
|
|
1954
|
-
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: ' ahoj ' })).toEqual('/VtG7sR9rRJqwNEdM2/ahoj');
|
|
1955
|
-
* > expect(encodeRoutePath({ uriId: 'VtG7sR9rRJqwNEdM2', name: ' ahoj_ahojAhoj ahoj ' })).toEqual('/VtG7sR9rRJqwNEdM2/ahoj-ahoj-ahoj-ahoj');
|
|
1956
|
-
* TODO: [๐บ] Use some intermediate util splitWords
|
|
1957
|
-
*/
|
|
1958
|
-
|
|
1959
|
-
/**
|
|
1960
|
-
* @@@
|
|
1961
|
-
*
|
|
1962
|
-
* @param text @@@
|
|
1963
|
-
* @returns @@@
|
|
1964
|
-
* @example 'hello_world'
|
|
1965
|
-
* @example 'i_love_promptbook'
|
|
1966
|
-
* @public exported from `@promptbook/utils`
|
|
1967
|
-
*/
|
|
1968
|
-
function normalizeTo_snake_case(text) {
|
|
1969
|
-
return normalizeTo_SCREAMING_CASE(text).toLowerCase();
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
|
-
/**
|
|
1973
|
-
* Register is @@@
|
|
1974
|
-
*
|
|
1975
|
-
* Note: `$` is used to indicate that this function is not a pure function - it accesses and adds variables in global scope.
|
|
1976
|
-
*
|
|
1977
|
-
* @private internal utility, exported are only signleton instances of this class
|
|
1978
|
-
*/
|
|
1979
|
-
class $Register {
|
|
1980
|
-
constructor(registerName) {
|
|
1981
|
-
this.registerName = registerName;
|
|
1982
|
-
const storageName = `_promptbook_${normalizeTo_snake_case(registerName)}`;
|
|
1983
|
-
const globalScope = $getGlobalScope();
|
|
1984
|
-
if (globalScope[storageName] === undefined) {
|
|
1985
|
-
globalScope[storageName] = [];
|
|
1986
|
-
}
|
|
1987
|
-
else if (!Array.isArray(globalScope[storageName])) {
|
|
1988
|
-
throw new UnexpectedError(`Expected (global) ${storageName} to be an array, but got ${typeof globalScope[storageName]}`);
|
|
1989
|
-
}
|
|
1990
|
-
this.storage = globalScope[storageName];
|
|
1991
|
-
}
|
|
1992
|
-
list() {
|
|
1993
|
-
// <- TODO: ReadonlyDeep<ReadonlyArray<TRegistered>>
|
|
1994
|
-
return this.storage;
|
|
1995
|
-
}
|
|
1996
|
-
register(registered) {
|
|
1997
|
-
const { packageName, className } = registered;
|
|
1998
|
-
const existingRegistrationIndex = this.storage.findIndex((item) => item.packageName === packageName && item.className === className);
|
|
1999
|
-
const existingRegistration = this.storage[existingRegistrationIndex];
|
|
2000
|
-
if (!existingRegistration) {
|
|
2001
|
-
this.storage.push(registered);
|
|
2002
|
-
}
|
|
2003
|
-
else {
|
|
2004
|
-
this.storage[existingRegistrationIndex] = registered;
|
|
2005
|
-
}
|
|
2006
|
-
return {
|
|
2007
|
-
registerName: this.registerName,
|
|
2008
|
-
packageName,
|
|
2009
|
-
className,
|
|
2010
|
-
get isDestroyed() {
|
|
2011
|
-
return false;
|
|
2012
|
-
},
|
|
2013
|
-
destroy() {
|
|
2014
|
-
throw new NotYetImplementedError(`Registration to ${this.registerName} is permanent in this version of Promptbook`);
|
|
2015
|
-
},
|
|
2016
|
-
};
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
/**
|
|
2021
|
-
* @@@
|
|
2022
|
-
*
|
|
2023
|
-
* Note: `$` is used to indicate that this interacts with the global scope
|
|
2024
|
-
* @singleton Only one instance of each register is created per build, but thare can be more @@@
|
|
2025
|
-
* @public exported from `@promptbook/core`
|
|
2026
|
-
*/
|
|
2027
|
-
const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
|
|
2028
|
-
/**
|
|
2029
|
-
* TODO: [ยฎ] DRY Register logic
|
|
2030
|
-
*/
|
|
2031
|
-
|
|
2032
|
-
/**
|
|
2033
|
-
* Determines if the given path is a root path.
|
|
2034
|
-
*
|
|
2035
|
-
* Note: This does not check if the file exists only if the path is valid
|
|
2036
|
-
* @public exported from `@promptbook/utils`
|
|
2037
|
-
*/
|
|
2038
|
-
function isRootPath(value) {
|
|
2039
|
-
if (value === '/') {
|
|
2040
|
-
return true;
|
|
2041
|
-
}
|
|
2042
|
-
if (/^[A-Z]:\\$/i.test(value)) {
|
|
2043
|
-
return true;
|
|
2044
|
-
}
|
|
2045
|
-
return false;
|
|
2046
|
-
}
|
|
2047
|
-
/**
|
|
2048
|
-
* TODO: [๐] Make for MacOS paths
|
|
2049
|
-
*/
|
|
2050
|
-
|
|
2051
|
-
/**
|
|
2052
|
-
* @@@
|
|
2053
|
-
*
|
|
2054
|
-
* Note: `$` is used to indicate that this interacts with the global scope
|
|
2055
|
-
* @singleton Only one instance of each register is created per build, but thare can be more @@@
|
|
2056
|
-
* @public exported from `@promptbook/core`
|
|
2057
|
-
*/
|
|
2058
|
-
const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
|
|
2059
|
-
/**
|
|
2060
|
-
* TODO: [ยฎ] DRY Register logic
|
|
2061
|
-
*/
|
|
2062
|
-
|
|
2063
|
-
/**
|
|
2064
|
-
* Path to the `.env` file which was used to configure LLM tools
|
|
2065
|
-
*
|
|
2066
|
-
* Note: `$` is used to indicate that this variable is changed by side effect in `$provideLlmToolsConfigurationFromEnv` through `$setUsedEnvFilename`
|
|
2067
|
-
*/
|
|
2068
|
-
let $usedEnvFilename = null;
|
|
2630
|
+
}
|
|
2069
2631
|
/**
|
|
2070
|
-
*
|
|
2071
|
-
*
|
|
2072
|
-
*
|
|
2073
|
-
*
|
|
2074
|
-
*
|
|
2632
|
+
* TODO: [๐ง ][๐ธ] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
|
|
2633
|
+
* TODO: [๐ง ] Is there some meaningfull way how to test this util
|
|
2634
|
+
* TODO: [๐ง ][๐ฏ] Maybe a way how to hide ability to `get totalUsage`
|
|
2635
|
+
* > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
|
|
2636
|
+
* TODO: [๐ทโโ๏ธ] @@@ Manual about construction of llmTools
|
|
2075
2637
|
*/
|
|
2076
|
-
|
|
2077
|
-
$usedEnvFilename = filepath;
|
|
2078
|
-
}
|
|
2638
|
+
|
|
2079
2639
|
/**
|
|
2080
|
-
*
|
|
2081
|
-
*
|
|
2082
|
-
* 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.
|
|
2083
2641
|
*
|
|
2084
|
-
*
|
|
2642
|
+
* Note: This does not check if the file exists only if the path is valid
|
|
2643
|
+
* @public exported from `@promptbook/utils`
|
|
2085
2644
|
*/
|
|
2086
|
-
function
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
env = process.env;
|
|
2090
|
-
// <- TODO: [โ] Some DRY way how to get to `process.env` and pass it into functions - ACRY search for `env`
|
|
2091
|
-
}
|
|
2092
|
-
else {
|
|
2093
|
-
env = {};
|
|
2094
|
-
}
|
|
2095
|
-
/**
|
|
2096
|
-
* Mixes registered LLM tools from $llmToolsMetadataRegister and $llmToolsRegister
|
|
2097
|
-
*/
|
|
2098
|
-
const all = [];
|
|
2099
|
-
for (const { title, packageName, className, envVariables } of $llmToolsMetadataRegister.list()) {
|
|
2100
|
-
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
2101
|
-
continue;
|
|
2102
|
-
}
|
|
2103
|
-
all.push({ title, packageName, className, envVariables });
|
|
2104
|
-
}
|
|
2105
|
-
for (const { packageName, className } of $llmToolsRegister.list()) {
|
|
2106
|
-
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
2107
|
-
continue;
|
|
2108
|
-
}
|
|
2109
|
-
all.push({ packageName, className });
|
|
2645
|
+
function isRootPath(value) {
|
|
2646
|
+
if (value === '/') {
|
|
2647
|
+
return true;
|
|
2110
2648
|
}
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
const isMetadataAviailable = $llmToolsMetadataRegister
|
|
2114
|
-
.list()
|
|
2115
|
-
.find(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
|
|
2116
|
-
const isInstalled = $llmToolsRegister
|
|
2117
|
-
.list()
|
|
2118
|
-
.find(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
|
|
2119
|
-
const isFullyConfigured = ((_a = metadata.envVariables) === null || _a === void 0 ? void 0 : _a.every((envVariableName) => env[envVariableName] !== undefined)) || false;
|
|
2120
|
-
const isPartiallyConfigured = ((_b = metadata.envVariables) === null || _b === void 0 ? void 0 : _b.some((envVariableName) => env[envVariableName] !== undefined)) || false;
|
|
2121
|
-
// <- Note: [๐จ]
|
|
2122
|
-
return { ...metadata, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured };
|
|
2123
|
-
});
|
|
2124
|
-
const usedEnvMessage = $usedEnvFilename === null ? `Unknown \`.env\` file` : `Used \`.env\` file:\n${$usedEnvFilename}`;
|
|
2125
|
-
if (metadata.length === 0) {
|
|
2126
|
-
return spaceTrim((block) => `
|
|
2127
|
-
No LLM providers are available.
|
|
2128
|
-
|
|
2129
|
-
${block(usedEnvMessage)}
|
|
2130
|
-
`);
|
|
2649
|
+
if (/^[A-Z]:\\$/i.test(value)) {
|
|
2650
|
+
return true;
|
|
2131
2651
|
}
|
|
2132
|
-
return
|
|
2133
|
-
|
|
2134
|
-
${block(usedEnvMessage)}
|
|
2135
|
-
|
|
2136
|
-
Relevant environment variables:
|
|
2137
|
-
${block(Object.keys(env)
|
|
2138
|
-
.filter((envVariableName) => metadata.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)))
|
|
2139
|
-
.map((envVariableName) => `- \`${envVariableName}\``)
|
|
2140
|
-
.join('\n'))}
|
|
2141
|
-
|
|
2142
|
-
Available LLM providers are:
|
|
2143
|
-
${block(metadata
|
|
2144
|
-
.map(({ title, packageName, className, envVariables, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured, }, i) => {
|
|
2145
|
-
const morePieces = [];
|
|
2146
|
-
if (just(false)) ;
|
|
2147
|
-
else if (!isMetadataAviailable && !isInstalled) {
|
|
2148
|
-
// TODO: [๏ฟฝ][๏ฟฝ] Maybe do allow to do auto-install if package not registered and not found
|
|
2149
|
-
morePieces.push(`Not installed and no metadata, looks like a unexpected behavior`);
|
|
2150
|
-
}
|
|
2151
|
-
else if (isMetadataAviailable && !isInstalled) {
|
|
2152
|
-
// TODO: [๏ฟฝ][๏ฟฝ]
|
|
2153
|
-
morePieces.push(`Not installed`);
|
|
2154
|
-
}
|
|
2155
|
-
else if (!isMetadataAviailable && isInstalled) {
|
|
2156
|
-
morePieces.push(`No metadata but installed, looks like a unexpected behavior`);
|
|
2157
|
-
}
|
|
2158
|
-
else if (isMetadataAviailable && isInstalled) {
|
|
2159
|
-
morePieces.push(`Installed`);
|
|
2160
|
-
}
|
|
2161
|
-
else {
|
|
2162
|
-
morePieces.push(`unknown state, looks like a unexpected behavior`);
|
|
2163
|
-
} /* not else */
|
|
2164
|
-
if (isFullyConfigured) {
|
|
2165
|
-
morePieces.push(`Configured`);
|
|
2166
|
-
}
|
|
2167
|
-
else if (isPartiallyConfigured) {
|
|
2168
|
-
morePieces.push(`Partially confugured, missing ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined).join(' + ')}`);
|
|
2169
|
-
}
|
|
2170
|
-
else {
|
|
2171
|
-
if (envVariables !== null) {
|
|
2172
|
-
morePieces.push(`Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`);
|
|
2173
|
-
}
|
|
2174
|
-
else {
|
|
2175
|
-
morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
let providerMessage = spaceTrim(`
|
|
2179
|
-
${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
2180
|
-
${morePieces.join('; ')}
|
|
2181
|
-
`);
|
|
2182
|
-
if ($isRunningInNode) {
|
|
2183
|
-
if (isInstalled && isFullyConfigured) {
|
|
2184
|
-
providerMessage = colors.green(providerMessage);
|
|
2185
|
-
}
|
|
2186
|
-
else if (isInstalled && isPartiallyConfigured) {
|
|
2187
|
-
providerMessage = colors.yellow(providerMessage);
|
|
2188
|
-
}
|
|
2189
|
-
else {
|
|
2190
|
-
providerMessage = colors.gray(providerMessage);
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
return providerMessage;
|
|
2194
|
-
})
|
|
2195
|
-
.join('\n'))}
|
|
2196
|
-
`);
|
|
2652
|
+
return false;
|
|
2197
2653
|
}
|
|
2198
2654
|
/**
|
|
2199
|
-
* TODO: [
|
|
2200
|
-
* TODO: [๐ง ][โ] Maybe pass env as argument
|
|
2655
|
+
* TODO: [๐] Make for MacOS paths
|
|
2201
2656
|
*/
|
|
2202
2657
|
|
|
2203
2658
|
/**
|
|
@@ -2560,10 +3015,33 @@ async function $provideLlmToolsForWizzardOrCli(options) {
|
|
|
2560
3015
|
if (!$isRunningInNode()) {
|
|
2561
3016
|
throw new EnvironmentMismatchError('Function `$provideLlmToolsForWizzardOrCli` works only in Node.js environment');
|
|
2562
3017
|
}
|
|
2563
|
-
|
|
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
|
+
}
|
|
2564
3041
|
return cacheLlmTools(countUsage(
|
|
3042
|
+
// <- TODO: [๐ฏ] We dont use countUsage at all, maybe just unwrap it
|
|
2565
3043
|
// <- Note: for example here we don`t want the [๐ฏ]
|
|
2566
|
-
|
|
3044
|
+
llmExecutionTools), {
|
|
2567
3045
|
storage: new FileCacheStorage({ fs: $provideFilesystemForNode() }, {
|
|
2568
3046
|
rootFolderPath: join(process.cwd(), DEFAULT_EXECUTION_CACHE_DIRNAME),
|
|
2569
3047
|
}),
|
|
@@ -2579,31 +3057,88 @@ async function $provideLlmToolsForWizzardOrCli(options) {
|
|
|
2579
3057
|
*/
|
|
2580
3058
|
|
|
2581
3059
|
/**
|
|
2582
|
-
*
|
|
2583
|
-
* No side effects.
|
|
2584
|
-
*
|
|
2585
|
-
* Note: It can be usefull for:
|
|
2586
|
-
*
|
|
2587
|
-
* 1) Suppressing eager optimization of unused imports
|
|
2588
|
-
* 2) Suppressing eslint errors of unused variables in the tests
|
|
2589
|
-
* 3) Keeping the type of the variable for type testing
|
|
2590
|
-
*
|
|
2591
|
-
* @param value any values
|
|
2592
|
-
* @returns void
|
|
2593
|
-
* @private within the repository
|
|
2594
|
-
*/
|
|
2595
|
-
function keepUnused(...valuesToKeep) {
|
|
2596
|
-
}
|
|
2597
|
-
|
|
2598
|
-
/**
|
|
2599
|
-
* Just says that the variable is not used directlys but should be kept because the existence of the variable is important
|
|
2600
|
-
*
|
|
2601
|
-
* @param value any values
|
|
2602
|
-
* @returns void
|
|
2603
|
-
* @private within the repository
|
|
3060
|
+
* @private utility of CLI
|
|
2604
3061
|
*/
|
|
2605
|
-
function $
|
|
2606
|
-
|
|
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
|
+
}
|
|
2607
3142
|
}
|
|
2608
3143
|
|
|
2609
3144
|
/**
|
|
@@ -2620,8 +3155,10 @@ function $initializeListModelsCommand(program) {
|
|
|
2620
3155
|
`));
|
|
2621
3156
|
listModelsCommand.alias('models');
|
|
2622
3157
|
listModelsCommand.alias('llm');
|
|
2623
|
-
listModelsCommand.action(handleActionErrors(async () => {
|
|
2624
|
-
|
|
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 });
|
|
2625
3162
|
$sideEffect(llm);
|
|
2626
3163
|
// <- Note: Providing LLM tools will make a side effect of registering all available LLM tools to show the message
|
|
2627
3164
|
console.info($registeredLlmToolsMessage());
|
|
@@ -3181,49 +3718,73 @@ function $initializeListScrapersCommand(program) {
|
|
|
3181
3718
|
*/
|
|
3182
3719
|
|
|
3183
3720
|
/**
|
|
3184
|
-
*
|
|
3721
|
+
* Initializes `login` command for Promptbook CLI utilities
|
|
3185
3722
|
*
|
|
3186
|
-
* Note:
|
|
3723
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
3187
3724
|
*
|
|
3188
|
-
* @
|
|
3189
|
-
*/
|
|
3190
|
-
async function collectionToJson(collection) {
|
|
3191
|
-
const pipelineUrls = await collection.listPipelines();
|
|
3192
|
-
const promptbooks = await Promise.all(pipelineUrls.map((url) => collection.getPipelineByUrl(url)));
|
|
3193
|
-
return promptbooks;
|
|
3194
|
-
}
|
|
3195
|
-
/**
|
|
3196
|
-
* TODO: [๐ง ] Maybe clear `sourceFile` or clear when exposing through API or remote server
|
|
3725
|
+
* @private internal function of `promptbookCli`
|
|
3197
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.
|
|
3198
3757
|
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
super(message);
|
|
3207
|
-
this.name = 'ParseError';
|
|
3208
|
-
Object.setPrototypeOf(this, ParseError.prototype);
|
|
3209
|
-
}
|
|
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
|
+
}));
|
|
3210
3765
|
}
|
|
3211
3766
|
/**
|
|
3212
|
-
* 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`
|
|
3213
3771
|
*/
|
|
3214
3772
|
|
|
3215
3773
|
/**
|
|
3216
|
-
*
|
|
3774
|
+
* Converts PipelineCollection to serialized JSON
|
|
3775
|
+
*
|
|
3776
|
+
* Note: Functions `collectionToJson` and `createCollectionFromJson` are complementary
|
|
3217
3777
|
*
|
|
3218
3778
|
* @public exported from `@promptbook/core`
|
|
3219
3779
|
*/
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
Object.setPrototypeOf(this, PipelineLogicError.prototype);
|
|
3225
|
-
}
|
|
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;
|
|
3226
3784
|
}
|
|
3785
|
+
/**
|
|
3786
|
+
* TODO: [๐ง ] Maybe clear `sourceFile` or clear when exposing through API or remote server
|
|
3787
|
+
*/
|
|
3227
3788
|
|
|
3228
3789
|
/**
|
|
3229
3790
|
* Tests if given string is valid semantic version
|
|
@@ -3621,21 +4182,6 @@ async function loadArchive(filePath, fs) {
|
|
|
3621
4182
|
|
|
3622
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"}];
|
|
3623
4184
|
|
|
3624
|
-
/**
|
|
3625
|
-
* Checks if value is valid email
|
|
3626
|
-
*
|
|
3627
|
-
* @public exported from `@promptbook/utils`
|
|
3628
|
-
*/
|
|
3629
|
-
function isValidEmail(email) {
|
|
3630
|
-
if (typeof email !== 'string') {
|
|
3631
|
-
return false;
|
|
3632
|
-
}
|
|
3633
|
-
if (email.split('\n').length > 1) {
|
|
3634
|
-
return false;
|
|
3635
|
-
}
|
|
3636
|
-
return /^.+@.+\..+$/.test(email);
|
|
3637
|
-
}
|
|
3638
|
-
|
|
3639
4185
|
/**
|
|
3640
4186
|
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
3641
4187
|
*
|
|
@@ -3869,32 +4415,6 @@ function taskParameterJsonToString(taskParameterJson) {
|
|
|
3869
4415
|
* TODO: [๐ง ] Should be in generated .book.md file GENERATOR_WARNING
|
|
3870
4416
|
*/
|
|
3871
4417
|
|
|
3872
|
-
/**
|
|
3873
|
-
* This error indicates that promptbook not found in the collection
|
|
3874
|
-
*
|
|
3875
|
-
* @public exported from `@promptbook/core`
|
|
3876
|
-
*/
|
|
3877
|
-
class NotFoundError extends Error {
|
|
3878
|
-
constructor(message) {
|
|
3879
|
-
super(message);
|
|
3880
|
-
this.name = 'NotFoundError';
|
|
3881
|
-
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
3882
|
-
}
|
|
3883
|
-
}
|
|
3884
|
-
|
|
3885
|
-
/**
|
|
3886
|
-
* This error indicates errors in referencing promptbooks between each other
|
|
3887
|
-
*
|
|
3888
|
-
* @public exported from `@promptbook/core`
|
|
3889
|
-
*/
|
|
3890
|
-
class PipelineUrlError extends Error {
|
|
3891
|
-
constructor(message) {
|
|
3892
|
-
super(message);
|
|
3893
|
-
this.name = 'PipelineUrlError';
|
|
3894
|
-
Object.setPrototypeOf(this, PipelineUrlError.prototype);
|
|
3895
|
-
}
|
|
3896
|
-
}
|
|
3897
|
-
|
|
3898
4418
|
/**
|
|
3899
4419
|
* Parses the task and returns the list of all parameter names
|
|
3900
4420
|
*
|
|
@@ -4065,24 +4585,6 @@ function createCollectionFromJson(...promptbooks) {
|
|
|
4065
4585
|
return new SimplePipelineCollection(...promptbooks);
|
|
4066
4586
|
}
|
|
4067
4587
|
|
|
4068
|
-
/**
|
|
4069
|
-
* This error type indicates that some tools are missing for pipeline execution or preparation
|
|
4070
|
-
*
|
|
4071
|
-
* @public exported from `@promptbook/core`
|
|
4072
|
-
*/
|
|
4073
|
-
class MissingToolsError extends Error {
|
|
4074
|
-
constructor(message) {
|
|
4075
|
-
super(spaceTrim$1((block) => `
|
|
4076
|
-
${block(message)}
|
|
4077
|
-
|
|
4078
|
-
Note: You have probbably forgot to provide some tools for pipeline execution or preparation
|
|
4079
|
-
|
|
4080
|
-
`));
|
|
4081
|
-
this.name = 'MissingToolsError';
|
|
4082
|
-
Object.setPrototypeOf(this, MissingToolsError.prototype);
|
|
4083
|
-
}
|
|
4084
|
-
}
|
|
4085
|
-
|
|
4086
4588
|
/**
|
|
4087
4589
|
* Determine if the pipeline is fully prepared
|
|
4088
4590
|
*
|
|
@@ -4108,217 +4610,47 @@ function isPipelinePrepared(pipeline) {
|
|
|
4108
4610
|
> return false;
|
|
4109
4611
|
> }
|
|
4110
4612
|
*/
|
|
4111
|
-
return true;
|
|
4112
|
-
}
|
|
4113
|
-
/**
|
|
4114
|
-
* TODO: [๐][main] If the pipeline was prepared with different version or different set of models, prepare it once again
|
|
4115
|
-
* TODO: [๐ ] Maybe base this on `makeValidator`
|
|
4116
|
-
* TODO: [๐ง] Pipeline can be partially prepared, this should return true ONLY if fully prepared
|
|
4117
|
-
* TODO: [๐งฟ] Maybe do same process with same granularity and subfinctions as `preparePipeline`
|
|
4118
|
-
* - [๐] ? Is context in each task
|
|
4119
|
-
* - [โจ] Are examples prepared
|
|
4120
|
-
* - [โจ] Are tasks prepared
|
|
4121
|
-
*/
|
|
4122
|
-
|
|
4123
|
-
/**
|
|
4124
|
-
* Recursively converts JSON strings to JSON objects
|
|
4125
|
-
|
|
4126
|
-
* @public exported from `@promptbook/utils`
|
|
4127
|
-
*/
|
|
4128
|
-
function jsonStringsToJsons(object) {
|
|
4129
|
-
if (object === null) {
|
|
4130
|
-
return object;
|
|
4131
|
-
}
|
|
4132
|
-
if (Array.isArray(object)) {
|
|
4133
|
-
return object.map(jsonStringsToJsons);
|
|
4134
|
-
}
|
|
4135
|
-
if (typeof object !== 'object') {
|
|
4136
|
-
return object;
|
|
4137
|
-
}
|
|
4138
|
-
const newObject = { ...object };
|
|
4139
|
-
for (const [key, value] of Object.entries(object)) {
|
|
4140
|
-
if (typeof value === 'string' && isValidJsonString(value)) {
|
|
4141
|
-
newObject[key] = JSON.parse(value);
|
|
4142
|
-
}
|
|
4143
|
-
else {
|
|
4144
|
-
newObject[key] = jsonStringsToJsons(value);
|
|
4145
|
-
}
|
|
4146
|
-
}
|
|
4147
|
-
return newObject;
|
|
4148
|
-
}
|
|
4149
|
-
/**
|
|
4150
|
-
* TODO: Type the return type correctly
|
|
4151
|
-
*/
|
|
4152
|
-
|
|
4153
|
-
/**
|
|
4154
|
-
* This error indicates problems parsing the format value
|
|
4155
|
-
*
|
|
4156
|
-
* For example, when the format value is not a valid JSON or CSV
|
|
4157
|
-
* This is not thrown directly but in extended classes
|
|
4158
|
-
*
|
|
4159
|
-
* @public exported from `@promptbook/core`
|
|
4160
|
-
*/
|
|
4161
|
-
class AbstractFormatError extends Error {
|
|
4162
|
-
// Note: To allow instanceof do not put here error `name`
|
|
4163
|
-
// public readonly name = 'AbstractFormatError';
|
|
4164
|
-
constructor(message) {
|
|
4165
|
-
super(message);
|
|
4166
|
-
Object.setPrototypeOf(this, AbstractFormatError.prototype);
|
|
4167
|
-
}
|
|
4168
|
-
}
|
|
4169
|
-
|
|
4170
|
-
/**
|
|
4171
|
-
* This error indicates problem with parsing of CSV
|
|
4172
|
-
*
|
|
4173
|
-
* @public exported from `@promptbook/core`
|
|
4174
|
-
*/
|
|
4175
|
-
class CsvFormatError extends AbstractFormatError {
|
|
4176
|
-
constructor(message) {
|
|
4177
|
-
super(message);
|
|
4178
|
-
this.name = 'CsvFormatError';
|
|
4179
|
-
Object.setPrototypeOf(this, CsvFormatError.prototype);
|
|
4180
|
-
}
|
|
4181
|
-
}
|
|
4182
|
-
|
|
4183
|
-
/**
|
|
4184
|
-
* This error indicates that the pipeline collection cannot be propperly loaded
|
|
4185
|
-
*
|
|
4186
|
-
* @public exported from `@promptbook/core`
|
|
4187
|
-
*/
|
|
4188
|
-
class CollectionError extends Error {
|
|
4189
|
-
constructor(message) {
|
|
4190
|
-
super(message);
|
|
4191
|
-
this.name = 'CollectionError';
|
|
4192
|
-
Object.setPrototypeOf(this, CollectionError.prototype);
|
|
4193
|
-
}
|
|
4194
|
-
}
|
|
4195
|
-
|
|
4196
|
-
/**
|
|
4197
|
-
* This error occurs when some expectation is not met in the execution of the pipeline
|
|
4198
|
-
*
|
|
4199
|
-
* @public exported from `@promptbook/core`
|
|
4200
|
-
* Note: Do not throw this error, its reserved for `checkExpectations` and `createPipelineExecutor` and public ONLY to be serializable through remote server
|
|
4201
|
-
* Note: Always thrown in `checkExpectations` and catched in `createPipelineExecutor` and rethrown as `PipelineExecutionError`
|
|
4202
|
-
* Note: This is a kindof subtype of PipelineExecutionError
|
|
4203
|
-
*/
|
|
4204
|
-
class ExpectError extends Error {
|
|
4205
|
-
constructor(message) {
|
|
4206
|
-
super(message);
|
|
4207
|
-
this.name = 'ExpectError';
|
|
4208
|
-
Object.setPrototypeOf(this, ExpectError.prototype);
|
|
4209
|
-
}
|
|
4210
|
-
}
|
|
4211
|
-
|
|
4212
|
-
/**
|
|
4213
|
-
* This error indicates that the promptbook can not retrieve knowledge from external sources
|
|
4214
|
-
*
|
|
4215
|
-
* @public exported from `@promptbook/core`
|
|
4216
|
-
*/
|
|
4217
|
-
class KnowledgeScrapeError extends Error {
|
|
4218
|
-
constructor(message) {
|
|
4219
|
-
super(message);
|
|
4220
|
-
this.name = 'KnowledgeScrapeError';
|
|
4221
|
-
Object.setPrototypeOf(this, KnowledgeScrapeError.prototype);
|
|
4222
|
-
}
|
|
4223
|
-
}
|
|
4224
|
-
|
|
4225
|
-
/**
|
|
4226
|
-
* This error type indicates that some limit was reached
|
|
4227
|
-
*
|
|
4228
|
-
* @public exported from `@promptbook/core`
|
|
4229
|
-
*/
|
|
4230
|
-
class LimitReachedError extends Error {
|
|
4231
|
-
constructor(message) {
|
|
4232
|
-
super(message);
|
|
4233
|
-
this.name = 'LimitReachedError';
|
|
4234
|
-
Object.setPrototypeOf(this, LimitReachedError.prototype);
|
|
4235
|
-
}
|
|
4236
|
-
}
|
|
4237
|
-
|
|
4238
|
-
/**
|
|
4239
|
-
* Index of all custom errors
|
|
4240
|
-
*
|
|
4241
|
-
* @public exported from `@promptbook/core`
|
|
4242
|
-
*/
|
|
4243
|
-
const PROMPTBOOK_ERRORS = {
|
|
4244
|
-
AbstractFormatError,
|
|
4245
|
-
CsvFormatError,
|
|
4246
|
-
CollectionError,
|
|
4247
|
-
EnvironmentMismatchError,
|
|
4248
|
-
ExpectError,
|
|
4249
|
-
KnowledgeScrapeError,
|
|
4250
|
-
LimitReachedError,
|
|
4251
|
-
MissingToolsError,
|
|
4252
|
-
NotFoundError,
|
|
4253
|
-
NotYetImplementedError,
|
|
4254
|
-
ParseError,
|
|
4255
|
-
PipelineExecutionError,
|
|
4256
|
-
PipelineLogicError,
|
|
4257
|
-
PipelineUrlError,
|
|
4258
|
-
UnexpectedError,
|
|
4259
|
-
// TODO: [๐ช]> VersionMismatchError,
|
|
4260
|
-
};
|
|
4261
|
-
/**
|
|
4262
|
-
* Index of all javascript errors
|
|
4263
|
-
*
|
|
4264
|
-
* @private for internal usage
|
|
4265
|
-
*/
|
|
4266
|
-
const COMMON_JAVASCRIPT_ERRORS = {
|
|
4267
|
-
Error,
|
|
4268
|
-
EvalError,
|
|
4269
|
-
RangeError,
|
|
4270
|
-
ReferenceError,
|
|
4271
|
-
SyntaxError,
|
|
4272
|
-
TypeError,
|
|
4273
|
-
URIError,
|
|
4274
|
-
AggregateError,
|
|
4275
|
-
/*
|
|
4276
|
-
Note: Not widely supported
|
|
4277
|
-
> InternalError,
|
|
4278
|
-
> ModuleError,
|
|
4279
|
-
> HeapError,
|
|
4280
|
-
> WebAssemblyCompileError,
|
|
4281
|
-
> WebAssemblyRuntimeError,
|
|
4282
|
-
*/
|
|
4283
|
-
};
|
|
4284
|
-
/**
|
|
4285
|
-
* Index of all errors
|
|
4286
|
-
*
|
|
4287
|
-
* @private for internal usage
|
|
4288
|
-
*/
|
|
4289
|
-
const ALL_ERRORS = {
|
|
4290
|
-
...PROMPTBOOK_ERRORS,
|
|
4291
|
-
...COMMON_JAVASCRIPT_ERRORS,
|
|
4292
|
-
};
|
|
4613
|
+
return true;
|
|
4614
|
+
}
|
|
4293
4615
|
/**
|
|
4294
|
-
*
|
|
4616
|
+
* TODO: [๐][main] If the pipeline was prepared with different version or different set of models, prepare it once again
|
|
4617
|
+
* TODO: [๐ ] Maybe base this on `makeValidator`
|
|
4618
|
+
* TODO: [๐ง] Pipeline can be partially prepared, this should return true ONLY if fully prepared
|
|
4619
|
+
* TODO: [๐งฟ] Maybe do same process with same granularity and subfinctions as `preparePipeline`
|
|
4620
|
+
* - [๐] ? Is context in each task
|
|
4621
|
+
* - [โจ] Are examples prepared
|
|
4622
|
+
* - [โจ] Are tasks prepared
|
|
4295
4623
|
*/
|
|
4296
4624
|
|
|
4297
4625
|
/**
|
|
4298
|
-
*
|
|
4299
|
-
|
|
4626
|
+
* Recursively converts JSON strings to JSON objects
|
|
4627
|
+
|
|
4300
4628
|
* @public exported from `@promptbook/utils`
|
|
4301
4629
|
*/
|
|
4302
|
-
function
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
let ErrorClass = ALL_ERRORS[error.name];
|
|
4306
|
-
if (ErrorClass === undefined) {
|
|
4307
|
-
ErrorClass = Error;
|
|
4308
|
-
message = `${name}: ${message}`;
|
|
4630
|
+
function jsonStringsToJsons(object) {
|
|
4631
|
+
if (object === null) {
|
|
4632
|
+
return object;
|
|
4309
4633
|
}
|
|
4310
|
-
if (
|
|
4311
|
-
|
|
4312
|
-
${block(message)}
|
|
4313
|
-
|
|
4314
|
-
Original stack trace:
|
|
4315
|
-
${block(stack || '')}
|
|
4316
|
-
`);
|
|
4634
|
+
if (Array.isArray(object)) {
|
|
4635
|
+
return object.map(jsonStringsToJsons);
|
|
4317
4636
|
}
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
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;
|
|
4321
4650
|
}
|
|
4651
|
+
/**
|
|
4652
|
+
* TODO: Type the return type correctly
|
|
4653
|
+
*/
|
|
4322
4654
|
|
|
4323
4655
|
/**
|
|
4324
4656
|
* Asserts that the execution of a Promptbook is successful
|
|
@@ -4471,6 +4803,10 @@ function serializeError(error) {
|
|
|
4471
4803
|
|
|
4472
4804
|
Cannot serialize error with name "${name}"
|
|
4473
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
|
+
|
|
4474
4810
|
${block(stack || message)}
|
|
4475
4811
|
|
|
4476
4812
|
`));
|
|
@@ -11381,7 +11717,6 @@ function $initializeMakeCommand(program) {
|
|
|
11381
11717
|
makeCommand.option('--no-validation', `Do not validate logic of pipelines in collection`, true);
|
|
11382
11718
|
makeCommand.option('--validation', `Types of validations separated by comma (options "logic","imports")`, 'logic,imports');
|
|
11383
11719
|
makeCommand.option('-r, --reload', `Call LLM models even if same prompt with result is in the cache`, false);
|
|
11384
|
-
makeCommand.option('-v, --verbose', `Is output verbose`, false);
|
|
11385
11720
|
makeCommand.option('-o, --output <path>', spaceTrim(`
|
|
11386
11721
|
Where to save the builded collection
|
|
11387
11722
|
|
|
@@ -11395,7 +11730,8 @@ function $initializeMakeCommand(program) {
|
|
|
11395
11730
|
Note: This can be used only with "javascript" or "typescript" format
|
|
11396
11731
|
|
|
11397
11732
|
`), DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME);
|
|
11398
|
-
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;
|
|
11399
11735
|
if (!isValidJavascriptName(functionName)) {
|
|
11400
11736
|
console.error(colors.red(`Function name "${functionName}" is not valid javascript name`));
|
|
11401
11737
|
return process.exit(1);
|
|
@@ -11423,7 +11759,10 @@ function $initializeMakeCommand(program) {
|
|
|
11423
11759
|
isCacheReloaded,
|
|
11424
11760
|
}; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
|
|
11425
11761
|
const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
|
|
11426
|
-
const llm = await $
|
|
11762
|
+
const llm = await $provideLlmToolsForCli({
|
|
11763
|
+
cliOptions,
|
|
11764
|
+
...prepareAndScrapeOptions,
|
|
11765
|
+
});
|
|
11427
11766
|
const executables = await $provideExecutablesForNode(prepareAndScrapeOptions);
|
|
11428
11767
|
const tools = {
|
|
11429
11768
|
llm,
|
|
@@ -11691,8 +12030,8 @@ function $initializePrettifyCommand(program) {
|
|
|
11691
12030
|
// <- TODO: [๐งโโ๏ธ] Unite path to promptbook collection argument
|
|
11692
12031
|
'Pipelines to prettify as glob pattern');
|
|
11693
12032
|
prettifyCommand.option('-i, --ignore <glob>', `Ignore as glob pattern`);
|
|
11694
|
-
prettifyCommand.
|
|
11695
|
-
|
|
12033
|
+
prettifyCommand.action(handleActionErrors(async (filesGlob, cliOptions) => {
|
|
12034
|
+
const { ignore, verbose: isVerbose } = cliOptions;
|
|
11696
12035
|
const filenames = await glob(filesGlob, { ignore });
|
|
11697
12036
|
// <- TODO: [๐ถ]
|
|
11698
12037
|
for (const filename of filenames) {
|
|
@@ -12293,13 +12632,12 @@ function $initializeRunCommand(program) {
|
|
|
12293
12632
|
// TODO: [๐ง
] DRY command arguments
|
|
12294
12633
|
runCommand.argument('[pipelineSource]', 'Path to book file OR URL to book file, if not provided it will be asked');
|
|
12295
12634
|
runCommand.option('-r, --reload', `Call LLM models even if same prompt with result is in the cache`, false);
|
|
12296
|
-
runCommand.option('-v, --verbose', `Is output verbose`, false);
|
|
12297
|
-
runCommand.option('--no-interactive', `Input is not interactive, if true you need to pass all the input parameters through --json`);
|
|
12298
12635
|
runCommand.option('--no-formfactor', `When set, behavior of the interactive mode is not changed by the formfactor of the pipeline`);
|
|
12299
12636
|
runCommand.option('-j, --json <json>', `Pass all or some input parameters as JSON record, if used the output is also returned as JSON`);
|
|
12300
12637
|
runCommand.option('-s, --save-report <path>', `Save report to file`);
|
|
12301
|
-
runCommand.action(handleActionErrors(async (pipelineSource,
|
|
12302
|
-
|
|
12638
|
+
runCommand.action(handleActionErrors(async (pipelineSource, cliOptions) => {
|
|
12639
|
+
console.log('!!!', cliOptions);
|
|
12640
|
+
const { reload: isCacheReloaded, interactive: isInteractive, formfactor: isFormfactorUsed, json, verbose: isVerbose, saveReport, } = cliOptions;
|
|
12303
12641
|
if (pipelineSource.includes('-') && normalizeToKebabCase(pipelineSource) === pipelineSource) {
|
|
12304
12642
|
console.error(colors.red(`""${pipelineSource}" is not a valid command or book. See 'ptbk --help'.`));
|
|
12305
12643
|
return process.exit(1);
|
|
@@ -12324,7 +12662,7 @@ function $initializeRunCommand(program) {
|
|
|
12324
12662
|
const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
|
|
12325
12663
|
let llm;
|
|
12326
12664
|
try {
|
|
12327
|
-
llm = await $
|
|
12665
|
+
llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
|
|
12328
12666
|
}
|
|
12329
12667
|
catch (error) {
|
|
12330
12668
|
if (!(error instanceof Error)) {
|
|
@@ -12381,7 +12719,7 @@ function $initializeRunCommand(program) {
|
|
|
12381
12719
|
fs,
|
|
12382
12720
|
fetch: scraperFetch,
|
|
12383
12721
|
scrapers: await $provideScrapersForNode({ fs, llm, executables }, prepareAndScrapeOptions),
|
|
12384
|
-
script: [new JavascriptExecutionTools(
|
|
12722
|
+
script: [new JavascriptExecutionTools(cliOptions)],
|
|
12385
12723
|
};
|
|
12386
12724
|
if (isVerbose) {
|
|
12387
12725
|
console.info(colors.gray('--- Getting the book ---'));
|
|
@@ -12545,11 +12883,12 @@ function $initializeRunCommand(program) {
|
|
|
12545
12883
|
* @public exported from `@promptbook/remote-server`
|
|
12546
12884
|
*/
|
|
12547
12885
|
function startRemoteServer(options) {
|
|
12548
|
-
const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, } = {
|
|
12886
|
+
const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
|
|
12549
12887
|
isAnonymousModeAllowed: false,
|
|
12550
12888
|
isApplicationModeAllowed: false,
|
|
12551
12889
|
collection: null,
|
|
12552
12890
|
createLlmExecutionTools: null,
|
|
12891
|
+
login: null,
|
|
12553
12892
|
...options,
|
|
12554
12893
|
};
|
|
12555
12894
|
// <- TODO: [๐ฆช] Some helper type to be able to use discriminant union types with destructuring
|
|
@@ -12627,13 +12966,14 @@ function startRemoteServer(options) {
|
|
|
12627
12966
|
servers: [
|
|
12628
12967
|
{
|
|
12629
12968
|
url: `http://localhost:${port}${rootPath}`,
|
|
12969
|
+
// <- TODO: !!!!! Probbably: Pass `remoteServerUrl` instead of `port` and `rootPath`
|
|
12630
12970
|
},
|
|
12631
12971
|
],
|
|
12632
12972
|
},
|
|
12633
12973
|
apis: ['./src/remote-server/**/*.ts'], // Adjust path as needed
|
|
12634
12974
|
};
|
|
12635
12975
|
const swaggerSpec = swaggerJsdoc(swaggerOptions);
|
|
12636
|
-
app.use(`${rootPath}/api-docs
|
|
12976
|
+
app.use([`/api-docs`, `${rootPath}/api-docs`], swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
12637
12977
|
const runningExecutionTasks = [];
|
|
12638
12978
|
// <- TODO: [๐คฌ] Identify the users
|
|
12639
12979
|
// TODO: [๐ง ] Do here some garbage collection of finished tasks
|
|
@@ -12683,9 +13023,12 @@ function startRemoteServer(options) {
|
|
|
12683
13023
|
|
|
12684
13024
|
## Paths
|
|
12685
13025
|
|
|
12686
|
-
${block(
|
|
12687
|
-
.
|
|
12688
|
-
|
|
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
|
+
]
|
|
12689
13032
|
.map((path) => `- ${path}`)
|
|
12690
13033
|
.join('\n'))}
|
|
12691
13034
|
|
|
@@ -12703,7 +13046,64 @@ function startRemoteServer(options) {
|
|
|
12703
13046
|
https://github.com/webgptorg/promptbook
|
|
12704
13047
|
`));
|
|
12705
13048
|
});
|
|
12706
|
-
|
|
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
|
+
});
|
|
12707
13107
|
/**
|
|
12708
13108
|
* @swagger
|
|
12709
13109
|
* /books:
|
|
@@ -12720,7 +13120,7 @@ function startRemoteServer(options) {
|
|
|
12720
13120
|
* items:
|
|
12721
13121
|
* type: string
|
|
12722
13122
|
*/
|
|
12723
|
-
app.get(`${rootPath}/books
|
|
13123
|
+
app.get([`/books`, `${rootPath}/books`], async (request, response) => {
|
|
12724
13124
|
if (collection === null) {
|
|
12725
13125
|
response.status(500).send('No collection available');
|
|
12726
13126
|
return;
|
|
@@ -12753,7 +13153,7 @@ function startRemoteServer(options) {
|
|
|
12753
13153
|
* 404:
|
|
12754
13154
|
* description: Book not found.
|
|
12755
13155
|
*/
|
|
12756
|
-
app.get(`${rootPath}/books
|
|
13156
|
+
app.get([`/books/*`, `${rootPath}/books/*`], async (request, response) => {
|
|
12757
13157
|
try {
|
|
12758
13158
|
if (collection === null) {
|
|
12759
13159
|
response.status(500).send('No collection nor books available');
|
|
@@ -12823,10 +13223,10 @@ function startRemoteServer(options) {
|
|
|
12823
13223
|
* items:
|
|
12824
13224
|
* type: object
|
|
12825
13225
|
*/
|
|
12826
|
-
app.get(`${rootPath}/executions
|
|
13226
|
+
app.get([`/executions`, `${rootPath}/executions`], async (request, response) => {
|
|
12827
13227
|
response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)));
|
|
12828
13228
|
});
|
|
12829
|
-
app.get(`${rootPath}/executions/last
|
|
13229
|
+
app.get([`/executions/last`, `${rootPath}/executions/last`], async (request, response) => {
|
|
12830
13230
|
// TODO: [๐คฌ] Filter only for user
|
|
12831
13231
|
if (runningExecutionTasks.length === 0) {
|
|
12832
13232
|
response.status(404).send('No execution tasks found');
|
|
@@ -12835,7 +13235,7 @@ function startRemoteServer(options) {
|
|
|
12835
13235
|
const lastExecutionTask = runningExecutionTasks[runningExecutionTasks.length - 1];
|
|
12836
13236
|
response.send(exportExecutionTask(lastExecutionTask, true));
|
|
12837
13237
|
});
|
|
12838
|
-
app.get(`${rootPath}/executions/:taskId
|
|
13238
|
+
app.get([`/executions/:taskId`, `${rootPath}/executions/:taskId`], async (request, response) => {
|
|
12839
13239
|
const { taskId } = request.params;
|
|
12840
13240
|
// TODO: [๐คฌ] Filter only for user
|
|
12841
13241
|
const executionTask = runningExecutionTasks.find((executionTask) => executionTask.taskId === taskId);
|
|
@@ -12876,7 +13276,7 @@ function startRemoteServer(options) {
|
|
|
12876
13276
|
* 400:
|
|
12877
13277
|
* description: Invalid input.
|
|
12878
13278
|
*/
|
|
12879
|
-
app.post(`${rootPath}/executions/new
|
|
13279
|
+
app.post([`/executions/new`, `${rootPath}/executions/new`], async (request, response) => {
|
|
12880
13280
|
try {
|
|
12881
13281
|
const { inputParameters, identification /* <- [๐คฌ] */ } = request.body;
|
|
12882
13282
|
const pipelineUrl = request.body.pipelineUrl || request.body.book;
|
|
@@ -13112,12 +13512,12 @@ function $initializeStartServerCommand(program) {
|
|
|
13112
13512
|
`));
|
|
13113
13513
|
startServerCommand.option('--allow-anonymous', `Is anonymous mode allowed`, false);
|
|
13114
13514
|
startServerCommand.option('-r, --reload', `Call LLM models even if same prompt with result is in the cache`, false);
|
|
13115
|
-
startServerCommand.option('-v, --verbose', `Is output verbose`, false);
|
|
13116
13515
|
startServerCommand.description(spaceTrim(`
|
|
13117
13516
|
Starts a remote server to execute books
|
|
13118
13517
|
`));
|
|
13119
13518
|
startServerCommand.alias('server');
|
|
13120
|
-
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;
|
|
13121
13521
|
if (rawUrl && !isValidUrl(rawUrl)) {
|
|
13122
13522
|
console.error(colors.red(`Invalid URL: ${rawUrl}`));
|
|
13123
13523
|
return process.exit(1);
|
|
@@ -13146,7 +13546,7 @@ function $initializeStartServerCommand(program) {
|
|
|
13146
13546
|
isCacheReloaded,
|
|
13147
13547
|
}; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
|
|
13148
13548
|
const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
|
|
13149
|
-
const llm = await $
|
|
13549
|
+
const llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
|
|
13150
13550
|
const executables = await $provideExecutablesForNode(prepareAndScrapeOptions);
|
|
13151
13551
|
const tools = {
|
|
13152
13552
|
llm,
|
|
@@ -13170,6 +13570,9 @@ function $initializeStartServerCommand(program) {
|
|
|
13170
13570
|
isAnonymousModeAllowed,
|
|
13171
13571
|
isApplicationModeAllowed: true,
|
|
13172
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
|
+
},
|
|
13173
13576
|
createLlmExecutionTools(options) {
|
|
13174
13577
|
const { appId, userId } = options;
|
|
13175
13578
|
TODO_USE({ appId, userId });
|
|
@@ -13206,8 +13609,8 @@ function $initializeTestCommand(program) {
|
|
|
13206
13609
|
testCommand.option('--no-validation', `Do not validate logic of pipelines in collection`, true);
|
|
13207
13610
|
testCommand.option('--no-prepare', `Do not prepare the pipelines, ideal when no LLM tools or scrapers available`, true);
|
|
13208
13611
|
testCommand.option('-r, --reload', `Call LLM models even if same prompt with result is in the cache `, false);
|
|
13209
|
-
testCommand.
|
|
13210
|
-
|
|
13612
|
+
testCommand.action(handleActionErrors(async (filesGlob, cliOptions) => {
|
|
13613
|
+
const { ignore: ignoreRaw = '', validation: isValidated, prepare: isPrepared, reload: isCacheReloaded, verbose: isVerbose, } = cliOptions;
|
|
13211
13614
|
let tools = undefined;
|
|
13212
13615
|
if (isPrepared) {
|
|
13213
13616
|
// TODO: DRY [โฝ]
|
|
@@ -13216,7 +13619,7 @@ function $initializeTestCommand(program) {
|
|
|
13216
13619
|
isCacheReloaded,
|
|
13217
13620
|
}; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
|
|
13218
13621
|
const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
|
|
13219
|
-
const llm = await $
|
|
13622
|
+
const llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
|
|
13220
13623
|
const executables = await $provideExecutablesForNode(prepareAndScrapeOptions);
|
|
13221
13624
|
tools = {
|
|
13222
13625
|
llm,
|
|
@@ -13277,56 +13680,16 @@ function $initializeTestCommand(program) {
|
|
|
13277
13680
|
*/
|
|
13278
13681
|
|
|
13279
13682
|
/**
|
|
13280
|
-
*
|
|
13281
|
-
*
|
|
13282
|
-
* 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
|
|
13283
13684
|
*
|
|
13284
|
-
* @private
|
|
13685
|
+
* @private utility of CLI
|
|
13285
13686
|
*/
|
|
13286
|
-
function $
|
|
13287
|
-
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
loginCommand.action(handleActionErrors(async () => {
|
|
13292
|
-
// @@@
|
|
13293
|
-
console.error(colors.green(spaceTrim(`
|
|
13294
|
-
You will be logged in to https://promptbook.studio server.
|
|
13295
|
-
If you don't have an account, it will be created automatically.
|
|
13296
|
-
`)));
|
|
13297
|
-
const { email, password } = await prompts([
|
|
13298
|
-
{
|
|
13299
|
-
type: 'text',
|
|
13300
|
-
name: 'email',
|
|
13301
|
-
message: 'Enter your email:',
|
|
13302
|
-
validate: (value) => (isValidEmail(value) ? true : 'Valid email is required'),
|
|
13303
|
-
},
|
|
13304
|
-
{
|
|
13305
|
-
type: 'password',
|
|
13306
|
-
name: 'password',
|
|
13307
|
-
message: 'Enter your password:',
|
|
13308
|
-
validate: (value) => value.length /* <- TODO: [๐ง ] Better password validation */ > 0 ? true : 'Password is required',
|
|
13309
|
-
},
|
|
13310
|
-
]);
|
|
13311
|
-
TODO_USE(email, password);
|
|
13312
|
-
await forTime(1000);
|
|
13313
|
-
console.error(colors.green(spaceTrim(`
|
|
13314
|
-
Your account ${email} was successfully created.
|
|
13315
|
-
|
|
13316
|
-
Please verify your email:
|
|
13317
|
-
https://brj.app/api/v1/customer/register-account?apiKey=PRODdh003eNKaec7PoO1AzU244tsL4WO
|
|
13318
|
-
|
|
13319
|
-
After verification, you will receive 500 000 credits for free ๐
|
|
13320
|
-
`)));
|
|
13321
|
-
return process.exit(0);
|
|
13322
|
-
}));
|
|
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);
|
|
13323
13692
|
}
|
|
13324
|
-
/**
|
|
13325
|
-
* TODO: Pass remote server URL (and path)
|
|
13326
|
-
* TODO: Implement non-interactive login
|
|
13327
|
-
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
13328
|
-
* Note: [๐ก] Code in this file should never be published outside of `@promptbook/cli`
|
|
13329
|
-
*/
|
|
13330
13693
|
|
|
13331
13694
|
/**
|
|
13332
13695
|
* Runs CLI utilities of Promptbook package
|
|
@@ -13352,6 +13715,7 @@ async function promptbookCli() {
|
|
|
13352
13715
|
program.alias('ptbk');
|
|
13353
13716
|
program.version(PROMPTBOOK_ENGINE_VERSION);
|
|
13354
13717
|
program.description(CLAIM);
|
|
13718
|
+
// Note: Theese options are valid for all commands
|
|
13355
13719
|
$initializeAboutCommand(program);
|
|
13356
13720
|
$initializeRunCommand(program);
|
|
13357
13721
|
$initializeLoginCommand(program);
|
|
@@ -13362,6 +13726,8 @@ async function promptbookCli() {
|
|
|
13362
13726
|
$initializeListModelsCommand(program);
|
|
13363
13727
|
$initializeListScrapersCommand(program);
|
|
13364
13728
|
$initializeStartServerCommand(program);
|
|
13729
|
+
// TODO: [๐ง ] Should it be here or not> $addGlobalOptionsToCommand(program);
|
|
13730
|
+
program.commands.forEach($addGlobalOptionsToCommand);
|
|
13365
13731
|
program.parse(process.argv);
|
|
13366
13732
|
}
|
|
13367
13733
|
/**
|
|
@@ -13411,8 +13777,7 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
|
|
|
13411
13777
|
options: {
|
|
13412
13778
|
apiKey: 'sk-ant-api03-',
|
|
13413
13779
|
isProxied: true,
|
|
13414
|
-
|
|
13415
|
-
path: DEFAULT_REMOTE_URL_PATH,
|
|
13780
|
+
remoteServerUrl: DEFAULT_REMOTE_SERVER_URL,
|
|
13416
13781
|
},
|
|
13417
13782
|
};
|
|
13418
13783
|
},
|
|
@@ -13435,146 +13800,6 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
|
|
|
13435
13800
|
* Note: [๐] Ignore a discrepancy between file name and entity name
|
|
13436
13801
|
*/
|
|
13437
13802
|
|
|
13438
|
-
/**
|
|
13439
|
-
* Creates a connection to the remote proxy server.
|
|
13440
|
-
*
|
|
13441
|
-
* Note: This function creates a connection to the remote server and returns a socket but responsibility of closing the connection is on the caller
|
|
13442
|
-
*
|
|
13443
|
-
* @private internal utility function
|
|
13444
|
-
*/
|
|
13445
|
-
async function createRemoteClient(options) {
|
|
13446
|
-
const { remoteUrl, path } = options;
|
|
13447
|
-
return new Promise((resolve, reject) => {
|
|
13448
|
-
const socket = io(remoteUrl, {
|
|
13449
|
-
retries: CONNECTION_RETRIES_LIMIT,
|
|
13450
|
-
timeout: CONNECTION_TIMEOUT_MS,
|
|
13451
|
-
path,
|
|
13452
|
-
// path: `${this.remoteUrl.pathname}/socket.io`,
|
|
13453
|
-
transports: [/*'websocket', <- TODO: [๐ฌ] Make websocket transport work */ 'polling'],
|
|
13454
|
-
});
|
|
13455
|
-
// console.log('Connecting to', this.options.remoteUrl.href, { socket });
|
|
13456
|
-
socket.on('connect', () => {
|
|
13457
|
-
resolve(socket);
|
|
13458
|
-
});
|
|
13459
|
-
// TODO: [๐ฉ] Better timeout handling
|
|
13460
|
-
setTimeout(() => {
|
|
13461
|
-
reject(new Error(`Timeout while connecting to ${remoteUrl}`));
|
|
13462
|
-
}, CONNECTION_TIMEOUT_MS);
|
|
13463
|
-
});
|
|
13464
|
-
}
|
|
13465
|
-
|
|
13466
|
-
/**
|
|
13467
|
-
* Remote server is a proxy server that uses its execution tools internally and exposes the executor interface externally.
|
|
13468
|
-
*
|
|
13469
|
-
* You can simply use `RemoteExecutionTools` on client-side javascript and connect to your remote server.
|
|
13470
|
-
* This is useful to make all logic on browser side but not expose your API keys or no need to use customer's GPU.
|
|
13471
|
-
*
|
|
13472
|
-
* @see https://github.com/webgptorg/promptbook#remote-server
|
|
13473
|
-
* @public exported from `@promptbook/remote-client`
|
|
13474
|
-
*/
|
|
13475
|
-
class RemoteLlmExecutionTools {
|
|
13476
|
-
/* <- TODO: [๐] `, Destroyable` */
|
|
13477
|
-
constructor(options) {
|
|
13478
|
-
this.options = options;
|
|
13479
|
-
}
|
|
13480
|
-
get title() {
|
|
13481
|
-
// TODO: [๐ง ] Maybe fetch title+description from the remote server (as well as if model methods are defined)
|
|
13482
|
-
return 'Remote server';
|
|
13483
|
-
}
|
|
13484
|
-
get description() {
|
|
13485
|
-
return 'Use all models by your remote server';
|
|
13486
|
-
}
|
|
13487
|
-
/**
|
|
13488
|
-
* Check the configuration of all execution tools
|
|
13489
|
-
*/
|
|
13490
|
-
async checkConfiguration() {
|
|
13491
|
-
const socket = await createRemoteClient(this.options);
|
|
13492
|
-
socket.disconnect();
|
|
13493
|
-
// TODO: [main] !!3 Check version of the remote server and compatibility
|
|
13494
|
-
// TODO: [๐] Send checkConfiguration
|
|
13495
|
-
}
|
|
13496
|
-
/**
|
|
13497
|
-
* List all available models that can be used
|
|
13498
|
-
*/
|
|
13499
|
-
async listModels() {
|
|
13500
|
-
// TODO: [๐] Listing models (and checking configuration) probbably should go through REST API not Socket.io
|
|
13501
|
-
const socket = await createRemoteClient(this.options);
|
|
13502
|
-
socket.emit('listModels-request', {
|
|
13503
|
-
identification: this.options.identification,
|
|
13504
|
-
} /* <- Note: [๐ค] */);
|
|
13505
|
-
const promptResult = await new Promise((resolve, reject) => {
|
|
13506
|
-
socket.on('listModels-response', (response) => {
|
|
13507
|
-
resolve(response.models);
|
|
13508
|
-
socket.disconnect();
|
|
13509
|
-
});
|
|
13510
|
-
socket.on('error', (error) => {
|
|
13511
|
-
reject(deserializeError(error));
|
|
13512
|
-
socket.disconnect();
|
|
13513
|
-
});
|
|
13514
|
-
});
|
|
13515
|
-
socket.disconnect();
|
|
13516
|
-
return promptResult;
|
|
13517
|
-
}
|
|
13518
|
-
/**
|
|
13519
|
-
* Calls remote proxy server to use a chat model
|
|
13520
|
-
*/
|
|
13521
|
-
callChatModel(prompt) {
|
|
13522
|
-
if (this.options.isVerbose) {
|
|
13523
|
-
console.info(`๐ Remote callChatModel call`);
|
|
13524
|
-
}
|
|
13525
|
-
return /* not await */ this.callCommonModel(prompt);
|
|
13526
|
-
}
|
|
13527
|
-
/**
|
|
13528
|
-
* Calls remote proxy server to use a completion model
|
|
13529
|
-
*/
|
|
13530
|
-
callCompletionModel(prompt) {
|
|
13531
|
-
if (this.options.isVerbose) {
|
|
13532
|
-
console.info(`๐ฌ Remote callCompletionModel call`);
|
|
13533
|
-
}
|
|
13534
|
-
return /* not await */ this.callCommonModel(prompt);
|
|
13535
|
-
}
|
|
13536
|
-
/**
|
|
13537
|
-
* Calls remote proxy server to use a embedding model
|
|
13538
|
-
*/
|
|
13539
|
-
callEmbeddingModel(prompt) {
|
|
13540
|
-
if (this.options.isVerbose) {
|
|
13541
|
-
console.info(`๐ฌ Remote callEmbeddingModel call`);
|
|
13542
|
-
}
|
|
13543
|
-
return /* not await */ this.callCommonModel(prompt);
|
|
13544
|
-
}
|
|
13545
|
-
// <- Note: [๐ค] callXxxModel
|
|
13546
|
-
/**
|
|
13547
|
-
* Calls remote proxy server to use both completion or chat model
|
|
13548
|
-
*/
|
|
13549
|
-
async callCommonModel(prompt) {
|
|
13550
|
-
const socket = await createRemoteClient(this.options);
|
|
13551
|
-
socket.emit('prompt-request', {
|
|
13552
|
-
identification: this.options.identification,
|
|
13553
|
-
prompt,
|
|
13554
|
-
} /* <- Note: [๐ค] */);
|
|
13555
|
-
const promptResult = await new Promise((resolve, reject) => {
|
|
13556
|
-
socket.on('prompt-response', (response) => {
|
|
13557
|
-
resolve(response.promptResult);
|
|
13558
|
-
socket.disconnect();
|
|
13559
|
-
});
|
|
13560
|
-
socket.on('error', (error) => {
|
|
13561
|
-
reject(deserializeError(error));
|
|
13562
|
-
socket.disconnect();
|
|
13563
|
-
});
|
|
13564
|
-
});
|
|
13565
|
-
socket.disconnect();
|
|
13566
|
-
return promptResult;
|
|
13567
|
-
}
|
|
13568
|
-
}
|
|
13569
|
-
/**
|
|
13570
|
-
* TODO: Maybe use `$exportJson`
|
|
13571
|
-
* TODO: [๐ง ][๐] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`
|
|
13572
|
-
* TODO: [๐] Allow to list compatible models with each variant
|
|
13573
|
-
* TODO: [๐ฏ] RemoteLlmExecutionTools should extend Destroyable and implement IDestroyable
|
|
13574
|
-
* TODO: [๐ง ][๐ฐ] Allow to pass `title` for tracking purposes
|
|
13575
|
-
* TODO: [๐ง ] Maybe remove `@promptbook/remote-client` and just use `@promptbook/core`
|
|
13576
|
-
*/
|
|
13577
|
-
|
|
13578
13803
|
/**
|
|
13579
13804
|
* Function computeUsage will create price per one token based on the string value found on openai page
|
|
13580
13805
|
*
|