@promptbook/cli 0.81.0-8 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +25 -8
  2. package/esm/index.es.js +1192 -794
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/books/index.d.ts +38 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +12 -4
  6. package/esm/typings/src/_packages/markdown-utils.index.d.ts +2 -2
  7. package/esm/typings/src/_packages/node.index.d.ts +0 -2
  8. package/esm/typings/src/_packages/templates.index.d.ts +2 -2
  9. package/esm/typings/src/_packages/types.index.d.ts +4 -0
  10. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  11. package/esm/typings/src/_packages/wizzard.index.d.ts +44 -0
  12. package/esm/typings/src/cli/cli-commands/make.d.ts +1 -1
  13. package/esm/typings/src/cli/cli-commands/run.d.ts +2 -2
  14. package/esm/typings/src/collection/constructors/createCollectionFromDirectory.d.ts +11 -0
  15. package/esm/typings/src/collection/constructors/createCollectionFromUrl.d.ts +1 -1
  16. package/esm/typings/src/commands/index.d.ts +1 -1
  17. package/esm/typings/src/config.d.ts +3 -3
  18. package/esm/typings/src/conversion/compilePipeline.d.ts +1 -4
  19. package/esm/typings/src/conversion/{precompilePipeline.d.ts → parsePipeline.d.ts} +3 -3
  20. package/esm/typings/src/conversion/prettify/renderPipelineMermaidOptions.d.ts +3 -3
  21. package/esm/typings/src/conversion/validation/validatePipeline.d.ts +7 -7
  22. package/esm/typings/src/errors/utils/getErrorReportUrl.d.ts +1 -1
  23. package/esm/typings/src/execution/PipelineExecutor.d.ts +2 -2
  24. package/esm/typings/src/execution/createPipelineExecutor/10-executePipeline.d.ts +2 -2
  25. package/esm/typings/src/formfactors/generator/GeneratorFormfactorDefinition.d.ts +9 -4
  26. package/esm/typings/src/formfactors/image-generator/ImageGeneratorFormfactorDefinition.d.ts +24 -0
  27. package/esm/typings/src/formfactors/index.d.ts +31 -9
  28. package/esm/typings/src/high-level-abstractions/_common/HighLevelAbstraction.d.ts +1 -1
  29. package/esm/typings/src/high-level-abstractions/index.d.ts +3 -3
  30. package/esm/typings/src/high-level-abstractions/quick-chatbot/QuickChatbotHla.d.ts +3 -0
  31. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  32. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +1 -1
  33. package/esm/typings/src/llm-providers/_common/register/{$provideLlmToolsForCli.d.ts → $provideLlmToolsForWizzardOrCli.d.ts} +2 -2
  34. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  35. package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  36. package/esm/typings/src/llm-providers/anthropic-claude/createAnthropicClaudeExecutionTools.d.ts +2 -2
  37. package/esm/typings/src/llm-providers/anthropic-claude/playground/playground.d.ts +2 -2
  38. package/esm/typings/src/llm-providers/anthropic-claude/register-configuration.d.ts +1 -0
  39. package/esm/typings/src/llm-providers/anthropic-claude/register-constructor.d.ts +2 -0
  40. package/esm/typings/src/llm-providers/azure-openai/register-configuration.d.ts +1 -0
  41. package/esm/typings/src/llm-providers/azure-openai/register-constructor.d.ts +1 -0
  42. package/esm/typings/src/llm-providers/google/register-configuration.d.ts +1 -0
  43. package/esm/typings/src/llm-providers/google/register-constructor.d.ts +1 -0
  44. package/esm/typings/src/llm-providers/openai/playground/playground.d.ts +1 -1
  45. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +2 -0
  46. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +2 -0
  47. package/esm/typings/src/llm-providers/vercel/playground/playground.d.ts +1 -1
  48. package/esm/typings/src/other/templates/getBookTemplates.d.ts +22 -0
  49. package/esm/typings/src/personas/preparePersona.d.ts +4 -4
  50. package/esm/typings/src/pipeline/PipelineString.d.ts +0 -3
  51. package/esm/typings/src/pipeline/book-notation.d.ts +14 -0
  52. package/esm/typings/src/pipeline/isValidPipelineString.d.ts +13 -0
  53. package/esm/typings/src/pipeline/isValidPipelineString.test.d.ts +4 -0
  54. package/esm/typings/src/pipeline/validatePipelineString.d.ts +14 -0
  55. package/esm/typings/src/prepare/isPipelinePrepared.d.ts +3 -1
  56. package/esm/typings/src/prepare/preparePipeline.d.ts +2 -0
  57. package/esm/typings/src/prepare/prepareTasks.d.ts +1 -1
  58. package/esm/typings/src/scrapers/_common/Converter.d.ts +1 -0
  59. package/esm/typings/src/scrapers/_common/Scraper.d.ts +1 -1
  60. package/esm/typings/src/scrapers/_common/ScraperIntermediateSource.d.ts +3 -0
  61. package/esm/typings/src/scrapers/_common/register/ScraperAndConverterMetadata.d.ts +2 -0
  62. package/esm/typings/src/scrapers/_common/utils/scraperFetch.d.ts +3 -0
  63. package/esm/typings/src/scrapers/document/register-constructor.d.ts +1 -0
  64. package/esm/typings/src/scrapers/document/register-metadata.d.ts +1 -0
  65. package/esm/typings/src/scrapers/document-legacy/register-constructor.d.ts +1 -0
  66. package/esm/typings/src/scrapers/document-legacy/register-metadata.d.ts +1 -0
  67. package/esm/typings/src/scrapers/markdown/register-constructor.d.ts +1 -0
  68. package/esm/typings/src/scrapers/markdown/register-metadata.d.ts +1 -0
  69. package/esm/typings/src/scrapers/pdf/PdfScraper.d.ts +1 -0
  70. package/esm/typings/src/scrapers/pdf/createPdfScraper.d.ts +1 -1
  71. package/esm/typings/src/scrapers/pdf/register-constructor.d.ts +1 -0
  72. package/esm/typings/src/scrapers/pdf/register-metadata.d.ts +2 -1
  73. package/esm/typings/src/scrapers/website/createWebsiteScraper.d.ts +3 -1
  74. package/esm/typings/src/scrapers/website/register-constructor.d.ts +1 -0
  75. package/esm/typings/src/scrapers/website/register-metadata.d.ts +1 -0
  76. package/esm/typings/src/scripting/javascript/JavascriptEvalExecutionTools.test.d.ts +1 -1
  77. package/esm/typings/src/scripting/javascript/utils/preserve.d.ts +2 -1
  78. package/esm/typings/src/types/typeAliases.d.ts +16 -2
  79. package/esm/typings/src/utils/markdown/flattenMarkdown.d.ts +1 -1
  80. package/esm/typings/src/utils/markdown/{removeContentComments.d.ts → removeMarkdownComments.d.ts} +2 -2
  81. package/esm/typings/src/utils/organization/$sideEffect.d.ts +9 -0
  82. package/esm/typings/src/utils/serialization/checkSerializableAsJson.d.ts +1 -1
  83. package/esm/typings/src/utils/serialization/isSerializableAsJson.d.ts +2 -2
  84. package/esm/typings/src/utils/validators/filePath/isRootPath.d.ts +12 -0
  85. package/esm/typings/src/utils/validators/filePath/isRootPath.test.d.ts +4 -0
  86. package/esm/typings/src/utils/validators/filePath/isValidFilePath.d.ts +3 -0
  87. package/esm/typings/src/wizzard/$getCompiledBook.d.ts +16 -0
  88. package/esm/typings/src/wizzard/wizzard.d.ts +52 -8
  89. package/package.json +1 -1
  90. package/umd/index.umd.js +1192 -794
  91. package/umd/index.umd.js.map +1 -1
  92. package/esm/typings/src/other/templates/getBookTemplate.d.ts +0 -21
  93. package/esm/typings/src/scripting/javascript/utils/unknownToString.d.ts +0 -8
  94. /package/esm/typings/src/conversion/{precompilePipeline.test.d.ts → parsePipeline.test.d.ts} +0 -0
  95. /package/esm/typings/src/utils/markdown/{removeContentComments.test.d.ts → removeMarkdownComments.test.d.ts} +0 -0
package/umd/index.umd.js CHANGED
@@ -51,7 +51,7 @@
51
51
  * @generated
52
52
  * @see https://github.com/webgptorg/promptbook
53
53
  */
54
- var PROMPTBOOK_ENGINE_VERSION = '0.81.0-7';
54
+ var PROMPTBOOK_ENGINE_VERSION = '0.81.0-24';
55
55
  /**
56
56
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
57
57
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -235,7 +235,7 @@
235
235
  *
236
236
  * @public exported from `@promptbook/core`
237
237
  */
238
- var DEFAULT_TITLE = "Untitled";
238
+ var DEFAULT_BOOK_TITLE = "\u2728 Untitled Book";
239
239
  /**
240
240
  * Warning message for the generated sections and files files
241
241
  *
@@ -320,6 +320,7 @@
320
320
  * @public exported from `@promptbook/core`
321
321
  */
322
322
  var DEFAULT_BOOKS_DIRNAME = './books';
323
+ // <- TODO: [🕝] Make also `BOOKS_DIRNAME_ALTERNATIVES`
323
324
  /**
324
325
  * Where to store the cache of executions for promptbook CLI
325
326
  *
@@ -531,7 +532,7 @@
531
532
  /**
532
533
  * Make error report URL for the given error
533
534
  *
534
- * @private !!!!!!
535
+ * @private private within the repository
535
536
  */
536
537
  function getErrorReportUrl(error) {
537
538
  var report = {
@@ -702,7 +703,7 @@
702
703
  if (!(error instanceof Error)) {
703
704
  throw error;
704
705
  }
705
- throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is not serializable\n\n ").concat(block(error.toString()), "\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
706
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is not serializable\n\n ").concat(block(error.stack || error.message), "\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
706
707
  }
707
708
  /*
708
709
  TODO: [0] Is there some more elegant way to check circular references?
@@ -732,7 +733,7 @@
732
733
  }
733
734
  /**
734
735
  * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
735
- * TODO: [🧠][main] !!! In-memory cache of same values to prevent multiple checks
736
+ * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
736
737
  * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
737
738
  */
738
739
 
@@ -744,7 +745,6 @@
744
745
  function deepClone(objectValue) {
745
746
  return JSON.parse(JSON.stringify(objectValue));
746
747
  /*
747
- !!!!!!!!
748
748
  TODO: [🧠] Is there a better implementation?
749
749
  > const propertyNames = Object.getOwnPropertyNames(objectValue);
750
750
  > for (const propertyName of propertyNames) {
@@ -856,7 +856,7 @@
856
856
  * Note: [💞] Ignore a discrepancy between file name and entity name
857
857
  */
858
858
 
859
- // <- TODO: !!!!!!! Auto convert to type `import { ... } from 'type-fest';`
859
+ // <- TODO: Auto convert to type `import { ... } from 'type-fest';`
860
860
  /**
861
861
  * Tests if the value is [🚉] serializable as JSON
862
862
  *
@@ -886,7 +886,7 @@
886
886
  }
887
887
  }
888
888
  /**
889
- * TODO: [🧠][main] !!! In-memory cache of same values to prevent multiple checks
889
+ * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
890
890
  * TODO: [🧠][💺] Can be done this on type-level?
891
891
  */
892
892
 
@@ -979,21 +979,44 @@
979
979
  if (typeof filename !== 'string') {
980
980
  return false;
981
981
  }
982
+ if (filename.split('\n').length > 1) {
983
+ return false;
984
+ }
985
+ if (filename.split(' ').length >
986
+ 5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
987
+ return false;
988
+ }
982
989
  var filenameSlashes = filename.split('\\').join('/');
983
990
  // Absolute Unix path: /hello.txt
984
991
  if (/^(\/)/i.test(filenameSlashes)) {
992
+ // console.log(filename, 'Absolute Unix path: /hello.txt');
985
993
  return true;
986
994
  }
987
995
  // Absolute Windows path: /hello.txt
988
996
  if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
997
+ // console.log(filename, 'Absolute Windows path: /hello.txt');
989
998
  return true;
990
999
  }
991
1000
  // Relative path: ./hello.txt
992
1001
  if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
1002
+ // console.log(filename, 'Relative path: ./hello.txt');
1003
+ return true;
1004
+ }
1005
+ // Allow paths like foo/hello
1006
+ if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
1007
+ // console.log(filename, 'Allow paths like foo/hello');
1008
+ return true;
1009
+ }
1010
+ // Allow paths like hello.book
1011
+ if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
1012
+ // console.log(filename, 'Allow paths like hello.book');
993
1013
  return true;
994
1014
  }
995
1015
  return false;
996
1016
  }
1017
+ /**
1018
+ * TODO: [🍏] Implement for MacOs
1019
+ */
997
1020
 
998
1021
  /**
999
1022
  * Tests if given string is valid URL.
@@ -1628,7 +1651,7 @@
1628
1651
  return [3 /*break*/, 11];
1629
1652
  case 10: throw new PipelineExecutionError("Unknown model variant \"".concat(prompt.modelRequirements.modelVariant, "\""));
1630
1653
  case 11:
1631
- // TODO: [🧠] !!!!! How to do timing in mixed cache / non-cache situation
1654
+ // TODO: [🧠] !!5 How to do timing in mixed cache / non-cache situation
1632
1655
  // promptResult.timing: FromtoItems
1633
1656
  return [4 /*yield*/, storage.setItem(key, {
1634
1657
  date: $getCurrentDate(),
@@ -1637,7 +1660,7 @@
1637
1660
  promptResult: promptResult,
1638
1661
  })];
1639
1662
  case 12:
1640
- // TODO: [🧠] !!!!! How to do timing in mixed cache / non-cache situation
1663
+ // TODO: [🧠] !!5 How to do timing in mixed cache / non-cache situation
1641
1664
  // promptResult.timing: FromtoItems
1642
1665
  _c.sent();
1643
1666
  return [2 /*return*/, promptResult];
@@ -2058,6 +2081,25 @@
2058
2081
  * TODO: [®] DRY Register logic
2059
2082
  */
2060
2083
 
2084
+ /**
2085
+ * Determines if the given path is a root path.
2086
+ *
2087
+ * Note: This does not check if the file exists only if the path is valid
2088
+ * @public exported from `@promptbook/utils`
2089
+ */
2090
+ function isRootPath(value) {
2091
+ if (value === '/') {
2092
+ return true;
2093
+ }
2094
+ if (/^[A-Z]:\\$/i.test(value)) {
2095
+ return true;
2096
+ }
2097
+ return false;
2098
+ }
2099
+ /**
2100
+ * TODO: [🍏] Make for MacOS paths
2101
+ */
2102
+
2061
2103
  /**
2062
2104
  * @@@
2063
2105
  *
@@ -2072,17 +2114,45 @@
2072
2114
  * @public exported from `@promptbook/node`
2073
2115
  */
2074
2116
  function $provideLlmToolsConfigurationFromEnv() {
2075
- if (!$isRunningInNode()) {
2076
- throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
2077
- }
2078
- dotenv__namespace.config();
2079
- // TODO: Walk to the root of the project and find the nearest `.env` file
2080
- // @see https://collboard.fra1.cdn.digitaloceanspaces.com/usercontent/education/image/png/1/2/ad/image.png
2081
- var llmToolsConfiguration = $llmToolsMetadataRegister
2082
- .list()
2083
- .map(function (metadata) { return metadata.createConfigurationFromEnv(process.env); })
2084
- .filter(function (configuration) { return configuration !== null; });
2085
- return llmToolsConfiguration;
2117
+ return __awaiter(this, void 0, void 0, function () {
2118
+ var rootDirname, i, envFilename, llmToolsConfiguration;
2119
+ return __generator(this, function (_a) {
2120
+ switch (_a.label) {
2121
+ case 0:
2122
+ if (!$isRunningInNode()) {
2123
+ throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
2124
+ }
2125
+ rootDirname = process.cwd();
2126
+ i = 0;
2127
+ _a.label = 1;
2128
+ case 1:
2129
+ if (!(i < LOOP_LIMIT)) return [3 /*break*/, 4];
2130
+ envFilename = path.join(rootDirname, '.env' /* <- TODO: [🕝] Make here more candidates */);
2131
+ return [4 /*yield*/, isFileExisting(envFilename, $provideFilesystemForNode())];
2132
+ case 2:
2133
+ // console.log({ rootDirname, envFilename });
2134
+ if (_a.sent()) {
2135
+ dotenv__namespace.config({ path: envFilename });
2136
+ return [3 /*break*/, 4];
2137
+ }
2138
+ if (isRootPath(rootDirname)) {
2139
+ return [3 /*break*/, 4];
2140
+ }
2141
+ // Note: If the directory does not exist, try the parent directory
2142
+ rootDirname = path.join(rootDirname, '..');
2143
+ _a.label = 3;
2144
+ case 3:
2145
+ i++;
2146
+ return [3 /*break*/, 1];
2147
+ case 4:
2148
+ llmToolsConfiguration = $llmToolsMetadataRegister
2149
+ .list()
2150
+ .map(function (metadata) { return metadata.createConfigurationFromEnv(process.env); })
2151
+ .filter(function (configuration) { return configuration !== null; });
2152
+ return [2 /*return*/, llmToolsConfiguration];
2153
+ }
2154
+ });
2155
+ });
2086
2156
  }
2087
2157
  /**
2088
2158
  * TODO: [🧠][🪁] Maybe do allow to do auto-install if package not registered and not found
@@ -2597,15 +2667,28 @@
2597
2667
  */
2598
2668
  function $provideLlmToolsFromEnv(options) {
2599
2669
  if (options === void 0) { options = {}; }
2600
- if (!$isRunningInNode()) {
2601
- throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
2602
- }
2603
- var configuration = $provideLlmToolsConfigurationFromEnv();
2604
- if (configuration.length === 0) {
2605
- // TODO: [🥃]
2606
- throw new Error(spaceTrim__default["default"](function (block) { return "\n No LLM tools found in the environment\n\n Please set one of environment variables:\n - OPENAI_API_KEY\n - ANTHROPIC_CLAUDE_API_KEY\n\n ".concat(block($registeredLlmToolsMessage()), "}\n "); }));
2607
- }
2608
- return createLlmToolsFromConfiguration(configuration, options);
2670
+ return __awaiter(this, void 0, void 0, function () {
2671
+ var configuration;
2672
+ return __generator(this, function (_a) {
2673
+ switch (_a.label) {
2674
+ case 0:
2675
+ if (!$isRunningInNode()) {
2676
+ throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
2677
+ }
2678
+ return [4 /*yield*/, $provideLlmToolsConfigurationFromEnv()];
2679
+ case 1:
2680
+ configuration = _a.sent();
2681
+ if (configuration.length === 0) {
2682
+ if ($llmToolsMetadataRegister.list().length === 0) {
2683
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n No LLM tools registered, this is probably a bug in the Promptbook library\n\n ".concat(block($registeredLlmToolsMessage()), "}\n "); }));
2684
+ }
2685
+ // TODO: [🥃]
2686
+ throw new Error(spaceTrim__default["default"](function (block) { return "\n No LLM tools found in the environment\n\n ".concat(block($registeredLlmToolsMessage()), "}\n "); }));
2687
+ }
2688
+ return [2 /*return*/, createLlmToolsFromConfiguration(configuration, options)];
2689
+ }
2690
+ });
2691
+ });
2609
2692
  }
2610
2693
  /**
2611
2694
  * TODO: @@@ write `$provideLlmToolsFromEnv` vs `$provideLlmToolsConfigurationFromEnv` vs `createLlmToolsFromConfiguration`
@@ -2622,22 +2705,35 @@
2622
2705
  *
2623
2706
  * @private within the repository - for CLI utils
2624
2707
  */
2625
- function $provideLlmToolsForCli(options) {
2626
- if (!$isRunningInNode()) {
2627
- throw new EnvironmentMismatchError('Function `$provideLlmToolsForTestingAndScriptsAndPlayground` works only in Node.js environment');
2628
- }
2629
- var isCacheReloaded = (options !== null && options !== void 0 ? options : {}).isCacheReloaded;
2630
- return cacheLlmTools(countTotalUsage(
2631
- // <- Note: for example here we don`t want the [🌯]
2632
- $provideLlmToolsFromEnv()), {
2633
- storage: new FileCacheStorage({ fs: $provideFilesystemForNode() }, {
2634
- rootFolderPath: path.join(process.cwd(), DEFAULT_EXECUTIONS_CACHE_DIRNAME),
2635
- }),
2636
- isCacheReloaded: isCacheReloaded,
2708
+ function $provideLlmToolsForWizzardOrCli(options) {
2709
+ return __awaiter(this, void 0, void 0, function () {
2710
+ var isCacheReloaded, _a, _b;
2711
+ return __generator(this, function (_c) {
2712
+ switch (_c.label) {
2713
+ case 0:
2714
+ if (!$isRunningInNode()) {
2715
+ throw new EnvironmentMismatchError('Function `$provideLlmToolsForWizzardOrCli` works only in Node.js environment');
2716
+ }
2717
+ isCacheReloaded = (options !== null && options !== void 0 ? options : {}).isCacheReloaded;
2718
+ _a = cacheLlmTools;
2719
+ _b = countTotalUsage;
2720
+ // <- Note: for example here we don`t want the [🌯]
2721
+ return [4 /*yield*/, $provideLlmToolsFromEnv()];
2722
+ case 1: return [2 /*return*/, _a.apply(void 0, [_b.apply(void 0, [
2723
+ // <- Note: for example here we don`t want the [🌯]
2724
+ _c.sent()]),
2725
+ {
2726
+ storage: new FileCacheStorage({ fs: $provideFilesystemForNode() }, {
2727
+ rootFolderPath: path.join(process.cwd(), DEFAULT_EXECUTIONS_CACHE_DIRNAME),
2728
+ }),
2729
+ isCacheReloaded: isCacheReloaded,
2730
+ }])];
2731
+ }
2732
+ });
2637
2733
  });
2638
2734
  }
2639
2735
  /**
2640
- * Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
2736
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
2641
2737
  * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
2642
2738
  * TODO: [🥃] Allow `ptbk make` without llm tools
2643
2739
  * TODO: This should be maybe not under `_common` but under `utils-internal` / `utils/internal`
@@ -2655,10 +2751,14 @@
2655
2751
  listModelsCommand.description(spaceTrim__default["default"]("\n List all available and configured LLM models\n "));
2656
2752
  listModelsCommand.action(function () { return __awaiter(_this, void 0, void 0, function () {
2657
2753
  return __generator(this, function (_a) {
2658
- $provideLlmToolsForCli({});
2659
- // <- Note: Providing LLM tools will make a side effect of registering all available LLM tools to show the message
2660
- console.info($registeredLlmToolsMessage());
2661
- return [2 /*return*/, process.exit(0)];
2754
+ switch (_a.label) {
2755
+ case 0: return [4 /*yield*/, $provideLlmToolsForWizzardOrCli({})];
2756
+ case 1:
2757
+ _a.sent();
2758
+ // <- Note: Providing LLM tools will make a side effect of registering all available LLM tools to show the message
2759
+ console.info($registeredLlmToolsMessage());
2760
+ return [2 /*return*/, process.exit(0)];
2761
+ }
2662
2762
  });
2663
2763
  }); });
2664
2764
  }
@@ -2694,99 +2794,67 @@
2694
2794
  * TODO: [🧠] Maybe clear `sourceFile` or clear when exposing through API or remote server
2695
2795
  */
2696
2796
 
2797
+ var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book.md",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.md`\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.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book.md",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.md`\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.md"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book.md",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.md`\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.md"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book.md",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.md`\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.md"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book.md",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.md`\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.md"}];
2798
+
2697
2799
  /**
2698
- * This error type indicates that some tools are missing for pipeline execution or preparation
2800
+ * Function isValidJsonString will tell you if the string is valid JSON or not
2801
+ *
2802
+ * @public exported from `@promptbook/utils`
2803
+ */
2804
+ function isValidJsonString(value /* <- [👨‍⚖️] */) {
2805
+ try {
2806
+ JSON.parse(value);
2807
+ return true;
2808
+ }
2809
+ catch (error) {
2810
+ if (!(error instanceof Error)) {
2811
+ throw error;
2812
+ }
2813
+ if (error.message.includes('Unexpected token')) {
2814
+ return false;
2815
+ }
2816
+ return false;
2817
+ }
2818
+ }
2819
+
2820
+ /**
2821
+ * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
2699
2822
  *
2700
2823
  * @public exported from `@promptbook/core`
2701
2824
  */
2702
- var MissingToolsError = /** @class */ (function (_super) {
2703
- __extends(MissingToolsError, _super);
2704
- function MissingToolsError(message) {
2705
- var _this = _super.call(this, spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(message), "\n\n Note: You have probbably forgot to provide some tools for pipeline execution or preparation\n\n "); })) || this;
2706
- _this.name = 'MissingToolsError';
2707
- Object.setPrototypeOf(_this, MissingToolsError.prototype);
2825
+ var ParseError = /** @class */ (function (_super) {
2826
+ __extends(ParseError, _super);
2827
+ function ParseError(message) {
2828
+ var _this = _super.call(this, message) || this;
2829
+ _this.name = 'ParseError';
2830
+ Object.setPrototypeOf(_this, ParseError.prototype);
2708
2831
  return _this;
2709
2832
  }
2710
- return MissingToolsError;
2833
+ return ParseError;
2711
2834
  }(Error));
2835
+ /**
2836
+ * TODO: Maybe split `ParseError` and `ApplyError`
2837
+ */
2712
2838
 
2713
2839
  /**
2714
- * Async version of Array.forEach
2840
+ * Function `validatePipelineString` will validate the if the string is a valid pipeline string
2841
+ * It does not check if the string is fully logically correct, but if it is a string that can be a pipeline string or the string looks completely different.
2715
2842
  *
2716
- * @param array - Array to iterate over
2717
- * @param options - Options for the function
2718
- * @param callbackfunction - Function to call for each item
2719
- * @public exported from `@promptbook/utils`
2720
- * @deprecated [🪂] Use queues instead
2843
+ * @param {string} pipelineString the candidate for a pipeline string
2844
+ * @returns {PipelineString} the same string as input, but validated as valid
2845
+ * @throws {ParseError} if the string is not a valid pipeline string
2846
+ * @public exported from `@promptbook/core`
2721
2847
  */
2722
- function forEachAsync(array, options, callbackfunction) {
2723
- return __awaiter(this, void 0, void 0, function () {
2724
- var _a, maxParallelCount, index, runningTasks, tasks, _loop_1, _b, _c, item, e_1_1;
2725
- var e_1, _d;
2726
- return __generator(this, function (_e) {
2727
- switch (_e.label) {
2728
- case 0:
2729
- _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? Infinity : _a;
2730
- index = 0;
2731
- runningTasks = [];
2732
- tasks = [];
2733
- _loop_1 = function (item) {
2734
- var currentIndex, task;
2735
- return __generator(this, function (_f) {
2736
- switch (_f.label) {
2737
- case 0:
2738
- currentIndex = index++;
2739
- task = callbackfunction(item, currentIndex, array);
2740
- tasks.push(task);
2741
- runningTasks.push(task);
2742
- /* not await */ Promise.resolve(task).then(function () {
2743
- runningTasks = runningTasks.filter(function (t) { return t !== task; });
2744
- });
2745
- if (!(maxParallelCount < runningTasks.length)) return [3 /*break*/, 2];
2746
- return [4 /*yield*/, Promise.race(runningTasks)];
2747
- case 1:
2748
- _f.sent();
2749
- _f.label = 2;
2750
- case 2: return [2 /*return*/];
2751
- }
2752
- });
2753
- };
2754
- _e.label = 1;
2755
- case 1:
2756
- _e.trys.push([1, 6, 7, 8]);
2757
- _b = __values(array), _c = _b.next();
2758
- _e.label = 2;
2759
- case 2:
2760
- if (!!_c.done) return [3 /*break*/, 5];
2761
- item = _c.value;
2762
- return [5 /*yield**/, _loop_1(item)];
2763
- case 3:
2764
- _e.sent();
2765
- _e.label = 4;
2766
- case 4:
2767
- _c = _b.next();
2768
- return [3 /*break*/, 2];
2769
- case 5: return [3 /*break*/, 8];
2770
- case 6:
2771
- e_1_1 = _e.sent();
2772
- e_1 = { error: e_1_1 };
2773
- return [3 /*break*/, 8];
2774
- case 7:
2775
- try {
2776
- if (_c && !_c.done && (_d = _b.return)) _d.call(_b);
2777
- }
2778
- finally { if (e_1) throw e_1.error; }
2779
- return [7 /*endfinally*/];
2780
- case 8: return [4 /*yield*/, Promise.all(tasks)];
2781
- case 9:
2782
- _e.sent();
2783
- return [2 /*return*/];
2784
- }
2785
- });
2786
- });
2848
+ function validatePipelineString(pipelineString) {
2849
+ if (isValidJsonString(pipelineString)) {
2850
+ throw new ParseError('Expected a book, but got a JSON string');
2851
+ }
2852
+ // <- TODO: Implement the validation + add tests when the pipeline logic considered as invalid
2853
+ return pipelineString;
2787
2854
  }
2788
-
2789
- var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book.md",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.md`\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.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book.md",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.md`\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.md"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book.md",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- Title should be concise and clear\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 Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book.md`\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- Title should be concise and clear\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.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book.md",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 Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book.md`\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.md"}];
2855
+ /**
2856
+ * TODO: [🧠][🈴] Where is the best location for this file
2857
+ */
2790
2858
 
2791
2859
  /**
2792
2860
  * Prettify the html code
@@ -2854,7 +2922,7 @@
2854
2922
  if (bookVersion !== "undefined") {
2855
2923
  commands.push("BOOK VERSION ".concat(bookVersion));
2856
2924
  }
2857
- // TODO: [main] !!!!! This increases size of the bundle and is probbably not necessary
2925
+ // TODO: [main] !!5 This increases size of the bundle and is probbably not necessary
2858
2926
  pipelineString = prettifyMarkdown(pipelineString);
2859
2927
  try {
2860
2928
  for (var _g = __values(parameters.filter(function (_a) {
@@ -3002,12 +3070,12 @@
3002
3070
  pipelineString += '```' + contentLanguage;
3003
3071
  pipelineString += '\n';
3004
3072
  pipelineString += spaceTrim__default["default"](content);
3005
- // <- TODO: [main] !!! Escape
3073
+ // <- TODO: [main] !!3 Escape
3006
3074
  // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
3007
3075
  pipelineString += '\n';
3008
3076
  pipelineString += '```';
3009
3077
  pipelineString += '\n\n';
3010
- pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!! If the parameter here has description, add it and use taskParameterJsonToString
3078
+ pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
3011
3079
  }
3012
3080
  }
3013
3081
  catch (e_3_1) { e_3 = { error: e_3_1 }; }
@@ -3017,7 +3085,7 @@
3017
3085
  }
3018
3086
  finally { if (e_3) throw e_3.error; }
3019
3087
  }
3020
- return pipelineString;
3088
+ return validatePipelineString(pipelineString);
3021
3089
  }
3022
3090
  /**
3023
3091
  * @private internal utility of `pipelineJsonToString`
@@ -3038,25 +3106,6 @@
3038
3106
  * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
3039
3107
  */
3040
3108
 
3041
- /**
3042
- * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
3043
- *
3044
- * @public exported from `@promptbook/core`
3045
- */
3046
- var ParseError = /** @class */ (function (_super) {
3047
- __extends(ParseError, _super);
3048
- function ParseError(message) {
3049
- var _this = _super.call(this, message) || this;
3050
- _this.name = 'ParseError';
3051
- Object.setPrototypeOf(_this, ParseError.prototype);
3052
- return _this;
3053
- }
3054
- return ParseError;
3055
- }(Error));
3056
- /**
3057
- * TODO: Maybe split `ParseError` and `ApplyError`
3058
- */
3059
-
3060
3109
  /**
3061
3110
  * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
3062
3111
  *
@@ -3111,7 +3160,7 @@
3111
3160
  if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
3112
3161
  return false;
3113
3162
  }
3114
- // <- TODO: [main] !!! Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
3163
+ // <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
3115
3164
  return true;
3116
3165
  }
3117
3166
 
@@ -3182,9 +3231,6 @@
3182
3231
  if (!url.startsWith('https://')) {
3183
3232
  return false;
3184
3233
  }
3185
- if (!(url.endsWith('.book.md') || url.endsWith('.book') || url.endsWith('.book.md') || url.endsWith('.ptbk'))) {
3186
- return false;
3187
- }
3188
3234
  if (url.includes('#')) {
3189
3235
  // TODO: [🐠]
3190
3236
  return false;
@@ -3215,11 +3261,11 @@
3215
3261
  */
3216
3262
  function validatePipeline(pipeline) {
3217
3263
  if (IS_PIPELINE_LOGIC_VALIDATED) {
3218
- validatePipelineCore(pipeline);
3264
+ validatePipeline_InnerFunction(pipeline);
3219
3265
  }
3220
3266
  else {
3221
3267
  try {
3222
- validatePipelineCore(pipeline);
3268
+ validatePipeline_InnerFunction(pipeline);
3223
3269
  }
3224
3270
  catch (error) {
3225
3271
  if (!(error instanceof PipelineLogicError)) {
@@ -3233,7 +3279,7 @@
3233
3279
  /**
3234
3280
  * @private internal function for `validatePipeline`
3235
3281
  */
3236
- function validatePipelineCore(pipeline) {
3282
+ function validatePipeline_InnerFunction(pipeline) {
3237
3283
  // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
3238
3284
  var e_1, _a, e_2, _b, e_3, _c;
3239
3285
  var pipelineIdentification = (function () {
@@ -3457,11 +3503,11 @@
3457
3503
  _loop_3();
3458
3504
  }
3459
3505
  // Note: Check that formfactor is corresponding to the pipeline interface
3460
- // TODO: !!!!!! Implement this
3506
+ // TODO: !!6 Implement this
3461
3507
  // pipeline.formfactorName
3462
3508
  }
3463
3509
  /**
3464
- * TODO: !! [🧞‍♀️] Do not allow joker + foreach
3510
+ * TODO: [🧞‍♀️] Do not allow joker + foreach
3465
3511
  * TODO: [🧠] Work with promptbookVersion
3466
3512
  * TODO: Use here some json-schema, Zod or something similar and change it to:
3467
3513
  * > /**
@@ -3473,11 +3519,11 @@
3473
3519
  * > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
3474
3520
  */
3475
3521
  /**
3476
- * TODO: [🧳][main] !!!! Validate that all examples match expectations
3477
- * TODO: [🧳][🐝][main] !!!! Validate that knowledge is valid (non-void)
3478
- * TODO: [🧳][main] !!!! Validate that persona can be used only with CHAT variant
3479
- * TODO: [🧳][main] !!!! Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
3480
- * TODO: [🧳][main] !!!! Validate that reserved parameter is not used as joker
3522
+ * TODO: [🧳][main] !!4 Validate that all examples match expectations
3523
+ * TODO: [🧳][🐝][main] !!4 Validate that knowledge is valid (non-void)
3524
+ * TODO: [🧳][main] !!4 Validate that persona can be used only with CHAT variant
3525
+ * TODO: [🧳][main] !!4 Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
3526
+ * TODO: [🧳][main] !!4 Validate that reserved parameter is not used as joker
3481
3527
  * TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
3482
3528
  * TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
3483
3529
  */
@@ -3613,7 +3659,7 @@
3613
3659
  pipelineJsonToString(unpreparePipeline(pipeline)) !==
3614
3660
  pipelineJsonToString(unpreparePipeline(this.collection.get(pipeline.pipelineUrl)))) {
3615
3661
  var existing = this.collection.get(pipeline.pipelineUrl);
3616
- throw new PipelineUrlError(spaceTrim.spaceTrim("\n Pipeline with URL \"".concat(pipeline.pipelineUrl, "\" is already in the collection \uD83C\uDF4E\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
3662
+ throw new PipelineUrlError(spaceTrim.spaceTrim("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is already in the collection \uD83C\uDF4E\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
3617
3663
  }
3618
3664
  // Note: [🧠] Overwrite existing pipeline with the same URL
3619
3665
  this.collection.set(pipeline.pipelineUrl, pipeline);
@@ -3678,6 +3724,22 @@
3678
3724
  return new (SimplePipelineCollection.bind.apply(SimplePipelineCollection, __spreadArray([void 0], __read(promptbooks), false)))();
3679
3725
  }
3680
3726
 
3727
+ /**
3728
+ * This error type indicates that some tools are missing for pipeline execution or preparation
3729
+ *
3730
+ * @public exported from `@promptbook/core`
3731
+ */
3732
+ var MissingToolsError = /** @class */ (function (_super) {
3733
+ __extends(MissingToolsError, _super);
3734
+ function MissingToolsError(message) {
3735
+ var _this = _super.call(this, spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(message), "\n\n Note: You have probbably forgot to provide some tools for pipeline execution or preparation\n\n "); })) || this;
3736
+ _this.name = 'MissingToolsError';
3737
+ Object.setPrototypeOf(_this, MissingToolsError.prototype);
3738
+ return _this;
3739
+ }
3740
+ return MissingToolsError;
3741
+ }(Error));
3742
+
3681
3743
  /**
3682
3744
  * This error indicates problems parsing the format value
3683
3745
  *
@@ -3885,11 +3947,16 @@
3885
3947
  /**
3886
3948
  * Determine if the pipeline is fully prepared
3887
3949
  *
3950
+ * @see https://github.com/webgptorg/promptbook/discussions/196
3951
+ *
3888
3952
  * @public exported from `@promptbook/core`
3889
3953
  */
3890
3954
  function isPipelinePrepared(pipeline) {
3891
3955
  // Note: Ignoring `pipeline.preparations` @@@
3892
3956
  // Note: Ignoring `pipeline.knowledgePieces` @@@
3957
+ if (pipeline.title === undefined || pipeline.title === '' || pipeline.title === DEFAULT_BOOK_TITLE) {
3958
+ return false;
3959
+ }
3893
3960
  if (!pipeline.personas.every(function (persona) { return persona.modelRequirements !== undefined; })) {
3894
3961
  return false;
3895
3962
  }
@@ -3905,7 +3972,7 @@
3905
3972
  return true;
3906
3973
  }
3907
3974
  /**
3908
- * TODO: [🔃][main] !! If the pipeline was prepared with different version or different set of models, prepare it once again
3975
+ * TODO: [🔃][main] If the pipeline was prepared with different version or different set of models, prepare it once again
3909
3976
  * TODO: [🐠] Maybe base this on `makeValidator`
3910
3977
  * TODO: [🧊] Pipeline can be partially prepared, this should return true ONLY if fully prepared
3911
3978
  * TODO: [🧿] Maybe do same process with same granularity and subfinctions as `preparePipeline`
@@ -3915,25 +3982,100 @@
3915
3982
  */
3916
3983
 
3917
3984
  /**
3918
- * Serializes an error into a [🚉] JSON-serializable object
3985
+ * Format either small or big number
3919
3986
  *
3920
3987
  * @public exported from `@promptbook/utils`
3921
3988
  */
3922
- function serializeError(error) {
3923
- var name = error.name, message = error.message, stack = error.stack;
3924
- if (!Object.keys(ALL_ERRORS).includes(name)) {
3925
- console.error(spaceTrim__default["default"](function (block) { return "\n \n Cannot serialize error with name \"".concat(name, "\"\n\n ").concat(block(stack || message), "\n \n "); }));
3989
+ function numberToString(value) {
3990
+ if (value === 0) {
3991
+ return '0';
3926
3992
  }
3927
- return {
3928
- name: name,
3929
- message: message,
3930
- stack: stack,
3931
- };
3932
- }
3933
-
3934
- /**
3935
- * Parses the given script and returns the list of all used variables that are not defined in the script
3936
- *
3993
+ else if (Number.isNaN(value)) {
3994
+ return VALUE_STRINGS.nan;
3995
+ }
3996
+ else if (value === Infinity) {
3997
+ return VALUE_STRINGS.infinity;
3998
+ }
3999
+ else if (value === -Infinity) {
4000
+ return VALUE_STRINGS.negativeInfinity;
4001
+ }
4002
+ for (var exponent = 0; exponent < 15; exponent++) {
4003
+ var factor = Math.pow(10, exponent);
4004
+ var valueRounded = Math.round(value * factor) / factor;
4005
+ if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
4006
+ return valueRounded.toFixed(exponent);
4007
+ }
4008
+ }
4009
+ return value.toString();
4010
+ }
4011
+
4012
+ /**
4013
+ * Function `valueToString` will convert the given value to string
4014
+ * This is useful and used in the `templateParameters` function
4015
+ *
4016
+ * Note: This function is not just calling `toString` method
4017
+ * It's more complex and can handle this conversion specifically for LLM models
4018
+ * See `VALUE_STRINGS`
4019
+ *
4020
+ * Note: There are 2 similar functions
4021
+ * - `valueToString` converts value to string for LLM models as human-readable string
4022
+ * - `asSerializable` converts value to string to preserve full information to be able to convert it back
4023
+ *
4024
+ * @public exported from `@promptbook/utils`
4025
+ */
4026
+ function valueToString(value) {
4027
+ try {
4028
+ if (value === '') {
4029
+ return VALUE_STRINGS.empty;
4030
+ }
4031
+ else if (value === null) {
4032
+ return VALUE_STRINGS.null;
4033
+ }
4034
+ else if (value === undefined) {
4035
+ return VALUE_STRINGS.undefined;
4036
+ }
4037
+ else if (typeof value === 'string') {
4038
+ return value;
4039
+ }
4040
+ else if (typeof value === 'number') {
4041
+ return numberToString(value);
4042
+ }
4043
+ else if (value instanceof Date) {
4044
+ return value.toISOString();
4045
+ }
4046
+ else {
4047
+ return JSON.stringify(value);
4048
+ }
4049
+ }
4050
+ catch (error) {
4051
+ if (!(error instanceof Error)) {
4052
+ throw error;
4053
+ }
4054
+ console.error(error);
4055
+ return VALUE_STRINGS.unserializable;
4056
+ }
4057
+ }
4058
+
4059
+ /**
4060
+ * Serializes an error into a [🚉] JSON-serializable object
4061
+ *
4062
+ * @public exported from `@promptbook/utils`
4063
+ */
4064
+ function serializeError(error) {
4065
+ var name = error.name, message = error.message, stack = error.stack;
4066
+ if (!Object.keys(ALL_ERRORS).includes(name)) {
4067
+ console.error(spaceTrim__default["default"](function (block) { return "\n \n Cannot serialize error with name \"".concat(name, "\"\n\n ").concat(block(stack || message), "\n \n "); }));
4068
+ }
4069
+ return {
4070
+ name: name,
4071
+ message: message,
4072
+ stack: stack,
4073
+ };
4074
+ }
4075
+
4076
+ /**
4077
+ * Parses the given script and returns the list of all used variables that are not defined in the script
4078
+ *
3937
4079
  * @param script from which to extract the variables
3938
4080
  * @returns the list of variable names
3939
4081
  * @throws {ParseError} if the script is invalid
@@ -3985,7 +4127,7 @@
3985
4127
  if (!(error instanceof Error)) {
3986
4128
  throw error;
3987
4129
  }
3988
- throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Can not extract variables from the script\n\n ".concat(block(error.toString()), "}\n\n\n Found variables:\n\n ").concat(Array.from(variables)
4130
+ throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Can not extract variables from the script\n ".concat(block(error.stack || error.message), "\n\n Found variables:\n ").concat(Array.from(variables)
3989
4131
  .map(function (variableName, i) { return "".concat(i + 1, ") ").concat(variableName); })
3990
4132
  .join('\n'), "\n\n\n The script:\n\n ```javascript\n ").concat(block(originalScript), "\n ```\n "); }));
3991
4133
  }
@@ -4265,27 +4407,6 @@
4265
4407
  * TODO: [🏢] Allow to expect something inside CSV objects and other formats
4266
4408
  */
4267
4409
 
4268
- /**
4269
- * Function isValidJsonString will tell you if the string is valid JSON or not
4270
- *
4271
- * @public exported from `@promptbook/utils`
4272
- */
4273
- function isValidJsonString(value /* <- [👨‍⚖️] */) {
4274
- try {
4275
- JSON.parse(value);
4276
- return true;
4277
- }
4278
- catch (error) {
4279
- if (!(error instanceof Error)) {
4280
- throw error;
4281
- }
4282
- if (error.message.includes('Unexpected token')) {
4283
- return false;
4284
- }
4285
- return false;
4286
- }
4287
- }
4288
-
4289
4410
  /**
4290
4411
  * Definition for JSON format
4291
4412
  *
@@ -4623,81 +4744,6 @@
4623
4744
  return [input];
4624
4745
  }
4625
4746
 
4626
- /**
4627
- * Format either small or big number
4628
- *
4629
- * @public exported from `@promptbook/utils`
4630
- */
4631
- function numberToString(value) {
4632
- if (value === 0) {
4633
- return '0';
4634
- }
4635
- else if (Number.isNaN(value)) {
4636
- return VALUE_STRINGS.nan;
4637
- }
4638
- else if (value === Infinity) {
4639
- return VALUE_STRINGS.infinity;
4640
- }
4641
- else if (value === -Infinity) {
4642
- return VALUE_STRINGS.negativeInfinity;
4643
- }
4644
- for (var exponent = 0; exponent < 15; exponent++) {
4645
- var factor = Math.pow(10, exponent);
4646
- var valueRounded = Math.round(value * factor) / factor;
4647
- if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
4648
- return valueRounded.toFixed(exponent);
4649
- }
4650
- }
4651
- return value.toString();
4652
- }
4653
-
4654
- /**
4655
- * Function `valueToString` will convert the given value to string
4656
- * This is useful and used in the `templateParameters` function
4657
- *
4658
- * Note: This function is not just calling `toString` method
4659
- * It's more complex and can handle this conversion specifically for LLM models
4660
- * See `VALUE_STRINGS`
4661
- *
4662
- * Note: There are 2 similar functions
4663
- * - `valueToString` converts value to string for LLM models as human-readable string
4664
- * - `asSerializable` converts value to string to preserve full information to be able to convert it back
4665
- *
4666
- * @public exported from `@promptbook/utils`
4667
- */
4668
- function valueToString(value) {
4669
- try {
4670
- if (value === '') {
4671
- return VALUE_STRINGS.empty;
4672
- }
4673
- else if (value === null) {
4674
- return VALUE_STRINGS.null;
4675
- }
4676
- else if (value === undefined) {
4677
- return VALUE_STRINGS.undefined;
4678
- }
4679
- else if (typeof value === 'string') {
4680
- return value;
4681
- }
4682
- else if (typeof value === 'number') {
4683
- return numberToString(value);
4684
- }
4685
- else if (value instanceof Date) {
4686
- return value.toISOString();
4687
- }
4688
- else {
4689
- return JSON.stringify(value);
4690
- }
4691
- }
4692
- catch (error) {
4693
- if (!(error instanceof Error)) {
4694
- throw error;
4695
- }
4696
- console.error(error);
4697
- return VALUE_STRINGS.unserializable;
4698
- }
4699
- }
4700
-
4701
4747
  /**
4702
4748
  * Replaces parameters in template with values from parameters object
4703
4749
  *
@@ -4754,6 +4800,8 @@
4754
4800
  throw new PipelineExecutionError("Parameter `{".concat(parameterName, "}` is not defined"));
4755
4801
  }
4756
4802
  parameterValue = valueToString(parameterValue);
4803
+ // Escape curly braces in parameter values to prevent prompt-injection
4804
+ parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4757
4805
  if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
4758
4806
  parameterValue = parameterValue
4759
4807
  .split('\n')
@@ -5113,7 +5161,7 @@
5113
5161
  promptTitle: task.title,
5114
5162
  promptMessage: templateParameters(task.description || '', parameters),
5115
5163
  defaultValue: templateParameters(preparedContent, parameters),
5116
- // TODO: [🧠] !! Figure out how to define placeholder in .book.md file
5164
+ // TODO: [🧠] Figure out how to define placeholder in .book.md file
5117
5165
  placeholder: undefined,
5118
5166
  priority: priority,
5119
5167
  }))];
@@ -5813,7 +5861,10 @@
5813
5861
  finally { if (e_2) throw e_2.error; }
5814
5862
  return [7 /*endfinally*/];
5815
5863
  case 19:
5816
- parametersToPass = inputParameters;
5864
+ parametersToPass = Object.fromEntries(Object.entries(inputParameters).map(function (_a) {
5865
+ var _b = __read(_a, 2), key = _b[0], value = _b[1];
5866
+ return [key, valueToString(value)];
5867
+ }));
5817
5868
  _g.label = 20;
5818
5869
  case 20:
5819
5870
  _g.trys.push([20, 25, , 28]);
@@ -6049,46 +6100,122 @@
6049
6100
  */
6050
6101
 
6051
6102
  /**
6052
- * Prepares the persona for the pipeline
6103
+ * Async version of Array.forEach
6053
6104
  *
6054
- * @see https://github.com/webgptorg/promptbook/discussions/22
6055
- * @public exported from `@promptbook/core`
6105
+ * @param array - Array to iterate over
6106
+ * @param options - Options for the function
6107
+ * @param callbackfunction - Function to call for each item
6108
+ * @public exported from `@promptbook/utils`
6109
+ * @deprecated [🪂] Use queues instead
6056
6110
  */
6057
- function preparePersona(personaDescription, tools, options) {
6111
+ function forEachAsync(array, options, callbackfunction) {
6058
6112
  return __awaiter(this, void 0, void 0, function () {
6059
- var _a, isVerbose, collection, preparePersonaExecutor, _b, _llms, llmTools, availableModels, availableModelNames, result, outputParameters, modelRequirementsRaw, modelRequirements, modelName, systemMessage, temperature;
6060
- var _c;
6061
- return __generator(this, function (_d) {
6062
- switch (_d.label) {
6113
+ var _a, maxParallelCount, index, runningTasks, tasks, _loop_1, _b, _c, item, e_1_1;
6114
+ var e_1, _d;
6115
+ return __generator(this, function (_e) {
6116
+ switch (_e.label) {
6063
6117
  case 0:
6064
- _a = options.isVerbose, isVerbose = _a === void 0 ? DEFAULT_IS_VERBOSE : _a;
6065
- if (tools === undefined || tools.llm === undefined) {
6066
- throw new MissingToolsError('LLM tools are required for preparing persona');
6067
- }
6068
- collection = createCollectionFromJson.apply(void 0, __spreadArray([], __read(PipelineCollection), false));
6069
- _b = createPipelineExecutor;
6070
- _c = {};
6071
- return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-persona.book.md')];
6118
+ _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? Infinity : _a;
6119
+ index = 0;
6120
+ runningTasks = [];
6121
+ tasks = [];
6122
+ _loop_1 = function (item) {
6123
+ var currentIndex, task;
6124
+ return __generator(this, function (_f) {
6125
+ switch (_f.label) {
6126
+ case 0:
6127
+ currentIndex = index++;
6128
+ task = callbackfunction(item, currentIndex, array);
6129
+ tasks.push(task);
6130
+ runningTasks.push(task);
6131
+ /* not await */ Promise.resolve(task).then(function () {
6132
+ runningTasks = runningTasks.filter(function (t) { return t !== task; });
6133
+ });
6134
+ if (!(maxParallelCount < runningTasks.length)) return [3 /*break*/, 2];
6135
+ return [4 /*yield*/, Promise.race(runningTasks)];
6136
+ case 1:
6137
+ _f.sent();
6138
+ _f.label = 2;
6139
+ case 2: return [2 /*return*/];
6140
+ }
6141
+ });
6142
+ };
6143
+ _e.label = 1;
6072
6144
  case 1:
6073
- preparePersonaExecutor = _b.apply(void 0, [(_c.pipeline = _d.sent(),
6074
- _c.tools = tools,
6075
- _c)]);
6076
- _llms = arrayableToArray(tools.llm);
6077
- llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools.apply(void 0, __spreadArray([], __read(_llms), false));
6078
- return [4 /*yield*/, llmTools.listModels()];
6145
+ _e.trys.push([1, 6, 7, 8]);
6146
+ _b = __values(array), _c = _b.next();
6147
+ _e.label = 2;
6079
6148
  case 2:
6080
- availableModels = _d.sent();
6081
- availableModelNames = availableModels
6082
- .filter(function (_a) {
6083
- var modelVariant = _a.modelVariant;
6084
- return modelVariant === 'CHAT';
6085
- })
6086
- .map(function (_a) {
6087
- var modelName = _a.modelName;
6088
- return modelName;
6089
- })
6090
- .join(',');
6091
- return [4 /*yield*/, preparePersonaExecutor({ availableModelNames: availableModelNames, personaDescription: personaDescription })];
6149
+ if (!!_c.done) return [3 /*break*/, 5];
6150
+ item = _c.value;
6151
+ return [5 /*yield**/, _loop_1(item)];
6152
+ case 3:
6153
+ _e.sent();
6154
+ _e.label = 4;
6155
+ case 4:
6156
+ _c = _b.next();
6157
+ return [3 /*break*/, 2];
6158
+ case 5: return [3 /*break*/, 8];
6159
+ case 6:
6160
+ e_1_1 = _e.sent();
6161
+ e_1 = { error: e_1_1 };
6162
+ return [3 /*break*/, 8];
6163
+ case 7:
6164
+ try {
6165
+ if (_c && !_c.done && (_d = _b.return)) _d.call(_b);
6166
+ }
6167
+ finally { if (e_1) throw e_1.error; }
6168
+ return [7 /*endfinally*/];
6169
+ case 8: return [4 /*yield*/, Promise.all(tasks)];
6170
+ case 9:
6171
+ _e.sent();
6172
+ return [2 /*return*/];
6173
+ }
6174
+ });
6175
+ });
6176
+ }
6177
+
6178
+ /**
6179
+ * Prepares the persona for the pipeline
6180
+ *
6181
+ * @see https://github.com/webgptorg/promptbook/discussions/22
6182
+ * @public exported from `@promptbook/core`
6183
+ */
6184
+ function preparePersona(personaDescription, tools, options) {
6185
+ return __awaiter(this, void 0, void 0, function () {
6186
+ var _a, isVerbose, collection, preparePersonaExecutor, _b, _llms, llmTools, availableModels, availableModelNames, result, outputParameters, modelRequirementsRaw, modelRequirements, modelName, systemMessage, temperature;
6187
+ var _c;
6188
+ return __generator(this, function (_d) {
6189
+ switch (_d.label) {
6190
+ case 0:
6191
+ _a = options.isVerbose, isVerbose = _a === void 0 ? DEFAULT_IS_VERBOSE : _a;
6192
+ if (tools === undefined || tools.llm === undefined) {
6193
+ throw new MissingToolsError('LLM tools are required for preparing persona');
6194
+ }
6195
+ collection = createCollectionFromJson.apply(void 0, __spreadArray([], __read(PipelineCollection), false));
6196
+ _b = createPipelineExecutor;
6197
+ _c = {};
6198
+ return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-persona.book.md')];
6199
+ case 1:
6200
+ preparePersonaExecutor = _b.apply(void 0, [(_c.pipeline = _d.sent(),
6201
+ _c.tools = tools,
6202
+ _c)]);
6203
+ _llms = arrayableToArray(tools.llm);
6204
+ llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools.apply(void 0, __spreadArray([], __read(_llms), false));
6205
+ return [4 /*yield*/, llmTools.listModels()];
6206
+ case 2:
6207
+ availableModels = _d.sent();
6208
+ availableModelNames = availableModels
6209
+ .filter(function (_a) {
6210
+ var modelVariant = _a.modelVariant;
6211
+ return modelVariant === 'CHAT';
6212
+ })
6213
+ .map(function (_a) {
6214
+ var modelName = _a.modelName;
6215
+ return modelName;
6216
+ })
6217
+ .join(',');
6218
+ return [4 /*yield*/, preparePersonaExecutor({ availableModelNames: availableModelNames, personaDescription: personaDescription })];
6092
6219
  case 3:
6093
6220
  result = _d.sent();
6094
6221
  assertsExecutionSuccessful(result);
@@ -6110,10 +6237,10 @@
6110
6237
  });
6111
6238
  }
6112
6239
  /**
6113
- * TODO: [🔃][main] !! If the persona was prepared with different version or different set of models, prepare it once again
6114
- * TODO: [🏢] !! Check validity of `modelName` in pipeline
6115
- * TODO: [🏢] !! Check validity of `systemMessage` in pipeline
6116
- * TODO: [🏢] !! Check validity of `temperature` in pipeline
6240
+ * TODO: [🔃][main] If the persona was prepared with different version or different set of models, prepare it once again
6241
+ * TODO: [🏢] Check validity of `modelName` in pipeline
6242
+ * TODO: [🏢] Check validity of `systemMessage` in pipeline
6243
+ * TODO: [🏢] Check validity of `temperature` in pipeline
6117
6244
  */
6118
6245
 
6119
6246
  /**
@@ -6323,6 +6450,9 @@
6323
6450
  }
6324
6451
  });
6325
6452
  }); };
6453
+ /**
6454
+ * TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
6455
+ */
6326
6456
 
6327
6457
  /**
6328
6458
  * @@@
@@ -6390,7 +6520,7 @@
6390
6520
  },
6391
6521
  }];
6392
6522
  case 2:
6393
- if (!(isValidFilePath(sourceContent) || /\.[a-z]{1,10}$/i.exec(sourceContent))) return [3 /*break*/, 4];
6523
+ if (!isValidFilePath(sourceContent)) return [3 /*break*/, 4];
6394
6524
  if (tools.fs === undefined) {
6395
6525
  throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
6396
6526
  // <- TODO: [🧠] What is the best error type here`
@@ -6405,7 +6535,7 @@
6405
6535
  return [4 /*yield*/, isFileExisting(filename_1, tools.fs)];
6406
6536
  case 3:
6407
6537
  if (!(_f.sent())) {
6408
- throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Can not make source handler for file which does not exist:\n\n File:\n ".concat(block(filename_1), "\n "); }));
6538
+ throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Can not make source handler for file which does not exist:\n\n File:\n ".concat(block(sourceContent), "\n\n Full file path:\n ").concat(block(filename_1), "\n "); }));
6409
6539
  }
6410
6540
  // TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
6411
6541
  return [2 /*return*/, {
@@ -6518,7 +6648,7 @@
6518
6648
  partialPieces = __spreadArray([], __read(partialPiecesUnchecked), false);
6519
6649
  return [2 /*return*/, "break"];
6520
6650
  }
6521
- console.warn(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge from source despite the scraper `".concat(scraper.metadata.className, "` supports the mime type \"").concat(sourceHandler.mimeType, "\".\n \n The source:\n > ").concat(block(knowledgeSource.sourceContent
6651
+ console.warn(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge from source despite the scraper `".concat(scraper.metadata.className, "` supports the mime type \"").concat(sourceHandler.mimeType, "\".\n\n The source:\n ").concat(block(knowledgeSource.sourceContent
6522
6652
  .split('\n')
6523
6653
  .map(function (line) { return "> ".concat(line); })
6524
6654
  .join('\n')), "\n\n ").concat(block($registeredScrapersMessage(scrapers)), "\n\n\n "); }));
@@ -6556,7 +6686,7 @@
6556
6686
  return [7 /*endfinally*/];
6557
6687
  case 9:
6558
6688
  if (partialPieces === null) {
6559
- throw new KnowledgeScrapeError(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge\n \n The source:\n > ".concat(block(knowledgeSource.sourceContent
6689
+ throw new KnowledgeScrapeError(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge\n\n The source:\n > ".concat(block(knowledgeSource.sourceContent
6560
6690
  .split('\n')
6561
6691
  .map(function (line) { return "> ".concat(line); })
6562
6692
  .join('\n')), "\n\n No scraper found for the mime type \"").concat(sourceHandler.mimeType, "\"\n\n ").concat(block($registeredScrapersMessage(scrapers)), "\n\n\n "); }));
@@ -6647,7 +6777,7 @@
6647
6777
  * TODO: [😂] Adding knowledge should be convert to async high-level abstractions, simmilar thing with expectations to sync high-level abstractions
6648
6778
  * TODO: [🧠] Add context to each task (if missing)
6649
6779
  * TODO: [🧠] What is better name `prepareTask` or `prepareTaskAndParameters`
6650
- * TODO: [♨][main] !!! Prepare index the examples and maybe tasks
6780
+ * TODO: [♨][main] !!3 Prepare index the examples and maybe tasks
6651
6781
  * TODO: Write tests for `preparePipeline`
6652
6782
  * TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
6653
6783
  * TODO: [🧊] In future one preparation can take data from previous preparation and save tokens and time
@@ -6657,6 +6787,8 @@
6657
6787
  /**
6658
6788
  * Prepare pipeline from string (markdown) format to JSON format
6659
6789
  *
6790
+ * @see https://github.com/webgptorg/promptbook/discussions/196
6791
+ *
6660
6792
  * Note: This function does not validate logic of the pipeline
6661
6793
  * Note: This function acts as part of compilation process
6662
6794
  * Note: When the pipeline is already prepared, it returns the same pipeline
@@ -6669,16 +6801,17 @@
6669
6801
  <- TODO: [🧠][🪑] `promptbookVersion` */
6670
6802
  knowledgeSources /*
6671
6803
  <- TODO: [🧊] `knowledgePieces` */, personas /*
6672
- <- TODO: [🧊] `preparations` */, _llms, llmTools, llmToolsWithUsage, currentPreparation, preparations, preparedPersonas, knowledgeSourcesPrepared, partialknowledgePiecesPrepared, knowledgePiecesPrepared, tasksPrepared /* TODO: parameters: parametersPrepared*/;
6804
+ <- TODO: [🧊] `preparations` */, sources, _llms, llmTools, llmToolsWithUsage, currentPreparation, preparations, title, collection, prepareTitleExecutor, _c, result, outputParameters, titleRaw, preparedPersonas, knowledgeSourcesPrepared, partialknowledgePiecesPrepared, knowledgePiecesPrepared, tasksPrepared /* TODO: parameters: parametersPrepared*/;
6805
+ var _d;
6673
6806
  var _this = this;
6674
- return __generator(this, function (_c) {
6675
- switch (_c.label) {
6807
+ return __generator(this, function (_e) {
6808
+ switch (_e.label) {
6676
6809
  case 0:
6677
6810
  if (isPipelinePrepared(pipeline)) {
6678
6811
  return [2 /*return*/, pipeline];
6679
6812
  }
6680
6813
  rootDirname = options.rootDirname, _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? DEFAULT_MAX_PARALLEL_COUNT : _a, _b = options.isVerbose, isVerbose = _b === void 0 ? DEFAULT_IS_VERBOSE : _b;
6681
- parameters = pipeline.parameters, tasks = pipeline.tasks, knowledgeSources = pipeline.knowledgeSources, personas = pipeline.personas;
6814
+ parameters = pipeline.parameters, tasks = pipeline.tasks, knowledgeSources = pipeline.knowledgeSources, personas = pipeline.personas, sources = pipeline.sources;
6682
6815
  if (tools === undefined || tools.llm === undefined) {
6683
6816
  throw new MissingToolsError('LLM tools are required for preparing the pipeline');
6684
6817
  }
@@ -6696,6 +6829,33 @@
6696
6829
  // <- TODO: [🧊]
6697
6830
  currentPreparation,
6698
6831
  ];
6832
+ title = pipeline.title;
6833
+ if (!(title === undefined || title === '' || title === DEFAULT_BOOK_TITLE)) return [3 /*break*/, 3];
6834
+ collection = createCollectionFromJson.apply(void 0, __spreadArray([], __read(PipelineCollection), false));
6835
+ _c = createPipelineExecutor;
6836
+ _d = {};
6837
+ return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-title.book.md')];
6838
+ case 1:
6839
+ prepareTitleExecutor = _c.apply(void 0, [(_d.pipeline = _e.sent(),
6840
+ _d.tools = tools,
6841
+ _d)]);
6842
+ return [4 /*yield*/, prepareTitleExecutor({
6843
+ book: sources.map(function (_a) {
6844
+ var content = _a.content;
6845
+ return content;
6846
+ }).join('\n\n'),
6847
+ })];
6848
+ case 2:
6849
+ result = _e.sent();
6850
+ assertsExecutionSuccessful(result);
6851
+ outputParameters = result.outputParameters;
6852
+ titleRaw = outputParameters.title;
6853
+ if (isVerbose) {
6854
+ console.info("The title is \"".concat(titleRaw, "\""));
6855
+ }
6856
+ title = titleRaw || DEFAULT_BOOK_TITLE;
6857
+ _e.label = 3;
6858
+ case 3:
6699
6859
  preparedPersonas = new Array(personas.length);
6700
6860
  return [4 /*yield*/, forEachAsync(personas, { maxParallelCount: maxParallelCount /* <- TODO: [🪂] When there are subtasks, this maximul limit can be broken */ }, function (persona, index) { return __awaiter(_this, void 0, void 0, function () {
6701
6861
  var modelRequirements, preparedPersona;
@@ -6714,12 +6874,12 @@
6714
6874
  }
6715
6875
  });
6716
6876
  }); })];
6717
- case 1:
6718
- _c.sent();
6877
+ case 4:
6878
+ _e.sent();
6719
6879
  knowledgeSourcesPrepared = knowledgeSources.map(function (source) { return (__assign(__assign({}, source), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
6720
6880
  return [4 /*yield*/, prepareKnowledgePieces(knowledgeSources /* <- TODO: [🧊] {knowledgeSources, knowledgePieces} */, __assign(__assign({}, tools), { llm: llmToolsWithUsage }), __assign(__assign({}, options), { rootDirname: rootDirname, maxParallelCount: maxParallelCount /* <- TODO: [🪂] */, isVerbose: isVerbose }))];
6721
- case 2:
6722
- partialknowledgePiecesPrepared = _c.sent();
6881
+ case 5:
6882
+ partialknowledgePiecesPrepared = _e.sent();
6723
6883
  knowledgePiecesPrepared = partialknowledgePiecesPrepared.map(function (piece) { return (__assign(__assign({}, piece), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
6724
6884
  return [4 /*yield*/, prepareTasks({
6725
6885
  parameters: parameters,
@@ -6730,8 +6890,8 @@
6730
6890
  maxParallelCount: maxParallelCount /* <- TODO: [🪂] */,
6731
6891
  isVerbose: isVerbose,
6732
6892
  })];
6733
- case 3:
6734
- tasksPrepared = (_c.sent()).tasksPrepared;
6893
+ case 6:
6894
+ tasksPrepared = (_e.sent()).tasksPrepared;
6735
6895
  // ----- /Tasks preparation -----
6736
6896
  // TODO: [😂] Use here all `AsyncHighLevelAbstraction`
6737
6897
  // Note: Count total usage
@@ -6742,7 +6902,7 @@
6742
6902
  order: ORDER_OF_PIPELINE_JSON,
6743
6903
  value: __assign(__assign({}, pipeline), {
6744
6904
  // <- TODO: Probbably deeply clone the pipeline because `$exportJson` freezes the subobjects
6745
- knowledgeSources: knowledgeSourcesPrepared, knowledgePieces: knowledgePiecesPrepared, tasks: __spreadArray([], __read(tasksPrepared), false),
6905
+ title: title, knowledgeSources: knowledgeSourcesPrepared, knowledgePieces: knowledgePiecesPrepared, tasks: __spreadArray([], __read(tasksPrepared), false),
6746
6906
  // <- TODO: [🪓] Here should be no need for spreading new array, just ` tasks: tasksPrepared`
6747
6907
  personas: preparedPersonas, preparations: __spreadArray([], __read(preparations), false) }),
6748
6908
  })];
@@ -6837,7 +6997,7 @@
6837
6997
  if (sourceContent === '') {
6838
6998
  throw new ParseError("Source is not defined");
6839
6999
  }
6840
- // TODO: [main] !!!! Following checks should be applied every link in the `sourceContent`
7000
+ // TODO: [main] !!4 Following checks should be applied every link in the `sourceContent`
6841
7001
  if (sourceContent.startsWith('http://')) {
6842
7002
  throw new ParseError("Source is not secure");
6843
7003
  }
@@ -7009,7 +7169,7 @@
7009
7169
  expectResultingParameterName();
7010
7170
  var parameter = $pipelineJson.parameters.find(function (param) { return param.name === $taskJson.resultingParameterName; });
7011
7171
  if (parameter === undefined) {
7012
- // TODO: !!!!!! Change to logic error for higher level abstractions to work
7172
+ // TODO: !!6 Change to logic error for higher level abstraction of chatbot to work
7013
7173
  throw new ParseError("Parameter `{".concat($taskJson.resultingParameterName, "}` is not defined so can not define example value of it"));
7014
7174
  }
7015
7175
  parameter.exampleValues = parameter.exampleValues || [];
@@ -7020,7 +7180,7 @@
7020
7180
  if (command.taskType === 'KNOWLEDGE') {
7021
7181
  knowledgeCommandParser.$applyToPipelineJson({
7022
7182
  type: 'KNOWLEDGE',
7023
- sourceContent: $taskJson.content, // <- TODO: [🐝][main] !!! Work with KNOWLEDGE which not referring to the source file or website, but its content itself
7183
+ sourceContent: $taskJson.content, // <- TODO: [🐝][main] !!3 Work with KNOWLEDGE which not referring to the source file or website, but its content itself
7024
7184
  }, $pipelineJson);
7025
7185
  $taskJson.isTask = false;
7026
7186
  return;
@@ -7899,20 +8059,24 @@
7899
8059
  */
7900
8060
  var GeneratorFormfactorDefinition = {
7901
8061
  name: 'GENERATOR',
7902
- description: "@@@",
8062
+ description: "Generates any kind (in HTML with possible scripts and css format) of content from input message",
7903
8063
  documentationUrl: "https://github.com/webgptorg/promptbook/discussions/184",
7904
8064
  pipelineInterface: {
7905
8065
  inputParameters: [
7906
- /* @@@ */
7907
8066
  {
7908
- name: 'nonce',
7909
- description: 'Just to prevent GENERATOR to be set as implicit formfactor',
8067
+ name: 'inputMessage',
8068
+ description: "Input message to be image made from",
7910
8069
  isInput: true,
7911
8070
  isOutput: false,
7912
8071
  },
7913
8072
  ],
7914
8073
  outputParameters: [
7915
- /* @@@ */
8074
+ {
8075
+ name: 'result',
8076
+ description: "Result in HTML to be shown to user",
8077
+ isInput: false,
8078
+ isOutput: true,
8079
+ },
7916
8080
  ],
7917
8081
  },
7918
8082
  };
@@ -7944,6 +8108,35 @@
7944
8108
  pipelineInterface: GENERIC_PIPELINE_INTERFACE,
7945
8109
  };
7946
8110
 
8111
+ /**
8112
+ * Image generator is form of app that generates image from input message
8113
+ *
8114
+ * @public exported from `@promptbook/core`
8115
+ */
8116
+ var ImageGeneratorFormfactorDefinition = {
8117
+ name: 'IMAGE_GENERATOR',
8118
+ description: "Generates prompt for image generation from input message",
8119
+ documentationUrl: "https://github.com/webgptorg/promptbook/discussions/184",
8120
+ pipelineInterface: {
8121
+ inputParameters: [
8122
+ {
8123
+ name: 'inputMessage',
8124
+ description: "Input message to be image made from",
8125
+ isInput: true,
8126
+ isOutput: false,
8127
+ },
8128
+ ],
8129
+ outputParameters: [
8130
+ {
8131
+ name: 'prompt',
8132
+ description: "Prompt to be used for image generation",
8133
+ isInput: false,
8134
+ isOutput: true,
8135
+ },
8136
+ ],
8137
+ },
8138
+ };
8139
+
7947
8140
  /**
7948
8141
  * Matcher is form of app that @@@
7949
8142
  *
@@ -8042,6 +8235,7 @@
8042
8235
  SheetsFormfactorDefinition,
8043
8236
  MatcherFormfactorDefinition,
8044
8237
  GeneratorFormfactorDefinition,
8238
+ ImageGeneratorFormfactorDefinition,
8045
8239
  ];
8046
8240
  /**
8047
8241
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -8428,7 +8622,7 @@
8428
8622
  * Note: `$` is used to indicate that this function mutates given `pipelineJson`
8429
8623
  */
8430
8624
  $applyToPipelineJson: function (command, $pipelineJson) {
8431
- // Note: [🍣] Do nothing, its application is implemented separately in `precompilePipeline`
8625
+ // Note: [🍣] Do nothing, its application is implemented separately in `parsePipeline`
8432
8626
  },
8433
8627
  /**
8434
8628
  * Apply the PARAMETER command to the `pipelineJson`
@@ -8436,7 +8630,7 @@
8436
8630
  * Note: `$` is used to indicate that this function mutates given `taskJson`
8437
8631
  */
8438
8632
  $applyToTaskJson: function (command, $taskJson, $pipelineJson) {
8439
- // Note: [🍣] Do nothing, its application is implemented separately in `precompilePipeline`
8633
+ // Note: [🍣] Do nothing, its application is implemented separately in `parsePipeline`
8440
8634
  },
8441
8635
  /**
8442
8636
  * Converts the PARAMETER command back to string
@@ -8931,7 +9125,7 @@
8931
9125
  instrumentCommandParser,
8932
9126
  personaCommandParser,
8933
9127
  foreachCommandParser,
8934
- boilerplateCommandParser, // <- TODO: !! Only in development, remove in production
9128
+ boilerplateCommandParser, // <- TODO: Only in development, remove in production
8935
9129
  // <- Note: [♓️][💩] This is the order of the commands in the pipeline, BUT its not used in parsing and before usage maybe it should be done better
8936
9130
  ];
8937
9131
  /**
@@ -9355,7 +9549,7 @@
9355
9549
  isOutput: true,
9356
9550
  exampleValues: ['Hello, I am a Pavol`s virtual avatar. How can I help you?'],
9357
9551
  });
9358
- // TODO: !!!!!! spaceTrim
9552
+ // TODO: Use spaceTrim in multiline strings
9359
9553
  $pipelineJson.tasks.push({
9360
9554
  taskType: 'PROMPT_TASK',
9361
9555
  name: 'create-an-answer',
@@ -9363,8 +9557,11 @@
9363
9557
  content: 'Write a response to the user message:\n\n**Question from user**\n\n> {userMessage}\n\n**Previous conversation**\n\n> {previousConversationSummary}',
9364
9558
  resultingParameterName: 'chatbotResponse',
9365
9559
  personaName: personaName,
9366
- dependentParameterNames: ['userMessage', 'previousConversationSummary' /* !!!!!!, 'knowledge'*/],
9367
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
9560
+ dependentParameterNames: [
9561
+ 'userMessage',
9562
+ 'previousConversationSummary' /* TODO: [🧠][📛], 'knowledge'*/,
9563
+ ],
9564
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
9368
9565
  }, {
9369
9566
  taskType: 'PROMPT_TASK',
9370
9567
  name: 'summarize-the-conversation',
@@ -9378,24 +9575,27 @@
9378
9575
  max: 10,
9379
9576
  },
9380
9577
  },
9381
- dependentParameterNames: ['userMessage', 'chatbotResponse' /* !!!!!!, 'knowledge'*/],
9382
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
9578
+ dependentParameterNames: ['userMessage', 'chatbotResponse' /* TODO: [🧠][📛], 'knowledge'*/],
9579
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
9383
9580
  }, {
9384
9581
  taskType: 'SIMPLE_TASK',
9385
9582
  name: 'title',
9386
9583
  title: 'Title',
9387
9584
  content: '{conversationSummary}',
9388
9585
  resultingParameterName: 'title',
9389
- dependentParameterNames: ['conversationSummary' /* !!!!!!, 'knowledge'*/],
9390
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
9586
+ dependentParameterNames: ['conversationSummary' /* TODO: [🧠][📛], 'knowledge'*/],
9587
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
9391
9588
  });
9392
9589
  },
9393
9590
  };
9591
+ /**
9592
+ * TODO: [🧠][📛] Should this be here?
9593
+ */
9394
9594
 
9395
9595
  /**
9396
9596
  * All high-level abstractions
9397
9597
  *
9398
- * @private internal index of `precompilePipeline` (= used for sync) and `preparePipeline` (= used for async)
9598
+ * @private internal index of `parsePipeline` (= used for sync) and `preparePipeline` (= used for async)
9399
9599
  */
9400
9600
  var HIGH_LEVEL_ABSTRACTIONS = [
9401
9601
  ImplicitFormfactorHla,
@@ -9529,7 +9729,7 @@
9529
9729
  return;
9530
9730
  }
9531
9731
  if (!section.startsWith('#')) {
9532
- section = "# ".concat(DEFAULT_TITLE, "\n\n").concat(section);
9732
+ section = "# ".concat(DEFAULT_BOOK_TITLE, "\n\n").concat(section);
9533
9733
  }
9534
9734
  sections.push(section);
9535
9735
  buffer = [];
@@ -9584,7 +9784,7 @@
9584
9784
  /**
9585
9785
  * Normalizes the markdown by flattening the structure
9586
9786
  *
9587
- * - It always have h1 - if there is no h1 in the markdown, it will be added "# Untitled"
9787
+ * - It always have h1 - if there is no h1 in the markdown, it will be added `DEFAULT_BOOK_TITLE`
9588
9788
  * - All other headings are normalized to h2
9589
9789
  *
9590
9790
  * @public exported from `@promptbook/markdown-utils`
@@ -9593,7 +9793,7 @@
9593
9793
  var e_1, _a;
9594
9794
  var sections = splitMarkdownIntoSections(markdown);
9595
9795
  if (sections.length === 0) {
9596
- return "# ".concat(DEFAULT_TITLE);
9796
+ return "# ".concat(DEFAULT_BOOK_TITLE);
9597
9797
  }
9598
9798
  var flattenedMarkdown = '';
9599
9799
  var parsedSections = sections.map(parseMarkdownSection);
@@ -9604,7 +9804,7 @@
9604
9804
  }
9605
9805
  else {
9606
9806
  parsedSections.unshift(firstSection);
9607
- flattenedMarkdown += "# ".concat(DEFAULT_TITLE) + "\n\n"; // <- [🧠] Maybe 3 new lines?
9807
+ flattenedMarkdown += "# ".concat(DEFAULT_BOOK_TITLE) + "\n\n"; // <- [🧠] Maybe 3 new lines?
9608
9808
  }
9609
9809
  try {
9610
9810
  for (var parsedSections_1 = __values(parsedSections), parsedSections_1_1 = parsedSections_1.next(); !parsedSections_1_1.done; parsedSections_1_1 = parsedSections_1.next()) {
@@ -9631,13 +9831,13 @@
9631
9831
  */
9632
9832
 
9633
9833
  /**
9634
- * Removes HTML or Markdown comments from a string.
9834
+ * Removes Markdown (or HTML) comments
9635
9835
  *
9636
9836
  * @param {string} content - The string to remove comments from.
9637
9837
  * @returns {string} The input string with all comments removed.
9638
9838
  * @public exported from `@promptbook/markdown-utils`
9639
9839
  */
9640
- function removeContentComments(content) {
9840
+ function removeMarkdownComments(content) {
9641
9841
  return spaceTrim.spaceTrim(content.replace(/<!--(.*?)-->/gs, ''));
9642
9842
  }
9643
9843
 
@@ -9646,7 +9846,7 @@
9646
9846
  *
9647
9847
  * Note: There are 3 similar functions:
9648
9848
  * - `compilePipeline` **(preferred)** - which propperly compiles the promptbook and use embedding for external knowledge
9649
- * - `precompilePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
9849
+ * - `parsePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
9650
9850
  * - `preparePipeline` - just one step in the compilation process
9651
9851
  *
9652
9852
  * Note: This function does not validate logic of the pipeline only the parsing
@@ -9657,10 +9857,10 @@
9657
9857
  * @throws {ParseError} if the promptbook string is not valid
9658
9858
  * @public exported from `@promptbook/core`
9659
9859
  */
9660
- function precompilePipeline(pipelineString) {
9860
+ function parsePipeline(pipelineString) {
9661
9861
  var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
9662
9862
  var $pipelineJson = {
9663
- title: DEFAULT_TITLE,
9863
+ title: DEFAULT_BOOK_TITLE,
9664
9864
  parameters: [],
9665
9865
  tasks: [],
9666
9866
  knowledgeSources: [],
@@ -9671,7 +9871,7 @@
9671
9871
  {
9672
9872
  type: 'BOOK',
9673
9873
  path: null,
9674
- // <- TODO: !!!!!! Pass here path of the file
9874
+ // <- TODO: !!6 Pass here path of the file
9675
9875
  content: pipelineString,
9676
9876
  },
9677
9877
  ],
@@ -9689,18 +9889,44 @@
9689
9889
  }
9690
9890
  // =============================================================
9691
9891
  // Note: 1️⃣ Parsing of the markdown into object
9892
+ // ==============
9893
+ // Note: 1️⃣◽1️⃣ Remove #!shebang and comments
9692
9894
  if (pipelineString.startsWith('#!')) {
9693
9895
  var _g = __read(pipelineString.split('\n')), shebangLine_1 = _g[0], restLines = _g.slice(1);
9694
9896
  if (!(shebangLine_1 || '').includes('ptbk')) {
9695
9897
  throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n It seems that you try to parse a book file which has non-standard shebang line for book files:\n Shebang line must contain 'ptbk'\n\n You have:\n ".concat(block(shebangLine_1 || '(empty line)'), "\n\n It should look like this:\n #!/usr/bin/env ptbk\n\n ").concat(block(getPipelineIdentification()), "\n "); }));
9696
9898
  }
9697
- pipelineString = restLines.join('\n');
9698
- }
9699
- pipelineString = removeContentComments(pipelineString);
9899
+ pipelineString = validatePipelineString(restLines.join('\n'));
9900
+ }
9901
+ pipelineString = removeMarkdownComments(pipelineString);
9902
+ pipelineString = spaceTrim.spaceTrim(pipelineString);
9903
+ // <- TODO: [😧] `spaceTrim` should preserve discriminated type *(or at lease `PipelineString`)*
9904
+ // ==============
9905
+ // Note: 1️⃣◽2️⃣ Process flat pipeline
9906
+ var isMarkdownBeginningWithHeadline = pipelineString.startsWith('# ');
9907
+ var isLastLineReturnStatement = pipelineString.split('\n').pop().split('`').join('').startsWith('->');
9908
+ // TODO: Also (double)check
9909
+ // > const usedCommands
9910
+ // > const isBlocksUsed
9911
+ // > const returnStatementCount
9912
+ var isFlatPipeline = !isMarkdownBeginningWithHeadline && isLastLineReturnStatement;
9913
+ // console.log({ isMarkdownBeginningWithHeadline, isLastLineReturnStatement, isFlatPipeline });
9914
+ if (isFlatPipeline) {
9915
+ var pipelineStringLines = pipelineString.split('\n');
9916
+ var returnStatement_1 = pipelineStringLines.pop();
9917
+ var prompt_1 = spaceTrim.spaceTrim(pipelineStringLines.join('\n'));
9918
+ pipelineString = validatePipelineString(spaceTrim.spaceTrim(function (block) { return "\n # ".concat(DEFAULT_BOOK_TITLE, "\n\n ## Prompt\n\n ```\n ").concat(block(prompt_1), "\n ```\n\n ").concat(returnStatement_1, "\n "); }));
9919
+ // <- TODO: Maybe use book` notation
9920
+ // console.log(pipelineString);
9921
+ }
9922
+ // ==============
9923
+ // Note: 1️⃣◽3️⃣ Parse the markdown
9700
9924
  pipelineString = flattenMarkdown(pipelineString) /* <- Note: [🥞] */;
9701
9925
  pipelineString = pipelineString.replaceAll(/`\{(?<parameterName>[a-z0-9_]+)\}`/gi, '{$<parameterName>}');
9702
9926
  pipelineString = pipelineString.replaceAll(/`->\s+\{(?<parameterName>[a-z0-9_]+)\}`/gi, '-> {$<parameterName>}');
9703
9927
  var _h = __read(splitMarkdownIntoSections(pipelineString).map(parseMarkdownSection)), pipelineHead = _h[0], pipelineSections = _h.slice(1); /* <- Note: [🥞] */
9928
+ // ==============
9929
+ // Note: 1️⃣◽4️⃣ Check markdown structure
9704
9930
  if (pipelineHead === undefined) {
9705
9931
  throw new UnexpectedError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline head is not defined\n\n ".concat(block(getPipelineIdentification()), "\n\n This should never happen, because the pipeline already flattened\n "); }));
9706
9932
  }
@@ -10084,14 +10310,14 @@
10084
10310
  // =============================================================
10085
10311
  return exportJson({
10086
10312
  name: 'pipelineJson',
10087
- message: "Result of `precompilePipeline`",
10313
+ message: "Result of `parsePipeline`",
10088
10314
  order: ORDER_OF_PIPELINE_JSON,
10089
10315
  value: __assign({ formfactorName: 'GENERIC' }, $pipelineJson),
10090
10316
  });
10091
10317
  }
10092
10318
  /**
10093
10319
  * TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
10094
- * TODO: [main] !!!! Warn if used only sync version
10320
+ * TODO: [main] !!4 Warn if used only sync version
10095
10321
  * TODO: [🚞] Report here line/column of error
10096
10322
  * TODO: Use spaceTrim more effectively
10097
10323
  * TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
@@ -10104,10 +10330,7 @@
10104
10330
  /**
10105
10331
  * Compile pipeline from string (markdown) format to JSON format
10106
10332
  *
10107
- * Note: There are 3 similar functions:
10108
- * - `compilePipeline` **(preferred)** - which propperly compiles the promptbook and use embedding for external knowledge
10109
- * - `precompilePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
10110
- * - `preparePipeline` - just one step in the compilation process
10333
+ * @see https://github.com/webgptorg/promptbook/discussions/196
10111
10334
  *
10112
10335
  * Note: This function does not validate logic of the pipeline only the parsing
10113
10336
  * Note: This function acts as compilation process
@@ -10125,7 +10348,7 @@
10125
10348
  return __generator(this, function (_a) {
10126
10349
  switch (_a.label) {
10127
10350
  case 0:
10128
- pipelineJson = precompilePipeline(pipelineString);
10351
+ pipelineJson = parsePipeline(pipelineString);
10129
10352
  if (!(tools !== undefined && tools.llm !== undefined)) return [3 /*break*/, 2];
10130
10353
  return [4 /*yield*/, preparePipeline(pipelineJson, tools, options || {
10131
10354
  rootDirname: null,
@@ -10134,7 +10357,7 @@
10134
10357
  pipelineJson = _a.sent();
10135
10358
  _a.label = 2;
10136
10359
  case 2:
10137
- // Note: No need to use `$exportJson` because `precompilePipeline` and `preparePipeline` already do that
10360
+ // Note: No need to use `$exportJson` because `parsePipeline` and `preparePipeline` already do that
10138
10361
  return [2 /*return*/, pipelineJson];
10139
10362
  }
10140
10363
  });
@@ -10498,23 +10721,95 @@
10498
10721
  */
10499
10722
 
10500
10723
  /**
10501
- * Extracts code block from markdown.
10502
- *
10503
- * - When there are multiple or no code blocks the function throws a `ParseError`
10724
+ * Creates a Mermaid graph based on the promptbook
10504
10725
  *
10505
- * Note: There are multiple simmilar function:
10506
- * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
10507
- * - `extractJsonBlock` extracts exactly one valid JSON code block
10508
- * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
10509
- * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
10726
+ * Note: The result is not wrapped in a Markdown code block
10510
10727
  *
10511
- * @public exported from `@promptbook/markdown-utils`
10512
- * @throws {ParseError} if there is not exactly one code block in the markdown
10728
+ * @public exported from `@promptbook/utils`
10513
10729
  */
10514
- function extractBlock(markdown) {
10515
- var content = extractOneBlockFromMarkdown(markdown).content;
10516
- return content;
10730
+ function renderPromptbookMermaid(pipelineJson, options) {
10731
+ var _a = (options || {}).linkTask, linkTask = _a === void 0 ? function () { return null; } : _a;
10732
+ var parameterNameToTaskName = function (parameterName) {
10733
+ var parameter = pipelineJson.parameters.find(function (parameter) { return parameter.name === parameterName; });
10734
+ if (!parameter) {
10735
+ throw new UnexpectedError("Could not find {".concat(parameterName, "}"));
10736
+ // <- TODO: !!6 This causes problems when {knowledge} and other reserved parameters are used
10737
+ }
10738
+ if (parameter.isInput) {
10739
+ return 'input';
10740
+ }
10741
+ var task = pipelineJson.tasks.find(function (task) { return task.resultingParameterName === parameterName; });
10742
+ if (!task) {
10743
+ throw new Error("Could not find task for {".concat(parameterName, "}"));
10744
+ }
10745
+ return task.name || normalizeTo_camelCase('task-' + titleToName(task.title));
10746
+ };
10747
+ var promptbookMermaid = spaceTrim.spaceTrim(function (block) { return "\n\n %% \uD83D\uDD2E Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually\n\n flowchart LR\n subgraph \"".concat(pipelineJson.title, "\"\n\n direction TB\n\n input((Input)):::input\n ").concat(block(pipelineJson.tasks
10748
+ .flatMap(function (_a) {
10749
+ var title = _a.title, dependentParameterNames = _a.dependentParameterNames, resultingParameterName = _a.resultingParameterName;
10750
+ return __spreadArray([
10751
+ "".concat(parameterNameToTaskName(resultingParameterName), "(\"").concat(title, "\")")
10752
+ ], __read(dependentParameterNames.map(function (dependentParameterName) {
10753
+ return "".concat(parameterNameToTaskName(dependentParameterName), "--\"{").concat(dependentParameterName, "}\"-->").concat(parameterNameToTaskName(resultingParameterName));
10754
+ })), false);
10755
+ })
10756
+ .join('\n')), "\n\n ").concat(block(pipelineJson.parameters
10757
+ .filter(function (_a) {
10758
+ var isOutput = _a.isOutput;
10759
+ return isOutput;
10760
+ })
10761
+ .map(function (_a) {
10762
+ var name = _a.name;
10763
+ return "".concat(parameterNameToTaskName(name), "--\"{").concat(name, "}\"-->output");
10764
+ })
10765
+ .join('\n')), "\n output((Output)):::output\n\n ").concat(block(pipelineJson.tasks
10766
+ .map(function (task) {
10767
+ var link = linkTask(task);
10768
+ if (link === null) {
10769
+ return '';
10770
+ }
10771
+ var href = link.href, title = link.title;
10772
+ var taskName = parameterNameToTaskName(task.resultingParameterName);
10773
+ return "click ".concat(taskName, " href \"").concat(href, "\" \"").concat(title, "\";");
10774
+ })
10775
+ .filter(function (line) { return line !== ''; })
10776
+ .join('\n')), "\n\n classDef input color: grey;\n classDef output color: grey;\n\n end;\n\n "); });
10777
+ return promptbookMermaid;
10517
10778
  }
10779
+ /**
10780
+ * TODO: [🧠] FOREACH in mermaid graph
10781
+ * TODO: [🧠] Knowledge in mermaid graph
10782
+ * TODO: [🧠] Personas in mermaid graph
10783
+ * TODO: Maybe use some Mermaid package instead of string templating
10784
+ * TODO: [🕌] When more than 2 functionalities, split into separate functions
10785
+ */
10786
+
10787
+ /**
10788
+ * Detects if the code is running in a browser environment in main thread (Not in a web worker)
10789
+ *
10790
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10791
+ *
10792
+ * @public exported from `@promptbook/utils`
10793
+ */
10794
+ var $isRunningInBrowser = new Function("\n try {\n return this === window;\n } catch (e) {\n return false;\n }\n");
10795
+
10796
+ /**
10797
+ * Detects if the code is running in jest environment
10798
+ *
10799
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10800
+ *
10801
+ * @public exported from `@promptbook/utils`
10802
+ */
10803
+ var $isRunningInJest = new Function("\n try {\n return process.env.JEST_WORKER_ID !== undefined;\n } catch (e) {\n return false;\n }\n");
10804
+
10805
+ /**
10806
+ * Detects if the code is running in a web worker
10807
+ *
10808
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10809
+ *
10810
+ * @public exported from `@promptbook/utils`
10811
+ */
10812
+ var $isRunningInWebWorker = new Function("\n try {\n if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {\n return true;\n } else {\n return false;\n }\n } catch (e) {\n return false;\n }\n");
10518
10813
 
10519
10814
  /**
10520
10815
  * Makes first letter of a string uppercase
@@ -10525,6 +10820,21 @@
10525
10820
  return word.substring(0, 1).toLowerCase() + word.substring(1);
10526
10821
  }
10527
10822
 
10823
+ /**
10824
+ * Parses keywords from a string
10825
+ *
10826
+ * @param {string} input
10827
+ * @returns {Set} of keywords without diacritics in lowercase
10828
+ * @public exported from `@promptbook/utils`
10829
+ */
10830
+ function parseKeywordsFromString(input) {
10831
+ var keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
10832
+ .toLowerCase()
10833
+ .split(/[^a-z0-9]+/gs)
10834
+ .filter(function (value) { return value; });
10835
+ return new Set(keywords);
10836
+ }
10837
+
10528
10838
  /**
10529
10839
  * @@@
10530
10840
  *
@@ -10578,21 +10888,6 @@
10578
10888
  return sentence.replace(/\s+/gs, ' ').trim();
10579
10889
  }
10580
10890
 
10581
- /**
10582
- * Parses keywords from a string
10583
- *
10584
- * @param {string} input
10585
- * @returns {Set} of keywords without diacritics in lowercase
10586
- * @public exported from `@promptbook/utils`
10587
- */
10588
- function parseKeywordsFromString(input) {
10589
- var keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
10590
- .toLowerCase()
10591
- .split(/[^a-z0-9]+/gs)
10592
- .filter(function (value) { return value; });
10593
- return new Set(keywords);
10594
- }
10595
-
10596
10891
  /**
10597
10892
  * Function trimCodeBlock will trim starting and ending code block from the string if it is present.
10598
10893
  *
@@ -10699,6 +10994,25 @@
10699
10994
  * TODO: [🧠] Should this also unwrap the (parenthesis)
10700
10995
  */
10701
10996
 
10997
+ /**
10998
+ * Extracts code block from markdown.
10999
+ *
11000
+ * - When there are multiple or no code blocks the function throws a `ParseError`
11001
+ *
11002
+ * Note: There are multiple simmilar function:
11003
+ * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
11004
+ * - `extractJsonBlock` extracts exactly one valid JSON code block
11005
+ * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
11006
+ * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
11007
+ *
11008
+ * @public exported from `@promptbook/markdown-utils`
11009
+ * @throws {ParseError} if there is not exactly one code block in the markdown
11010
+ */
11011
+ function extractBlock(markdown) {
11012
+ var content = extractOneBlockFromMarkdown(markdown).content;
11013
+ return content;
11014
+ }
11015
+
10702
11016
  /**
10703
11017
  * Does nothing, but preserves the function in the bundle
10704
11018
  * Compiler is tricked into thinking the function is used
@@ -10733,37 +11047,10 @@
10733
11047
  }); })();
10734
11048
  }
10735
11049
  /**
10736
- * TODO: !! [1] This maybe does memory leak
11050
+ * TODO: Probbably remove in favour of `keepImported`
11051
+ * TODO: [1] This maybe does memory leak
10737
11052
  */
10738
11053
 
10739
- /**
10740
- * Converts anything to string that can be used for debugging and logging
10741
- *
10742
- * @param value String value for logging
10743
- * @private internal util
10744
- */
10745
- function unknownToString(value) {
10746
- if (value === undefined) {
10747
- return 'undefined';
10748
- }
10749
- else if (value === null) {
10750
- return 'null';
10751
- }
10752
- else if (['number', 'string', 'boolean'].includes(typeof value)) {
10753
- return typeof value + ' ' + value.toString();
10754
- }
10755
- else if (typeof value === 'object' && Array.isArray(value)) {
10756
- return 'array containing [' + value.map(function (item) { return unknownToString(item); }).join(', ') + ']';
10757
- }
10758
- else if (typeof value === 'object') {
10759
- // TODO: Maybe serialize the object
10760
- return 'object';
10761
- }
10762
- else {
10763
- return 'unknown (Search in promptbook code for [🔹])';
10764
- }
10765
- }
10766
-
10767
11054
  /**
10768
11055
  * ScriptExecutionTools for JavaScript implemented via eval
10769
11056
  *
@@ -10893,7 +11180,7 @@
10893
11180
  case 2:
10894
11181
  result = _a.sent();
10895
11182
  if (typeof result !== 'string') {
10896
- throw new PipelineExecutionError("Script must return a string, but returned ".concat(unknownToString(result)));
11183
+ throw new PipelineExecutionError("Script must return a string, but returned ".concat(valueToString(result)));
10897
11184
  }
10898
11185
  return [3 /*break*/, 4];
10899
11186
  case 3:
@@ -10920,7 +11207,7 @@
10920
11207
  throw error_1;
10921
11208
  case 4:
10922
11209
  if (typeof result !== 'string') {
10923
- throw new PipelineExecutionError("Script must return a string, but ".concat(unknownToString(result)));
11210
+ throw new PipelineExecutionError("Script must return a string, but ".concat(valueToString(result)));
10924
11211
  }
10925
11212
  return [2 /*return*/, result];
10926
11213
  }
@@ -10960,9 +11247,11 @@
10960
11247
  throw new EnvironmentMismatchError('Function `$getExecutionToolsForNode` works only in Node.js environment');
10961
11248
  }
10962
11249
  fs = $provideFilesystemForNode();
10963
- llm = $provideLlmToolsFromEnv(options);
10964
- return [4 /*yield*/, $provideExecutablesForNode(options)];
11250
+ return [4 /*yield*/, $provideLlmToolsFromEnv(options)];
10965
11251
  case 1:
11252
+ llm = _b.sent();
11253
+ return [4 /*yield*/, $provideExecutablesForNode(options)];
11254
+ case 2:
10966
11255
  executables = _b.sent();
10967
11256
  _a = {
10968
11257
  llm: llm,
@@ -10970,7 +11259,7 @@
10970
11259
  executables: executables
10971
11260
  };
10972
11261
  return [4 /*yield*/, $provideScrapersForNode({ fs: fs, llm: llm, executables: executables }, options)];
10973
- case 2:
11262
+ case 3:
10974
11263
  tools = (_a.scrapers = _b.sent(),
10975
11264
  _a.script = [new JavascriptExecutionTools(options)],
10976
11265
  _a);
@@ -11193,7 +11482,7 @@
11193
11482
  */
11194
11483
  function createCollectionFromDirectory(path$1, tools, options) {
11195
11484
  return __awaiter(this, void 0, void 0, function () {
11196
- var makedLibraryFilePath, _a, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, collection;
11485
+ var madeLibraryFilePath, _a, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, rootUrl, collection;
11197
11486
  var _this = this;
11198
11487
  return __generator(this, function (_f) {
11199
11488
  switch (_f.label) {
@@ -11208,18 +11497,18 @@
11208
11497
  throw new EnvironmentMismatchError('Can not create collection without filesystem tools');
11209
11498
  // <- TODO: [🧠] What is the best error type here`
11210
11499
  }
11211
- makedLibraryFilePath = path.join(path$1, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
11500
+ madeLibraryFilePath = path.join(path$1, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
11212
11501
  // <- TODO: [🦒] Allow to override (pass different value into the function)
11213
11502
  , ".json"));
11214
- return [4 /*yield*/, isFileExisting(makedLibraryFilePath, tools.fs)];
11503
+ return [4 /*yield*/, isFileExisting(madeLibraryFilePath, tools.fs)];
11215
11504
  case 3:
11216
11505
  if (!(_f.sent())) ;
11217
11506
  else {
11218
- colors__default["default"].green("(In future, not implemented yet) Using your compiled pipeline collection ".concat(makedLibraryFilePath));
11219
- // TODO: !! Implement;
11507
+ colors__default["default"].green("(In future, not implemented yet) Using your compiled pipeline collection ".concat(madeLibraryFilePath));
11508
+ // TODO: Implement;
11220
11509
  // TODO: [🌗]
11221
11510
  }
11222
- _a = options || {}, _b = _a.isRecursive, isRecursive = _b === void 0 ? true : _b, _c = _a.isVerbose, isVerbose = _c === void 0 ? DEFAULT_IS_VERBOSE : _c, _d = _a.isLazyLoaded, isLazyLoaded = _d === void 0 ? false : _d, _e = _a.isCrashedOnError, isCrashedOnError = _e === void 0 ? true : _e;
11511
+ _a = options || {}, _b = _a.isRecursive, isRecursive = _b === void 0 ? true : _b, _c = _a.isVerbose, isVerbose = _c === void 0 ? DEFAULT_IS_VERBOSE : _c, _d = _a.isLazyLoaded, isLazyLoaded = _d === void 0 ? false : _d, _e = _a.isCrashedOnError, isCrashedOnError = _e === void 0 ? true : _e, rootUrl = _a.rootUrl;
11223
11512
  collection = createCollectionFromPromise(function () { return __awaiter(_this, void 0, void 0, function () {
11224
11513
  var fileNames, collection, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1;
11225
11514
  var e_1, _a;
@@ -11245,34 +11534,35 @@
11245
11534
  });
11246
11535
  collection = new Map();
11247
11536
  _loop_1 = function (fileName) {
11248
- var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, existing, error_1, wrappedErrorMessage;
11249
- return __generator(this, function (_e) {
11250
- switch (_e.label) {
11537
+ var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, _e, pipelineUrl, existing, error_1, wrappedErrorMessage;
11538
+ return __generator(this, function (_f) {
11539
+ switch (_f.label) {
11251
11540
  case 0:
11252
11541
  sourceFile = './' + fileName.split('\\').join('/');
11253
11542
  rootDirname = path.dirname(sourceFile).split('\\').join('/');
11254
- _e.label = 1;
11543
+ _f.label = 1;
11255
11544
  case 1:
11256
- _e.trys.push([1, 8, , 9]);
11545
+ _f.trys.push([1, 8, , 9]);
11257
11546
  pipeline = null;
11258
11547
  if (!fileName.endsWith('.book.md')) return [3 /*break*/, 4];
11548
+ _c = validatePipelineString;
11259
11549
  return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
11260
11550
  case 2:
11261
- pipelineString = (_e.sent());
11551
+ pipelineString = _c.apply(void 0, [_f.sent()]);
11262
11552
  return [4 /*yield*/, compilePipeline(pipelineString, tools, {
11263
11553
  rootDirname: rootDirname,
11264
11554
  })];
11265
11555
  case 3:
11266
- pipeline = _e.sent();
11556
+ pipeline = _f.sent();
11267
11557
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
11268
11558
  return [3 /*break*/, 7];
11269
11559
  case 4:
11270
11560
  if (!fileName.endsWith('.book.json')) return [3 /*break*/, 6];
11271
- _d = (_c = JSON).parse;
11561
+ _e = (_d = JSON).parse;
11272
11562
  return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
11273
11563
  case 5:
11274
11564
  // TODO: Handle non-valid JSON files
11275
- pipeline = _d.apply(_c, [_e.sent()]);
11565
+ pipeline = _e.apply(_d, [_f.sent()]);
11276
11566
  // TODO: [🌗]
11277
11567
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
11278
11568
  return [3 /*break*/, 7];
@@ -11280,10 +11570,24 @@
11280
11570
  if (isVerbose) {
11281
11571
  console.info(colors__default["default"].gray("Skipped file ".concat(fileName.split('\\').join('/'), " \u2013\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060 Not a book")));
11282
11572
  }
11283
- _e.label = 7;
11573
+ _f.label = 7;
11284
11574
  case 7:
11285
11575
  // ---
11286
11576
  if (pipeline !== null) {
11577
+ if (rootUrl !== undefined) {
11578
+ if (pipeline.pipelineUrl === undefined) {
11579
+ pipelineUrl = rootUrl + '/' + fileName.split('\\').join('/');
11580
+ if (isVerbose) {
11581
+ console.info(colors__default["default"].yellow("Implicitly set pipeline URL to ".concat(pipelineUrl, " from ").concat(fileName
11582
+ .split('\\')
11583
+ .join('/'))));
11584
+ }
11585
+ pipeline = __assign(__assign({}, pipeline), { pipelineUrl: pipelineUrl });
11586
+ }
11587
+ else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
11588
+ throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is not a child of the root URL ").concat(rootUrl, " \uD83C\uDF4F\n\n File:\n ").concat(sourceFile || 'Unknown', "\n\n ")));
11589
+ }
11590
+ }
11287
11591
  // TODO: [👠] DRY
11288
11592
  if (pipeline.pipelineUrl === undefined) {
11289
11593
  if (isVerbose) {
@@ -11315,17 +11619,17 @@
11315
11619
  }
11316
11620
  else {
11317
11621
  existing = collection.get(pipeline.pipelineUrl);
11318
- throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL \"".concat(pipeline.pipelineUrl, "\" is already in the collection \uD83C\uDF4F\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
11622
+ throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is already in the collection \uD83C\uDF4F\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
11319
11623
  }
11320
11624
  }
11321
11625
  }
11322
11626
  return [3 /*break*/, 9];
11323
11627
  case 8:
11324
- error_1 = _e.sent();
11628
+ error_1 = _f.sent();
11325
11629
  if (!(error_1 instanceof Error)) {
11326
11630
  throw error_1;
11327
11631
  }
11328
- wrappedErrorMessage = spaceTrim__default["default"](function (block) { return "\n ".concat(error_1.name, " in pipeline ").concat(fileName.split('\\').join('/'), "\u2060:\n\n Original error message:\n ").concat(block(error_1.message), "\n\n Original stack trace:\n ").concat(block(error_1.stack || ''), "\n\n ---\n\n "); }) + '\n';
11632
+ wrappedErrorMessage = spaceTrim__default["default"](function (block) { return "\n ".concat(error_1.name, " in pipeline ").concat(fileName.split('\\').join('/'), "\u2060:\n\n Original error message:\n ").concat(block(error_1.message), "\n\n Original stack trace:\n ").concat(block(error_1.stack || ''), "\n\n ---\n\n "); }) + '\n';
11329
11633
  if (isCrashedOnError) {
11330
11634
  throw new CollectionError(wrappedErrorMessage);
11331
11635
  }
@@ -11460,6 +11764,7 @@
11460
11764
  // <- TODO: [🧟‍♂️] Unite path to promptbook collection argument
11461
11765
  'Path to promptbook collection directory', DEFAULT_BOOKS_DIRNAME);
11462
11766
  makeCommand.option('--project-name', "Name of the project for whom collection is", 'Untitled Promptbook project');
11767
+ makeCommand.option('--root-url <url>', "Root URL of all pipelines to make", undefined);
11463
11768
  makeCommand.option('-f, --format <format>', spaceTrim__default["default"]("\n Output format of builded collection \"javascript\", \"typescript\" or \"json\"\n\n Note: You can use multiple formats separated by comma\n "), 'javascript' /* <- Note: [🏳‍🌈] */);
11464
11769
  makeCommand.option('--no-validation', "Do not validate logic of pipelines in collection", true);
11465
11770
  makeCommand.option('--validation', "Types of validations separated by comma (options \"logic\",\"imports\")", 'logic,imports');
@@ -11468,7 +11773,7 @@
11468
11773
  makeCommand.option('-o, --output <path>', spaceTrim__default["default"]("\n Where to save the builded collection\n\n Note: If you keep it \"".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, "\" it will be saved in the root of the promptbook directory\n If you set it to a path, it will be saved in that path\n BUT you can use only one format and set correct extension\n ")), DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME);
11469
11774
  makeCommand.option('-fn, --function-name <functionName>', spaceTrim__default["default"]("\n Name of the function to get pipeline collection\n\n Note: This can be used only with \"javascript\" or \"typescript\" format\n\n "), DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME);
11470
11775
  makeCommand.action(function (path$1, _a) {
11471
- var projectName = _a.projectName, format = _a.format, functionName = _a.functionName, validation = _a.validation, isCacheReloaded = _a.reload, isVerbose = _a.verbose, output = _a.output;
11776
+ var projectName = _a.projectName, rootUrl = _a.rootUrl, format = _a.format, functionName = _a.functionName, validation = _a.validation, isCacheReloaded = _a.reload, isVerbose = _a.verbose, output = _a.output;
11472
11777
  return __awaiter(_this, void 0, void 0, function () {
11473
11778
  var formats, validations, prepareAndScrapeOptions, fs, llm, executables, tools, collection, pipelinesUrls, validations_1, validations_1_1, validation_1, pipelinesUrls_1, pipelinesUrls_1_1, pipelineUrl, pipeline, e_1_1, e_2_1, collectionJson, collectionJsonString, collectionJsonItems, saveFile;
11474
11779
  var _b, e_2, _c, e_1, _d;
@@ -11480,6 +11785,11 @@
11480
11785
  console.error(colors__default["default"].red("Function name \"".concat(functionName, "\" is not valid javascript name")));
11481
11786
  return [2 /*return*/, process.exit(1)];
11482
11787
  }
11788
+ if (rootUrl !== undefined &&
11789
+ !isValidUrl(rootUrl) /* <- Note: Not using `isValidPipelineUrl` because this is root url not book url */) {
11790
+ console.error(colors__default["default"].red("Root URL ".concat(rootUrl, " is not valid URL")));
11791
+ return [2 /*return*/, process.exit(1)];
11792
+ }
11483
11793
  formats = (format || '')
11484
11794
  .split(',')
11485
11795
  .map(function (_) { return _.trim(); })
@@ -11497,16 +11807,18 @@
11497
11807
  isCacheReloaded: isCacheReloaded,
11498
11808
  };
11499
11809
  fs = $provideFilesystemForNode(prepareAndScrapeOptions);
11500
- llm = $provideLlmToolsForCli(prepareAndScrapeOptions);
11501
- return [4 /*yield*/, $provideExecutablesForNode(prepareAndScrapeOptions)];
11810
+ return [4 /*yield*/, $provideLlmToolsForWizzardOrCli(prepareAndScrapeOptions)];
11502
11811
  case 1:
11812
+ llm = _e.sent();
11813
+ return [4 /*yield*/, $provideExecutablesForNode(prepareAndScrapeOptions)];
11814
+ case 2:
11503
11815
  executables = _e.sent();
11504
11816
  _b = {
11505
11817
  llm: llm,
11506
11818
  fs: fs
11507
11819
  };
11508
11820
  return [4 /*yield*/, $provideScrapersForNode({ fs: fs, llm: llm, executables: executables }, prepareAndScrapeOptions)];
11509
- case 2:
11821
+ case 3:
11510
11822
  tools = (_b.scrapers = _e.sent(),
11511
11823
  _b.script = [
11512
11824
  /*new JavascriptExecutionTools(options)*/
@@ -11514,36 +11826,39 @@
11514
11826
  _b);
11515
11827
  return [4 /*yield*/, createCollectionFromDirectory(path$1, tools, {
11516
11828
  isVerbose: isVerbose,
11829
+ rootUrl: rootUrl,
11517
11830
  isRecursive: true,
11831
+ isLazyLoaded: false,
11832
+ isCrashedOnError: true,
11518
11833
  // <- TODO: [🍖] Add `intermediateFilesStrategy`
11519
11834
  })];
11520
- case 3:
11835
+ case 4:
11521
11836
  collection = _e.sent();
11522
11837
  return [4 /*yield*/, collection.listPipelines()];
11523
- case 4:
11838
+ case 5:
11524
11839
  pipelinesUrls = _e.sent();
11525
11840
  if (pipelinesUrls.length === 0) {
11526
11841
  console.error(colors__default["default"].red("No books found in \"".concat(path$1, "\"")));
11527
11842
  return [2 /*return*/, process.exit(1)];
11528
11843
  }
11529
- _e.label = 5;
11530
- case 5:
11531
- _e.trys.push([5, 16, 17, 18]);
11532
- validations_1 = __values(validations), validations_1_1 = validations_1.next();
11533
11844
  _e.label = 6;
11534
11845
  case 6:
11535
- if (!!validations_1_1.done) return [3 /*break*/, 15];
11536
- validation_1 = validations_1_1.value;
11846
+ _e.trys.push([6, 17, 18, 19]);
11847
+ validations_1 = __values(validations), validations_1_1 = validations_1.next();
11537
11848
  _e.label = 7;
11538
11849
  case 7:
11539
- _e.trys.push([7, 12, 13, 14]);
11540
- pipelinesUrls_1 = (e_1 = void 0, __values(pipelinesUrls)), pipelinesUrls_1_1 = pipelinesUrls_1.next();
11850
+ if (!!validations_1_1.done) return [3 /*break*/, 16];
11851
+ validation_1 = validations_1_1.value;
11541
11852
  _e.label = 8;
11542
11853
  case 8:
11543
- if (!!pipelinesUrls_1_1.done) return [3 /*break*/, 11];
11854
+ _e.trys.push([8, 13, 14, 15]);
11855
+ pipelinesUrls_1 = (e_1 = void 0, __values(pipelinesUrls)), pipelinesUrls_1_1 = pipelinesUrls_1.next();
11856
+ _e.label = 9;
11857
+ case 9:
11858
+ if (!!pipelinesUrls_1_1.done) return [3 /*break*/, 12];
11544
11859
  pipelineUrl = pipelinesUrls_1_1.value;
11545
11860
  return [4 /*yield*/, collection.getPipelineByUrl(pipelineUrl)];
11546
- case 9:
11861
+ case 10:
11547
11862
  pipeline = _e.sent();
11548
11863
  if (validation_1 === 'logic') {
11549
11864
  validatePipeline(pipeline);
@@ -11551,37 +11866,37 @@
11551
11866
  console.info(colors__default["default"].cyan("Validated logic of ".concat(pipeline.pipelineUrl)));
11552
11867
  }
11553
11868
  }
11554
- _e.label = 10;
11555
- case 10:
11869
+ _e.label = 11;
11870
+ case 11:
11556
11871
  pipelinesUrls_1_1 = pipelinesUrls_1.next();
11557
- return [3 /*break*/, 8];
11558
- case 11: return [3 /*break*/, 14];
11559
- case 12:
11872
+ return [3 /*break*/, 9];
11873
+ case 12: return [3 /*break*/, 15];
11874
+ case 13:
11560
11875
  e_1_1 = _e.sent();
11561
11876
  e_1 = { error: e_1_1 };
11562
- return [3 /*break*/, 14];
11563
- case 13:
11877
+ return [3 /*break*/, 15];
11878
+ case 14:
11564
11879
  try {
11565
11880
  if (pipelinesUrls_1_1 && !pipelinesUrls_1_1.done && (_d = pipelinesUrls_1.return)) _d.call(pipelinesUrls_1);
11566
11881
  }
11567
11882
  finally { if (e_1) throw e_1.error; }
11568
11883
  return [7 /*endfinally*/];
11569
- case 14:
11884
+ case 15:
11570
11885
  validations_1_1 = validations_1.next();
11571
- return [3 /*break*/, 6];
11572
- case 15: return [3 /*break*/, 18];
11573
- case 16:
11886
+ return [3 /*break*/, 7];
11887
+ case 16: return [3 /*break*/, 19];
11888
+ case 17:
11574
11889
  e_2_1 = _e.sent();
11575
11890
  e_2 = { error: e_2_1 };
11576
- return [3 /*break*/, 18];
11577
- case 17:
11891
+ return [3 /*break*/, 19];
11892
+ case 18:
11578
11893
  try {
11579
11894
  if (validations_1_1 && !validations_1_1.done && (_c = validations_1.return)) _c.call(validations_1);
11580
11895
  }
11581
11896
  finally { if (e_2) throw e_2.error; }
11582
11897
  return [7 /*endfinally*/];
11583
- case 18: return [4 /*yield*/, collectionToJson(collection)];
11584
- case 19:
11898
+ case 19: return [4 /*yield*/, collectionToJson(collection)];
11899
+ case 20:
11585
11900
  collectionJson = _e.sent();
11586
11901
  collectionJsonString = stringifyPipelineJson(collectionJson).trim();
11587
11902
  collectionJsonItems = (function () {
@@ -11613,32 +11928,32 @@
11613
11928
  case 2:
11614
11929
  _a.sent();
11615
11930
  // Note: Log despite of verbose mode
11616
- console.info(colors__default["default"].green("Maked ".concat(filename.split('\\').join('/'))));
11931
+ console.info(colors__default["default"].green("Made ".concat(filename.split('\\').join('/'))));
11617
11932
  return [2 /*return*/];
11618
11933
  }
11619
11934
  });
11620
11935
  }); };
11621
- if (!formats.includes('json')) return [3 /*break*/, 21];
11936
+ if (!formats.includes('json')) return [3 /*break*/, 22];
11622
11937
  formats = formats.filter(function (format) { return format !== 'json'; });
11623
11938
  return [4 /*yield*/, saveFile('json', collectionJsonString)];
11624
- case 20:
11625
- _e.sent();
11626
- _e.label = 21;
11627
11939
  case 21:
11628
- if (!(formats.includes('javascript') || formats.includes('js'))) return [3 /*break*/, 23];
11940
+ _e.sent();
11941
+ _e.label = 22;
11942
+ case 22:
11943
+ if (!(formats.includes('javascript') || formats.includes('js'))) return [3 /*break*/, 24];
11629
11944
  formats = formats.filter(function (format) { return format !== 'javascript' && format !== 'js'; });
11630
11945
  return [4 /*yield*/, saveFile('js', spaceTrim__default["default"](function (block) { return "\n // ".concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n\n import { createCollectionFromJson } from '@promptbook/core';\n\n /**\n * Pipeline collection for ").concat(projectName, "\n *\n * ").concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n *\n * @generated\n * @private internal cache for `").concat(functionName, "`\n */\n let pipelineCollection = null;\n\n\n /**\n * Get pipeline collection for ").concat(projectName, "\n *\n * ").concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n *\n * @generated\n * @returns {PipelineCollection} Library of promptbooks for ").concat(projectName, "\n */\n export function ").concat(functionName, "(){\n if(pipelineCollection===null){\n pipelineCollection = createCollectionFromJson(\n ").concat(block(collectionJsonItems), "\n );\n }\n\n return pipelineCollection;\n }\n "); }))];
11631
- case 22:
11632
- (_e.sent()) + '\n';
11633
- _e.label = 23;
11634
11946
  case 23:
11635
- if (!(formats.includes('typescript') || formats.includes('ts'))) return [3 /*break*/, 25];
11636
- formats = formats.filter(function (format) { return format !== 'typescript' && format !== 'ts'; });
11637
- return [4 /*yield*/, saveFile('ts', spaceTrim__default["default"](function (block) { return "\n // ".concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n\n import { createCollectionFromJson } from '@promptbook/core';\n import type { PipelineCollection } from '@promptbook/types';\n\n /**\n * Pipeline collection for ").concat(projectName, "\n *\n * ").concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n *\n * @private internal cache for `").concat(functionName, "`\n * @generated\n */\n let pipelineCollection: null | PipelineCollection = null;\n\n\n /**\n * Get pipeline collection for ").concat(projectName, "\n *\n * ").concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n *\n * @generated\n * @returns {PipelineCollection} Library of promptbooks for ").concat(projectName, "\n */\n export function ").concat(functionName, "(): PipelineCollection{\n if(pipelineCollection===null){\n\n // TODO: !!!!!! Use book string literal notation\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pipelineCollection = (createCollectionFromJson as (..._: any) => PipelineCollection)(\n ").concat(block(collectionJsonItems), "\n );\n }\n\n return pipelineCollection;\n }\n "); }) + '\n')];
11947
+ (_e.sent()) + '\n';
11948
+ _e.label = 24;
11638
11949
  case 24:
11639
- _e.sent();
11640
- _e.label = 25;
11950
+ if (!(formats.includes('typescript') || formats.includes('ts'))) return [3 /*break*/, 26];
11951
+ formats = formats.filter(function (format) { return format !== 'typescript' && format !== 'ts'; });
11952
+ return [4 /*yield*/, saveFile('ts', spaceTrim__default["default"](function (block) { return "\n // ".concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n\n import { createCollectionFromJson } from '@promptbook/core';\n import type { PipelineCollection } from '@promptbook/types';\n\n /**\n * Pipeline collection for ").concat(projectName, "\n *\n * ").concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n *\n * @private internal cache for `").concat(functionName, "`\n * @generated\n */\n let pipelineCollection: null | PipelineCollection = null;\n\n\n /**\n * Get pipeline collection for ").concat(projectName, "\n *\n * ").concat(block(GENERATOR_WARNING_BY_PROMPTBOOK_CLI), "\n *\n * @generated\n * @returns {PipelineCollection} Library of promptbooks for ").concat(projectName, "\n */\n export function ").concat(functionName, "(): PipelineCollection{\n if(pipelineCollection===null){\n\n // TODO: !!6 Use book string literal notation\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pipelineCollection = (createCollectionFromJson as (..._: any) => PipelineCollection)(\n ").concat(block(collectionJsonItems), "\n );\n }\n\n return pipelineCollection;\n }\n "); }) + '\n')];
11641
11953
  case 25:
11954
+ _e.sent();
11955
+ _e.label = 26;
11956
+ case 26:
11642
11957
  if (formats.length > 0) {
11643
11958
  console.warn(colors__default["default"].yellow("Format ".concat(formats.join(' and '), " is not supported")));
11644
11959
  }
@@ -11653,7 +11968,7 @@
11653
11968
  });
11654
11969
  }
11655
11970
  /**
11656
- * TODO: [🥃][main] !!! Allow `ptbk make` without configuring any llm tools
11971
+ * TODO: [🥃][main] !!3 Allow `ptbk make` without configuring any llm tools
11657
11972
  * TODO: [0] DRY Javascript and typescript - Maybe make ONLY typescript and for javascript just remove types
11658
11973
  * Note: [💞] Ignore a discrepancy between file name and entity name
11659
11974
  * Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
@@ -11671,85 +11986,21 @@
11671
11986
  var sectionRegex = new RegExp("<!--".concat(sectionName, "-->([\\s\\S]*?)<!--/").concat(sectionName, "-->"), 'g');
11672
11987
  var sectionMatch = content.match(sectionRegex);
11673
11988
  var contentToInsert = spaceTrim.spaceTrim(function (block) { return "\n <!--".concat(sectionName, "-->\n ").concat(block(warningLine), "\n ").concat(block(sectionContent), "\n <!--/").concat(sectionName, "-->\n "); });
11674
- if (sectionMatch) {
11675
- return content.replace(sectionRegex, contentToInsert);
11676
- }
11677
- // Note: Following is the case when the section is not found in the file so we add it there
11678
- var placeForSection = removeContentComments(content).match(/^##.*$/im);
11679
- if (placeForSection !== null) {
11680
- var _a = __read(placeForSection, 1), heading_1 = _a[0];
11681
- return content.replace(heading_1, spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(contentToInsert), "\n\n ").concat(block(heading_1), "\n "); }));
11682
- }
11683
- console.warn("No place where to put the section <!--".concat(sectionName, "-->, using the end of the file"));
11684
- // <- TODO: [🚎][💩] Some better way how to get warnings from pipeline parsing / logic
11685
- return spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(content), "\n\n ").concat(block(contentToInsert), "\n "); });
11686
- }
11687
- /**
11688
- * TODO: [🏛] This can be part of markdown builder
11689
- */
11690
-
11691
- /**
11692
- * Creates a Mermaid graph based on the promptbook
11693
- *
11694
- * Note: The result is not wrapped in a Markdown code block
11695
- *
11696
- * @public exported from `@promptbook/utils`
11697
- */
11698
- function renderPromptbookMermaid(pipelineJson, options) {
11699
- var _a = (options || {}).linkTask, linkTask = _a === void 0 ? function () { return null; } : _a;
11700
- var parameterNameToTaskName = function (parameterName) {
11701
- var parameter = pipelineJson.parameters.find(function (parameter) { return parameter.name === parameterName; });
11702
- if (!parameter) {
11703
- throw new UnexpectedError("Could not find {".concat(parameterName, "}"));
11704
- // <- TODO: !!!!!! This causes problems when {knowledge} and other reserved parameters are used
11705
- }
11706
- if (parameter.isInput) {
11707
- return 'input';
11708
- }
11709
- var task = pipelineJson.tasks.find(function (task) { return task.resultingParameterName === parameterName; });
11710
- if (!task) {
11711
- throw new Error("Could not find task for {".concat(parameterName, "}"));
11712
- }
11713
- return task.name || normalizeTo_camelCase('task-' + titleToName(task.title));
11714
- };
11715
- var promptbookMermaid = spaceTrim.spaceTrim(function (block) { return "\n\n %% \uD83D\uDD2E Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually\n\n flowchart LR\n subgraph \"".concat(pipelineJson.title, "\"\n\n direction TB\n\n input((Input)):::input\n ").concat(block(pipelineJson.tasks
11716
- .flatMap(function (_a) {
11717
- var title = _a.title, dependentParameterNames = _a.dependentParameterNames, resultingParameterName = _a.resultingParameterName;
11718
- return __spreadArray([
11719
- "".concat(parameterNameToTaskName(resultingParameterName), "(\"").concat(title, "\")")
11720
- ], __read(dependentParameterNames.map(function (dependentParameterName) {
11721
- return "".concat(parameterNameToTaskName(dependentParameterName), "--\"{").concat(dependentParameterName, "}\"-->").concat(parameterNameToTaskName(resultingParameterName));
11722
- })), false);
11723
- })
11724
- .join('\n')), "\n\n ").concat(block(pipelineJson.parameters
11725
- .filter(function (_a) {
11726
- var isOutput = _a.isOutput;
11727
- return isOutput;
11728
- })
11729
- .map(function (_a) {
11730
- var name = _a.name;
11731
- return "".concat(parameterNameToTaskName(name), "--\"{").concat(name, "}\"-->output");
11732
- })
11733
- .join('\n')), "\n output((Output)):::output\n\n ").concat(block(pipelineJson.tasks
11734
- .map(function (task) {
11735
- var link = linkTask(task);
11736
- if (link === null) {
11737
- return '';
11738
- }
11739
- var href = link.href, title = link.title;
11740
- var taskName = parameterNameToTaskName(task.resultingParameterName);
11741
- return "click ".concat(taskName, " href \"").concat(href, "\" \"").concat(title, "\";");
11742
- })
11743
- .filter(function (line) { return line !== ''; })
11744
- .join('\n')), "\n\n classDef input color: grey;\n classDef output color: grey;\n\n end;\n\n "); });
11745
- return promptbookMermaid;
11989
+ if (sectionMatch) {
11990
+ return content.replace(sectionRegex, contentToInsert);
11991
+ }
11992
+ // Note: Following is the case when the section is not found in the file so we add it there
11993
+ var placeForSection = removeMarkdownComments(content).match(/^##.*$/im);
11994
+ if (placeForSection !== null) {
11995
+ var _a = __read(placeForSection, 1), heading_1 = _a[0];
11996
+ return content.replace(heading_1, spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(contentToInsert), "\n\n ").concat(block(heading_1), "\n "); }));
11997
+ }
11998
+ console.warn("No place where to put the section <!--".concat(sectionName, "-->, using the end of the file"));
11999
+ // <- TODO: [🚎][💩] Some better way how to get warnings from pipeline parsing / logic
12000
+ return spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(content), "\n\n ").concat(block(contentToInsert), "\n "); });
11746
12001
  }
11747
12002
  /**
11748
- * TODO: [🧠] !! FOREACH in mermaid graph
11749
- * TODO: [🧠] !! Knowledge in mermaid graph
11750
- * TODO: [🧠] !! Personas in mermaid graph
11751
- * TODO: Maybe use some Mermaid package instead of string templating
11752
- * TODO: [🕌] When more than 2 functionalities, split into separate functions
12003
+ * TODO: [🏛] This can be part of markdown builder
11753
12004
  */
11754
12005
 
11755
12006
  /**
@@ -11782,6 +12033,7 @@
11782
12033
  case 2:
11783
12034
  if (isPrettifyed) {
11784
12035
  pipelineString = prettifyMarkdown(pipelineString);
12036
+ // <- TODO: [😧] `prettifyMarkdown` should preserve discriminated type *(or at lease `PipelineString`)*
11785
12037
  }
11786
12038
  return [2 /*return*/, pipelineString];
11787
12039
  }
@@ -11810,18 +12062,18 @@
11810
12062
  prettifyCommand.action(function (filesGlob, _a) {
11811
12063
  var ignore = _a.ignore, isVerbose = _a.verbose;
11812
12064
  return __awaiter(_this, void 0, void 0, function () {
11813
- var filenames, filenames_1, filenames_1_1, filename, pipelineMarkdown, error_1, e_1_1;
11814
- var e_1, _b;
11815
- return __generator(this, function (_c) {
11816
- switch (_c.label) {
12065
+ var filenames, filenames_1, filenames_1_1, filename, pipelineMarkdown, _b, error_1, e_1_1;
12066
+ var e_1, _c;
12067
+ return __generator(this, function (_d) {
12068
+ switch (_d.label) {
11817
12069
  case 0: return [4 /*yield*/, glob__default["default"](filesGlob, { ignore: ignore })];
11818
12070
  case 1:
11819
- filenames = _c.sent();
11820
- _c.label = 2;
12071
+ filenames = _d.sent();
12072
+ _d.label = 2;
11821
12073
  case 2:
11822
- _c.trys.push([2, 11, 12, 13]);
12074
+ _d.trys.push([2, 11, 12, 13]);
11823
12075
  filenames_1 = __values(filenames), filenames_1_1 = filenames_1.next();
11824
- _c.label = 3;
12076
+ _d.label = 3;
11825
12077
  case 3:
11826
12078
  if (!!filenames_1_1.done) return [3 /*break*/, 10];
11827
12079
  filename = filenames_1_1.value;
@@ -11829,28 +12081,29 @@
11829
12081
  console.info(colors__default["default"].gray("Skipping ".concat(filename)));
11830
12082
  return [3 /*break*/, 9];
11831
12083
  }
12084
+ _b = validatePipelineString;
11832
12085
  return [4 /*yield*/, promises.readFile(filename, 'utf-8')];
11833
12086
  case 4:
11834
- pipelineMarkdown = (_c.sent());
11835
- _c.label = 5;
12087
+ pipelineMarkdown = _b.apply(void 0, [_d.sent()]);
12088
+ _d.label = 5;
11836
12089
  case 5:
11837
- _c.trys.push([5, 8, , 9]);
12090
+ _d.trys.push([5, 8, , 9]);
11838
12091
  return [4 /*yield*/, prettifyPipelineString(pipelineMarkdown, {
11839
12092
  isGraphAdded: true,
11840
12093
  isPrettifyed: true,
11841
12094
  // <- [🕌]
11842
12095
  })];
11843
12096
  case 6:
11844
- pipelineMarkdown = _c.sent();
12097
+ pipelineMarkdown = _d.sent();
11845
12098
  return [4 /*yield*/, promises.writeFile(filename, pipelineMarkdown)];
11846
12099
  case 7:
11847
- _c.sent();
12100
+ _d.sent();
11848
12101
  if (isVerbose) {
11849
12102
  console.info(colors__default["default"].green("Prettify ".concat(filename)));
11850
12103
  }
11851
12104
  return [3 /*break*/, 9];
11852
12105
  case 8:
11853
- error_1 = _c.sent();
12106
+ error_1 = _d.sent();
11854
12107
  if (!(error_1 instanceof Error)) {
11855
12108
  throw error_1;
11856
12109
  }
@@ -11863,12 +12116,12 @@
11863
12116
  return [3 /*break*/, 3];
11864
12117
  case 10: return [3 /*break*/, 13];
11865
12118
  case 11:
11866
- e_1_1 = _c.sent();
12119
+ e_1_1 = _d.sent();
11867
12120
  e_1 = { error: e_1_1 };
11868
12121
  return [3 /*break*/, 13];
11869
12122
  case 12:
11870
12123
  try {
11871
- if (filenames_1_1 && !filenames_1_1.done && (_b = filenames_1.return)) _b.call(filenames_1);
12124
+ if (filenames_1_1 && !filenames_1_1.done && (_c = filenames_1.return)) _c.call(filenames_1);
11872
12125
  }
11873
12126
  finally { if (e_1) throw e_1.error; }
11874
12127
  return [7 /*endfinally*/];
@@ -12194,6 +12447,187 @@
12194
12447
  * TODO: [🧠] Should be in generated file GENERATOR_WARNING
12195
12448
  */
12196
12449
 
12450
+ /**
12451
+ * Function `isValidPipelineString` will validate the if the string is a valid pipeline string
12452
+ * It does not check if the string is fully logically correct, but if it is a string that can be a pipeline string or the string looks completely different.
12453
+ *
12454
+ * @param {string} pipelineString the candidate for a pipeline string
12455
+ * @returns {boolean} if the string is a valid pipeline string
12456
+ * @public exported from `@promptbook/core`
12457
+ */
12458
+ function isValidPipelineString(pipelineString) {
12459
+ try {
12460
+ validatePipelineString(pipelineString);
12461
+ return true;
12462
+ }
12463
+ catch (error) {
12464
+ if (!(error instanceof Error)) {
12465
+ throw error;
12466
+ }
12467
+ return false;
12468
+ }
12469
+ }
12470
+ /**
12471
+ * TODO: [🧠][🈴] Where is the best location for this file
12472
+ */
12473
+
12474
+ /**
12475
+ * @see ./wizzard.ts `getPipeline` method
12476
+ *
12477
+ * @private usable through `ptbk run` and `@prompbook/wizzard`
12478
+ */
12479
+ function $getCompiledBook(tools, pipelineSource, options) {
12480
+ return __awaiter(this, void 0, void 0, function () {
12481
+ var fs, fetch, filePathRaw, filePath, filePathCandidates, filePathCandidates_1, filePathCandidates_1_1, filePathCandidate, pipelineString, _a, pipelineJson, e_1_1, rootDirname, _loop_1, i, state_1, response_1, pipelineString, pipelineJson, pipelineJson;
12482
+ var e_1, _b;
12483
+ var _this = this;
12484
+ return __generator(this, function (_c) {
12485
+ switch (_c.label) {
12486
+ case 0:
12487
+ fs = tools.fs, fetch = tools.fetch;
12488
+ if (!isValidFilePath(pipelineSource)) return [3 /*break*/, 10];
12489
+ filePathRaw = pipelineSource;
12490
+ filePath = null;
12491
+ filePathCandidates = [filePathRaw, "".concat(filePathRaw, ".md"), "".concat(filePathRaw, ".book.md"), "".concat(filePathRaw, ".book.md")];
12492
+ filePathCandidates = __spreadArray(__spreadArray([], __read(filePathCandidates), false), __read(filePathCandidates.map(function (path) { return path.split('\\').join('/'); })), false);
12493
+ _c.label = 1;
12494
+ case 1:
12495
+ _c.trys.push([1, 8, 9, 10]);
12496
+ filePathCandidates_1 = __values(filePathCandidates), filePathCandidates_1_1 = filePathCandidates_1.next();
12497
+ _c.label = 2;
12498
+ case 2:
12499
+ if (!!filePathCandidates_1_1.done) return [3 /*break*/, 7];
12500
+ filePathCandidate = filePathCandidates_1_1.value;
12501
+ return [4 /*yield*/, isFileExisting(filePathCandidate, fs)
12502
+ // <- TODO: Also test that among the candidates the file is book not just any file
12503
+ ];
12504
+ case 3:
12505
+ if (!_c.sent()) return [3 /*break*/, 6];
12506
+ filePath = filePathCandidate;
12507
+ _a = validatePipelineString;
12508
+ return [4 /*yield*/, fs.readFile(filePath, 'utf-8')];
12509
+ case 4:
12510
+ pipelineString = _a.apply(void 0, [_c.sent()]);
12511
+ return [4 /*yield*/, compilePipeline(pipelineString, tools, __assign({ rootDirname: process.cwd() }, options))];
12512
+ case 5:
12513
+ pipelineJson = _c.sent();
12514
+ return [2 /*return*/, pipelineJson];
12515
+ case 6:
12516
+ filePathCandidates_1_1 = filePathCandidates_1.next();
12517
+ return [3 /*break*/, 2];
12518
+ case 7: return [3 /*break*/, 10];
12519
+ case 8:
12520
+ e_1_1 = _c.sent();
12521
+ e_1 = { error: e_1_1 };
12522
+ return [3 /*break*/, 10];
12523
+ case 9:
12524
+ try {
12525
+ if (filePathCandidates_1_1 && !filePathCandidates_1_1.done && (_b = filePathCandidates_1.return)) _b.call(filePathCandidates_1);
12526
+ }
12527
+ finally { if (e_1) throw e_1.error; }
12528
+ return [7 /*endfinally*/];
12529
+ case 10:
12530
+ if (!isValidPipelineUrl(pipelineSource)) return [3 /*break*/, 14];
12531
+ rootDirname = process.cwd();
12532
+ _loop_1 = function (i) {
12533
+ var booksDirname, collection_1, pipeline;
12534
+ return __generator(this, function (_d) {
12535
+ switch (_d.label) {
12536
+ case 0:
12537
+ booksDirname = path.join(rootDirname, DEFAULT_BOOKS_DIRNAME /* <- TODO: [🕝] Make here more candidates */);
12538
+ return [4 /*yield*/, isDirectoryExisting(booksDirname, fs)];
12539
+ case 1:
12540
+ if (!_d.sent()) return [3 /*break*/, 4];
12541
+ return [4 /*yield*/, createCollectionFromDirectory(booksDirname, tools, __assign({ isRecursive: true, rootDirname: booksDirname }, options))];
12542
+ case 2:
12543
+ collection_1 = _d.sent();
12544
+ return [4 /*yield*/, (function () { return __awaiter(_this, void 0, void 0, function () {
12545
+ var error_1;
12546
+ return __generator(this, function (_a) {
12547
+ switch (_a.label) {
12548
+ case 0:
12549
+ _a.trys.push([0, 2, , 3]);
12550
+ return [4 /*yield*/, collection_1.getPipelineByUrl(pipelineSource)];
12551
+ case 1: return [2 /*return*/, _a.sent()];
12552
+ case 2:
12553
+ error_1 = _a.sent();
12554
+ if (!(error_1 instanceof NotFoundError)) {
12555
+ throw error_1;
12556
+ }
12557
+ // Note: If the pipeline was not found in the collection, try next strategy
12558
+ return [2 /*return*/, null];
12559
+ case 3: return [2 /*return*/];
12560
+ }
12561
+ });
12562
+ }); })()];
12563
+ case 3:
12564
+ pipeline = _d.sent();
12565
+ // console.log({ pipeline });
12566
+ if (pipeline !== null) {
12567
+ return [2 /*return*/, { value: pipeline }];
12568
+ }
12569
+ _d.label = 4;
12570
+ case 4:
12571
+ if (isRootPath(rootDirname)) {
12572
+ return [2 /*return*/, "break-up_to_root"];
12573
+ }
12574
+ // Note: If the directory does not exist, try the parent directory
12575
+ rootDirname = path.join(rootDirname, '..');
12576
+ return [2 /*return*/];
12577
+ }
12578
+ });
12579
+ };
12580
+ i = 0;
12581
+ _c.label = 11;
12582
+ case 11:
12583
+ if (!(i < LOOP_LIMIT)) return [3 /*break*/, 14];
12584
+ return [5 /*yield**/, _loop_1(i)];
12585
+ case 12:
12586
+ state_1 = _c.sent();
12587
+ if (typeof state_1 === "object")
12588
+ return [2 /*return*/, state_1.value];
12589
+ switch (state_1) {
12590
+ case "break-up_to_root": return [3 /*break*/, 14];
12591
+ }
12592
+ _c.label = 13;
12593
+ case 13:
12594
+ i++;
12595
+ return [3 /*break*/, 11];
12596
+ case 14:
12597
+ if (!isValidPipelineUrl(pipelineSource)) return [3 /*break*/, 18];
12598
+ return [4 /*yield*/, fetch(pipelineSource)];
12599
+ case 15:
12600
+ response_1 = _c.sent();
12601
+ if (response_1.status >= 300) {
12602
+ throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Book not found on URL:\n ".concat(block(pipelineSource), "\n\n Request failed with status ").concat(block(response_1.status.toString()), " ").concat(block(response_1.statusText), "\n "); }));
12603
+ }
12604
+ return [4 /*yield*/, response_1.text()];
12605
+ case 16:
12606
+ pipelineString = _c.sent();
12607
+ // console.log({ pipelineString });
12608
+ if (!isValidPipelineString(pipelineString)) {
12609
+ throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Book not found on URL:\n ".concat(block(pipelineSource), "\n\n Requested URL does not seem to contain a valid book\n "); }));
12610
+ }
12611
+ return [4 /*yield*/, compilePipeline(pipelineString, tools, __assign({ rootDirname: null }, options))];
12612
+ case 17:
12613
+ pipelineJson = _c.sent();
12614
+ return [2 /*return*/, pipelineJson];
12615
+ case 18:
12616
+ if (!isValidPipelineString(pipelineSource)) return [3 /*break*/, 20];
12617
+ return [4 /*yield*/, compilePipeline(pipelineSource, tools, __assign({ rootDirname: null }, options))];
12618
+ case 19:
12619
+ pipelineJson = _c.sent();
12620
+ return [2 /*return*/, pipelineJson];
12621
+ case 20: /* not else */ throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Book not found:\n ".concat(block(pipelineSource), "\n\n Pipelines can be loaded from:\n 1) As a file ./books/write-cv.book.md\n 2) As a URL https://promptbook.studio/hejny/write-cv.book.md found in ./books folder recursively\n 2) As a URL https://promptbook.studio/hejny/write-cv.book.md fetched from the internet\n 3) As a string\n\n\n "); }));
12622
+ }
12623
+ });
12624
+ });
12625
+ }
12626
+ /**
12627
+ * TODO: Write unit test
12628
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
12629
+ */
12630
+
12197
12631
  /**
12198
12632
  * Run the interactive chatbot in CLI
12199
12633
  *
@@ -12320,20 +12754,20 @@
12320
12754
  var runCommand = program.command('run', { isDefault: true });
12321
12755
  runCommand.description(spaceTrim__default["default"]("\n Runs a pipeline\n "));
12322
12756
  // TODO: [🧅] DRY command arguments
12323
- runCommand.argument('<path>',
12757
+ runCommand.argument('<pipelineSource>',
12324
12758
  // <- Note: [🧟‍♂️] This is NOT promptbook collection directory BUT direct path to .book.md file
12325
- 'Path to book file');
12759
+ 'Path to book file OR URL to book file');
12326
12760
  runCommand.option('-r, --reload', "Call LLM models even if same prompt with result is in the cache", false);
12327
12761
  runCommand.option('-v, --verbose', "Is output verbose", false);
12328
12762
  runCommand.option('--no-interactive', "Input is not interactive, if true you need to pass all the input parameters through --json");
12329
12763
  runCommand.option('--no-formfactor', "When set, behavior of the interactive mode is not changed by the formfactor of the pipeline");
12330
12764
  runCommand.option('-j, --json <json>', "Pass all or some input parameters as JSON record, if used the output is also returned as JSON");
12331
12765
  runCommand.option('-s, --save-report <path>', "Save report to file");
12332
- runCommand.action(function (filePathRaw, options) { return __awaiter(_this, void 0, void 0, function () {
12333
- var isCacheReloaded, isInteractive, isFormfactorUsed, json, isVerbose, saveReport, inputParameters, prepareAndScrapeOptions, fs, filePath, filePathCandidates, filePathCandidates_1, filePathCandidates_1_1, filePathCandidate, e_1_1, llm, executables, tools, pipelineString, pipeline, error_1, pipelineExecutor, questions, response, result, isSuccessful, errors, warnings, outputParameters, executionReport, executionReportString, _a, _b, error, _c, _d, warning, _e, _f, key, value, separator;
12334
- var e_1, _g, _h, e_2, _j, e_3, _k, e_4, _l;
12335
- return __generator(this, function (_m) {
12336
- switch (_m.label) {
12766
+ runCommand.action(function (pipelineSource, options) { return __awaiter(_this, void 0, void 0, function () {
12767
+ var isCacheReloaded, isInteractive, isFormfactorUsed, json, isVerbose, saveReport, inputParameters, prepareAndScrapeOptions, fs, llm, error_1, executables, tools, pipeline, error_2, pipelineExecutor, questions, response, result, isSuccessful, errors, warnings, outputParameters, executionReport, executionReportString, _a, _b, error, _c, _d, warning, _e, _f, key, value, separator;
12768
+ var _g, e_1, _h, e_2, _j, e_3, _k;
12769
+ return __generator(this, function (_l) {
12770
+ switch (_l.label) {
12337
12771
  case 0:
12338
12772
  isCacheReloaded = options.reload, isInteractive = options.interactive, isFormfactorUsed = options.formfactor, json = options.json, isVerbose = options.verbose, saveReport = options.saveReport;
12339
12773
  if (saveReport && !saveReport.endsWith('.json') && !saveReport.endsWith('.md')) {
@@ -12353,104 +12787,65 @@
12353
12787
  console.info(colors__default["default"].gray('--- Preparing tools ---'));
12354
12788
  }
12355
12789
  fs = $provideFilesystemForNode(prepareAndScrapeOptions);
12356
- filePath = null;
12357
- filePathCandidates = [filePathRaw, "".concat(filePathRaw, ".md"), "".concat(filePathRaw, ".book.md"), "".concat(filePathRaw, ".book.md")];
12358
- filePathCandidates = __spreadArray(__spreadArray([], __read(filePathCandidates), false), __read(filePathCandidates.map(function (path) { return path.split('\\').join('/'); })), false);
12359
- _m.label = 1;
12790
+ _l.label = 1;
12360
12791
  case 1:
12361
- _m.trys.push([1, 6, 7, 8]);
12362
- filePathCandidates_1 = __values(filePathCandidates), filePathCandidates_1_1 = filePathCandidates_1.next();
12363
- _m.label = 2;
12792
+ _l.trys.push([1, 3, , 4]);
12793
+ return [4 /*yield*/, $provideLlmToolsForWizzardOrCli(prepareAndScrapeOptions)];
12364
12794
  case 2:
12365
- if (!!filePathCandidates_1_1.done) return [3 /*break*/, 5];
12366
- filePathCandidate = filePathCandidates_1_1.value;
12367
- return [4 /*yield*/, isFileExisting(filePathCandidate, fs)
12368
- // <- TODO: Also test that among the candidates the file is book not just any file
12369
- ];
12795
+ llm = _l.sent();
12796
+ return [3 /*break*/, 4];
12370
12797
  case 3:
12371
- if (_m.sent()
12372
- // <- TODO: Also test that among the candidates the file is book not just any file
12373
- ) {
12374
- filePath = filePathCandidate;
12375
- return [3 /*break*/, 5];
12376
- }
12377
- _m.label = 4;
12378
- case 4:
12379
- filePathCandidates_1_1 = filePathCandidates_1.next();
12380
- return [3 /*break*/, 2];
12381
- case 5: return [3 /*break*/, 8];
12382
- case 6:
12383
- e_1_1 = _m.sent();
12384
- e_1 = { error: e_1_1 };
12385
- return [3 /*break*/, 8];
12386
- case 7:
12387
- try {
12388
- if (filePathCandidates_1_1 && !filePathCandidates_1_1.done && (_g = filePathCandidates_1.return)) _g.call(filePathCandidates_1);
12389
- }
12390
- finally { if (e_1) throw e_1.error; }
12391
- return [7 /*endfinally*/];
12392
- case 8:
12393
- if (filePath === null) {
12394
- console.error(colors__default["default"].red("File \"".concat(filePathRaw, "\" does not exist")));
12395
- return [2 /*return*/, process.exit(1)];
12396
- }
12397
- try {
12398
- llm = $provideLlmToolsForCli(prepareAndScrapeOptions);
12798
+ error_1 = _l.sent();
12799
+ if (!(error_1 instanceof Error)) {
12800
+ throw error_1;
12399
12801
  }
12400
- catch (error) {
12401
- if (!(error instanceof Error)) {
12402
- throw error;
12403
- }
12404
- if (!error.message.includes('No LLM tools')) {
12405
- throw error;
12406
- }
12407
- console.error(colors__default["default"].red(spaceTrim__default["default"](function (block) { return "\n You need to configure LLM tools first\n\n 1) Create .env file at the root of your project\n 2) Configure API keys for LLM tools\n\n For example:\n ".concat(block($llmToolsMetadataRegister
12408
- .list()
12409
- .map(function (_a) {
12410
- var title = _a.title, envVariables = _a.envVariables;
12411
- return "- ".concat((envVariables || []).join(' + '), " (").concat(title, ")");
12412
- })
12413
- .join('\n')), "\n\n ").concat(block($registeredLlmToolsMessage()), "\n "); })));
12414
- return [2 /*return*/, process.exit(1)];
12802
+ if (!error_1.message.includes('No LLM tools')) {
12803
+ throw error_1;
12415
12804
  }
12416
- return [4 /*yield*/, $provideExecutablesForNode(prepareAndScrapeOptions)];
12417
- case 9:
12418
- executables = _m.sent();
12419
- _h = {
12805
+ console.error(colors__default["default"].red(spaceTrim__default["default"](function (block) { return "\n You need to configure LLM tools first\n\n 1) Create .env file at the root of your project\n 2) Configure API keys for LLM tools\n\n For example:\n ".concat(block($llmToolsMetadataRegister
12806
+ .list()
12807
+ .map(function (_a) {
12808
+ var title = _a.title, envVariables = _a.envVariables;
12809
+ return "- ".concat((envVariables || []).join(' + '), " (").concat(title, ")");
12810
+ })
12811
+ .join('\n')), "\n\n ").concat(block($registeredLlmToolsMessage()), "\n "); })));
12812
+ return [2 /*return*/, process.exit(1)];
12813
+ case 4: return [4 /*yield*/, $provideExecutablesForNode(prepareAndScrapeOptions)];
12814
+ case 5:
12815
+ executables = _l.sent();
12816
+ _g = {
12420
12817
  llm: llm,
12421
- fs: fs
12818
+ fs: fs,
12819
+ fetch: scraperFetch
12422
12820
  };
12423
12821
  return [4 /*yield*/, $provideScrapersForNode({ fs: fs, llm: llm, executables: executables }, prepareAndScrapeOptions)];
12424
- case 10:
12425
- tools = (_h.scrapers = _m.sent(),
12426
- _h.script = [
12822
+ case 6:
12823
+ tools = (_g.scrapers = _l.sent(),
12824
+ _g.script = [
12427
12825
  /*new JavascriptExecutionTools(options)*/
12428
12826
  ],
12429
- _h);
12827
+ _g);
12430
12828
  if (isVerbose) {
12431
12829
  console.info(colors__default["default"].gray('--- Reading file ---'));
12432
12830
  }
12433
- return [4 /*yield*/, promises.readFile(filePath, 'utf-8')];
12434
- case 11:
12435
- pipelineString = (_m.sent());
12436
12831
  if (isVerbose) {
12437
12832
  console.info(colors__default["default"].gray('--- Preparing pipeline ---'));
12438
12833
  }
12439
- _m.label = 12;
12440
- case 12:
12441
- _m.trys.push([12, 14, , 15]);
12442
- return [4 /*yield*/, compilePipeline(pipelineString, tools)];
12443
- case 13:
12444
- pipeline = _m.sent();
12445
- return [3 /*break*/, 15];
12446
- case 14:
12447
- error_1 = _m.sent();
12448
- if (!(error_1 instanceof ParseError)) {
12449
- throw error_1;
12834
+ _l.label = 7;
12835
+ case 7:
12836
+ _l.trys.push([7, 9, , 10]);
12837
+ return [4 /*yield*/, $getCompiledBook(tools, pipelineSource, prepareAndScrapeOptions)];
12838
+ case 8:
12839
+ pipeline = _l.sent();
12840
+ return [3 /*break*/, 10];
12841
+ case 9:
12842
+ error_2 = _l.sent();
12843
+ if (!(error_2 instanceof ParseError)) {
12844
+ throw error_2;
12450
12845
  }
12451
- console.error(colors__default["default"].red(spaceTrim__default["default"](function (block) { return "\n ".concat(block(error_1.message), "\n\n in ").concat(filePath, "\n "); })));
12846
+ console.error(colors__default["default"].red(spaceTrim__default["default"](function (block) { return "\n ".concat(block(error_2.message), "\n\n in ").concat(pipelineSource, "\n "); })));
12452
12847
  return [2 /*return*/, process.exit(1)];
12453
- case 15:
12848
+ case 10:
12454
12849
  if (isVerbose) {
12455
12850
  console.info(colors__default["default"].gray('--- Validating pipeline ---'));
12456
12851
  }
@@ -12536,8 +12931,8 @@
12536
12931
  return [2 /*return*/, process.exit(1)];
12537
12932
  }
12538
12933
  return [4 /*yield*/, prompts__default["default"](questions)];
12539
- case 16:
12540
- response = _m.sent();
12934
+ case 11:
12935
+ response = _l.sent();
12541
12936
  // <- TODO: [🧠][🍼] Change behavior according to the formfactor
12542
12937
  inputParameters = __assign(__assign({}, inputParameters), response);
12543
12938
  // TODO: Maybe do some validation of the response (and --json argument which is passed)
@@ -12550,26 +12945,26 @@
12550
12945
  console.info(taskProgress);
12551
12946
  }
12552
12947
  })];
12553
- case 17:
12554
- result = _m.sent();
12948
+ case 12:
12949
+ result = _l.sent();
12555
12950
  isSuccessful = result.isSuccessful, errors = result.errors, warnings = result.warnings, outputParameters = result.outputParameters, executionReport = result.executionReport;
12556
12951
  if (isVerbose) {
12557
12952
  console.info(colors__default["default"].gray('--- Detailed Result ---'));
12558
12953
  console.info({ isSuccessful: isSuccessful, errors: errors, warnings: warnings, outputParameters: outputParameters, executionReport: executionReport });
12559
12954
  }
12560
- if (!(saveReport && saveReport.endsWith('.json'))) return [3 /*break*/, 19];
12955
+ if (!(saveReport && saveReport.endsWith('.json'))) return [3 /*break*/, 14];
12561
12956
  return [4 /*yield*/, promises.writeFile(saveReport, JSON.stringify(executionReport, null, 4) + '\n', 'utf-8')];
12562
- case 18:
12563
- _m.sent();
12564
- return [3 /*break*/, 21];
12565
- case 19:
12566
- if (!(saveReport && saveReport.endsWith('.md'))) return [3 /*break*/, 21];
12957
+ case 13:
12958
+ _l.sent();
12959
+ return [3 /*break*/, 16];
12960
+ case 14:
12961
+ if (!(saveReport && saveReport.endsWith('.md'))) return [3 /*break*/, 16];
12567
12962
  executionReportString = executionReportJsonToString(executionReport);
12568
12963
  return [4 /*yield*/, promises.writeFile(saveReport, executionReportString, 'utf-8')];
12569
- case 20:
12570
- _m.sent();
12571
- _m.label = 21;
12572
- case 21:
12964
+ case 15:
12965
+ _l.sent();
12966
+ _l.label = 16;
12967
+ case 16:
12573
12968
  if (saveReport && isVerbose) {
12574
12969
  console.info(colors__default["default"].green("Report saved to ".concat(saveReport)));
12575
12970
  }
@@ -12587,12 +12982,12 @@
12587
12982
  console.error(colors__default["default"].red(colors__default["default"].bold(error.name) + ': ' + error.message));
12588
12983
  }
12589
12984
  }
12590
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
12985
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
12591
12986
  finally {
12592
12987
  try {
12593
- if (_b && !_b.done && (_j = _a.return)) _j.call(_a);
12988
+ if (_b && !_b.done && (_h = _a.return)) _h.call(_a);
12594
12989
  }
12595
- finally { if (e_2) throw e_2.error; }
12990
+ finally { if (e_1) throw e_1.error; }
12596
12991
  }
12597
12992
  try {
12598
12993
  for (_c = __values(warnings || []), _d = _c.next(); !_d.done; _d = _c.next()) {
@@ -12600,12 +12995,12 @@
12600
12995
  console.error(colors__default["default"].red(colors__default["default"].bold(warning.name) + ': ' + warning.message));
12601
12996
  }
12602
12997
  }
12603
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
12998
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
12604
12999
  finally {
12605
13000
  try {
12606
- if (_d && !_d.done && (_k = _c.return)) _k.call(_c);
13001
+ if (_d && !_d.done && (_j = _c.return)) _j.call(_c);
12607
13002
  }
12608
- finally { if (e_3) throw e_3.error; }
13003
+ finally { if (e_2) throw e_2.error; }
12609
13004
  }
12610
13005
  if (json === undefined) {
12611
13006
  try {
@@ -12616,12 +13011,12 @@
12616
13011
  console.info(colors__default["default"].green(colors__default["default"].bold(key) + separator + value));
12617
13012
  }
12618
13013
  }
12619
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
13014
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
12620
13015
  finally {
12621
13016
  try {
12622
- if (_f && !_f.done && (_l = _e.return)) _l.call(_e);
13017
+ if (_f && !_f.done && (_k = _e.return)) _k.call(_e);
12623
13018
  }
12624
- finally { if (e_4) throw e_4.error; }
13019
+ finally { if (e_3) throw e_3.error; }
12625
13020
  }
12626
13021
  }
12627
13022
  else {
@@ -12633,9 +13028,9 @@
12633
13028
  }); });
12634
13029
  }
12635
13030
  /**
12636
- * TODO: !!!!! Catch and wrap all errors from CLI
13031
+ * TODO: !!5 Catch and wrap all errors from CLI
12637
13032
  * TODO: [🧠] Pass `maxExecutionAttempts`, `csvSettings`
12638
- * TODO: [🥃][main] !!! Allow `ptbk run` without configuring any llm tools
13033
+ * TODO: [🥃][main] !!3 Allow `ptbk run` without configuring any llm tools
12639
13034
  * Note: [💞] Ignore a discrepancy between file name and entity name
12640
13035
  * Note: [🟡] Code in this file should never be published outside of `@promptbook/cli`
12641
13036
  * TODO: [🖇] What about symlinks? Maybe flag --follow-symlinks
@@ -12659,75 +13054,78 @@
12659
13054
  testCommand.action(function (filesGlob, _a) {
12660
13055
  var ignore = _a.ignore, isCacheReloaded = _a.reload, isVerbose = _a.verbose;
12661
13056
  return __awaiter(_this, void 0, void 0, function () {
12662
- var prepareAndScrapeOptions, fs, llm, executables, tools, filenames, filenames_1, filenames_1_1, filename, pipeline, pipelineMarkdown, _b, _c, error_1, e_1_1;
12663
- var _d, e_1, _e;
12664
- return __generator(this, function (_f) {
12665
- switch (_f.label) {
13057
+ var prepareAndScrapeOptions, fs, llm, executables, tools, filenames, filenames_1, filenames_1_1, filename, pipeline, pipelineMarkdown, _b, _c, _d, error_1, e_1_1;
13058
+ var _e, e_1, _f;
13059
+ return __generator(this, function (_g) {
13060
+ switch (_g.label) {
12666
13061
  case 0:
12667
13062
  prepareAndScrapeOptions = {
12668
13063
  isVerbose: isVerbose,
12669
13064
  isCacheReloaded: isCacheReloaded,
12670
13065
  };
12671
13066
  fs = $provideFilesystemForNode(prepareAndScrapeOptions);
12672
- llm = $provideLlmToolsForCli(prepareAndScrapeOptions);
12673
- return [4 /*yield*/, $provideExecutablesForNode(prepareAndScrapeOptions)];
13067
+ return [4 /*yield*/, $provideLlmToolsForWizzardOrCli(prepareAndScrapeOptions)];
12674
13068
  case 1:
12675
- executables = _f.sent();
12676
- _d = {
13069
+ llm = _g.sent();
13070
+ return [4 /*yield*/, $provideExecutablesForNode(prepareAndScrapeOptions)];
13071
+ case 2:
13072
+ executables = _g.sent();
13073
+ _e = {
12677
13074
  llm: llm,
12678
13075
  fs: fs
12679
13076
  };
12680
13077
  return [4 /*yield*/, $provideScrapersForNode({ fs: fs, llm: llm, executables: executables }, prepareAndScrapeOptions)];
12681
- case 2:
12682
- tools = (_d.scrapers = _f.sent(),
12683
- _d.script = [
13078
+ case 3:
13079
+ tools = (_e.scrapers = _g.sent(),
13080
+ _e.script = [
12684
13081
  /*new JavascriptExecutionTools(options)*/
12685
13082
  ],
12686
- _d);
13083
+ _e);
12687
13084
  return [4 /*yield*/, glob__default["default"](filesGlob, { ignore: ignore })];
12688
- case 3:
12689
- filenames = _f.sent();
12690
- _f.label = 4;
12691
13085
  case 4:
12692
- _f.trys.push([4, 16, 17, 18]);
12693
- filenames_1 = __values(filenames), filenames_1_1 = filenames_1.next();
12694
- _f.label = 5;
13086
+ filenames = _g.sent();
13087
+ _g.label = 5;
12695
13088
  case 5:
12696
- if (!!filenames_1_1.done) return [3 /*break*/, 15];
12697
- filename = filenames_1_1.value;
12698
- _f.label = 6;
13089
+ _g.trys.push([5, 17, 18, 19]);
13090
+ filenames_1 = __values(filenames), filenames_1_1 = filenames_1.next();
13091
+ _g.label = 6;
12699
13092
  case 6:
12700
- _f.trys.push([6, 13, , 14]);
13093
+ if (!!filenames_1_1.done) return [3 /*break*/, 16];
13094
+ filename = filenames_1_1.value;
13095
+ _g.label = 7;
13096
+ case 7:
13097
+ _g.trys.push([7, 14, , 15]);
12701
13098
  pipeline = void 0;
12702
- if (!filename.endsWith('.book.md')) return [3 /*break*/, 9];
13099
+ if (!filename.endsWith('.book.md')) return [3 /*break*/, 10];
13100
+ _b = validatePipelineString;
12703
13101
  return [4 /*yield*/, promises.readFile(filename, 'utf-8')];
12704
- case 7:
12705
- pipelineMarkdown = (_f.sent());
12706
- return [4 /*yield*/, compilePipeline(pipelineMarkdown, tools)];
12707
13102
  case 8:
12708
- pipeline = _f.sent();
13103
+ pipelineMarkdown = _b.apply(void 0, [_g.sent()]);
13104
+ return [4 /*yield*/, compilePipeline(pipelineMarkdown, tools)];
13105
+ case 9:
13106
+ pipeline = _g.sent();
12709
13107
  if (isVerbose) {
12710
13108
  console.info(colors__default["default"].green("Parsed ".concat(filename)));
12711
13109
  }
12712
- _f.label = 9;
12713
- case 9:
12714
- if (!filename.endsWith('.book.json')) return [3 /*break*/, 11];
12715
- _c = (_b = JSON).parse;
12716
- return [4 /*yield*/, promises.readFile(filename, 'utf-8')];
13110
+ _g.label = 10;
12717
13111
  case 10:
12718
- pipeline = _c.apply(_b, [_f.sent()]);
12719
- return [3 /*break*/, 12];
13112
+ if (!filename.endsWith('.book.json')) return [3 /*break*/, 12];
13113
+ _d = (_c = JSON).parse;
13114
+ return [4 /*yield*/, promises.readFile(filename, 'utf-8')];
12720
13115
  case 11:
13116
+ pipeline = _d.apply(_c, [_g.sent()]);
13117
+ return [3 /*break*/, 13];
13118
+ case 12:
12721
13119
  if (isVerbose) {
12722
13120
  console.info(colors__default["default"].gray("Skipping ".concat(filename)));
12723
13121
  }
12724
- return [3 /*break*/, 14];
12725
- case 12:
13122
+ return [3 /*break*/, 15];
13123
+ case 13:
12726
13124
  validatePipeline(pipeline);
12727
13125
  console.info(colors__default["default"].green("Validated ".concat(filename)));
12728
- return [3 /*break*/, 14];
12729
- case 13:
12730
- error_1 = _f.sent();
13126
+ return [3 /*break*/, 15];
13127
+ case 14:
13128
+ error_1 = _g.sent();
12731
13129
  if (!(error_1 instanceof Error)) {
12732
13130
  throw error_1;
12733
13131
  }
@@ -12735,21 +13133,21 @@
12735
13133
  console.error(colors__default["default"].bgRed(error_1.name /* <- 11:11 */));
12736
13134
  console.error(colors__default["default"].red(error_1.stack || error_1.message));
12737
13135
  return [2 /*return*/, process.exit(1)];
12738
- case 14:
13136
+ case 15:
12739
13137
  filenames_1_1 = filenames_1.next();
12740
- return [3 /*break*/, 5];
12741
- case 15: return [3 /*break*/, 18];
12742
- case 16:
12743
- e_1_1 = _f.sent();
12744
- e_1 = { error: e_1_1 };
12745
- return [3 /*break*/, 18];
13138
+ return [3 /*break*/, 6];
13139
+ case 16: return [3 /*break*/, 19];
12746
13140
  case 17:
13141
+ e_1_1 = _g.sent();
13142
+ e_1 = { error: e_1_1 };
13143
+ return [3 /*break*/, 19];
13144
+ case 18:
12747
13145
  try {
12748
- if (filenames_1_1 && !filenames_1_1.done && (_e = filenames_1.return)) _e.call(filenames_1);
13146
+ if (filenames_1_1 && !filenames_1_1.done && (_f = filenames_1.return)) _f.call(filenames_1);
12749
13147
  }
12750
13148
  finally { if (e_1) throw e_1.error; }
12751
13149
  return [7 /*endfinally*/];
12752
- case 18:
13150
+ case 19:
12753
13151
  console.info(colors__default["default"].green("All pipelines are valid"));
12754
13152
  return [2 /*return*/, process.exit(0)];
12755
13153
  }
@@ -12830,6 +13228,7 @@
12830
13228
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available LLM tools
12831
13229
  *
12832
13230
  * @public exported from `@promptbook/core`
13231
+ * @public exported from `@promptbook/wizzard`
12833
13232
  * @public exported from `@promptbook/cli`
12834
13233
  */
12835
13234
  var _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register({
@@ -13151,12 +13550,12 @@
13151
13550
  output: computeUsage("$2.40 / 1M tokens"),
13152
13551
  },
13153
13552
  },
13154
- // TODO: [main] !!! Claude 1 and 2 has also completion versions - ask Hoagy
13553
+ // TODO: [main] !!3 Claude 1 and 2 has also completion versions - ask Hoagy
13155
13554
  ],
13156
13555
  });
13157
13556
  /**
13158
13557
  * Note: [🤖] Add models of new variant
13159
- * TODO: [🧠][main] !!! Add embedding models OR Anthropic has only chat+completion models?
13558
+ * TODO: [🧠][main] !!3 Add embedding models OR Anthropic has only chat+completion models?
13160
13559
  * TODO: [🧠] Some mechanism to propagate unsureness
13161
13560
  * TODO: [🧠][👮‍♀️] Put here more info like description, isVision, trainingDateCutoff, languages, strengths ( Top-level performance, intelligence, fluency, and understanding), contextWindow,...
13162
13561
  * TODO: [🎰] Some mechanism to auto-update available models
@@ -13531,8 +13930,8 @@
13531
13930
  className: 'AnthropicClaudeExecutionTools',
13532
13931
  });
13533
13932
  /**
13534
- * TODO: [🧠][main] !!!! Make anonymous this with all LLM providers
13535
- * TODO: [🧠][🧱][main] !!!! Maybe change all `new AnthropicClaudeExecutionTools` -> `createAnthropicClaudeExecutionTools` in manual
13933
+ * TODO: [🧠][main] !!4 Make anonymous this with all LLM providers
13934
+ * TODO: [🧠][🧱][main] !!4 Maybe change all `new AnthropicClaudeExecutionTools` -> `createAnthropicClaudeExecutionTools` in manual
13536
13935
  * TODO: [🧠] Maybe auto-detect usage in browser and determine default value of `isProxied`
13537
13936
  * TODO: [🦺] Is there some way how to put `packageName` and `className` on top and function definition on bottom?
13538
13937
  * TODO: [🎶] Naming "constructor" vs "creator" vs "factory"
@@ -13544,7 +13943,9 @@
13544
13943
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available LLM tools
13545
13944
  *
13546
13945
  * @public exported from `@promptbook/anthropic-claude`
13946
+ * @public exported from `@promptbook/wizzard`
13547
13947
  * @public exported from `@promptbook/cli`
13948
+ *
13548
13949
  */
13549
13950
  var _AnthropicClaudeRegistration = $llmToolsRegister.register(createAnthropicClaudeExecutionTools);
13550
13951
  /**
@@ -13558,6 +13959,7 @@
13558
13959
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available LLM tools
13559
13960
  *
13560
13961
  * @public exported from `@promptbook/core`
13962
+ * @public exported from `@promptbook/wizzard`
13561
13963
  * @public exported from `@promptbook/cli`
13562
13964
  */
13563
13965
  var _AzureOpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
@@ -13934,7 +14336,7 @@
13934
14336
  prompt: computeUsage("$5.00 / 1M tokens"),
13935
14337
  output: computeUsage("$15.00 / 1M tokens"),
13936
14338
  },
13937
- //TODO: [main] !!! Add gpt-4o-mini-2024-07-18 and all others to be up to date
14339
+ //TODO: [main] !!3 Add gpt-4o-mini-2024-07-18 and all others to be up to date
13938
14340
  },
13939
14341
  /**/
13940
14342
  /**/
@@ -14084,7 +14486,7 @@
14084
14486
  AzureOpenAiExecutionTools.prototype.listModels = function () {
14085
14487
  return __awaiter(this, void 0, void 0, function () {
14086
14488
  return __generator(this, function (_a) {
14087
- // TODO: [main] !!! Do here some filtering which models are really available as deployment
14489
+ // TODO: [main] !!3 Do here some filtering which models are really available as deployment
14088
14490
  // @see https://management.azure.com/subscriptions/subscriptionId/resourceGroups/resourceGroupName/providers/Microsoft.CognitiveServices/accounts/accountName/deployments?api-version=2023-05-01
14089
14491
  return [2 /*return*/, OPENAI_MODELS.map(function (_a) {
14090
14492
  var modelTitle = _a.modelTitle, modelName = _a.modelName, modelVariant = _a.modelVariant;
@@ -14365,6 +14767,7 @@
14365
14767
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available LLM tools
14366
14768
  *
14367
14769
  * @public exported from `@promptbook/azure-openai`
14770
+ * @public exported from `@promptbook/wizzard`
14368
14771
  * @public exported from `@promptbook/cli`
14369
14772
  */
14370
14773
  var _AzureOpenAiRegistration = $llmToolsRegister.register(createAzureOpenAiExecutionTools);
@@ -14373,21 +14776,13 @@
14373
14776
  * Note: [💞] Ignore a discrepancy between file name and entity name
14374
14777
  */
14375
14778
 
14376
- /**
14377
- * Detects if the code is running in jest environment
14378
- *
14379
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
14380
- *
14381
- * @public exported from `@promptbook/utils`
14382
- */
14383
- var $isRunningInJest = new Function("\n try {\n return process.env.JEST_WORKER_ID !== undefined;\n } catch (e) {\n return false;\n }\n");
14384
-
14385
14779
  /**
14386
14780
  * Registration of LLM provider metadata
14387
14781
  *
14388
14782
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available LLM tools
14389
14783
  *
14390
14784
  * @public exported from `@promptbook/core`
14785
+ * @public exported from `@promptbook/wizzard`
14391
14786
  * @public exported from `@promptbook/cli`
14392
14787
  */
14393
14788
  var _GoogleMetadataRegistration = $llmToolsMetadataRegister.register({
@@ -14646,6 +15041,7 @@
14646
15041
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available LLM tools
14647
15042
  *
14648
15043
  * @public exported from `@promptbook/google`
15044
+ * @public exported from `@promptbook/wizzard`
14649
15045
  * @public exported from `@promptbook/cli`
14650
15046
  */
14651
15047
  var _GoogleRegistration = $llmToolsRegister.register(createGoogleExecutionTools);
@@ -14660,6 +15056,7 @@
14660
15056
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available LLM tools
14661
15057
  *
14662
15058
  * @public exported from `@promptbook/core`
15059
+ * @public exported from `@promptbook/wizzard`
14663
15060
  * @public exported from `@promptbook/cli`
14664
15061
  */
14665
15062
  var _OpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
@@ -14698,6 +15095,7 @@
14698
15095
  * Note: [🏐] Configurations registrations are done in @@@ BUT constructor @@@
14699
15096
  *
14700
15097
  * @public exported from `@promptbook/core`
15098
+ * @public exported from `@promptbook/wizzard`
14701
15099
  * @public exported from `@promptbook/cli`
14702
15100
  */
14703
15101
  var _OpenAiAssistantMetadataRegistration = $llmToolsMetadataRegister.register({
@@ -14740,24 +15138,6 @@
14740
15138
  * Note: [💞] Ignore a discrepancy between file name and entity name
14741
15139
  */
14742
15140
 
14743
- /**
14744
- * Detects if the code is running in a browser environment in main thread (Not in a web worker)
14745
- *
14746
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
14747
- *
14748
- * @public exported from `@promptbook/utils`
14749
- */
14750
- var $isRunningInBrowser = new Function("\n try {\n return this === window;\n } catch (e) {\n return false;\n }\n");
14751
-
14752
- /**
14753
- * Detects if the code is running in a web worker
14754
- *
14755
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
14756
- *
14757
- * @public exported from `@promptbook/utils`
14758
- */
14759
- var $isRunningInWebWorker = new Function("\n try {\n if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {\n return true;\n } else {\n return false;\n }\n } catch (e) {\n return false;\n }\n");
14760
-
14761
15141
  /**
14762
15142
  * Computes the usage of the OpenAI API based on the response from OpenAI
14763
15143
  *
@@ -15260,7 +15640,7 @@
15260
15640
  assistant_id: this.assistantId,
15261
15641
  thread: {
15262
15642
  messages: [
15263
- // TODO: [🗯] !! Allow threads to be passed
15643
+ // TODO: [🗯] Allow threads to be passed
15264
15644
  { role: 'user', content: rawPromptContent },
15265
15645
  ],
15266
15646
  },
@@ -15363,7 +15743,7 @@
15363
15743
  * @public exported from `@promptbook/openai`
15364
15744
  */
15365
15745
  var createOpenAiAssistantExecutionTools = Object.assign(function (options) {
15366
- // TODO: [🧠][main] !!!! If browser, auto add `dangerouslyAllowBrowser`
15746
+ // TODO: [🧠][main] !!4 If browser, auto add `dangerouslyAllowBrowser`
15367
15747
  if (($isRunningInBrowser() || $isRunningInWebWorker()) && !options.dangerouslyAllowBrowser) {
15368
15748
  options = __assign(__assign({}, options), { dangerouslyAllowBrowser: true });
15369
15749
  }
@@ -15383,7 +15763,7 @@
15383
15763
  * @public exported from `@promptbook/openai`
15384
15764
  */
15385
15765
  var createOpenAiExecutionTools = Object.assign(function (options) {
15386
- // TODO: [🧠][main] !!!! If browser, auto add `dangerouslyAllowBrowser`
15766
+ // TODO: [🧠][main] !!4 If browser, auto add `dangerouslyAllowBrowser`
15387
15767
  if (($isRunningInBrowser() || $isRunningInWebWorker()) && !options.dangerouslyAllowBrowser) {
15388
15768
  options = __assign(__assign({}, options), { dangerouslyAllowBrowser: true });
15389
15769
  }
@@ -15403,6 +15783,7 @@
15403
15783
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available LLM tools
15404
15784
  *
15405
15785
  * @public exported from `@promptbook/openai`
15786
+ * @public exported from `@promptbook/wizzard`
15406
15787
  * @public exported from `@promptbook/cli`
15407
15788
  */
15408
15789
  var _OpenAiRegistration = $llmToolsRegister.register(createOpenAiExecutionTools);
@@ -15412,6 +15793,7 @@
15412
15793
  * Note: [🏐] Configurations registrations are done in @@@ BUT constructor @@@
15413
15794
  *
15414
15795
  * @public exported from `@promptbook/openai`
15796
+ * @public exported from `@promptbook/wizzard`
15415
15797
  * @public exported from `@promptbook/cli`
15416
15798
  */
15417
15799
  var _OpenAiAssistantRegistration = $llmToolsRegister.register(createOpenAiAssistantExecutionTools);
@@ -15665,6 +16047,7 @@
15665
16047
  mimeTypes: ['text/markdown', 'text/plain'],
15666
16048
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
15667
16049
  isAvilableInBrowser: true,
16050
+ // <- Note: [🌏] This is the only scraper which makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
15668
16051
  requiredExecutables: [],
15669
16052
  }); /* <- Note: [🤛] */
15670
16053
  /**
@@ -15673,6 +16056,7 @@
15673
16056
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
15674
16057
  *
15675
16058
  * @public exported from `@promptbook/core`
16059
+ * @public exported from `@promptbook/wizzard`
15676
16060
  * @public exported from `@promptbook/cli`
15677
16061
  */
15678
16062
  var _MarkdownScraperMetadataRegistration = $scrapersMetadataRegister.register(markdownScraperMetadata);
@@ -15758,12 +16142,12 @@
15758
16142
  outputParameters = result.outputParameters;
15759
16143
  knowledgePiecesRaw = outputParameters.knowledgePieces;
15760
16144
  knowledgeTextPieces = (knowledgePiecesRaw || '').split('\n---\n');
15761
- // <- TODO: [main] !! Smarter split and filter out empty pieces
16145
+ // <- TODO: [main] Smarter split and filter out empty pieces
15762
16146
  if (isVerbose) {
15763
16147
  console.info('knowledgeTextPieces:', knowledgeTextPieces);
15764
16148
  }
15765
16149
  return [4 /*yield*/, Promise.all(
15766
- // TODO: [🪂] !! Do not send all at once but in chunks
16150
+ // TODO: [🪂] Do not send all at once but in chunks
15767
16151
  knowledgeTextPieces.map(function (knowledgeTextPiece, i) { return __awaiter(_this, void 0, void 0, function () {
15768
16152
  var name, title, knowledgePieceContent, keywords, index, titleResult, _a, titleRaw, keywordsResult, _b, keywordsRaw, embeddingResult, error_1;
15769
16153
  return __generator(this, function (_c) {
@@ -15862,6 +16246,7 @@
15862
16246
  mimeTypes: ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
15863
16247
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
15864
16248
  isAvilableInBrowser: false,
16249
+ // <- Note: [🌏] Only `MarkdownScraper` makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
15865
16250
  requiredExecutables: ['Pandoc'],
15866
16251
  }); /* <- Note: [🤛] */
15867
16252
  /**
@@ -15870,6 +16255,7 @@
15870
16255
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
15871
16256
  *
15872
16257
  * @public exported from `@promptbook/core`
16258
+ * @public exported from `@promptbook/wizzard`
15873
16259
  * @public exported from `@promptbook/cli`
15874
16260
  */
15875
16261
  var _DocumentScraperMetadataRegistration = $scrapersMetadataRegister.register(documentScraperMetadata);
@@ -16025,6 +16411,7 @@
16025
16411
  mimeTypes: ['application/msword', 'text/rtf'],
16026
16412
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
16027
16413
  isAvilableInBrowser: false,
16414
+ // <- Note: [🌏] Only `MarkdownScraper` makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
16028
16415
  requiredExecutables: [
16029
16416
  'Pandoc',
16030
16417
  'LibreOffice',
@@ -16037,6 +16424,7 @@
16037
16424
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
16038
16425
  *
16039
16426
  * @public exported from `@promptbook/core`
16427
+ * @public exported from `@promptbook/wizzard`
16040
16428
  * @public exported from `@promptbook/cli`
16041
16429
  */
16042
16430
  var _LegacyDocumentScraperMetadataRegistration = $scrapersMetadataRegister.register(legacyDocumentScraperMetadata);
@@ -16207,6 +16595,7 @@
16207
16595
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
16208
16596
  *
16209
16597
  * @public exported from `@promptbook/legacy-documents`
16598
+ * @public exported from `@promptbook/wizzard`
16210
16599
  * @public exported from `@promptbook/cli`
16211
16600
  */
16212
16601
  var _LegacyDocumentScraperRegistration = $scrapersRegister.register(createLegacyDocumentScraper);
@@ -16233,6 +16622,7 @@
16233
16622
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
16234
16623
  *
16235
16624
  * @public exported from `@promptbook/documents`
16625
+ * @public exported from `@promptbook/wizzard`
16236
16626
  * @public exported from `@promptbook/cli`
16237
16627
  */
16238
16628
  var _DocumentScraperRegistration = $scrapersRegister.register(createDocumentScraper);
@@ -16259,6 +16649,7 @@
16259
16649
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
16260
16650
  *
16261
16651
  * @public exported from `@promptbook/markdown-utils`
16652
+ * @public exported from `@promptbook/wizzard`
16262
16653
  * @public exported from `@promptbook/cli`
16263
16654
  */
16264
16655
  var _MarkdownScraperRegistration = $scrapersRegister.register(createMarkdownScraper);
@@ -16278,7 +16669,8 @@
16278
16669
  className: 'PdfScraper',
16279
16670
  mimeTypes: ['application/pdf'],
16280
16671
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
16281
- isAvilableInBrowser: true,
16672
+ isAvilableInBrowser: false,
16673
+ // <- Note: [🌏] Only `MarkdownScraper` makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
16282
16674
  requiredExecutables: [],
16283
16675
  }); /* <- Note: [🤛] */
16284
16676
  /**
@@ -16287,6 +16679,7 @@
16287
16679
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
16288
16680
  *
16289
16681
  * @public exported from `@promptbook/core`
16682
+ * @public exported from `@promptbook/wizzard`
16290
16683
  * @public exported from `@promptbook/cli`
16291
16684
  */
16292
16685
  var _PdfScraperMetadataRegistration = $scrapersMetadataRegister.register(pdfScraperMetadata);
@@ -16351,6 +16744,7 @@
16351
16744
  * TODO: [👣] Converted pdf documents can act as cached items - there is no need to run conversion each time
16352
16745
  * TODO: [🪂] Do it in parallel 11:11
16353
16746
  * Note: No need to aggregate usage here, it is done by intercepting the llmTools
16747
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
16354
16748
  */
16355
16749
 
16356
16750
  /**
@@ -16371,6 +16765,7 @@
16371
16765
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
16372
16766
  *
16373
16767
  * @public exported from `@promptbook/pdf`
16768
+ * @public exported from `@promptbook/wizzard`
16374
16769
  * @public exported from `@promptbook/cli`
16375
16770
  */
16376
16771
  var _PdfScraperRegistration = $scrapersRegister.register(createPdfScraper);
@@ -16391,6 +16786,7 @@
16391
16786
  mimeTypes: ['text/html'],
16392
16787
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
16393
16788
  isAvilableInBrowser: false,
16789
+ // <- Note: [🌏] Only `MarkdownScraper` makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
16394
16790
  requiredExecutables: [],
16395
16791
  }); /* <- Note: [🤛] */
16396
16792
  /**
@@ -16399,6 +16795,7 @@
16399
16795
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
16400
16796
  *
16401
16797
  * @public exported from `@promptbook/core`
16798
+ * @public exported from `@promptbook/wizzard`
16402
16799
  * @public exported from `@promptbook/cli`
16403
16800
  */
16404
16801
  var _WebsiteScraperMetadataRegistration = $scrapersMetadataRegister.register(websiteScraperMetadata);
@@ -16572,6 +16969,7 @@
16572
16969
  * Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
16573
16970
  *
16574
16971
  * @public exported from `@promptbook/website-crawler`
16972
+ * @public exported from `@promptbook/wizzard`
16575
16973
  * @public exported from `@promptbook/cli`
16576
16974
  */
16577
16975
  var _WebsiteScraperRegistration = $scrapersRegister.register(createWebsiteScraper);