@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/umd/index.umd.js CHANGED
@@ -1,16 +1,34 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('express'), require('http'), require('socket.io'), require('spacetrim'), require('swagger-jsdoc'), require('swagger-ui-express'), require('waitasecond'), require('crypto'), require('child_process'), require('fs/promises'), require('path'), require('rxjs'), require('prettier'), require('prettier/parser-html'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('crypto-js'), require('mime-types'), require('papaparse')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'colors', 'express', 'http', 'socket.io', 'spacetrim', 'swagger-jsdoc', 'swagger-ui-express', 'waitasecond', 'crypto', 'child_process', 'fs/promises', 'path', 'rxjs', 'prettier', 'prettier/parser-html', 'crypto-js/enc-hex', 'crypto-js/sha256', 'crypto-js', 'mime-types', 'papaparse'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-remote-server"] = {}, global.colors, global.express, global.http, global.socket_io, global.spaceTrim, global.swaggerJsdoc, global.swaggerUi, global.waitasecond, global.crypto, global.child_process, global.promises, global.path, global.rxjs, global.prettier, global.parserHtml, global.hexEncoder, global.sha256, global.cryptoJs, global.mimeTypes, global.papaparse));
5
- })(this, (function (exports, colors, express, http, socket_io, spaceTrim, swaggerJsdoc, swaggerUi, waitasecond, crypto, child_process, promises, path, rxjs, prettier, parserHtml, hexEncoder, sha256, cryptoJs, mimeTypes, papaparse) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('express'), require('http'), require('socket.io'), require('spacetrim'), require('express-openapi-validator'), require('swagger-ui-express'), require('waitasecond'), require('crypto'), require('child_process'), require('fs/promises'), require('path'), require('rxjs'), require('prettier'), require('prettier/parser-html'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('crypto-js'), require('mime-types'), require('papaparse')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'colors', 'express', 'http', 'socket.io', 'spacetrim', 'express-openapi-validator', 'swagger-ui-express', 'waitasecond', 'crypto', 'child_process', 'fs/promises', 'path', 'rxjs', 'prettier', 'prettier/parser-html', 'crypto-js/enc-hex', 'crypto-js/sha256', 'crypto-js', 'mime-types', 'papaparse'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-remote-server"] = {}, global.colors, global.express, global.http, global.socket_io, global.spaceTrim, global.OpenApiValidator, global.swaggerUi, global.waitasecond, global.crypto, global.child_process, global.promises, global.path, global.rxjs, global.prettier, global.parserHtml, global.hexEncoder, global.sha256, global.cryptoJs, global.mimeTypes, global.papaparse));
5
+ })(this, (function (exports, colors, express, http, socket_io, spaceTrim, OpenApiValidator, swaggerUi, waitasecond, crypto, child_process, promises, path, rxjs, prettier, parserHtml, hexEncoder, sha256, cryptoJs, mimeTypes, papaparse) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n["default"] = e;
24
+ return Object.freeze(n);
25
+ }
26
+
9
27
  var colors__default = /*#__PURE__*/_interopDefaultLegacy(colors);
10
28
  var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
11
29
  var http__default = /*#__PURE__*/_interopDefaultLegacy(http);
12
30
  var spaceTrim__default = /*#__PURE__*/_interopDefaultLegacy(spaceTrim);
13
- var swaggerJsdoc__default = /*#__PURE__*/_interopDefaultLegacy(swaggerJsdoc);
31
+ var OpenApiValidator__namespace = /*#__PURE__*/_interopNamespace(OpenApiValidator);
14
32
  var swaggerUi__default = /*#__PURE__*/_interopDefaultLegacy(swaggerUi);
15
33
  var parserHtml__default = /*#__PURE__*/_interopDefaultLegacy(parserHtml);
16
34
  var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
@@ -30,7 +48,7 @@
30
48
  * @generated
31
49
  * @see https://github.com/webgptorg/promptbook
32
50
  */
33
- const PROMPTBOOK_ENGINE_VERSION = '0.89.0-9';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.92.0-10';
34
52
  /**
35
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
36
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -84,6 +102,7 @@
84
102
  * @public exported from `@promptbook/core`
85
103
  */
86
104
  const CLAIM = `It's time for a paradigm shift. The future of software in plain English, French or Latin`;
105
+ // <- TODO: [🐊] Pick the best claim
87
106
  /**
88
107
  * When the title is not provided, the default title is used
89
108
  *
@@ -116,6 +135,7 @@
116
135
  infinity: '(infinity; ∞)',
117
136
  negativeInfinity: '(negative infinity; -∞)',
118
137
  unserializable: '(unserializable value)',
138
+ circular: '(circular JSON)',
119
139
  };
120
140
  /**
121
141
  * Small number limit
@@ -155,7 +175,7 @@
155
175
  */
156
176
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [🤹‍♂️]
157
177
  // <- TODO: [🕝] Make also `BOOKS_DIRNAME_ALTERNATIVES`
158
- // TODO: !!!!!! Just .promptbook dir, hardocode others
178
+ // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
159
179
  /**
160
180
  * Where to store the temporary downloads
161
181
  *
@@ -210,6 +230,122 @@
210
230
  * TODO: [🧠][🧜‍♂️] Maybe join remoteServerUrl and path into single value
211
231
  */
212
232
 
233
+ /**
234
+ * Make error report URL for the given error
235
+ *
236
+ * @private private within the repository
237
+ */
238
+ function getErrorReportUrl(error) {
239
+ const report = {
240
+ title: `🐜 Error report from ${NAME}`,
241
+ body: spaceTrim__default["default"]((block) => `
242
+
243
+
244
+ \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
245
+
246
+ \`\`\`
247
+ ${block(error.message || '(no error message)')}
248
+ \`\`\`
249
+
250
+
251
+ ## More info:
252
+
253
+ - **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
254
+ - **Book language version:** ${BOOK_LANGUAGE_VERSION}
255
+ - **Time:** ${new Date().toISOString()}
256
+
257
+ <details>
258
+ <summary>Stack trace:</summary>
259
+
260
+ ## Stack trace:
261
+
262
+ \`\`\`stacktrace
263
+ ${block(error.stack || '(empty)')}
264
+ \`\`\`
265
+ </details>
266
+
267
+ `),
268
+ };
269
+ const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
270
+ reportUrl.searchParams.set('labels', 'bug');
271
+ reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
272
+ reportUrl.searchParams.set('title', report.title);
273
+ reportUrl.searchParams.set('body', report.body);
274
+ return reportUrl;
275
+ }
276
+
277
+ /**
278
+ * This error type indicates that the error should not happen and its last check before crashing with some other error
279
+ *
280
+ * @public exported from `@promptbook/core`
281
+ */
282
+ class UnexpectedError extends Error {
283
+ constructor(message) {
284
+ super(spaceTrim.spaceTrim((block) => `
285
+ ${block(message)}
286
+
287
+ Note: This error should not happen.
288
+ It's probbably a bug in the pipeline collection
289
+
290
+ Please report issue:
291
+ ${block(getErrorReportUrl(new Error(message)).href)}
292
+
293
+ Or contact us on ${ADMIN_EMAIL}
294
+
295
+ `));
296
+ this.name = 'UnexpectedError';
297
+ Object.setPrototypeOf(this, UnexpectedError.prototype);
298
+ }
299
+ }
300
+
301
+ /**
302
+ * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
303
+ *
304
+ * @public exported from `@promptbook/core`
305
+ */
306
+ class WrappedError extends Error {
307
+ constructor(whatWasThrown) {
308
+ const tag = `[🤮]`;
309
+ console.error(tag, whatWasThrown);
310
+ super(spaceTrim.spaceTrim(`
311
+ Non-Error object was thrown
312
+
313
+ Note: Look for ${tag} in the console for more details
314
+ Please report issue on ${ADMIN_EMAIL}
315
+ `));
316
+ this.name = 'WrappedError';
317
+ Object.setPrototypeOf(this, WrappedError.prototype);
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Helper used in catch blocks to assert that the error is an instance of `Error`
323
+ *
324
+ * @param whatWasThrown Any object that was thrown
325
+ * @returns Nothing if the error is an instance of `Error`
326
+ * @throws `WrappedError` or `UnexpectedError` if the error is not standard
327
+ *
328
+ * @private within the repository
329
+ */
330
+ function assertsError(whatWasThrown) {
331
+ // Case 1: Handle error which was rethrown as `WrappedError`
332
+ if (whatWasThrown instanceof WrappedError) {
333
+ const wrappedError = whatWasThrown;
334
+ throw wrappedError;
335
+ }
336
+ // Case 2: Handle unexpected errors
337
+ if (whatWasThrown instanceof UnexpectedError) {
338
+ const unexpectedError = whatWasThrown;
339
+ throw unexpectedError;
340
+ }
341
+ // Case 3: Handle standard errors - keep them up to consumer
342
+ if (whatWasThrown instanceof Error) {
343
+ return;
344
+ }
345
+ // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
346
+ throw new WrappedError(whatWasThrown);
347
+ }
348
+
213
349
  /**
214
350
  * AuthenticationError is thrown from login function which is dependency of remote server
215
351
  *
@@ -254,7 +390,7 @@
254
390
  }
255
391
  }
256
392
  /**
257
- * TODO: !!!!!! Add id to all errors
393
+ * TODO: [🧠][🌂] Add id to all errors
258
394
  */
259
395
 
260
396
  /**
@@ -464,74 +600,6 @@
464
600
  }
465
601
  }
466
602
 
467
- /**
468
- * Make error report URL for the given error
469
- *
470
- * @private private within the repository
471
- */
472
- function getErrorReportUrl(error) {
473
- const report = {
474
- title: `🐜 Error report from ${NAME}`,
475
- body: spaceTrim__default["default"]((block) => `
476
-
477
-
478
- \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
479
-
480
- \`\`\`
481
- ${block(error.message || '(no error message)')}
482
- \`\`\`
483
-
484
-
485
- ## More info:
486
-
487
- - **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
488
- - **Book language version:** ${BOOK_LANGUAGE_VERSION}
489
- - **Time:** ${new Date().toISOString()}
490
-
491
- <details>
492
- <summary>Stack trace:</summary>
493
-
494
- ## Stack trace:
495
-
496
- \`\`\`stacktrace
497
- ${block(error.stack || '(empty)')}
498
- \`\`\`
499
- </details>
500
-
501
- `),
502
- };
503
- const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
504
- reportUrl.searchParams.set('labels', 'bug');
505
- reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
506
- reportUrl.searchParams.set('title', report.title);
507
- reportUrl.searchParams.set('body', report.body);
508
- return reportUrl;
509
- }
510
-
511
- /**
512
- * This error type indicates that the error should not happen and its last check before crashing with some other error
513
- *
514
- * @public exported from `@promptbook/core`
515
- */
516
- class UnexpectedError extends Error {
517
- constructor(message) {
518
- super(spaceTrim.spaceTrim((block) => `
519
- ${block(message)}
520
-
521
- Note: This error should not happen.
522
- It's probbably a bug in the pipeline collection
523
-
524
- Please report issue:
525
- ${block(getErrorReportUrl(new Error(message)).href)}
526
-
527
- Or contact us on ${ADMIN_EMAIL}
528
-
529
- `));
530
- this.name = 'UnexpectedError';
531
- Object.setPrototypeOf(this, UnexpectedError.prototype);
532
- }
533
- }
534
-
535
603
  /**
536
604
  * Index of all custom errors
537
605
  *
@@ -552,7 +620,10 @@
552
620
  PipelineExecutionError,
553
621
  PipelineLogicError,
554
622
  PipelineUrlError,
623
+ AuthenticationError,
624
+ PromptbookFetchError,
555
625
  UnexpectedError,
626
+ WrappedError,
556
627
  // TODO: [🪑]> VersionMismatchError,
557
628
  };
558
629
  /**
@@ -569,8 +640,6 @@
569
640
  TypeError,
570
641
  URIError,
571
642
  AggregateError,
572
- AuthenticationError,
573
- PromptbookFetchError,
574
643
  /*
575
644
  Note: Not widely supported
576
645
  > InternalError,
@@ -813,9 +882,7 @@
813
882
  return result.trim();
814
883
  }
815
884
  catch (error) {
816
- if (!(error instanceof Error)) {
817
- throw error;
818
- }
885
+ assertsError(error);
819
886
  return null;
820
887
  }
821
888
  }
@@ -893,9 +960,7 @@
893
960
  return result.trim() + toExec;
894
961
  }
895
962
  catch (error) {
896
- if (!(error instanceof Error)) {
897
- throw error;
898
- }
963
+ assertsError(error);
899
964
  return null;
900
965
  }
901
966
  }
@@ -926,9 +991,7 @@
926
991
  throw new Error(`Can not locate app ${appName} on Windows.`);
927
992
  }
928
993
  catch (error) {
929
- if (!(error instanceof Error)) {
930
- throw error;
931
- }
994
+ assertsError(error);
932
995
  return null;
933
996
  }
934
997
  }
@@ -1166,9 +1229,7 @@
1166
1229
  JSON.stringify(value); // <- TODO: [0]
1167
1230
  }
1168
1231
  catch (error) {
1169
- if (!(error instanceof Error)) {
1170
- throw error;
1171
- }
1232
+ assertsError(error);
1172
1233
  throw new UnexpectedError(spaceTrim__default["default"]((block) => `
1173
1234
  \`${name}\` is not serializable
1174
1235
 
@@ -1741,7 +1802,7 @@
1741
1802
  if (pipeline.title === undefined || pipeline.title === '' || pipeline.title === DEFAULT_BOOK_TITLE) {
1742
1803
  return false;
1743
1804
  }
1744
- if (!pipeline.personas.every((persona) => persona.modelRequirements !== undefined)) {
1805
+ if (!pipeline.personas.every((persona) => persona.modelsRequirements !== undefined)) {
1745
1806
  return false;
1746
1807
  }
1747
1808
  if (!pipeline.knowledgeSources.every((knowledgeSource) => knowledgeSource.preparationIds !== undefined)) {
@@ -1768,6 +1829,9 @@
1768
1829
  /**
1769
1830
  * Function isValidJsonString will tell you if the string is valid JSON or not
1770
1831
  *
1832
+ * @param value The string to check
1833
+ * @returns True if the string is a valid JSON string, false otherwise
1834
+ *
1771
1835
  * @public exported from `@promptbook/utils`
1772
1836
  */
1773
1837
  function isValidJsonString(value /* <- [👨‍⚖️] */) {
@@ -1776,9 +1840,7 @@
1776
1840
  return true;
1777
1841
  }
1778
1842
  catch (error) {
1779
- if (!(error instanceof Error)) {
1780
- throw error;
1781
- }
1843
+ assertsError(error);
1782
1844
  if (error.message.includes('Unexpected token')) {
1783
1845
  return false;
1784
1846
  }
@@ -1786,6 +1848,45 @@
1786
1848
  }
1787
1849
  }
1788
1850
 
1851
+ /**
1852
+ * Converts a JavaScript Object Notation (JSON) string into an object.
1853
+ *
1854
+ * Note: This is wrapper around `JSON.parse()` with better error and type handling
1855
+ *
1856
+ * @public exported from `@promptbook/utils`
1857
+ */
1858
+ function jsonParse(value) {
1859
+ if (value === undefined) {
1860
+ throw new Error(`Can not parse JSON from undefined value.`);
1861
+ }
1862
+ else if (typeof value !== 'string') {
1863
+ console.error('Can not parse JSON from non-string value.', { text: value });
1864
+ throw new Error(spaceTrim__default["default"](`
1865
+ Can not parse JSON from non-string value.
1866
+
1867
+ The value type: ${typeof value}
1868
+ See more in console.
1869
+ `));
1870
+ }
1871
+ try {
1872
+ return JSON.parse(value);
1873
+ }
1874
+ catch (error) {
1875
+ if (!(error instanceof Error)) {
1876
+ throw error;
1877
+ }
1878
+ throw new Error(spaceTrim__default["default"]((block) => `
1879
+ ${block(error.message)}
1880
+
1881
+ The JSON text:
1882
+ ${block(value)}
1883
+ `));
1884
+ }
1885
+ }
1886
+ /**
1887
+ * TODO: !!!! Use in Promptbook.studio
1888
+ */
1889
+
1789
1890
  /**
1790
1891
  * Recursively converts JSON strings to JSON objects
1791
1892
 
@@ -1804,7 +1905,7 @@
1804
1905
  const newObject = { ...object };
1805
1906
  for (const [key, value] of Object.entries(object)) {
1806
1907
  if (typeof value === 'string' && isValidJsonString(value)) {
1807
- newObject[key] = JSON.parse(value);
1908
+ newObject[key] = jsonParse(value);
1808
1909
  }
1809
1910
  else {
1810
1911
  newObject[key] = jsonStringsToJsons(value);
@@ -1916,8 +2017,8 @@
1916
2017
  updatedAt = new Date();
1917
2018
  errors.push(...executionResult.errors);
1918
2019
  warnings.push(...executionResult.warnings);
1919
- // <- TODO: !!! Only unique errors and warnings should be added (or filtered)
1920
- // TODO: [🧠] !!! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
2020
+ // <- TODO: [🌂] Only unique errors and warnings should be added (or filtered)
2021
+ // TODO: [🧠] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
1921
2022
  // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
1922
2023
  // And delete `ExecutionTask.currentValue.preparedPipeline`
1923
2024
  assertsTaskSuccessful(executionResult);
@@ -1927,6 +2028,7 @@
1927
2028
  partialResultSubject.next(executionResult);
1928
2029
  }
1929
2030
  catch (error) {
2031
+ assertsError(error);
1930
2032
  status = 'ERROR';
1931
2033
  errors.push(error);
1932
2034
  partialResultSubject.error(error);
@@ -1980,7 +2082,7 @@
1980
2082
  * TODO: [🐚] Split into more files and make `PrepareTask` & `RemoteTask` + split the function
1981
2083
  */
1982
2084
 
1983
- 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"}];
2085
+ 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"}];
1984
2086
 
1985
2087
  /**
1986
2088
  * Checks if value is valid email
@@ -2283,7 +2385,7 @@
2283
2385
  */
2284
2386
  function unpreparePipeline(pipeline) {
2285
2387
  let { personas, knowledgeSources, tasks } = pipeline;
2286
- personas = personas.map((persona) => ({ ...persona, modelRequirements: undefined, preparationIds: undefined }));
2388
+ personas = personas.map((persona) => ({ ...persona, modelsRequirements: undefined, preparationIds: undefined }));
2287
2389
  knowledgeSources = knowledgeSources.map((knowledgeSource) => ({ ...knowledgeSource, preparationIds: undefined }));
2288
2390
  tasks = tasks.map((task) => {
2289
2391
  let { dependentParameterNames } = task;
@@ -2737,14 +2839,15 @@
2737
2839
  }
2738
2840
  }
2739
2841
  catch (error) {
2740
- if (!(error instanceof Error) || error instanceof UnexpectedError) {
2842
+ assertsError(error);
2843
+ if (error instanceof UnexpectedError) {
2741
2844
  throw error;
2742
2845
  }
2743
2846
  errors.push({ llmExecutionTools, error });
2744
2847
  }
2745
2848
  }
2746
2849
  if (errors.length === 1) {
2747
- throw errors[0];
2850
+ throw errors[0].error;
2748
2851
  }
2749
2852
  else if (errors.length > 1) {
2750
2853
  throw new PipelineExecutionError(
@@ -2870,27 +2973,48 @@
2870
2973
  pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-persona.book'),
2871
2974
  tools,
2872
2975
  });
2873
- // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
2874
2976
  const _llms = arrayableToArray(tools.llm);
2875
2977
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
2876
- const availableModels = await llmTools.listModels();
2877
- const availableModelNames = availableModels
2978
+ const availableModels = (await llmTools.listModels())
2878
2979
  .filter(({ modelVariant }) => modelVariant === 'CHAT')
2879
- .map(({ modelName }) => modelName)
2880
- .join(',');
2881
- const result = await preparePersonaExecutor({ availableModelNames, personaDescription }).asPromise();
2980
+ .map(({ modelName, modelDescription }) => ({
2981
+ modelName,
2982
+ modelDescription,
2983
+ // <- Note: `modelTitle` and `modelVariant` is not relevant for this task
2984
+ }));
2985
+ const result = await preparePersonaExecutor({
2986
+ availableModels /* <- Note: Passing as JSON */,
2987
+ personaDescription,
2988
+ }).asPromise();
2882
2989
  const { outputParameters } = result;
2883
- const { modelRequirements: modelRequirementsRaw } = outputParameters;
2884
- const modelRequirements = JSON.parse(modelRequirementsRaw);
2990
+ const { modelsRequirements: modelsRequirementsJson } = outputParameters;
2991
+ let modelsRequirementsUnchecked = jsonParse(modelsRequirementsJson);
2885
2992
  if (isVerbose) {
2886
- console.info(`PERSONA ${personaDescription}`, modelRequirements);
2993
+ console.info(`PERSONA ${personaDescription}`, modelsRequirementsUnchecked);
2887
2994
  }
2888
- const { modelName, systemMessage, temperature } = modelRequirements;
2889
- return {
2995
+ if (!Array.isArray(modelsRequirementsUnchecked)) {
2996
+ // <- TODO: Book should have syntax and system to enforce shape of JSON
2997
+ modelsRequirementsUnchecked = [modelsRequirementsUnchecked];
2998
+ /*
2999
+ throw new UnexpectedError(
3000
+ spaceTrim(
3001
+ (block) => `
3002
+ Invalid \`modelsRequirements\`:
3003
+
3004
+ \`\`\`json
3005
+ ${block(JSON.stringify(modelsRequirementsUnchecked, null, 4))}
3006
+ \`\`\`
3007
+ `,
3008
+ ),
3009
+ );
3010
+ */
3011
+ }
3012
+ const modelsRequirements = modelsRequirementsUnchecked.map((modelRequirements) => ({
2890
3013
  modelVariant: 'CHAT',
2891
- modelName,
2892
- systemMessage,
2893
- temperature,
3014
+ ...modelRequirements,
3015
+ }));
3016
+ return {
3017
+ modelsRequirements,
2894
3018
  };
2895
3019
  }
2896
3020
  /**
@@ -3585,9 +3709,7 @@
3585
3709
  return await fetch(urlOrRequest, init);
3586
3710
  }
3587
3711
  catch (error) {
3588
- if (!(error instanceof Error)) {
3589
- throw error;
3590
- }
3712
+ assertsError(error);
3591
3713
  let url;
3592
3714
  if (typeof urlOrRequest === 'string') {
3593
3715
  url = urlOrRequest;
@@ -3716,7 +3838,7 @@
3716
3838
  > },
3717
3839
  */
3718
3840
  async asJson() {
3719
- return JSON.parse(await tools.fs.readFile(filename, 'utf-8'));
3841
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
3720
3842
  },
3721
3843
  async asText() {
3722
3844
  return await tools.fs.readFile(filename, 'utf-8');
@@ -3818,9 +3940,7 @@
3818
3940
  knowledgePreparedUnflatten[index] = pieces;
3819
3941
  }
3820
3942
  catch (error) {
3821
- if (!(error instanceof Error)) {
3822
- throw error;
3823
- }
3943
+ assertsError(error);
3824
3944
  console.warn(error);
3825
3945
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
3826
3946
  }
@@ -3976,14 +4096,14 @@
3976
4096
  // TODO: [🖌][🧠] Implement some `mapAsync` function
3977
4097
  const preparedPersonas = new Array(personas.length);
3978
4098
  await forEachAsync(personas, { maxParallelCount /* <- TODO: [🪂] When there are subtasks, this maximul limit can be broken */ }, async (persona, index) => {
3979
- const modelRequirements = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
4099
+ const { modelsRequirements } = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
3980
4100
  rootDirname,
3981
4101
  maxParallelCount /* <- TODO: [🪂] */,
3982
4102
  isVerbose,
3983
4103
  });
3984
4104
  const preparedPersona = {
3985
4105
  ...persona,
3986
- modelRequirements,
4106
+ modelsRequirements,
3987
4107
  preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id],
3988
4108
  // <- TODO: [🍙] Make some standard order of json properties
3989
4109
  };
@@ -4112,13 +4232,19 @@
4112
4232
  return value.toISOString();
4113
4233
  }
4114
4234
  else {
4115
- return JSON.stringify(value);
4235
+ try {
4236
+ return JSON.stringify(value);
4237
+ }
4238
+ catch (error) {
4239
+ if (error instanceof TypeError && error.message.includes('circular structure')) {
4240
+ return VALUE_STRINGS.circular;
4241
+ }
4242
+ throw error;
4243
+ }
4116
4244
  }
4117
4245
  }
4118
4246
  catch (error) {
4119
- if (!(error instanceof Error)) {
4120
- throw error;
4121
- }
4247
+ assertsError(error);
4122
4248
  console.error(error);
4123
4249
  return VALUE_STRINGS.unserializable;
4124
4250
  }
@@ -4175,9 +4301,7 @@
4175
4301
  }
4176
4302
  }
4177
4303
  catch (error) {
4178
- if (!(error instanceof Error)) {
4179
- throw error;
4180
- }
4304
+ assertsError(error);
4181
4305
  throw new ParseError(spaceTrim.spaceTrim((block) => `
4182
4306
  Can not extract variables from the script
4183
4307
  ${block(error.stack || error.message)}
@@ -4296,6 +4420,46 @@
4296
4420
  // encoding: 'utf-8',
4297
4421
  });
4298
4422
 
4423
+ /**
4424
+ * Function to check if a string is valid CSV
4425
+ *
4426
+ * @param value The string to check
4427
+ * @returns True if the string is a valid CSV string, false otherwise
4428
+ *
4429
+ * @public exported from `@promptbook/utils`
4430
+ */
4431
+ function isValidCsvString(value) {
4432
+ try {
4433
+ // A simple check for CSV format: at least one comma and no invalid characters
4434
+ if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
4435
+ return true;
4436
+ }
4437
+ return false;
4438
+ }
4439
+ catch (error) {
4440
+ assertsError(error);
4441
+ return false;
4442
+ }
4443
+ }
4444
+
4445
+ /**
4446
+ * Converts a CSV string into an object
4447
+ *
4448
+ * Note: This is wrapper around `papaparse.parse()` with better autohealing
4449
+ *
4450
+ * @private - for now until `@promptbook/csv` is released
4451
+ */
4452
+ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO: Make CSV Schemas */) {
4453
+ settings = { ...settings, ...MANDATORY_CSV_SETTINGS };
4454
+ // Note: Autoheal invalid '\n' characters
4455
+ if (settings.newline && !settings.newline.includes('\r') && value.includes('\r')) {
4456
+ console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
4457
+ value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
4458
+ }
4459
+ const csv = papaparse.parse(value, settings);
4460
+ return csv;
4461
+ }
4462
+
4299
4463
  /**
4300
4464
  * Definition for CSV spreadsheet
4301
4465
  *
@@ -4306,7 +4470,7 @@
4306
4470
  formatName: 'CSV',
4307
4471
  aliases: ['SPREADSHEET', 'TABLE'],
4308
4472
  isValid(value, settings, schema) {
4309
- return true;
4473
+ return isValidCsvString(value);
4310
4474
  },
4311
4475
  canBeValid(partialValue, settings, schema) {
4312
4476
  return true;
@@ -4318,8 +4482,7 @@
4318
4482
  {
4319
4483
  subvalueName: 'ROW',
4320
4484
  async mapValues(value, outputParameterName, settings, mapCallback) {
4321
- // TODO: [👨🏾‍🤝‍👨🏼] DRY csv parsing
4322
- const csv = papaparse.parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
4485
+ const csv = csvParse(value, settings);
4323
4486
  if (csv.errors.length !== 0) {
4324
4487
  throw new CsvFormatError(spaceTrim__default["default"]((block) => `
4325
4488
  CSV parsing error
@@ -4349,8 +4512,7 @@
4349
4512
  {
4350
4513
  subvalueName: 'CELL',
4351
4514
  async mapValues(value, outputParameterName, settings, mapCallback) {
4352
- // TODO: [👨🏾‍🤝‍👨🏼] DRY csv parsing
4353
- const csv = papaparse.parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
4515
+ const csv = csvParse(value, settings);
4354
4516
  if (csv.errors.length !== 0) {
4355
4517
  throw new CsvFormatError(spaceTrim__default["default"]((block) => `
4356
4518
  CSV parsing error
@@ -4460,6 +4622,30 @@
4460
4622
  * TODO: [🏢] Allow to expect something inside each item of list and other formats
4461
4623
  */
4462
4624
 
4625
+ /**
4626
+ * Function to check if a string is valid XML
4627
+ *
4628
+ * @param value
4629
+ * @returns True if the string is a valid XML string, false otherwise
4630
+ *
4631
+ * @public exported from `@promptbook/utils`
4632
+ */
4633
+ function isValidXmlString(value) {
4634
+ try {
4635
+ const parser = new DOMParser();
4636
+ const parsedDocument = parser.parseFromString(value, 'application/xml');
4637
+ const parserError = parsedDocument.getElementsByTagName('parsererror');
4638
+ if (parserError.length > 0) {
4639
+ return false;
4640
+ }
4641
+ return true;
4642
+ }
4643
+ catch (error) {
4644
+ assertsError(error);
4645
+ return false;
4646
+ }
4647
+ }
4648
+
4463
4649
  /**
4464
4650
  * Definition for XML format
4465
4651
  *
@@ -4469,7 +4655,7 @@
4469
4655
  formatName: 'XML',
4470
4656
  mimeType: 'application/xml',
4471
4657
  isValid(value, settings, schema) {
4472
- return true;
4658
+ return isValidXmlString(value);
4473
4659
  },
4474
4660
  canBeValid(partialValue, settings, schema) {
4475
4661
  return true;
@@ -5059,9 +5245,7 @@
5059
5245
  break scripts;
5060
5246
  }
5061
5247
  catch (error) {
5062
- if (!(error instanceof Error)) {
5063
- throw error;
5064
- }
5248
+ assertsError(error);
5065
5249
  if (error instanceof UnexpectedError) {
5066
5250
  throw error;
5067
5251
  }
@@ -5131,9 +5315,7 @@
5131
5315
  break scripts;
5132
5316
  }
5133
5317
  catch (error) {
5134
- if (!(error instanceof Error)) {
5135
- throw error;
5136
- }
5318
+ assertsError(error);
5137
5319
  if (error instanceof UnexpectedError) {
5138
5320
  throw error;
5139
5321
  }
@@ -5376,13 +5558,79 @@
5376
5558
  /**
5377
5559
  * @@@
5378
5560
  *
5561
+ * Here is the place where RAG (retrieval-augmented generation) happens
5562
+ *
5379
5563
  * @private internal utility of `createPipelineExecutor`
5380
5564
  */
5381
5565
  async function getKnowledgeForTask(options) {
5382
- const { preparedPipeline, task } = options;
5383
- return preparedPipeline.knowledgePieces.map(({ content }) => `- ${content}`).join('\n');
5566
+ const { tools, preparedPipeline, task } = options;
5567
+ const firstKnowlegePiece = preparedPipeline.knowledgePieces[0];
5568
+ const firstKnowlegeIndex = firstKnowlegePiece === null || firstKnowlegePiece === void 0 ? void 0 : firstKnowlegePiece.index[0];
5569
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model, use also keyword search
5570
+ if (firstKnowlegePiece === undefined || firstKnowlegeIndex === undefined) {
5571
+ return 'No knowledge pieces found';
5572
+ }
5573
+ // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
5574
+ const _llms = arrayableToArray(tools.llm);
5575
+ const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
5576
+ const taskEmbeddingPrompt = {
5577
+ title: 'Knowledge Search',
5578
+ modelRequirements: {
5579
+ modelVariant: 'EMBEDDING',
5580
+ modelName: firstKnowlegeIndex.modelName,
5581
+ },
5582
+ content: task.content,
5583
+ parameters: {
5584
+ /* !!!!!!!! */
5585
+ },
5586
+ };
5587
+ const taskEmbeddingResult = await llmTools.callEmbeddingModel(taskEmbeddingPrompt);
5588
+ const knowledgePiecesWithRelevance = preparedPipeline.knowledgePieces.map((knowledgePiece) => {
5589
+ const { index } = knowledgePiece;
5590
+ const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowlegeIndex.modelName);
5591
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model
5592
+ if (knowledgePieceIndex === undefined) {
5593
+ return {
5594
+ content: knowledgePiece.content,
5595
+ relevance: 0,
5596
+ };
5597
+ }
5598
+ const relevance = computeCosineSimilarity(knowledgePieceIndex.position, taskEmbeddingResult.content);
5599
+ return {
5600
+ content: knowledgePiece.content,
5601
+ relevance,
5602
+ };
5603
+ });
5604
+ const knowledgePiecesSorted = knowledgePiecesWithRelevance.sort((a, b) => a.relevance - b.relevance);
5605
+ const knowledgePiecesLimited = knowledgePiecesSorted.slice(0, 5);
5606
+ console.log('!!! Embedding', {
5607
+ task,
5608
+ taskEmbeddingPrompt,
5609
+ taskEmbeddingResult,
5610
+ firstKnowlegePiece,
5611
+ firstKnowlegeIndex,
5612
+ knowledgePiecesWithRelevance,
5613
+ knowledgePiecesSorted,
5614
+ knowledgePiecesLimited,
5615
+ });
5616
+ return knowledgePiecesLimited.map(({ content }) => `- ${content}`).join('\n');
5384
5617
  // <- TODO: [🧠] Some smart aggregation of knowledge pieces, single-line vs multi-line vs mixed
5385
5618
  }
5619
+ // TODO: !!!!!! Annotate + to new file
5620
+ function computeCosineSimilarity(embeddingVector1, embeddingVector2) {
5621
+ if (embeddingVector1.length !== embeddingVector2.length) {
5622
+ throw new TypeError('Embedding vectors must have the same length');
5623
+ }
5624
+ const dotProduct = embeddingVector1.reduce((sum, value, index) => sum + value * embeddingVector2[index], 0);
5625
+ const magnitude1 = Math.sqrt(embeddingVector1.reduce((sum, value) => sum + value * value, 0));
5626
+ const magnitude2 = Math.sqrt(embeddingVector2.reduce((sum, value) => sum + value * value, 0));
5627
+ return 1 - dotProduct / (magnitude1 * magnitude2);
5628
+ }
5629
+ /**
5630
+ * TODO: !!!! Verify if this is working
5631
+ * TODO: [♨] Implement Better - use keyword search
5632
+ * TODO: [♨] Examples of values
5633
+ */
5386
5634
 
5387
5635
  /**
5388
5636
  * @@@
@@ -5390,9 +5638,9 @@
5390
5638
  * @private internal utility of `createPipelineExecutor`
5391
5639
  */
5392
5640
  async function getReservedParametersForTask(options) {
5393
- const { preparedPipeline, task, pipelineIdentification } = options;
5641
+ const { tools, preparedPipeline, task, pipelineIdentification } = options;
5394
5642
  const context = await getContextForTask(); // <- [🏍]
5395
- const knowledge = await getKnowledgeForTask({ preparedPipeline, task });
5643
+ const knowledge = await getKnowledgeForTask({ tools, preparedPipeline, task });
5396
5644
  const examples = await getExamplesForTask();
5397
5645
  const currentDate = new Date().toISOString(); // <- TODO: [🧠][💩] Better
5398
5646
  const modelName = RESERVED_PARAMETER_MISSING_VALUE;
@@ -5454,6 +5702,7 @@
5454
5702
  }
5455
5703
  const definedParameters = Object.freeze({
5456
5704
  ...(await getReservedParametersForTask({
5705
+ tools,
5457
5706
  preparedPipeline,
5458
5707
  task: currentTask,
5459
5708
  pipelineIdentification,
@@ -5754,9 +6003,7 @@
5754
6003
  await Promise.all(resolving);
5755
6004
  }
5756
6005
  catch (error /* <- Note: [3] */) {
5757
- if (!(error instanceof Error)) {
5758
- throw error;
5759
- }
6006
+ assertsError(error);
5760
6007
  // Note: No need to rethrow UnexpectedError
5761
6008
  // if (error instanceof UnexpectedError) {
5762
6009
  // Note: Count usage, [🧠] Maybe put to separate function executionReportJsonToUsage + DRY [🤹‍♂️]
@@ -6658,9 +6905,7 @@
6658
6905
  }
6659
6906
  }
6660
6907
  catch (error) {
6661
- if (!(error instanceof Error)) {
6662
- throw error;
6663
- }
6908
+ assertsError(error);
6664
6909
  if (error instanceof ReferenceError) {
6665
6910
  const undefinedName = error.message.split(' ')[0];
6666
6911
  /*
@@ -6740,6 +6985,604 @@
6740
6985
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
6741
6986
  */
6742
6987
 
6988
+ // TODO: !!!! List running services from REMOTE_SERVER_URLS
6989
+ // TODO: !!!! Import directly from YML
6990
+ /**
6991
+ * @private !!!! Decide how to expose this
6992
+ */
6993
+ const openapiJson = {
6994
+ openapi: '3.0.0',
6995
+ info: {
6996
+ title: 'Promptbook Remote Server API (!!!! From YML)',
6997
+ version: '1.0.0',
6998
+ description: 'API documentation for the Promptbook Remote Server',
6999
+ },
7000
+ paths: {
7001
+ '/': {
7002
+ get: {
7003
+ summary: 'Get server details',
7004
+ description: 'Returns details about the Promptbook server.',
7005
+ responses: {
7006
+ '200': {
7007
+ description: 'Server details in markdown format.',
7008
+ content: {
7009
+ 'text/markdown': {
7010
+ schema: {
7011
+ type: 'string',
7012
+ },
7013
+ },
7014
+ },
7015
+ },
7016
+ },
7017
+ },
7018
+ },
7019
+ '/login': {
7020
+ post: {
7021
+ summary: 'Login to the server',
7022
+ description: 'Login to the server and get identification.',
7023
+ requestBody: {
7024
+ required: true,
7025
+ content: {
7026
+ 'application/json': {
7027
+ schema: {
7028
+ type: 'object',
7029
+ properties: {
7030
+ username: {
7031
+ type: 'string',
7032
+ },
7033
+ password: {
7034
+ type: 'string',
7035
+ },
7036
+ appId: {
7037
+ type: 'string',
7038
+ },
7039
+ },
7040
+ },
7041
+ },
7042
+ },
7043
+ },
7044
+ responses: {
7045
+ '201': {
7046
+ description: 'Successful login',
7047
+ content: {
7048
+ 'application/json': {
7049
+ schema: {
7050
+ type: 'object',
7051
+ properties: {
7052
+ isSuccess: {
7053
+ type: 'boolean',
7054
+ },
7055
+ message: {
7056
+ type: 'string',
7057
+ },
7058
+ error: {
7059
+ type: 'object',
7060
+ },
7061
+ identification: {
7062
+ type: 'object',
7063
+ },
7064
+ },
7065
+ },
7066
+ },
7067
+ },
7068
+ },
7069
+ '400': {
7070
+ description: 'Bad request or login failed',
7071
+ content: {
7072
+ 'application/json': {
7073
+ schema: {
7074
+ type: 'object',
7075
+ properties: {
7076
+ error: {
7077
+ type: 'object',
7078
+ },
7079
+ },
7080
+ },
7081
+ },
7082
+ },
7083
+ },
7084
+ '401': {
7085
+ description: 'Authentication error',
7086
+ content: {
7087
+ 'application/json': {
7088
+ schema: {
7089
+ type: 'object',
7090
+ properties: {
7091
+ isSuccess: {
7092
+ type: 'boolean',
7093
+ enum: [false],
7094
+ },
7095
+ message: {
7096
+ type: 'string',
7097
+ },
7098
+ error: {
7099
+ type: 'object',
7100
+ },
7101
+ },
7102
+ },
7103
+ },
7104
+ },
7105
+ },
7106
+ },
7107
+ },
7108
+ },
7109
+ '/books': {
7110
+ get: {
7111
+ summary: 'List all books',
7112
+ description: 'Returns a list of all available books in the collection.',
7113
+ responses: {
7114
+ '200': {
7115
+ description: 'A list of books.',
7116
+ content: {
7117
+ 'application/json': {
7118
+ schema: {
7119
+ type: 'array',
7120
+ items: {
7121
+ type: 'string',
7122
+ },
7123
+ },
7124
+ },
7125
+ },
7126
+ },
7127
+ '500': {
7128
+ description: 'No collection available',
7129
+ content: {
7130
+ 'text/plain': {
7131
+ schema: {
7132
+ type: 'string',
7133
+ },
7134
+ },
7135
+ },
7136
+ },
7137
+ },
7138
+ },
7139
+ },
7140
+ '/books/{bookId}': {
7141
+ get: {
7142
+ summary: 'Get book content',
7143
+ description: 'Returns the content of a specific book.',
7144
+ parameters: [
7145
+ {
7146
+ in: 'path',
7147
+ name: 'bookId',
7148
+ required: true,
7149
+ schema: {
7150
+ type: 'string',
7151
+ },
7152
+ description: 'The ID of the book to retrieve.',
7153
+ },
7154
+ ],
7155
+ responses: {
7156
+ '200': {
7157
+ description: 'The content of the book.',
7158
+ content: {
7159
+ 'text/markdown': {
7160
+ schema: {
7161
+ type: 'string',
7162
+ },
7163
+ },
7164
+ },
7165
+ },
7166
+ '404': {
7167
+ description: 'Book not found.',
7168
+ content: {
7169
+ 'application/json': {
7170
+ schema: {
7171
+ type: 'object',
7172
+ properties: {
7173
+ error: {
7174
+ type: 'object',
7175
+ },
7176
+ },
7177
+ },
7178
+ },
7179
+ },
7180
+ },
7181
+ '500': {
7182
+ description: 'No collection available',
7183
+ content: {
7184
+ 'text/plain': {
7185
+ schema: {
7186
+ type: 'string',
7187
+ },
7188
+ },
7189
+ },
7190
+ },
7191
+ },
7192
+ },
7193
+ },
7194
+ '/executions': {
7195
+ get: {
7196
+ summary: 'List all executions',
7197
+ description: 'Returns a list of all running execution tasks.',
7198
+ responses: {
7199
+ '200': {
7200
+ description: 'A list of execution tasks.',
7201
+ content: {
7202
+ 'application/json': {
7203
+ schema: {
7204
+ type: 'array',
7205
+ items: {
7206
+ type: 'object',
7207
+ properties: {
7208
+ nonce: {
7209
+ type: 'string',
7210
+ },
7211
+ taskId: {
7212
+ type: 'string',
7213
+ },
7214
+ taskType: {
7215
+ type: 'string',
7216
+ },
7217
+ status: {
7218
+ type: 'string',
7219
+ },
7220
+ createdAt: {
7221
+ type: 'string',
7222
+ format: 'date-time',
7223
+ },
7224
+ updatedAt: {
7225
+ type: 'string',
7226
+ format: 'date-time',
7227
+ },
7228
+ },
7229
+ },
7230
+ },
7231
+ },
7232
+ },
7233
+ },
7234
+ },
7235
+ },
7236
+ },
7237
+ '/executions/last': {
7238
+ get: {
7239
+ summary: 'Get the last execution',
7240
+ description: 'Returns details of the last execution task.',
7241
+ responses: {
7242
+ '200': {
7243
+ description: 'The last execution task with full details.',
7244
+ content: {
7245
+ 'application/json': {
7246
+ schema: {
7247
+ type: 'object',
7248
+ properties: {
7249
+ nonce: {
7250
+ type: 'string',
7251
+ },
7252
+ taskId: {
7253
+ type: 'string',
7254
+ },
7255
+ taskType: {
7256
+ type: 'string',
7257
+ },
7258
+ status: {
7259
+ type: 'string',
7260
+ },
7261
+ errors: {
7262
+ type: 'array',
7263
+ items: {
7264
+ type: 'object',
7265
+ },
7266
+ },
7267
+ warnings: {
7268
+ type: 'array',
7269
+ items: {
7270
+ type: 'object',
7271
+ },
7272
+ },
7273
+ createdAt: {
7274
+ type: 'string',
7275
+ format: 'date-time',
7276
+ },
7277
+ updatedAt: {
7278
+ type: 'string',
7279
+ format: 'date-time',
7280
+ },
7281
+ currentValue: {
7282
+ type: 'object',
7283
+ },
7284
+ },
7285
+ },
7286
+ },
7287
+ },
7288
+ },
7289
+ '404': {
7290
+ description: 'No execution tasks found.',
7291
+ content: {
7292
+ 'text/plain': {
7293
+ schema: {
7294
+ type: 'string',
7295
+ },
7296
+ },
7297
+ },
7298
+ },
7299
+ },
7300
+ },
7301
+ },
7302
+ '/executions/{taskId}': {
7303
+ get: {
7304
+ summary: 'Get specific execution',
7305
+ description: 'Returns details of a specific execution task.',
7306
+ parameters: [
7307
+ {
7308
+ in: 'path',
7309
+ name: 'taskId',
7310
+ required: true,
7311
+ schema: {
7312
+ type: 'string',
7313
+ },
7314
+ description: 'The ID of the execution task to retrieve.',
7315
+ },
7316
+ ],
7317
+ responses: {
7318
+ '200': {
7319
+ description: 'The execution task with full details.',
7320
+ content: {
7321
+ 'application/json': {
7322
+ schema: {
7323
+ type: 'object',
7324
+ properties: {
7325
+ nonce: {
7326
+ type: 'string',
7327
+ },
7328
+ taskId: {
7329
+ type: 'string',
7330
+ },
7331
+ taskType: {
7332
+ type: 'string',
7333
+ },
7334
+ status: {
7335
+ type: 'string',
7336
+ },
7337
+ errors: {
7338
+ type: 'array',
7339
+ items: {
7340
+ type: 'object',
7341
+ },
7342
+ },
7343
+ warnings: {
7344
+ type: 'array',
7345
+ items: {
7346
+ type: 'object',
7347
+ },
7348
+ },
7349
+ createdAt: {
7350
+ type: 'string',
7351
+ format: 'date-time',
7352
+ },
7353
+ updatedAt: {
7354
+ type: 'string',
7355
+ format: 'date-time',
7356
+ },
7357
+ currentValue: {
7358
+ type: 'object',
7359
+ },
7360
+ },
7361
+ },
7362
+ },
7363
+ },
7364
+ },
7365
+ '404': {
7366
+ description: 'Execution task not found.',
7367
+ content: {
7368
+ 'text/plain': {
7369
+ schema: {
7370
+ type: 'string',
7371
+ },
7372
+ },
7373
+ },
7374
+ },
7375
+ },
7376
+ },
7377
+ },
7378
+ '/executions/new': {
7379
+ post: {
7380
+ summary: 'Start a new execution',
7381
+ description: 'Starts a new execution task for a given pipeline.',
7382
+ requestBody: {
7383
+ required: true,
7384
+ content: {
7385
+ 'application/json': {
7386
+ schema: {
7387
+ type: 'object',
7388
+ properties: {
7389
+ pipelineUrl: {
7390
+ type: 'string',
7391
+ description: 'URL of the pipeline to execute',
7392
+ },
7393
+ book: {
7394
+ type: 'string',
7395
+ description: 'Alternative field for pipelineUrl',
7396
+ },
7397
+ inputParameters: {
7398
+ type: 'object',
7399
+ description: 'Parameters for pipeline execution',
7400
+ },
7401
+ identification: {
7402
+ type: 'object',
7403
+ description: 'User identification data',
7404
+ },
7405
+ },
7406
+ },
7407
+ },
7408
+ },
7409
+ },
7410
+ responses: {
7411
+ '200': {
7412
+ description: 'The newly created execution task.',
7413
+ content: {
7414
+ 'application/json': {
7415
+ schema: {
7416
+ type: 'object',
7417
+ },
7418
+ },
7419
+ },
7420
+ },
7421
+ '400': {
7422
+ description: 'Invalid input.',
7423
+ content: {
7424
+ 'application/json': {
7425
+ schema: {
7426
+ type: 'object',
7427
+ properties: {
7428
+ error: {
7429
+ type: 'object',
7430
+ },
7431
+ },
7432
+ },
7433
+ },
7434
+ },
7435
+ },
7436
+ '404': {
7437
+ description: 'Pipeline not found.',
7438
+ content: {
7439
+ 'text/plain': {
7440
+ schema: {
7441
+ type: 'string',
7442
+ },
7443
+ },
7444
+ },
7445
+ },
7446
+ },
7447
+ },
7448
+ },
7449
+ '/api-docs': {
7450
+ get: {
7451
+ summary: 'API documentation UI',
7452
+ description: 'Swagger UI for API documentation',
7453
+ responses: {
7454
+ '200': {
7455
+ description: 'HTML Swagger UI',
7456
+ },
7457
+ },
7458
+ },
7459
+ },
7460
+ '/swagger': {
7461
+ get: {
7462
+ summary: 'API documentation UI (alternative path)',
7463
+ description: 'Swagger UI for API documentation',
7464
+ responses: {
7465
+ '200': {
7466
+ description: 'HTML Swagger UI',
7467
+ },
7468
+ },
7469
+ },
7470
+ },
7471
+ '/openapi': {
7472
+ get: {
7473
+ summary: 'OpenAPI specification',
7474
+ description: 'Returns the OpenAPI JSON specification',
7475
+ responses: {
7476
+ '200': {
7477
+ description: 'OpenAPI specification',
7478
+ content: {
7479
+ 'application/json': {
7480
+ schema: {
7481
+ type: 'object',
7482
+ },
7483
+ },
7484
+ },
7485
+ },
7486
+ },
7487
+ },
7488
+ },
7489
+ },
7490
+ components: {
7491
+ schemas: {
7492
+ Error: {
7493
+ type: 'object',
7494
+ properties: {
7495
+ error: {
7496
+ type: 'object',
7497
+ },
7498
+ },
7499
+ },
7500
+ ExecutionTaskSummary: {
7501
+ type: 'object',
7502
+ properties: {
7503
+ nonce: {
7504
+ type: 'string',
7505
+ },
7506
+ taskId: {
7507
+ type: 'string',
7508
+ },
7509
+ taskType: {
7510
+ type: 'string',
7511
+ },
7512
+ status: {
7513
+ type: 'string',
7514
+ },
7515
+ createdAt: {
7516
+ type: 'string',
7517
+ format: 'date-time',
7518
+ },
7519
+ updatedAt: {
7520
+ type: 'string',
7521
+ format: 'date-time',
7522
+ },
7523
+ },
7524
+ },
7525
+ ExecutionTaskFull: {
7526
+ type: 'object',
7527
+ properties: {
7528
+ nonce: {
7529
+ type: 'string',
7530
+ },
7531
+ taskId: {
7532
+ type: 'string',
7533
+ },
7534
+ taskType: {
7535
+ type: 'string',
7536
+ },
7537
+ status: {
7538
+ type: 'string',
7539
+ },
7540
+ errors: {
7541
+ type: 'array',
7542
+ items: {
7543
+ type: 'object',
7544
+ },
7545
+ },
7546
+ warnings: {
7547
+ type: 'array',
7548
+ items: {
7549
+ type: 'object',
7550
+ },
7551
+ },
7552
+ createdAt: {
7553
+ type: 'string',
7554
+ format: 'date-time',
7555
+ },
7556
+ updatedAt: {
7557
+ type: 'string',
7558
+ format: 'date-time',
7559
+ },
7560
+ currentValue: {
7561
+ type: 'object',
7562
+ },
7563
+ },
7564
+ },
7565
+ },
7566
+ },
7567
+ tags: [
7568
+ {
7569
+ name: 'Books',
7570
+ description: 'Operations related to books and pipelines',
7571
+ },
7572
+ {
7573
+ name: 'Executions',
7574
+ description: 'Operations related to execution tasks',
7575
+ },
7576
+ {
7577
+ name: 'Authentication',
7578
+ description: 'Authentication operations',
7579
+ },
7580
+ ],
7581
+ };
7582
+ /**
7583
+ * Note: [💞] Ignore a discrepancy between file name and entity name
7584
+ */
7585
+
6743
7586
  /**
6744
7587
  * Remote server is a proxy server that uses its execution tools internally and exposes the executor interface externally.
6745
7588
  *
@@ -6750,7 +7593,7 @@
6750
7593
  * @public exported from `@promptbook/remote-server`
6751
7594
  */
6752
7595
  function startRemoteServer(options) {
6753
- const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
7596
+ const { port, collection, createLlmExecutionTools, createExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
6754
7597
  isAnonymousModeAllowed: false,
6755
7598
  isApplicationModeAllowed: false,
6756
7599
  collection: null,
@@ -6758,22 +7601,6 @@
6758
7601
  login: null,
6759
7602
  ...options,
6760
7603
  };
6761
- // <- TODO: [🦪] Some helper type to be able to use discriminant union types with destructuring
6762
- let { rootPath = '/' } = options;
6763
- if (!rootPath.startsWith('/')) {
6764
- rootPath = `/${rootPath}`;
6765
- } /* not else */
6766
- if (rootPath.endsWith('/')) {
6767
- rootPath = rootPath.slice(0, -1);
6768
- } /* not else */
6769
- if (rootPath === '/') {
6770
- rootPath = '';
6771
- }
6772
- const socketioPath = '/' +
6773
- `${rootPath}/socket.io`
6774
- .split('/')
6775
- .filter((part) => part !== '')
6776
- .join('/');
6777
7604
  const startupDate = new Date();
6778
7605
  async function getExecutionToolsFromIdentification(identification) {
6779
7606
  if (identification === null || identification === undefined) {
@@ -6796,23 +7623,25 @@
6796
7623
  }
6797
7624
  else if (isAnonymous === false && createLlmExecutionTools !== null) {
6798
7625
  // Note: Application mode
6799
- const { appId, userId, customOptions } = identification;
6800
- llm = await createLlmExecutionTools({
6801
- appId,
6802
- userId,
6803
- customOptions,
6804
- });
7626
+ llm = await createLlmExecutionTools(identification);
6805
7627
  }
6806
7628
  else {
6807
7629
  throw new PipelineExecutionError(`You must provide either llmToolsConfiguration or non-anonymous mode must be propperly configured`);
6808
7630
  }
6809
- const fs = $provideFilesystemForNode();
6810
- const executables = await $provideExecutablesForNode();
7631
+ const customExecutionTools = createExecutionTools ? await createExecutionTools(identification) : {};
7632
+ const fs = customExecutionTools.fs || $provideFilesystemForNode();
7633
+ const executables = customExecutionTools.executables || (await $provideExecutablesForNode());
7634
+ const scrapers = customExecutionTools.scrapers || (await $provideScrapersForNode({ fs, llm, executables }));
7635
+ const script = customExecutionTools.script || (await $provideScriptingForNode({}));
7636
+ const fetch = customExecutionTools.fetch || promptbookFetch;
7637
+ const userInterface = customExecutionTools.userInterface || undefined;
6811
7638
  const tools = {
6812
7639
  llm,
6813
7640
  fs,
6814
- scrapers: await $provideScrapersForNode({ fs, llm, executables }),
6815
- script: await $provideScriptingForNode({}),
7641
+ scrapers,
7642
+ script,
7643
+ fetch,
7644
+ userInterface,
6816
7645
  };
6817
7646
  return tools;
6818
7647
  }
@@ -6822,39 +7651,27 @@
6822
7651
  response.setHeader('X-Powered-By', 'Promptbook engine');
6823
7652
  next();
6824
7653
  });
6825
- const swaggerOptions = {
6826
- definition: {
6827
- openapi: '3.0.0',
6828
- info: {
6829
- title: 'Promptbook Remote Server API',
6830
- version: '1.0.0',
6831
- description: 'API documentation for the Promptbook Remote Server',
6832
- },
6833
- servers: [
6834
- {
6835
- url: `http://localhost:${port}${rootPath}`,
6836
- // <- TODO: !!!!! Probbably: Pass `remoteServerUrl` instead of `port` and `rootPath`
6837
- },
6838
- ],
7654
+ // TODO: !!!! Expose openapiJson to consumer and also allow to add new routes
7655
+ app.use(OpenApiValidator__namespace.middleware({
7656
+ apiSpec: openapiJson,
7657
+ ignorePaths(path) {
7658
+ return path.startsWith('/api-docs') || path.startsWith('/swagger') || path.startsWith('/openapi');
6839
7659
  },
6840
- apis: ['./src/remote-server/**/*.ts'], // Adjust path as needed
6841
- };
6842
- const swaggerSpec = swaggerJsdoc__default["default"](swaggerOptions);
6843
- app.use([`/api-docs`, `${rootPath}/api-docs`], swaggerUi__default["default"].serve, swaggerUi__default["default"].setup(swaggerSpec));
7660
+ validateRequests: true,
7661
+ validateResponses: true,
7662
+ }));
7663
+ app.use([`/api-docs`, `/swagger`], swaggerUi__default["default"].serve, swaggerUi__default["default"].setup(openapiJson, {
7664
+ // customCss: '.swagger-ui .topbar { display: none }',
7665
+ // customSiteTitle: 'BRJ API',
7666
+ // customfavIcon: 'https://brj.app/favicon.ico',
7667
+ }));
7668
+ app.get(`/openapi`, (request, response) => {
7669
+ response.json(openapiJson);
7670
+ });
6844
7671
  const runningExecutionTasks = [];
6845
7672
  // <- TODO: [🤬] Identify the users
6846
7673
  // TODO: [🧠] Do here some garbage collection of finished tasks
6847
- /**
6848
- * @swagger
6849
- * /:
6850
- * get:
6851
- * summary: Get server details
6852
- * description: Returns details about the Promptbook server.
6853
- * responses:
6854
- * 200:
6855
- * description: Server details in markdown format.
6856
- */
6857
- app.get(['/', rootPath], async (request, response) => {
7674
+ app.get('/', async (request, response) => {
6858
7675
  var _a;
6859
7676
  if ((_a = request.url) === null || _a === void 0 ? void 0 : _a.includes('socket.io')) {
6860
7677
  return;
@@ -6873,8 +7690,6 @@
6873
7690
  ## Details
6874
7691
 
6875
7692
  **Server port:** ${port}
6876
- **Server root path:** ${rootPath}
6877
- **Socket.io path:** ${socketioPath}
6878
7693
  **Startup date:** ${startupDate.toISOString()}
6879
7694
  **Anonymouse mode:** ${isAnonymousModeAllowed ? 'enabled' : 'disabled'}
6880
7695
  **Application mode:** ${isApplicationModeAllowed ? 'enabled' : 'disabled'}
@@ -6913,38 +7728,7 @@
6913
7728
  https://github.com/webgptorg/promptbook
6914
7729
  `));
6915
7730
  });
6916
- /**
6917
- * @swagger
6918
- *
6919
- * /login:
6920
- * post:
6921
- * summary: Login to the server
6922
- * description: Login to the server and get identification.
6923
- * requestBody:
6924
- * required: true
6925
- * content:
6926
- * application/json:
6927
- * schema:
6928
- * type: object
6929
- * properties:
6930
- * username:
6931
- * type: string
6932
- * password:
6933
- * type: string
6934
- * appId:
6935
- * type: string
6936
- * responses:
6937
- * 200:
6938
- * description: Successful login
6939
- * content:
6940
- * application/json:
6941
- * schema:
6942
- * type: object
6943
- * properties:
6944
- * identification:
6945
- * type: object
6946
- */
6947
- app.post([`/login`, `${rootPath}/login`], async (request, response) => {
7731
+ app.post(`/login`, async (request, response) => {
6948
7732
  if (!isApplicationModeAllowed || login === null) {
6949
7733
  response.status(400).send('Application mode is not allowed');
6950
7734
  return;
@@ -6969,9 +7753,7 @@
6969
7753
  return;
6970
7754
  }
6971
7755
  catch (error) {
6972
- if (!(error instanceof Error)) {
6973
- throw error;
6974
- }
7756
+ assertsError(error);
6975
7757
  if (error instanceof AuthenticationError) {
6976
7758
  response.status(401).send({
6977
7759
  isSuccess: false,
@@ -6986,23 +7768,7 @@
6986
7768
  response.status(400).send({ error: serializeError(error) });
6987
7769
  }
6988
7770
  });
6989
- /**
6990
- * @swagger
6991
- * /books:
6992
- * get:
6993
- * summary: List all books
6994
- * description: Returns a list of all available books in the collection.
6995
- * responses:
6996
- * 200:
6997
- * description: A list of books.
6998
- * content:
6999
- * application/json:
7000
- * schema:
7001
- * type: array
7002
- * items:
7003
- * type: string
7004
- */
7005
- app.get([`/books`, `${rootPath}/books`], async (request, response) => {
7771
+ app.get(`/books`, async (request, response) => {
7006
7772
  if (collection === null) {
7007
7773
  response.status(500).send('No collection available');
7008
7774
  return;
@@ -7012,30 +7778,7 @@
7012
7778
  response.send(pipelines);
7013
7779
  });
7014
7780
  // TODO: [🧠] Is it secure / good idea to expose source codes of hosted books
7015
- /**
7016
- * @swagger
7017
- * /books/{bookId}:
7018
- * get:
7019
- * summary: Get book content
7020
- * description: Returns the content of a specific book.
7021
- * parameters:
7022
- * - in: path
7023
- * name: bookId
7024
- * required: true
7025
- * schema:
7026
- * type: string
7027
- * description: The ID of the book to retrieve.
7028
- * responses:
7029
- * 200:
7030
- * description: The content of the book.
7031
- * content:
7032
- * text/markdown:
7033
- * schema:
7034
- * type: string
7035
- * 404:
7036
- * description: Book not found.
7037
- */
7038
- app.get([`/books/*`, `${rootPath}/books/*`], async (request, response) => {
7781
+ app.get(`/books/*`, async (request, response) => {
7039
7782
  try {
7040
7783
  if (collection === null) {
7041
7784
  response.status(500).send('No collection nor books available');
@@ -7054,9 +7797,7 @@
7054
7797
  .send(source.content);
7055
7798
  }
7056
7799
  catch (error) {
7057
- if (!(error instanceof Error)) {
7058
- throw error;
7059
- }
7800
+ assertsError(error);
7060
7801
  response
7061
7802
  .status(404)
7062
7803
  .send({ error: serializeError(error) });
@@ -7089,26 +7830,10 @@
7089
7830
  };
7090
7831
  }
7091
7832
  }
7092
- /**
7093
- * @swagger
7094
- * /executions:
7095
- * get:
7096
- * summary: List all executions
7097
- * description: Returns a list of all running execution tasks.
7098
- * responses:
7099
- * 200:
7100
- * description: A list of execution tasks.
7101
- * content:
7102
- * application/json:
7103
- * schema:
7104
- * type: array
7105
- * items:
7106
- * type: object
7107
- */
7108
- app.get([`/executions`, `${rootPath}/executions`], async (request, response) => {
7109
- response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)));
7833
+ app.get(`/executions`, async (request, response) => {
7834
+ response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)) /* <- TODO: satisfies paths['/executions']['get']['responses']['200']['content']['application/json'] */);
7110
7835
  });
7111
- app.get([`/executions/last`, `${rootPath}/executions/last`], async (request, response) => {
7836
+ app.get(`/executions/last`, async (request, response) => {
7112
7837
  // TODO: [🤬] Filter only for user
7113
7838
  if (runningExecutionTasks.length === 0) {
7114
7839
  response.status(404).send('No execution tasks found');
@@ -7117,7 +7842,7 @@
7117
7842
  const lastExecutionTask = runningExecutionTasks[runningExecutionTasks.length - 1];
7118
7843
  response.send(exportExecutionTask(lastExecutionTask, true));
7119
7844
  });
7120
- app.get([`/executions/:taskId`, `${rootPath}/executions/:taskId`], async (request, response) => {
7845
+ app.get(`/executions/:taskId`, async (request, response) => {
7121
7846
  const { taskId } = request.params;
7122
7847
  // TODO: [🤬] Filter only for user
7123
7848
  const executionTask = runningExecutionTasks.find((executionTask) => executionTask.taskId === taskId);
@@ -7129,39 +7854,12 @@
7129
7854
  }
7130
7855
  response.send(exportExecutionTask(executionTask, true));
7131
7856
  });
7132
- /**
7133
- * @swagger
7134
- * /executions/new:
7135
- * post:
7136
- * summary: Start a new execution
7137
- * description: Starts a new execution task for a given pipeline.
7138
- * requestBody:
7139
- * required: true
7140
- * content:
7141
- * application/json:
7142
- * schema:
7143
- * type: object
7144
- * properties:
7145
- * pipelineUrl:
7146
- * type: string
7147
- * inputParameters:
7148
- * type: object
7149
- * identification:
7150
- * type: object
7151
- * responses:
7152
- * 200:
7153
- * description: The newly created execution task.
7154
- * content:
7155
- * application/json:
7156
- * schema:
7157
- * type: object
7158
- * 400:
7159
- * description: Invalid input.
7160
- */
7161
- app.post([`/executions/new`, `${rootPath}/executions/new`], async (request, response) => {
7857
+ app.post(`/executions/new`, async (request, response) => {
7162
7858
  try {
7163
7859
  const { inputParameters, identification /* <- [🤬] */ } = request.body;
7164
- const pipelineUrl = request.body.pipelineUrl || request.body.book;
7860
+ const pipelineUrl = request.body
7861
+ .pipelineUrl /* <- TODO: as paths['/executions/new']['post']['requestBody']['content']['application/json'] */ ||
7862
+ request.body.book;
7165
7863
  // TODO: [🧠] Check `pipelineUrl` and `inputParameters` here or it should be responsibility of `collection.getPipelineByUrl` and `pipelineExecutor`
7166
7864
  const pipeline = await (collection === null || collection === void 0 ? void 0 : collection.getPipelineByUrl(pipelineUrl));
7167
7865
  if (pipeline === undefined) {
@@ -7175,7 +7873,7 @@
7175
7873
  await waitasecond.forTime(10);
7176
7874
  // <- Note: Wait for a while to wait for quick responses or sudden but asynchronous errors
7177
7875
  // <- TODO: Put this into configuration
7178
- response.send(executionTask);
7876
+ response.send(executionTask /* <- TODO: satisfies paths['/executions/new']['post']['responses']['200']['content']['application/json'] */);
7179
7877
  /*/
7180
7878
  executionTask.asObservable().subscribe({
7181
7879
  next(partialResult) {
@@ -7195,19 +7893,24 @@
7195
7893
  */
7196
7894
  }
7197
7895
  catch (error) {
7198
- if (!(error instanceof Error)) {
7199
- throw error;
7200
- }
7896
+ assertsError(error);
7201
7897
  response.status(400).send({ error: serializeError(error) });
7202
7898
  }
7203
7899
  });
7900
+ /**
7901
+ * Catch-all handler for unmatched routes
7902
+ */
7903
+ app.use((request, response) => {
7904
+ response.status(404).send(`URL "${request.originalUrl}" was not found on Promptbook server.`);
7905
+ });
7204
7906
  const httpServer = http__default["default"].createServer(app);
7205
7907
  const server = new socket_io.Server(httpServer, {
7206
- path: socketioPath,
7207
- transports: [/*'websocket', <- TODO: [🌬] Make websocket transport work */ 'polling'],
7908
+ path: '/socket.io',
7909
+ transports: ['polling', 'websocket' /*, <- TODO: [🌬] Allow to pass `transports`, add 'webtransport' */],
7208
7910
  cors: {
7209
7911
  origin: '*',
7210
7912
  methods: ['GET', 'POST'],
7913
+ // <- TODO: [🌡] Allow to pass
7211
7914
  },
7212
7915
  });
7213
7916
  server.on('connection', (socket) => {
@@ -7261,9 +7964,7 @@
7261
7964
  socket.emit('prompt-response', { promptResult } /* <- Note: [🤛] */);
7262
7965
  }
7263
7966
  catch (error) {
7264
- if (!(error instanceof Error)) {
7265
- throw error;
7266
- }
7967
+ assertsError(error);
7267
7968
  socket.emit('error', serializeError(error) /* <- Note: [🤛] */);
7268
7969
  }
7269
7970
  finally {
@@ -7285,9 +7986,7 @@
7285
7986
  socket.emit('listModels-response', { models } /* <- Note: [🤛] */);
7286
7987
  }
7287
7988
  catch (error) {
7288
- if (!(error instanceof Error)) {
7289
- throw error;
7290
- }
7989
+ assertsError(error);
7291
7990
  socket.emit('error', serializeError(error));
7292
7991
  }
7293
7992
  finally {
@@ -7308,9 +8007,7 @@
7308
8007
  socket.emit('preparePipeline-response', { preparedPipeline } /* <- Note: [🤛] */);
7309
8008
  }
7310
8009
  catch (error) {
7311
- if (!(error instanceof Error)) {
7312
- throw error;
7313
- }
8010
+ assertsError(error);
7314
8011
  socket.emit('error', serializeError(error));
7315
8012
  // <- TODO: [🚋] There is a problem with the remote server handling errors and sending them back to the client
7316
8013
  }
@@ -7358,8 +8055,7 @@
7358
8055
  };
7359
8056
  }
7360
8057
  /**
7361
- * TODO: !! Add CORS and security - probbably via `helmet`
7362
- * TODO: [👩🏾‍🤝‍🧑🏾] Allow to pass custom fetch function here - PromptbookFetch
8058
+ * TODO: [🌡] Add CORS and security - probbably via `helmet`
7363
8059
  * TODO: Split this file into multiple functions - handler for each request
7364
8060
  * TODO: Maybe use `$exportJson`
7365
8061
  * TODO: [🧠][🛍] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`