@promptbook/remote-server 0.89.0-9 โ†’ 0.92.0-10

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 (56) hide show
  1. package/README.md +9 -7
  2. package/esm/index.es.js +1133 -455
  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 +14 -4
  6. package/esm/typings/src/_packages/deepseek.index.d.ts +2 -0
  7. package/esm/typings/src/_packages/google.index.d.ts +2 -0
  8. package/esm/typings/src/_packages/types.index.d.ts +18 -0
  9. package/esm/typings/src/_packages/utils.index.d.ts +6 -0
  10. package/esm/typings/src/cli/cli-commands/login.d.ts +0 -1
  11. package/esm/typings/src/cli/common/$provideLlmToolsForCli.d.ts +16 -3
  12. package/esm/typings/src/cli/test/ptbk.d.ts +1 -1
  13. package/esm/typings/src/commands/EXPECT/expectCommandParser.d.ts +2 -0
  14. package/esm/typings/src/config.d.ts +10 -19
  15. package/esm/typings/src/conversion/archive/loadArchive.d.ts +2 -2
  16. package/esm/typings/src/errors/0-index.d.ts +7 -4
  17. package/esm/typings/src/errors/PipelineExecutionError.d.ts +1 -1
  18. package/esm/typings/src/errors/WrappedError.d.ts +10 -0
  19. package/esm/typings/src/errors/assertsError.d.ts +11 -0
  20. package/esm/typings/src/execution/CommonToolsOptions.d.ts +4 -0
  21. package/esm/typings/src/execution/PromptbookFetch.d.ts +1 -1
  22. package/esm/typings/src/execution/createPipelineExecutor/getKnowledgeForTask.d.ts +12 -0
  23. package/esm/typings/src/execution/createPipelineExecutor/getReservedParametersForTask.d.ts +5 -0
  24. package/esm/typings/src/formats/csv/utils/csvParse.d.ts +12 -0
  25. package/esm/typings/src/formats/csv/utils/isValidCsvString.d.ts +9 -0
  26. package/esm/typings/src/formats/csv/utils/isValidCsvString.test.d.ts +1 -0
  27. package/esm/typings/src/formats/json/utils/isValidJsonString.d.ts +3 -0
  28. package/esm/typings/src/formats/json/utils/jsonParse.d.ts +11 -0
  29. package/esm/typings/src/formats/xml/utils/isValidXmlString.d.ts +9 -0
  30. package/esm/typings/src/formats/xml/utils/isValidXmlString.test.d.ts +1 -0
  31. package/esm/typings/src/llm-providers/_common/filterModels.d.ts +15 -0
  32. package/esm/typings/src/llm-providers/_common/register/{$provideEnvFilepath.d.ts โ†’ $provideEnvFilename.d.ts} +2 -2
  33. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  34. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +1 -1
  35. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizzardOrCli.d.ts +11 -2
  36. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  37. package/esm/typings/src/llm-providers/_common/register/LlmToolsMetadata.d.ts +43 -0
  38. package/esm/typings/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +4 -0
  39. package/esm/typings/src/llm-providers/deepseek/deepseek-models.d.ts +23 -0
  40. package/esm/typings/src/llm-providers/google/google-models.d.ts +23 -0
  41. package/esm/typings/src/llm-providers/openai/OpenAiExecutionTools.d.ts +4 -0
  42. package/esm/typings/src/personas/preparePersona.d.ts +1 -1
  43. package/esm/typings/src/pipeline/PipelineJson/PersonaJson.d.ts +4 -2
  44. package/esm/typings/src/remote-server/openapi-types.d.ts +626 -0
  45. package/esm/typings/src/remote-server/openapi.d.ts +581 -0
  46. package/esm/typings/src/remote-server/socket-types/_subtypes/Identification.d.ts +7 -1
  47. package/esm/typings/src/remote-server/socket-types/_subtypes/identificationToPromptbookToken.d.ts +11 -0
  48. package/esm/typings/src/remote-server/socket-types/_subtypes/promptbookTokenToIdentification.d.ts +10 -0
  49. package/esm/typings/src/remote-server/startRemoteServer.d.ts +1 -2
  50. package/esm/typings/src/remote-server/types/RemoteServerOptions.d.ts +15 -9
  51. package/esm/typings/src/storage/env-storage/$EnvStorage.d.ts +40 -0
  52. package/esm/typings/src/types/typeAliases.d.ts +26 -0
  53. package/package.json +11 -7
  54. package/umd/index.umd.js +1054 -358
  55. package/umd/index.umd.js.map +1 -1
  56. package/esm/typings/src/cli/test/ptbk2.d.ts +0 -5
package/esm/index.es.js CHANGED
@@ -2,8 +2,8 @@ import colors from 'colors';
2
2
  import express from 'express';
3
3
  import http from 'http';
4
4
  import { Server } from 'socket.io';
5
- import spaceTrim$1, { spaceTrim } from 'spacetrim';
6
- import swaggerJsdoc from 'swagger-jsdoc';
5
+ import spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
6
+ import * as OpenApiValidator from 'express-openapi-validator';
7
7
  import swaggerUi from 'swagger-ui-express';
8
8
  import { forTime } from 'waitasecond';
9
9
  import { randomBytes } from 'crypto';
@@ -33,7 +33,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
33
33
  * @generated
34
34
  * @see https://github.com/webgptorg/promptbook
35
35
  */
36
- const PROMPTBOOK_ENGINE_VERSION = '0.89.0-9';
36
+ const PROMPTBOOK_ENGINE_VERSION = '0.92.0-10';
37
37
  /**
38
38
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
39
39
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -87,6 +87,7 @@ const ADMIN_GITHUB_NAME = 'hejny';
87
87
  * @public exported from `@promptbook/core`
88
88
  */
89
89
  const CLAIM = `It's time for a paradigm shift. The future of software in plain English, French or Latin`;
90
+ // <- TODO: [๐ŸŠ] Pick the best claim
90
91
  /**
91
92
  * When the title is not provided, the default title is used
92
93
  *
@@ -119,6 +120,7 @@ const VALUE_STRINGS = {
119
120
  infinity: '(infinity; โˆž)',
120
121
  negativeInfinity: '(negative infinity; -โˆž)',
121
122
  unserializable: '(unserializable value)',
123
+ circular: '(circular JSON)',
122
124
  };
123
125
  /**
124
126
  * Small number limit
@@ -158,7 +160,7 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
158
160
  */
159
161
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
160
162
  // <- TODO: [๐Ÿ•] Make also `BOOKS_DIRNAME_ALTERNATIVES`
161
- // TODO: !!!!!! Just .promptbook dir, hardocode others
163
+ // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
162
164
  /**
163
165
  * Where to store the temporary downloads
164
166
  *
@@ -213,6 +215,122 @@ true);
213
215
  * TODO: [๐Ÿง ][๐Ÿงœโ€โ™‚๏ธ] Maybe join remoteServerUrl and path into single value
214
216
  */
215
217
 
218
+ /**
219
+ * Make error report URL for the given error
220
+ *
221
+ * @private private within the repository
222
+ */
223
+ function getErrorReportUrl(error) {
224
+ const report = {
225
+ title: `๐Ÿœ Error report from ${NAME}`,
226
+ body: spaceTrim((block) => `
227
+
228
+
229
+ \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
230
+
231
+ \`\`\`
232
+ ${block(error.message || '(no error message)')}
233
+ \`\`\`
234
+
235
+
236
+ ## More info:
237
+
238
+ - **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
239
+ - **Book language version:** ${BOOK_LANGUAGE_VERSION}
240
+ - **Time:** ${new Date().toISOString()}
241
+
242
+ <details>
243
+ <summary>Stack trace:</summary>
244
+
245
+ ## Stack trace:
246
+
247
+ \`\`\`stacktrace
248
+ ${block(error.stack || '(empty)')}
249
+ \`\`\`
250
+ </details>
251
+
252
+ `),
253
+ };
254
+ const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
255
+ reportUrl.searchParams.set('labels', 'bug');
256
+ reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
257
+ reportUrl.searchParams.set('title', report.title);
258
+ reportUrl.searchParams.set('body', report.body);
259
+ return reportUrl;
260
+ }
261
+
262
+ /**
263
+ * This error type indicates that the error should not happen and its last check before crashing with some other error
264
+ *
265
+ * @public exported from `@promptbook/core`
266
+ */
267
+ class UnexpectedError extends Error {
268
+ constructor(message) {
269
+ super(spaceTrim$1((block) => `
270
+ ${block(message)}
271
+
272
+ Note: This error should not happen.
273
+ It's probbably a bug in the pipeline collection
274
+
275
+ Please report issue:
276
+ ${block(getErrorReportUrl(new Error(message)).href)}
277
+
278
+ Or contact us on ${ADMIN_EMAIL}
279
+
280
+ `));
281
+ this.name = 'UnexpectedError';
282
+ Object.setPrototypeOf(this, UnexpectedError.prototype);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
288
+ *
289
+ * @public exported from `@promptbook/core`
290
+ */
291
+ class WrappedError extends Error {
292
+ constructor(whatWasThrown) {
293
+ const tag = `[๐Ÿคฎ]`;
294
+ console.error(tag, whatWasThrown);
295
+ super(spaceTrim$1(`
296
+ Non-Error object was thrown
297
+
298
+ Note: Look for ${tag} in the console for more details
299
+ Please report issue on ${ADMIN_EMAIL}
300
+ `));
301
+ this.name = 'WrappedError';
302
+ Object.setPrototypeOf(this, WrappedError.prototype);
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Helper used in catch blocks to assert that the error is an instance of `Error`
308
+ *
309
+ * @param whatWasThrown Any object that was thrown
310
+ * @returns Nothing if the error is an instance of `Error`
311
+ * @throws `WrappedError` or `UnexpectedError` if the error is not standard
312
+ *
313
+ * @private within the repository
314
+ */
315
+ function assertsError(whatWasThrown) {
316
+ // Case 1: Handle error which was rethrown as `WrappedError`
317
+ if (whatWasThrown instanceof WrappedError) {
318
+ const wrappedError = whatWasThrown;
319
+ throw wrappedError;
320
+ }
321
+ // Case 2: Handle unexpected errors
322
+ if (whatWasThrown instanceof UnexpectedError) {
323
+ const unexpectedError = whatWasThrown;
324
+ throw unexpectedError;
325
+ }
326
+ // Case 3: Handle standard errors - keep them up to consumer
327
+ if (whatWasThrown instanceof Error) {
328
+ return;
329
+ }
330
+ // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
331
+ throw new WrappedError(whatWasThrown);
332
+ }
333
+
216
334
  /**
217
335
  * AuthenticationError is thrown from login function which is dependency of remote server
218
336
  *
@@ -257,7 +375,7 @@ class PipelineExecutionError extends Error {
257
375
  }
258
376
  }
259
377
  /**
260
- * TODO: !!!!!! Add id to all errors
378
+ * TODO: [๐Ÿง ][๐ŸŒ‚] Add id to all errors
261
379
  */
262
380
 
263
381
  /**
@@ -365,7 +483,7 @@ class LimitReachedError extends Error {
365
483
  */
366
484
  class MissingToolsError extends Error {
367
485
  constructor(message) {
368
- super(spaceTrim((block) => `
486
+ super(spaceTrim$1((block) => `
369
487
  ${block(message)}
370
488
 
371
489
  Note: You have probbably forgot to provide some tools for pipeline execution or preparation
@@ -396,7 +514,7 @@ class NotFoundError extends Error {
396
514
  */
397
515
  class NotYetImplementedError extends Error {
398
516
  constructor(message) {
399
- super(spaceTrim((block) => `
517
+ super(spaceTrim$1((block) => `
400
518
  ${block(message)}
401
519
 
402
520
  Note: This feature is not implemented yet but it will be soon.
@@ -467,74 +585,6 @@ class PromptbookFetchError extends Error {
467
585
  }
468
586
  }
469
587
 
470
- /**
471
- * Make error report URL for the given error
472
- *
473
- * @private private within the repository
474
- */
475
- function getErrorReportUrl(error) {
476
- const report = {
477
- title: `๐Ÿœ Error report from ${NAME}`,
478
- body: spaceTrim$1((block) => `
479
-
480
-
481
- \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
482
-
483
- \`\`\`
484
- ${block(error.message || '(no error message)')}
485
- \`\`\`
486
-
487
-
488
- ## More info:
489
-
490
- - **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
491
- - **Book language version:** ${BOOK_LANGUAGE_VERSION}
492
- - **Time:** ${new Date().toISOString()}
493
-
494
- <details>
495
- <summary>Stack trace:</summary>
496
-
497
- ## Stack trace:
498
-
499
- \`\`\`stacktrace
500
- ${block(error.stack || '(empty)')}
501
- \`\`\`
502
- </details>
503
-
504
- `),
505
- };
506
- const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
507
- reportUrl.searchParams.set('labels', 'bug');
508
- reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
509
- reportUrl.searchParams.set('title', report.title);
510
- reportUrl.searchParams.set('body', report.body);
511
- return reportUrl;
512
- }
513
-
514
- /**
515
- * This error type indicates that the error should not happen and its last check before crashing with some other error
516
- *
517
- * @public exported from `@promptbook/core`
518
- */
519
- class UnexpectedError extends Error {
520
- constructor(message) {
521
- super(spaceTrim((block) => `
522
- ${block(message)}
523
-
524
- Note: This error should not happen.
525
- It's probbably a bug in the pipeline collection
526
-
527
- Please report issue:
528
- ${block(getErrorReportUrl(new Error(message)).href)}
529
-
530
- Or contact us on ${ADMIN_EMAIL}
531
-
532
- `));
533
- this.name = 'UnexpectedError';
534
- Object.setPrototypeOf(this, UnexpectedError.prototype);
535
- }
536
- }
537
-
538
588
  /**
539
589
  * Index of all custom errors
540
590
  *
@@ -555,7 +605,10 @@ const PROMPTBOOK_ERRORS = {
555
605
  PipelineExecutionError,
556
606
  PipelineLogicError,
557
607
  PipelineUrlError,
608
+ AuthenticationError,
609
+ PromptbookFetchError,
558
610
  UnexpectedError,
611
+ WrappedError,
559
612
  // TODO: [๐Ÿช‘]> VersionMismatchError,
560
613
  };
561
614
  /**
@@ -572,8 +625,6 @@ const COMMON_JAVASCRIPT_ERRORS = {
572
625
  TypeError,
573
626
  URIError,
574
627
  AggregateError,
575
- AuthenticationError,
576
- PromptbookFetchError,
577
628
  /*
578
629
  Note: Not widely supported
579
630
  > InternalError,
@@ -605,7 +656,7 @@ function serializeError(error) {
605
656
  const { name, message, stack } = error;
606
657
  const { id } = error;
607
658
  if (!Object.keys(ALL_ERRORS).includes(name)) {
608
- console.error(spaceTrim$1((block) => `
659
+ console.error(spaceTrim((block) => `
609
660
 
610
661
  Cannot serialize error with name "${name}"
611
662
 
@@ -769,11 +820,11 @@ function $execCommand(options) {
769
820
  console.warn(`Command "${humanReadableCommand}" exited with code ${code}`);
770
821
  // <- TODO: [๐Ÿฎ] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
771
822
  }
772
- resolve(spaceTrim(output.join('\n')));
823
+ resolve(spaceTrim$1(output.join('\n')));
773
824
  }
774
825
  }
775
826
  else {
776
- resolve(spaceTrim(output.join('\n')));
827
+ resolve(spaceTrim$1(output.join('\n')));
777
828
  }
778
829
  };
779
830
  commandProcess.on('close', finishWithCode);
@@ -791,7 +842,7 @@ function $execCommand(options) {
791
842
  console.warn(error);
792
843
  // <- TODO: [๐Ÿฎ] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
793
844
  }
794
- resolve(spaceTrim(output.join('\n')));
845
+ resolve(spaceTrim$1(output.join('\n')));
795
846
  }
796
847
  });
797
848
  }
@@ -816,9 +867,7 @@ async function locateAppOnLinux({ linuxWhich, }) {
816
867
  return result.trim();
817
868
  }
818
869
  catch (error) {
819
- if (!(error instanceof Error)) {
820
- throw error;
821
- }
870
+ assertsError(error);
822
871
  return null;
823
872
  }
824
873
  }
@@ -896,9 +945,7 @@ async function locateAppOnMacOs({ macOsName, }) {
896
945
  return result.trim() + toExec;
897
946
  }
898
947
  catch (error) {
899
- if (!(error instanceof Error)) {
900
- throw error;
901
- }
948
+ assertsError(error);
902
949
  return null;
903
950
  }
904
951
  }
@@ -929,9 +976,7 @@ async function locateAppOnWindows({ appName, windowsSuffix, }) {
929
976
  throw new Error(`Can not locate app ${appName} on Windows.`);
930
977
  }
931
978
  catch (error) {
932
- if (!(error instanceof Error)) {
933
- throw error;
934
- }
979
+ assertsError(error);
935
980
  return null;
936
981
  }
937
982
  }
@@ -1128,7 +1173,7 @@ function checkSerializableAsJson(options) {
1128
1173
  }
1129
1174
  else if (typeof value === 'object') {
1130
1175
  if (value instanceof Date) {
1131
- throw new UnexpectedError(spaceTrim$1((block) => `
1176
+ throw new UnexpectedError(spaceTrim((block) => `
1132
1177
  \`${name}\` is Date
1133
1178
 
1134
1179
  Use \`string_date_iso8601\` instead
@@ -1147,7 +1192,7 @@ function checkSerializableAsJson(options) {
1147
1192
  throw new UnexpectedError(`${name} is RegExp`);
1148
1193
  }
1149
1194
  else if (value instanceof Error) {
1150
- throw new UnexpectedError(spaceTrim$1((block) => `
1195
+ throw new UnexpectedError(spaceTrim((block) => `
1151
1196
  \`${name}\` is unserialized Error
1152
1197
 
1153
1198
  Use function \`serializeError\`
@@ -1169,10 +1214,8 @@ function checkSerializableAsJson(options) {
1169
1214
  JSON.stringify(value); // <- TODO: [0]
1170
1215
  }
1171
1216
  catch (error) {
1172
- if (!(error instanceof Error)) {
1173
- throw error;
1174
- }
1175
- throw new UnexpectedError(spaceTrim$1((block) => `
1217
+ assertsError(error);
1218
+ throw new UnexpectedError(spaceTrim((block) => `
1176
1219
  \`${name}\` is not serializable
1177
1220
 
1178
1221
  ${block(error.stack || error.message)}
@@ -1204,7 +1247,7 @@ function checkSerializableAsJson(options) {
1204
1247
  }
1205
1248
  }
1206
1249
  else {
1207
- throw new UnexpectedError(spaceTrim$1((block) => `
1250
+ throw new UnexpectedError(spaceTrim((block) => `
1208
1251
  \`${name}\` is unknown type
1209
1252
 
1210
1253
  Additional message for \`${name}\`:
@@ -1467,7 +1510,7 @@ function validatePipeline(pipeline) {
1467
1510
  if (!(error instanceof PipelineLogicError)) {
1468
1511
  throw error;
1469
1512
  }
1470
- console.error(spaceTrim((block) => `
1513
+ console.error(spaceTrim$1((block) => `
1471
1514
  Pipeline is not valid but logic errors are temporarily disabled via \`IS_PIPELINE_LOGIC_VALIDATED\`
1472
1515
 
1473
1516
  ${block(error.message)}
@@ -1494,7 +1537,7 @@ function validatePipeline_InnerFunction(pipeline) {
1494
1537
  })();
1495
1538
  if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
1496
1539
  // <- Note: [๐Ÿšฒ]
1497
- throw new PipelineLogicError(spaceTrim((block) => `
1540
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1498
1541
  Invalid promptbook URL "${pipeline.pipelineUrl}"
1499
1542
 
1500
1543
  ${block(pipelineIdentification)}
@@ -1502,7 +1545,7 @@ function validatePipeline_InnerFunction(pipeline) {
1502
1545
  }
1503
1546
  if (pipeline.bookVersion !== undefined && !isValidPromptbookVersion(pipeline.bookVersion)) {
1504
1547
  // <- Note: [๐Ÿšฒ]
1505
- throw new PipelineLogicError(spaceTrim((block) => `
1548
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1506
1549
  Invalid Promptbook Version "${pipeline.bookVersion}"
1507
1550
 
1508
1551
  ${block(pipelineIdentification)}
@@ -1511,7 +1554,7 @@ function validatePipeline_InnerFunction(pipeline) {
1511
1554
  // TODO: [๐Ÿง ] Maybe do here some propper JSON-schema / ZOD checking
1512
1555
  if (!Array.isArray(pipeline.parameters)) {
1513
1556
  // TODO: [๐Ÿง ] what is the correct error tp throw - maybe PromptbookSchemaError
1514
- throw new ParseError(spaceTrim((block) => `
1557
+ throw new ParseError(spaceTrim$1((block) => `
1515
1558
  Pipeline is valid JSON but with wrong structure
1516
1559
 
1517
1560
  \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
@@ -1522,7 +1565,7 @@ function validatePipeline_InnerFunction(pipeline) {
1522
1565
  // TODO: [๐Ÿง ] Maybe do here some propper JSON-schema / ZOD checking
1523
1566
  if (!Array.isArray(pipeline.tasks)) {
1524
1567
  // TODO: [๐Ÿง ] what is the correct error tp throw - maybe PromptbookSchemaError
1525
- throw new ParseError(spaceTrim((block) => `
1568
+ throw new ParseError(spaceTrim$1((block) => `
1526
1569
  Pipeline is valid JSON but with wrong structure
1527
1570
 
1528
1571
  \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
@@ -1548,7 +1591,7 @@ function validatePipeline_InnerFunction(pipeline) {
1548
1591
  // Note: Check each parameter individually
1549
1592
  for (const parameter of pipeline.parameters) {
1550
1593
  if (parameter.isInput && parameter.isOutput) {
1551
- throw new PipelineLogicError(spaceTrim((block) => `
1594
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1552
1595
 
1553
1596
  Parameter \`{${parameter.name}}\` can not be both input and output
1554
1597
 
@@ -1559,7 +1602,7 @@ function validatePipeline_InnerFunction(pipeline) {
1559
1602
  if (!parameter.isInput &&
1560
1603
  !parameter.isOutput &&
1561
1604
  !pipeline.tasks.some((task) => task.dependentParameterNames.includes(parameter.name))) {
1562
- throw new PipelineLogicError(spaceTrim((block) => `
1605
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1563
1606
  Parameter \`{${parameter.name}}\` is created but not used
1564
1607
 
1565
1608
  You can declare {${parameter.name}} as output parameter by adding in the header:
@@ -1571,7 +1614,7 @@ function validatePipeline_InnerFunction(pipeline) {
1571
1614
  }
1572
1615
  // Note: Testing that parameter is either input or result of some task
1573
1616
  if (!parameter.isInput && !pipeline.tasks.some((task) => task.resultingParameterName === parameter.name)) {
1574
- throw new PipelineLogicError(spaceTrim((block) => `
1617
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1575
1618
  Parameter \`{${parameter.name}}\` is declared but not defined
1576
1619
 
1577
1620
  You can do one of these:
@@ -1587,14 +1630,14 @@ function validatePipeline_InnerFunction(pipeline) {
1587
1630
  // Note: Checking each task individually
1588
1631
  for (const task of pipeline.tasks) {
1589
1632
  if (definedParameters.has(task.resultingParameterName)) {
1590
- throw new PipelineLogicError(spaceTrim((block) => `
1633
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1591
1634
  Parameter \`{${task.resultingParameterName}}\` is defined multiple times
1592
1635
 
1593
1636
  ${block(pipelineIdentification)}
1594
1637
  `));
1595
1638
  }
1596
1639
  if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
1597
- throw new PipelineLogicError(spaceTrim((block) => `
1640
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1598
1641
  Parameter name {${task.resultingParameterName}} is reserved, please use different name
1599
1642
 
1600
1643
  ${block(pipelineIdentification)}
@@ -1604,7 +1647,7 @@ function validatePipeline_InnerFunction(pipeline) {
1604
1647
  if (task.jokerParameterNames && task.jokerParameterNames.length > 0) {
1605
1648
  if (!task.format &&
1606
1649
  !task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
1607
- throw new PipelineLogicError(spaceTrim((block) => `
1650
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1608
1651
  Joker parameters are used for {${task.resultingParameterName}} but no expectations are defined
1609
1652
 
1610
1653
  ${block(pipelineIdentification)}
@@ -1612,7 +1655,7 @@ function validatePipeline_InnerFunction(pipeline) {
1612
1655
  }
1613
1656
  for (const joker of task.jokerParameterNames) {
1614
1657
  if (!task.dependentParameterNames.includes(joker)) {
1615
- throw new PipelineLogicError(spaceTrim((block) => `
1658
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1616
1659
  Parameter \`{${joker}}\` is used for {${task.resultingParameterName}} as joker but not in \`dependentParameterNames\`
1617
1660
 
1618
1661
  ${block(pipelineIdentification)}
@@ -1623,21 +1666,21 @@ function validatePipeline_InnerFunction(pipeline) {
1623
1666
  if (task.expectations) {
1624
1667
  for (const [unit, { min, max }] of Object.entries(task.expectations)) {
1625
1668
  if (min !== undefined && max !== undefined && min > max) {
1626
- throw new PipelineLogicError(spaceTrim((block) => `
1669
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1627
1670
  Min expectation (=${min}) of ${unit} is higher than max expectation (=${max})
1628
1671
 
1629
1672
  ${block(pipelineIdentification)}
1630
1673
  `));
1631
1674
  }
1632
1675
  if (min !== undefined && min < 0) {
1633
- throw new PipelineLogicError(spaceTrim((block) => `
1676
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1634
1677
  Min expectation of ${unit} must be zero or positive
1635
1678
 
1636
1679
  ${block(pipelineIdentification)}
1637
1680
  `));
1638
1681
  }
1639
1682
  if (max !== undefined && max <= 0) {
1640
- throw new PipelineLogicError(spaceTrim((block) => `
1683
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1641
1684
  Max expectation of ${unit} must be positive
1642
1685
 
1643
1686
  ${block(pipelineIdentification)}
@@ -1659,7 +1702,7 @@ function validatePipeline_InnerFunction(pipeline) {
1659
1702
  while (unresovedTasks.length > 0) {
1660
1703
  if (loopLimit-- < 0) {
1661
1704
  // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
1662
- throw new UnexpectedError(spaceTrim((block) => `
1705
+ throw new UnexpectedError(spaceTrim$1((block) => `
1663
1706
  Loop limit reached during detection of circular dependencies in \`validatePipeline\`
1664
1707
 
1665
1708
  ${block(pipelineIdentification)}
@@ -1669,7 +1712,7 @@ function validatePipeline_InnerFunction(pipeline) {
1669
1712
  if (currentlyResovedTasks.length === 0) {
1670
1713
  throw new PipelineLogicError(
1671
1714
  // TODO: [๐ŸŽ] DRY
1672
- spaceTrim((block) => `
1715
+ spaceTrim$1((block) => `
1673
1716
 
1674
1717
  Can not resolve some parameters:
1675
1718
  Either you are using a parameter that is not defined, or there are some circular dependencies.
@@ -1744,7 +1787,7 @@ function isPipelinePrepared(pipeline) {
1744
1787
  if (pipeline.title === undefined || pipeline.title === '' || pipeline.title === DEFAULT_BOOK_TITLE) {
1745
1788
  return false;
1746
1789
  }
1747
- if (!pipeline.personas.every((persona) => persona.modelRequirements !== undefined)) {
1790
+ if (!pipeline.personas.every((persona) => persona.modelsRequirements !== undefined)) {
1748
1791
  return false;
1749
1792
  }
1750
1793
  if (!pipeline.knowledgeSources.every((knowledgeSource) => knowledgeSource.preparationIds !== undefined)) {
@@ -1771,6 +1814,9 @@ function isPipelinePrepared(pipeline) {
1771
1814
  /**
1772
1815
  * Function isValidJsonString will tell you if the string is valid JSON or not
1773
1816
  *
1817
+ * @param value The string to check
1818
+ * @returns True if the string is a valid JSON string, false otherwise
1819
+ *
1774
1820
  * @public exported from `@promptbook/utils`
1775
1821
  */
1776
1822
  function isValidJsonString(value /* <- [๐Ÿ‘จโ€โš–๏ธ] */) {
@@ -1779,9 +1825,7 @@ function isValidJsonString(value /* <- [๐Ÿ‘จโ€โš–๏ธ] */) {
1779
1825
  return true;
1780
1826
  }
1781
1827
  catch (error) {
1782
- if (!(error instanceof Error)) {
1783
- throw error;
1784
- }
1828
+ assertsError(error);
1785
1829
  if (error.message.includes('Unexpected token')) {
1786
1830
  return false;
1787
1831
  }
@@ -1789,6 +1833,45 @@ function isValidJsonString(value /* <- [๐Ÿ‘จโ€โš–๏ธ] */) {
1789
1833
  }
1790
1834
  }
1791
1835
 
1836
+ /**
1837
+ * Converts a JavaScript Object Notation (JSON) string into an object.
1838
+ *
1839
+ * Note: This is wrapper around `JSON.parse()` with better error and type handling
1840
+ *
1841
+ * @public exported from `@promptbook/utils`
1842
+ */
1843
+ function jsonParse(value) {
1844
+ if (value === undefined) {
1845
+ throw new Error(`Can not parse JSON from undefined value.`);
1846
+ }
1847
+ else if (typeof value !== 'string') {
1848
+ console.error('Can not parse JSON from non-string value.', { text: value });
1849
+ throw new Error(spaceTrim(`
1850
+ Can not parse JSON from non-string value.
1851
+
1852
+ The value type: ${typeof value}
1853
+ See more in console.
1854
+ `));
1855
+ }
1856
+ try {
1857
+ return JSON.parse(value);
1858
+ }
1859
+ catch (error) {
1860
+ if (!(error instanceof Error)) {
1861
+ throw error;
1862
+ }
1863
+ throw new Error(spaceTrim((block) => `
1864
+ ${block(error.message)}
1865
+
1866
+ The JSON text:
1867
+ ${block(value)}
1868
+ `));
1869
+ }
1870
+ }
1871
+ /**
1872
+ * TODO: !!!! Use in Promptbook.studio
1873
+ */
1874
+
1792
1875
  /**
1793
1876
  * Recursively converts JSON strings to JSON objects
1794
1877
 
@@ -1807,7 +1890,7 @@ function jsonStringsToJsons(object) {
1807
1890
  const newObject = { ...object };
1808
1891
  for (const [key, value] of Object.entries(object)) {
1809
1892
  if (typeof value === 'string' && isValidJsonString(value)) {
1810
- newObject[key] = JSON.parse(value);
1893
+ newObject[key] = jsonParse(value);
1811
1894
  }
1812
1895
  else {
1813
1896
  newObject[key] = jsonStringsToJsons(value);
@@ -1833,7 +1916,7 @@ function deserializeError(error) {
1833
1916
  message = `${name}: ${message}`;
1834
1917
  }
1835
1918
  if (stack !== undefined && stack !== '') {
1836
- message = spaceTrim$1((block) => `
1919
+ message = spaceTrim((block) => `
1837
1920
  ${block(message)}
1838
1921
 
1839
1922
  Original stack trace:
@@ -1870,11 +1953,11 @@ function assertsTaskSuccessful(executionResult) {
1870
1953
  throw deserializeError(errors[0]);
1871
1954
  }
1872
1955
  else {
1873
- throw new PipelineExecutionError(spaceTrim((block) => `
1956
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
1874
1957
  Multiple errors occurred during Promptbook execution
1875
1958
 
1876
1959
  ${block(errors
1877
- .map(({ name, stack, message }, index) => spaceTrim((block) => `
1960
+ .map(({ name, stack, message }, index) => spaceTrim$1((block) => `
1878
1961
  ${name} ${index + 1}:
1879
1962
  ${block(stack || message)}
1880
1963
  `))
@@ -1919,8 +2002,8 @@ function createTask(options) {
1919
2002
  updatedAt = new Date();
1920
2003
  errors.push(...executionResult.errors);
1921
2004
  warnings.push(...executionResult.warnings);
1922
- // <- TODO: !!! Only unique errors and warnings should be added (or filtered)
1923
- // TODO: [๐Ÿง ] !!! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
2005
+ // <- TODO: [๐ŸŒ‚] Only unique errors and warnings should be added (or filtered)
2006
+ // TODO: [๐Ÿง ] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
1924
2007
  // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
1925
2008
  // And delete `ExecutionTask.currentValue.preparedPipeline`
1926
2009
  assertsTaskSuccessful(executionResult);
@@ -1930,6 +2013,7 @@ function createTask(options) {
1930
2013
  partialResultSubject.next(executionResult);
1931
2014
  }
1932
2015
  catch (error) {
2016
+ assertsError(error);
1933
2017
  status = 'ERROR';
1934
2018
  errors.push(error);
1935
2019
  partialResultSubject.error(error);
@@ -1983,7 +2067,7 @@ function createTask(options) {
1983
2067
  * TODO: [๐Ÿš] Split into more files and make `PrepareTask` & `RemoteTask` + split the function
1984
2068
  */
1985
2069
 
1986
- 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"}];
2070
+ 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:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with 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\nHere are the available models:\n\n```json\n{availableModels}\n```\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:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","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 `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with 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\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\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`-> {modelsRequirements}`\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"}];
1987
2071
 
1988
2072
  /**
1989
2073
  * Checks if value is valid email
@@ -2232,7 +2316,7 @@ function pipelineJsonToString(pipelineJson) {
2232
2316
  pipelineString += '\n\n';
2233
2317
  pipelineString += '```' + contentLanguage;
2234
2318
  pipelineString += '\n';
2235
- pipelineString += spaceTrim$1(content);
2319
+ pipelineString += spaceTrim(content);
2236
2320
  // <- TODO: [main] !!3 Escape
2237
2321
  // <- TODO: [๐Ÿง ] Some clear strategy how to spaceTrim the blocks
2238
2322
  pipelineString += '\n';
@@ -2286,7 +2370,7 @@ function extractParameterNames(template) {
2286
2370
  */
2287
2371
  function unpreparePipeline(pipeline) {
2288
2372
  let { personas, knowledgeSources, tasks } = pipeline;
2289
- personas = personas.map((persona) => ({ ...persona, modelRequirements: undefined, preparationIds: undefined }));
2373
+ personas = personas.map((persona) => ({ ...persona, modelsRequirements: undefined, preparationIds: undefined }));
2290
2374
  knowledgeSources = knowledgeSources.map((knowledgeSource) => ({ ...knowledgeSource, preparationIds: undefined }));
2291
2375
  tasks = tasks.map((task) => {
2292
2376
  let { dependentParameterNames } = task;
@@ -2337,7 +2421,7 @@ class SimplePipelineCollection {
2337
2421
  for (const pipeline of pipelines) {
2338
2422
  // TODO: [๐Ÿ‘ ] DRY
2339
2423
  if (pipeline.pipelineUrl === undefined) {
2340
- throw new PipelineUrlError(spaceTrim(`
2424
+ throw new PipelineUrlError(spaceTrim$1(`
2341
2425
  Pipeline with name "${pipeline.title}" does not have defined URL
2342
2426
 
2343
2427
  File:
@@ -2359,7 +2443,7 @@ class SimplePipelineCollection {
2359
2443
  pipelineJsonToString(unpreparePipeline(pipeline)) !==
2360
2444
  pipelineJsonToString(unpreparePipeline(this.collection.get(pipeline.pipelineUrl)))) {
2361
2445
  const existing = this.collection.get(pipeline.pipelineUrl);
2362
- throw new PipelineUrlError(spaceTrim(`
2446
+ throw new PipelineUrlError(spaceTrim$1(`
2363
2447
  Pipeline with URL ${pipeline.pipelineUrl} is already in the collection ๐ŸŽ
2364
2448
 
2365
2449
  Conflicting files:
@@ -2391,13 +2475,13 @@ class SimplePipelineCollection {
2391
2475
  const pipeline = this.collection.get(url);
2392
2476
  if (!pipeline) {
2393
2477
  if (this.listPipelines().length === 0) {
2394
- throw new NotFoundError(spaceTrim(`
2478
+ throw new NotFoundError(spaceTrim$1(`
2395
2479
  Pipeline with url "${url}" not found
2396
2480
 
2397
2481
  No pipelines available
2398
2482
  `));
2399
2483
  }
2400
- throw new NotFoundError(spaceTrim((block) => `
2484
+ throw new NotFoundError(spaceTrim$1((block) => `
2401
2485
  Pipeline with url "${url}" not found
2402
2486
 
2403
2487
  Available pipelines:
@@ -2740,14 +2824,15 @@ class MultipleLlmExecutionTools {
2740
2824
  }
2741
2825
  }
2742
2826
  catch (error) {
2743
- if (!(error instanceof Error) || error instanceof UnexpectedError) {
2827
+ assertsError(error);
2828
+ if (error instanceof UnexpectedError) {
2744
2829
  throw error;
2745
2830
  }
2746
2831
  errors.push({ llmExecutionTools, error });
2747
2832
  }
2748
2833
  }
2749
2834
  if (errors.length === 1) {
2750
- throw errors[0];
2835
+ throw errors[0].error;
2751
2836
  }
2752
2837
  else if (errors.length > 1) {
2753
2838
  throw new PipelineExecutionError(
@@ -2755,7 +2840,7 @@ class MultipleLlmExecutionTools {
2755
2840
  // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
2756
2841
  // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
2757
2842
  // 3) ...
2758
- spaceTrim$1((block) => `
2843
+ spaceTrim((block) => `
2759
2844
  All execution tools failed:
2760
2845
 
2761
2846
  ${block(errors
@@ -2768,7 +2853,7 @@ class MultipleLlmExecutionTools {
2768
2853
  throw new PipelineExecutionError(`You have not provided any \`LlmExecutionTools\``);
2769
2854
  }
2770
2855
  else {
2771
- throw new PipelineExecutionError(spaceTrim$1((block) => `
2856
+ throw new PipelineExecutionError(spaceTrim((block) => `
2772
2857
  You have not provided any \`LlmExecutionTools\` that support model variant "${prompt.modelRequirements.modelVariant}"
2773
2858
 
2774
2859
  Available \`LlmExecutionTools\`:
@@ -2801,7 +2886,7 @@ class MultipleLlmExecutionTools {
2801
2886
  */
2802
2887
  function joinLlmExecutionTools(...llmExecutionTools) {
2803
2888
  if (llmExecutionTools.length === 0) {
2804
- const warningMessage = spaceTrim$1(`
2889
+ const warningMessage = spaceTrim(`
2805
2890
  You have not provided any \`LlmExecutionTools\`
2806
2891
  This means that you won't be able to execute any prompts that require large language models like GPT-4 or Anthropic's Claude.
2807
2892
 
@@ -2873,27 +2958,48 @@ async function preparePersona(personaDescription, tools, options) {
2873
2958
  pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-persona.book'),
2874
2959
  tools,
2875
2960
  });
2876
- // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
2877
2961
  const _llms = arrayableToArray(tools.llm);
2878
2962
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
2879
- const availableModels = await llmTools.listModels();
2880
- const availableModelNames = availableModels
2963
+ const availableModels = (await llmTools.listModels())
2881
2964
  .filter(({ modelVariant }) => modelVariant === 'CHAT')
2882
- .map(({ modelName }) => modelName)
2883
- .join(',');
2884
- const result = await preparePersonaExecutor({ availableModelNames, personaDescription }).asPromise();
2965
+ .map(({ modelName, modelDescription }) => ({
2966
+ modelName,
2967
+ modelDescription,
2968
+ // <- Note: `modelTitle` and `modelVariant` is not relevant for this task
2969
+ }));
2970
+ const result = await preparePersonaExecutor({
2971
+ availableModels /* <- Note: Passing as JSON */,
2972
+ personaDescription,
2973
+ }).asPromise();
2885
2974
  const { outputParameters } = result;
2886
- const { modelRequirements: modelRequirementsRaw } = outputParameters;
2887
- const modelRequirements = JSON.parse(modelRequirementsRaw);
2975
+ const { modelsRequirements: modelsRequirementsJson } = outputParameters;
2976
+ let modelsRequirementsUnchecked = jsonParse(modelsRequirementsJson);
2888
2977
  if (isVerbose) {
2889
- console.info(`PERSONA ${personaDescription}`, modelRequirements);
2978
+ console.info(`PERSONA ${personaDescription}`, modelsRequirementsUnchecked);
2890
2979
  }
2891
- const { modelName, systemMessage, temperature } = modelRequirements;
2892
- return {
2980
+ if (!Array.isArray(modelsRequirementsUnchecked)) {
2981
+ // <- TODO: Book should have syntax and system to enforce shape of JSON
2982
+ modelsRequirementsUnchecked = [modelsRequirementsUnchecked];
2983
+ /*
2984
+ throw new UnexpectedError(
2985
+ spaceTrim(
2986
+ (block) => `
2987
+ Invalid \`modelsRequirements\`:
2988
+
2989
+ \`\`\`json
2990
+ ${block(JSON.stringify(modelsRequirementsUnchecked, null, 4))}
2991
+ \`\`\`
2992
+ `,
2993
+ ),
2994
+ );
2995
+ */
2996
+ }
2997
+ const modelsRequirements = modelsRequirementsUnchecked.map((modelRequirements) => ({
2893
2998
  modelVariant: 'CHAT',
2894
- modelName,
2895
- systemMessage,
2896
- temperature,
2999
+ ...modelRequirements,
3000
+ }));
3001
+ return {
3002
+ modelsRequirements,
2897
3003
  };
2898
3004
  }
2899
3005
  /**
@@ -3092,14 +3198,14 @@ function $registeredScrapersMessage(availableScrapers) {
3092
3198
  return { ...metadata, isMetadataAviailable, isInstalled, isAvilableInTools };
3093
3199
  });
3094
3200
  if (metadata.length === 0) {
3095
- return spaceTrim$1(`
3201
+ return spaceTrim(`
3096
3202
  **No scrapers are available**
3097
3203
 
3098
3204
  This is a unexpected behavior, you are probably using some broken version of Promptbook
3099
3205
  At least there should be available the metadata of the scrapers
3100
3206
  `);
3101
3207
  }
3102
- return spaceTrim$1((block) => `
3208
+ return spaceTrim((block) => `
3103
3209
  Available scrapers are:
3104
3210
  ${block(metadata
3105
3211
  .map(({ packageName, className, isMetadataAviailable, isInstalled, mimeTypes, isAvilableInBrowser, isAvilableInTools, }, i) => {
@@ -3588,9 +3694,7 @@ const promptbookFetch = async (urlOrRequest, init) => {
3588
3694
  return await fetch(urlOrRequest, init);
3589
3695
  }
3590
3696
  catch (error) {
3591
- if (!(error instanceof Error)) {
3592
- throw error;
3593
- }
3697
+ assertsError(error);
3594
3698
  let url;
3595
3699
  if (typeof urlOrRequest === 'string') {
3596
3700
  url = urlOrRequest;
@@ -3598,7 +3702,7 @@ const promptbookFetch = async (urlOrRequest, init) => {
3598
3702
  else if (urlOrRequest instanceof Request) {
3599
3703
  url = urlOrRequest.url;
3600
3704
  }
3601
- throw new PromptbookFetchError(spaceTrim$1((block) => `
3705
+ throw new PromptbookFetchError(spaceTrim((block) => `
3602
3706
  Can not fetch "${url}"
3603
3707
 
3604
3708
  Fetch error:
@@ -3689,7 +3793,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
3689
3793
  const fileExtension = getFileExtension(filename);
3690
3794
  const mimeType = extensionToMimeType(fileExtension || '');
3691
3795
  if (!(await isFileExisting(filename, tools.fs))) {
3692
- throw new NotFoundError(spaceTrim$1((block) => `
3796
+ throw new NotFoundError(spaceTrim((block) => `
3693
3797
  Can not make source handler for file which does not exist:
3694
3798
 
3695
3799
  File:
@@ -3719,7 +3823,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
3719
3823
  > },
3720
3824
  */
3721
3825
  async asJson() {
3722
- return JSON.parse(await tools.fs.readFile(filename, 'utf-8'));
3826
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
3723
3827
  },
3724
3828
  async asText() {
3725
3829
  return await tools.fs.readFile(filename, 'utf-8');
@@ -3776,7 +3880,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
3776
3880
  // <- TODO: [๐Ÿช“] Here should be no need for spreading new array, just `partialPieces = partialPiecesUnchecked`
3777
3881
  break;
3778
3882
  }
3779
- console.warn(spaceTrim$1((block) => `
3883
+ console.warn(spaceTrim((block) => `
3780
3884
  Cannot scrape knowledge from source despite the scraper \`${scraper.metadata.className}\` supports the mime type "${sourceHandler.mimeType}".
3781
3885
 
3782
3886
  The source:
@@ -3792,7 +3896,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
3792
3896
  // <- TODO: [๐Ÿฎ] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
3793
3897
  }
3794
3898
  if (partialPieces === null) {
3795
- throw new KnowledgeScrapeError(spaceTrim$1((block) => `
3899
+ throw new KnowledgeScrapeError(spaceTrim((block) => `
3796
3900
  Cannot scrape knowledge
3797
3901
 
3798
3902
  The source:
@@ -3821,9 +3925,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
3821
3925
  knowledgePreparedUnflatten[index] = pieces;
3822
3926
  }
3823
3927
  catch (error) {
3824
- if (!(error instanceof Error)) {
3825
- throw error;
3826
- }
3928
+ assertsError(error);
3827
3929
  console.warn(error);
3828
3930
  // <- TODO: [๐Ÿฎ] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
3829
3931
  }
@@ -3870,7 +3972,7 @@ async function prepareTasks(pipeline, tools, options) {
3870
3972
  if (task.taskType === 'PROMPT_TASK' &&
3871
3973
  knowledgePiecesCount > 0 &&
3872
3974
  !dependentParameterNames.includes('knowledge')) {
3873
- preparedContent = spaceTrim(`
3975
+ preparedContent = spaceTrim$1(`
3874
3976
  {content}
3875
3977
 
3876
3978
  ## Knowledge
@@ -3979,14 +4081,14 @@ async function preparePipeline(pipeline, tools, options) {
3979
4081
  // TODO: [๐Ÿ–Œ][๐Ÿง ] Implement some `mapAsync` function
3980
4082
  const preparedPersonas = new Array(personas.length);
3981
4083
  await forEachAsync(personas, { maxParallelCount /* <- TODO: [๐Ÿช‚] When there are subtasks, this maximul limit can be broken */ }, async (persona, index) => {
3982
- const modelRequirements = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
4084
+ const { modelsRequirements } = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
3983
4085
  rootDirname,
3984
4086
  maxParallelCount /* <- TODO: [๐Ÿช‚] */,
3985
4087
  isVerbose,
3986
4088
  });
3987
4089
  const preparedPersona = {
3988
4090
  ...persona,
3989
- modelRequirements,
4091
+ modelsRequirements,
3990
4092
  preparationIds: [/* TODO: [๐ŸงŠ] -> */ currentPreparation.id],
3991
4093
  // <- TODO: [๐Ÿ™] Make some standard order of json properties
3992
4094
  };
@@ -4115,13 +4217,19 @@ function valueToString(value) {
4115
4217
  return value.toISOString();
4116
4218
  }
4117
4219
  else {
4118
- return JSON.stringify(value);
4220
+ try {
4221
+ return JSON.stringify(value);
4222
+ }
4223
+ catch (error) {
4224
+ if (error instanceof TypeError && error.message.includes('circular structure')) {
4225
+ return VALUE_STRINGS.circular;
4226
+ }
4227
+ throw error;
4228
+ }
4119
4229
  }
4120
4230
  }
4121
4231
  catch (error) {
4122
- if (!(error instanceof Error)) {
4123
- throw error;
4124
- }
4232
+ assertsError(error);
4125
4233
  console.error(error);
4126
4234
  return VALUE_STRINGS.unserializable;
4127
4235
  }
@@ -4178,10 +4286,8 @@ function extractVariablesFromJavascript(script) {
4178
4286
  }
4179
4287
  }
4180
4288
  catch (error) {
4181
- if (!(error instanceof Error)) {
4182
- throw error;
4183
- }
4184
- throw new ParseError(spaceTrim((block) => `
4289
+ assertsError(error);
4290
+ throw new ParseError(spaceTrim$1((block) => `
4185
4291
  Can not extract variables from the script
4186
4292
  ${block(error.stack || error.message)}
4187
4293
 
@@ -4299,6 +4405,46 @@ const MANDATORY_CSV_SETTINGS = Object.freeze({
4299
4405
  // encoding: 'utf-8',
4300
4406
  });
4301
4407
 
4408
+ /**
4409
+ * Function to check if a string is valid CSV
4410
+ *
4411
+ * @param value The string to check
4412
+ * @returns True if the string is a valid CSV string, false otherwise
4413
+ *
4414
+ * @public exported from `@promptbook/utils`
4415
+ */
4416
+ function isValidCsvString(value) {
4417
+ try {
4418
+ // A simple check for CSV format: at least one comma and no invalid characters
4419
+ if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
4420
+ return true;
4421
+ }
4422
+ return false;
4423
+ }
4424
+ catch (error) {
4425
+ assertsError(error);
4426
+ return false;
4427
+ }
4428
+ }
4429
+
4430
+ /**
4431
+ * Converts a CSV string into an object
4432
+ *
4433
+ * Note: This is wrapper around `papaparse.parse()` with better autohealing
4434
+ *
4435
+ * @private - for now until `@promptbook/csv` is released
4436
+ */
4437
+ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO: Make CSV Schemas */) {
4438
+ settings = { ...settings, ...MANDATORY_CSV_SETTINGS };
4439
+ // Note: Autoheal invalid '\n' characters
4440
+ if (settings.newline && !settings.newline.includes('\r') && value.includes('\r')) {
4441
+ console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
4442
+ value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
4443
+ }
4444
+ const csv = parse(value, settings);
4445
+ return csv;
4446
+ }
4447
+
4302
4448
  /**
4303
4449
  * Definition for CSV spreadsheet
4304
4450
  *
@@ -4309,7 +4455,7 @@ const CsvFormatDefinition = {
4309
4455
  formatName: 'CSV',
4310
4456
  aliases: ['SPREADSHEET', 'TABLE'],
4311
4457
  isValid(value, settings, schema) {
4312
- return true;
4458
+ return isValidCsvString(value);
4313
4459
  },
4314
4460
  canBeValid(partialValue, settings, schema) {
4315
4461
  return true;
@@ -4321,10 +4467,9 @@ const CsvFormatDefinition = {
4321
4467
  {
4322
4468
  subvalueName: 'ROW',
4323
4469
  async mapValues(value, outputParameterName, settings, mapCallback) {
4324
- // TODO: [๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ] DRY csv parsing
4325
- const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
4470
+ const csv = csvParse(value, settings);
4326
4471
  if (csv.errors.length !== 0) {
4327
- throw new CsvFormatError(spaceTrim$1((block) => `
4472
+ throw new CsvFormatError(spaceTrim((block) => `
4328
4473
  CSV parsing error
4329
4474
 
4330
4475
  Error(s) from CSV parsing:
@@ -4352,10 +4497,9 @@ const CsvFormatDefinition = {
4352
4497
  {
4353
4498
  subvalueName: 'CELL',
4354
4499
  async mapValues(value, outputParameterName, settings, mapCallback) {
4355
- // TODO: [๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ] DRY csv parsing
4356
- const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
4500
+ const csv = csvParse(value, settings);
4357
4501
  if (csv.errors.length !== 0) {
4358
- throw new CsvFormatError(spaceTrim$1((block) => `
4502
+ throw new CsvFormatError(spaceTrim((block) => `
4359
4503
  CSV parsing error
4360
4504
 
4361
4505
  Error(s) from CSV parsing:
@@ -4463,6 +4607,30 @@ const TextFormatDefinition = {
4463
4607
  * TODO: [๐Ÿข] Allow to expect something inside each item of list and other formats
4464
4608
  */
4465
4609
 
4610
+ /**
4611
+ * Function to check if a string is valid XML
4612
+ *
4613
+ * @param value
4614
+ * @returns True if the string is a valid XML string, false otherwise
4615
+ *
4616
+ * @public exported from `@promptbook/utils`
4617
+ */
4618
+ function isValidXmlString(value) {
4619
+ try {
4620
+ const parser = new DOMParser();
4621
+ const parsedDocument = parser.parseFromString(value, 'application/xml');
4622
+ const parserError = parsedDocument.getElementsByTagName('parsererror');
4623
+ if (parserError.length > 0) {
4624
+ return false;
4625
+ }
4626
+ return true;
4627
+ }
4628
+ catch (error) {
4629
+ assertsError(error);
4630
+ return false;
4631
+ }
4632
+ }
4633
+
4466
4634
  /**
4467
4635
  * Definition for XML format
4468
4636
  *
@@ -4472,7 +4640,7 @@ const XmlFormatDefinition = {
4472
4640
  formatName: 'XML',
4473
4641
  mimeType: 'application/xml',
4474
4642
  isValid(value, settings, schema) {
4475
- return true;
4643
+ return isValidXmlString(value);
4476
4644
  },
4477
4645
  canBeValid(partialValue, settings, schema) {
4478
4646
  return true;
@@ -4545,7 +4713,7 @@ function mapAvailableToExpectedParameters(options) {
4545
4713
  }
4546
4714
  // Phase 2๏ธโƒฃ: Non-matching mapping
4547
4715
  if (expectedParameterNames.size !== availableParametersNames.size) {
4548
- throw new PipelineExecutionError(spaceTrim$1((block) => `
4716
+ throw new PipelineExecutionError(spaceTrim((block) => `
4549
4717
  Can not map available parameters to expected parameters
4550
4718
 
4551
4719
  Mapped parameters:
@@ -4947,7 +5115,7 @@ async function executeAttempts(options) {
4947
5115
  const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
4948
5116
  // TODO: [๐Ÿง ][๐Ÿญ] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
4949
5117
  if (isJokerAttempt && !jokerParameterName) {
4950
- throw new UnexpectedError(spaceTrim((block) => `
5118
+ throw new UnexpectedError(spaceTrim$1((block) => `
4951
5119
  Joker not found in attempt ${attempt}
4952
5120
 
4953
5121
  ${block(pipelineIdentification)}
@@ -4958,7 +5126,7 @@ async function executeAttempts(options) {
4958
5126
  $ongoingTaskResult.$expectError = null;
4959
5127
  if (isJokerAttempt) {
4960
5128
  if (parameters[jokerParameterName] === undefined) {
4961
- throw new PipelineExecutionError(spaceTrim((block) => `
5129
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
4962
5130
  Joker parameter {${jokerParameterName}} not defined
4963
5131
 
4964
5132
  ${block(pipelineIdentification)}
@@ -5016,7 +5184,7 @@ async function executeAttempts(options) {
5016
5184
  $ongoingTaskResult.$resultString = $ongoingTaskResult.$completionResult.content;
5017
5185
  break variant;
5018
5186
  case 'EMBEDDING':
5019
- throw new PipelineExecutionError(spaceTrim((block) => `
5187
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5020
5188
  Embedding model can not be used in pipeline
5021
5189
 
5022
5190
  This should be catched during parsing
@@ -5027,7 +5195,7 @@ async function executeAttempts(options) {
5027
5195
  break variant;
5028
5196
  // <- case [๐Ÿค–]:
5029
5197
  default:
5030
- throw new PipelineExecutionError(spaceTrim((block) => `
5198
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5031
5199
  Unknown model variant "${task.modelRequirements.modelVariant}"
5032
5200
 
5033
5201
  ${block(pipelineIdentification)}
@@ -5038,14 +5206,14 @@ async function executeAttempts(options) {
5038
5206
  break;
5039
5207
  case 'SCRIPT_TASK':
5040
5208
  if (arrayableToArray(tools.script).length === 0) {
5041
- throw new PipelineExecutionError(spaceTrim((block) => `
5209
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5042
5210
  No script execution tools are available
5043
5211
 
5044
5212
  ${block(pipelineIdentification)}
5045
5213
  `));
5046
5214
  }
5047
5215
  if (!task.contentLanguage) {
5048
- throw new PipelineExecutionError(spaceTrim((block) => `
5216
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5049
5217
  Script language is not defined for SCRIPT TASK "${task.name}"
5050
5218
 
5051
5219
  ${block(pipelineIdentification)}
@@ -5062,9 +5230,7 @@ async function executeAttempts(options) {
5062
5230
  break scripts;
5063
5231
  }
5064
5232
  catch (error) {
5065
- if (!(error instanceof Error)) {
5066
- throw error;
5067
- }
5233
+ assertsError(error);
5068
5234
  if (error instanceof UnexpectedError) {
5069
5235
  throw error;
5070
5236
  }
@@ -5078,7 +5244,7 @@ async function executeAttempts(options) {
5078
5244
  throw $ongoingTaskResult.$scriptPipelineExecutionErrors[0];
5079
5245
  }
5080
5246
  else {
5081
- throw new PipelineExecutionError(spaceTrim((block) => `
5247
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5082
5248
  Script execution failed ${$ongoingTaskResult.$scriptPipelineExecutionErrors.length}x
5083
5249
 
5084
5250
  ${block(pipelineIdentification)}
@@ -5092,7 +5258,7 @@ async function executeAttempts(options) {
5092
5258
  break taskType;
5093
5259
  case 'DIALOG_TASK':
5094
5260
  if (tools.userInterface === undefined) {
5095
- throw new PipelineExecutionError(spaceTrim((block) => `
5261
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5096
5262
  User interface tools are not available
5097
5263
 
5098
5264
  ${block(pipelineIdentification)}
@@ -5110,7 +5276,7 @@ async function executeAttempts(options) {
5110
5276
  break taskType;
5111
5277
  // <- case: [๐Ÿ…ฑ]
5112
5278
  default:
5113
- throw new PipelineExecutionError(spaceTrim((block) => `
5279
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5114
5280
  Unknown execution type "${task.taskType}"
5115
5281
 
5116
5282
  ${block(pipelineIdentification)}
@@ -5134,9 +5300,7 @@ async function executeAttempts(options) {
5134
5300
  break scripts;
5135
5301
  }
5136
5302
  catch (error) {
5137
- if (!(error instanceof Error)) {
5138
- throw error;
5139
- }
5303
+ assertsError(error);
5140
5304
  if (error instanceof UnexpectedError) {
5141
5305
  throw error;
5142
5306
  }
@@ -5159,7 +5323,7 @@ async function executeAttempts(options) {
5159
5323
  }
5160
5324
  catch (error) {
5161
5325
  keepUnused(error);
5162
- throw new ExpectError(spaceTrim((block) => `
5326
+ throw new ExpectError(spaceTrim$1((block) => `
5163
5327
  Expected valid JSON string
5164
5328
 
5165
5329
  ${block(
@@ -5169,7 +5333,7 @@ async function executeAttempts(options) {
5169
5333
  }
5170
5334
  }
5171
5335
  else {
5172
- throw new UnexpectedError(spaceTrim((block) => `
5336
+ throw new UnexpectedError(spaceTrim$1((block) => `
5173
5337
  Unknown format "${task.format}"
5174
5338
 
5175
5339
  ${block(pipelineIdentification)}
@@ -5209,7 +5373,7 @@ async function executeAttempts(options) {
5209
5373
  }
5210
5374
  }
5211
5375
  if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
5212
- throw new PipelineExecutionError(spaceTrim((block) => {
5376
+ throw new PipelineExecutionError(spaceTrim$1((block) => {
5213
5377
  var _a, _b, _c;
5214
5378
  return `
5215
5379
  LLM execution failed ${maxExecutionAttempts}x
@@ -5232,7 +5396,7 @@ async function executeAttempts(options) {
5232
5396
  Last result:
5233
5397
  ${block($ongoingTaskResult.$resultString === null
5234
5398
  ? 'null'
5235
- : spaceTrim($ongoingTaskResult.$resultString)
5399
+ : spaceTrim$1($ongoingTaskResult.$resultString)
5236
5400
  .split('\n')
5237
5401
  .map((line) => `> ${line}`)
5238
5402
  .join('\n'))}
@@ -5242,7 +5406,7 @@ async function executeAttempts(options) {
5242
5406
  }
5243
5407
  }
5244
5408
  if ($ongoingTaskResult.$resultString === null) {
5245
- throw new UnexpectedError(spaceTrim((block) => `
5409
+ throw new UnexpectedError(spaceTrim$1((block) => `
5246
5410
  Something went wrong and prompt result is null
5247
5411
 
5248
5412
  ${block(pipelineIdentification)}
@@ -5265,7 +5429,7 @@ async function executeFormatSubvalues(options) {
5265
5429
  return /* not await */ executeAttempts(options);
5266
5430
  }
5267
5431
  if (jokerParameterNames.length !== 0) {
5268
- throw new UnexpectedError(spaceTrim$1((block) => `
5432
+ throw new UnexpectedError(spaceTrim((block) => `
5269
5433
  JOKER parameters are not supported together with FOREACH command
5270
5434
 
5271
5435
  [๐Ÿงžโ€โ™€๏ธ] This should be prevented in \`validatePipeline\`
@@ -5278,7 +5442,7 @@ async function executeFormatSubvalues(options) {
5278
5442
  if (formatDefinition === undefined) {
5279
5443
  throw new UnexpectedError(
5280
5444
  // <- TODO: [๐Ÿง ][๐Ÿง] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
5281
- spaceTrim$1((block) => `
5445
+ spaceTrim((block) => `
5282
5446
  Unsupported format "${task.foreach.formatName}"
5283
5447
 
5284
5448
  Available formats:
@@ -5295,7 +5459,7 @@ async function executeFormatSubvalues(options) {
5295
5459
  if (subvalueDefinition === undefined) {
5296
5460
  throw new UnexpectedError(
5297
5461
  // <- TODO: [๐Ÿง ][๐Ÿง] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
5298
- spaceTrim$1((block) => `
5462
+ spaceTrim((block) => `
5299
5463
  Unsupported subformat name "${task.foreach.subformatName}" for format "${task.foreach.formatName}"
5300
5464
 
5301
5465
  Available subformat names for format "${formatDefinition.formatName}":
@@ -5328,7 +5492,7 @@ async function executeFormatSubvalues(options) {
5328
5492
  if (!(error instanceof PipelineExecutionError)) {
5329
5493
  throw error;
5330
5494
  }
5331
- throw new PipelineExecutionError(spaceTrim$1((block) => `
5495
+ throw new PipelineExecutionError(spaceTrim((block) => `
5332
5496
  ${error.message}
5333
5497
 
5334
5498
  This is error in FOREACH command
@@ -5348,7 +5512,7 @@ async function executeFormatSubvalues(options) {
5348
5512
  ...options,
5349
5513
  priority: priority + index,
5350
5514
  parameters: allSubparameters,
5351
- pipelineIdentification: spaceTrim$1((block) => `
5515
+ pipelineIdentification: spaceTrim((block) => `
5352
5516
  ${block(pipelineIdentification)}
5353
5517
  Subparameter index: ${index}
5354
5518
  `),
@@ -5379,13 +5543,79 @@ async function getExamplesForTask(task) {
5379
5543
  /**
5380
5544
  * @@@
5381
5545
  *
5546
+ * Here is the place where RAG (retrieval-augmented generation) happens
5547
+ *
5382
5548
  * @private internal utility of `createPipelineExecutor`
5383
5549
  */
5384
5550
  async function getKnowledgeForTask(options) {
5385
- const { preparedPipeline, task } = options;
5386
- return preparedPipeline.knowledgePieces.map(({ content }) => `- ${content}`).join('\n');
5551
+ const { tools, preparedPipeline, task } = options;
5552
+ const firstKnowlegePiece = preparedPipeline.knowledgePieces[0];
5553
+ const firstKnowlegeIndex = firstKnowlegePiece === null || firstKnowlegePiece === void 0 ? void 0 : firstKnowlegePiece.index[0];
5554
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model, use also keyword search
5555
+ if (firstKnowlegePiece === undefined || firstKnowlegeIndex === undefined) {
5556
+ return 'No knowledge pieces found';
5557
+ }
5558
+ // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
5559
+ const _llms = arrayableToArray(tools.llm);
5560
+ const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
5561
+ const taskEmbeddingPrompt = {
5562
+ title: 'Knowledge Search',
5563
+ modelRequirements: {
5564
+ modelVariant: 'EMBEDDING',
5565
+ modelName: firstKnowlegeIndex.modelName,
5566
+ },
5567
+ content: task.content,
5568
+ parameters: {
5569
+ /* !!!!!!!! */
5570
+ },
5571
+ };
5572
+ const taskEmbeddingResult = await llmTools.callEmbeddingModel(taskEmbeddingPrompt);
5573
+ const knowledgePiecesWithRelevance = preparedPipeline.knowledgePieces.map((knowledgePiece) => {
5574
+ const { index } = knowledgePiece;
5575
+ const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowlegeIndex.modelName);
5576
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model
5577
+ if (knowledgePieceIndex === undefined) {
5578
+ return {
5579
+ content: knowledgePiece.content,
5580
+ relevance: 0,
5581
+ };
5582
+ }
5583
+ const relevance = computeCosineSimilarity(knowledgePieceIndex.position, taskEmbeddingResult.content);
5584
+ return {
5585
+ content: knowledgePiece.content,
5586
+ relevance,
5587
+ };
5588
+ });
5589
+ const knowledgePiecesSorted = knowledgePiecesWithRelevance.sort((a, b) => a.relevance - b.relevance);
5590
+ const knowledgePiecesLimited = knowledgePiecesSorted.slice(0, 5);
5591
+ console.log('!!! Embedding', {
5592
+ task,
5593
+ taskEmbeddingPrompt,
5594
+ taskEmbeddingResult,
5595
+ firstKnowlegePiece,
5596
+ firstKnowlegeIndex,
5597
+ knowledgePiecesWithRelevance,
5598
+ knowledgePiecesSorted,
5599
+ knowledgePiecesLimited,
5600
+ });
5601
+ return knowledgePiecesLimited.map(({ content }) => `- ${content}`).join('\n');
5387
5602
  // <- TODO: [๐Ÿง ] Some smart aggregation of knowledge pieces, single-line vs multi-line vs mixed
5388
5603
  }
5604
+ // TODO: !!!!!! Annotate + to new file
5605
+ function computeCosineSimilarity(embeddingVector1, embeddingVector2) {
5606
+ if (embeddingVector1.length !== embeddingVector2.length) {
5607
+ throw new TypeError('Embedding vectors must have the same length');
5608
+ }
5609
+ const dotProduct = embeddingVector1.reduce((sum, value, index) => sum + value * embeddingVector2[index], 0);
5610
+ const magnitude1 = Math.sqrt(embeddingVector1.reduce((sum, value) => sum + value * value, 0));
5611
+ const magnitude2 = Math.sqrt(embeddingVector2.reduce((sum, value) => sum + value * value, 0));
5612
+ return 1 - dotProduct / (magnitude1 * magnitude2);
5613
+ }
5614
+ /**
5615
+ * TODO: !!!! Verify if this is working
5616
+ * TODO: [โ™จ] Implement Better - use keyword search
5617
+ * TODO: [โ™จ] Examples of values
5618
+ */
5389
5619
 
5390
5620
  /**
5391
5621
  * @@@
@@ -5393,9 +5623,9 @@ async function getKnowledgeForTask(options) {
5393
5623
  * @private internal utility of `createPipelineExecutor`
5394
5624
  */
5395
5625
  async function getReservedParametersForTask(options) {
5396
- const { preparedPipeline, task, pipelineIdentification } = options;
5626
+ const { tools, preparedPipeline, task, pipelineIdentification } = options;
5397
5627
  const context = await getContextForTask(); // <- [๐Ÿ]
5398
- const knowledge = await getKnowledgeForTask({ preparedPipeline, task });
5628
+ const knowledge = await getKnowledgeForTask({ tools, preparedPipeline, task });
5399
5629
  const examples = await getExamplesForTask();
5400
5630
  const currentDate = new Date().toISOString(); // <- TODO: [๐Ÿง ][๐Ÿ’ฉ] Better
5401
5631
  const modelName = RESERVED_PARAMETER_MISSING_VALUE;
@@ -5410,7 +5640,7 @@ async function getReservedParametersForTask(options) {
5410
5640
  // Note: Doublecheck that ALL reserved parameters are defined:
5411
5641
  for (const parameterName of RESERVED_PARAMETER_NAMES) {
5412
5642
  if (reservedParameters[parameterName] === undefined) {
5413
- throw new UnexpectedError(spaceTrim((block) => `
5643
+ throw new UnexpectedError(spaceTrim$1((block) => `
5414
5644
  Reserved parameter {${parameterName}} is not defined
5415
5645
 
5416
5646
  ${block(pipelineIdentification)}
@@ -5438,7 +5668,7 @@ async function executeTask(options) {
5438
5668
  const dependentParameterNames = new Set(currentTask.dependentParameterNames);
5439
5669
  // TODO: [๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘ฉ๐Ÿป] Use here `mapAvailableToExpectedParameters`
5440
5670
  if (union(difference(usedParameterNames, dependentParameterNames), difference(dependentParameterNames, usedParameterNames)).size !== 0) {
5441
- throw new UnexpectedError(spaceTrim((block) => `
5671
+ throw new UnexpectedError(spaceTrim$1((block) => `
5442
5672
  Dependent parameters are not consistent with used parameters:
5443
5673
 
5444
5674
  Dependent parameters:
@@ -5457,6 +5687,7 @@ async function executeTask(options) {
5457
5687
  }
5458
5688
  const definedParameters = Object.freeze({
5459
5689
  ...(await getReservedParametersForTask({
5690
+ tools,
5460
5691
  preparedPipeline,
5461
5692
  task: currentTask,
5462
5693
  pipelineIdentification,
@@ -5478,7 +5709,7 @@ async function executeTask(options) {
5478
5709
  else if (!definedParameterNames.has(parameterName) && usedParameterNames.has(parameterName)) {
5479
5710
  // Houston, we have a problem
5480
5711
  // Note: Checking part is also done in `validatePipeline`, but itโ€™s good to doublecheck
5481
- throw new UnexpectedError(spaceTrim((block) => `
5712
+ throw new UnexpectedError(spaceTrim$1((block) => `
5482
5713
  Parameter \`{${parameterName}}\` is NOT defined
5483
5714
  BUT used in task "${currentTask.title || currentTask.name}"
5484
5715
 
@@ -5544,7 +5775,7 @@ function filterJustOutputParameters(options) {
5544
5775
  for (const parameter of preparedPipeline.parameters.filter(({ isOutput }) => isOutput)) {
5545
5776
  if (parametersToPass[parameter.name] === undefined) {
5546
5777
  // [4]
5547
- $warnings.push(new PipelineExecutionError(spaceTrim((block) => `
5778
+ $warnings.push(new PipelineExecutionError(spaceTrim$1((block) => `
5548
5779
  Parameter \`{${parameter.name}}\` should be an output parameter, but it was not generated during pipeline execution
5549
5780
 
5550
5781
  ${block(pipelineIdentification)}
@@ -5619,7 +5850,7 @@ async function executePipeline(options) {
5619
5850
  for (const parameterName of Object.keys(inputParameters)) {
5620
5851
  const parameter = preparedPipeline.parameters.find(({ name }) => name === parameterName);
5621
5852
  if (parameter === undefined) {
5622
- warnings.push(new PipelineExecutionError(spaceTrim((block) => `
5853
+ warnings.push(new PipelineExecutionError(spaceTrim$1((block) => `
5623
5854
  Extra parameter {${parameterName}} is being passed which is not part of the pipeline.
5624
5855
 
5625
5856
  ${block(pipelineIdentification)}
@@ -5634,7 +5865,7 @@ async function executePipeline(options) {
5634
5865
  // TODO: [๐Ÿง ] This should be also non-critical error
5635
5866
  return exportJson({
5636
5867
  name: 'pipelineExecutorResult',
5637
- message: spaceTrim((block) => `
5868
+ message: spaceTrim$1((block) => `
5638
5869
  Unuccessful PipelineExecutorResult (with extra parameter {${parameter.name}}) PipelineExecutorResult
5639
5870
 
5640
5871
  ${block(pipelineIdentification)}
@@ -5643,7 +5874,7 @@ async function executePipeline(options) {
5643
5874
  value: {
5644
5875
  isSuccessful: false,
5645
5876
  errors: [
5646
- new PipelineExecutionError(spaceTrim((block) => `
5877
+ new PipelineExecutionError(spaceTrim$1((block) => `
5647
5878
  Parameter \`{${parameter.name}}\` is passed as input parameter but it is not input
5648
5879
 
5649
5880
  ${block(pipelineIdentification)}
@@ -5670,7 +5901,7 @@ async function executePipeline(options) {
5670
5901
  while (unresovedTasks.length > 0) {
5671
5902
  if (loopLimit-- < 0) {
5672
5903
  // Note: Really UnexpectedError not LimitReachedError - this should be catched during validatePipeline
5673
- throw new UnexpectedError(spaceTrim((block) => `
5904
+ throw new UnexpectedError(spaceTrim$1((block) => `
5674
5905
  Loop limit reached during resolving parameters pipeline execution
5675
5906
 
5676
5907
  ${block(pipelineIdentification)}
@@ -5680,7 +5911,7 @@ async function executePipeline(options) {
5680
5911
  if (!currentTask && resolving.length === 0) {
5681
5912
  throw new UnexpectedError(
5682
5913
  // TODO: [๐ŸŽ] DRY
5683
- spaceTrim((block) => `
5914
+ spaceTrim$1((block) => `
5684
5915
  Can not resolve some parameters:
5685
5916
 
5686
5917
  ${block(pipelineIdentification)}
@@ -5720,7 +5951,7 @@ async function executePipeline(options) {
5720
5951
  tools,
5721
5952
  onProgress(newOngoingResult) {
5722
5953
  if (isReturned) {
5723
- throw new UnexpectedError(spaceTrim((block) => `
5954
+ throw new UnexpectedError(spaceTrim$1((block) => `
5724
5955
  Can not call \`onProgress\` after pipeline execution is finished
5725
5956
 
5726
5957
  ${block(pipelineIdentification)}
@@ -5736,7 +5967,7 @@ async function executePipeline(options) {
5736
5967
  }
5737
5968
  },
5738
5969
  $executionReport: executionReport,
5739
- pipelineIdentification: spaceTrim((block) => `
5970
+ pipelineIdentification: spaceTrim$1((block) => `
5740
5971
  ${block(pipelineIdentification)}
5741
5972
  Task name: ${currentTask.name}
5742
5973
  Task title: ${currentTask.title}
@@ -5757,9 +5988,7 @@ async function executePipeline(options) {
5757
5988
  await Promise.all(resolving);
5758
5989
  }
5759
5990
  catch (error /* <- Note: [3] */) {
5760
- if (!(error instanceof Error)) {
5761
- throw error;
5762
- }
5991
+ assertsError(error);
5763
5992
  // Note: No need to rethrow UnexpectedError
5764
5993
  // if (error instanceof UnexpectedError) {
5765
5994
  // Note: Count usage, [๐Ÿง ] Maybe put to separate function executionReportJsonToUsage + DRY [๐Ÿคนโ€โ™‚๏ธ]
@@ -5847,7 +6076,7 @@ function createPipelineExecutor(options) {
5847
6076
  preparedPipeline = pipeline;
5848
6077
  }
5849
6078
  else if (isNotPreparedWarningSupressed !== true) {
5850
- console.warn(spaceTrim((block) => `
6079
+ console.warn(spaceTrim$1((block) => `
5851
6080
  Pipeline is not prepared
5852
6081
 
5853
6082
  ${block(pipelineIdentification)}
@@ -5871,7 +6100,7 @@ function createPipelineExecutor(options) {
5871
6100
  inputParameters,
5872
6101
  tools,
5873
6102
  onProgress,
5874
- pipelineIdentification: spaceTrim((block) => `
6103
+ pipelineIdentification: spaceTrim$1((block) => `
5875
6104
  ${block(pipelineIdentification)}
5876
6105
  ${runCount === 1 ? '' : `Run #${runCount}`}
5877
6106
  `),
@@ -5969,13 +6198,13 @@ function $registeredLlmToolsMessage() {
5969
6198
  });
5970
6199
  const usedEnvMessage = `Unknown \`.env\` file` ;
5971
6200
  if (metadata.length === 0) {
5972
- return spaceTrim$1((block) => `
6201
+ return spaceTrim((block) => `
5973
6202
  No LLM providers are available.
5974
6203
 
5975
6204
  ${block(usedEnvMessage)}
5976
6205
  `);
5977
6206
  }
5978
- return spaceTrim$1((block) => `
6207
+ return spaceTrim((block) => `
5979
6208
 
5980
6209
  ${block(usedEnvMessage)}
5981
6210
 
@@ -6021,7 +6250,7 @@ function $registeredLlmToolsMessage() {
6021
6250
  morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
6022
6251
  }
6023
6252
  }
6024
- let providerMessage = spaceTrim$1(`
6253
+ let providerMessage = spaceTrim(`
6025
6254
  ${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
6026
6255
  ${morePieces.join('; ')}
6027
6256
  `);
@@ -6061,7 +6290,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
6061
6290
  .list()
6062
6291
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
6063
6292
  if (registeredItem === undefined) {
6064
- throw new Error(spaceTrim$1((block) => `
6293
+ throw new Error(spaceTrim((block) => `
6065
6294
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
6066
6295
 
6067
6296
  You have probably forgotten install and import the provider package.
@@ -6345,13 +6574,13 @@ function removeQuotes(text) {
6345
6574
  * @public exported from `@promptbook/utils`
6346
6575
  */
6347
6576
  function trimCodeBlock(value) {
6348
- value = spaceTrim(value);
6577
+ value = spaceTrim$1(value);
6349
6578
  if (!/^```[a-z]*(.*)```$/is.test(value)) {
6350
6579
  return value;
6351
6580
  }
6352
6581
  value = value.replace(/^```[a-z]*/i, '');
6353
6582
  value = value.replace(/```$/i, '');
6354
- value = spaceTrim(value);
6583
+ value = spaceTrim$1(value);
6355
6584
  return value;
6356
6585
  }
6357
6586
 
@@ -6364,9 +6593,9 @@ function trimCodeBlock(value) {
6364
6593
  * @public exported from `@promptbook/utils`
6365
6594
  */
6366
6595
  function trimEndOfCodeBlock(value) {
6367
- value = spaceTrim(value);
6596
+ value = spaceTrim$1(value);
6368
6597
  value = value.replace(/```$/g, '');
6369
- value = spaceTrim(value);
6598
+ value = spaceTrim$1(value);
6370
6599
  return value;
6371
6600
  }
6372
6601
 
@@ -6388,7 +6617,7 @@ function unwrapResult(text, options) {
6388
6617
  let trimmedText = text;
6389
6618
  // Remove leading and trailing spaces and newlines
6390
6619
  if (isTrimmed) {
6391
- trimmedText = spaceTrim(trimmedText);
6620
+ trimmedText = spaceTrim$1(trimmedText);
6392
6621
  }
6393
6622
  let processedText = trimmedText;
6394
6623
  if (isIntroduceSentenceRemoved) {
@@ -6397,7 +6626,7 @@ function unwrapResult(text, options) {
6397
6626
  // Remove the introduce sentence and quotes by replacing it with an empty string
6398
6627
  processedText = processedText.replace(introduceSentenceRegex, '');
6399
6628
  }
6400
- processedText = spaceTrim(processedText);
6629
+ processedText = spaceTrim$1(processedText);
6401
6630
  }
6402
6631
  if (processedText.length < 3) {
6403
6632
  return trimmedText;
@@ -6460,7 +6689,7 @@ function unwrapResult(text, options) {
6460
6689
  function extractOneBlockFromMarkdown(markdown) {
6461
6690
  const codeBlocks = extractAllBlocksFromMarkdown(markdown);
6462
6691
  if (codeBlocks.length !== 1) {
6463
- throw new ParseError(spaceTrim$1((block) => `
6692
+ throw new ParseError(spaceTrim((block) => `
6464
6693
  There should be exactly 1 code block in task section, found ${codeBlocks.length} code blocks
6465
6694
 
6466
6695
  ${block(codeBlocks.map((block, i) => `Block ${i + 1}:\n${block.content}`).join('\n\n\n'))}
@@ -6542,8 +6771,8 @@ class JavascriptEvalExecutionTools {
6542
6771
  }
6543
6772
  // Note: [๐Ÿ’Ž]
6544
6773
  // Note: Using direct eval, following variables are in same scope as eval call so they are accessible from inside the evaluated script:
6545
- const spaceTrim = (_) => spaceTrim$1(_);
6546
- preserve(spaceTrim);
6774
+ const spaceTrim$1 = (_) => spaceTrim(_);
6775
+ preserve(spaceTrim$1);
6547
6776
  const removeQuotes$1 = removeQuotes;
6548
6777
  preserve(removeQuotes$1);
6549
6778
  const unwrapResult$1 = unwrapResult;
@@ -6596,7 +6825,7 @@ class JavascriptEvalExecutionTools {
6596
6825
  // TODO: DRY [๐Ÿฏ]
6597
6826
  const buildinFunctions = {
6598
6827
  // TODO: [๐Ÿฏ] DRY all these functions across the file
6599
- spaceTrim,
6828
+ spaceTrim: spaceTrim$1,
6600
6829
  removeQuotes: removeQuotes$1,
6601
6830
  unwrapResult: unwrapResult$1,
6602
6831
  trimEndOfCodeBlock: trimEndOfCodeBlock$1,
@@ -6633,7 +6862,7 @@ class JavascriptEvalExecutionTools {
6633
6862
  .join('\n');
6634
6863
  // script = templateParameters(script, parameters);
6635
6864
  // <- TODO: [๐Ÿง ][๐Ÿฅณ] Should be this is one of two variants how to use parameters in script
6636
- const statementToEvaluate = spaceTrim$1((block) => `
6865
+ const statementToEvaluate = spaceTrim((block) => `
6637
6866
 
6638
6867
  // Build-in functions:
6639
6868
  ${block(buildinFunctionsStatement)}
@@ -6648,7 +6877,7 @@ class JavascriptEvalExecutionTools {
6648
6877
  (()=>{ ${script} })()
6649
6878
  `);
6650
6879
  if (this.options.isVerbose) {
6651
- console.info(spaceTrim$1((block) => `
6880
+ console.info(spaceTrim((block) => `
6652
6881
  ๐Ÿš€ Evaluating ${scriptLanguage} script:
6653
6882
 
6654
6883
  ${block(statementToEvaluate)}`));
@@ -6661,9 +6890,7 @@ class JavascriptEvalExecutionTools {
6661
6890
  }
6662
6891
  }
6663
6892
  catch (error) {
6664
- if (!(error instanceof Error)) {
6665
- throw error;
6666
- }
6893
+ assertsError(error);
6667
6894
  if (error instanceof ReferenceError) {
6668
6895
  const undefinedName = error.message.split(' ')[0];
6669
6896
  /*
@@ -6672,7 +6899,7 @@ class JavascriptEvalExecutionTools {
6672
6899
  To: [PipelineExecutionError: Parameter `{thing}` is not defined],
6673
6900
  */
6674
6901
  if (!statementToEvaluate.includes(undefinedName + '(')) {
6675
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6902
+ throw new PipelineExecutionError(spaceTrim((block) => `
6676
6903
 
6677
6904
  Parameter \`{${undefinedName}}\` is not defined
6678
6905
 
@@ -6694,7 +6921,7 @@ class JavascriptEvalExecutionTools {
6694
6921
  `));
6695
6922
  }
6696
6923
  else {
6697
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6924
+ throw new PipelineExecutionError(spaceTrim((block) => `
6698
6925
  Function ${undefinedName}() is not defined
6699
6926
 
6700
6927
  - Make sure that the function is one of built-in functions
@@ -6743,6 +6970,604 @@ async function $provideScriptingForNode(options) {
6743
6970
  * Note: [๐ŸŸข] Code in this file should never be never released in packages that could be imported into browser environment
6744
6971
  */
6745
6972
 
6973
+ // TODO: !!!! List running services from REMOTE_SERVER_URLS
6974
+ // TODO: !!!! Import directly from YML
6975
+ /**
6976
+ * @private !!!! Decide how to expose this
6977
+ */
6978
+ const openapiJson = {
6979
+ openapi: '3.0.0',
6980
+ info: {
6981
+ title: 'Promptbook Remote Server API (!!!! From YML)',
6982
+ version: '1.0.0',
6983
+ description: 'API documentation for the Promptbook Remote Server',
6984
+ },
6985
+ paths: {
6986
+ '/': {
6987
+ get: {
6988
+ summary: 'Get server details',
6989
+ description: 'Returns details about the Promptbook server.',
6990
+ responses: {
6991
+ '200': {
6992
+ description: 'Server details in markdown format.',
6993
+ content: {
6994
+ 'text/markdown': {
6995
+ schema: {
6996
+ type: 'string',
6997
+ },
6998
+ },
6999
+ },
7000
+ },
7001
+ },
7002
+ },
7003
+ },
7004
+ '/login': {
7005
+ post: {
7006
+ summary: 'Login to the server',
7007
+ description: 'Login to the server and get identification.',
7008
+ requestBody: {
7009
+ required: true,
7010
+ content: {
7011
+ 'application/json': {
7012
+ schema: {
7013
+ type: 'object',
7014
+ properties: {
7015
+ username: {
7016
+ type: 'string',
7017
+ },
7018
+ password: {
7019
+ type: 'string',
7020
+ },
7021
+ appId: {
7022
+ type: 'string',
7023
+ },
7024
+ },
7025
+ },
7026
+ },
7027
+ },
7028
+ },
7029
+ responses: {
7030
+ '201': {
7031
+ description: 'Successful login',
7032
+ content: {
7033
+ 'application/json': {
7034
+ schema: {
7035
+ type: 'object',
7036
+ properties: {
7037
+ isSuccess: {
7038
+ type: 'boolean',
7039
+ },
7040
+ message: {
7041
+ type: 'string',
7042
+ },
7043
+ error: {
7044
+ type: 'object',
7045
+ },
7046
+ identification: {
7047
+ type: 'object',
7048
+ },
7049
+ },
7050
+ },
7051
+ },
7052
+ },
7053
+ },
7054
+ '400': {
7055
+ description: 'Bad request or login failed',
7056
+ content: {
7057
+ 'application/json': {
7058
+ schema: {
7059
+ type: 'object',
7060
+ properties: {
7061
+ error: {
7062
+ type: 'object',
7063
+ },
7064
+ },
7065
+ },
7066
+ },
7067
+ },
7068
+ },
7069
+ '401': {
7070
+ description: 'Authentication error',
7071
+ content: {
7072
+ 'application/json': {
7073
+ schema: {
7074
+ type: 'object',
7075
+ properties: {
7076
+ isSuccess: {
7077
+ type: 'boolean',
7078
+ enum: [false],
7079
+ },
7080
+ message: {
7081
+ type: 'string',
7082
+ },
7083
+ error: {
7084
+ type: 'object',
7085
+ },
7086
+ },
7087
+ },
7088
+ },
7089
+ },
7090
+ },
7091
+ },
7092
+ },
7093
+ },
7094
+ '/books': {
7095
+ get: {
7096
+ summary: 'List all books',
7097
+ description: 'Returns a list of all available books in the collection.',
7098
+ responses: {
7099
+ '200': {
7100
+ description: 'A list of books.',
7101
+ content: {
7102
+ 'application/json': {
7103
+ schema: {
7104
+ type: 'array',
7105
+ items: {
7106
+ type: 'string',
7107
+ },
7108
+ },
7109
+ },
7110
+ },
7111
+ },
7112
+ '500': {
7113
+ description: 'No collection available',
7114
+ content: {
7115
+ 'text/plain': {
7116
+ schema: {
7117
+ type: 'string',
7118
+ },
7119
+ },
7120
+ },
7121
+ },
7122
+ },
7123
+ },
7124
+ },
7125
+ '/books/{bookId}': {
7126
+ get: {
7127
+ summary: 'Get book content',
7128
+ description: 'Returns the content of a specific book.',
7129
+ parameters: [
7130
+ {
7131
+ in: 'path',
7132
+ name: 'bookId',
7133
+ required: true,
7134
+ schema: {
7135
+ type: 'string',
7136
+ },
7137
+ description: 'The ID of the book to retrieve.',
7138
+ },
7139
+ ],
7140
+ responses: {
7141
+ '200': {
7142
+ description: 'The content of the book.',
7143
+ content: {
7144
+ 'text/markdown': {
7145
+ schema: {
7146
+ type: 'string',
7147
+ },
7148
+ },
7149
+ },
7150
+ },
7151
+ '404': {
7152
+ description: 'Book not found.',
7153
+ content: {
7154
+ 'application/json': {
7155
+ schema: {
7156
+ type: 'object',
7157
+ properties: {
7158
+ error: {
7159
+ type: 'object',
7160
+ },
7161
+ },
7162
+ },
7163
+ },
7164
+ },
7165
+ },
7166
+ '500': {
7167
+ description: 'No collection available',
7168
+ content: {
7169
+ 'text/plain': {
7170
+ schema: {
7171
+ type: 'string',
7172
+ },
7173
+ },
7174
+ },
7175
+ },
7176
+ },
7177
+ },
7178
+ },
7179
+ '/executions': {
7180
+ get: {
7181
+ summary: 'List all executions',
7182
+ description: 'Returns a list of all running execution tasks.',
7183
+ responses: {
7184
+ '200': {
7185
+ description: 'A list of execution tasks.',
7186
+ content: {
7187
+ 'application/json': {
7188
+ schema: {
7189
+ type: 'array',
7190
+ items: {
7191
+ type: 'object',
7192
+ properties: {
7193
+ nonce: {
7194
+ type: 'string',
7195
+ },
7196
+ taskId: {
7197
+ type: 'string',
7198
+ },
7199
+ taskType: {
7200
+ type: 'string',
7201
+ },
7202
+ status: {
7203
+ type: 'string',
7204
+ },
7205
+ createdAt: {
7206
+ type: 'string',
7207
+ format: 'date-time',
7208
+ },
7209
+ updatedAt: {
7210
+ type: 'string',
7211
+ format: 'date-time',
7212
+ },
7213
+ },
7214
+ },
7215
+ },
7216
+ },
7217
+ },
7218
+ },
7219
+ },
7220
+ },
7221
+ },
7222
+ '/executions/last': {
7223
+ get: {
7224
+ summary: 'Get the last execution',
7225
+ description: 'Returns details of the last execution task.',
7226
+ responses: {
7227
+ '200': {
7228
+ description: 'The last execution task with full details.',
7229
+ content: {
7230
+ 'application/json': {
7231
+ schema: {
7232
+ type: 'object',
7233
+ properties: {
7234
+ nonce: {
7235
+ type: 'string',
7236
+ },
7237
+ taskId: {
7238
+ type: 'string',
7239
+ },
7240
+ taskType: {
7241
+ type: 'string',
7242
+ },
7243
+ status: {
7244
+ type: 'string',
7245
+ },
7246
+ errors: {
7247
+ type: 'array',
7248
+ items: {
7249
+ type: 'object',
7250
+ },
7251
+ },
7252
+ warnings: {
7253
+ type: 'array',
7254
+ items: {
7255
+ type: 'object',
7256
+ },
7257
+ },
7258
+ createdAt: {
7259
+ type: 'string',
7260
+ format: 'date-time',
7261
+ },
7262
+ updatedAt: {
7263
+ type: 'string',
7264
+ format: 'date-time',
7265
+ },
7266
+ currentValue: {
7267
+ type: 'object',
7268
+ },
7269
+ },
7270
+ },
7271
+ },
7272
+ },
7273
+ },
7274
+ '404': {
7275
+ description: 'No execution tasks found.',
7276
+ content: {
7277
+ 'text/plain': {
7278
+ schema: {
7279
+ type: 'string',
7280
+ },
7281
+ },
7282
+ },
7283
+ },
7284
+ },
7285
+ },
7286
+ },
7287
+ '/executions/{taskId}': {
7288
+ get: {
7289
+ summary: 'Get specific execution',
7290
+ description: 'Returns details of a specific execution task.',
7291
+ parameters: [
7292
+ {
7293
+ in: 'path',
7294
+ name: 'taskId',
7295
+ required: true,
7296
+ schema: {
7297
+ type: 'string',
7298
+ },
7299
+ description: 'The ID of the execution task to retrieve.',
7300
+ },
7301
+ ],
7302
+ responses: {
7303
+ '200': {
7304
+ description: 'The execution task with full details.',
7305
+ content: {
7306
+ 'application/json': {
7307
+ schema: {
7308
+ type: 'object',
7309
+ properties: {
7310
+ nonce: {
7311
+ type: 'string',
7312
+ },
7313
+ taskId: {
7314
+ type: 'string',
7315
+ },
7316
+ taskType: {
7317
+ type: 'string',
7318
+ },
7319
+ status: {
7320
+ type: 'string',
7321
+ },
7322
+ errors: {
7323
+ type: 'array',
7324
+ items: {
7325
+ type: 'object',
7326
+ },
7327
+ },
7328
+ warnings: {
7329
+ type: 'array',
7330
+ items: {
7331
+ type: 'object',
7332
+ },
7333
+ },
7334
+ createdAt: {
7335
+ type: 'string',
7336
+ format: 'date-time',
7337
+ },
7338
+ updatedAt: {
7339
+ type: 'string',
7340
+ format: 'date-time',
7341
+ },
7342
+ currentValue: {
7343
+ type: 'object',
7344
+ },
7345
+ },
7346
+ },
7347
+ },
7348
+ },
7349
+ },
7350
+ '404': {
7351
+ description: 'Execution task not found.',
7352
+ content: {
7353
+ 'text/plain': {
7354
+ schema: {
7355
+ type: 'string',
7356
+ },
7357
+ },
7358
+ },
7359
+ },
7360
+ },
7361
+ },
7362
+ },
7363
+ '/executions/new': {
7364
+ post: {
7365
+ summary: 'Start a new execution',
7366
+ description: 'Starts a new execution task for a given pipeline.',
7367
+ requestBody: {
7368
+ required: true,
7369
+ content: {
7370
+ 'application/json': {
7371
+ schema: {
7372
+ type: 'object',
7373
+ properties: {
7374
+ pipelineUrl: {
7375
+ type: 'string',
7376
+ description: 'URL of the pipeline to execute',
7377
+ },
7378
+ book: {
7379
+ type: 'string',
7380
+ description: 'Alternative field for pipelineUrl',
7381
+ },
7382
+ inputParameters: {
7383
+ type: 'object',
7384
+ description: 'Parameters for pipeline execution',
7385
+ },
7386
+ identification: {
7387
+ type: 'object',
7388
+ description: 'User identification data',
7389
+ },
7390
+ },
7391
+ },
7392
+ },
7393
+ },
7394
+ },
7395
+ responses: {
7396
+ '200': {
7397
+ description: 'The newly created execution task.',
7398
+ content: {
7399
+ 'application/json': {
7400
+ schema: {
7401
+ type: 'object',
7402
+ },
7403
+ },
7404
+ },
7405
+ },
7406
+ '400': {
7407
+ description: 'Invalid input.',
7408
+ content: {
7409
+ 'application/json': {
7410
+ schema: {
7411
+ type: 'object',
7412
+ properties: {
7413
+ error: {
7414
+ type: 'object',
7415
+ },
7416
+ },
7417
+ },
7418
+ },
7419
+ },
7420
+ },
7421
+ '404': {
7422
+ description: 'Pipeline not found.',
7423
+ content: {
7424
+ 'text/plain': {
7425
+ schema: {
7426
+ type: 'string',
7427
+ },
7428
+ },
7429
+ },
7430
+ },
7431
+ },
7432
+ },
7433
+ },
7434
+ '/api-docs': {
7435
+ get: {
7436
+ summary: 'API documentation UI',
7437
+ description: 'Swagger UI for API documentation',
7438
+ responses: {
7439
+ '200': {
7440
+ description: 'HTML Swagger UI',
7441
+ },
7442
+ },
7443
+ },
7444
+ },
7445
+ '/swagger': {
7446
+ get: {
7447
+ summary: 'API documentation UI (alternative path)',
7448
+ description: 'Swagger UI for API documentation',
7449
+ responses: {
7450
+ '200': {
7451
+ description: 'HTML Swagger UI',
7452
+ },
7453
+ },
7454
+ },
7455
+ },
7456
+ '/openapi': {
7457
+ get: {
7458
+ summary: 'OpenAPI specification',
7459
+ description: 'Returns the OpenAPI JSON specification',
7460
+ responses: {
7461
+ '200': {
7462
+ description: 'OpenAPI specification',
7463
+ content: {
7464
+ 'application/json': {
7465
+ schema: {
7466
+ type: 'object',
7467
+ },
7468
+ },
7469
+ },
7470
+ },
7471
+ },
7472
+ },
7473
+ },
7474
+ },
7475
+ components: {
7476
+ schemas: {
7477
+ Error: {
7478
+ type: 'object',
7479
+ properties: {
7480
+ error: {
7481
+ type: 'object',
7482
+ },
7483
+ },
7484
+ },
7485
+ ExecutionTaskSummary: {
7486
+ type: 'object',
7487
+ properties: {
7488
+ nonce: {
7489
+ type: 'string',
7490
+ },
7491
+ taskId: {
7492
+ type: 'string',
7493
+ },
7494
+ taskType: {
7495
+ type: 'string',
7496
+ },
7497
+ status: {
7498
+ type: 'string',
7499
+ },
7500
+ createdAt: {
7501
+ type: 'string',
7502
+ format: 'date-time',
7503
+ },
7504
+ updatedAt: {
7505
+ type: 'string',
7506
+ format: 'date-time',
7507
+ },
7508
+ },
7509
+ },
7510
+ ExecutionTaskFull: {
7511
+ type: 'object',
7512
+ properties: {
7513
+ nonce: {
7514
+ type: 'string',
7515
+ },
7516
+ taskId: {
7517
+ type: 'string',
7518
+ },
7519
+ taskType: {
7520
+ type: 'string',
7521
+ },
7522
+ status: {
7523
+ type: 'string',
7524
+ },
7525
+ errors: {
7526
+ type: 'array',
7527
+ items: {
7528
+ type: 'object',
7529
+ },
7530
+ },
7531
+ warnings: {
7532
+ type: 'array',
7533
+ items: {
7534
+ type: 'object',
7535
+ },
7536
+ },
7537
+ createdAt: {
7538
+ type: 'string',
7539
+ format: 'date-time',
7540
+ },
7541
+ updatedAt: {
7542
+ type: 'string',
7543
+ format: 'date-time',
7544
+ },
7545
+ currentValue: {
7546
+ type: 'object',
7547
+ },
7548
+ },
7549
+ },
7550
+ },
7551
+ },
7552
+ tags: [
7553
+ {
7554
+ name: 'Books',
7555
+ description: 'Operations related to books and pipelines',
7556
+ },
7557
+ {
7558
+ name: 'Executions',
7559
+ description: 'Operations related to execution tasks',
7560
+ },
7561
+ {
7562
+ name: 'Authentication',
7563
+ description: 'Authentication operations',
7564
+ },
7565
+ ],
7566
+ };
7567
+ /**
7568
+ * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
7569
+ */
7570
+
6746
7571
  /**
6747
7572
  * Remote server is a proxy server that uses its execution tools internally and exposes the executor interface externally.
6748
7573
  *
@@ -6753,7 +7578,7 @@ async function $provideScriptingForNode(options) {
6753
7578
  * @public exported from `@promptbook/remote-server`
6754
7579
  */
6755
7580
  function startRemoteServer(options) {
6756
- const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
7581
+ const { port, collection, createLlmExecutionTools, createExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
6757
7582
  isAnonymousModeAllowed: false,
6758
7583
  isApplicationModeAllowed: false,
6759
7584
  collection: null,
@@ -6761,22 +7586,6 @@ function startRemoteServer(options) {
6761
7586
  login: null,
6762
7587
  ...options,
6763
7588
  };
6764
- // <- TODO: [๐Ÿฆช] Some helper type to be able to use discriminant union types with destructuring
6765
- let { rootPath = '/' } = options;
6766
- if (!rootPath.startsWith('/')) {
6767
- rootPath = `/${rootPath}`;
6768
- } /* not else */
6769
- if (rootPath.endsWith('/')) {
6770
- rootPath = rootPath.slice(0, -1);
6771
- } /* not else */
6772
- if (rootPath === '/') {
6773
- rootPath = '';
6774
- }
6775
- const socketioPath = '/' +
6776
- `${rootPath}/socket.io`
6777
- .split('/')
6778
- .filter((part) => part !== '')
6779
- .join('/');
6780
7589
  const startupDate = new Date();
6781
7590
  async function getExecutionToolsFromIdentification(identification) {
6782
7591
  if (identification === null || identification === undefined) {
@@ -6799,23 +7608,25 @@ function startRemoteServer(options) {
6799
7608
  }
6800
7609
  else if (isAnonymous === false && createLlmExecutionTools !== null) {
6801
7610
  // Note: Application mode
6802
- const { appId, userId, customOptions } = identification;
6803
- llm = await createLlmExecutionTools({
6804
- appId,
6805
- userId,
6806
- customOptions,
6807
- });
7611
+ llm = await createLlmExecutionTools(identification);
6808
7612
  }
6809
7613
  else {
6810
7614
  throw new PipelineExecutionError(`You must provide either llmToolsConfiguration or non-anonymous mode must be propperly configured`);
6811
7615
  }
6812
- const fs = $provideFilesystemForNode();
6813
- const executables = await $provideExecutablesForNode();
7616
+ const customExecutionTools = createExecutionTools ? await createExecutionTools(identification) : {};
7617
+ const fs = customExecutionTools.fs || $provideFilesystemForNode();
7618
+ const executables = customExecutionTools.executables || (await $provideExecutablesForNode());
7619
+ const scrapers = customExecutionTools.scrapers || (await $provideScrapersForNode({ fs, llm, executables }));
7620
+ const script = customExecutionTools.script || (await $provideScriptingForNode({}));
7621
+ const fetch = customExecutionTools.fetch || promptbookFetch;
7622
+ const userInterface = customExecutionTools.userInterface || undefined;
6814
7623
  const tools = {
6815
7624
  llm,
6816
7625
  fs,
6817
- scrapers: await $provideScrapersForNode({ fs, llm, executables }),
6818
- script: await $provideScriptingForNode({}),
7626
+ scrapers,
7627
+ script,
7628
+ fetch,
7629
+ userInterface,
6819
7630
  };
6820
7631
  return tools;
6821
7632
  }
@@ -6825,44 +7636,32 @@ function startRemoteServer(options) {
6825
7636
  response.setHeader('X-Powered-By', 'Promptbook engine');
6826
7637
  next();
6827
7638
  });
6828
- const swaggerOptions = {
6829
- definition: {
6830
- openapi: '3.0.0',
6831
- info: {
6832
- title: 'Promptbook Remote Server API',
6833
- version: '1.0.0',
6834
- description: 'API documentation for the Promptbook Remote Server',
6835
- },
6836
- servers: [
6837
- {
6838
- url: `http://localhost:${port}${rootPath}`,
6839
- // <- TODO: !!!!! Probbably: Pass `remoteServerUrl` instead of `port` and `rootPath`
6840
- },
6841
- ],
7639
+ // TODO: !!!! Expose openapiJson to consumer and also allow to add new routes
7640
+ app.use(OpenApiValidator.middleware({
7641
+ apiSpec: openapiJson,
7642
+ ignorePaths(path) {
7643
+ return path.startsWith('/api-docs') || path.startsWith('/swagger') || path.startsWith('/openapi');
6842
7644
  },
6843
- apis: ['./src/remote-server/**/*.ts'], // Adjust path as needed
6844
- };
6845
- const swaggerSpec = swaggerJsdoc(swaggerOptions);
6846
- app.use([`/api-docs`, `${rootPath}/api-docs`], swaggerUi.serve, swaggerUi.setup(swaggerSpec));
7645
+ validateRequests: true,
7646
+ validateResponses: true,
7647
+ }));
7648
+ app.use([`/api-docs`, `/swagger`], swaggerUi.serve, swaggerUi.setup(openapiJson, {
7649
+ // customCss: '.swagger-ui .topbar { display: none }',
7650
+ // customSiteTitle: 'BRJ API',
7651
+ // customfavIcon: 'https://brj.app/favicon.ico',
7652
+ }));
7653
+ app.get(`/openapi`, (request, response) => {
7654
+ response.json(openapiJson);
7655
+ });
6847
7656
  const runningExecutionTasks = [];
6848
7657
  // <- TODO: [๐Ÿคฌ] Identify the users
6849
7658
  // TODO: [๐Ÿง ] Do here some garbage collection of finished tasks
6850
- /**
6851
- * @swagger
6852
- * /:
6853
- * get:
6854
- * summary: Get server details
6855
- * description: Returns details about the Promptbook server.
6856
- * responses:
6857
- * 200:
6858
- * description: Server details in markdown format.
6859
- */
6860
- app.get(['/', rootPath], async (request, response) => {
7659
+ app.get('/', async (request, response) => {
6861
7660
  var _a;
6862
7661
  if ((_a = request.url) === null || _a === void 0 ? void 0 : _a.includes('socket.io')) {
6863
7662
  return;
6864
7663
  }
6865
- response.type('text/markdown').send(await spaceTrim(async (block) => `
7664
+ response.type('text/markdown').send(await spaceTrim$1(async (block) => `
6866
7665
  # Promptbook
6867
7666
 
6868
7667
  > ${block(CLAIM)}
@@ -6876,8 +7675,6 @@ function startRemoteServer(options) {
6876
7675
  ## Details
6877
7676
 
6878
7677
  **Server port:** ${port}
6879
- **Server root path:** ${rootPath}
6880
- **Socket.io path:** ${socketioPath}
6881
7678
  **Startup date:** ${startupDate.toISOString()}
6882
7679
  **Anonymouse mode:** ${isAnonymousModeAllowed ? 'enabled' : 'disabled'}
6883
7680
  **Application mode:** ${isApplicationModeAllowed ? 'enabled' : 'disabled'}
@@ -6916,38 +7713,7 @@ function startRemoteServer(options) {
6916
7713
  https://github.com/webgptorg/promptbook
6917
7714
  `));
6918
7715
  });
6919
- /**
6920
- * @swagger
6921
- *
6922
- * /login:
6923
- * post:
6924
- * summary: Login to the server
6925
- * description: Login to the server and get identification.
6926
- * requestBody:
6927
- * required: true
6928
- * content:
6929
- * application/json:
6930
- * schema:
6931
- * type: object
6932
- * properties:
6933
- * username:
6934
- * type: string
6935
- * password:
6936
- * type: string
6937
- * appId:
6938
- * type: string
6939
- * responses:
6940
- * 200:
6941
- * description: Successful login
6942
- * content:
6943
- * application/json:
6944
- * schema:
6945
- * type: object
6946
- * properties:
6947
- * identification:
6948
- * type: object
6949
- */
6950
- app.post([`/login`, `${rootPath}/login`], async (request, response) => {
7716
+ app.post(`/login`, async (request, response) => {
6951
7717
  if (!isApplicationModeAllowed || login === null) {
6952
7718
  response.status(400).send('Application mode is not allowed');
6953
7719
  return;
@@ -6972,9 +7738,7 @@ function startRemoteServer(options) {
6972
7738
  return;
6973
7739
  }
6974
7740
  catch (error) {
6975
- if (!(error instanceof Error)) {
6976
- throw error;
6977
- }
7741
+ assertsError(error);
6978
7742
  if (error instanceof AuthenticationError) {
6979
7743
  response.status(401).send({
6980
7744
  isSuccess: false,
@@ -6989,23 +7753,7 @@ function startRemoteServer(options) {
6989
7753
  response.status(400).send({ error: serializeError(error) });
6990
7754
  }
6991
7755
  });
6992
- /**
6993
- * @swagger
6994
- * /books:
6995
- * get:
6996
- * summary: List all books
6997
- * description: Returns a list of all available books in the collection.
6998
- * responses:
6999
- * 200:
7000
- * description: A list of books.
7001
- * content:
7002
- * application/json:
7003
- * schema:
7004
- * type: array
7005
- * items:
7006
- * type: string
7007
- */
7008
- app.get([`/books`, `${rootPath}/books`], async (request, response) => {
7756
+ app.get(`/books`, async (request, response) => {
7009
7757
  if (collection === null) {
7010
7758
  response.status(500).send('No collection available');
7011
7759
  return;
@@ -7015,30 +7763,7 @@ function startRemoteServer(options) {
7015
7763
  response.send(pipelines);
7016
7764
  });
7017
7765
  // TODO: [๐Ÿง ] Is it secure / good idea to expose source codes of hosted books
7018
- /**
7019
- * @swagger
7020
- * /books/{bookId}:
7021
- * get:
7022
- * summary: Get book content
7023
- * description: Returns the content of a specific book.
7024
- * parameters:
7025
- * - in: path
7026
- * name: bookId
7027
- * required: true
7028
- * schema:
7029
- * type: string
7030
- * description: The ID of the book to retrieve.
7031
- * responses:
7032
- * 200:
7033
- * description: The content of the book.
7034
- * content:
7035
- * text/markdown:
7036
- * schema:
7037
- * type: string
7038
- * 404:
7039
- * description: Book not found.
7040
- */
7041
- app.get([`/books/*`, `${rootPath}/books/*`], async (request, response) => {
7766
+ app.get(`/books/*`, async (request, response) => {
7042
7767
  try {
7043
7768
  if (collection === null) {
7044
7769
  response.status(500).send('No collection nor books available');
@@ -7057,9 +7782,7 @@ function startRemoteServer(options) {
7057
7782
  .send(source.content);
7058
7783
  }
7059
7784
  catch (error) {
7060
- if (!(error instanceof Error)) {
7061
- throw error;
7062
- }
7785
+ assertsError(error);
7063
7786
  response
7064
7787
  .status(404)
7065
7788
  .send({ error: serializeError(error) });
@@ -7092,26 +7815,10 @@ function startRemoteServer(options) {
7092
7815
  };
7093
7816
  }
7094
7817
  }
7095
- /**
7096
- * @swagger
7097
- * /executions:
7098
- * get:
7099
- * summary: List all executions
7100
- * description: Returns a list of all running execution tasks.
7101
- * responses:
7102
- * 200:
7103
- * description: A list of execution tasks.
7104
- * content:
7105
- * application/json:
7106
- * schema:
7107
- * type: array
7108
- * items:
7109
- * type: object
7110
- */
7111
- app.get([`/executions`, `${rootPath}/executions`], async (request, response) => {
7112
- response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)));
7818
+ app.get(`/executions`, async (request, response) => {
7819
+ response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)) /* <- TODO: satisfies paths['/executions']['get']['responses']['200']['content']['application/json'] */);
7113
7820
  });
7114
- app.get([`/executions/last`, `${rootPath}/executions/last`], async (request, response) => {
7821
+ app.get(`/executions/last`, async (request, response) => {
7115
7822
  // TODO: [๐Ÿคฌ] Filter only for user
7116
7823
  if (runningExecutionTasks.length === 0) {
7117
7824
  response.status(404).send('No execution tasks found');
@@ -7120,7 +7827,7 @@ function startRemoteServer(options) {
7120
7827
  const lastExecutionTask = runningExecutionTasks[runningExecutionTasks.length - 1];
7121
7828
  response.send(exportExecutionTask(lastExecutionTask, true));
7122
7829
  });
7123
- app.get([`/executions/:taskId`, `${rootPath}/executions/:taskId`], async (request, response) => {
7830
+ app.get(`/executions/:taskId`, async (request, response) => {
7124
7831
  const { taskId } = request.params;
7125
7832
  // TODO: [๐Ÿคฌ] Filter only for user
7126
7833
  const executionTask = runningExecutionTasks.find((executionTask) => executionTask.taskId === taskId);
@@ -7132,39 +7839,12 @@ function startRemoteServer(options) {
7132
7839
  }
7133
7840
  response.send(exportExecutionTask(executionTask, true));
7134
7841
  });
7135
- /**
7136
- * @swagger
7137
- * /executions/new:
7138
- * post:
7139
- * summary: Start a new execution
7140
- * description: Starts a new execution task for a given pipeline.
7141
- * requestBody:
7142
- * required: true
7143
- * content:
7144
- * application/json:
7145
- * schema:
7146
- * type: object
7147
- * properties:
7148
- * pipelineUrl:
7149
- * type: string
7150
- * inputParameters:
7151
- * type: object
7152
- * identification:
7153
- * type: object
7154
- * responses:
7155
- * 200:
7156
- * description: The newly created execution task.
7157
- * content:
7158
- * application/json:
7159
- * schema:
7160
- * type: object
7161
- * 400:
7162
- * description: Invalid input.
7163
- */
7164
- app.post([`/executions/new`, `${rootPath}/executions/new`], async (request, response) => {
7842
+ app.post(`/executions/new`, async (request, response) => {
7165
7843
  try {
7166
7844
  const { inputParameters, identification /* <- [๐Ÿคฌ] */ } = request.body;
7167
- const pipelineUrl = request.body.pipelineUrl || request.body.book;
7845
+ const pipelineUrl = request.body
7846
+ .pipelineUrl /* <- TODO: as paths['/executions/new']['post']['requestBody']['content']['application/json'] */ ||
7847
+ request.body.book;
7168
7848
  // TODO: [๐Ÿง ] Check `pipelineUrl` and `inputParameters` here or it should be responsibility of `collection.getPipelineByUrl` and `pipelineExecutor`
7169
7849
  const pipeline = await (collection === null || collection === void 0 ? void 0 : collection.getPipelineByUrl(pipelineUrl));
7170
7850
  if (pipeline === undefined) {
@@ -7178,7 +7858,7 @@ function startRemoteServer(options) {
7178
7858
  await forTime(10);
7179
7859
  // <- Note: Wait for a while to wait for quick responses or sudden but asynchronous errors
7180
7860
  // <- TODO: Put this into configuration
7181
- response.send(executionTask);
7861
+ response.send(executionTask /* <- TODO: satisfies paths['/executions/new']['post']['responses']['200']['content']['application/json'] */);
7182
7862
  /*/
7183
7863
  executionTask.asObservable().subscribe({
7184
7864
  next(partialResult) {
@@ -7198,19 +7878,24 @@ function startRemoteServer(options) {
7198
7878
  */
7199
7879
  }
7200
7880
  catch (error) {
7201
- if (!(error instanceof Error)) {
7202
- throw error;
7203
- }
7881
+ assertsError(error);
7204
7882
  response.status(400).send({ error: serializeError(error) });
7205
7883
  }
7206
7884
  });
7885
+ /**
7886
+ * Catch-all handler for unmatched routes
7887
+ */
7888
+ app.use((request, response) => {
7889
+ response.status(404).send(`URL "${request.originalUrl}" was not found on Promptbook server.`);
7890
+ });
7207
7891
  const httpServer = http.createServer(app);
7208
7892
  const server = new Server(httpServer, {
7209
- path: socketioPath,
7210
- transports: [/*'websocket', <- TODO: [๐ŸŒฌ] Make websocket transport work */ 'polling'],
7893
+ path: '/socket.io',
7894
+ transports: ['polling', 'websocket' /*, <- TODO: [๐ŸŒฌ] Allow to pass `transports`, add 'webtransport' */],
7211
7895
  cors: {
7212
7896
  origin: '*',
7213
7897
  methods: ['GET', 'POST'],
7898
+ // <- TODO: [๐ŸŒก] Allow to pass
7214
7899
  },
7215
7900
  });
7216
7901
  server.on('connection', (socket) => {
@@ -7264,9 +7949,7 @@ function startRemoteServer(options) {
7264
7949
  socket.emit('prompt-response', { promptResult } /* <- Note: [๐Ÿค›] */);
7265
7950
  }
7266
7951
  catch (error) {
7267
- if (!(error instanceof Error)) {
7268
- throw error;
7269
- }
7952
+ assertsError(error);
7270
7953
  socket.emit('error', serializeError(error) /* <- Note: [๐Ÿค›] */);
7271
7954
  }
7272
7955
  finally {
@@ -7288,9 +7971,7 @@ function startRemoteServer(options) {
7288
7971
  socket.emit('listModels-response', { models } /* <- Note: [๐Ÿค›] */);
7289
7972
  }
7290
7973
  catch (error) {
7291
- if (!(error instanceof Error)) {
7292
- throw error;
7293
- }
7974
+ assertsError(error);
7294
7975
  socket.emit('error', serializeError(error));
7295
7976
  }
7296
7977
  finally {
@@ -7311,9 +7992,7 @@ function startRemoteServer(options) {
7311
7992
  socket.emit('preparePipeline-response', { preparedPipeline } /* <- Note: [๐Ÿค›] */);
7312
7993
  }
7313
7994
  catch (error) {
7314
- if (!(error instanceof Error)) {
7315
- throw error;
7316
- }
7995
+ assertsError(error);
7317
7996
  socket.emit('error', serializeError(error));
7318
7997
  // <- TODO: [๐Ÿš‹] There is a problem with the remote server handling errors and sending them back to the client
7319
7998
  }
@@ -7361,8 +8040,7 @@ function startRemoteServer(options) {
7361
8040
  };
7362
8041
  }
7363
8042
  /**
7364
- * TODO: !! Add CORS and security - probbably via `helmet`
7365
- * TODO: [๐Ÿ‘ฉ๐Ÿพโ€๐Ÿคโ€๐Ÿง‘๐Ÿพ] Allow to pass custom fetch function here - PromptbookFetch
8043
+ * TODO: [๐ŸŒก] Add CORS and security - probbably via `helmet`
7366
8044
  * TODO: Split this file into multiple functions - handler for each request
7367
8045
  * TODO: Maybe use `$exportJson`
7368
8046
  * TODO: [๐Ÿง ][๐Ÿ›] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`