@promptbook/cli 0.89.0-9 → 0.89.0

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.
Files changed (40) hide show
  1. package/README.md +9 -11
  2. package/esm/index.es.js +925 -616
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/servers.d.ts +40 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +8 -4
  6. package/esm/typings/src/_packages/types.index.d.ts +18 -0
  7. package/esm/typings/src/_packages/utils.index.d.ts +4 -0
  8. package/esm/typings/src/cli/cli-commands/login.d.ts +0 -1
  9. package/esm/typings/src/cli/common/$provideLlmToolsForCli.d.ts +16 -3
  10. package/esm/typings/src/cli/test/ptbk.d.ts +1 -1
  11. package/esm/typings/src/commands/EXPECT/expectCommandParser.d.ts +2 -0
  12. package/esm/typings/src/config.d.ts +10 -19
  13. package/esm/typings/src/errors/0-index.d.ts +7 -4
  14. package/esm/typings/src/errors/PipelineExecutionError.d.ts +1 -1
  15. package/esm/typings/src/errors/WrappedError.d.ts +10 -0
  16. package/esm/typings/src/errors/assertsError.d.ts +11 -0
  17. package/esm/typings/src/execution/PromptbookFetch.d.ts +1 -1
  18. package/esm/typings/src/formats/csv/utils/isValidCsvString.d.ts +9 -0
  19. package/esm/typings/src/formats/csv/utils/isValidCsvString.test.d.ts +1 -0
  20. package/esm/typings/src/formats/json/utils/isValidJsonString.d.ts +3 -0
  21. package/esm/typings/src/formats/xml/utils/isValidXmlString.d.ts +9 -0
  22. package/esm/typings/src/formats/xml/utils/isValidXmlString.test.d.ts +1 -0
  23. package/esm/typings/src/llm-providers/_common/register/{$provideEnvFilepath.d.ts → $provideEnvFilename.d.ts} +2 -2
  24. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  25. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +1 -1
  26. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizzardOrCli.d.ts +11 -2
  27. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  28. package/esm/typings/src/remote-server/openapi-types.d.ts +284 -0
  29. package/esm/typings/src/remote-server/openapi.d.ts +187 -0
  30. package/esm/typings/src/remote-server/socket-types/_subtypes/Identification.d.ts +7 -1
  31. package/esm/typings/src/remote-server/socket-types/_subtypes/identificationToPromptbookToken.d.ts +11 -0
  32. package/esm/typings/src/remote-server/socket-types/_subtypes/promptbookTokenToIdentification.d.ts +10 -0
  33. package/esm/typings/src/remote-server/startRemoteServer.d.ts +1 -2
  34. package/esm/typings/src/remote-server/types/RemoteServerOptions.d.ts +15 -9
  35. package/esm/typings/src/storage/env-storage/$EnvStorage.d.ts +40 -0
  36. package/esm/typings/src/types/typeAliases.d.ts +26 -0
  37. package/package.json +15 -11
  38. package/umd/index.umd.js +929 -620
  39. package/umd/index.umd.js.map +1 -1
  40. package/esm/typings/src/cli/test/ptbk2.d.ts +0 -5
package/esm/index.es.js CHANGED
@@ -3,14 +3,14 @@ import commander from 'commander';
3
3
  import spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
4
  import { forTime, forEver } from 'waitasecond';
5
5
  import prompts from 'prompts';
6
- import { basename, join, dirname, relative } from 'path';
6
+ import { join, basename, dirname, relative } from 'path';
7
7
  import { stat, access, constants, readFile, writeFile, readdir, mkdir, unlink, rm, rename, rmdir } from 'fs/promises';
8
+ import * as dotenv from 'dotenv';
8
9
  import hexEncoder from 'crypto-js/enc-hex';
9
10
  import sha256 from 'crypto-js/sha256';
10
11
  import { randomBytes } from 'crypto';
11
12
  import { io } from 'socket.io-client';
12
13
  import { Subject } from 'rxjs';
13
- import * as dotenv from 'dotenv';
14
14
  import { spawn } from 'child_process';
15
15
  import JSZip from 'jszip';
16
16
  import { format } from 'prettier';
@@ -23,7 +23,7 @@ import moment from 'moment';
23
23
  import express from 'express';
24
24
  import http from 'http';
25
25
  import { Server } from 'socket.io';
26
- import swaggerJsdoc from 'swagger-jsdoc';
26
+ import * as OpenApiValidator from 'express-openapi-validator';
27
27
  import swaggerUi from 'swagger-ui-express';
28
28
  import Anthropic from '@anthropic-ai/sdk';
29
29
  import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
@@ -46,12 +46,43 @@ 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-9';
49
+ const PROMPTBOOK_ENGINE_VERSION = '0.89.0';
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
53
53
  */
54
54
 
55
+ /**
56
+ * Available remote servers for the Promptbook
57
+ *
58
+ * @public exported from `@promptbook/core`
59
+ */
60
+ const REMOTE_SERVER_URLS = [
61
+ {
62
+ title: 'Promptbook',
63
+ description: `Servers of Promptbook.studio`,
64
+ owner: 'AI Web, LLC <legal@ptbk.io> (https://www.ptbk.io/)',
65
+ isAnonymousModeAllowed: true,
66
+ urls: [
67
+ 'https://promptbook.s5.ptbk.io/',
68
+ // Note: Servers 1-4 are not running
69
+ ],
70
+ },
71
+ /*
72
+ Note: Working on older version of Promptbook and not supported anymore
73
+ {
74
+ title: 'Pavol Promptbook Server',
75
+ description: `Personal server of Pavol Hejný with simple testing server, DO NOT USE IT FOR PRODUCTION`,
76
+ owner: 'Pavol Hejný <pavol@ptbk.io> (https://www.pavolhejny.com/)',
77
+ isAnonymousModeAllowed: true,
78
+ urls: ['https://api.pavolhejny.com/promptbook'],
79
+ },
80
+ */
81
+ ];
82
+ /**
83
+ * Note: [💞] Ignore a discrepancy between file name and entity name
84
+ */
85
+
55
86
  /**
56
87
  * Returns the same value that is passed as argument.
57
88
  * No side effects.
@@ -106,6 +137,7 @@ const ADMIN_GITHUB_NAME = 'hejny';
106
137
  * @public exported from `@promptbook/core`
107
138
  */
108
139
  const CLAIM = `It's time for a paradigm shift. The future of software in plain English, French or Latin`;
140
+ // <- TODO: [🐊] Pick the best claim
109
141
  /**
110
142
  * When the title is not provided, the default title is used
111
143
  *
@@ -136,6 +168,12 @@ const DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
136
168
  * @private within the repository
137
169
  */
138
170
  const GENERATOR_WARNING_BY_PROMPTBOOK_CLI = `⚠️ WARNING: This code has been generated by \`@promptbook/cli\` so that any manual changes will be overwritten`;
171
+ /**
172
+ * Warning message for the automatically generated sections of `.env` files
173
+ *
174
+ * @private within the repository
175
+ */
176
+ const GENERATOR_WARNING_IN_ENV = `Note: Added by Promptbook`;
139
177
  // <- TODO: [🧠] Better system for generator warnings - not always "code" and "by `@promptbook/cli`"
140
178
  /**
141
179
  * The maximum number of iterations for a loops
@@ -156,6 +194,7 @@ const VALUE_STRINGS = {
156
194
  infinity: '(infinity; ∞)',
157
195
  negativeInfinity: '(negative infinity; -∞)',
158
196
  unserializable: '(unserializable value)',
197
+ circular: '(circular JSON)',
159
198
  };
160
199
  /**
161
200
  * Small number limit
@@ -215,7 +254,7 @@ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [🤹‍♂️]
215
254
  */
216
255
  const DEFAULT_BOOKS_DIRNAME = './books';
217
256
  // <- TODO: [🕝] Make also `BOOKS_DIRNAME_ALTERNATIVES`
218
- // TODO: !!!!!! Just .promptbook dir, hardocode others
257
+ // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
219
258
  /**
220
259
  * Where to store the temporary downloads
221
260
  *
@@ -271,11 +310,11 @@ const MOMENT_ARG_THRESHOLDS = {
271
310
  ss: 3, // <- least number of seconds to be counted in seconds, minus 1. Must be set after setting the `s` unit or without setting the `s` unit.
272
311
  };
273
312
  /**
274
- * @@@
313
+ * Default remote server URL for the Promptbook
275
314
  *
276
315
  * @public exported from `@promptbook/core`
277
316
  */
278
- const DEFAULT_REMOTE_SERVER_URL = 'https://api.pavolhejny.com/promptbook';
317
+ const DEFAULT_REMOTE_SERVER_URL = REMOTE_SERVER_URLS[0].urls[0];
279
318
  // <- TODO: [🧜‍♂️]
280
319
  /**
281
320
  * @@@
@@ -409,6 +448,122 @@ const $isRunningInWebWorker = new Function(`
409
448
  * TODO: [🎺]
410
449
  */
411
450
 
451
+ /**
452
+ * Make error report URL for the given error
453
+ *
454
+ * @private private within the repository
455
+ */
456
+ function getErrorReportUrl(error) {
457
+ const report = {
458
+ title: `🐜 Error report from ${NAME}`,
459
+ body: spaceTrim((block) => `
460
+
461
+
462
+ \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
463
+
464
+ \`\`\`
465
+ ${block(error.message || '(no error message)')}
466
+ \`\`\`
467
+
468
+
469
+ ## More info:
470
+
471
+ - **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
472
+ - **Book language version:** ${BOOK_LANGUAGE_VERSION}
473
+ - **Time:** ${new Date().toISOString()}
474
+
475
+ <details>
476
+ <summary>Stack trace:</summary>
477
+
478
+ ## Stack trace:
479
+
480
+ \`\`\`stacktrace
481
+ ${block(error.stack || '(empty)')}
482
+ \`\`\`
483
+ </details>
484
+
485
+ `),
486
+ };
487
+ const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
488
+ reportUrl.searchParams.set('labels', 'bug');
489
+ reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
490
+ reportUrl.searchParams.set('title', report.title);
491
+ reportUrl.searchParams.set('body', report.body);
492
+ return reportUrl;
493
+ }
494
+
495
+ /**
496
+ * This error type indicates that the error should not happen and its last check before crashing with some other error
497
+ *
498
+ * @public exported from `@promptbook/core`
499
+ */
500
+ class UnexpectedError extends Error {
501
+ constructor(message) {
502
+ super(spaceTrim$1((block) => `
503
+ ${block(message)}
504
+
505
+ Note: This error should not happen.
506
+ It's probbably a bug in the pipeline collection
507
+
508
+ Please report issue:
509
+ ${block(getErrorReportUrl(new Error(message)).href)}
510
+
511
+ Or contact us on ${ADMIN_EMAIL}
512
+
513
+ `));
514
+ this.name = 'UnexpectedError';
515
+ Object.setPrototypeOf(this, UnexpectedError.prototype);
516
+ }
517
+ }
518
+
519
+ /**
520
+ * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
521
+ *
522
+ * @public exported from `@promptbook/core`
523
+ */
524
+ class WrappedError extends Error {
525
+ constructor(whatWasThrown) {
526
+ const tag = `[🤮]`;
527
+ console.error(tag, whatWasThrown);
528
+ super(spaceTrim$1(`
529
+ Non-Error object was thrown
530
+
531
+ Note: Look for ${tag} in the console for more details
532
+ Please report issue on ${ADMIN_EMAIL}
533
+ `));
534
+ this.name = 'WrappedError';
535
+ Object.setPrototypeOf(this, WrappedError.prototype);
536
+ }
537
+ }
538
+
539
+ /**
540
+ * Helper used in catch blocks to assert that the error is an instance of `Error`
541
+ *
542
+ * @param whatWasThrown Any object that was thrown
543
+ * @returns Nothing if the error is an instance of `Error`
544
+ * @throws `WrappedError` or `UnexpectedError` if the error is not standard
545
+ *
546
+ * @private within the repository
547
+ */
548
+ function assertsError(whatWasThrown) {
549
+ // Case 1: Handle error which was rethrown as `WrappedError`
550
+ if (whatWasThrown instanceof WrappedError) {
551
+ const wrappedError = whatWasThrown;
552
+ throw wrappedError;
553
+ }
554
+ // Case 2: Handle unexpected errors
555
+ if (whatWasThrown instanceof UnexpectedError) {
556
+ const unexpectedError = whatWasThrown;
557
+ throw unexpectedError;
558
+ }
559
+ // Case 3: Handle standard errors - keep them up to consumer
560
+ if (whatWasThrown instanceof Error) {
561
+ return;
562
+ }
563
+ // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
564
+ throw new WrappedError(whatWasThrown);
565
+ }
566
+
412
567
  /**
413
568
  * Wraps action to handle error console logging and exit process with error code
414
569
  *
@@ -423,9 +578,7 @@ function handleActionErrors(action) {
423
578
  return process.exit(0);
424
579
  }
425
580
  catch (error) {
426
- if (!(error instanceof Error)) {
427
- throw error;
428
- }
581
+ assertsError(error);
429
582
  // console.error(colors.bgRed(error.name));
430
583
  console.error(colors.red(/* error.stack || */ error.message));
431
584
  return process.exit(1);
@@ -532,74 +685,6 @@ class NotYetImplementedError extends Error {
532
685
  }
533
686
  }
534
687
 
535
- /**
536
- * Make error report URL for the given error
537
- *
538
- * @private private within the repository
539
- */
540
- function getErrorReportUrl(error) {
541
- const report = {
542
- title: `🐜 Error report from ${NAME}`,
543
- body: spaceTrim((block) => `
544
-
545
-
546
- \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
547
-
548
- \`\`\`
549
- ${block(error.message || '(no error message)')}
550
- \`\`\`
551
-
552
-
553
- ## More info:
554
-
555
- - **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
556
- - **Book language version:** ${BOOK_LANGUAGE_VERSION}
557
- - **Time:** ${new Date().toISOString()}
558
-
559
- <details>
560
- <summary>Stack trace:</summary>
561
-
562
- ## Stack trace:
563
-
564
- \`\`\`stacktrace
565
- ${block(error.stack || '(empty)')}
566
- \`\`\`
567
- </details>
568
-
569
- `),
570
- };
571
- const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
572
- reportUrl.searchParams.set('labels', 'bug');
573
- reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
574
- reportUrl.searchParams.set('title', report.title);
575
- reportUrl.searchParams.set('body', report.body);
576
- return reportUrl;
577
- }
578
-
579
- /**
580
- * This error type indicates that the error should not happen and its last check before crashing with some other error
581
- *
582
- * @public exported from `@promptbook/core`
583
- */
584
- class UnexpectedError extends Error {
585
- constructor(message) {
586
- super(spaceTrim$1((block) => `
587
- ${block(message)}
588
-
589
- Note: This error should not happen.
590
- It's probbably a bug in the pipeline collection
591
-
592
- Please report issue:
593
- ${block(getErrorReportUrl(new Error(message)).href)}
594
-
595
- Or contact us on ${ADMIN_EMAIL}
596
-
597
- `));
598
- this.name = 'UnexpectedError';
599
- Object.setPrototypeOf(this, UnexpectedError.prototype);
600
- }
601
- }
602
-
603
688
  /**
604
689
  * @@@
605
690
  *
@@ -919,6 +1004,40 @@ function $sideEffect(...sideEffectSubjects) {
919
1004
  keepUnused(...sideEffectSubjects);
920
1005
  }
921
1006
 
1007
+ /**
1008
+ * Convert identification to Promptbook token
1009
+ *
1010
+ * @param identification
1011
+ *
1012
+ * @public exported from `@promptbook/core`
1013
+ */
1014
+ function identificationToPromptbookToken(identification) {
1015
+ const { appId, userId, userToken } = identification;
1016
+ const promptbookToken = `${appId}-${userId}-${userToken}`;
1017
+ return promptbookToken;
1018
+ }
1019
+
1020
+ /**
1021
+ * Convert Promptbook token to identification
1022
+ *
1023
+ * @param promptbookToken
1024
+ *
1025
+ * @public exported from `@promptbook/core`
1026
+ */
1027
+ function promptbookTokenToIdentification(promptbookToken) {
1028
+ const [appId, userId, userToken] = promptbookToken.split('-');
1029
+ if (!appId || !userId || !userToken) {
1030
+ throw new Error(`Invalid promptbook token: ${promptbookToken}`);
1031
+ }
1032
+ const identification = {
1033
+ appId,
1034
+ userId,
1035
+ userToken,
1036
+ isAnonymous: false,
1037
+ };
1038
+ return identification;
1039
+ }
1040
+
922
1041
  /**
923
1042
  * Just marks a place of place where should be something implemented
924
1043
  * No side effects.
@@ -932,27 +1051,199 @@ function $sideEffect(...sideEffectSubjects) {
932
1051
  function TODO_USE(...value) {
933
1052
  }
934
1053
 
935
- /**
936
- * @@@
937
- *
938
- * @public exported from `@promptbook/node`
939
- */
940
- function $provideFilesystemForNode(options) {
941
- if (!$isRunningInNode()) {
942
- throw new EnvironmentMismatchError('Function `$provideFilesystemForNode` works only in Node.js environment');
1054
+ /**
1055
+ * @@@
1056
+ *
1057
+ * @public exported from `@promptbook/node`
1058
+ */
1059
+ function $provideFilesystemForNode(options) {
1060
+ if (!$isRunningInNode()) {
1061
+ throw new EnvironmentMismatchError('Function `$provideFilesystemForNode` works only in Node.js environment');
1062
+ }
1063
+ return {
1064
+ stat,
1065
+ access,
1066
+ constants,
1067
+ readFile,
1068
+ writeFile,
1069
+ readdir,
1070
+ mkdir,
1071
+ };
1072
+ }
1073
+ /**
1074
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
1075
+ */
1076
+
1077
+ /**
1078
+ * Checks if the file exists
1079
+ *
1080
+ * @private within the repository
1081
+ */
1082
+ async function isFileExisting(filename, fs) {
1083
+ const isReadAccessAllowed = await fs
1084
+ .access(filename, fs.constants.R_OK)
1085
+ .then(() => true)
1086
+ .catch(() => false);
1087
+ if (!isReadAccessAllowed) {
1088
+ return false;
1089
+ }
1090
+ const isFile = await fs
1091
+ .stat(filename)
1092
+ .then((fileStat) => fileStat.isFile())
1093
+ .catch(() => false);
1094
+ return isFile;
1095
+ }
1096
+ /**
1097
+ * Note: Not [~🟢~] because it is not directly dependent on `fs
1098
+ * TODO: [🐠] This can be a validator - with variants that return true/false and variants that throw errors with meaningless messages
1099
+ * TODO: [🖇] What about symlinks?
1100
+ */
1101
+
1102
+ /**
1103
+ * Determines if the given path is a root path.
1104
+ *
1105
+ * Note: This does not check if the file exists only if the path is valid
1106
+ * @public exported from `@promptbook/utils`
1107
+ */
1108
+ function isRootPath(value) {
1109
+ if (value === '/') {
1110
+ return true;
1111
+ }
1112
+ if (/^[A-Z]:\\$/i.test(value)) {
1113
+ return true;
1114
+ }
1115
+ return false;
1116
+ }
1117
+ /**
1118
+ * TODO: [🍏] Make for MacOS paths
1119
+ */
1120
+
1121
+ /**
1122
+ * Provides the path to the `.env` file
1123
+ *
1124
+ * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
1125
+ *
1126
+ * @private within the repository - for CLI utils
1127
+ */
1128
+ async function $provideEnvFilename() {
1129
+ if (!$isRunningInNode()) {
1130
+ throw new EnvironmentMismatchError('Function `$provideEnvFilename` works only in Node.js environment');
1131
+ }
1132
+ const envFilePatterns = [
1133
+ '.env',
1134
+ '.env.test',
1135
+ '.env.local',
1136
+ '.env.development.local',
1137
+ '.env.development',
1138
+ '.env.production.local',
1139
+ '.env.production',
1140
+ '.env.prod.local',
1141
+ '.env.prod',
1142
+ // <- TODO: Maybe add more patterns
1143
+ ];
1144
+ let rootDirname = process.cwd();
1145
+ up_to_root: for (let i = 0; i < LOOP_LIMIT; i++) {
1146
+ for (const pattern of envFilePatterns) {
1147
+ const envFilename = join(rootDirname, pattern);
1148
+ if (await isFileExisting(envFilename, $provideFilesystemForNode())) {
1149
+ $setUsedEnvFilename(envFilename);
1150
+ return envFilename;
1151
+ }
1152
+ }
1153
+ if (isRootPath(rootDirname)) {
1154
+ break up_to_root;
1155
+ }
1156
+ // Note: If the directory does not exist, try the parent directory
1157
+ rootDirname = join(rootDirname, '..');
1158
+ }
1159
+ return null;
1160
+ }
1161
+ /**
1162
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
1163
+ */
1164
+
1165
+ /**
1166
+ * Stores data in .env variables
1167
+ *
1168
+ * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file and also writes to `process.env`
1169
+ *
1170
+ * @private within the repository - for CLI utils
1171
+ */
1172
+ class $EnvStorage {
1173
+ constructor() {
1174
+ this.envFilename = null;
1175
+ }
1176
+ async $provideOrCreateEnvFile() {
1177
+ if (this.envFilename !== null) {
1178
+ return this.envFilename;
1179
+ }
1180
+ let envFilename = await $provideEnvFilename();
1181
+ if (envFilename !== null) {
1182
+ this.envFilename = envFilename;
1183
+ return envFilename;
1184
+ }
1185
+ envFilename = join(process.cwd(), '.env');
1186
+ await writeFile(envFilename, '# This file was initialized by Promptbook', 'utf-8');
1187
+ this.envFilename = envFilename;
1188
+ return envFilename;
1189
+ }
1190
+ transformKey(key) {
1191
+ return normalizeTo_SCREAMING_CASE(key);
1192
+ }
1193
+ /**
1194
+ * Returns the number of key/value pairs currently present in the list associated with the object.
1195
+ */
1196
+ get length() {
1197
+ throw new NotYetImplementedError('Method `$EnvStorage.length` not implemented.');
1198
+ }
1199
+ /**
1200
+ * Empties the list associated with the object of all key/value pairs, if there are any.
1201
+ */
1202
+ clear() {
1203
+ throw new NotYetImplementedError('Method `$EnvStorage.clear` not implemented.');
1204
+ }
1205
+ /**
1206
+ * 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.
1207
+ */
1208
+ async getItem(key) {
1209
+ dotenv.config({ path: await this.$provideOrCreateEnvFile() });
1210
+ return process.env[this.transformKey(key)] || null;
1211
+ }
1212
+ /**
1213
+ * 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.
1214
+ */
1215
+ key(index) {
1216
+ throw new NotYetImplementedError('Method `$EnvStorage.key` not implemented.');
1217
+ }
1218
+ /**
1219
+ * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
1220
+ */
1221
+ async setItem(key, value) {
1222
+ const envFilename = await this.$provideOrCreateEnvFile();
1223
+ const envContent = await readFile(envFilename, 'utf-8');
1224
+ const transformedKey = this.transformKey(key);
1225
+ const updatedEnvContent = envContent
1226
+ .split('\n')
1227
+ .filter((line) => !line.startsWith(`# ${GENERATOR_WARNING_IN_ENV}`)) // Remove GENERATOR_WARNING_IN_ENV
1228
+ .filter((line) => !line.startsWith(`${transformedKey}=`)) // Remove existing key if present
1229
+ .join('\n');
1230
+ const newEnvContent = spaceTrim((block) => `
1231
+ ${block(updatedEnvContent)}
1232
+
1233
+ # ${GENERATOR_WARNING_IN_ENV}
1234
+ ${transformedKey}=${JSON.stringify(value)}
1235
+ `);
1236
+ await writeFile(envFilename, newEnvContent, 'utf-8');
1237
+ }
1238
+ /**
1239
+ * 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.
1240
+ */
1241
+ removeItem(key) {
1242
+ throw new NotYetImplementedError('Method `$EnvStorage.removeItem` not implemented.');
943
1243
  }
944
- return {
945
- stat,
946
- access,
947
- constants,
948
- readFile,
949
- writeFile,
950
- readdir,
951
- mkdir,
952
- };
953
1244
  }
954
1245
  /**
955
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
1246
+ * TODO: Write file more securely - ensure that there can be no accidental overwriting of existing variables and other content
956
1247
  */
957
1248
 
958
1249
  /**
@@ -1088,9 +1379,7 @@ function checkSerializableAsJson(options) {
1088
1379
  JSON.stringify(value); // <- TODO: [0]
1089
1380
  }
1090
1381
  catch (error) {
1091
- if (!(error instanceof Error)) {
1092
- throw error;
1093
- }
1382
+ assertsError(error);
1094
1383
  throw new UnexpectedError(spaceTrim((block) => `
1095
1384
  \`${name}\` is not serializable
1096
1385
 
@@ -1323,31 +1612,6 @@ function stringifyPipelineJson(pipeline) {
1323
1612
  * TODO: [🍙] Make some standard order of json properties
1324
1613
  */
1325
1614
 
1326
- /**
1327
- * Checks if the file exists
1328
- *
1329
- * @private within the repository
1330
- */
1331
- async function isFileExisting(filename, fs) {
1332
- const isReadAccessAllowed = await fs
1333
- .access(filename, fs.constants.R_OK)
1334
- .then(() => true)
1335
- .catch(() => false);
1336
- if (!isReadAccessAllowed) {
1337
- return false;
1338
- }
1339
- const isFile = await fs
1340
- .stat(filename)
1341
- .then((fileStat) => fileStat.isFile())
1342
- .catch(() => false);
1343
- return isFile;
1344
- }
1345
- /**
1346
- * Note: Not [~🟢~] because it is not directly dependent on `fs
1347
- * TODO: [🐠] This can be a validator - with variants that return true/false and variants that throw errors with meaningless messages
1348
- * TODO: [🖇] What about symlinks?
1349
- */
1350
-
1351
1615
  /**
1352
1616
  * Removes emojis from a string and fix whitespaces
1353
1617
  *
@@ -1853,53 +2117,6 @@ class FileCacheStorage {
1853
2117
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
1854
2118
  */
1855
2119
 
1856
- /**
1857
- * Stores data in memory (HEAP)
1858
- *
1859
- * @public exported from `@promptbook/core`
1860
- */
1861
- class MemoryStorage {
1862
- constructor() {
1863
- this.storage = {};
1864
- }
1865
- /**
1866
- * Returns the number of key/value pairs currently present in the list associated with the object.
1867
- */
1868
- get length() {
1869
- return Object.keys(this.storage).length;
1870
- }
1871
- /**
1872
- * Empties the list associated with the object of all key/value pairs, if there are any.
1873
- */
1874
- clear() {
1875
- this.storage = {};
1876
- }
1877
- /**
1878
- * 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.
1879
- */
1880
- getItem(key) {
1881
- return this.storage[key] || null;
1882
- }
1883
- /**
1884
- * 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.
1885
- */
1886
- key(index) {
1887
- return Object.keys(this.storage)[index] || null;
1888
- }
1889
- /**
1890
- * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
1891
- */
1892
- setItem(key, value) {
1893
- this.storage[key] = value;
1894
- }
1895
- /**
1896
- * 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.
1897
- */
1898
- removeItem(key) {
1899
- delete this.storage[key];
1900
- }
1901
- }
1902
-
1903
2120
  /**
1904
2121
  * This error indicates problems parsing the format value
1905
2122
  *
@@ -2076,7 +2293,7 @@ class PipelineExecutionError extends Error {
2076
2293
  }
2077
2294
  }
2078
2295
  /**
2079
- * TODO: !!!!!! Add id to all errors
2296
+ * TODO: [🧠][🌂] Add id to all errors
2080
2297
  */
2081
2298
 
2082
2299
  /**
@@ -2138,7 +2355,10 @@ const PROMPTBOOK_ERRORS = {
2138
2355
  PipelineExecutionError,
2139
2356
  PipelineLogicError,
2140
2357
  PipelineUrlError,
2358
+ AuthenticationError,
2359
+ PromptbookFetchError,
2141
2360
  UnexpectedError,
2361
+ WrappedError,
2142
2362
  // TODO: [🪑]> VersionMismatchError,
2143
2363
  };
2144
2364
  /**
@@ -2155,8 +2375,6 @@ const COMMON_JAVASCRIPT_ERRORS = {
2155
2375
  TypeError,
2156
2376
  URIError,
2157
2377
  AggregateError,
2158
- AuthenticationError,
2159
- PromptbookFetchError,
2160
2378
  /*
2161
2379
  Note: Not widely supported
2162
2380
  > InternalError,
@@ -2214,17 +2432,31 @@ function deserializeError(error) {
2214
2432
  */
2215
2433
  async function createRemoteClient(options) {
2216
2434
  const { remoteServerUrl } = options;
2217
- let path = new URL(remoteServerUrl).pathname;
2218
- if (path.endsWith('/')) {
2219
- path = path.slice(0, -1);
2435
+ if (!isValidUrl(remoteServerUrl)) {
2436
+ throw new Error(`Invalid \`remoteServerUrl\`: "${remoteServerUrl}"`);
2437
+ }
2438
+ const remoteServerUrlParsed = new URL(remoteServerUrl);
2439
+ if (remoteServerUrlParsed.pathname !== '/' && remoteServerUrlParsed.pathname !== '') {
2440
+ remoteServerUrlParsed.pathname = '/';
2441
+ throw new Error(spaceTrim((block) => `
2442
+ Remote server requires root url \`/\`
2443
+
2444
+ You have provided \`remoteServerUrl\`:
2445
+ ${block(remoteServerUrl)}
2446
+
2447
+ But something like this is expected:
2448
+ ${block(remoteServerUrlParsed.href)}
2449
+
2450
+ Note: If you need to run multiple services on the same server, use 3rd or 4th degree subdomain
2451
+
2452
+ `));
2220
2453
  }
2221
- path = `${path}/socket.io`;
2222
2454
  return new Promise((resolve, reject) => {
2223
2455
  const socket = io(remoteServerUrl, {
2224
2456
  retries: CONNECTION_RETRIES_LIMIT,
2225
2457
  timeout: CONNECTION_TIMEOUT_MS,
2226
- path,
2227
- transports: [/*'websocket', <- TODO: [🌬] Make websocket transport work */ 'polling'],
2458
+ path: '/socket.io',
2459
+ transports: ['polling', 'websocket' /*, <- TODO: [🌬] Allow to pass `transports`, add 'webtransport' */],
2228
2460
  });
2229
2461
  // console.log('Connecting to', this.options.remoteServerUrl.href, { socket });
2230
2462
  socket.on('connect', () => {
@@ -2349,6 +2581,53 @@ class RemoteLlmExecutionTools {
2349
2581
  * TODO: [🧠] Maybe remove `@promptbook/remote-client` and just use `@promptbook/core`
2350
2582
  */
2351
2583
 
2584
+ /**
2585
+ * Stores data in memory (HEAP)
2586
+ *
2587
+ * @public exported from `@promptbook/core`
2588
+ */
2589
+ class MemoryStorage {
2590
+ constructor() {
2591
+ this.storage = {};
2592
+ }
2593
+ /**
2594
+ * Returns the number of key/value pairs currently present in the list associated with the object.
2595
+ */
2596
+ get length() {
2597
+ return Object.keys(this.storage).length;
2598
+ }
2599
+ /**
2600
+ * Empties the list associated with the object of all key/value pairs, if there are any.
2601
+ */
2602
+ clear() {
2603
+ this.storage = {};
2604
+ }
2605
+ /**
2606
+ * 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.
2607
+ */
2608
+ getItem(key) {
2609
+ return this.storage[key] || null;
2610
+ }
2611
+ /**
2612
+ * 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.
2613
+ */
2614
+ key(index) {
2615
+ return Object.keys(this.storage)[index] || null;
2616
+ }
2617
+ /**
2618
+ * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
2619
+ */
2620
+ setItem(key, value) {
2621
+ this.storage[key] = value;
2622
+ }
2623
+ /**
2624
+ * 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.
2625
+ */
2626
+ removeItem(key) {
2627
+ delete this.storage[key];
2628
+ }
2629
+ }
2630
+
2352
2631
  /**
2353
2632
  * Simple wrapper `new Date().toISOString()`
2354
2633
  *
@@ -2608,101 +2887,38 @@ function countUsage(llmTools) {
2608
2887
  }
2609
2888
  if (llmTools.callCompletionModel !== undefined) {
2610
2889
  proxyTools.callCompletionModel = async (prompt) => {
2611
- // console.info('[🚕] callCompletionModel through countTotalUsage');
2612
- const promptResult = await llmTools.callCompletionModel(prompt);
2613
- totalUsage = addUsage(totalUsage, promptResult.usage);
2614
- spending.next(promptResult.usage);
2615
- return promptResult;
2616
- };
2617
- }
2618
- if (llmTools.callEmbeddingModel !== undefined) {
2619
- proxyTools.callEmbeddingModel = async (prompt) => {
2620
- // console.info('[🚕] callEmbeddingModel through countTotalUsage');
2621
- const promptResult = await llmTools.callEmbeddingModel(prompt);
2622
- totalUsage = addUsage(totalUsage, promptResult.usage);
2623
- spending.next(promptResult.usage);
2624
- return promptResult;
2625
- };
2626
- }
2627
- // <- Note: [🤖]
2628
- return proxyTools;
2629
- }
2630
- /**
2631
- * TODO: [🧠][💸] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
2632
- * TODO: [🧠] Is there some meaningfull way how to test this util
2633
- * TODO: [🧠][🌯] Maybe a way how to hide ability to `get totalUsage`
2634
- * > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
2635
- * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
2636
- */
2637
-
2638
- /**
2639
- * Determines if the given path is a root path.
2640
- *
2641
- * Note: This does not check if the file exists only if the path is valid
2642
- * @public exported from `@promptbook/utils`
2643
- */
2644
- function isRootPath(value) {
2645
- if (value === '/') {
2646
- return true;
2647
- }
2648
- if (/^[A-Z]:\\$/i.test(value)) {
2649
- return true;
2650
- }
2651
- return false;
2652
- }
2653
- /**
2654
- * TODO: [🍏] Make for MacOS paths
2655
- */
2656
-
2657
- /**
2658
- * Provides the path to the `.env` file
2659
- *
2660
- * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access .env file
2661
- *
2662
- * @private within the repository - for CLI utils
2663
- */
2664
- async function $provideEnvFilepath() {
2665
- if (!$isRunningInNode()) {
2666
- throw new EnvironmentMismatchError('Function `$provideEnvFilepath` works only in Node.js environment');
2667
- }
2668
- const envFilePatterns = [
2669
- '.env',
2670
- '.env.test',
2671
- '.env.local',
2672
- '.env.development.local',
2673
- '.env.development',
2674
- '.env.production.local',
2675
- '.env.production',
2676
- '.env.prod.local',
2677
- '.env.prod',
2678
- // <- TODO: Maybe add more patterns
2679
- ];
2680
- let rootDirname = process.cwd();
2681
- up_to_root: for (let i = 0; i < LOOP_LIMIT; i++) {
2682
- for (const pattern of envFilePatterns) {
2683
- const envFilename = join(rootDirname, pattern);
2684
- if (await isFileExisting(envFilename, $provideFilesystemForNode())) {
2685
- $setUsedEnvFilename(envFilename);
2686
- return envFilename;
2687
- }
2688
- }
2689
- if (isRootPath(rootDirname)) {
2690
- break up_to_root;
2691
- }
2692
- // Note: If the directory does not exist, try the parent directory
2693
- rootDirname = join(rootDirname, '..');
2890
+ // console.info('[🚕] callCompletionModel through countTotalUsage');
2891
+ const promptResult = await llmTools.callCompletionModel(prompt);
2892
+ totalUsage = addUsage(totalUsage, promptResult.usage);
2893
+ spending.next(promptResult.usage);
2894
+ return promptResult;
2895
+ };
2694
2896
  }
2695
- return null;
2897
+ if (llmTools.callEmbeddingModel !== undefined) {
2898
+ proxyTools.callEmbeddingModel = async (prompt) => {
2899
+ // console.info('[🚕] callEmbeddingModel through countTotalUsage');
2900
+ const promptResult = await llmTools.callEmbeddingModel(prompt);
2901
+ totalUsage = addUsage(totalUsage, promptResult.usage);
2902
+ spending.next(promptResult.usage);
2903
+ return promptResult;
2904
+ };
2905
+ }
2906
+ // <- Note: [🤖]
2907
+ return proxyTools;
2696
2908
  }
2697
2909
  /**
2698
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
2910
+ * TODO: [🧠][💸] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
2911
+ * TODO: [🧠] Is there some meaningfull way how to test this util
2912
+ * TODO: [🧠][🌯] Maybe a way how to hide ability to `get totalUsage`
2913
+ * > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
2914
+ * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
2699
2915
  */
2700
2916
 
2701
2917
  /**
2702
2918
  * @@@
2703
2919
  *
2704
2920
  * @@@ .env
2705
- * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access .env file
2921
+ * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
2706
2922
  *
2707
2923
  * It looks for environment variables:
2708
2924
  * - `process.env.OPENAI_API_KEY`
@@ -2716,7 +2932,7 @@ async function $provideLlmToolsConfigurationFromEnv() {
2716
2932
  if (!$isRunningInNode()) {
2717
2933
  throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
2718
2934
  }
2719
- const envFilepath = await $provideEnvFilepath();
2935
+ const envFilepath = await $provideEnvFilename();
2720
2936
  if (envFilepath !== null) {
2721
2937
  dotenv.config({ path: envFilepath });
2722
2938
  }
@@ -2824,14 +3040,15 @@ class MultipleLlmExecutionTools {
2824
3040
  }
2825
3041
  }
2826
3042
  catch (error) {
2827
- if (!(error instanceof Error) || error instanceof UnexpectedError) {
3043
+ assertsError(error);
3044
+ if (error instanceof UnexpectedError) {
2828
3045
  throw error;
2829
3046
  }
2830
3047
  errors.push({ llmExecutionTools, error });
2831
3048
  }
2832
3049
  }
2833
3050
  if (errors.length === 1) {
2834
- throw errors[0];
3051
+ throw errors[0].error;
2835
3052
  }
2836
3053
  else if (errors.length > 1) {
2837
3054
  throw new PipelineExecutionError(
@@ -2978,7 +3195,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
2978
3195
  * Note: This function is not cached, every call creates new instance of `MultipleLlmExecutionTools`
2979
3196
  *
2980
3197
  * @@@ .env
2981
- * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access .env file
3198
+ * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
2982
3199
  *
2983
3200
  * It looks for environment variables:
2984
3201
  * - `process.env.OPENAI_API_KEY`
@@ -3023,7 +3240,7 @@ async function $provideLlmToolsFromEnv(options = {}) {
3023
3240
  /**
3024
3241
  * Returns LLM tools for CLI
3025
3242
  *
3026
- * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access .env file and also writes this .env file
3243
+ * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file and also writes this .env file
3027
3244
  *
3028
3245
  * @private within the repository - for CLI utils
3029
3246
  */
@@ -3032,18 +3249,27 @@ async function $provideLlmToolsForWizzardOrCli(options) {
3032
3249
  throw new EnvironmentMismatchError('Function `$provideLlmToolsForWizzardOrCli` works only in Node.js environment');
3033
3250
  }
3034
3251
  options = options !== null && options !== void 0 ? options : { strategy: 'BRING_YOUR_OWN_KEYS' };
3035
- const { strategy, isCacheReloaded } = options;
3252
+ const { isLoginloaded, strategy, isCacheReloaded } = options;
3036
3253
  let llmExecutionTools;
3037
3254
  if (strategy === 'REMOTE_SERVER') {
3038
3255
  const { remoteServerUrl = DEFAULT_REMOTE_SERVER_URL, loginPrompt } = options;
3039
- // TODO: !!!
3040
- // const envFilepath = await $provideEnvFilepath();
3041
- const storage = new MemoryStorage(); // <- TODO: !!!!!! Save to `.promptbook` folder
3042
- const key = `${remoteServerUrl}-identification`;
3043
- let identification = await storage.getItem(key);
3044
- if (identification === null) {
3256
+ const storage = new $EnvStorage();
3257
+ let key = `PROMPTBOOK_TOKEN`;
3258
+ if (remoteServerUrl !== DEFAULT_REMOTE_SERVER_URL) {
3259
+ key = `${key}_${remoteServerUrl.replace(/^https?:\/\//i, '')}`;
3260
+ }
3261
+ let identification = null;
3262
+ let promptbookToken = await storage.getItem(key);
3263
+ if (promptbookToken === null || isLoginloaded) {
3045
3264
  identification = await loginPrompt();
3046
- await storage.setItem(key, identification);
3265
+ // Note: When login prompt fails, `process.exit(1)` is called so no need to check for null
3266
+ if (identification.isAnonymous === false) {
3267
+ promptbookToken = identificationToPromptbookToken(identification);
3268
+ await storage.setItem(key, promptbookToken);
3269
+ }
3270
+ }
3271
+ else {
3272
+ identification = promptbookTokenToIdentification(promptbookToken);
3047
3273
  }
3048
3274
  llmExecutionTools = new RemoteLlmExecutionTools({
3049
3275
  remoteServerUrl,
@@ -3084,9 +3310,7 @@ const promptbookFetch = async (urlOrRequest, init) => {
3084
3310
  return await fetch(urlOrRequest, init);
3085
3311
  }
3086
3312
  catch (error) {
3087
- if (!(error instanceof Error)) {
3088
- throw error;
3089
- }
3313
+ assertsError(error);
3090
3314
  let url;
3091
3315
  if (typeof urlOrRequest === 'string') {
3092
3316
  url = urlOrRequest;
@@ -3125,8 +3349,8 @@ function isValidEmail(email) {
3125
3349
  /**
3126
3350
  * @private utility of CLI
3127
3351
  */
3128
- function $provideLlmToolsForCli(options) {
3129
- const { cliOptions: {
3352
+ async function $provideLlmToolsForCli(options) {
3353
+ const { isLoginloaded, cliOptions: {
3130
3354
  /* TODO: Use verbose: isVerbose, */ interactive: isInteractive, provider, remoteServerUrl: remoteServerUrlRaw, }, } = options;
3131
3355
  let strategy;
3132
3356
  if (/^b/i.test(provider)) {
@@ -3140,7 +3364,11 @@ function $provideLlmToolsForCli(options) {
3140
3364
  process.exit(1);
3141
3365
  }
3142
3366
  if (strategy === 'BRING_YOUR_OWN_KEYS') {
3143
- return /* not await */ $provideLlmToolsForWizzardOrCli({ strategy, ...options });
3367
+ if (isLoginloaded) {
3368
+ throw new UnexpectedError(`\`$provideLlmToolsForCli\` isLoginloaded is not supported for strategy "BRING_YOUR_OWN_KEYS"`);
3369
+ }
3370
+ const llm = await $provideLlmToolsForWizzardOrCli({ strategy, ...options });
3371
+ return { strategy, llm };
3144
3372
  }
3145
3373
  else if (strategy === 'REMOTE_SERVER') {
3146
3374
  if (!isValidUrl(remoteServerUrlRaw)) {
@@ -3148,7 +3376,8 @@ function $provideLlmToolsForCli(options) {
3148
3376
  process.exit(1);
3149
3377
  }
3150
3378
  const remoteServerUrl = remoteServerUrlRaw.endsWith('/') ? remoteServerUrlRaw.slice(0, -1) : remoteServerUrlRaw;
3151
- return /* not await */ $provideLlmToolsForWizzardOrCli({
3379
+ const llm = await $provideLlmToolsForWizzardOrCli({
3380
+ isLoginloaded,
3152
3381
  strategy,
3153
3382
  appId: CLI_APP_ID,
3154
3383
  remoteServerUrl,
@@ -3158,6 +3387,10 @@ function $provideLlmToolsForCli(options) {
3158
3387
  console.log(colors.red(`You can not login to remote server in non-interactive mode`));
3159
3388
  process.exit(1);
3160
3389
  }
3390
+ console.info(colors.cyan(spaceTrim(`
3391
+ You will be logged in to ${remoteServerUrl}
3392
+ If you don't have an account, it will be created automatically.
3393
+ `)));
3161
3394
  const { username, password } = await prompts([
3162
3395
  {
3163
3396
  type: 'text',
@@ -3175,7 +3408,6 @@ function $provideLlmToolsForCli(options) {
3175
3408
  },
3176
3409
  ]);
3177
3410
  const loginUrl = `${remoteServerUrl}/login`;
3178
- console.log('!!!', { loginUrl });
3179
3411
  // TODO: [🧠] Should we use normal `fetch` or `scraperFetch`
3180
3412
  const response = await promptbookFetch(loginUrl, {
3181
3413
  method: 'POST',
@@ -3188,20 +3420,7 @@ function $provideLlmToolsForCli(options) {
3188
3420
  password,
3189
3421
  }),
3190
3422
  });
3191
- console.log('!!!', {
3192
- loginUrl,
3193
- username,
3194
- password,
3195
- // type: response.type,
3196
- // text: await response.text(),
3197
- });
3198
3423
  const { isSuccess, message, error, identification } = (await response.json());
3199
- console.log('!!!', {
3200
- isSuccess,
3201
- message,
3202
- error,
3203
- identification,
3204
- });
3205
3424
  if (message) {
3206
3425
  if (isSuccess) {
3207
3426
  console.log(colors.green(message));
@@ -3222,6 +3441,7 @@ function $provideLlmToolsForCli(options) {
3222
3441
  return identification;
3223
3442
  },
3224
3443
  });
3444
+ return { strategy, llm };
3225
3445
  }
3226
3446
  else {
3227
3447
  throw new UnexpectedError(`\`$provideLlmToolsForCli\` wrong strategy "${strategy}"`);
@@ -3243,11 +3463,12 @@ function $initializeListModelsCommand(program) {
3243
3463
  listModelsCommand.alias('models');
3244
3464
  listModelsCommand.alias('llm');
3245
3465
  listModelsCommand.action(handleActionErrors(async (cliOptions) => {
3246
- console.log('!!!', cliOptions);
3247
- // TODO: !!!!!! Not relevant for remote server and also for `about` command
3248
- const llm = await $provideLlmToolsForCli({ cliOptions });
3466
+ const { strategy, llm } = await $provideLlmToolsForCli({ cliOptions });
3249
3467
  $sideEffect(llm);
3250
3468
  // <- Note: Providing LLM tools will make a side effect of registering all available LLM tools to show the message
3469
+ if (strategy !== 'BRING_YOUR_OWN_KEYS') {
3470
+ console.warn(colors.yellow(`You are using --strategy ${strategy} but models listed below are relevant for --strategy BRING_YOUR_OWN_KEYS`));
3471
+ }
3251
3472
  console.info($registeredLlmToolsMessage());
3252
3473
  return process.exit(0);
3253
3474
  }));
@@ -3430,9 +3651,7 @@ async function locateAppOnLinux({ linuxWhich, }) {
3430
3651
  return result.trim();
3431
3652
  }
3432
3653
  catch (error) {
3433
- if (!(error instanceof Error)) {
3434
- throw error;
3435
- }
3654
+ assertsError(error);
3436
3655
  return null;
3437
3656
  }
3438
3657
  }
@@ -3487,9 +3706,7 @@ async function locateAppOnMacOs({ macOsName, }) {
3487
3706
  return result.trim() + toExec;
3488
3707
  }
3489
3708
  catch (error) {
3490
- if (!(error instanceof Error)) {
3491
- throw error;
3492
- }
3709
+ assertsError(error);
3493
3710
  return null;
3494
3711
  }
3495
3712
  }
@@ -3520,9 +3737,7 @@ async function locateAppOnWindows({ appName, windowsSuffix, }) {
3520
3737
  throw new Error(`Can not locate app ${appName} on Windows.`);
3521
3738
  }
3522
3739
  catch (error) {
3523
- if (!(error instanceof Error)) {
3524
- throw error;
3525
- }
3740
+ assertsError(error);
3526
3741
  return null;
3527
3742
  }
3528
3743
  }
@@ -3781,6 +3996,7 @@ function $initializeListScrapersCommand(program) {
3781
3996
  `));
3782
3997
  listModelsCommand.alias('scrapers');
3783
3998
  listModelsCommand.action(handleActionErrors(async () => {
3999
+ // TODO: [🌞] Do not allow on REMOTE_SERVER strategy
3784
4000
  const scrapers = await $provideScrapersForNode({});
3785
4001
  const executables = await $provideExecutablesForNode();
3786
4002
  console.info(spaceTrim((block) => `
@@ -3816,42 +4032,20 @@ function $initializeLoginCommand(program) {
3816
4032
  loginCommand.description(spaceTrim(`
3817
4033
  Login to the remote Promptbook server
3818
4034
  `));
3819
- loginCommand.action(handleActionErrors(async () => {
3820
- // @@@
3821
- console.error(colors.green(spaceTrim(`
3822
- You will be logged in to https://promptbook.studio server.
3823
- If you don't have an account, it will be created automatically.
3824
- `)));
3825
- // !!!!!!!!! Remove from here and use $provideLlmToolsForCli
3826
- const { email, password } = await prompts([
3827
- {
3828
- type: 'text',
3829
- name: 'email',
3830
- message: 'Enter your email:',
3831
- validate: (value) => (isValidEmail(value) ? true : 'Valid email is required'),
3832
- },
3833
- {
3834
- type: 'password',
3835
- name: 'password',
3836
- message: 'Enter your password:',
3837
- validate: (value) => value.length /* <- TODO: [🧠] Better password validation */ > 0 ? true : 'Password is required',
4035
+ loginCommand.action(handleActionErrors(async (cliOptions) => {
4036
+ // Note: Not interested in return value of this function but the side effect of logging in
4037
+ await $provideLlmToolsForCli({
4038
+ isLoginloaded: true,
4039
+ cliOptions: {
4040
+ ...cliOptions,
4041
+ strategy: 'REMOTE_SERVER', // <- Note: Overriding strategy to `REMOTE_SERVER`
4042
+ // TODO: Do not allow flag `--strategy` in `login` command at all
3838
4043
  },
3839
- ]);
3840
- TODO_USE(email, password);
3841
- await forTime(1000);
3842
- console.error(colors.green(spaceTrim(`
3843
- Your account ${email} was successfully created.
3844
-
3845
- Please verify your email:
3846
- https://brj.app/api/v1/customer/register-account?apiKey=PRODdh003eNKaec7PoO1AzU244tsL4WO
3847
-
3848
- After verification, you will receive 500 000 credits for free 🎉
3849
- `)));
4044
+ });
3850
4045
  return process.exit(0);
3851
4046
  }));
3852
4047
  }
3853
4048
  /**
3854
- * TODO: Pass remote server URL (and path)
3855
4049
  * TODO: Implement non-interactive login
3856
4050
  * Note: [💞] Ignore a discrepancy between file name and entity name
3857
4051
  * Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
@@ -4272,6 +4466,9 @@ var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"
4272
4466
  /**
4273
4467
  * Function isValidJsonString will tell you if the string is valid JSON or not
4274
4468
  *
4469
+ * @param value The string to check
4470
+ * @returns True if the string is a valid JSON string, false otherwise
4471
+ *
4275
4472
  * @public exported from `@promptbook/utils`
4276
4473
  */
4277
4474
  function isValidJsonString(value /* <- [👨‍⚖️] */) {
@@ -4280,9 +4477,7 @@ function isValidJsonString(value /* <- [👨‍⚖️] */) {
4280
4477
  return true;
4281
4478
  }
4282
4479
  catch (error) {
4283
- if (!(error instanceof Error)) {
4284
- throw error;
4285
- }
4480
+ assertsError(error);
4286
4481
  if (error.message.includes('Unexpected token')) {
4287
4482
  return false;
4288
4483
  }
@@ -4813,8 +5008,8 @@ function createTask(options) {
4813
5008
  updatedAt = new Date();
4814
5009
  errors.push(...executionResult.errors);
4815
5010
  warnings.push(...executionResult.warnings);
4816
- // <- TODO: !!! Only unique errors and warnings should be added (or filtered)
4817
- // TODO: [🧠] !!! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
5011
+ // <- TODO: [🌂] Only unique errors and warnings should be added (or filtered)
5012
+ // TODO: [🧠] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
4818
5013
  // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
4819
5014
  // And delete `ExecutionTask.currentValue.preparedPipeline`
4820
5015
  assertsTaskSuccessful(executionResult);
@@ -4824,6 +5019,7 @@ function createTask(options) {
4824
5019
  partialResultSubject.next(executionResult);
4825
5020
  }
4826
5021
  catch (error) {
5022
+ assertsError(error);
4827
5023
  status = 'ERROR';
4828
5024
  errors.push(error);
4829
5025
  partialResultSubject.error(error);
@@ -4969,13 +5165,19 @@ function valueToString(value) {
4969
5165
  return value.toISOString();
4970
5166
  }
4971
5167
  else {
4972
- return JSON.stringify(value);
5168
+ try {
5169
+ return JSON.stringify(value);
5170
+ }
5171
+ catch (error) {
5172
+ if (error instanceof TypeError && error.message.includes('circular structure')) {
5173
+ return VALUE_STRINGS.circular;
5174
+ }
5175
+ throw error;
5176
+ }
4973
5177
  }
4974
5178
  }
4975
5179
  catch (error) {
4976
- if (!(error instanceof Error)) {
4977
- throw error;
4978
- }
5180
+ assertsError(error);
4979
5181
  console.error(error);
4980
5182
  return VALUE_STRINGS.unserializable;
4981
5183
  }
@@ -5032,9 +5234,7 @@ function extractVariablesFromJavascript(script) {
5032
5234
  }
5033
5235
  }
5034
5236
  catch (error) {
5035
- if (!(error instanceof Error)) {
5036
- throw error;
5037
- }
5237
+ assertsError(error);
5038
5238
  throw new ParseError(spaceTrim$1((block) => `
5039
5239
  Can not extract variables from the script
5040
5240
  ${block(error.stack || error.message)}
@@ -5153,6 +5353,28 @@ const MANDATORY_CSV_SETTINGS = Object.freeze({
5153
5353
  // encoding: 'utf-8',
5154
5354
  });
5155
5355
 
5356
+ /**
5357
+ * Function to check if a string is valid CSV
5358
+ *
5359
+ * @param value The string to check
5360
+ * @returns True if the string is a valid CSV string, false otherwise
5361
+ *
5362
+ * @public exported from `@promptbook/utils`
5363
+ */
5364
+ function isValidCsvString(value) {
5365
+ try {
5366
+ // A simple check for CSV format: at least one comma and no invalid characters
5367
+ if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
5368
+ return true;
5369
+ }
5370
+ return false;
5371
+ }
5372
+ catch (error) {
5373
+ assertsError(error);
5374
+ return false;
5375
+ }
5376
+ }
5377
+
5156
5378
  /**
5157
5379
  * Definition for CSV spreadsheet
5158
5380
  *
@@ -5163,7 +5385,7 @@ const CsvFormatDefinition = {
5163
5385
  formatName: 'CSV',
5164
5386
  aliases: ['SPREADSHEET', 'TABLE'],
5165
5387
  isValid(value, settings, schema) {
5166
- return true;
5388
+ return isValidCsvString(value);
5167
5389
  },
5168
5390
  canBeValid(partialValue, settings, schema) {
5169
5391
  return true;
@@ -5317,6 +5539,30 @@ const TextFormatDefinition = {
5317
5539
  * TODO: [🏢] Allow to expect something inside each item of list and other formats
5318
5540
  */
5319
5541
 
5542
+ /**
5543
+ * Function to check if a string is valid XML
5544
+ *
5545
+ * @param value
5546
+ * @returns True if the string is a valid XML string, false otherwise
5547
+ *
5548
+ * @public exported from `@promptbook/utils`
5549
+ */
5550
+ function isValidXmlString(value) {
5551
+ try {
5552
+ const parser = new DOMParser();
5553
+ const parsedDocument = parser.parseFromString(value, 'application/xml');
5554
+ const parserError = parsedDocument.getElementsByTagName('parsererror');
5555
+ if (parserError.length > 0) {
5556
+ return false;
5557
+ }
5558
+ return true;
5559
+ }
5560
+ catch (error) {
5561
+ assertsError(error);
5562
+ return false;
5563
+ }
5564
+ }
5565
+
5320
5566
  /**
5321
5567
  * Definition for XML format
5322
5568
  *
@@ -5326,7 +5572,7 @@ const XmlFormatDefinition = {
5326
5572
  formatName: 'XML',
5327
5573
  mimeType: 'application/xml',
5328
5574
  isValid(value, settings, schema) {
5329
- return true;
5575
+ return isValidXmlString(value);
5330
5576
  },
5331
5577
  canBeValid(partialValue, settings, schema) {
5332
5578
  return true;
@@ -5918,9 +6164,7 @@ async function executeAttempts(options) {
5918
6164
  break scripts;
5919
6165
  }
5920
6166
  catch (error) {
5921
- if (!(error instanceof Error)) {
5922
- throw error;
5923
- }
6167
+ assertsError(error);
5924
6168
  if (error instanceof UnexpectedError) {
5925
6169
  throw error;
5926
6170
  }
@@ -5990,9 +6234,7 @@ async function executeAttempts(options) {
5990
6234
  break scripts;
5991
6235
  }
5992
6236
  catch (error) {
5993
- if (!(error instanceof Error)) {
5994
- throw error;
5995
- }
6237
+ assertsError(error);
5996
6238
  if (error instanceof UnexpectedError) {
5997
6239
  throw error;
5998
6240
  }
@@ -6613,9 +6855,7 @@ async function executePipeline(options) {
6613
6855
  await Promise.all(resolving);
6614
6856
  }
6615
6857
  catch (error /* <- Note: [3] */) {
6616
- if (!(error instanceof Error)) {
6617
- throw error;
6618
- }
6858
+ assertsError(error);
6619
6859
  // Note: No need to rethrow UnexpectedError
6620
6860
  // if (error instanceof UnexpectedError) {
6621
6861
  // Note: Count usage, [🧠] Maybe put to separate function executionReportJsonToUsage + DRY [🤹‍♂️]
@@ -7091,9 +7331,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
7091
7331
  knowledgePreparedUnflatten[index] = pieces;
7092
7332
  }
7093
7333
  catch (error) {
7094
- if (!(error instanceof Error)) {
7095
- throw error;
7096
- }
7334
+ assertsError(error);
7097
7335
  console.warn(error);
7098
7336
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
7099
7337
  }
@@ -7891,6 +8129,8 @@ function parseNumber(value) {
7891
8129
  */
7892
8130
 
7893
8131
  /**
8132
+ import { WrappedError } from '../../errors/WrappedError';
8133
+ import { assertsError } from '../../errors/assertsError';
7894
8134
  * Parses the expect command
7895
8135
  *
7896
8136
  * @see `documentationUrl` for more details
@@ -7982,9 +8222,7 @@ const expectCommandParser = {
7982
8222
  };
7983
8223
  }
7984
8224
  catch (error) {
7985
- if (!(error instanceof Error)) {
7986
- throw error;
7987
- }
8225
+ assertsError(error);
7988
8226
  throw new ParseError(spaceTrim((block) => `
7989
8227
  Invalid FORMAT command
7990
8228
  ${block(error.message)}:
@@ -11232,9 +11470,7 @@ class JavascriptEvalExecutionTools {
11232
11470
  }
11233
11471
  }
11234
11472
  catch (error) {
11235
- if (!(error instanceof Error)) {
11236
- throw error;
11237
- }
11473
+ assertsError(error);
11238
11474
  if (error instanceof ReferenceError) {
11239
11475
  const undefinedName = error.message.split(' ')[0];
11240
11476
  /*
@@ -11509,9 +11745,7 @@ async function createCollectionFromDirectory(rootPath, tools, options) {
11509
11745
  // ---
11510
11746
  }
11511
11747
  catch (error) {
11512
- if (!(error instanceof Error)) {
11513
- throw error;
11514
- }
11748
+ assertsError(error);
11515
11749
  // TODO: [7] DRY
11516
11750
  const wrappedErrorMessage = spaceTrim((block) => `
11517
11751
  ${error.name} in pipeline ${fileName.split('\\').join('/')}⁠:
@@ -11602,9 +11836,7 @@ async function createCollectionFromDirectory(rootPath, tools, options) {
11602
11836
  }
11603
11837
  }
11604
11838
  catch (error) {
11605
- if (!(error instanceof Error)) {
11606
- throw error;
11607
- }
11839
+ assertsError(error);
11608
11840
  // TODO: [7] DRY
11609
11841
  const wrappedErrorMessage = spaceTrim((block) => `
11610
11842
  ${error.name} in pipeline ${fileName.split('\\').join('/')}⁠:
@@ -11820,7 +12052,7 @@ function $initializeMakeCommand(program) {
11820
12052
  isCacheReloaded,
11821
12053
  }; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
11822
12054
  const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
11823
- const llm = await $provideLlmToolsForCli({
12055
+ const { llm } = await $provideLlmToolsForCli({
11824
12056
  cliOptions,
11825
12057
  ...prepareAndScrapeOptions,
11826
12058
  });
@@ -12113,9 +12345,7 @@ function $initializePrettifyCommand(program) {
12113
12345
  }
12114
12346
  }
12115
12347
  catch (error) {
12116
- if (!(error instanceof Error)) {
12117
- throw error;
12118
- }
12348
+ assertsError(error);
12119
12349
  console.info(colors.red(`Prettify ${error.name} ${filename}`));
12120
12350
  console.error(colors.bgRed(`${error.name} in ${basename(__filename)}`));
12121
12351
  console.error(colors.red(error.stack || error.message));
@@ -12435,9 +12665,7 @@ function isValidPipelineString(pipelineString) {
12435
12665
  return true;
12436
12666
  }
12437
12667
  catch (error) {
12438
- if (!(error instanceof Error)) {
12439
- throw error;
12440
- }
12668
+ assertsError(error);
12441
12669
  return false;
12442
12670
  }
12443
12671
  }
@@ -12662,9 +12890,7 @@ async function runInteractiveChatbot(options) {
12662
12890
  ongoingParameters = result.outputParameters;
12663
12891
  }
12664
12892
  catch (error) {
12665
- if (!(error instanceof Error)) {
12666
- throw error;
12667
- }
12893
+ assertsError(error);
12668
12894
  // TODO: Allow to ressurect the chatbot after an error - prompt the user to continue
12669
12895
  console.error(colors.red(error.stack || error.message));
12670
12896
  return process.exit(1);
@@ -12697,7 +12923,6 @@ function $initializeRunCommand(program) {
12697
12923
  runCommand.option('-j, --json <json>', `Pass all or some input parameters as JSON record, if used the output is also returned as JSON`);
12698
12924
  runCommand.option('-s, --save-report <path>', `Save report to file`);
12699
12925
  runCommand.action(handleActionErrors(async (pipelineSource, cliOptions) => {
12700
- console.log('!!!', cliOptions);
12701
12926
  const { reload: isCacheReloaded, interactive: isInteractive, formfactor: isFormfactorUsed, json, verbose: isVerbose, saveReport, } = cliOptions;
12702
12927
  if (pipelineSource.includes('-') && normalizeToKebabCase(pipelineSource) === pipelineSource) {
12703
12928
  console.error(colors.red(`""${pipelineSource}" is not a valid command or book. See 'ptbk --help'.`));
@@ -12723,12 +12948,10 @@ function $initializeRunCommand(program) {
12723
12948
  const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
12724
12949
  let llm;
12725
12950
  try {
12726
- llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
12951
+ llm = (await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions })).llm;
12727
12952
  }
12728
12953
  catch (error) {
12729
- if (!(error instanceof Error)) {
12730
- throw error;
12731
- }
12954
+ assertsError(error);
12732
12955
  if (!error.message.includes('No LLM tools')) {
12733
12956
  throw error;
12734
12957
  }
@@ -12934,6 +13157,198 @@ function $initializeRunCommand(program) {
12934
13157
  * TODO: [🖇] What about symlinks? Maybe flag --follow-symlinks
12935
13158
  */
12936
13159
 
13160
+ // TODO: !!!! List running services from REMOTE_SERVER_URLS
13161
+ // TODO: !!!! Import directly from YML
13162
+ /**
13163
+ * @private !!!! Decide how to expose this
13164
+ */
13165
+ const openapiJson = {
13166
+ openapi: '3.0.0',
13167
+ info: {
13168
+ title: 'Promptbook Remote Server API (!!!! From TS)',
13169
+ version: '1.0.0',
13170
+ description: 'API documentation for the Promptbook Remote Server',
13171
+ },
13172
+ paths: {
13173
+ '/': {
13174
+ get: {
13175
+ summary: 'Get server details',
13176
+ description: 'Returns details about the Promptbook server.',
13177
+ responses: {
13178
+ '200': {
13179
+ description: 'Server details in markdown format.',
13180
+ },
13181
+ },
13182
+ },
13183
+ },
13184
+ '/login': {
13185
+ post: {
13186
+ summary: 'Login to the server',
13187
+ description: 'Login to the server and get identification.',
13188
+ requestBody: {
13189
+ required: true,
13190
+ content: {
13191
+ 'application/json': {
13192
+ schema: {
13193
+ type: 'object',
13194
+ properties: {
13195
+ username: {
13196
+ type: 'string',
13197
+ },
13198
+ password: {
13199
+ type: 'string',
13200
+ },
13201
+ appId: {
13202
+ type: 'string',
13203
+ },
13204
+ },
13205
+ },
13206
+ },
13207
+ },
13208
+ },
13209
+ responses: {
13210
+ '200': {
13211
+ description: 'Successful login',
13212
+ content: {
13213
+ 'application/json': {
13214
+ schema: {
13215
+ type: 'object',
13216
+ properties: {
13217
+ identification: {
13218
+ type: 'object',
13219
+ },
13220
+ },
13221
+ },
13222
+ },
13223
+ },
13224
+ },
13225
+ },
13226
+ },
13227
+ },
13228
+ '/books': {
13229
+ get: {
13230
+ summary: 'List all books',
13231
+ description: 'Returns a list of all available books in the collection.',
13232
+ responses: {
13233
+ '200': {
13234
+ description: 'A list of books.',
13235
+ content: {
13236
+ 'application/json': {
13237
+ schema: {
13238
+ type: 'array',
13239
+ items: {
13240
+ type: 'string',
13241
+ },
13242
+ },
13243
+ },
13244
+ },
13245
+ },
13246
+ },
13247
+ },
13248
+ },
13249
+ '/books/{bookId}': {
13250
+ get: {
13251
+ summary: 'Get book content',
13252
+ description: 'Returns the content of a specific book.',
13253
+ parameters: [
13254
+ {
13255
+ in: 'path',
13256
+ name: 'bookId',
13257
+ required: true,
13258
+ schema: {
13259
+ type: 'string',
13260
+ },
13261
+ description: 'The ID of the book to retrieve.',
13262
+ },
13263
+ ],
13264
+ responses: {
13265
+ '200': {
13266
+ description: 'The content of the book.',
13267
+ content: {
13268
+ 'text/markdown': {
13269
+ schema: {
13270
+ type: 'string',
13271
+ },
13272
+ },
13273
+ },
13274
+ },
13275
+ '404': {
13276
+ description: 'Book not found.',
13277
+ },
13278
+ },
13279
+ },
13280
+ },
13281
+ '/executions': {
13282
+ get: {
13283
+ summary: 'List all executions',
13284
+ description: 'Returns a list of all running execution tasks.',
13285
+ responses: {
13286
+ '200': {
13287
+ description: 'A list of execution tasks.',
13288
+ content: {
13289
+ 'application/json': {
13290
+ schema: {
13291
+ type: 'array',
13292
+ items: {
13293
+ type: 'object',
13294
+ },
13295
+ },
13296
+ },
13297
+ },
13298
+ },
13299
+ },
13300
+ },
13301
+ },
13302
+ '/executions/new': {
13303
+ post: {
13304
+ summary: 'Start a new execution',
13305
+ description: 'Starts a new execution task for a given pipeline.',
13306
+ requestBody: {
13307
+ required: true,
13308
+ content: {
13309
+ 'application/json': {
13310
+ schema: {
13311
+ type: 'object',
13312
+ properties: {
13313
+ pipelineUrl: {
13314
+ type: 'string',
13315
+ },
13316
+ inputParameters: {
13317
+ type: 'object',
13318
+ },
13319
+ identification: {
13320
+ type: 'object',
13321
+ },
13322
+ },
13323
+ },
13324
+ },
13325
+ },
13326
+ },
13327
+ responses: {
13328
+ '200': {
13329
+ description: 'The newly created execution task.',
13330
+ content: {
13331
+ 'application/json': {
13332
+ schema: {
13333
+ type: 'object',
13334
+ },
13335
+ },
13336
+ },
13337
+ },
13338
+ '400': {
13339
+ description: 'Invalid input.',
13340
+ },
13341
+ },
13342
+ },
13343
+ },
13344
+ },
13345
+ components: {},
13346
+ tags: [],
13347
+ };
13348
+ /**
13349
+ * Note: [💞] Ignore a discrepancy between file name and entity name
13350
+ */
13351
+
12937
13352
  /**
12938
13353
  * Remote server is a proxy server that uses its execution tools internally and exposes the executor interface externally.
12939
13354
  *
@@ -12944,7 +13359,7 @@ function $initializeRunCommand(program) {
12944
13359
  * @public exported from `@promptbook/remote-server`
12945
13360
  */
12946
13361
  function startRemoteServer(options) {
12947
- const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
13362
+ const { port, collection, createLlmExecutionTools, createExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
12948
13363
  isAnonymousModeAllowed: false,
12949
13364
  isApplicationModeAllowed: false,
12950
13365
  collection: null,
@@ -12952,22 +13367,6 @@ function startRemoteServer(options) {
12952
13367
  login: null,
12953
13368
  ...options,
12954
13369
  };
12955
- // <- TODO: [🦪] Some helper type to be able to use discriminant union types with destructuring
12956
- let { rootPath = '/' } = options;
12957
- if (!rootPath.startsWith('/')) {
12958
- rootPath = `/${rootPath}`;
12959
- } /* not else */
12960
- if (rootPath.endsWith('/')) {
12961
- rootPath = rootPath.slice(0, -1);
12962
- } /* not else */
12963
- if (rootPath === '/') {
12964
- rootPath = '';
12965
- }
12966
- const socketioPath = '/' +
12967
- `${rootPath}/socket.io`
12968
- .split('/')
12969
- .filter((part) => part !== '')
12970
- .join('/');
12971
13370
  const startupDate = new Date();
12972
13371
  async function getExecutionToolsFromIdentification(identification) {
12973
13372
  if (identification === null || identification === undefined) {
@@ -12990,23 +13389,25 @@ function startRemoteServer(options) {
12990
13389
  }
12991
13390
  else if (isAnonymous === false && createLlmExecutionTools !== null) {
12992
13391
  // Note: Application mode
12993
- const { appId, userId, customOptions } = identification;
12994
- llm = await createLlmExecutionTools({
12995
- appId,
12996
- userId,
12997
- customOptions,
12998
- });
13392
+ llm = await createLlmExecutionTools(identification);
12999
13393
  }
13000
13394
  else {
13001
13395
  throw new PipelineExecutionError(`You must provide either llmToolsConfiguration or non-anonymous mode must be propperly configured`);
13002
13396
  }
13003
- const fs = $provideFilesystemForNode();
13004
- const executables = await $provideExecutablesForNode();
13397
+ const customExecutionTools = createExecutionTools ? await createExecutionTools(identification) : {};
13398
+ const fs = customExecutionTools.fs || $provideFilesystemForNode();
13399
+ const executables = customExecutionTools.executables || (await $provideExecutablesForNode());
13400
+ const scrapers = customExecutionTools.scrapers || (await $provideScrapersForNode({ fs, llm, executables }));
13401
+ const script = customExecutionTools.script || (await $provideScriptingForNode({}));
13402
+ const fetch = customExecutionTools.fetch || promptbookFetch;
13403
+ const userInterface = customExecutionTools.userInterface || undefined;
13005
13404
  const tools = {
13006
13405
  llm,
13007
13406
  fs,
13008
- scrapers: await $provideScrapersForNode({ fs, llm, executables }),
13009
- script: await $provideScriptingForNode({}),
13407
+ scrapers,
13408
+ script,
13409
+ fetch,
13410
+ userInterface,
13010
13411
  };
13011
13412
  return tools;
13012
13413
  }
@@ -13016,39 +13417,27 @@ function startRemoteServer(options) {
13016
13417
  response.setHeader('X-Powered-By', 'Promptbook engine');
13017
13418
  next();
13018
13419
  });
13019
- const swaggerOptions = {
13020
- definition: {
13021
- openapi: '3.0.0',
13022
- info: {
13023
- title: 'Promptbook Remote Server API',
13024
- version: '1.0.0',
13025
- description: 'API documentation for the Promptbook Remote Server',
13026
- },
13027
- servers: [
13028
- {
13029
- url: `http://localhost:${port}${rootPath}`,
13030
- // <- TODO: !!!!! Probbably: Pass `remoteServerUrl` instead of `port` and `rootPath`
13031
- },
13032
- ],
13420
+ // TODO: !!!! Expose openapiJson to consumer and also allow to add new routes
13421
+ app.use(OpenApiValidator.middleware({
13422
+ apiSpec: openapiJson,
13423
+ ignorePaths(path) {
13424
+ return path.startsWith('/api-docs') || path.startsWith('/swagger') || path.startsWith('/openapi');
13033
13425
  },
13034
- apis: ['./src/remote-server/**/*.ts'], // Adjust path as needed
13035
- };
13036
- const swaggerSpec = swaggerJsdoc(swaggerOptions);
13037
- app.use([`/api-docs`, `${rootPath}/api-docs`], swaggerUi.serve, swaggerUi.setup(swaggerSpec));
13426
+ validateRequests: true,
13427
+ validateResponses: true,
13428
+ }));
13429
+ app.use([`/api-docs`, `/swagger`], swaggerUi.serve, swaggerUi.setup(openapiJson, {
13430
+ // customCss: '.swagger-ui .topbar { display: none }',
13431
+ // customSiteTitle: 'BRJ API',
13432
+ // customfavIcon: 'https://brj.app/favicon.ico',
13433
+ }));
13434
+ app.get(`/openapi`, (request, response) => {
13435
+ response.json(openapiJson);
13436
+ });
13038
13437
  const runningExecutionTasks = [];
13039
13438
  // <- TODO: [🤬] Identify the users
13040
13439
  // TODO: [🧠] Do here some garbage collection of finished tasks
13041
- /**
13042
- * @swagger
13043
- * /:
13044
- * get:
13045
- * summary: Get server details
13046
- * description: Returns details about the Promptbook server.
13047
- * responses:
13048
- * 200:
13049
- * description: Server details in markdown format.
13050
- */
13051
- app.get(['/', rootPath], async (request, response) => {
13440
+ app.get('/', async (request, response) => {
13052
13441
  var _a;
13053
13442
  if ((_a = request.url) === null || _a === void 0 ? void 0 : _a.includes('socket.io')) {
13054
13443
  return;
@@ -13067,8 +13456,6 @@ function startRemoteServer(options) {
13067
13456
  ## Details
13068
13457
 
13069
13458
  **Server port:** ${port}
13070
- **Server root path:** ${rootPath}
13071
- **Socket.io path:** ${socketioPath}
13072
13459
  **Startup date:** ${startupDate.toISOString()}
13073
13460
  **Anonymouse mode:** ${isAnonymousModeAllowed ? 'enabled' : 'disabled'}
13074
13461
  **Application mode:** ${isApplicationModeAllowed ? 'enabled' : 'disabled'}
@@ -13107,38 +13494,7 @@ function startRemoteServer(options) {
13107
13494
  https://github.com/webgptorg/promptbook
13108
13495
  `));
13109
13496
  });
13110
- /**
13111
- * @swagger
13112
- *
13113
- * /login:
13114
- * post:
13115
- * summary: Login to the server
13116
- * description: Login to the server and get identification.
13117
- * requestBody:
13118
- * required: true
13119
- * content:
13120
- * application/json:
13121
- * schema:
13122
- * type: object
13123
- * properties:
13124
- * username:
13125
- * type: string
13126
- * password:
13127
- * type: string
13128
- * appId:
13129
- * type: string
13130
- * responses:
13131
- * 200:
13132
- * description: Successful login
13133
- * content:
13134
- * application/json:
13135
- * schema:
13136
- * type: object
13137
- * properties:
13138
- * identification:
13139
- * type: object
13140
- */
13141
- app.post([`/login`, `${rootPath}/login`], async (request, response) => {
13497
+ app.post(`/login`, async (request, response) => {
13142
13498
  if (!isApplicationModeAllowed || login === null) {
13143
13499
  response.status(400).send('Application mode is not allowed');
13144
13500
  return;
@@ -13163,9 +13519,7 @@ function startRemoteServer(options) {
13163
13519
  return;
13164
13520
  }
13165
13521
  catch (error) {
13166
- if (!(error instanceof Error)) {
13167
- throw error;
13168
- }
13522
+ assertsError(error);
13169
13523
  if (error instanceof AuthenticationError) {
13170
13524
  response.status(401).send({
13171
13525
  isSuccess: false,
@@ -13180,23 +13534,7 @@ function startRemoteServer(options) {
13180
13534
  response.status(400).send({ error: serializeError(error) });
13181
13535
  }
13182
13536
  });
13183
- /**
13184
- * @swagger
13185
- * /books:
13186
- * get:
13187
- * summary: List all books
13188
- * description: Returns a list of all available books in the collection.
13189
- * responses:
13190
- * 200:
13191
- * description: A list of books.
13192
- * content:
13193
- * application/json:
13194
- * schema:
13195
- * type: array
13196
- * items:
13197
- * type: string
13198
- */
13199
- app.get([`/books`, `${rootPath}/books`], async (request, response) => {
13537
+ app.get(`/books`, async (request, response) => {
13200
13538
  if (collection === null) {
13201
13539
  response.status(500).send('No collection available');
13202
13540
  return;
@@ -13206,30 +13544,7 @@ function startRemoteServer(options) {
13206
13544
  response.send(pipelines);
13207
13545
  });
13208
13546
  // TODO: [🧠] Is it secure / good idea to expose source codes of hosted books
13209
- /**
13210
- * @swagger
13211
- * /books/{bookId}:
13212
- * get:
13213
- * summary: Get book content
13214
- * description: Returns the content of a specific book.
13215
- * parameters:
13216
- * - in: path
13217
- * name: bookId
13218
- * required: true
13219
- * schema:
13220
- * type: string
13221
- * description: The ID of the book to retrieve.
13222
- * responses:
13223
- * 200:
13224
- * description: The content of the book.
13225
- * content:
13226
- * text/markdown:
13227
- * schema:
13228
- * type: string
13229
- * 404:
13230
- * description: Book not found.
13231
- */
13232
- app.get([`/books/*`, `${rootPath}/books/*`], async (request, response) => {
13547
+ app.get(`/books/*`, async (request, response) => {
13233
13548
  try {
13234
13549
  if (collection === null) {
13235
13550
  response.status(500).send('No collection nor books available');
@@ -13248,9 +13563,7 @@ function startRemoteServer(options) {
13248
13563
  .send(source.content);
13249
13564
  }
13250
13565
  catch (error) {
13251
- if (!(error instanceof Error)) {
13252
- throw error;
13253
- }
13566
+ assertsError(error);
13254
13567
  response
13255
13568
  .status(404)
13256
13569
  .send({ error: serializeError(error) });
@@ -13283,26 +13596,10 @@ function startRemoteServer(options) {
13283
13596
  };
13284
13597
  }
13285
13598
  }
13286
- /**
13287
- * @swagger
13288
- * /executions:
13289
- * get:
13290
- * summary: List all executions
13291
- * description: Returns a list of all running execution tasks.
13292
- * responses:
13293
- * 200:
13294
- * description: A list of execution tasks.
13295
- * content:
13296
- * application/json:
13297
- * schema:
13298
- * type: array
13299
- * items:
13300
- * type: object
13301
- */
13302
- app.get([`/executions`, `${rootPath}/executions`], async (request, response) => {
13303
- response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)));
13599
+ app.get(`/executions`, async (request, response) => {
13600
+ response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)) /* <- TODO: satisfies paths['/executions']['get']['responses']['200']['content']['application/json'] */);
13304
13601
  });
13305
- app.get([`/executions/last`, `${rootPath}/executions/last`], async (request, response) => {
13602
+ app.get(`/executions/last`, async (request, response) => {
13306
13603
  // TODO: [🤬] Filter only for user
13307
13604
  if (runningExecutionTasks.length === 0) {
13308
13605
  response.status(404).send('No execution tasks found');
@@ -13311,7 +13608,7 @@ function startRemoteServer(options) {
13311
13608
  const lastExecutionTask = runningExecutionTasks[runningExecutionTasks.length - 1];
13312
13609
  response.send(exportExecutionTask(lastExecutionTask, true));
13313
13610
  });
13314
- app.get([`/executions/:taskId`, `${rootPath}/executions/:taskId`], async (request, response) => {
13611
+ app.get(`/executions/:taskId`, async (request, response) => {
13315
13612
  const { taskId } = request.params;
13316
13613
  // TODO: [🤬] Filter only for user
13317
13614
  const executionTask = runningExecutionTasks.find((executionTask) => executionTask.taskId === taskId);
@@ -13323,39 +13620,12 @@ function startRemoteServer(options) {
13323
13620
  }
13324
13621
  response.send(exportExecutionTask(executionTask, true));
13325
13622
  });
13326
- /**
13327
- * @swagger
13328
- * /executions/new:
13329
- * post:
13330
- * summary: Start a new execution
13331
- * description: Starts a new execution task for a given pipeline.
13332
- * requestBody:
13333
- * required: true
13334
- * content:
13335
- * application/json:
13336
- * schema:
13337
- * type: object
13338
- * properties:
13339
- * pipelineUrl:
13340
- * type: string
13341
- * inputParameters:
13342
- * type: object
13343
- * identification:
13344
- * type: object
13345
- * responses:
13346
- * 200:
13347
- * description: The newly created execution task.
13348
- * content:
13349
- * application/json:
13350
- * schema:
13351
- * type: object
13352
- * 400:
13353
- * description: Invalid input.
13354
- */
13355
- app.post([`/executions/new`, `${rootPath}/executions/new`], async (request, response) => {
13623
+ app.post(`/executions/new`, async (request, response) => {
13356
13624
  try {
13357
13625
  const { inputParameters, identification /* <- [🤬] */ } = request.body;
13358
- const pipelineUrl = request.body.pipelineUrl || request.body.book;
13626
+ const pipelineUrl = request.body
13627
+ .pipelineUrl /* <- TODO: as paths['/executions/new']['post']['requestBody']['content']['application/json'] */ ||
13628
+ request.body.book;
13359
13629
  // TODO: [🧠] Check `pipelineUrl` and `inputParameters` here or it should be responsibility of `collection.getPipelineByUrl` and `pipelineExecutor`
13360
13630
  const pipeline = await (collection === null || collection === void 0 ? void 0 : collection.getPipelineByUrl(pipelineUrl));
13361
13631
  if (pipeline === undefined) {
@@ -13369,7 +13639,7 @@ function startRemoteServer(options) {
13369
13639
  await forTime(10);
13370
13640
  // <- Note: Wait for a while to wait for quick responses or sudden but asynchronous errors
13371
13641
  // <- TODO: Put this into configuration
13372
- response.send(executionTask);
13642
+ response.send(executionTask /* <- TODO: satisfies paths['/executions/new']['post']['responses']['200']['content']['application/json'] */);
13373
13643
  /*/
13374
13644
  executionTask.asObservable().subscribe({
13375
13645
  next(partialResult) {
@@ -13389,19 +13659,24 @@ function startRemoteServer(options) {
13389
13659
  */
13390
13660
  }
13391
13661
  catch (error) {
13392
- if (!(error instanceof Error)) {
13393
- throw error;
13394
- }
13662
+ assertsError(error);
13395
13663
  response.status(400).send({ error: serializeError(error) });
13396
13664
  }
13397
13665
  });
13666
+ /**
13667
+ * Catch-all handler for unmatched routes
13668
+ */
13669
+ app.use((request, response) => {
13670
+ response.status(404).send(`URL "${request.originalUrl}" was not found on Promptbook server.`);
13671
+ });
13398
13672
  const httpServer = http.createServer(app);
13399
13673
  const server = new Server(httpServer, {
13400
- path: socketioPath,
13401
- transports: [/*'websocket', <- TODO: [🌬] Make websocket transport work */ 'polling'],
13674
+ path: '/socket.io',
13675
+ transports: ['polling', 'websocket' /*, <- TODO: [🌬] Allow to pass `transports`, add 'webtransport' */],
13402
13676
  cors: {
13403
13677
  origin: '*',
13404
13678
  methods: ['GET', 'POST'],
13679
+ // <- TODO: [🌡] Allow to pass
13405
13680
  },
13406
13681
  });
13407
13682
  server.on('connection', (socket) => {
@@ -13455,9 +13730,7 @@ function startRemoteServer(options) {
13455
13730
  socket.emit('prompt-response', { promptResult } /* <- Note: [🤛] */);
13456
13731
  }
13457
13732
  catch (error) {
13458
- if (!(error instanceof Error)) {
13459
- throw error;
13460
- }
13733
+ assertsError(error);
13461
13734
  socket.emit('error', serializeError(error) /* <- Note: [🤛] */);
13462
13735
  }
13463
13736
  finally {
@@ -13479,9 +13752,7 @@ function startRemoteServer(options) {
13479
13752
  socket.emit('listModels-response', { models } /* <- Note: [🤛] */);
13480
13753
  }
13481
13754
  catch (error) {
13482
- if (!(error instanceof Error)) {
13483
- throw error;
13484
- }
13755
+ assertsError(error);
13485
13756
  socket.emit('error', serializeError(error));
13486
13757
  }
13487
13758
  finally {
@@ -13502,9 +13773,7 @@ function startRemoteServer(options) {
13502
13773
  socket.emit('preparePipeline-response', { preparedPipeline } /* <- Note: [🤛] */);
13503
13774
  }
13504
13775
  catch (error) {
13505
- if (!(error instanceof Error)) {
13506
- throw error;
13507
- }
13776
+ assertsError(error);
13508
13777
  socket.emit('error', serializeError(error));
13509
13778
  // <- TODO: [🚋] There is a problem with the remote server handling errors and sending them back to the client
13510
13779
  }
@@ -13552,8 +13821,7 @@ function startRemoteServer(options) {
13552
13821
  };
13553
13822
  }
13554
13823
  /**
13555
- * TODO: !! Add CORS and security - probbably via `helmet`
13556
- * TODO: [👩🏾‍🤝‍🧑🏾] Allow to pass custom fetch function here - PromptbookFetch
13824
+ * TODO: [🌡] Add CORS and security - probbably via `helmet`
13557
13825
  * TODO: Split this file into multiple functions - handler for each request
13558
13826
  * TODO: Maybe use `$exportJson`
13559
13827
  * TODO: [🧠][🛍] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`
@@ -13612,9 +13880,9 @@ function $initializeStartServerCommand(program) {
13612
13880
  if (url !== null) {
13613
13881
  rootUrl = suffixUrl(url, '/books');
13614
13882
  }
13615
- let rootPath = '/';
13616
- if (url !== null) {
13617
- rootPath = url.pathname;
13883
+ if (url !== null && url.pathname !== '/' && url.pathname !== '') {
13884
+ console.error(colors.red(`URL of the server can not have path, but got "${url.pathname}"`));
13885
+ process.exit(1);
13618
13886
  }
13619
13887
  // TODO: DRY [◽]
13620
13888
  const prepareAndScrapeOptions = {
@@ -13622,7 +13890,7 @@ function $initializeStartServerCommand(program) {
13622
13890
  isCacheReloaded,
13623
13891
  }; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
13624
13892
  const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
13625
- const llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
13893
+ const { /* [0] strategy,*/ llm } = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
13626
13894
  const executables = await $provideExecutablesForNode(prepareAndScrapeOptions);
13627
13895
  const tools = {
13628
13896
  llm,
@@ -13641,7 +13909,6 @@ function $initializeStartServerCommand(program) {
13641
13909
  });
13642
13910
  // console.log(path, await collection.listPipelines());
13643
13911
  const server = startRemoteServer({
13644
- rootPath,
13645
13912
  port,
13646
13913
  isAnonymousModeAllowed,
13647
13914
  isApplicationModeAllowed: true,
@@ -13654,6 +13921,7 @@ function $initializeStartServerCommand(program) {
13654
13921
  TODO_USE({ appId, userId });
13655
13922
  return llm;
13656
13923
  },
13924
+ // <- TODO: [🧠][0] Maybe pass here strategy
13657
13925
  });
13658
13926
  keepUnused(server);
13659
13927
  // Note: Already logged by `startRemoteServer`
@@ -13695,7 +13963,7 @@ function $initializeTestCommand(program) {
13695
13963
  isCacheReloaded,
13696
13964
  }; /* <- TODO: ` satisfies PrepareAndScrapeOptions` */
13697
13965
  const fs = $provideFilesystemForNode(prepareAndScrapeOptions);
13698
- const llm = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
13966
+ const { llm } = await $provideLlmToolsForCli({ cliOptions, ...prepareAndScrapeOptions });
13699
13967
  const executables = await $provideExecutablesForNode(prepareAndScrapeOptions);
13700
13968
  tools = {
13701
13969
  llm,
@@ -13734,9 +14002,7 @@ function $initializeTestCommand(program) {
13734
14002
  }
13735
14003
  }
13736
14004
  catch (error) {
13737
- if (!(error instanceof Error)) {
13738
- throw error;
13739
- }
14005
+ assertsError(error);
13740
14006
  console.info(colors.red(`Pipeline is not valid ${filename}`));
13741
14007
  console.error(colors.bgRed(`${error.name} in ${basename(__filename)}`));
13742
14008
  console.error(colors.red(error.stack || error.message));
@@ -13960,7 +14226,25 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
13960
14226
  output: computeUsage(`$2.40 / 1M tokens`),
13961
14227
  },
13962
14228
  },
13963
- // TODO: [main] !!3 Claude 1 and 2 has also completion versions - ask Hoagy
14229
+ {
14230
+ modelVariant: 'CHAT',
14231
+ modelTitle: 'Claude 3.7 Sonnet',
14232
+ modelName: 'claude-3-7-sonnet-20250219',
14233
+ pricing: {
14234
+ prompt: computeUsage(`$3.00 / 1M tokens`),
14235
+ output: computeUsage(`$15.00 / 1M tokens`),
14236
+ },
14237
+ },
14238
+ {
14239
+ modelVariant: 'CHAT',
14240
+ modelTitle: 'Claude 3.5 Haiku',
14241
+ modelName: 'claude-3-5-haiku-20241022',
14242
+ pricing: {
14243
+ prompt: computeUsage(`$0.25 / 1M tokens`),
14244
+ output: computeUsage(`$1.25 / 1M tokens`),
14245
+ },
14246
+ },
14247
+ // <- [🕕]
13964
14248
  ],
13965
14249
  });
13966
14250
  /**
@@ -14725,7 +15009,6 @@ const OPENAI_MODELS = exportJson({
14725
15009
  prompt: computeUsage(`$5.00 / 1M tokens`),
14726
15010
  output: computeUsage(`$15.00 / 1M tokens`),
14727
15011
  },
14728
- //TODO: [main] !!3 Add gpt-4o-mini-2024-07-18 and all others to be up to date
14729
15012
  },
14730
15013
  /**/
14731
15014
  /**/
@@ -14740,6 +15023,17 @@ const OPENAI_MODELS = exportJson({
14740
15023
  },
14741
15024
  /**/
14742
15025
  /**/
15026
+ {
15027
+ modelVariant: 'CHAT',
15028
+ modelTitle: 'gpt-4o-mini',
15029
+ modelName: 'gpt-4o-mini',
15030
+ pricing: {
15031
+ prompt: computeUsage(`$3.00 / 1M tokens`),
15032
+ output: computeUsage(`$9.00 / 1M tokens`),
15033
+ },
15034
+ },
15035
+ /**/
15036
+ /**/
14743
15037
  {
14744
15038
  modelVariant: 'CHAT',
14745
15039
  modelTitle: 'o1-preview',
@@ -14819,6 +15113,7 @@ const OPENAI_MODELS = exportJson({
14819
15113
  },
14820
15114
  },
14821
15115
  /**/
15116
+ // <- [🕕]
14822
15117
  ],
14823
15118
  });
14824
15119
  /**
@@ -15387,11 +15682,17 @@ const createDeepseekExecutionTools = Object.assign((options) => {
15387
15682
  description: 'Implementation of Deepseek models',
15388
15683
  vercelProvider: deepseekVercelProvider,
15389
15684
  availableModels: [
15390
- // TODO: [🕘] Maybe list models in same way as in other providers - in separate file with metadata
15391
- 'deepseek-chat',
15392
- 'deepseek-reasoner',
15685
+ {
15686
+ modelName: 'deepseek-chat',
15687
+ modelVariant: 'CHAT',
15688
+ },
15689
+ {
15690
+ modelName: 'deepseek-reasoner',
15691
+ modelVariant: 'CHAT',
15692
+ },
15693
+ // <- [🕕]
15393
15694
  // <- TODO: How picking of the default model looks like in `createExecutionToolsFromVercelProvider`
15394
- ].map((modelName) => ({ modelName, modelVariant: 'CHAT' })),
15695
+ ],
15395
15696
  ...options,
15396
15697
  });
15397
15698
  }, {
@@ -15489,6 +15790,10 @@ const createGoogleExecutionTools = Object.assign((options) => {
15489
15790
  vercelProvider: googleGeminiVercelProvider,
15490
15791
  availableModels: [
15491
15792
  // TODO: [🕘] Maybe list models in same way as in other providers - in separate file with metadata
15793
+ 'gemini-2.5-pro-preview-03-25',
15794
+ 'gemini-2.0-flash',
15795
+ 'gemini-2.0-flash-lite',
15796
+ 'gemini-2.0-flash-thinking-exp-01-21',
15492
15797
  'gemini-1.5-flash',
15493
15798
  'gemini-1.5-flash-latest',
15494
15799
  'gemini-1.5-flash-001',
@@ -15504,6 +15809,7 @@ const createGoogleExecutionTools = Object.assign((options) => {
15504
15809
  'gemini-1.5-pro-002',
15505
15810
  'gemini-1.5-pro-exp-0827',
15506
15811
  'gemini-1.0-pro',
15812
+ // <- [🕕]
15507
15813
  ].map((modelName) => ({ modelName, modelVariant: 'CHAT' })),
15508
15814
  ...options,
15509
15815
  });
@@ -15783,6 +16089,7 @@ class OpenAiExecutionTools {
15783
16089
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
15784
16090
  }
15785
16091
  const rawResponse = await client.chat.completions.create(rawRequest).catch((error) => {
16092
+ assertsError(error);
15786
16093
  if (this.options.isVerbose) {
15787
16094
  console.info(colors.bgRed('error'), error);
15788
16095
  }
@@ -15859,6 +16166,7 @@ class OpenAiExecutionTools {
15859
16166
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
15860
16167
  }
15861
16168
  const rawResponse = await client.completions.create(rawRequest).catch((error) => {
16169
+ assertsError(error);
15862
16170
  if (this.options.isVerbose) {
15863
16171
  console.info(colors.bgRed('error'), error);
15864
16172
  }
@@ -15922,6 +16230,7 @@ class OpenAiExecutionTools {
15922
16230
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
15923
16231
  }
15924
16232
  const rawResponse = await client.embeddings.create(rawRequest).catch((error) => {
16233
+ assertsError(error);
15925
16234
  if (this.options.isVerbose) {
15926
16235
  console.info(colors.bgRed('error'), error);
15927
16236
  }