@promptbook/node 0.72.0-6 → 0.72.0-8

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 (120) hide show
  1. package/README.md +6 -0
  2. package/esm/index.es.js +2427 -1361
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/src/_packages/browser.index.d.ts +1 -1
  5. package/esm/typings/src/_packages/core.index.d.ts +22 -10
  6. package/esm/typings/src/_packages/node.index.d.ts +6 -2
  7. package/esm/typings/src/_packages/types.index.d.ts +28 -20
  8. package/esm/typings/src/cli/cli-commands/about.d.ts +1 -1
  9. package/esm/typings/src/cli/cli-commands/hello.d.ts +2 -1
  10. package/esm/typings/src/cli/cli-commands/make.d.ts +1 -1
  11. package/esm/typings/src/cli/cli-commands/prettify.d.ts +2 -1
  12. package/esm/typings/src/cli/cli-commands/test-command.d.ts +13 -0
  13. package/esm/typings/src/cli/main.d.ts +1 -1
  14. package/esm/typings/src/cli/promptbookCli.d.ts +1 -1
  15. package/esm/typings/src/collection/constructors/createCollectionFromDirectory.d.ts +8 -5
  16. package/esm/typings/src/commands/EXPECT/expectCommandParser.d.ts +1 -1
  17. package/esm/typings/src/commands/FOREACH/foreachCommandParser.d.ts +1 -1
  18. package/esm/typings/src/commands/FORMAT/formatCommandParser.d.ts +1 -1
  19. package/esm/typings/src/commands/JOKER/jokerCommandParser.d.ts +1 -1
  20. package/esm/typings/src/commands/KNOWLEDGE/knowledgeCommandParser.d.ts +1 -1
  21. package/esm/typings/src/commands/KNOWLEDGE/utils/sourceContentToName.d.ts +11 -0
  22. package/esm/typings/src/commands/KNOWLEDGE/utils/sourceContentToName.test.d.ts +4 -0
  23. package/esm/typings/src/commands/MODEL/modelCommandParser.d.ts +1 -1
  24. package/esm/typings/src/commands/PARAMETER/parameterCommandParser.d.ts +1 -1
  25. package/esm/typings/src/commands/PERSONA/personaCommandParser.d.ts +1 -1
  26. package/esm/typings/src/commands/POSTPROCESS/postprocessCommandParser.d.ts +1 -1
  27. package/esm/typings/src/commands/PROMPTBOOK_VERSION/promptbookVersionCommandParser.d.ts +1 -1
  28. package/esm/typings/src/commands/TEMPLATE/templateCommandParser.d.ts +1 -1
  29. package/esm/typings/src/commands/URL/urlCommandParser.d.ts +1 -1
  30. package/esm/typings/src/commands/X_ACTION/actionCommandParser.d.ts +1 -1
  31. package/esm/typings/src/commands/X_INSTRUMENT/instrumentCommandParser.d.ts +1 -1
  32. package/esm/typings/src/commands/_BOILERPLATE/boilerplateCommandParser.d.ts +1 -1
  33. package/esm/typings/src/config.d.ts +10 -0
  34. package/esm/typings/src/conversion/pipelineStringToJson.d.ts +2 -15
  35. package/esm/typings/src/conversion/validation/_importPipeline.d.ts +1 -1
  36. package/esm/typings/src/conversion/validation/validatePipeline.d.ts +5 -5
  37. package/esm/typings/src/{knowledge/dialogs → dialogs}/callback/CallbackInterfaceTools.d.ts +2 -2
  38. package/esm/typings/src/{knowledge/dialogs → dialogs}/callback/CallbackInterfaceToolsOptions.d.ts +2 -2
  39. package/esm/typings/src/{knowledge/dialogs → dialogs}/simple-prompt/SimplePromptInterfaceTools.d.ts +4 -4
  40. package/esm/typings/src/errors/KnowledgeScrapeError.d.ts +9 -0
  41. package/esm/typings/src/errors/MissingToolsError.d.ts +9 -0
  42. package/esm/typings/src/execution/ExecutionTools.d.ts +3 -3
  43. package/esm/typings/src/execution/createPipelineExecutor/00-CreatePipelineExecutorOptions.d.ts +5 -2
  44. package/esm/typings/src/execution/createPipelineExecutor/00-CreatePipelineExecutorSettings.d.ts +2 -13
  45. package/esm/typings/src/execution/createPipelineExecutor/00-createPipelineExecutor.d.ts +1 -1
  46. package/esm/typings/src/execution/createPipelineExecutor/10-executePipeline.d.ts +1 -1
  47. package/esm/typings/src/execution/translation/automatic-translate/translateMessages.d.ts +3 -0
  48. package/esm/typings/src/execution/utils/forEachAsync.d.ts +1 -0
  49. package/esm/typings/src/llm-providers/_common/createLlmToolsFromConfigurationFromEnv.d.ts +1 -1
  50. package/esm/typings/src/llm-providers/_common/createLlmToolsFromEnv.d.ts +1 -1
  51. package/esm/typings/src/llm-providers/_common/getLlmToolsForCli.d.ts +1 -1
  52. package/esm/typings/src/llm-providers/anthropic-claude/playground/playground.d.ts +1 -0
  53. package/esm/typings/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +6 -0
  54. package/esm/typings/src/llm-providers/azure-openai/playground/playground.d.ts +1 -0
  55. package/esm/typings/src/llm-providers/langtail/playground/playground.d.ts +3 -0
  56. package/esm/typings/src/llm-providers/multiple/playground/playground.d.ts +3 -0
  57. package/esm/typings/src/llm-providers/openai/playground/playground.d.ts +1 -0
  58. package/esm/typings/src/llm-providers/remote/playground/playground.d.ts +3 -0
  59. package/esm/typings/src/personas/preparePersona.d.ts +2 -2
  60. package/esm/typings/src/prepare/PrepareAndScrapeOptions.d.ts +67 -0
  61. package/esm/typings/src/prepare/preparePipeline.d.ts +2 -2
  62. package/esm/typings/src/prepare/prepareTemplates.d.ts +2 -2
  63. package/esm/typings/src/scrapers/_common/Converter.d.ts +28 -0
  64. package/esm/typings/src/scrapers/_common/Scraper.d.ts +71 -0
  65. package/esm/typings/src/scrapers/_common/ScraperIntermediateSource.d.ts +11 -0
  66. package/esm/typings/src/{knowledge/prepare-knowledge → scrapers}/_common/prepareKnowledgePieces.d.ts +4 -4
  67. package/esm/typings/src/scrapers/_common/utils/getScraperIntermediateSource.d.ts +33 -0
  68. package/esm/typings/src/scrapers/_common/utils/getScraperIntermediateSource.test.d.ts +4 -0
  69. package/esm/typings/src/scrapers/_common/utils/makeKnowledgeSourceHandler.d.ts +10 -0
  70. package/esm/typings/src/scrapers/document/documentScraper.d.ts +37 -0
  71. package/esm/typings/src/scrapers/document/documentScraper.test.d.ts +4 -0
  72. package/esm/typings/src/scrapers/document/playground/document-scraper-playground.d.ts +5 -0
  73. package/esm/typings/src/scrapers/document-legacy/legacyDocumentScraper.d.ts +37 -0
  74. package/esm/typings/src/scrapers/document-legacy/legacyDocumentScraper.test.d.ts +4 -0
  75. package/esm/typings/src/scrapers/document-legacy/playground/legacy-document-scraper-playground.d.ts +5 -0
  76. package/esm/typings/src/scrapers/index.d.ts +8 -0
  77. package/esm/typings/src/scrapers/markdown/markdownScraper.d.ts +29 -0
  78. package/esm/typings/src/scrapers/markdown/playground/markdown-scraper-playground.d.ts +5 -0
  79. package/esm/typings/src/scrapers/pdf/pdfScraper.d.ts +35 -0
  80. package/esm/typings/src/scrapers/pdf/playground/pdf-scraper-playground.d.ts +5 -0
  81. package/esm/typings/src/scrapers/website/playground/website-scraper-playground.d.ts +5 -0
  82. package/esm/typings/src/scrapers/website/utils/markdownConverter.d.ts +12 -0
  83. package/esm/typings/src/scrapers/website/websiteScraper.d.ts +43 -0
  84. package/esm/typings/src/storage/{files-storage/FilesStorage.d.ts → file-cache-storage/FileCacheStorage.d.ts} +5 -5
  85. package/esm/typings/src/storage/file-cache-storage/FileCacheStorageOptions.d.ts +10 -0
  86. package/esm/typings/src/storage/{files-storage → file-cache-storage}/utils/nameToSubfolderPath.d.ts +1 -1
  87. package/esm/typings/src/storage/file-cache-storage/utils/nameToSubfolderPath.test.d.ts +1 -0
  88. package/esm/typings/src/storage/local-storage/getLocalStorage.d.ts +1 -1
  89. package/esm/typings/src/storage/local-storage/getSessionStorage.d.ts +1 -1
  90. package/esm/typings/src/types/PipelineJson/KnowledgeSourceJson.d.ts +9 -2
  91. package/esm/typings/src/types/PipelineJson/PipelineJson.d.ts +2 -2
  92. package/esm/typings/src/types/typeAliases.d.ts +8 -11
  93. package/esm/typings/src/utils/execCommand/$execCommand.d.ts +14 -0
  94. package/esm/typings/src/utils/execCommand/$execCommands.d.ts +17 -0
  95. package/esm/typings/src/utils/execCommand/IExecCommandOptions.d.ts +23 -0
  96. package/esm/typings/src/utils/execCommand/execCommand.test.d.ts +1 -0
  97. package/esm/typings/src/utils/execCommand/execCommandNormalizeOptions.d.ts +10 -0
  98. package/esm/typings/src/utils/execCommand/execCommandNormalizeOptions.test.d.ts +1 -0
  99. package/esm/typings/src/utils/files/$isDirectoryExisting.d.ts +3 -3
  100. package/esm/typings/src/utils/files/$isFileExisting.d.ts +3 -3
  101. package/esm/typings/src/utils/files/$listAllFiles.d.ts +5 -4
  102. package/esm/typings/src/utils/files/extensionToMimeType.d.ts +8 -0
  103. package/esm/typings/src/utils/files/extensionToMimeType.test.d.ts +1 -0
  104. package/esm/typings/src/utils/files/getFileExtension.d.ts +8 -0
  105. package/esm/typings/src/utils/files/getFileExtension.test.d.ts +1 -0
  106. package/esm/typings/src/utils/validators/filePath/isValidFilePath.d.ts +2 -2
  107. package/package.json +6 -2
  108. package/umd/index.umd.js +2428 -1365
  109. package/umd/index.umd.js.map +1 -1
  110. package/esm/typings/src/knowledge/prepare-knowledge/_common/Scraper.d.ts +0 -37
  111. package/esm/typings/src/knowledge/prepare-knowledge/markdown/playground/markdown-knowledge-playground.d.ts +0 -2
  112. package/esm/typings/src/knowledge/prepare-knowledge/markdown/prepareKnowledgeFromMarkdown.d.ts +0 -14
  113. package/esm/typings/src/knowledge/prepare-knowledge/pdf/prepareKnowledgeFromPdf.d.ts +0 -15
  114. package/esm/typings/src/prepare/PrepareOptions.d.ts +0 -22
  115. package/esm/typings/src/storage/files-storage/FilesStorageOptions.d.ts +0 -10
  116. /package/esm/typings/src/{knowledge/dialogs → dialogs}/user-interface-execution-tools.test.d.ts +0 -0
  117. /package/esm/typings/src/{knowledge/prepare-knowledge → scrapers}/_common/prepareKnowledgePieces.test.d.ts +0 -0
  118. /package/esm/typings/src/{knowledge/prepare-knowledge/markdown/prepareKnowledgeFromMarkdown.test.d.ts → scrapers/markdown/markdownScraper.test.d.ts} +0 -0
  119. /package/esm/typings/src/{knowledge/prepare-knowledge/pdf/prepareKnowledgeFromPdf.test.d.ts → scrapers/website/utils/markdownConverter.test.d.ts} +0 -0
  120. /package/esm/typings/src/{storage/files-storage/utils/nameToSubfolderPath.test.d.ts → scrapers/website/websiteScraper.test.d.ts} +0 -0
package/umd/index.umd.js CHANGED
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('fs/promises'), require('path'), require('spacetrim'), require('prettier'), require('prettier/parser-html'), require('waitasecond'), require('papaparse'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('path/posix'), require('dotenv')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'colors', 'fs/promises', 'path', 'spacetrim', 'prettier', 'prettier/parser-html', 'waitasecond', 'papaparse', 'crypto-js/enc-hex', 'crypto-js/sha256', 'path/posix', 'dotenv'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-node"] = {}, global.colors, global.promises, global.path, global.spaceTrim, global.prettier, global.parserHtml, global.waitasecond, global.papaparse, global.hexEncoder, global.sha256, global.posix, global.dotenv));
5
- })(this, (function (exports, colors, promises, path, spaceTrim, prettier, parserHtml, waitasecond, papaparse, hexEncoder, sha256, posix, dotenv) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('fs/promises'), require('path'), require('spacetrim'), require('prettier'), require('prettier/parser-html'), require('child_process'), require('waitasecond'), require('crypto-js'), require('crypto-js/enc-hex'), require('papaparse'), require('@mozilla/readability'), require('jsdom'), require('showdown'), require('mime-types'), require('dotenv'), require('crypto-js/sha256')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'colors', 'fs/promises', 'path', 'spacetrim', 'prettier', 'prettier/parser-html', 'child_process', 'waitasecond', 'crypto-js', 'crypto-js/enc-hex', 'papaparse', '@mozilla/readability', 'jsdom', 'showdown', 'mime-types', 'dotenv', 'crypto-js/sha256'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-node"] = {}, global.colors, global.promises, global.path, global.spaceTrim, global.prettier, global.parserHtml, global.child_process, global.waitasecond, global.cryptoJs, global.hexEncoder, global.papaparse, global.readability, global.jsdom, global.showdown, global.mimeTypes, global.dotenv, global.sha256));
5
+ })(this, (function (exports, colors, promises, path, spaceTrim, prettier, parserHtml, child_process, waitasecond, cryptoJs, hexEncoder, papaparse, readability, jsdom, showdown, mimeTypes, dotenv, sha256) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
@@ -28,14 +28,14 @@
28
28
  var spaceTrim__default = /*#__PURE__*/_interopDefaultLegacy(spaceTrim);
29
29
  var parserHtml__default = /*#__PURE__*/_interopDefaultLegacy(parserHtml);
30
30
  var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
31
- var sha256__default = /*#__PURE__*/_interopDefaultLegacy(sha256);
32
31
  var dotenv__namespace = /*#__PURE__*/_interopNamespace(dotenv);
32
+ var sha256__default = /*#__PURE__*/_interopDefaultLegacy(sha256);
33
33
 
34
34
  // ⚠️ WARNING: This code has been generated so that any manual changes will be overwritten
35
35
  /**
36
36
  * The version of the Promptbook library
37
37
  */
38
- var PROMPTBOOK_VERSION = '0.72.0-5';
38
+ var PROMPTBOOK_VERSION = '0.72.0-7';
39
39
  // TODO: [main] !!!! List here all the versions and annotate + put into script
40
40
 
41
41
  /*! *****************************************************************************
@@ -401,6 +401,14 @@
401
401
  * @public exported from `@promptbook/core`
402
402
  */
403
403
  var MAX_FILENAME_LENGTH = 30;
404
+ /**
405
+ * Where to store the scrape cache
406
+ *
407
+ * Note: When the folder does not exist, it is created recursively
408
+ *
409
+ * @public exported from `@promptbook/core`
410
+ */
411
+ var SCRAPE_CACHE_DIRNAME = '/.promptbook/scrape-cache';
404
412
  /**
405
413
  * The name of the builded pipeline collection made by CLI `ptbk make` and for lookup in `createCollectionFromDirectory`
406
414
  *
@@ -720,6 +728,22 @@
720
728
  * TODO: [🧠] Should be in generated .ptbk.md file GENERATOR_WARNING
721
729
  */
722
730
 
731
+ /**
732
+ * This error indicates that the promptbook can not retrieve knowledge from external sources
733
+ *
734
+ * @public exported from `@promptbook/core`
735
+ */
736
+ var KnowledgeScrapeError = /** @class */ (function (_super) {
737
+ __extends(KnowledgeScrapeError, _super);
738
+ function KnowledgeScrapeError(message) {
739
+ var _this = _super.call(this, message) || this;
740
+ _this.name = 'KnowledgeScrapeError';
741
+ Object.setPrototypeOf(_this, KnowledgeScrapeError.prototype);
742
+ return _this;
743
+ }
744
+ return KnowledgeScrapeError;
745
+ }(Error));
746
+
723
747
  /**
724
748
  * Async version of Array.forEach
725
749
  *
@@ -727,6 +751,7 @@
727
751
  * @param options - Options for the function
728
752
  * @param callbackfunction - Function to call for each item
729
753
  * @public exported from `@promptbook/utils`
754
+ * @deprecated [🪂] Use queues instead
730
755
  */
731
756
  function forEachAsync(array, options, callbackfunction) {
732
757
  return __awaiter(this, void 0, void 0, function () {
@@ -796,1105 +821,1399 @@
796
821
  }
797
822
 
798
823
  /**
799
- * Represents the usage with no resources consumed
800
- *
801
- * @public exported from `@promptbook/core`
802
- */
803
- var ZERO_USAGE = $deepFreeze({
804
- price: { value: 0 },
805
- input: {
806
- tokensCount: { value: 0 },
807
- charactersCount: { value: 0 },
808
- wordsCount: { value: 0 },
809
- sentencesCount: { value: 0 },
810
- linesCount: { value: 0 },
811
- paragraphsCount: { value: 0 },
812
- pagesCount: { value: 0 },
813
- },
814
- output: {
815
- tokensCount: { value: 0 },
816
- charactersCount: { value: 0 },
817
- wordsCount: { value: 0 },
818
- sentencesCount: { value: 0 },
819
- linesCount: { value: 0 },
820
- paragraphsCount: { value: 0 },
821
- pagesCount: { value: 0 },
822
- },
823
- });
824
- /**
825
- * Represents the usage with unknown resources consumed
826
- *
827
- * @public exported from `@promptbook/core`
828
- */
829
- $deepFreeze({
830
- price: { value: 0, isUncertain: true },
831
- input: {
832
- tokensCount: { value: 0, isUncertain: true },
833
- charactersCount: { value: 0, isUncertain: true },
834
- wordsCount: { value: 0, isUncertain: true },
835
- sentencesCount: { value: 0, isUncertain: true },
836
- linesCount: { value: 0, isUncertain: true },
837
- paragraphsCount: { value: 0, isUncertain: true },
838
- pagesCount: { value: 0, isUncertain: true },
839
- },
840
- output: {
841
- tokensCount: { value: 0, isUncertain: true },
842
- charactersCount: { value: 0, isUncertain: true },
843
- wordsCount: { value: 0, isUncertain: true },
844
- sentencesCount: { value: 0, isUncertain: true },
845
- linesCount: { value: 0, isUncertain: true },
846
- paragraphsCount: { value: 0, isUncertain: true },
847
- pagesCount: { value: 0, isUncertain: true },
848
- },
849
- });
850
-
851
- var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.ptbk.md",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],templates:[{templateType:"PROMPT_TEMPLATE",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"]}],knowledgeSources:[],knowledgePieces:[],personas:[],preparations:[],sourceFile:"./promptbook-collection/prepare-knowledge-from-markdown.ptbk.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.ptbk.md",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],templates:[{templateType:"PROMPT_TEMPLATE",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"]}],knowledgeSources:[],knowledgePieces:[],personas:[],preparations:[],sourceFile:"./promptbook-collection/prepare-knowledge-keywords.ptbk.md"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.ptbk.md",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],templates:[{templateType:"PROMPT_TEMPLATE",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"]}],knowledgeSources:[],knowledgePieces:[],personas:[],preparations:[],sourceFile:"./promptbook-collection/prepare-knowledge-title.ptbk.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.ptbk.md",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}],templates:[{templateType:"PROMPT_TEMPLATE",name:"make-model-requirements",title:"Make modelRequirements",content:"You are experienced AI engineer, you need to create virtual assistant.\nWrite\n\n## Sample\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"]}],knowledgeSources:[],knowledgePieces:[],personas:[],preparations:[],sourceFile:"./promptbook-collection/prepare-persona.ptbk.md"}];
852
-
853
- /**
854
- * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
824
+ * This error type indicates that some tools are missing for pipeline execution or preparation
855
825
  *
856
826
  * @public exported from `@promptbook/core`
857
827
  */
858
- var ParseError = /** @class */ (function (_super) {
859
- __extends(ParseError, _super);
860
- function ParseError(message) {
861
- var _this = _super.call(this, message) || this;
862
- _this.name = 'ParseError';
863
- Object.setPrototypeOf(_this, ParseError.prototype);
828
+ var MissingToolsError = /** @class */ (function (_super) {
829
+ __extends(MissingToolsError, _super);
830
+ function MissingToolsError(message) {
831
+ 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;
832
+ _this.name = 'MissingToolsError';
833
+ Object.setPrototypeOf(_this, MissingToolsError.prototype);
864
834
  return _this;
865
835
  }
866
- return ParseError;
836
+ return MissingToolsError;
867
837
  }(Error));
838
+
868
839
  /**
869
- * TODO: Maybe split `ParseError` and `ApplyError`
840
+ * Detects if the code is running in a Node.js environment
841
+ *
842
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
843
+ *
844
+ * @public exported from `@promptbook/utils`
870
845
  */
846
+ var $isRunningInNode = new Function("\n try {\n return this === global;\n } catch (e) {\n return false;\n }\n");
871
847
 
872
848
  /**
873
- * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
849
+ * This error type indicates that you try to use a feature that is not available in the current environment
874
850
  *
875
851
  * @public exported from `@promptbook/core`
876
852
  */
877
- var PipelineLogicError = /** @class */ (function (_super) {
878
- __extends(PipelineLogicError, _super);
879
- function PipelineLogicError(message) {
853
+ var EnvironmentMismatchError = /** @class */ (function (_super) {
854
+ __extends(EnvironmentMismatchError, _super);
855
+ function EnvironmentMismatchError(message) {
880
856
  var _this = _super.call(this, message) || this;
881
- _this.name = 'PipelineLogicError';
882
- Object.setPrototypeOf(_this, PipelineLogicError.prototype);
857
+ _this.name = 'EnvironmentMismatchError';
858
+ Object.setPrototypeOf(_this, EnvironmentMismatchError.prototype);
883
859
  return _this;
884
860
  }
885
- return PipelineLogicError;
861
+ return EnvironmentMismatchError;
886
862
  }(Error));
887
863
 
888
864
  /**
889
- * Tests if given string is valid semantic version
890
- *
891
- * Note: There are two simmilar functions:
892
- * - `isValidSemanticVersion` which tests any semantic version
893
- * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
865
+ * Normalize options for `execCommand` and `execCommands`
894
866
  *
895
- * @public exported from `@promptbook/utils`
867
+ * @private internal utility of `execCommand` and `execCommands`
896
868
  */
897
- function isValidSemanticVersion(version) {
898
- if (typeof version !== 'string') {
899
- return false;
869
+ function execCommandNormalizeOptions(options) {
870
+ var _a;
871
+ var _b, _c, _d;
872
+ var command;
873
+ var cwd;
874
+ var crashOnError;
875
+ var args = [];
876
+ var timeout;
877
+ if (typeof options === 'string') {
878
+ // TODO: [1] DRY default values
879
+ command = options;
880
+ cwd = process.cwd();
881
+ crashOnError = true;
882
+ timeout = Infinity;
900
883
  }
901
- if (version.startsWith('0.0.0')) {
902
- return false;
884
+ else {
885
+ /*
886
+ TODO:
887
+ if ((options as any).commands !== undefined) {
888
+ commands = (options as any).commands;
889
+ } else {
890
+ commands = [(options as any).command];
891
+ }
892
+ */
893
+ // TODO: [1] DRY default values
894
+ command = options.command;
895
+ cwd = (_b = options.cwd) !== null && _b !== void 0 ? _b : process.cwd();
896
+ crashOnError = (_c = options.crashOnError) !== null && _c !== void 0 ? _c : true;
897
+ timeout = (_d = options.timeout) !== null && _d !== void 0 ? _d : Infinity;
898
+ }
899
+ // TODO: /(-[a-zA-Z0-9-]+\s+[^\s]*)|[^\s]*/g
900
+ var _ = Array.from(command.matchAll(/(".*")|([^\s]*)/g))
901
+ .map(function (_a) {
902
+ var _b = __read(_a, 1), match = _b[0];
903
+ return match;
904
+ })
905
+ .filter(function (arg) { return arg !== ''; });
906
+ if (_.length > 1) {
907
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
908
+ _a = __read(_), command = _a[0], args = _a.slice(1);
903
909
  }
904
- return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
905
- }
906
-
907
- /**
908
- * Tests if given string is valid promptbook version
909
- * It looks into list of known promptbook versions.
910
- *
911
- * @see https://www.npmjs.com/package/promptbook?activeTab=versions
912
- * Note: When you are using for example promptbook 2.0.0 and there already is promptbook 3.0.0 it don`t know about it.
913
- * Note: There are two simmilar functions:
914
- * - `isValidSemanticVersion` which tests any semantic version
915
- * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
916
- *
917
- * @public exported from `@promptbook/utils`
918
- */
919
- function isValidPromptbookVersion(version) {
920
- if (!isValidSemanticVersion(version)) {
921
- return false;
910
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
911
+ if (options.args) {
912
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
913
+ args = __spreadArray(__spreadArray([], __read(args), false), __read(options.args), false);
922
914
  }
923
- if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
924
- return false;
915
+ var humanReadableCommand = !['npx', 'npm'].includes(command) ? command : args[0];
916
+ if (['ts-node'].includes(humanReadableCommand)) {
917
+ humanReadableCommand += " ".concat(args[1]);
925
918
  }
926
- // <- TODO: [main] !!! Check isValidPromptbookVersion against PROMPTBOOK_VERSIONS
927
- return true;
919
+ return { command: command, humanReadableCommand: humanReadableCommand, args: args, cwd: cwd, crashOnError: crashOnError, timeout: timeout };
928
920
  }
921
+ // TODO: This should show type error> execCommandNormalizeOptions({ command: '', commands: [''] });
929
922
 
930
923
  /**
931
- * Checks if an URL is reserved for private networks or localhost.
924
+ * Run one command in a shell
932
925
  *
933
- * Note: There are two simmilar functions:
934
- * - `isUrlOnPrivateNetwork` which tests full URL
935
- * - `isHostnameOnPrivateNetwork` *(this one)* which tests just hostname
926
+ * Note: There are 2 similar functions in the codebase:
927
+ * - `$execCommand` which runs a single command
928
+ * - `$execCommands` which runs multiple commands
936
929
  *
937
- * @public exported from `@promptbook/utils`
930
+ * @public exported from `@promptbook/node`
938
931
  */
939
- function isHostnameOnPrivateNetwork(hostname) {
940
- if (hostname === 'example.com' ||
941
- hostname === 'localhost' ||
942
- hostname.endsWith('.localhost') ||
943
- hostname.endsWith('.local') ||
944
- hostname.endsWith('.test') ||
945
- hostname === '127.0.0.1' ||
946
- hostname === '::1') {
947
- return true;
948
- }
949
- if (hostname.includes(':')) {
950
- // IPv6
951
- var ipParts = hostname.split(':');
952
- return ipParts[0] === 'fc00' || ipParts[0] === 'fd00' || ipParts[0] === 'fe80';
953
- }
954
- else {
955
- // IPv4
956
- var ipParts = hostname.split('.').map(function (part) { return Number.parseInt(part, 10); });
957
- return (ipParts[0] === 10 ||
958
- (ipParts[0] === 172 && ipParts[1] >= 16 && ipParts[1] <= 31) ||
959
- (ipParts[0] === 192 && ipParts[1] === 168));
960
- }
932
+ function $execCommand(options) {
933
+ if (!$isRunningInNode()) {
934
+ throw new EnvironmentMismatchError('Function `$execCommand` can run only in Node environment.js');
935
+ }
936
+ return new Promise(
937
+ // <- TODO: [🧱] Implement in a functional (not new Class) way
938
+ function (resolve, reject) {
939
+ // eslint-disable-next-line prefer-const
940
+ var _a = execCommandNormalizeOptions(options), command = _a.command, humanReadableCommand = _a.humanReadableCommand, args = _a.args, cwd = _a.cwd, crashOnError = _a.crashOnError, timeout = _a.timeout;
941
+ if (timeout !== Infinity) {
942
+ // TODO: In waitasecond forTime(Infinity) should be equivalent to forEver()
943
+ waitasecond.forTime(timeout).then(function () {
944
+ if (crashOnError) {
945
+ reject(new Error("Command \"".concat(humanReadableCommand, "\" exceeded time limit of ").concat(timeout, "ms")));
946
+ }
947
+ else {
948
+ console.warn("Command \"".concat(humanReadableCommand, "\" exceeded time limit of ").concat(timeout, "ms but continues running"));
949
+ resolve('Command exceeded time limit');
950
+ }
951
+ });
952
+ }
953
+ if (/^win/.test(process.platform) && ['npm', 'npx'].includes(command)) {
954
+ command = "".concat(command, ".cmd");
955
+ }
956
+ // !!!!!! Verbose mode - to all consoles
957
+ console.info(colors__default["default"].yellow(cwd) + ' ' + colors__default["default"].green(command) + ' ' + colors__default["default"].blue(args.join(' ')));
958
+ try {
959
+ var commandProcess = child_process.spawn(command, args, { cwd: cwd, shell: true });
960
+ commandProcess.on('message', function (message) {
961
+ console.info({ message: message });
962
+ });
963
+ var output_1 = [];
964
+ commandProcess.stdout.on('data', function (stdout) {
965
+ output_1.push(stdout.toString());
966
+ console.info(stdout.toString());
967
+ });
968
+ commandProcess.stderr.on('data', function (stderr) {
969
+ output_1.push(stderr.toString());
970
+ if (stderr.toString().trim()) {
971
+ console.warn(stderr.toString());
972
+ }
973
+ });
974
+ var finishWithCode = function (code) {
975
+ if (code !== 0) {
976
+ if (crashOnError) {
977
+ reject(new Error(output_1.join('\n').trim() ||
978
+ "Command \"".concat(humanReadableCommand, "\" exited with code ").concat(code)));
979
+ }
980
+ else {
981
+ console.warn("Command \"".concat(humanReadableCommand, "\" exited with code ").concat(code));
982
+ resolve(spaceTrim.spaceTrim(output_1.join('\n')));
983
+ }
984
+ }
985
+ else {
986
+ resolve(spaceTrim.spaceTrim(output_1.join('\n')));
987
+ }
988
+ };
989
+ commandProcess.on('close', finishWithCode);
990
+ commandProcess.on('exit', finishWithCode);
991
+ commandProcess.on('disconnect', function () {
992
+ // Note: Unexpected disconnection should always result in rejection
993
+ reject(new Error("Command \"".concat(humanReadableCommand, "\" disconnected")));
994
+ });
995
+ commandProcess.on('error', function (error) {
996
+ if (crashOnError) {
997
+ reject(new Error("Command \"".concat(humanReadableCommand, "\" failed: \n").concat(error.message)));
998
+ }
999
+ else {
1000
+ console.warn(error);
1001
+ resolve(spaceTrim.spaceTrim(output_1.join('\n')));
1002
+ }
1003
+ });
1004
+ }
1005
+ catch (error) {
1006
+ // Note: Unexpected error in sync code should always result in rejection
1007
+ reject(error);
1008
+ }
1009
+ });
961
1010
  }
1011
+ /**
1012
+ * Note: [🟢 <- TODO: [🦖] !!!!!! Split scrapers into packages and enable] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
1013
+ */
962
1014
 
963
1015
  /**
964
- * Checks if an IP address or hostname is reserved for private networks or localhost.
1016
+ * Checks if the file exists
965
1017
  *
966
- * Note: There are two simmilar functions:
967
- * - `isUrlOnPrivateNetwork` *(this one)* which tests full URL
968
- * - `isHostnameOnPrivateNetwork` which tests just hostname
1018
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the filesystem
969
1019
  *
970
- * @param {string} ipAddress - The IP address to check.
971
- * @returns {boolean} Returns true if the IP address is reserved for private networks or localhost, otherwise false.
972
- * @public exported from `@promptbook/utils`
1020
+ * @private within the repository
973
1021
  */
974
- function isUrlOnPrivateNetwork(url) {
975
- if (typeof url === 'string') {
976
- url = new URL(url);
977
- }
978
- return isHostnameOnPrivateNetwork(url.hostname);
1022
+ function $isFileExisting(filename) {
1023
+ return __awaiter(this, void 0, void 0, function () {
1024
+ var isReadAccessAllowed, isFile;
1025
+ return __generator(this, function (_a) {
1026
+ switch (_a.label) {
1027
+ case 0:
1028
+ if (!$isRunningInNode()) {
1029
+ throw new EnvironmentMismatchError('Function `$isFileExisting` works only in Node environment.js');
1030
+ }
1031
+ return [4 /*yield*/, promises.access(filename, promises.constants.R_OK)
1032
+ .then(function () { return true; })
1033
+ .catch(function () { return false; })];
1034
+ case 1:
1035
+ isReadAccessAllowed = _a.sent();
1036
+ if (!isReadAccessAllowed) {
1037
+ return [2 /*return*/, false];
1038
+ }
1039
+ return [4 /*yield*/, promises.stat(filename)
1040
+ .then(function (fileStat) { return fileStat.isFile(); })
1041
+ .catch(function () { return false; })];
1042
+ case 2:
1043
+ isFile = _a.sent();
1044
+ return [2 /*return*/, isFile];
1045
+ }
1046
+ });
1047
+ });
979
1048
  }
1049
+ /**
1050
+ * Note: [🟢 <- TODO: [🦖] !!!!!! Split scrapers into packages and enable] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
1051
+ * TODO: [🐠] This can be a validator - with variants that return true/false and variants that throw errors with meaningless messages
1052
+ * TODO: [🖇] What about symlinks?
1053
+ */
980
1054
 
981
1055
  /**
982
- * Tests if given string is valid URL.
983
- *
984
- * Note: Dataurl are considered perfectly valid.
985
- * Note: There are two simmilar functions:
986
- * - `isValidUrl` which tests any URL
987
- * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
1056
+ * Get the file extension from a file name
988
1057
  *
989
- * @public exported from `@promptbook/utils`
1058
+ * @private within the repository
990
1059
  */
991
- function isValidUrl(url) {
992
- if (typeof url !== 'string') {
993
- return false;
994
- }
995
- try {
996
- if (url.startsWith('blob:')) {
997
- url = url.replace(/^blob:/, '');
998
- }
999
- var urlObject = new URL(url /* because fail is handled */);
1000
- if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
1001
- return false;
1002
- }
1003
- return true;
1004
- }
1005
- catch (error) {
1006
- return false;
1007
- }
1060
+ function getFileExtension(value) {
1061
+ var match = value.match(/\.([0-9a-z]+)(?:[?#]|$)/i);
1062
+ return match ? match[1].toLowerCase() : null;
1008
1063
  }
1009
1064
 
1065
+ var defaultDiacriticsRemovalMap = [
1066
+ {
1067
+ base: 'A',
1068
+ letters: '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F',
1069
+ },
1070
+ { base: 'AA', letters: '\uA732' },
1071
+ { base: 'AE', letters: '\u00C6\u01FC\u01E2' },
1072
+ { base: 'AO', letters: '\uA734' },
1073
+ { base: 'AU', letters: '\uA736' },
1074
+ { base: 'AV', letters: '\uA738\uA73A' },
1075
+ { base: 'AY', letters: '\uA73C' },
1076
+ {
1077
+ base: 'B',
1078
+ letters: '\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181',
1079
+ },
1080
+ {
1081
+ base: 'C',
1082
+ letters: '\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E',
1083
+ },
1084
+ {
1085
+ base: 'D',
1086
+ letters: '\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779\u00D0',
1087
+ },
1088
+ { base: 'DZ', letters: '\u01F1\u01C4' },
1089
+ { base: 'Dz', letters: '\u01F2\u01C5' },
1090
+ {
1091
+ base: 'E',
1092
+ letters: '\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E',
1093
+ },
1094
+ { base: 'F', letters: '\u0046\u24BB\uFF26\u1E1E\u0191\uA77B' },
1095
+ {
1096
+ base: 'G',
1097
+ letters: '\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E',
1098
+ },
1099
+ {
1100
+ base: 'H',
1101
+ letters: '\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D',
1102
+ },
1103
+ {
1104
+ base: 'I',
1105
+ letters: '\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197',
1106
+ },
1107
+ { base: 'J', letters: '\u004A\u24BF\uFF2A\u0134\u0248' },
1108
+ {
1109
+ base: 'K',
1110
+ letters: '\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2',
1111
+ },
1112
+ {
1113
+ base: 'L',
1114
+ letters: '\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780',
1115
+ },
1116
+ { base: 'LJ', letters: '\u01C7' },
1117
+ { base: 'Lj', letters: '\u01C8' },
1118
+ { base: 'M', letters: '\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C' },
1119
+ {
1120
+ base: 'N',
1121
+ letters: '\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4',
1122
+ },
1123
+ { base: 'NJ', letters: '\u01CA' },
1124
+ { base: 'Nj', letters: '\u01CB' },
1125
+ {
1126
+ base: 'O',
1127
+ letters: '\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C',
1128
+ },
1129
+ { base: 'OI', letters: '\u01A2' },
1130
+ { base: 'OO', letters: '\uA74E' },
1131
+ { base: 'OU', letters: '\u0222' },
1132
+ { base: 'OE', letters: '\u008C\u0152' },
1133
+ { base: 'oe', letters: '\u009C\u0153' },
1134
+ {
1135
+ base: 'P',
1136
+ letters: '\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754',
1137
+ },
1138
+ { base: 'Q', letters: '\u0051\u24C6\uFF31\uA756\uA758\u024A' },
1139
+ {
1140
+ base: 'R',
1141
+ letters: '\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782',
1142
+ },
1143
+ {
1144
+ base: 'S',
1145
+ letters: '\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784',
1146
+ },
1147
+ {
1148
+ base: 'T',
1149
+ letters: '\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786',
1150
+ },
1151
+ { base: 'TZ', letters: '\uA728' },
1152
+ {
1153
+ base: 'U',
1154
+ letters: '\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244',
1155
+ },
1156
+ { base: 'V', letters: '\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245' },
1157
+ { base: 'VY', letters: '\uA760' },
1158
+ {
1159
+ base: 'W',
1160
+ letters: '\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72',
1161
+ },
1162
+ { base: 'X', letters: '\u0058\u24CD\uFF38\u1E8A\u1E8C' },
1163
+ {
1164
+ base: 'Y',
1165
+ letters: '\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE',
1166
+ },
1167
+ {
1168
+ base: 'Z',
1169
+ letters: '\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762',
1170
+ },
1171
+ {
1172
+ base: 'a',
1173
+ letters: '\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250',
1174
+ },
1175
+ { base: 'aa', letters: '\uA733' },
1176
+ { base: 'ae', letters: '\u00E6\u01FD\u01E3' },
1177
+ { base: 'ao', letters: '\uA735' },
1178
+ { base: 'au', letters: '\uA737' },
1179
+ { base: 'av', letters: '\uA739\uA73B' },
1180
+ { base: 'ay', letters: '\uA73D' },
1181
+ {
1182
+ base: 'b',
1183
+ letters: '\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253',
1184
+ },
1185
+ {
1186
+ base: 'c',
1187
+ letters: '\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184',
1188
+ },
1189
+ {
1190
+ base: 'd',
1191
+ letters: '\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A',
1192
+ },
1193
+ { base: 'dz', letters: '\u01F3\u01C6' },
1194
+ {
1195
+ base: 'e',
1196
+ letters: '\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD',
1197
+ },
1198
+ { base: 'f', letters: '\u0066\u24D5\uFF46\u1E1F\u0192\uA77C' },
1199
+ {
1200
+ base: 'g',
1201
+ letters: '\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F',
1202
+ },
1203
+ {
1204
+ base: 'h',
1205
+ letters: '\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265',
1206
+ },
1207
+ { base: 'hv', letters: '\u0195' },
1208
+ {
1209
+ base: 'i',
1210
+ letters: '\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131',
1211
+ },
1212
+ { base: 'j', letters: '\u006A\u24D9\uFF4A\u0135\u01F0\u0249' },
1213
+ {
1214
+ base: 'k',
1215
+ letters: '\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3',
1216
+ },
1217
+ {
1218
+ base: 'l',
1219
+ letters: '\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747',
1220
+ },
1221
+ { base: 'lj', letters: '\u01C9' },
1222
+ { base: 'm', letters: '\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F' },
1223
+ {
1224
+ base: 'n',
1225
+ letters: '\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5',
1226
+ },
1227
+ { base: 'nj', letters: '\u01CC' },
1228
+ {
1229
+ base: 'o',
1230
+ letters: '\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275',
1231
+ },
1232
+ { base: 'oi', letters: '\u01A3' },
1233
+ { base: 'ou', letters: '\u0223' },
1234
+ { base: 'oo', letters: '\uA74F' },
1235
+ {
1236
+ base: 'p',
1237
+ letters: '\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755',
1238
+ },
1239
+ { base: 'q', letters: '\u0071\u24E0\uFF51\u024B\uA757\uA759' },
1240
+ {
1241
+ base: 'r',
1242
+ letters: '\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783',
1243
+ },
1244
+ {
1245
+ base: 's',
1246
+ letters: '\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B',
1247
+ },
1248
+ {
1249
+ base: 't',
1250
+ letters: '\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787',
1251
+ },
1252
+ { base: 'tz', letters: '\uA729' },
1253
+ {
1254
+ base: 'u',
1255
+ letters: '\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289',
1256
+ },
1257
+ { base: 'v', letters: '\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C' },
1258
+ { base: 'vy', letters: '\uA761' },
1259
+ {
1260
+ base: 'w',
1261
+ letters: '\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73',
1262
+ },
1263
+ { base: 'x', letters: '\u0078\u24E7\uFF58\u1E8B\u1E8D' },
1264
+ {
1265
+ base: 'y',
1266
+ letters: '\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF',
1267
+ },
1268
+ {
1269
+ base: 'z',
1270
+ letters: '\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763',
1271
+ },
1272
+ ];
1010
1273
  /**
1011
- * Tests if given string is valid pipeline URL URL.
1274
+ * Map of letters from diacritic variant to diacritless variant
1275
+ * Contains lowercase and uppercase separatelly
1012
1276
  *
1013
- * Note: There are two simmilar functions:
1014
- * - `isValidUrl` which tests any URL
1015
- * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
1277
+ * > "á" => "a"
1278
+ * > "ě" => "e"
1279
+ * > "Ă" => "A"
1280
+ * > ...
1016
1281
  *
1017
1282
  * @public exported from `@promptbook/utils`
1018
1283
  */
1019
- function isValidPipelineUrl(url) {
1020
- if (!isValidUrl(url)) {
1021
- return false;
1022
- }
1023
- if (!url.startsWith('https://')) {
1024
- return false;
1025
- }
1026
- if (!url.endsWith('.ptbk.md')) {
1027
- return false;
1028
- }
1029
- if (url.includes('#')) {
1030
- // TODO: [🐠]
1031
- return false;
1032
- }
1033
- if (isUrlOnPrivateNetwork(url)) {
1034
- return false;
1284
+ var DIACRITIC_VARIANTS_LETTERS = {};
1285
+ // tslint:disable-next-line: prefer-for-of
1286
+ for (var i = 0; i < defaultDiacriticsRemovalMap.length; i++) {
1287
+ var letters = defaultDiacriticsRemovalMap[i].letters;
1288
+ // tslint:disable-next-line: prefer-for-of
1289
+ for (var j = 0; j < letters.length; j++) {
1290
+ DIACRITIC_VARIANTS_LETTERS[letters[j]] = defaultDiacriticsRemovalMap[i].base;
1035
1291
  }
1036
- return true;
1037
1292
  }
1038
- /**
1039
- * TODO: [🐠] Maybe more info why the URL is invalid
1040
- */
1041
-
1042
- /**
1043
- * Validates PipelineJson if it is logically valid
1044
- *
1045
- * It checks:
1046
- * - if it has correct parameters dependency
1047
- *
1048
- * It does NOT check:
1049
- * - if it is valid json
1050
- * - if it is meaningful
1293
+ // <- TODO: [🍓] Put to maker function to save execution time if not needed
1294
+ /*
1295
+ @see https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript
1296
+ Licensed under the Apache License, Version 2.0 (the "License");
1297
+ you may not use this file except in compliance with the License.
1298
+ You may obtain a copy of the License at
1299
+
1300
+ http://www.apache.org/licenses/LICENSE-2.0
1301
+
1302
+ Unless required by applicable law or agreed to in writing, software
1303
+ distributed under the License is distributed on an "AS IS" BASIS,
1304
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1305
+ See the License for the specific language governing permissions and
1306
+ limitations under the License.
1307
+ */
1308
+
1309
+ /**
1310
+ * @@@
1051
1311
  *
1052
- * @param pipeline valid or invalid PipelineJson
1053
- * @returns the same pipeline if it is logically valid
1054
- * @throws {PipelineLogicError} on logical error in the pipeline
1055
- * @public exported from `@promptbook/core`
1312
+ * @param input @@@
1313
+ * @returns @@@
1314
+ * @public exported from `@promptbook/utils`
1056
1315
  */
1057
- function validatePipeline(pipeline) {
1058
- if (IS_PIPELINE_LOGIC_VALIDATED) {
1059
- validatePipelineCore(pipeline);
1060
- }
1061
- else {
1062
- try {
1063
- validatePipelineCore(pipeline);
1064
- }
1065
- catch (error) {
1066
- if (!(error instanceof PipelineLogicError)) {
1067
- throw error;
1068
- }
1069
- console.error(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is not valid but logic errors are temporarily disabled via `IS_PIPELINE_LOGIC_VALIDATED`\n\n ".concat(block(error.message), "\n "); }));
1070
- }
1071
- }
1072
- return pipeline;
1316
+ function removeDiacritics(input) {
1317
+ /*eslint no-control-regex: "off"*/
1318
+ return input.replace(/[^\u0000-\u007E]/g, function (a) {
1319
+ return DIACRITIC_VARIANTS_LETTERS[a] || a;
1320
+ });
1073
1321
  }
1074
1322
  /**
1075
- * @private internal function for `validatePipeline`
1323
+ * TODO: [Ж] Variant for cyrillic (and in general non-latin) letters
1076
1324
  */
1077
- function validatePipelineCore(pipeline) {
1078
- // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
1079
- var e_1, _a, e_2, _b, e_3, _c;
1080
- var pipelineIdentification = (function () {
1081
- // Note: This is a 😐 implementation of [🚞]
1082
- var _ = [];
1083
- if (pipeline.sourceFile !== undefined) {
1084
- _.push("File: ".concat(pipeline.sourceFile));
1085
- }
1086
- if (pipeline.pipelineUrl !== undefined) {
1087
- _.push("Url: ".concat(pipeline.pipelineUrl));
1088
- }
1089
- return _.join('\n');
1090
- })();
1091
- if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
1092
- // <- Note: [🚲]
1093
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid promptbook URL \"".concat(pipeline.pipelineUrl, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
1094
- }
1095
- if (pipeline.promptbookVersion !== undefined && !isValidPromptbookVersion(pipeline.promptbookVersion)) {
1096
- // <- Note: [🚲]
1097
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid Promptbook Version \"".concat(pipeline.promptbookVersion, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
1098
- }
1099
- // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
1100
- if (!Array.isArray(pipeline.parameters)) {
1101
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
1102
- throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.parameters` expected to be an array, but got ".concat(typeof pipeline.parameters, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
1103
- }
1104
- // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
1105
- if (!Array.isArray(pipeline.templates)) {
1106
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
1107
- throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.templates` expected to be an array, but got ".concat(typeof pipeline.templates, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
1108
- }
1109
- var _loop_1 = function (parameter) {
1110
- if (parameter.isInput && parameter.isOutput) {
1111
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n\n Parameter {".concat(parameter.name, "} can not be both input and output\n\n ").concat(block(pipelineIdentification), "\n "); }));
1112
- }
1113
- // Note: Testing that parameter is either intermediate or output BUT not created and unused
1114
- if (!parameter.isInput &&
1115
- !parameter.isOutput &&
1116
- !pipeline.templates.some(function (template) { return template.dependentParameterNames.includes(parameter.name); })) {
1117
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter {".concat(parameter.name, "} is created but not used\n\n You can declare {").concat(parameter.name, "} as output parameter by adding in the header:\n - OUTPUT PARAMETER `{").concat(parameter.name, "}` ").concat(parameter.description || '', "\n\n ").concat(block(pipelineIdentification), "\n\n "); }));
1118
- }
1119
- // Note: Testing that parameter is either input or result of some template
1120
- if (!parameter.isInput &&
1121
- !pipeline.templates.some(function (template) { return template.resultingParameterName === parameter.name; })) {
1122
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter {".concat(parameter.name, "} is declared but not defined\n\n You can do one of these:\n 1) Remove declaration of {").concat(parameter.name, "}\n 2) Add template that results in -> {").concat(parameter.name, "}\n\n ").concat(block(pipelineIdentification), "\n "); }));
1123
- }
1124
- };
1325
+
1326
+ /**
1327
+ * @@@
1328
+ *
1329
+ * @param text @@@
1330
+ * @returns @@@
1331
+ * @example 'hello-world'
1332
+ * @example 'i-love-promptbook'
1333
+ * @public exported from `@promptbook/utils`
1334
+ */
1335
+ function normalizeToKebabCase(text) {
1336
+ var e_1, _a;
1337
+ text = removeDiacritics(text);
1338
+ var charType;
1339
+ var lastCharType = 'OTHER';
1340
+ var normalizedName = '';
1125
1341
  try {
1126
- // Note: Check each parameter individually
1127
- for (var _d = __values(pipeline.parameters), _e = _d.next(); !_e.done; _e = _d.next()) {
1128
- var parameter = _e.value;
1129
- _loop_1(parameter);
1342
+ for (var text_1 = __values(text), text_1_1 = text_1.next(); !text_1_1.done; text_1_1 = text_1.next()) {
1343
+ var char = text_1_1.value;
1344
+ var normalizedChar = void 0;
1345
+ if (/^[a-z]$/.test(char)) {
1346
+ charType = 'LOWERCASE';
1347
+ normalizedChar = char;
1348
+ }
1349
+ else if (/^[A-Z]$/.test(char)) {
1350
+ charType = 'UPPERCASE';
1351
+ normalizedChar = char.toLowerCase();
1352
+ }
1353
+ else if (/^[0-9]$/.test(char)) {
1354
+ charType = 'NUMBER';
1355
+ normalizedChar = char;
1356
+ }
1357
+ else {
1358
+ charType = 'OTHER';
1359
+ normalizedChar = '-';
1360
+ }
1361
+ if (charType !== lastCharType &&
1362
+ !(lastCharType === 'UPPERCASE' && charType === 'LOWERCASE') &&
1363
+ !(lastCharType === 'NUMBER') &&
1364
+ !(charType === 'NUMBER')) {
1365
+ normalizedName += '-';
1366
+ }
1367
+ normalizedName += normalizedChar;
1368
+ lastCharType = charType;
1130
1369
  }
1131
1370
  }
1132
1371
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
1133
1372
  finally {
1134
1373
  try {
1135
- if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
1374
+ if (text_1_1 && !text_1_1.done && (_a = text_1.return)) _a.call(text_1);
1136
1375
  }
1137
1376
  finally { if (e_1) throw e_1.error; }
1138
1377
  }
1139
- // Note: All input parameters are defined - so that they can be used as result of some template
1140
- var definedParameters = new Set(pipeline.parameters.filter(function (_a) {
1141
- var isInput = _a.isInput;
1142
- return isInput;
1143
- }).map(function (_a) {
1144
- var name = _a.name;
1145
- return name;
1146
- }));
1147
- var _loop_2 = function (template) {
1148
- var e_4, _h, e_5, _j;
1149
- if (definedParameters.has(template.resultingParameterName)) {
1150
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter {".concat(template.resultingParameterName, "} is defined multiple times\n\n ").concat(block(pipelineIdentification), "\n "); }));
1151
- }
1152
- if (RESERVED_PARAMETER_NAMES.includes(template.resultingParameterName)) {
1153
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter name {".concat(template.resultingParameterName, "} is reserved, please use different name\n\n ").concat(block(pipelineIdentification), "\n "); }));
1154
- }
1155
- definedParameters.add(template.resultingParameterName);
1156
- if (template.jokerParameterNames && template.jokerParameterNames.length > 0) {
1157
- if (!template.format &&
1158
- !template.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
1159
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Joker parameters are used for {".concat(template.resultingParameterName, "} but no expectations are defined\n\n ").concat(block(pipelineIdentification), "\n "); }));
1160
- }
1161
- var _loop_4 = function (joker) {
1162
- if (!template.dependentParameterNames.includes(joker)) {
1163
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter {".concat(joker, "} is used for {").concat(template.resultingParameterName, "} as joker but not in `dependentParameterNames`\n\n ").concat(block(pipelineIdentification), "\n "); }));
1164
- }
1165
- };
1166
- try {
1167
- for (var _k = (e_4 = void 0, __values(template.jokerParameterNames)), _l = _k.next(); !_l.done; _l = _k.next()) {
1168
- var joker = _l.value;
1169
- _loop_4(joker);
1170
- }
1171
- }
1172
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
1173
- finally {
1174
- try {
1175
- if (_l && !_l.done && (_h = _k.return)) _h.call(_k);
1176
- }
1177
- finally { if (e_4) throw e_4.error; }
1178
- }
1179
- }
1180
- if (template.expectations) {
1181
- var _loop_5 = function (unit, min, max) {
1182
- if (min !== undefined && max !== undefined && min > max) {
1183
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation (=".concat(min, ") of ").concat(unit, " is higher than max expectation (=").concat(max, ")\n\n ").concat(block(pipelineIdentification), "\n "); }));
1184
- }
1185
- if (min !== undefined && min < 0) {
1186
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation of ".concat(unit, " must be zero or positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
1187
- }
1188
- if (max !== undefined && max <= 0) {
1189
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Max expectation of ".concat(unit, " must be positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
1190
- }
1191
- };
1192
- try {
1193
- for (var _m = (e_5 = void 0, __values(Object.entries(template.expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
1194
- var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
1195
- _loop_5(unit, min, max);
1196
- }
1197
- }
1198
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
1199
- finally {
1200
- try {
1201
- if (_o && !_o.done && (_j = _m.return)) _j.call(_m);
1202
- }
1203
- finally { if (e_5) throw e_5.error; }
1204
- }
1205
- }
1206
- };
1378
+ normalizedName = normalizedName.split(/-+/g).join('-');
1379
+ normalizedName = normalizedName.split(/-?\/-?/g).join('/');
1380
+ normalizedName = normalizedName.replace(/^-/, '');
1381
+ normalizedName = normalizedName.replace(/-$/, '');
1382
+ return normalizedName;
1383
+ }
1384
+
1385
+ /**
1386
+ * Tests if given string is valid URL.
1387
+ *
1388
+ * Note: Dataurl are considered perfectly valid.
1389
+ * Note: There are two simmilar functions:
1390
+ * - `isValidUrl` which tests any URL
1391
+ * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
1392
+ *
1393
+ * @public exported from `@promptbook/utils`
1394
+ */
1395
+ function isValidUrl(url) {
1396
+ if (typeof url !== 'string') {
1397
+ return false;
1398
+ }
1207
1399
  try {
1208
- // Note: Checking each template individually
1209
- for (var _f = __values(pipeline.templates), _g = _f.next(); !_g.done; _g = _f.next()) {
1210
- var template = _g.value;
1211
- _loop_2(template);
1400
+ if (url.startsWith('blob:')) {
1401
+ url = url.replace(/^blob:/, '');
1212
1402
  }
1213
- }
1214
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
1215
- finally {
1216
- try {
1217
- if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
1403
+ var urlObject = new URL(url /* because fail is handled */);
1404
+ if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
1405
+ return false;
1218
1406
  }
1219
- finally { if (e_2) throw e_2.error; }
1407
+ return true;
1220
1408
  }
1221
- // Note: Detect circular dependencies
1222
- var resovedParameters = pipeline.parameters
1223
- .filter(function (_a) {
1224
- var isInput = _a.isInput;
1225
- return isInput;
1226
- })
1227
- .map(function (_a) {
1228
- var name = _a.name;
1229
- return name;
1230
- });
1231
- try {
1232
- // Note: All reserved parameters are resolved
1233
- for (var RESERVED_PARAMETER_NAMES_1 = __values(RESERVED_PARAMETER_NAMES), RESERVED_PARAMETER_NAMES_1_1 = RESERVED_PARAMETER_NAMES_1.next(); !RESERVED_PARAMETER_NAMES_1_1.done; RESERVED_PARAMETER_NAMES_1_1 = RESERVED_PARAMETER_NAMES_1.next()) {
1234
- var reservedParameterName = RESERVED_PARAMETER_NAMES_1_1.value;
1235
- resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), [reservedParameterName], false);
1236
- }
1409
+ catch (error) {
1410
+ return false;
1237
1411
  }
1238
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
1239
- finally {
1240
- try {
1241
- if (RESERVED_PARAMETER_NAMES_1_1 && !RESERVED_PARAMETER_NAMES_1_1.done && (_c = RESERVED_PARAMETER_NAMES_1.return)) _c.call(RESERVED_PARAMETER_NAMES_1);
1242
- }
1243
- finally { if (e_3) throw e_3.error; }
1412
+ }
1413
+
1414
+ /**
1415
+ * Removes emojis from a string and fix whitespaces
1416
+ *
1417
+ * @param text with emojis
1418
+ * @returns text without emojis
1419
+ * @public exported from `@promptbook/utils`
1420
+ */
1421
+ function removeEmojis(text) {
1422
+ // Replace emojis (and also ZWJ sequence) with hyphens
1423
+ text = text.replace(/(\p{Extended_Pictographic})\p{Modifier_Symbol}/gu, '$1');
1424
+ text = text.replace(/(\p{Extended_Pictographic})[\u{FE00}-\u{FE0F}]/gu, '$1');
1425
+ text = text.replace(/(\p{Extended_Pictographic})(\u{200D}\p{Extended_Pictographic})*/gu, '$1');
1426
+ text = text.replace(/\p{Extended_Pictographic}/gu, '');
1427
+ return text;
1428
+ }
1429
+
1430
+ /**
1431
+ * Tests if given string is valid URL.
1432
+ *
1433
+ * Note: This does not check if the file exists only if the path is valid
1434
+ * @public exported from `@promptbook/utils`
1435
+ */
1436
+ function isValidFilePath(filename) {
1437
+ if (typeof filename !== 'string') {
1438
+ return false;
1244
1439
  }
1245
- var unresovedTemplates = __spreadArray([], __read(pipeline.templates), false);
1246
- var loopLimit = LOOP_LIMIT;
1247
- var _loop_3 = function () {
1248
- if (loopLimit-- < 0) {
1249
- // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
1250
- throw new UnexpectedError(spaceTrim.spaceTrim(function (block) { return "\n Loop limit reached during detection of circular dependencies in `validatePipeline`\n\n ".concat(block(pipelineIdentification), "\n "); }));
1251
- }
1252
- var currentlyResovedTemplates = unresovedTemplates.filter(function (template) {
1253
- return template.dependentParameterNames.every(function (name) { return resovedParameters.includes(name); });
1254
- });
1255
- if (currentlyResovedTemplates.length === 0) {
1256
- throw new PipelineLogicError(
1257
- // TODO: [🐎] DRY
1258
- spaceTrim.spaceTrim(function (block) { return "\n\n Can not resolve some parameters:\n Either you are using a parameter that is not defined, or there are some circular dependencies.\n\n ".concat(block(pipelineIdentification), "\n\n Can not resolve:\n ").concat(block(unresovedTemplates
1259
- .map(function (_a) {
1260
- var resultingParameterName = _a.resultingParameterName, dependentParameterNames = _a.dependentParameterNames;
1261
- return "- Parameter {".concat(resultingParameterName, "} which depends on ").concat(dependentParameterNames
1262
- .map(function (dependentParameterName) { return "{".concat(dependentParameterName, "}"); })
1263
- .join(' and '));
1264
- })
1265
- .join('\n')), "\n\n Resolved:\n ").concat(block(resovedParameters.map(function (name) { return "- Parameter {".concat(name, "}"); }).join('\n')), "\n\n\n "); }));
1266
- }
1267
- resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), __read(currentlyResovedTemplates.map(function (_a) {
1268
- var resultingParameterName = _a.resultingParameterName;
1269
- return resultingParameterName;
1270
- })), false);
1271
- unresovedTemplates = unresovedTemplates.filter(function (template) { return !currentlyResovedTemplates.includes(template); });
1272
- };
1273
- while (unresovedTemplates.length > 0) {
1274
- _loop_3();
1440
+ var filenameSlashes = filename.split('\\').join('/');
1441
+ // Absolute Unix path: /hello.txt
1442
+ if (/^(\/)/i.test(filenameSlashes)) {
1443
+ return true;
1444
+ }
1445
+ // Absolute Windows path: /hello.txt
1446
+ if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
1447
+ return true;
1275
1448
  }
1449
+ // Relative path: ./hello.txt
1450
+ if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
1451
+ return true;
1452
+ }
1453
+ return false;
1276
1454
  }
1455
+
1277
1456
  /**
1278
- * TODO: !!!!! [🧞‍♀️] Do not allow joker + foreach
1279
- * TODO: [🧠] Work with promptbookVersion
1280
- * TODO: Use here some json-schema, Zod or something similar and change it to:
1281
- * > /**
1282
- * > * Validates PipelineJson if it is logically valid.
1283
- * > *
1284
- * > * It checks:
1285
- * > * - it has a valid structure
1286
- * > * - ...
1287
- * > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
1457
+ * @@@
1458
+ *
1459
+ * @param value @@@
1460
+ * @returns @@@
1461
+ * @example @@@
1462
+ * @public exported from `@promptbook/utils`
1288
1463
  */
1464
+ function titleToName(value) {
1465
+ if (isValidUrl(value)) {
1466
+ value = value.replace(/^https?:\/\//, '');
1467
+ value = value.replace(/\.html$/, '');
1468
+ }
1469
+ else if (isValidFilePath(value)) {
1470
+ value = path.basename(value);
1471
+ // Note: Keeping extension in the name
1472
+ }
1473
+ value = value.split('/').join('-');
1474
+ value = removeEmojis(value);
1475
+ value = normalizeToKebabCase(value);
1476
+ // TODO: [🧠] Maybe warn or add some padding to short name which are not good identifiers
1477
+ return value;
1478
+ }
1479
+
1289
1480
  /**
1290
- * TODO: [🐣][main] !!!! Validate that all samples match expectations
1291
- * TODO: [🐣][🐝][main] !!!! Validate that knowledge is valid (non-void)
1292
- * TODO: [🐣][main] !!!! Validate that persona can be used only with CHAT variant
1293
- * TODO: [🐣][main] !!!! Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
1294
- * TODO: [🐣][main] !!!! Validate that reserved parameter is not used as joker
1295
- * TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
1296
- * TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
1481
+ * @@@
1482
+ *
1483
+ * @private for `FileCacheStorage`
1297
1484
  */
1485
+ function nameToSubfolderPath(name) {
1486
+ return [name.substr(0, 1).toLowerCase(), name.substr(1, 1).toLowerCase()];
1487
+ }
1298
1488
 
1299
1489
  /**
1300
- * This error indicates that promptbook not found in the collection
1490
+ * Just marks a place of place where should be something implemented
1491
+ * No side effects.
1492
+ *
1493
+ * Note: It can be usefull suppressing eslint errors of unused variables
1494
+ *
1495
+ * @param value any values
1496
+ * @returns void
1497
+ * @private within the repository
1498
+ */
1499
+ function TODO_USE() {
1500
+ var value = [];
1501
+ for (var _i = 0; _i < arguments.length; _i++) {
1502
+ value[_i] = arguments[_i];
1503
+ }
1504
+ }
1505
+
1506
+ /**
1507
+ * Create a filename for intermediate cache for scrapers
1508
+ *
1509
+ * Note: It also checks if directory exists and creates it if not
1510
+ *
1511
+ * @private as internal utility for scrapers
1512
+ */
1513
+ function getScraperIntermediateSource(source, options) {
1514
+ return __awaiter(this, void 0, void 0, function () {
1515
+ var sourceFilename, url, rootDirname, cacheDirname, isCacheCleaned, extension, isVerbose, hash, semanticName, pieces, name, cacheFilename, isDestroyed, fileHandler;
1516
+ return __generator(this, function (_a) {
1517
+ switch (_a.label) {
1518
+ case 0:
1519
+ sourceFilename = source.filename, url = source.url;
1520
+ rootDirname = options.rootDirname, cacheDirname = options.cacheDirname, isCacheCleaned = options.isCacheCleaned, extension = options.extension, isVerbose = options.isVerbose;
1521
+ hash = cryptoJs.SHA256(
1522
+ // <- TODO: [🥬] Encapsulate sha256 to some private utility function
1523
+ hexEncoder__default["default"].parse(sourceFilename || url || 'untitled'))
1524
+ .toString( /* hex */)
1525
+ .substring(0, 20);
1526
+ semanticName = normalizeToKebabCase(titleToName((sourceFilename || url || '').split('intermediate').join(''))).substring(0, 20);
1527
+ pieces = ['intermediate', semanticName, hash].filter(function (piece) { return piece !== ''; });
1528
+ name = pieces.join('-').split('--').join('-');
1529
+ // <- TODO: Use MAX_FILENAME_LENGTH
1530
+ TODO_USE(rootDirname); // <- TODO: !!!!!!
1531
+ cacheFilename = path.join.apply(void 0, __spreadArray(__spreadArray([process.cwd(),
1532
+ cacheDirname], __read(nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */)), false), [name], false)).split('\\')
1533
+ .join('/') +
1534
+ '.' +
1535
+ extension;
1536
+ return [4 /*yield*/, promises.mkdir(path.dirname(cacheFilename), { recursive: true })];
1537
+ case 1:
1538
+ _a.sent();
1539
+ isDestroyed = true;
1540
+ fileHandler = {
1541
+ filename: cacheFilename,
1542
+ get isDestroyed() {
1543
+ return isDestroyed;
1544
+ },
1545
+ destroy: function () {
1546
+ return __awaiter(this, void 0, void 0, function () {
1547
+ return __generator(this, function (_a) {
1548
+ switch (_a.label) {
1549
+ case 0:
1550
+ if (!isCacheCleaned) return [3 /*break*/, 2];
1551
+ if (isVerbose) {
1552
+ console.info('legacyDocumentScraper: Clening cache');
1553
+ }
1554
+ return [4 /*yield*/, promises.rm(cacheFilename)];
1555
+ case 1:
1556
+ _a.sent();
1557
+ _a.label = 2;
1558
+ case 2:
1559
+ isDestroyed = true;
1560
+ return [2 /*return*/];
1561
+ }
1562
+ });
1563
+ });
1564
+ },
1565
+ };
1566
+ return [2 /*return*/, fileHandler];
1567
+ }
1568
+ });
1569
+ });
1570
+ }
1571
+ /**
1572
+ * Note: Not using `FileCacheStorage` for two reasons:
1573
+ * 1) Need to store more than serialized JSONs
1574
+ * 2) Need to switch between a `rootDirname` and `cacheDirname` <- TODO: !!!!
1575
+ * TODO: [🐱‍🐉][🧠] Make some smart crop
1576
+ */
1577
+
1578
+ var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.ptbk.md",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],templates:[{templateType:"PROMPT_TEMPLATE",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"]}],knowledgeSources:[],knowledgePieces:[],personas:[],preparations:[],sourceFile:"./promptbook-collection/prepare-knowledge-from-markdown.ptbk.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.ptbk.md",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],templates:[{templateType:"PROMPT_TEMPLATE",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"]}],knowledgeSources:[],knowledgePieces:[],personas:[],preparations:[],sourceFile:"./promptbook-collection/prepare-knowledge-keywords.ptbk.md"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.ptbk.md",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],templates:[{templateType:"PROMPT_TEMPLATE",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"]}],knowledgeSources:[],knowledgePieces:[],personas:[],preparations:[],sourceFile:"./promptbook-collection/prepare-knowledge-title.ptbk.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.ptbk.md",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}],templates:[{templateType:"PROMPT_TEMPLATE",name:"make-model-requirements",title:"Make modelRequirements",content:"You are experienced AI engineer, you need to create virtual assistant.\nWrite\n\n## Sample\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"]}],knowledgeSources:[],knowledgePieces:[],personas:[],preparations:[],sourceFile:"./promptbook-collection/prepare-persona.ptbk.md"}];
1579
+
1580
+ /**
1581
+ * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
1301
1582
  *
1302
1583
  * @public exported from `@promptbook/core`
1303
1584
  */
1304
- var NotFoundError = /** @class */ (function (_super) {
1305
- __extends(NotFoundError, _super);
1306
- function NotFoundError(message) {
1585
+ var ParseError = /** @class */ (function (_super) {
1586
+ __extends(ParseError, _super);
1587
+ function ParseError(message) {
1307
1588
  var _this = _super.call(this, message) || this;
1308
- _this.name = 'NotFoundError';
1309
- Object.setPrototypeOf(_this, NotFoundError.prototype);
1589
+ _this.name = 'ParseError';
1590
+ Object.setPrototypeOf(_this, ParseError.prototype);
1310
1591
  return _this;
1311
1592
  }
1312
- return NotFoundError;
1593
+ return ParseError;
1313
1594
  }(Error));
1595
+ /**
1596
+ * TODO: Maybe split `ParseError` and `ApplyError`
1597
+ */
1314
1598
 
1315
1599
  /**
1316
- * This error indicates errors in referencing promptbooks between each other
1600
+ * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
1317
1601
  *
1318
1602
  * @public exported from `@promptbook/core`
1319
1603
  */
1320
- var PipelineUrlError = /** @class */ (function (_super) {
1321
- __extends(PipelineUrlError, _super);
1322
- function PipelineUrlError(message) {
1604
+ var PipelineLogicError = /** @class */ (function (_super) {
1605
+ __extends(PipelineLogicError, _super);
1606
+ function PipelineLogicError(message) {
1323
1607
  var _this = _super.call(this, message) || this;
1324
- _this.name = 'PipelineUrlError';
1325
- Object.setPrototypeOf(_this, PipelineUrlError.prototype);
1608
+ _this.name = 'PipelineLogicError';
1609
+ Object.setPrototypeOf(_this, PipelineLogicError.prototype);
1326
1610
  return _this;
1327
1611
  }
1328
- return PipelineUrlError;
1612
+ return PipelineLogicError;
1329
1613
  }(Error));
1330
1614
 
1331
1615
  /**
1332
- * Parses the template and returns the list of all parameter names
1616
+ * Tests if given string is valid semantic version
1617
+ *
1618
+ * Note: There are two simmilar functions:
1619
+ * - `isValidSemanticVersion` which tests any semantic version
1620
+ * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
1333
1621
  *
1334
- * @param template the template with parameters in {curly} braces
1335
- * @returns the list of parameter names
1336
1622
  * @public exported from `@promptbook/utils`
1337
1623
  */
1338
- function extractParameterNames(template) {
1339
- var e_1, _a;
1340
- var matches = template.matchAll(/{\w+}/g);
1341
- var parameterNames = new Set();
1342
- try {
1343
- for (var matches_1 = __values(matches), matches_1_1 = matches_1.next(); !matches_1_1.done; matches_1_1 = matches_1.next()) {
1344
- var match = matches_1_1.value;
1345
- var parameterName = match[0].slice(1, -1);
1346
- parameterNames.add(parameterName);
1347
- }
1624
+ function isValidSemanticVersion(version) {
1625
+ if (typeof version !== 'string') {
1626
+ return false;
1348
1627
  }
1349
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
1350
- finally {
1351
- try {
1352
- if (matches_1_1 && !matches_1_1.done && (_a = matches_1.return)) _a.call(matches_1);
1353
- }
1354
- finally { if (e_1) throw e_1.error; }
1628
+ if (version.startsWith('0.0.0')) {
1629
+ return false;
1355
1630
  }
1356
- return parameterNames;
1631
+ return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
1357
1632
  }
1358
1633
 
1359
1634
  /**
1360
- * Unprepare just strips the preparation data of the pipeline
1635
+ * Tests if given string is valid promptbook version
1636
+ * It looks into list of known promptbook versions.
1361
1637
  *
1362
- * @public exported from `@promptbook/core`
1638
+ * @see https://www.npmjs.com/package/promptbook?activeTab=versions
1639
+ * Note: When you are using for example promptbook 2.0.0 and there already is promptbook 3.0.0 it don`t know about it.
1640
+ * Note: There are two simmilar functions:
1641
+ * - `isValidSemanticVersion` which tests any semantic version
1642
+ * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
1643
+ *
1644
+ * @public exported from `@promptbook/utils`
1363
1645
  */
1364
- function unpreparePipeline(pipeline) {
1365
- var personas = pipeline.personas, knowledgeSources = pipeline.knowledgeSources, templates = pipeline.templates;
1366
- personas = personas.map(function (persona) { return (__assign(__assign({}, persona), { modelRequirements: undefined, preparationIds: undefined })); });
1367
- knowledgeSources = knowledgeSources.map(function (knowledgeSource) { return (__assign(__assign({}, knowledgeSource), { preparationIds: undefined })); });
1368
- templates = templates.map(function (template) {
1369
- var dependentParameterNames = template.dependentParameterNames;
1370
- var parameterNames = extractParameterNames(template.preparedContent || '');
1371
- dependentParameterNames = dependentParameterNames.filter(function (dependentParameterName) { return !parameterNames.has(dependentParameterName); });
1372
- var templateUnprepared = __assign(__assign({}, template), { dependentParameterNames: dependentParameterNames });
1373
- delete templateUnprepared.preparedContent;
1374
- return templateUnprepared;
1375
- });
1376
- return $asDeeplyFrozenSerializableJson('Unprepared PipelineJson', __assign(__assign({}, pipeline), { templates: templates, knowledgeSources: knowledgeSources, knowledgePieces: [], personas: personas, preparations: [] }));
1646
+ function isValidPromptbookVersion(version) {
1647
+ if (!isValidSemanticVersion(version)) {
1648
+ return false;
1649
+ }
1650
+ if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
1651
+ return false;
1652
+ }
1653
+ // <- TODO: [main] !!! Check isValidPromptbookVersion against PROMPTBOOK_VERSIONS
1654
+ return true;
1377
1655
  }
1656
+
1378
1657
  /**
1379
- * TODO: [🧿] Maybe do same process with same granularity and subfinctions as `preparePipeline`
1380
- * TODO: Write tests for `preparePipeline`
1381
- * TODO: [🍙] Make some standard order of json properties
1658
+ * Checks if an URL is reserved for private networks or localhost.
1659
+ *
1660
+ * Note: There are two simmilar functions:
1661
+ * - `isUrlOnPrivateNetwork` which tests full URL
1662
+ * - `isHostnameOnPrivateNetwork` *(this one)* which tests just hostname
1663
+ *
1664
+ * @public exported from `@promptbook/utils`
1382
1665
  */
1666
+ function isHostnameOnPrivateNetwork(hostname) {
1667
+ if (hostname === 'example.com' ||
1668
+ hostname === 'localhost' ||
1669
+ hostname.endsWith('.localhost') ||
1670
+ hostname.endsWith('.local') ||
1671
+ hostname.endsWith('.test') ||
1672
+ hostname === '127.0.0.1' ||
1673
+ hostname === '::1') {
1674
+ return true;
1675
+ }
1676
+ if (hostname.includes(':')) {
1677
+ // IPv6
1678
+ var ipParts = hostname.split(':');
1679
+ return ipParts[0] === 'fc00' || ipParts[0] === 'fd00' || ipParts[0] === 'fe80';
1680
+ }
1681
+ else {
1682
+ // IPv4
1683
+ var ipParts = hostname.split('.').map(function (part) { return Number.parseInt(part, 10); });
1684
+ return (ipParts[0] === 10 ||
1685
+ (ipParts[0] === 172 && ipParts[1] >= 16 && ipParts[1] <= 31) ||
1686
+ (ipParts[0] === 192 && ipParts[1] === 168));
1687
+ }
1688
+ }
1383
1689
 
1384
1690
  /**
1385
- * Library of pipelines that groups together pipelines for an application.
1386
- * This implementation is a very thin wrapper around the Array / Map of pipelines.
1691
+ * Checks if an IP address or hostname is reserved for private networks or localhost.
1387
1692
  *
1388
- * @private internal function of `createCollectionFromJson`, use `createCollectionFromJson` instead
1389
- * @see https://github.com/webgptorg/pipeline#pipeline-collection
1693
+ * Note: There are two simmilar functions:
1694
+ * - `isUrlOnPrivateNetwork` *(this one)* which tests full URL
1695
+ * - `isHostnameOnPrivateNetwork` which tests just hostname
1696
+ *
1697
+ * @param {string} ipAddress - The IP address to check.
1698
+ * @returns {boolean} Returns true if the IP address is reserved for private networks or localhost, otherwise false.
1699
+ * @public exported from `@promptbook/utils`
1390
1700
  */
1391
- var SimplePipelineCollection = /** @class */ (function () {
1392
- /**
1393
- * Constructs a pipeline collection from pipelines
1394
- *
1395
- * @param pipelines @@@
1396
- *
1397
- * Note: During the construction logic of all pipelines are validated
1398
- * Note: It is not recommended to use this constructor directly, use `createCollectionFromJson` *(or other variant)* instead
1399
- */
1400
- function SimplePipelineCollection() {
1401
- var e_1, _a;
1402
- var pipelines = [];
1403
- for (var _i = 0; _i < arguments.length; _i++) {
1404
- pipelines[_i] = arguments[_i];
1405
- }
1406
- this.collection = new Map();
1407
- try {
1408
- for (var pipelines_1 = __values(pipelines), pipelines_1_1 = pipelines_1.next(); !pipelines_1_1.done; pipelines_1_1 = pipelines_1.next()) {
1409
- var pipeline = pipelines_1_1.value;
1410
- // TODO: [👠] DRY
1411
- if (pipeline.pipelineUrl === undefined) {
1412
- throw new PipelineUrlError(spaceTrim.spaceTrim("\n Pipeline with name \"".concat(pipeline.title, "\" does not have defined URL\n\n File:\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: Pipelines without URLs are called anonymous pipelines\n They can be used as standalone pipelines, but they cannot be referenced by other pipelines\n And also they cannot be used in the pipeline collection\n\n ")));
1413
- }
1414
- // Note: [🐨]
1415
- validatePipeline(pipeline);
1416
- // TODO: [🦄] DRY
1417
- // Note: [🦄]
1418
- if (
1419
- // TODO: [🐽]
1420
- this.collection.has(pipeline.pipelineUrl) &&
1421
- pipelineJsonToString(unpreparePipeline(pipeline)) !==
1422
- pipelineJsonToString(unpreparePipeline(this.collection.get(pipeline.pipelineUrl)))) {
1423
- var existing = this.collection.get(pipeline.pipelineUrl);
1424
- 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 ")));
1425
- }
1426
- // Note: [🧠] Overwrite existing pipeline with the same URL
1427
- this.collection.set(pipeline.pipelineUrl, pipeline);
1428
- }
1429
- }
1430
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
1431
- finally {
1432
- try {
1433
- if (pipelines_1_1 && !pipelines_1_1.done && (_a = pipelines_1.return)) _a.call(pipelines_1);
1434
- }
1435
- finally { if (e_1) throw e_1.error; }
1436
- }
1701
+ function isUrlOnPrivateNetwork(url) {
1702
+ if (typeof url === 'string') {
1703
+ url = new URL(url);
1437
1704
  }
1438
- /**
1439
- * Gets all pipelines in the collection
1440
- */
1441
- SimplePipelineCollection.prototype.listPipelines = function () {
1442
- return Array.from(this.collection.keys());
1443
- };
1444
- /**
1445
- * Gets pipeline by its URL
1446
- *
1447
- * Note: This is not a direct fetching from the URL, but a lookup in the collection
1448
- */
1449
- SimplePipelineCollection.prototype.getPipelineByUrl = function (url) {
1450
- var _this = this;
1451
- var pipeline = this.collection.get(url);
1452
- if (!pipeline) {
1453
- if (this.listPipelines().length === 0) {
1454
- throw new NotFoundError(spaceTrim.spaceTrim("\n Pipeline with url \"".concat(url, "\" not found\n\n No pipelines available\n ")));
1455
- }
1456
- throw new NotFoundError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline with url \"".concat(url, "\" not found\n\n Available pipelines:\n ").concat(block(_this.listPipelines()
1457
- .map(function (pipelineUrl) { return "- ".concat(pipelineUrl); })
1458
- .join('\n')), "\n\n "); }));
1459
- }
1460
- return pipeline;
1461
- };
1462
- /**
1463
- * Checks whether given prompt was defined in any pipeline in the collection
1464
- */
1465
- SimplePipelineCollection.prototype.isResponsibleForPrompt = function (prompt) {
1466
- return true;
1467
- };
1468
- return SimplePipelineCollection;
1469
- }());
1705
+ return isHostnameOnPrivateNetwork(url.hostname);
1706
+ }
1470
1707
 
1471
1708
  /**
1472
- * Creates PipelineCollection from array of PipelineJson or PipelineString
1709
+ * Tests if given string is valid pipeline URL URL.
1473
1710
  *
1474
- * Note: Functions `collectionToJson` and `createCollectionFromJson` are complementary
1475
- * Note: Syntax, parsing, and logic consistency checks are performed on all sources during build
1711
+ * Note: There are two simmilar functions:
1712
+ * - `isValidUrl` which tests any URL
1713
+ * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
1476
1714
  *
1477
- * @param promptbookSources
1478
- * @returns PipelineCollection
1479
- * @public exported from `@promptbook/core`
1715
+ * @public exported from `@promptbook/utils`
1480
1716
  */
1481
- function createCollectionFromJson() {
1482
- var promptbooks = [];
1483
- for (var _i = 0; _i < arguments.length; _i++) {
1484
- promptbooks[_i] = arguments[_i];
1717
+ function isValidPipelineUrl(url) {
1718
+ if (!isValidUrl(url)) {
1719
+ return false;
1485
1720
  }
1486
- return new (SimplePipelineCollection.bind.apply(SimplePipelineCollection, __spreadArray([void 0], __read(promptbooks), false)))();
1721
+ if (!url.startsWith('https://')) {
1722
+ return false;
1723
+ }
1724
+ if (!url.endsWith('.ptbk.md')) {
1725
+ return false;
1726
+ }
1727
+ if (url.includes('#')) {
1728
+ // TODO: [🐠]
1729
+ return false;
1730
+ }
1731
+ if (isUrlOnPrivateNetwork(url)) {
1732
+ return false;
1733
+ }
1734
+ return true;
1487
1735
  }
1736
+ /**
1737
+ * TODO: [🐠] Maybe more info why the URL is invalid
1738
+ */
1488
1739
 
1489
- var defaultDiacriticsRemovalMap = [
1490
- {
1491
- base: 'A',
1492
- letters: '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F',
1493
- },
1494
- { base: 'AA', letters: '\uA732' },
1495
- { base: 'AE', letters: '\u00C6\u01FC\u01E2' },
1496
- { base: 'AO', letters: '\uA734' },
1497
- { base: 'AU', letters: '\uA736' },
1498
- { base: 'AV', letters: '\uA738\uA73A' },
1499
- { base: 'AY', letters: '\uA73C' },
1500
- {
1501
- base: 'B',
1502
- letters: '\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181',
1503
- },
1504
- {
1505
- base: 'C',
1506
- letters: '\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E',
1507
- },
1508
- {
1509
- base: 'D',
1510
- letters: '\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779\u00D0',
1511
- },
1512
- { base: 'DZ', letters: '\u01F1\u01C4' },
1513
- { base: 'Dz', letters: '\u01F2\u01C5' },
1514
- {
1515
- base: 'E',
1516
- letters: '\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E',
1517
- },
1518
- { base: 'F', letters: '\u0046\u24BB\uFF26\u1E1E\u0191\uA77B' },
1519
- {
1520
- base: 'G',
1521
- letters: '\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E',
1522
- },
1523
- {
1524
- base: 'H',
1525
- letters: '\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D',
1526
- },
1527
- {
1528
- base: 'I',
1529
- letters: '\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197',
1530
- },
1531
- { base: 'J', letters: '\u004A\u24BF\uFF2A\u0134\u0248' },
1532
- {
1533
- base: 'K',
1534
- letters: '\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2',
1535
- },
1536
- {
1537
- base: 'L',
1538
- letters: '\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780',
1539
- },
1540
- { base: 'LJ', letters: '\u01C7' },
1541
- { base: 'Lj', letters: '\u01C8' },
1542
- { base: 'M', letters: '\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C' },
1543
- {
1544
- base: 'N',
1545
- letters: '\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4',
1546
- },
1547
- { base: 'NJ', letters: '\u01CA' },
1548
- { base: 'Nj', letters: '\u01CB' },
1549
- {
1550
- base: 'O',
1551
- letters: '\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C',
1552
- },
1553
- { base: 'OI', letters: '\u01A2' },
1554
- { base: 'OO', letters: '\uA74E' },
1555
- { base: 'OU', letters: '\u0222' },
1556
- { base: 'OE', letters: '\u008C\u0152' },
1557
- { base: 'oe', letters: '\u009C\u0153' },
1558
- {
1559
- base: 'P',
1560
- letters: '\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754',
1561
- },
1562
- { base: 'Q', letters: '\u0051\u24C6\uFF31\uA756\uA758\u024A' },
1563
- {
1564
- base: 'R',
1565
- letters: '\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782',
1566
- },
1567
- {
1568
- base: 'S',
1569
- letters: '\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784',
1570
- },
1571
- {
1572
- base: 'T',
1573
- letters: '\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786',
1574
- },
1575
- { base: 'TZ', letters: '\uA728' },
1576
- {
1577
- base: 'U',
1578
- letters: '\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244',
1579
- },
1580
- { base: 'V', letters: '\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245' },
1581
- { base: 'VY', letters: '\uA760' },
1582
- {
1583
- base: 'W',
1584
- letters: '\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72',
1585
- },
1586
- { base: 'X', letters: '\u0058\u24CD\uFF38\u1E8A\u1E8C' },
1587
- {
1588
- base: 'Y',
1589
- letters: '\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE',
1590
- },
1591
- {
1592
- base: 'Z',
1593
- letters: '\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762',
1594
- },
1595
- {
1596
- base: 'a',
1597
- letters: '\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250',
1598
- },
1599
- { base: 'aa', letters: '\uA733' },
1600
- { base: 'ae', letters: '\u00E6\u01FD\u01E3' },
1601
- { base: 'ao', letters: '\uA735' },
1602
- { base: 'au', letters: '\uA737' },
1603
- { base: 'av', letters: '\uA739\uA73B' },
1604
- { base: 'ay', letters: '\uA73D' },
1605
- {
1606
- base: 'b',
1607
- letters: '\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253',
1608
- },
1609
- {
1610
- base: 'c',
1611
- letters: '\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184',
1612
- },
1613
- {
1614
- base: 'd',
1615
- letters: '\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A',
1616
- },
1617
- { base: 'dz', letters: '\u01F3\u01C6' },
1618
- {
1619
- base: 'e',
1620
- letters: '\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD',
1621
- },
1622
- { base: 'f', letters: '\u0066\u24D5\uFF46\u1E1F\u0192\uA77C' },
1623
- {
1624
- base: 'g',
1625
- letters: '\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F',
1626
- },
1627
- {
1628
- base: 'h',
1629
- letters: '\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265',
1630
- },
1631
- { base: 'hv', letters: '\u0195' },
1632
- {
1633
- base: 'i',
1634
- letters: '\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131',
1635
- },
1636
- { base: 'j', letters: '\u006A\u24D9\uFF4A\u0135\u01F0\u0249' },
1637
- {
1638
- base: 'k',
1639
- letters: '\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3',
1640
- },
1641
- {
1642
- base: 'l',
1643
- letters: '\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747',
1644
- },
1645
- { base: 'lj', letters: '\u01C9' },
1646
- { base: 'm', letters: '\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F' },
1647
- {
1648
- base: 'n',
1649
- letters: '\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5',
1650
- },
1651
- { base: 'nj', letters: '\u01CC' },
1652
- {
1653
- base: 'o',
1654
- letters: '\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275',
1655
- },
1656
- { base: 'oi', letters: '\u01A3' },
1657
- { base: 'ou', letters: '\u0223' },
1658
- { base: 'oo', letters: '\uA74F' },
1659
- {
1660
- base: 'p',
1661
- letters: '\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755',
1662
- },
1663
- { base: 'q', letters: '\u0071\u24E0\uFF51\u024B\uA757\uA759' },
1664
- {
1665
- base: 'r',
1666
- letters: '\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783',
1667
- },
1668
- {
1669
- base: 's',
1670
- letters: '\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B',
1671
- },
1672
- {
1673
- base: 't',
1674
- letters: '\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787',
1675
- },
1676
- { base: 'tz', letters: '\uA729' },
1677
- {
1678
- base: 'u',
1679
- letters: '\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289',
1680
- },
1681
- { base: 'v', letters: '\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C' },
1682
- { base: 'vy', letters: '\uA761' },
1683
- {
1684
- base: 'w',
1685
- letters: '\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73',
1686
- },
1687
- { base: 'x', letters: '\u0078\u24E7\uFF58\u1E8B\u1E8D' },
1688
- {
1689
- base: 'y',
1690
- letters: '\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF',
1691
- },
1692
- {
1693
- base: 'z',
1694
- letters: '\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763',
1695
- },
1696
- ];
1697
1740
  /**
1698
- * Map of letters from diacritic variant to diacritless variant
1699
- * Contains lowercase and uppercase separatelly
1700
- *
1701
- * > "á" => "a"
1702
- * > "ě" => "e"
1703
- * > "Ă" => "A"
1704
- * > ...
1741
+ * Validates PipelineJson if it is logically valid
1705
1742
  *
1706
- * @public exported from `@promptbook/utils`
1707
- */
1708
- var DIACRITIC_VARIANTS_LETTERS = {};
1709
- // tslint:disable-next-line: prefer-for-of
1710
- for (var i = 0; i < defaultDiacriticsRemovalMap.length; i++) {
1711
- var letters = defaultDiacriticsRemovalMap[i].letters;
1712
- // tslint:disable-next-line: prefer-for-of
1713
- for (var j = 0; j < letters.length; j++) {
1714
- DIACRITIC_VARIANTS_LETTERS[letters[j]] = defaultDiacriticsRemovalMap[i].base;
1715
- }
1716
- }
1717
- // <- TODO: [🍓] Put to maker function to save execution time if not needed
1718
- /*
1719
- @see https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript
1720
- Licensed under the Apache License, Version 2.0 (the "License");
1721
- you may not use this file except in compliance with the License.
1722
- You may obtain a copy of the License at
1723
-
1724
- http://www.apache.org/licenses/LICENSE-2.0
1725
-
1726
- Unless required by applicable law or agreed to in writing, software
1727
- distributed under the License is distributed on an "AS IS" BASIS,
1728
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1729
- See the License for the specific language governing permissions and
1730
- limitations under the License.
1731
- */
1732
-
1733
- /**
1734
- * @@@
1743
+ * It checks:
1744
+ * - if it has correct parameters dependency
1735
1745
  *
1736
- * @param input @@@
1737
- * @returns @@@
1738
- * @public exported from `@promptbook/utils`
1739
- */
1740
- function removeDiacritics(input) {
1741
- /*eslint no-control-regex: "off"*/
1742
- return input.replace(/[^\u0000-\u007E]/g, function (a) {
1743
- return DIACRITIC_VARIANTS_LETTERS[a] || a;
1744
- });
1745
- }
1746
- /**
1747
- * TODO: [Ж] Variant for cyrillic (and in general non-latin) letters
1748
- */
1749
-
1750
- /**
1751
- * @@@
1746
+ * It does NOT check:
1747
+ * - if it is valid json
1748
+ * - if it is meaningful
1752
1749
  *
1753
- * @param text @@@
1754
- * @returns @@@
1755
- * @example 'hello-world'
1756
- * @example 'i-love-promptbook'
1757
- * @public exported from `@promptbook/utils`
1750
+ * @param pipeline valid or invalid PipelineJson
1751
+ * @returns the same pipeline if it is logically valid
1752
+ * @throws {PipelineLogicError} on logical error in the pipeline
1753
+ * @public exported from `@promptbook/core`
1758
1754
  */
1759
- function normalizeToKebabCase(text) {
1760
- var e_1, _a;
1761
- text = removeDiacritics(text);
1762
- var charType;
1763
- var lastCharType = 'OTHER';
1764
- var normalizedName = '';
1765
- try {
1766
- for (var text_1 = __values(text), text_1_1 = text_1.next(); !text_1_1.done; text_1_1 = text_1.next()) {
1767
- var char = text_1_1.value;
1768
- var normalizedChar = void 0;
1769
- if (/^[a-z]$/.test(char)) {
1770
- charType = 'LOWERCASE';
1771
- normalizedChar = char;
1772
- }
1773
- else if (/^[A-Z]$/.test(char)) {
1774
- charType = 'UPPERCASE';
1775
- normalizedChar = char.toLowerCase();
1776
- }
1777
- else if (/^[0-9]$/.test(char)) {
1778
- charType = 'NUMBER';
1779
- normalizedChar = char;
1780
- }
1781
- else if (/^\/$/.test(char)) {
1782
- charType = 'SLASH';
1783
- normalizedChar = char;
1784
- }
1785
- else {
1786
- charType = 'OTHER';
1787
- normalizedChar = '-';
1788
- }
1789
- if (charType !== lastCharType &&
1790
- !(lastCharType === 'UPPERCASE' && charType === 'LOWERCASE') &&
1791
- !(lastCharType === 'NUMBER') &&
1792
- !(charType === 'NUMBER')) {
1793
- normalizedName += '-';
1794
- }
1795
- normalizedName += normalizedChar;
1796
- lastCharType = charType;
1797
- }
1755
+ function validatePipeline(pipeline) {
1756
+ if (IS_PIPELINE_LOGIC_VALIDATED) {
1757
+ validatePipelineCore(pipeline);
1798
1758
  }
1799
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
1800
- finally {
1759
+ else {
1801
1760
  try {
1802
- if (text_1_1 && !text_1_1.done && (_a = text_1.return)) _a.call(text_1);
1761
+ validatePipelineCore(pipeline);
1762
+ }
1763
+ catch (error) {
1764
+ if (!(error instanceof PipelineLogicError)) {
1765
+ throw error;
1766
+ }
1767
+ console.error(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is not valid but logic errors are temporarily disabled via `IS_PIPELINE_LOGIC_VALIDATED`\n\n ".concat(block(error.message), "\n "); }));
1803
1768
  }
1804
- finally { if (e_1) throw e_1.error; }
1805
1769
  }
1806
- normalizedName = normalizedName.split(/-+/g).join('-');
1807
- normalizedName = normalizedName.split(/-?\/-?/g).join('/');
1808
- normalizedName = normalizedName.replace(/^-/, '');
1809
- normalizedName = normalizedName.replace(/-$/, '');
1810
- return normalizedName;
1770
+ return pipeline;
1811
1771
  }
1812
-
1813
1772
  /**
1814
- * Removes emojis from a string and fix whitespaces
1815
- *
1816
- * @param text with emojis
1817
- * @returns text without emojis
1818
- * @public exported from `@promptbook/utils`
1773
+ * @private internal function for `validatePipeline`
1819
1774
  */
1820
- function removeEmojis(text) {
1821
- // Replace emojis (and also ZWJ sequence) with hyphens
1822
- text = text.replace(/(\p{Extended_Pictographic})\p{Modifier_Symbol}/gu, '$1');
1823
- text = text.replace(/(\p{Extended_Pictographic})[\u{FE00}-\u{FE0F}]/gu, '$1');
1824
- text = text.replace(/(\p{Extended_Pictographic})(\u{200D}\p{Extended_Pictographic})*/gu, '$1');
1825
- text = text.replace(/\p{Extended_Pictographic}/gu, '');
1826
- return text;
1827
- }
1828
-
1829
- /**
1830
- * @@@
1831
- *
1832
- * @param value @@@
1833
- * @returns @@@
1834
- * @example @@@
1835
- * @public exported from `@promptbook/utils`
1836
- */
1837
- function titleToName(value) {
1838
- if (value.startsWith('http://') || value.startsWith('https://')) {
1839
- // TODO: Maybe check against some list unallowed characters
1840
- return value;
1775
+ function validatePipelineCore(pipeline) {
1776
+ // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
1777
+ var e_1, _a, e_2, _b, e_3, _c;
1778
+ var pipelineIdentification = (function () {
1779
+ // Note: This is a 😐 implementation of [🚞]
1780
+ var _ = [];
1781
+ if (pipeline.sourceFile !== undefined) {
1782
+ _.push("File: ".concat(pipeline.sourceFile));
1783
+ }
1784
+ if (pipeline.pipelineUrl !== undefined) {
1785
+ _.push("Url: ".concat(pipeline.pipelineUrl));
1786
+ }
1787
+ return _.join('\n');
1788
+ })();
1789
+ if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
1790
+ // <- Note: [🚲]
1791
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid promptbook URL \"".concat(pipeline.pipelineUrl, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
1841
1792
  }
1842
- if (value.startsWith('./') || value.startsWith('../')) {
1843
- // TODO: Maybe check against some list unallowed characters
1844
- return value;
1793
+ if (pipeline.promptbookVersion !== undefined && !isValidPromptbookVersion(pipeline.promptbookVersion)) {
1794
+ // <- Note: [🚲]
1795
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid Promptbook Version \"".concat(pipeline.promptbookVersion, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
1796
+ }
1797
+ // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
1798
+ if (!Array.isArray(pipeline.parameters)) {
1799
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
1800
+ throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.parameters` expected to be an array, but got ".concat(typeof pipeline.parameters, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
1801
+ }
1802
+ // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
1803
+ if (!Array.isArray(pipeline.templates)) {
1804
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
1805
+ throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.templates` expected to be an array, but got ".concat(typeof pipeline.templates, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
1806
+ }
1807
+ var _loop_1 = function (parameter) {
1808
+ if (parameter.isInput && parameter.isOutput) {
1809
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n\n Parameter {".concat(parameter.name, "} can not be both input and output\n\n ").concat(block(pipelineIdentification), "\n "); }));
1810
+ }
1811
+ // Note: Testing that parameter is either intermediate or output BUT not created and unused
1812
+ if (!parameter.isInput &&
1813
+ !parameter.isOutput &&
1814
+ !pipeline.templates.some(function (template) { return template.dependentParameterNames.includes(parameter.name); })) {
1815
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter {".concat(parameter.name, "} is created but not used\n\n You can declare {").concat(parameter.name, "} as output parameter by adding in the header:\n - OUTPUT PARAMETER `{").concat(parameter.name, "}` ").concat(parameter.description || '', "\n\n ").concat(block(pipelineIdentification), "\n\n "); }));
1816
+ }
1817
+ // Note: Testing that parameter is either input or result of some template
1818
+ if (!parameter.isInput &&
1819
+ !pipeline.templates.some(function (template) { return template.resultingParameterName === parameter.name; })) {
1820
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter {".concat(parameter.name, "} is declared but not defined\n\n You can do one of these:\n 1) Remove declaration of {").concat(parameter.name, "}\n 2) Add template that results in -> {").concat(parameter.name, "}\n\n ").concat(block(pipelineIdentification), "\n "); }));
1821
+ }
1822
+ };
1823
+ try {
1824
+ // Note: Check each parameter individually
1825
+ for (var _d = __values(pipeline.parameters), _e = _d.next(); !_e.done; _e = _d.next()) {
1826
+ var parameter = _e.value;
1827
+ _loop_1(parameter);
1828
+ }
1829
+ }
1830
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
1831
+ finally {
1832
+ try {
1833
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
1834
+ }
1835
+ finally { if (e_1) throw e_1.error; }
1836
+ }
1837
+ // Note: All input parameters are defined - so that they can be used as result of some template
1838
+ var definedParameters = new Set(pipeline.parameters.filter(function (_a) {
1839
+ var isInput = _a.isInput;
1840
+ return isInput;
1841
+ }).map(function (_a) {
1842
+ var name = _a.name;
1843
+ return name;
1844
+ }));
1845
+ var _loop_2 = function (template) {
1846
+ var e_4, _h, e_5, _j;
1847
+ if (definedParameters.has(template.resultingParameterName)) {
1848
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter {".concat(template.resultingParameterName, "} is defined multiple times\n\n ").concat(block(pipelineIdentification), "\n "); }));
1849
+ }
1850
+ if (RESERVED_PARAMETER_NAMES.includes(template.resultingParameterName)) {
1851
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter name {".concat(template.resultingParameterName, "} is reserved, please use different name\n\n ").concat(block(pipelineIdentification), "\n "); }));
1852
+ }
1853
+ definedParameters.add(template.resultingParameterName);
1854
+ if (template.jokerParameterNames && template.jokerParameterNames.length > 0) {
1855
+ if (!template.format &&
1856
+ !template.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
1857
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Joker parameters are used for {".concat(template.resultingParameterName, "} but no expectations are defined\n\n ").concat(block(pipelineIdentification), "\n "); }));
1858
+ }
1859
+ var _loop_4 = function (joker) {
1860
+ if (!template.dependentParameterNames.includes(joker)) {
1861
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter {".concat(joker, "} is used for {").concat(template.resultingParameterName, "} as joker but not in `dependentParameterNames`\n\n ").concat(block(pipelineIdentification), "\n "); }));
1862
+ }
1863
+ };
1864
+ try {
1865
+ for (var _k = (e_4 = void 0, __values(template.jokerParameterNames)), _l = _k.next(); !_l.done; _l = _k.next()) {
1866
+ var joker = _l.value;
1867
+ _loop_4(joker);
1868
+ }
1869
+ }
1870
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
1871
+ finally {
1872
+ try {
1873
+ if (_l && !_l.done && (_h = _k.return)) _h.call(_k);
1874
+ }
1875
+ finally { if (e_4) throw e_4.error; }
1876
+ }
1877
+ }
1878
+ if (template.expectations) {
1879
+ var _loop_5 = function (unit, min, max) {
1880
+ if (min !== undefined && max !== undefined && min > max) {
1881
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation (=".concat(min, ") of ").concat(unit, " is higher than max expectation (=").concat(max, ")\n\n ").concat(block(pipelineIdentification), "\n "); }));
1882
+ }
1883
+ if (min !== undefined && min < 0) {
1884
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation of ".concat(unit, " must be zero or positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
1885
+ }
1886
+ if (max !== undefined && max <= 0) {
1887
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Max expectation of ".concat(unit, " must be positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
1888
+ }
1889
+ };
1890
+ try {
1891
+ for (var _m = (e_5 = void 0, __values(Object.entries(template.expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
1892
+ var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
1893
+ _loop_5(unit, min, max);
1894
+ }
1895
+ }
1896
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
1897
+ finally {
1898
+ try {
1899
+ if (_o && !_o.done && (_j = _m.return)) _j.call(_m);
1900
+ }
1901
+ finally { if (e_5) throw e_5.error; }
1902
+ }
1903
+ }
1904
+ };
1905
+ try {
1906
+ // Note: Checking each template individually
1907
+ for (var _f = __values(pipeline.templates), _g = _f.next(); !_g.done; _g = _f.next()) {
1908
+ var template = _g.value;
1909
+ _loop_2(template);
1910
+ }
1911
+ }
1912
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
1913
+ finally {
1914
+ try {
1915
+ if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
1916
+ }
1917
+ finally { if (e_2) throw e_2.error; }
1918
+ }
1919
+ // Note: Detect circular dependencies
1920
+ var resovedParameters = pipeline.parameters
1921
+ .filter(function (_a) {
1922
+ var isInput = _a.isInput;
1923
+ return isInput;
1924
+ })
1925
+ .map(function (_a) {
1926
+ var name = _a.name;
1927
+ return name;
1928
+ });
1929
+ try {
1930
+ // Note: All reserved parameters are resolved
1931
+ for (var RESERVED_PARAMETER_NAMES_1 = __values(RESERVED_PARAMETER_NAMES), RESERVED_PARAMETER_NAMES_1_1 = RESERVED_PARAMETER_NAMES_1.next(); !RESERVED_PARAMETER_NAMES_1_1.done; RESERVED_PARAMETER_NAMES_1_1 = RESERVED_PARAMETER_NAMES_1.next()) {
1932
+ var reservedParameterName = RESERVED_PARAMETER_NAMES_1_1.value;
1933
+ resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), [reservedParameterName], false);
1934
+ }
1935
+ }
1936
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
1937
+ finally {
1938
+ try {
1939
+ if (RESERVED_PARAMETER_NAMES_1_1 && !RESERVED_PARAMETER_NAMES_1_1.done && (_c = RESERVED_PARAMETER_NAMES_1.return)) _c.call(RESERVED_PARAMETER_NAMES_1);
1940
+ }
1941
+ finally { if (e_3) throw e_3.error; }
1942
+ }
1943
+ var unresovedTemplates = __spreadArray([], __read(pipeline.templates), false);
1944
+ var loopLimit = LOOP_LIMIT;
1945
+ var _loop_3 = function () {
1946
+ if (loopLimit-- < 0) {
1947
+ // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
1948
+ throw new UnexpectedError(spaceTrim.spaceTrim(function (block) { return "\n Loop limit reached during detection of circular dependencies in `validatePipeline`\n\n ".concat(block(pipelineIdentification), "\n "); }));
1949
+ }
1950
+ var currentlyResovedTemplates = unresovedTemplates.filter(function (template) {
1951
+ return template.dependentParameterNames.every(function (name) { return resovedParameters.includes(name); });
1952
+ });
1953
+ if (currentlyResovedTemplates.length === 0) {
1954
+ throw new PipelineLogicError(
1955
+ // TODO: [🐎] DRY
1956
+ spaceTrim.spaceTrim(function (block) { return "\n\n Can not resolve some parameters:\n Either you are using a parameter that is not defined, or there are some circular dependencies.\n\n ".concat(block(pipelineIdentification), "\n\n Can not resolve:\n ").concat(block(unresovedTemplates
1957
+ .map(function (_a) {
1958
+ var resultingParameterName = _a.resultingParameterName, dependentParameterNames = _a.dependentParameterNames;
1959
+ return "- Parameter {".concat(resultingParameterName, "} which depends on ").concat(dependentParameterNames
1960
+ .map(function (dependentParameterName) { return "{".concat(dependentParameterName, "}"); })
1961
+ .join(' and '));
1962
+ })
1963
+ .join('\n')), "\n\n Resolved:\n ").concat(block(resovedParameters.map(function (name) { return "- Parameter {".concat(name, "}"); }).join('\n')), "\n\n\n "); }));
1964
+ }
1965
+ resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), __read(currentlyResovedTemplates.map(function (_a) {
1966
+ var resultingParameterName = _a.resultingParameterName;
1967
+ return resultingParameterName;
1968
+ })), false);
1969
+ unresovedTemplates = unresovedTemplates.filter(function (template) { return !currentlyResovedTemplates.includes(template); });
1970
+ };
1971
+ while (unresovedTemplates.length > 0) {
1972
+ _loop_3();
1845
1973
  }
1846
- value = removeEmojis(value);
1847
- value = normalizeToKebabCase(value);
1848
- // TODO: [🧠] Maybe warn or add some padding to short name which are not good identifiers
1849
- return value;
1850
1974
  }
1975
+ /**
1976
+ * TODO: !!!!! [🧞‍♀️] Do not allow joker + foreach
1977
+ * TODO: [🧠] Work with promptbookVersion
1978
+ * TODO: Use here some json-schema, Zod or something similar and change it to:
1979
+ * > /**
1980
+ * > * Validates PipelineJson if it is logically valid.
1981
+ * > *
1982
+ * > * It checks:
1983
+ * > * - it has a valid structure
1984
+ * > * - ...
1985
+ * > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
1986
+ */
1987
+ /**
1988
+ * TODO: [🧳][main] !!!! Validate that all samples match expectations
1989
+ * TODO: [🧳][🐝][main] !!!! Validate that knowledge is valid (non-void)
1990
+ * TODO: [🧳][main] !!!! Validate that persona can be used only with CHAT variant
1991
+ * TODO: [🧳][main] !!!! Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
1992
+ * TODO: [🧳][main] !!!! Validate that reserved parameter is not used as joker
1993
+ * TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
1994
+ * TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
1995
+ */
1851
1996
 
1852
1997
  /**
1853
- * This error indicates errors during the execution of the pipeline
1998
+ * This error indicates that promptbook not found in the collection
1854
1999
  *
1855
2000
  * @public exported from `@promptbook/core`
1856
2001
  */
1857
- var PipelineExecutionError = /** @class */ (function (_super) {
1858
- __extends(PipelineExecutionError, _super);
1859
- function PipelineExecutionError(message) {
2002
+ var NotFoundError = /** @class */ (function (_super) {
2003
+ __extends(NotFoundError, _super);
2004
+ function NotFoundError(message) {
1860
2005
  var _this = _super.call(this, message) || this;
1861
- _this.name = 'PipelineExecutionError';
1862
- Object.setPrototypeOf(_this, PipelineExecutionError.prototype);
2006
+ _this.name = 'NotFoundError';
2007
+ Object.setPrototypeOf(_this, NotFoundError.prototype);
1863
2008
  return _this;
1864
2009
  }
1865
- return PipelineExecutionError;
2010
+ return NotFoundError;
1866
2011
  }(Error));
1867
2012
 
1868
2013
  /**
1869
- * This error indicates that the pipeline collection cannot be propperly loaded
2014
+ * This error indicates errors in referencing promptbooks between each other
1870
2015
  *
1871
2016
  * @public exported from `@promptbook/core`
1872
2017
  */
1873
- var CollectionError = /** @class */ (function (_super) {
1874
- __extends(CollectionError, _super);
1875
- function CollectionError(message) {
2018
+ var PipelineUrlError = /** @class */ (function (_super) {
2019
+ __extends(PipelineUrlError, _super);
2020
+ function PipelineUrlError(message) {
1876
2021
  var _this = _super.call(this, message) || this;
1877
- _this.name = 'CollectionError';
1878
- Object.setPrototypeOf(_this, CollectionError.prototype);
2022
+ _this.name = 'PipelineUrlError';
2023
+ Object.setPrototypeOf(_this, PipelineUrlError.prototype);
1879
2024
  return _this;
1880
2025
  }
1881
- return CollectionError;
2026
+ return PipelineUrlError;
1882
2027
  }(Error));
1883
2028
 
1884
2029
  /**
1885
- * This error type indicates that you try to use a feature that is not available in the current environment
2030
+ * Parses the template and returns the list of all parameter names
2031
+ *
2032
+ * @param template the template with parameters in {curly} braces
2033
+ * @returns the list of parameter names
2034
+ * @public exported from `@promptbook/utils`
2035
+ */
2036
+ function extractParameterNames(template) {
2037
+ var e_1, _a;
2038
+ var matches = template.matchAll(/{\w+}/g);
2039
+ var parameterNames = new Set();
2040
+ try {
2041
+ for (var matches_1 = __values(matches), matches_1_1 = matches_1.next(); !matches_1_1.done; matches_1_1 = matches_1.next()) {
2042
+ var match = matches_1_1.value;
2043
+ var parameterName = match[0].slice(1, -1);
2044
+ parameterNames.add(parameterName);
2045
+ }
2046
+ }
2047
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
2048
+ finally {
2049
+ try {
2050
+ if (matches_1_1 && !matches_1_1.done && (_a = matches_1.return)) _a.call(matches_1);
2051
+ }
2052
+ finally { if (e_1) throw e_1.error; }
2053
+ }
2054
+ return parameterNames;
2055
+ }
2056
+
2057
+ /**
2058
+ * Unprepare just strips the preparation data of the pipeline
1886
2059
  *
1887
2060
  * @public exported from `@promptbook/core`
1888
2061
  */
1889
- var EnvironmentMismatchError = /** @class */ (function (_super) {
1890
- __extends(EnvironmentMismatchError, _super);
1891
- function EnvironmentMismatchError(message) {
2062
+ function unpreparePipeline(pipeline) {
2063
+ var personas = pipeline.personas, knowledgeSources = pipeline.knowledgeSources, templates = pipeline.templates;
2064
+ personas = personas.map(function (persona) { return (__assign(__assign({}, persona), { modelRequirements: undefined, preparationIds: undefined })); });
2065
+ knowledgeSources = knowledgeSources.map(function (knowledgeSource) { return (__assign(__assign({}, knowledgeSource), { preparationIds: undefined })); });
2066
+ templates = templates.map(function (template) {
2067
+ var dependentParameterNames = template.dependentParameterNames;
2068
+ var parameterNames = extractParameterNames(template.preparedContent || '');
2069
+ dependentParameterNames = dependentParameterNames.filter(function (dependentParameterName) { return !parameterNames.has(dependentParameterName); });
2070
+ var templateUnprepared = __assign(__assign({}, template), { dependentParameterNames: dependentParameterNames });
2071
+ delete templateUnprepared.preparedContent;
2072
+ return templateUnprepared;
2073
+ });
2074
+ return $asDeeplyFrozenSerializableJson('Unprepared PipelineJson', __assign(__assign({}, pipeline), { templates: templates, knowledgeSources: knowledgeSources, knowledgePieces: [], personas: personas, preparations: [] }));
2075
+ }
2076
+ /**
2077
+ * TODO: [🧿] Maybe do same process with same granularity and subfinctions as `preparePipeline`
2078
+ * TODO: Write tests for `preparePipeline`
2079
+ * TODO: [🍙] Make some standard order of json properties
2080
+ */
2081
+
2082
+ /**
2083
+ * Library of pipelines that groups together pipelines for an application.
2084
+ * This implementation is a very thin wrapper around the Array / Map of pipelines.
2085
+ *
2086
+ * @private internal function of `createCollectionFromJson`, use `createCollectionFromJson` instead
2087
+ * @see https://github.com/webgptorg/pipeline#pipeline-collection
2088
+ */
2089
+ var SimplePipelineCollection = /** @class */ (function () {
2090
+ /**
2091
+ * Constructs a pipeline collection from pipelines
2092
+ *
2093
+ * @param pipelines @@@
2094
+ *
2095
+ * Note: During the construction logic of all pipelines are validated
2096
+ * Note: It is not recommended to use this constructor directly, use `createCollectionFromJson` *(or other variant)* instead
2097
+ */
2098
+ function SimplePipelineCollection() {
2099
+ var e_1, _a;
2100
+ var pipelines = [];
2101
+ for (var _i = 0; _i < arguments.length; _i++) {
2102
+ pipelines[_i] = arguments[_i];
2103
+ }
2104
+ this.collection = new Map();
2105
+ try {
2106
+ for (var pipelines_1 = __values(pipelines), pipelines_1_1 = pipelines_1.next(); !pipelines_1_1.done; pipelines_1_1 = pipelines_1.next()) {
2107
+ var pipeline = pipelines_1_1.value;
2108
+ // TODO: [👠] DRY
2109
+ if (pipeline.pipelineUrl === undefined) {
2110
+ throw new PipelineUrlError(spaceTrim.spaceTrim("\n Pipeline with name \"".concat(pipeline.title, "\" does not have defined URL\n\n File:\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: Pipelines without URLs are called anonymous pipelines\n They can be used as standalone pipelines, but they cannot be referenced by other pipelines\n And also they cannot be used in the pipeline collection\n\n ")));
2111
+ }
2112
+ // Note: [🐨]
2113
+ validatePipeline(pipeline);
2114
+ // TODO: [🦄] DRY
2115
+ // Note: [🦄]
2116
+ if (
2117
+ // TODO: [🐽]
2118
+ this.collection.has(pipeline.pipelineUrl) &&
2119
+ pipelineJsonToString(unpreparePipeline(pipeline)) !==
2120
+ pipelineJsonToString(unpreparePipeline(this.collection.get(pipeline.pipelineUrl)))) {
2121
+ var existing = this.collection.get(pipeline.pipelineUrl);
2122
+ 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 ")));
2123
+ }
2124
+ // Note: [🧠] Overwrite existing pipeline with the same URL
2125
+ this.collection.set(pipeline.pipelineUrl, pipeline);
2126
+ }
2127
+ }
2128
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
2129
+ finally {
2130
+ try {
2131
+ if (pipelines_1_1 && !pipelines_1_1.done && (_a = pipelines_1.return)) _a.call(pipelines_1);
2132
+ }
2133
+ finally { if (e_1) throw e_1.error; }
2134
+ }
2135
+ }
2136
+ /**
2137
+ * Gets all pipelines in the collection
2138
+ */
2139
+ SimplePipelineCollection.prototype.listPipelines = function () {
2140
+ return Array.from(this.collection.keys());
2141
+ };
2142
+ /**
2143
+ * Gets pipeline by its URL
2144
+ *
2145
+ * Note: This is not a direct fetching from the URL, but a lookup in the collection
2146
+ */
2147
+ SimplePipelineCollection.prototype.getPipelineByUrl = function (url) {
2148
+ var _this = this;
2149
+ var pipeline = this.collection.get(url);
2150
+ if (!pipeline) {
2151
+ if (this.listPipelines().length === 0) {
2152
+ throw new NotFoundError(spaceTrim.spaceTrim("\n Pipeline with url \"".concat(url, "\" not found\n\n No pipelines available\n ")));
2153
+ }
2154
+ throw new NotFoundError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline with url \"".concat(url, "\" not found\n\n Available pipelines:\n ").concat(block(_this.listPipelines()
2155
+ .map(function (pipelineUrl) { return "- ".concat(pipelineUrl); })
2156
+ .join('\n')), "\n\n "); }));
2157
+ }
2158
+ return pipeline;
2159
+ };
2160
+ /**
2161
+ * Checks whether given prompt was defined in any pipeline in the collection
2162
+ */
2163
+ SimplePipelineCollection.prototype.isResponsibleForPrompt = function (prompt) {
2164
+ return true;
2165
+ };
2166
+ return SimplePipelineCollection;
2167
+ }());
2168
+
2169
+ /**
2170
+ * Creates PipelineCollection from array of PipelineJson or PipelineString
2171
+ *
2172
+ * Note: Functions `collectionToJson` and `createCollectionFromJson` are complementary
2173
+ * Note: Syntax, parsing, and logic consistency checks are performed on all sources during build
2174
+ *
2175
+ * @param promptbookSources
2176
+ * @returns PipelineCollection
2177
+ * @public exported from `@promptbook/core`
2178
+ */
2179
+ function createCollectionFromJson() {
2180
+ var promptbooks = [];
2181
+ for (var _i = 0; _i < arguments.length; _i++) {
2182
+ promptbooks[_i] = arguments[_i];
2183
+ }
2184
+ return new (SimplePipelineCollection.bind.apply(SimplePipelineCollection, __spreadArray([void 0], __read(promptbooks), false)))();
2185
+ }
2186
+
2187
+ /**
2188
+ * This error indicates errors during the execution of the pipeline
2189
+ *
2190
+ * @public exported from `@promptbook/core`
2191
+ */
2192
+ var PipelineExecutionError = /** @class */ (function (_super) {
2193
+ __extends(PipelineExecutionError, _super);
2194
+ function PipelineExecutionError(message) {
1892
2195
  var _this = _super.call(this, message) || this;
1893
- _this.name = 'EnvironmentMismatchError';
1894
- Object.setPrototypeOf(_this, EnvironmentMismatchError.prototype);
2196
+ _this.name = 'PipelineExecutionError';
2197
+ Object.setPrototypeOf(_this, PipelineExecutionError.prototype);
1895
2198
  return _this;
1896
2199
  }
1897
- return EnvironmentMismatchError;
2200
+ return PipelineExecutionError;
2201
+ }(Error));
2202
+
2203
+ /**
2204
+ * This error indicates that the pipeline collection cannot be propperly loaded
2205
+ *
2206
+ * @public exported from `@promptbook/core`
2207
+ */
2208
+ var CollectionError = /** @class */ (function (_super) {
2209
+ __extends(CollectionError, _super);
2210
+ function CollectionError(message) {
2211
+ var _this = _super.call(this, message) || this;
2212
+ _this.name = 'CollectionError';
2213
+ Object.setPrototypeOf(_this, CollectionError.prototype);
2214
+ return _this;
2215
+ }
2216
+ return CollectionError;
1898
2217
  }(Error));
1899
2218
 
1900
2219
  /**
@@ -2395,6 +2714,59 @@
2395
2714
  * TODO: [🧠] Is there a way how to meaningfully test this utility
2396
2715
  */
2397
2716
 
2717
+ /**
2718
+ * Represents the usage with no resources consumed
2719
+ *
2720
+ * @public exported from `@promptbook/core`
2721
+ */
2722
+ var ZERO_USAGE = $deepFreeze({
2723
+ price: { value: 0 },
2724
+ input: {
2725
+ tokensCount: { value: 0 },
2726
+ charactersCount: { value: 0 },
2727
+ wordsCount: { value: 0 },
2728
+ sentencesCount: { value: 0 },
2729
+ linesCount: { value: 0 },
2730
+ paragraphsCount: { value: 0 },
2731
+ pagesCount: { value: 0 },
2732
+ },
2733
+ output: {
2734
+ tokensCount: { value: 0 },
2735
+ charactersCount: { value: 0 },
2736
+ wordsCount: { value: 0 },
2737
+ sentencesCount: { value: 0 },
2738
+ linesCount: { value: 0 },
2739
+ paragraphsCount: { value: 0 },
2740
+ pagesCount: { value: 0 },
2741
+ },
2742
+ });
2743
+ /**
2744
+ * Represents the usage with unknown resources consumed
2745
+ *
2746
+ * @public exported from `@promptbook/core`
2747
+ */
2748
+ $deepFreeze({
2749
+ price: { value: 0, isUncertain: true },
2750
+ input: {
2751
+ tokensCount: { value: 0, isUncertain: true },
2752
+ charactersCount: { value: 0, isUncertain: true },
2753
+ wordsCount: { value: 0, isUncertain: true },
2754
+ sentencesCount: { value: 0, isUncertain: true },
2755
+ linesCount: { value: 0, isUncertain: true },
2756
+ paragraphsCount: { value: 0, isUncertain: true },
2757
+ pagesCount: { value: 0, isUncertain: true },
2758
+ },
2759
+ output: {
2760
+ tokensCount: { value: 0, isUncertain: true },
2761
+ charactersCount: { value: 0, isUncertain: true },
2762
+ wordsCount: { value: 0, isUncertain: true },
2763
+ sentencesCount: { value: 0, isUncertain: true },
2764
+ linesCount: { value: 0, isUncertain: true },
2765
+ paragraphsCount: { value: 0, isUncertain: true },
2766
+ pagesCount: { value: 0, isUncertain: true },
2767
+ },
2768
+ });
2769
+
2398
2770
  /**
2399
2771
  * Function `addUsage` will add multiple usages into one
2400
2772
  *
@@ -2673,29 +3045,12 @@
2673
3045
  }
2674
3046
 
2675
3047
  /**
2676
- * Just marks a place of place where should be something implemented
2677
- * No side effects.
3048
+ * This error indicates problems parsing the format value
2678
3049
  *
2679
- * Note: It can be usefull suppressing eslint errors of unused variables
3050
+ * For example, when the format value is not a valid JSON or CSV
3051
+ * This is not thrown directly but in extended classes
2680
3052
  *
2681
- * @param value any values
2682
- * @returns void
2683
- * @private within the repository
2684
- */
2685
- function TODO_USE() {
2686
- var value = [];
2687
- for (var _i = 0; _i < arguments.length; _i++) {
2688
- value[_i] = arguments[_i];
2689
- }
2690
- }
2691
-
2692
- /**
2693
- * This error indicates problems parsing the format value
2694
- *
2695
- * For example, when the format value is not a valid JSON or CSV
2696
- * This is not thrown directly but in extended classes
2697
- *
2698
- * @public exported from `@promptbook/core`
3053
+ * @public exported from `@promptbook/core`
2699
3054
  */
2700
3055
  var AbstractFormatError = /** @class */ (function (_super) {
2701
3056
  __extends(AbstractFormatError, _super);
@@ -2732,7 +3087,7 @@
2732
3087
  */
2733
3088
  var MANDATORY_CSV_SETTINGS = Object.freeze({
2734
3089
  header: true,
2735
- // encoding: 'utf8',
3090
+ // encoding: 'utf-8',
2736
3091
  });
2737
3092
 
2738
3093
  /**
@@ -4164,25 +4519,26 @@
4164
4519
  */
4165
4520
  function executePipeline(options) {
4166
4521
  return __awaiter(this, void 0, void 0, function () {
4167
- var inputParameters, tools, onProgress, pipeline, setPreparedPipeline, pipelineIdentification, settings, maxParallelCount, isVerbose, preparedPipeline, llmTools, errors, warnings, executionReport, isReturned, _a, _b, parameter, e_1_1, _loop_1, _c, _d, parameterName, state_1, e_2_1, parametersToPass, resovedParameterNames_1, unresovedTemplates_1, resolving_1, loopLimit, _loop_2, error_1, usage_1, outputParameters_1, usage, outputParameters;
4168
- var e_1, _e, e_2, _f;
4169
- return __generator(this, function (_g) {
4170
- switch (_g.label) {
4522
+ var inputParameters, tools, onProgress, pipeline, setPreparedPipeline, pipelineIdentification, settings, maxParallelCount, rootDirname, _a, isVerbose, preparedPipeline, llmTools, errors, warnings, executionReport, isReturned, _b, _c, parameter, e_1_1, _loop_1, _d, _e, parameterName, state_1, e_2_1, parametersToPass, resovedParameterNames_1, unresovedTemplates_1, resolving_1, loopLimit, _loop_2, error_1, usage_1, outputParameters_1, usage, outputParameters;
4523
+ var e_1, _f, e_2, _g;
4524
+ return __generator(this, function (_h) {
4525
+ switch (_h.label) {
4171
4526
  case 0:
4172
4527
  inputParameters = options.inputParameters, tools = options.tools, onProgress = options.onProgress, pipeline = options.pipeline, setPreparedPipeline = options.setPreparedPipeline, pipelineIdentification = options.pipelineIdentification, settings = options.settings;
4173
- maxParallelCount = settings.maxParallelCount, isVerbose = settings.isVerbose;
4528
+ maxParallelCount = settings.maxParallelCount, rootDirname = settings.rootDirname, _a = settings.isVerbose, isVerbose = _a === void 0 ? IS_VERBOSE : _a;
4174
4529
  preparedPipeline = options.preparedPipeline;
4175
4530
  llmTools = joinLlmExecutionTools.apply(void 0, __spreadArray([], __read(arrayableToArray(tools.llm)), false));
4176
4531
  if (!(preparedPipeline === undefined)) return [3 /*break*/, 2];
4177
4532
  return [4 /*yield*/, preparePipeline(pipeline, {
4178
4533
  llmTools: llmTools,
4534
+ rootDirname: rootDirname,
4179
4535
  isVerbose: isVerbose,
4180
4536
  maxParallelCount: maxParallelCount,
4181
4537
  })];
4182
4538
  case 1:
4183
- preparedPipeline = _g.sent();
4539
+ preparedPipeline = _h.sent();
4184
4540
  setPreparedPipeline(preparedPipeline);
4185
- _g.label = 2;
4541
+ _h.label = 2;
4186
4542
  case 2:
4187
4543
  errors = [];
4188
4544
  warnings = [];
@@ -4195,17 +4551,17 @@
4195
4551
  promptExecutions: [],
4196
4552
  };
4197
4553
  isReturned = false;
4198
- _g.label = 3;
4554
+ _h.label = 3;
4199
4555
  case 3:
4200
- _g.trys.push([3, 9, 10, 11]);
4201
- _a = __values(preparedPipeline.parameters.filter(function (_a) {
4556
+ _h.trys.push([3, 9, 10, 11]);
4557
+ _b = __values(preparedPipeline.parameters.filter(function (_a) {
4202
4558
  var isInput = _a.isInput;
4203
4559
  return isInput;
4204
- })), _b = _a.next();
4205
- _g.label = 4;
4560
+ })), _c = _b.next();
4561
+ _h.label = 4;
4206
4562
  case 4:
4207
- if (!!_b.done) return [3 /*break*/, 8];
4208
- parameter = _b.value;
4563
+ if (!!_c.done) return [3 /*break*/, 8];
4564
+ parameter = _c.value;
4209
4565
  if (!(inputParameters[parameter.name] === undefined)) return [3 /*break*/, 7];
4210
4566
  isReturned = true;
4211
4567
  if (!(onProgress !== undefined)) return [3 /*break*/, 6];
@@ -4213,8 +4569,8 @@
4213
4569
  return [4 /*yield*/, waitasecond.forTime(IMMEDIATE_TIME)];
4214
4570
  case 5:
4215
4571
  // Note: Wait a short time to prevent race conditions
4216
- _g.sent();
4217
- _g.label = 6;
4572
+ _h.sent();
4573
+ _h.label = 6;
4218
4574
  case 6: return [2 /*return*/, $asDeeplyFrozenSerializableJson("Unuccessful PipelineExecutorResult (with missing parameter {".concat(parameter.name, "}) PipelineExecutorResult"), {
4219
4575
  isSuccessful: false,
4220
4576
  errors: __spreadArray([
@@ -4227,24 +4583,24 @@
4227
4583
  preparedPipeline: preparedPipeline,
4228
4584
  })];
4229
4585
  case 7:
4230
- _b = _a.next();
4586
+ _c = _b.next();
4231
4587
  return [3 /*break*/, 4];
4232
4588
  case 8: return [3 /*break*/, 11];
4233
4589
  case 9:
4234
- e_1_1 = _g.sent();
4590
+ e_1_1 = _h.sent();
4235
4591
  e_1 = { error: e_1_1 };
4236
4592
  return [3 /*break*/, 11];
4237
4593
  case 10:
4238
4594
  try {
4239
- if (_b && !_b.done && (_e = _a.return)) _e.call(_a);
4595
+ if (_c && !_c.done && (_f = _b.return)) _f.call(_b);
4240
4596
  }
4241
4597
  finally { if (e_1) throw e_1.error; }
4242
4598
  return [7 /*endfinally*/];
4243
4599
  case 11:
4244
4600
  _loop_1 = function (parameterName) {
4245
4601
  var parameter;
4246
- return __generator(this, function (_h) {
4247
- switch (_h.label) {
4602
+ return __generator(this, function (_j) {
4603
+ switch (_j.label) {
4248
4604
  case 0:
4249
4605
  parameter = preparedPipeline.parameters.find(function (_a) {
4250
4606
  var name = _a.name;
@@ -4261,8 +4617,8 @@
4261
4617
  return [4 /*yield*/, waitasecond.forTime(IMMEDIATE_TIME)];
4262
4618
  case 2:
4263
4619
  // Note: Wait a short time to prevent race conditions
4264
- _h.sent();
4265
- _h.label = 3;
4620
+ _j.sent();
4621
+ _j.label = 3;
4266
4622
  case 3: return [2 /*return*/, { value: $asDeeplyFrozenSerializableJson(spaceTrim.spaceTrim(function (block) { return "\n Unuccessful PipelineExecutorResult (with extra parameter {".concat(parameter.name, "}) PipelineExecutorResult\n\n ").concat(block(pipelineIdentification), "\n "); }), {
4267
4623
  isSuccessful: false,
4268
4624
  errors: __spreadArray([
@@ -4278,39 +4634,39 @@
4278
4634
  }
4279
4635
  });
4280
4636
  };
4281
- _g.label = 12;
4637
+ _h.label = 12;
4282
4638
  case 12:
4283
- _g.trys.push([12, 17, 18, 19]);
4284
- _c = __values(Object.keys(inputParameters)), _d = _c.next();
4285
- _g.label = 13;
4639
+ _h.trys.push([12, 17, 18, 19]);
4640
+ _d = __values(Object.keys(inputParameters)), _e = _d.next();
4641
+ _h.label = 13;
4286
4642
  case 13:
4287
- if (!!_d.done) return [3 /*break*/, 16];
4288
- parameterName = _d.value;
4643
+ if (!!_e.done) return [3 /*break*/, 16];
4644
+ parameterName = _e.value;
4289
4645
  return [5 /*yield**/, _loop_1(parameterName)];
4290
4646
  case 14:
4291
- state_1 = _g.sent();
4647
+ state_1 = _h.sent();
4292
4648
  if (typeof state_1 === "object")
4293
4649
  return [2 /*return*/, state_1.value];
4294
- _g.label = 15;
4650
+ _h.label = 15;
4295
4651
  case 15:
4296
- _d = _c.next();
4652
+ _e = _d.next();
4297
4653
  return [3 /*break*/, 13];
4298
4654
  case 16: return [3 /*break*/, 19];
4299
4655
  case 17:
4300
- e_2_1 = _g.sent();
4656
+ e_2_1 = _h.sent();
4301
4657
  e_2 = { error: e_2_1 };
4302
4658
  return [3 /*break*/, 19];
4303
4659
  case 18:
4304
4660
  try {
4305
- if (_d && !_d.done && (_f = _c.return)) _f.call(_c);
4661
+ if (_e && !_e.done && (_g = _d.return)) _g.call(_d);
4306
4662
  }
4307
4663
  finally { if (e_2) throw e_2.error; }
4308
4664
  return [7 /*endfinally*/];
4309
4665
  case 19:
4310
4666
  parametersToPass = inputParameters;
4311
- _g.label = 20;
4667
+ _h.label = 20;
4312
4668
  case 20:
4313
- _g.trys.push([20, 25, , 28]);
4669
+ _h.trys.push([20, 25, , 28]);
4314
4670
  resovedParameterNames_1 = preparedPipeline.parameters
4315
4671
  .filter(function (_a) {
4316
4672
  var isInput = _a.isInput;
@@ -4325,8 +4681,8 @@
4325
4681
  loopLimit = LOOP_LIMIT;
4326
4682
  _loop_2 = function () {
4327
4683
  var currentTemplate, work_1;
4328
- return __generator(this, function (_j) {
4329
- switch (_j.label) {
4684
+ return __generator(this, function (_k) {
4685
+ switch (_k.label) {
4330
4686
  case 0:
4331
4687
  if (loopLimit-- < 0) {
4332
4688
  // Note: Really UnexpectedError not LimitReachedError - this should be catched during validatePipeline
@@ -4352,7 +4708,7 @@
4352
4708
  if (!!currentTemplate) return [3 /*break*/, 3];
4353
4709
  /* [🤹‍♂️] */ return [4 /*yield*/, Promise.race(resolving_1)];
4354
4710
  case 2:
4355
- /* [🤹‍♂️] */ _j.sent();
4711
+ /* [🤹‍♂️] */ _k.sent();
4356
4712
  return [3 /*break*/, 4];
4357
4713
  case 3:
4358
4714
  unresovedTemplates_1 = unresovedTemplates_1.filter(function (template) { return template !== currentTemplate; });
@@ -4387,24 +4743,24 @@
4387
4743
  // <- Note: Errors are catched here [3]
4388
4744
  // TODO: BUT if in multiple templates are errors, only the first one is catched so maybe we should catch errors here and save them to errors array here
4389
4745
  resolving_1.push(work_1);
4390
- _j.label = 4;
4746
+ _k.label = 4;
4391
4747
  case 4: return [2 /*return*/];
4392
4748
  }
4393
4749
  });
4394
4750
  };
4395
- _g.label = 21;
4751
+ _h.label = 21;
4396
4752
  case 21:
4397
4753
  if (!(unresovedTemplates_1.length > 0)) return [3 /*break*/, 23];
4398
4754
  return [5 /*yield**/, _loop_2()];
4399
4755
  case 22:
4400
- _g.sent();
4756
+ _h.sent();
4401
4757
  return [3 /*break*/, 21];
4402
4758
  case 23: return [4 /*yield*/, Promise.all(resolving_1)];
4403
4759
  case 24:
4404
- _g.sent();
4760
+ _h.sent();
4405
4761
  return [3 /*break*/, 28];
4406
4762
  case 25:
4407
- error_1 = _g.sent();
4763
+ error_1 = _h.sent();
4408
4764
  if (!(error_1 instanceof Error)) {
4409
4765
  throw error_1;
4410
4766
  }
@@ -4424,8 +4780,8 @@
4424
4780
  return [4 /*yield*/, waitasecond.forTime(IMMEDIATE_TIME)];
4425
4781
  case 26:
4426
4782
  // Note: Wait a short time to prevent race conditions
4427
- _g.sent();
4428
- _g.label = 27;
4783
+ _h.sent();
4784
+ _h.label = 27;
4429
4785
  case 27: return [2 /*return*/, $asDeeplyFrozenSerializableJson('Unuccessful PipelineExecutorResult (with misc errors) PipelineExecutorResult', {
4430
4786
  isSuccessful: false,
4431
4787
  errors: __spreadArray([error_1], __read(errors), false).map(serializeError),
@@ -4452,8 +4808,8 @@
4452
4808
  return [4 /*yield*/, waitasecond.forTime(IMMEDIATE_TIME)];
4453
4809
  case 29:
4454
4810
  // Note: Wait a short time to prevent race conditions
4455
- _g.sent();
4456
- _g.label = 30;
4811
+ _h.sent();
4812
+ _h.label = 30;
4457
4813
  case 30: return [2 /*return*/, $asDeeplyFrozenSerializableJson('Successful PipelineExecutorResult', {
4458
4814
  isSuccessful: true,
4459
4815
  errors: errors.map(serializeError),
@@ -4468,208 +4824,881 @@
4468
4824
  });
4469
4825
  }
4470
4826
  /**
4471
- * TODO: [🐚] Change onProgress to object that represents the running execution, can be subscribed via RxJS to and also awaited
4827
+ * TODO: [🐚] Change onProgress to object that represents the running execution, can be subscribed via RxJS to and also awaited
4828
+ */
4829
+
4830
+ /**
4831
+ * Creates executor function from pipeline and execution tools.
4832
+ *
4833
+ * @returns The executor function
4834
+ * @throws {PipelineLogicError} on logical error in the pipeline
4835
+ * @public exported from `@promptbook/core`
4836
+ */
4837
+ function createPipelineExecutor(options) {
4838
+ var _this = this;
4839
+ var pipeline = options.pipeline, tools = options.tools, _a = options.settings, settings = _a === void 0 ? {} : _a;
4840
+ var _b = settings.maxExecutionAttempts, maxExecutionAttempts = _b === void 0 ? MAX_EXECUTION_ATTEMPTS : _b, _c = settings.maxParallelCount, maxParallelCount = _c === void 0 ? MAX_PARALLEL_COUNT : _c, _d = settings.csvSettings, csvSettings = _d === void 0 ? DEFAULT_CSV_SETTINGS : _d, _e = settings.isVerbose, isVerbose = _e === void 0 ? IS_VERBOSE : _e, _f = settings.isNotPreparedWarningSupressed, isNotPreparedWarningSupressed = _f === void 0 ? false : _f, _g = settings.rootDirname, rootDirname = _g === void 0 ? null : _g;
4841
+ validatePipeline(pipeline);
4842
+ var pipelineIdentification = (function () {
4843
+ // Note: This is a 😐 implementation of [🚞]
4844
+ var _ = [];
4845
+ if (pipeline.sourceFile !== undefined) {
4846
+ _.push("File: ".concat(pipeline.sourceFile));
4847
+ }
4848
+ if (pipeline.pipelineUrl !== undefined) {
4849
+ _.push("Url: ".concat(pipeline.pipelineUrl));
4850
+ }
4851
+ return _.join('\n');
4852
+ })();
4853
+ var preparedPipeline;
4854
+ if (isPipelinePrepared(pipeline)) {
4855
+ preparedPipeline = pipeline;
4856
+ }
4857
+ else if (isNotPreparedWarningSupressed !== true) {
4858
+ console.warn(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is not prepared\n\n ".concat(block(pipelineIdentification), "\n\n It will be prepared ad-hoc before the first execution and **returned as `preparedPipeline` in `PipelineExecutorResult`**\n But it is recommended to prepare the pipeline during collection preparation\n\n @see more at https://ptbk.io/prepare-pipeline\n "); }));
4859
+ }
4860
+ var runCount = 0;
4861
+ var pipelineExecutor = function (inputParameters, onProgress) { return __awaiter(_this, void 0, void 0, function () {
4862
+ return __generator(this, function (_a) {
4863
+ runCount++;
4864
+ return [2 /*return*/, /* not await */ executePipeline({
4865
+ pipeline: pipeline,
4866
+ preparedPipeline: preparedPipeline,
4867
+ setPreparedPipeline: function (newPreparedPipeline) {
4868
+ preparedPipeline = newPreparedPipeline;
4869
+ },
4870
+ inputParameters: inputParameters,
4871
+ tools: tools,
4872
+ onProgress: onProgress,
4873
+ pipelineIdentification: spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(pipelineIdentification), "\n ").concat(runCount === 1 ? '' : "Run #".concat(runCount), "\n "); }),
4874
+ settings: {
4875
+ maxExecutionAttempts: maxExecutionAttempts,
4876
+ maxParallelCount: maxParallelCount,
4877
+ csvSettings: csvSettings,
4878
+ isVerbose: isVerbose,
4879
+ isNotPreparedWarningSupressed: isNotPreparedWarningSupressed,
4880
+ rootDirname: rootDirname,
4881
+ },
4882
+ })];
4883
+ });
4884
+ }); };
4885
+ return pipelineExecutor;
4886
+ }
4887
+ /**
4888
+ * TODO: [🐚] Change onProgress to object that represents the running execution, can be subscribed via RxJS to and also awaited
4889
+ */
4890
+
4891
+ /**
4892
+ * Scraper for markdown files
4893
+ *
4894
+ * @see `documentationUrl` for more details
4895
+ * @public exported from `@promptbook/core`
4896
+ */
4897
+ var markdownScraper = {
4898
+ /**
4899
+ * Mime types that this scraper can handle
4900
+ */
4901
+ mimeTypes: ['text/markdown', 'text/plain'],
4902
+ /**
4903
+ * Link to documentation
4904
+ */
4905
+ documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
4906
+ /**
4907
+ * Scrapes the markdown file and returns the knowledge pieces or `null` if it can't scrape it
4908
+ */
4909
+ scrape: function (source, options) {
4910
+ return __awaiter(this, void 0, void 0, function () {
4911
+ var llmTools, _a, maxParallelCount, _b, isVerbose, collection, prepareKnowledgeFromMarkdownExecutor, _c, prepareTitleExecutor, _d, prepareKeywordsExecutor, _e, knowledgeContent, result, outputParameters, knowledgePiecesRaw, knowledgeTextPieces, knowledge;
4912
+ var _f, _g, _h;
4913
+ var _this = this;
4914
+ return __generator(this, function (_j) {
4915
+ switch (_j.label) {
4916
+ case 0:
4917
+ llmTools = options.llmTools, _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? MAX_PARALLEL_COUNT : _a, _b = options.isVerbose, isVerbose = _b === void 0 ? IS_VERBOSE : _b;
4918
+ if (llmTools === undefined) {
4919
+ throw new MissingToolsError('LLM tools are required for scraping external files');
4920
+ // <- Note: This scraper is used in all other scrapers, so saying "external files" not "markdown files"
4921
+ }
4922
+ TODO_USE(maxParallelCount); // <- [🪂]
4923
+ collection = createCollectionFromJson.apply(void 0, __spreadArray([], __read(PipelineCollection), false));
4924
+ _c = createPipelineExecutor;
4925
+ _f = {};
4926
+ return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.ptbk.md')];
4927
+ case 1:
4928
+ prepareKnowledgeFromMarkdownExecutor = _c.apply(void 0, [(_f.pipeline = _j.sent(),
4929
+ _f.tools = {
4930
+ llm: llmTools,
4931
+ },
4932
+ _f)]);
4933
+ _d = createPipelineExecutor;
4934
+ _g = {};
4935
+ return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-title.ptbk.md')];
4936
+ case 2:
4937
+ prepareTitleExecutor = _d.apply(void 0, [(_g.pipeline = _j.sent(),
4938
+ _g.tools = {
4939
+ llm: llmTools,
4940
+ },
4941
+ _g)]);
4942
+ _e = createPipelineExecutor;
4943
+ _h = {};
4944
+ return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-keywords.ptbk.md')];
4945
+ case 3:
4946
+ prepareKeywordsExecutor = _e.apply(void 0, [(_h.pipeline = _j.sent(),
4947
+ _h.tools = {
4948
+ llm: llmTools,
4949
+ },
4950
+ _h)]);
4951
+ return [4 /*yield*/, source.asText()];
4952
+ case 4:
4953
+ knowledgeContent = _j.sent();
4954
+ return [4 /*yield*/, prepareKnowledgeFromMarkdownExecutor({ knowledgeContent: knowledgeContent })];
4955
+ case 5:
4956
+ result = _j.sent();
4957
+ assertsExecutionSuccessful(result);
4958
+ outputParameters = result.outputParameters;
4959
+ knowledgePiecesRaw = outputParameters.knowledgePieces;
4960
+ knowledgeTextPieces = (knowledgePiecesRaw || '').split('\n---\n');
4961
+ // <- TODO: [main] !!!!! Smarter split and filter out empty pieces
4962
+ if (isVerbose) {
4963
+ console.info('knowledgeTextPieces:', knowledgeTextPieces);
4964
+ }
4965
+ return [4 /*yield*/, Promise.all(
4966
+ // TODO: [🪂] !! Do not send all at once but in chunks
4967
+ knowledgeTextPieces.map(function (knowledgeTextPiece, i) { return __awaiter(_this, void 0, void 0, function () {
4968
+ var name, title, knowledgePieceContent, keywords, index, titleResult, _a, titleRaw, keywordsResult, _b, keywordsRaw, embeddingResult, error_1;
4969
+ return __generator(this, function (_c) {
4970
+ switch (_c.label) {
4971
+ case 0:
4972
+ name = "piece-".concat(i);
4973
+ title = spaceTrim__default["default"](knowledgeTextPiece.substring(0, 100));
4974
+ knowledgePieceContent = spaceTrim__default["default"](knowledgeTextPiece);
4975
+ keywords = [];
4976
+ index = [];
4977
+ _c.label = 1;
4978
+ case 1:
4979
+ _c.trys.push([1, 7, , 8]);
4980
+ return [4 /*yield*/, prepareTitleExecutor({ knowledgePieceContent: knowledgePieceContent })];
4981
+ case 2:
4982
+ titleResult = _c.sent();
4983
+ _a = titleResult.outputParameters.title, titleRaw = _a === void 0 ? 'Untitled' : _a;
4984
+ title = spaceTrim__default["default"](titleRaw) /* <- TODO: Maybe do in pipeline */;
4985
+ name = titleToName(title);
4986
+ return [4 /*yield*/, prepareKeywordsExecutor({ knowledgePieceContent: knowledgePieceContent })];
4987
+ case 3:
4988
+ keywordsResult = _c.sent();
4989
+ _b = keywordsResult.outputParameters.keywords, keywordsRaw = _b === void 0 ? '' : _b;
4990
+ keywords = (keywordsRaw || '')
4991
+ .split(',')
4992
+ .map(function (keyword) { return keyword.trim(); })
4993
+ .filter(function (keyword) { return keyword !== ''; });
4994
+ if (isVerbose) {
4995
+ console.info("Keywords for \"".concat(title, "\":"), keywords);
4996
+ }
4997
+ if (!!llmTools.callEmbeddingModel) return [3 /*break*/, 4];
4998
+ // TODO: [🟥] Detect browser / node and make it colorfull
4999
+ console.error('No callEmbeddingModel function provided');
5000
+ return [3 /*break*/, 6];
5001
+ case 4: return [4 /*yield*/, llmTools.callEmbeddingModel({
5002
+ title: "Embedding for ".concat(title) /* <- Note: No impact on embedding result itself, just for logging */,
5003
+ parameters: {},
5004
+ content: knowledgePieceContent,
5005
+ modelRequirements: {
5006
+ modelVariant: 'EMBEDDING',
5007
+ },
5008
+ })];
5009
+ case 5:
5010
+ embeddingResult = _c.sent();
5011
+ index.push({
5012
+ modelName: embeddingResult.modelName,
5013
+ position: embeddingResult.content,
5014
+ });
5015
+ _c.label = 6;
5016
+ case 6: return [3 /*break*/, 8];
5017
+ case 7:
5018
+ error_1 = _c.sent();
5019
+ // Note: Here is expected error:
5020
+ // > PipelineExecutionError: You have not provided any `LlmExecutionTools` that support model variant "EMBEDDING
5021
+ if (!(error_1 instanceof PipelineExecutionError)) {
5022
+ throw error_1;
5023
+ }
5024
+ // TODO: [🟥] Detect browser / node and make it colorfull
5025
+ console.error(error_1, "<- Note: This error is not critical to prepare the pipeline, just knowledge pieces won't have embeddings");
5026
+ return [3 /*break*/, 8];
5027
+ case 8: return [2 /*return*/, {
5028
+ name: name,
5029
+ title: title,
5030
+ content: knowledgePieceContent,
5031
+ keywords: keywords,
5032
+ index: index,
5033
+ // <- TODO: [☀] sources,
5034
+ }];
5035
+ }
5036
+ });
5037
+ }); }))];
5038
+ case 6:
5039
+ knowledge = _j.sent();
5040
+ return [2 /*return*/, knowledge];
5041
+ }
5042
+ });
5043
+ });
5044
+ },
5045
+ } /* TODO: [🦷] as const */;
5046
+ /**
5047
+ * TODO: [🦖] Make some system for putting scrapers to separete packages
5048
+ * TODO: [🪂] Do it in parallel 11:11
5049
+ * TODO: [🦷] Ideally use `as const satisfies Scraper` BUT this combination throws errors
5050
+ * Note: No need to aggregate usage here, it is done by intercepting the llmTools
5051
+ */
5052
+
5053
+ /**
5054
+ * Scraper of .docx and .odt files
5055
+ *
5056
+ * @see `documentationUrl` for more details
5057
+ * @public exported from `@promptbook/core`
5058
+ */
5059
+ var documentScraper = {
5060
+ /**
5061
+ * Mime types that this scraper can handle
5062
+ */
5063
+ mimeTypes: ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
5064
+ /**
5065
+ * Link to documentation
5066
+ */
5067
+ documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
5068
+ /**
5069
+ * Convert the `.docx` or `.odt` to `.md` file and returns intermediate source
5070
+ *
5071
+ * Note: `$` is used to indicate that this function is not a pure function - it leaves files on the disk and you are responsible for cleaning them by calling `destroy` method of returned object
5072
+ */
5073
+ $convert: function (source, options) {
5074
+ return __awaiter(this, void 0, void 0, function () {
5075
+ var _a, externalProgramsPaths, rootDirname, _b, cacheDirname, _c, isCacheCleaned, _d, isVerbose, extension, cacheFilehandler, command_1;
5076
+ return __generator(this, function (_e) {
5077
+ switch (_e.label) {
5078
+ case 0:
5079
+ _a = options.externalProgramsPaths, externalProgramsPaths = _a === void 0 ? {} : _a, rootDirname = options.rootDirname, _b = options.cacheDirname, cacheDirname = _b === void 0 ? SCRAPE_CACHE_DIRNAME : _b, _c = options.isCacheCleaned, isCacheCleaned = _c === void 0 ? false : _c, _d = options.isVerbose, isVerbose = _d === void 0 ? IS_VERBOSE : _d;
5080
+ if (!$isRunningInNode()) {
5081
+ throw new KnowledgeScrapeError('Scraping .docx files is only supported in Node environment');
5082
+ }
5083
+ if (externalProgramsPaths.pandocPath === undefined) {
5084
+ throw new MissingToolsError('Pandoc is required for scraping .docx files');
5085
+ }
5086
+ if (source.filename === null) {
5087
+ // TODO: [🧠] Maybe save file as temporary
5088
+ throw new KnowledgeScrapeError('When parsing .docx file, it must be real file in the file system');
5089
+ }
5090
+ extension = getFileExtension(source.filename);
5091
+ return [4 /*yield*/, getScraperIntermediateSource(source, {
5092
+ rootDirname: rootDirname,
5093
+ cacheDirname: cacheDirname,
5094
+ isCacheCleaned: isCacheCleaned,
5095
+ extension: 'md',
5096
+ isVerbose: isVerbose,
5097
+ })];
5098
+ case 1:
5099
+ cacheFilehandler = _e.sent();
5100
+ return [4 /*yield*/, $isFileExisting(cacheFilehandler.filename)];
5101
+ case 2:
5102
+ if (!!(_e.sent())) return [3 /*break*/, 5];
5103
+ command_1 = "\"".concat(externalProgramsPaths.pandocPath, "\" -f ").concat(extension, " -t markdown \"").concat(source.filename, "\" -o \"").concat(cacheFilehandler.filename, "\"");
5104
+ // TODO: !!!!!! [🕊] Make execCommand standard (?node-)util of the promptbook
5105
+ return [4 /*yield*/, $execCommand(command_1)];
5106
+ case 3:
5107
+ // TODO: !!!!!! [🕊] Make execCommand standard (?node-)util of the promptbook
5108
+ _e.sent();
5109
+ return [4 /*yield*/, $isFileExisting(cacheFilehandler.filename)];
5110
+ case 4:
5111
+ // Note: [0]
5112
+ if (!(_e.sent())) {
5113
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n File that was supposed to be created by Pandoc does not exist for unknown reason\n\n Expected file:\n ".concat(block(cacheFilehandler.filename), "\n\n Command:\n > ").concat(block(command_1), "\n\n "); }));
5114
+ }
5115
+ _e.label = 5;
5116
+ case 5: return [2 /*return*/, cacheFilehandler];
5117
+ }
5118
+ });
5119
+ });
5120
+ },
5121
+ /**
5122
+ * Scrapes the docx file and returns the knowledge pieces or `null` if it can't scrape it
5123
+ */
5124
+ scrape: function (source, options) {
5125
+ return __awaiter(this, void 0, void 0, function () {
5126
+ var cacheFilehandler, markdownSource, knowledge;
5127
+ return __generator(this, function (_a) {
5128
+ switch (_a.label) {
5129
+ case 0: return [4 /*yield*/, documentScraper.$convert(source, options)];
5130
+ case 1:
5131
+ cacheFilehandler = _a.sent();
5132
+ markdownSource = {
5133
+ source: source.source,
5134
+ filename: cacheFilehandler.filename,
5135
+ url: null,
5136
+ mimeType: 'text/markdown',
5137
+ asText: function () {
5138
+ return __awaiter(this, void 0, void 0, function () {
5139
+ return __generator(this, function (_a) {
5140
+ switch (_a.label) {
5141
+ case 0: return [4 /*yield*/, promises.readFile(cacheFilehandler.filename, 'utf-8')];
5142
+ case 1:
5143
+ // Note: [0] In $convert we check that the file exists
5144
+ return [2 /*return*/, _a.sent()];
5145
+ }
5146
+ });
5147
+ });
5148
+ },
5149
+ asJson: function () {
5150
+ throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
5151
+ },
5152
+ asBlob: function () {
5153
+ throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asBlob`');
5154
+ },
5155
+ };
5156
+ knowledge = markdownScraper.scrape(markdownSource, options);
5157
+ return [4 /*yield*/, cacheFilehandler.destroy()];
5158
+ case 2:
5159
+ _a.sent();
5160
+ return [2 /*return*/, knowledge];
5161
+ }
5162
+ });
5163
+ });
5164
+ },
5165
+ } /* TODO: [🦷] as const */;
5166
+ /**
5167
+ * TODO: [👣] Converted documents can act as cached items - there is no need to run conversion each time
5168
+ * TODO: [🦖] Make some system for putting scrapers to separete packages
5169
+ * TODO: [🪂] Do it in parallel 11:11
5170
+ * TODO: [🦷] Ideally use `as const satisfies Converter & Scraper` BUT this combination throws errors
5171
+ * Note: No need to aggregate usage here, it is done by intercepting the llmTools
5172
+ */
5173
+
5174
+ /**
5175
+ * Scraper for .docx files
5176
+ *
5177
+ * @see `documentationUrl` for more details
5178
+ * @public exported from `@promptbook/core`
5179
+ */
5180
+ var legacyDocumentScraper = {
5181
+ /**
5182
+ * Mime types that this scraper can handle
5183
+ */
5184
+ mimeTypes: ['application/msword', 'text/rtf'],
5185
+ /**
5186
+ * Link to documentation
5187
+ */
5188
+ documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
5189
+ /**
5190
+ * Convert the `.doc` or `.rtf` to `.doc` file and returns intermediate source
5191
+ *
5192
+ * Note: `$` is used to indicate that this function is not a pure function - it leaves files on the disk and you are responsible for cleaning them by calling `destroy` method of returned object
5193
+ */
5194
+ $convert: function (source, options) {
5195
+ return __awaiter(this, void 0, void 0, function () {
5196
+ var _a, externalProgramsPaths, rootDirname, _b, cacheDirname, _c, isCacheCleaned, _d, isVerbose, extension, cacheFilehandler, documentSourceOutdirPathForLibreOffice_1, command_1, files_1, file;
5197
+ return __generator(this, function (_e) {
5198
+ switch (_e.label) {
5199
+ case 0:
5200
+ _a = options.externalProgramsPaths, externalProgramsPaths = _a === void 0 ? {} : _a, rootDirname = options.rootDirname, _b = options.cacheDirname, cacheDirname = _b === void 0 ? SCRAPE_CACHE_DIRNAME : _b, _c = options.isCacheCleaned, isCacheCleaned = _c === void 0 ? false : _c, _d = options.isVerbose, isVerbose = _d === void 0 ? IS_VERBOSE : _d;
5201
+ if (!$isRunningInNode()) {
5202
+ throw new KnowledgeScrapeError('Scraping .doc files is only supported in Node environment');
5203
+ }
5204
+ if (externalProgramsPaths.libreOfficePath === undefined) {
5205
+ throw new MissingToolsError('LibreOffice is required for scraping .doc and .rtf files');
5206
+ }
5207
+ if (source.filename === null) {
5208
+ // TODO: [🧠] Maybe save file as temporary
5209
+ throw new KnowledgeScrapeError('When parsing .doc or .rtf file, it must be real file in the file system');
5210
+ }
5211
+ extension = getFileExtension(source.filename);
5212
+ return [4 /*yield*/, getScraperIntermediateSource(source, {
5213
+ rootDirname: rootDirname,
5214
+ cacheDirname: cacheDirname,
5215
+ isCacheCleaned: isCacheCleaned,
5216
+ extension: 'docx',
5217
+ isVerbose: isVerbose,
5218
+ })];
5219
+ case 1:
5220
+ cacheFilehandler = _e.sent();
5221
+ if (isVerbose) {
5222
+ console.info("documentScraper: Converting .".concat(extension, " -> .docx"));
5223
+ }
5224
+ return [4 /*yield*/, $isFileExisting(cacheFilehandler.filename)];
5225
+ case 2:
5226
+ if (!!(_e.sent())) return [3 /*break*/, 8];
5227
+ documentSourceOutdirPathForLibreOffice_1 = path.join(path.dirname(cacheFilehandler.filename), 'libreoffice')
5228
+ .split('\\')
5229
+ .join('/');
5230
+ command_1 = "\"".concat(externalProgramsPaths.libreOfficePath, "\" --headless --convert-to docx \"").concat(source.filename, "\" --outdir \"").concat(documentSourceOutdirPathForLibreOffice_1, "\"");
5231
+ // TODO: !!!!!! [🕊] Make execCommand standard (?node-)util of the promptbook - this should trigger build polution error
5232
+ return [4 /*yield*/, $execCommand(command_1)];
5233
+ case 3:
5234
+ // TODO: !!!!!! [🕊] Make execCommand standard (?node-)util of the promptbook - this should trigger build polution error
5235
+ _e.sent();
5236
+ return [4 /*yield*/, promises.readdir(documentSourceOutdirPathForLibreOffice_1)];
5237
+ case 4:
5238
+ files_1 = _e.sent();
5239
+ if (files_1.length !== 1) {
5240
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n Expected exactly 1 file in the LibreOffice output directory, got ".concat(files_1.length, "\n\n The temporary folder:\n ").concat(block(documentSourceOutdirPathForLibreOffice_1), "\n\n Command:\n > ").concat(block(command_1), "\n "); }));
5241
+ }
5242
+ file = files_1[0];
5243
+ return [4 /*yield*/, promises.rename(path.join(documentSourceOutdirPathForLibreOffice_1, file), cacheFilehandler.filename)];
5244
+ case 5:
5245
+ _e.sent();
5246
+ return [4 /*yield*/, promises.rmdir(documentSourceOutdirPathForLibreOffice_1)];
5247
+ case 6:
5248
+ _e.sent();
5249
+ return [4 /*yield*/, $isFileExisting(cacheFilehandler.filename)];
5250
+ case 7:
5251
+ if (!(_e.sent())) {
5252
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n File that was supposed to be created by LibreOffice does not exist for unknown reason\n\n Expected file:\n ".concat(block(cacheFilehandler.filename), "\n\n The temporary folder:\n ").concat(block(documentSourceOutdirPathForLibreOffice_1), "\n\n Command:\n > ").concat(block(command_1), "\n\n "); }));
5253
+ }
5254
+ _e.label = 8;
5255
+ case 8: return [2 /*return*/, cacheFilehandler];
5256
+ }
5257
+ });
5258
+ });
5259
+ },
5260
+ /**
5261
+ * Scrapes the `.doc` or `.rtf` file and returns the knowledge pieces or `null` if it can't scrape it
5262
+ */
5263
+ scrape: function (source, options) {
5264
+ return __awaiter(this, void 0, void 0, function () {
5265
+ var cacheFilehandler, markdownSource, knowledge;
5266
+ return __generator(this, function (_a) {
5267
+ switch (_a.label) {
5268
+ case 0: return [4 /*yield*/, legacyDocumentScraper.$convert(source, options)];
5269
+ case 1:
5270
+ cacheFilehandler = _a.sent();
5271
+ markdownSource = {
5272
+ source: source.source,
5273
+ filename: cacheFilehandler.filename,
5274
+ url: null,
5275
+ mimeType: 'text/markdown',
5276
+ asText: function () {
5277
+ throw new UnexpectedError('Did not expect that `documentScraper` would need to get the content `asText`');
5278
+ },
5279
+ asJson: function () {
5280
+ throw new UnexpectedError('Did not expect that `documentScraper` would need to get the content `asJson`');
5281
+ },
5282
+ asBlob: function () {
5283
+ throw new UnexpectedError('Did not expect that `documentScraper` would need to get the content `asBlob`');
5284
+ },
5285
+ };
5286
+ knowledge = documentScraper.scrape(markdownSource, options);
5287
+ return [4 /*yield*/, cacheFilehandler.destroy()];
5288
+ case 2:
5289
+ _a.sent();
5290
+ return [2 /*return*/, knowledge];
5291
+ }
5292
+ });
5293
+ });
5294
+ },
5295
+ } /* TODO: [🦷] as const */;
5296
+ /**
5297
+ * TODO: [👣] Converted documents can act as cached items - there is no need to run conversion each time
5298
+ * TODO: [🦖] Make some system for putting scrapers to separete packages
5299
+ * TODO: [🪂] Do it in parallel 11:11
5300
+ * TODO: [🦷] Ideally use `as const satisfies Converter & Scraper` BUT this combination throws errors
5301
+ * Note: No need to aggregate usage here, it is done by intercepting the llmTools
5302
+ */
5303
+
5304
+ /**
5305
+ * Scraper for .docx files
5306
+ *
5307
+ * @see `documentationUrl` for more details
5308
+ * @public exported from `@promptbook/core`
5309
+ */
5310
+ var pdfScraper = {
5311
+ /**
5312
+ * Mime types that this scraper can handle
5313
+ */
5314
+ mimeTypes: ['application/pdf'],
5315
+ /**
5316
+ * Link to documentation
5317
+ */
5318
+ documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
5319
+ /**
5320
+ * Converts the `.pdf` file to `.md` file and returns intermediate source
5321
+ */
5322
+ $convert: function (source, options) {
5323
+ return __awaiter(this, void 0, void 0, function () {
5324
+ return __generator(this, function (_a) {
5325
+ TODO_USE(source);
5326
+ TODO_USE(options);
5327
+ throw new NotYetImplementedError('PDF conversion not yet implemented');
5328
+ });
5329
+ });
5330
+ },
5331
+ /**
5332
+ * Scrapes the `.pdf` file and returns the knowledge pieces or `null` if it can't scrape it
5333
+ */
5334
+ scrape: function (source, options) {
5335
+ return __awaiter(this, void 0, void 0, function () {
5336
+ return __generator(this, function (_a) {
5337
+ TODO_USE(source);
5338
+ TODO_USE(options);
5339
+ /*
5340
+ const {
5341
+ externalProgramsPaths = {},
5342
+ cacheDirname = SCRAPE_CACHE_DIRNAME,
5343
+ isCacheCleaned = false,
5344
+ isVerbose = IS_VERBOSE,
5345
+ } = options;
5346
+ */
5347
+ throw new NotYetImplementedError('PDF scraping not yet implemented');
5348
+ });
5349
+ });
5350
+ },
5351
+ } /* TODO: [🦷] as const */;
5352
+ /**
5353
+ * TODO: [👣] Converted documents can act as cached items - there is no need to run conversion each time
5354
+ * TODO: [🦖] Make some system for putting scrapers to separete packages
5355
+ * TODO: [🪂] Do it in parallel 11:11
5356
+ * TODO: [🦷] Ideally use `as const satisfies Converter & Scraper` BUT this combination throws errors
5357
+ * Note: No need to aggregate usage here, it is done by intercepting the llmTools
5358
+ */
5359
+
5360
+ /**
5361
+ * A converter instance that uses showdown and highlight extensions
5362
+ *
5363
+ * @type {Converter}
5364
+ * @private for markdown and html knowledge scrapers
5365
+ */
5366
+ var markdownConverter = new showdown.Converter({
5367
+ flavor: 'github', // <- TODO: !!!!!! Explicitly specify the flavor of promptbook markdown
5368
+ /*
5369
+ > import showdownHighlight from 'showdown-highlight';
5370
+ > extensions: [
5371
+ > showdownHighlight({
5372
+ > // Whether to add the classes to the <pre> tag, default is false
5373
+ > pre: true,
5374
+ > // Whether to use hljs' auto language detection, default is true
5375
+ > auto_detection: true,
5376
+ > }),
5377
+ > ],
5378
+ */
5379
+ });
5380
+ /**
5381
+ * TODO: !!!!!! Figure out better name not to confuse with `Converter`
5382
+ * TODO: !!!!!! Lazy-make converter
5383
+ */
5384
+
5385
+ /**
5386
+ * Scraper for .docx files
5387
+ *
5388
+ * @see `documentationUrl` for more details
5389
+ * @public exported from `@promptbook/core`
5390
+ */
5391
+ var websiteScraper = {
5392
+ /**
5393
+ * Mime types that this scraper can handle
5394
+ */
5395
+ mimeTypes: ['text/html'],
5396
+ /**
5397
+ * Link to documentation
5398
+ */
5399
+ documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
5400
+ /**
5401
+ * Convert the website to `.md` file and returns intermediate source
5402
+ *
5403
+ * Note: `$` is used to indicate that this function is not a pure function - it leaves files on the disk and you are responsible for cleaning them by calling `destroy` method of returned object
5404
+ */
5405
+ $convert: function (source, options) {
5406
+ return __awaiter(this, void 0, void 0, function () {
5407
+ var
5408
+ // TODO: [🧠] Maybe in node use headless browser not just JSDOM
5409
+ // externalProgramsPaths = {},
5410
+ rootDirname, _a, cacheDirname, _b, isCacheCleaned, _c, isVerbose, jsdom$1, _d, reader, article, html, i, cacheFilehandler, markdown;
5411
+ return __generator(this, function (_e) {
5412
+ switch (_e.label) {
5413
+ case 0:
5414
+ rootDirname = options.rootDirname, _a = options.cacheDirname, cacheDirname = _a === void 0 ? SCRAPE_CACHE_DIRNAME : _a, _b = options.isCacheCleaned, isCacheCleaned = _b === void 0 ? false : _b, _c = options.isVerbose, isVerbose = _c === void 0 ? IS_VERBOSE : _c;
5415
+ // TODO: !!!!!! Does this work in browser? Make it work.
5416
+ if (source.url === null) {
5417
+ throw new KnowledgeScrapeError('Website scraper requires URL');
5418
+ }
5419
+ _d = jsdom.JSDOM.bind;
5420
+ return [4 /*yield*/, source.asText()];
5421
+ case 1:
5422
+ jsdom$1 = new (_d.apply(jsdom.JSDOM, [void 0, _e.sent(), {
5423
+ url: source.url,
5424
+ }]))();
5425
+ reader = new readability.Readability(jsdom$1.window.document);
5426
+ article = reader.parse();
5427
+ console.log(article);
5428
+ return [4 /*yield*/, waitasecond.forTime(10000)];
5429
+ case 2:
5430
+ _e.sent();
5431
+ html = (article === null || article === void 0 ? void 0 : article.content) || (article === null || article === void 0 ? void 0 : article.textContent) || jsdom$1.window.document.body.innerHTML;
5432
+ // Note: Unwrap html such as it is convertable by `markdownConverter`
5433
+ for (i = 0; i < 2; i++) {
5434
+ html = html.replace(/<div\s*(?:id="readability-page-\d+"\s+class="page")?>(.*)<\/div>/is, '$1');
5435
+ }
5436
+ if (html.includes('<div')) {
5437
+ html = (article === null || article === void 0 ? void 0 : article.textContent) || '';
5438
+ }
5439
+ return [4 /*yield*/, getScraperIntermediateSource(source, {
5440
+ rootDirname: rootDirname,
5441
+ cacheDirname: cacheDirname,
5442
+ isCacheCleaned: isCacheCleaned,
5443
+ extension: 'html',
5444
+ isVerbose: isVerbose,
5445
+ })];
5446
+ case 3:
5447
+ cacheFilehandler = _e.sent();
5448
+ return [4 /*yield*/, promises.writeFile(cacheFilehandler.filename, html, 'utf-8')];
5449
+ case 4:
5450
+ _e.sent();
5451
+ markdown = markdownConverter.makeMarkdown(html, jsdom$1.window.document);
5452
+ return [2 /*return*/, __assign(__assign({}, cacheFilehandler), { markdown: markdown })];
5453
+ }
5454
+ });
5455
+ });
5456
+ },
5457
+ /**
5458
+ * Scrapes the website and returns the knowledge pieces or `null` if it can't scrape it
5459
+ */
5460
+ scrape: function (source, options) {
5461
+ return __awaiter(this, void 0, void 0, function () {
5462
+ var cacheFilehandler, markdownSource, knowledge;
5463
+ return __generator(this, function (_a) {
5464
+ switch (_a.label) {
5465
+ case 0: return [4 /*yield*/, websiteScraper.$convert(source, options)];
5466
+ case 1:
5467
+ cacheFilehandler = _a.sent();
5468
+ markdownSource = {
5469
+ source: source.source,
5470
+ filename: cacheFilehandler.filename,
5471
+ url: null,
5472
+ mimeType: 'text/markdown',
5473
+ asText: function () {
5474
+ return cacheFilehandler.markdown;
5475
+ },
5476
+ asJson: function () {
5477
+ throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
5478
+ },
5479
+ asBlob: function () {
5480
+ throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asBlob`');
5481
+ },
5482
+ };
5483
+ knowledge = markdownScraper.scrape(markdownSource, options);
5484
+ return [4 /*yield*/, cacheFilehandler.destroy()];
5485
+ case 2:
5486
+ _a.sent();
5487
+ return [2 /*return*/, knowledge];
5488
+ }
5489
+ });
5490
+ });
5491
+ },
5492
+ } /* TODO: [🦷] as const */;
5493
+ /**
5494
+ * TODO: !!!!!! Put into separate package
5495
+ * TODO: [👣] Scraped website in .md can act as cache item - there is no need to run conversion each time
5496
+ * TODO: [🦖] Make some system for putting scrapers to separete packages
5497
+ * TODO: [🪂] Do it in parallel 11:11
5498
+ * TODO: [🦷] Ideally use `as const satisfies Converter & Scraper` BUT this combination throws errors
5499
+ * Note: No need to aggregate usage here, it is done by intercepting the llmTools
4472
5500
  */
4473
5501
 
5502
+ // TODO: [🦖] !!!!!! Pass scrapers as dependency,
4474
5503
  /**
4475
- * Creates executor function from pipeline and execution tools.
5504
+ * @@@
4476
5505
  *
4477
- * @returns The executor function
4478
- * @throws {PipelineLogicError} on logical error in the pipeline
4479
- * @public exported from `@promptbook/core`
5506
+ * @private because this will be replaced by a system of one scraper per package [🦖]
5507
+ * TODO: [🦖] System for scrapers NOT public exported from `@promptbook/core`
4480
5508
  */
4481
- function createPipelineExecutor(options) {
4482
- var _this = this;
4483
- var pipeline = options.pipeline, tools = options.tools, _a = options.settings, settings = _a === void 0 ? {} : _a;
4484
- var _b = settings.maxExecutionAttempts, maxExecutionAttempts = _b === void 0 ? MAX_EXECUTION_ATTEMPTS : _b, _c = settings.maxParallelCount, maxParallelCount = _c === void 0 ? MAX_PARALLEL_COUNT : _c, _d = settings.csvSettings, csvSettings = _d === void 0 ? DEFAULT_CSV_SETTINGS : _d, _e = settings.isVerbose, isVerbose = _e === void 0 ? IS_VERBOSE : _e, _f = settings.isNotPreparedWarningSupressed, isNotPreparedWarningSupressed = _f === void 0 ? false : _f;
4485
- validatePipeline(pipeline);
4486
- var pipelineIdentification = (function () {
4487
- // Note: This is a 😐 implementation of [🚞]
4488
- var _ = [];
4489
- if (pipeline.sourceFile !== undefined) {
4490
- _.push("File: ".concat(pipeline.sourceFile));
4491
- }
4492
- if (pipeline.pipelineUrl !== undefined) {
4493
- _.push("Url: ".concat(pipeline.pipelineUrl));
4494
- }
4495
- return _.join('\n');
4496
- })();
4497
- var preparedPipeline;
4498
- if (isPipelinePrepared(pipeline)) {
4499
- preparedPipeline = pipeline;
4500
- }
4501
- else if (isNotPreparedWarningSupressed !== true) {
4502
- console.warn(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is not prepared\n\n ".concat(block(pipelineIdentification), "\n\n It will be prepared ad-hoc before the first execution and **returned as `preparedPipeline` in `PipelineExecutorResult`**\n But it is recommended to prepare the pipeline during collection preparation\n\n @see more at https://ptbk.io/prepare-pipeline\n "); }));
4503
- }
4504
- var runCount = 0;
4505
- var pipelineExecutor = function (inputParameters, onProgress) { return __awaiter(_this, void 0, void 0, function () {
4506
- return __generator(this, function (_a) {
4507
- runCount++;
4508
- return [2 /*return*/, /* not await */ executePipeline({
4509
- pipeline: pipeline,
4510
- preparedPipeline: preparedPipeline,
4511
- setPreparedPipeline: function (newPreparedPipeline) {
4512
- preparedPipeline = newPreparedPipeline;
4513
- },
4514
- inputParameters: inputParameters,
4515
- tools: tools,
4516
- onProgress: onProgress,
4517
- pipelineIdentification: spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(pipelineIdentification), "\n ").concat(runCount === 1 ? '' : "Run #".concat(runCount), "\n "); }),
4518
- settings: {
4519
- maxExecutionAttempts: maxExecutionAttempts,
4520
- maxParallelCount: maxParallelCount,
4521
- csvSettings: csvSettings,
4522
- isVerbose: isVerbose,
4523
- isNotPreparedWarningSupressed: isNotPreparedWarningSupressed,
4524
- },
4525
- })];
4526
- });
4527
- }); };
4528
- return pipelineExecutor;
5509
+ var SCRAPERS = [
5510
+ markdownScraper,
5511
+ documentScraper,
5512
+ legacyDocumentScraper,
5513
+ pdfScraper,
5514
+ websiteScraper,
5515
+ // <- Note: [♓️] This is the order of the scrapers for knowledge, BUT consider some better (more explicit) way to do this
5516
+ ];
5517
+
5518
+ /**
5519
+ * Creates unique name for the source
5520
+ *
5521
+ * @private within the repository
5522
+ */
5523
+ function sourceContentToName(sourceContent) {
5524
+ // TODO: !!!!!! Better name for source than gibberish hash
5525
+ var hash = cryptoJs.SHA256(hexEncoder__default["default"].parse(JSON.stringify(sourceContent)))
5526
+ // <- TODO: [🥬] Encapsulate sha256 to some private utility function
5527
+ .toString( /* hex */)
5528
+ .substring(0, 20);
5529
+ // <- TODO: [🥬] Make some system for hashes and ids of promptbook
5530
+ var semanticName = normalizeToKebabCase(sourceContent.substring(0, 20));
5531
+ var pieces = ['source', semanticName, hash].filter(function (piece) { return piece !== ''; });
5532
+ var name = pieces.join('-').split('--').join('-');
5533
+ // <- TODO: Use MAX_FILENAME_LENGTH
5534
+ return name;
4529
5535
  }
4530
5536
  /**
4531
- * TODO: [🐚] Change onProgress to object that represents the running execution, can be subscribed via RxJS to and also awaited
5537
+ * TODO: [🐱‍🐉][🧠] Make some smart crop NOT source-i-m-pavol-a-develop-... BUT source-i-m-pavol-a-developer-...
5538
+ */
5539
+
5540
+ /**
5541
+ * Convert file extension to mime type
5542
+ *
5543
+ * @private within the repository
4532
5544
  */
5545
+ function extensionToMimeType(value) {
5546
+ return mimeTypes.lookup(value) || 'application/octet-stream';
5547
+ }
4533
5548
 
4534
5549
  /**
4535
5550
  * @@@
4536
5551
  *
4537
- * @public exported from `@promptbook/core`
5552
+ * @private for scraper utilities
4538
5553
  */
4539
- function prepareKnowledgeFromMarkdown(knowledgeContent /* <- TODO: [🖖] (?maybe not) Always the file */, options) {
5554
+ function makeKnowledgeSourceHandler(knowledgeSource, options) {
5555
+ var _a;
4540
5556
  return __awaiter(this, void 0, void 0, function () {
4541
- var llmTools, _a, maxParallelCount, _b, isVerbose, collection, prepareKnowledgeFromMarkdownExecutor, _c, prepareTitleExecutor, _d, prepareKeywordsExecutor, _e, result, outputParameters, knowledgePiecesRaw, knowledgeTextPieces, knowledge;
4542
- var _f, _g, _h;
4543
- var _this = this;
4544
- return __generator(this, function (_j) {
4545
- switch (_j.label) {
5557
+ var sourceContent, name, _b, _c, rootDirname, _d, isVerbose, url, response_1, mimeType, filename_1, fileExtension, mimeType_1;
5558
+ return __generator(this, function (_e) {
5559
+ switch (_e.label) {
4546
5560
  case 0:
4547
- llmTools = options.llmTools, _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? MAX_PARALLEL_COUNT : _a, _b = options.isVerbose, isVerbose = _b === void 0 ? IS_VERBOSE : _b;
4548
- TODO_USE(maxParallelCount); // <- [🪂]
4549
- collection = createCollectionFromJson.apply(void 0, __spreadArray([], __read(PipelineCollection), false));
4550
- _c = createPipelineExecutor;
4551
- _f = {};
4552
- return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.ptbk.md')];
5561
+ sourceContent = knowledgeSource.sourceContent;
5562
+ name = knowledgeSource.name;
5563
+ _b = options || {}, _c = _b.rootDirname, rootDirname = _c === void 0 ? null : _c, _d = _b.isVerbose, isVerbose = _d === void 0 ? IS_VERBOSE : _d;
5564
+ TODO_USE(isVerbose);
5565
+ if (!name) {
5566
+ name = sourceContentToName(sourceContent);
5567
+ }
5568
+ if (!isValidUrl(sourceContent)) return [3 /*break*/, 2];
5569
+ url = sourceContent;
5570
+ return [4 /*yield*/, fetch(url)];
4553
5571
  case 1:
4554
- prepareKnowledgeFromMarkdownExecutor = _c.apply(void 0, [(_f.pipeline = _j.sent(),
4555
- _f.tools = {
4556
- llm: llmTools,
5572
+ response_1 = _e.sent();
5573
+ mimeType = ((_a = response_1.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.split(';')[0]) || 'text/html';
5574
+ return [2 /*return*/, {
5575
+ source: name,
5576
+ filename: null,
5577
+ url: url,
5578
+ mimeType: mimeType,
5579
+ asBlob: function () {
5580
+ return __awaiter(this, void 0, void 0, function () {
5581
+ var content;
5582
+ return __generator(this, function (_a) {
5583
+ switch (_a.label) {
5584
+ case 0: return [4 /*yield*/, response_1.blob()];
5585
+ case 1:
5586
+ content = _a.sent();
5587
+ return [2 /*return*/, content];
5588
+ }
5589
+ });
5590
+ });
4557
5591
  },
4558
- _f)]);
4559
- _d = createPipelineExecutor;
4560
- _g = {};
4561
- return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-title.ptbk.md')];
4562
- case 2:
4563
- prepareTitleExecutor = _d.apply(void 0, [(_g.pipeline = _j.sent(),
4564
- _g.tools = {
4565
- llm: llmTools,
5592
+ asJson: function () {
5593
+ return __awaiter(this, void 0, void 0, function () {
5594
+ var content;
5595
+ return __generator(this, function (_a) {
5596
+ switch (_a.label) {
5597
+ case 0: return [4 /*yield*/, response_1.json()];
5598
+ case 1:
5599
+ content = _a.sent();
5600
+ return [2 /*return*/, content];
5601
+ }
5602
+ });
5603
+ });
4566
5604
  },
4567
- _g)]);
4568
- _e = createPipelineExecutor;
4569
- _h = {};
4570
- return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-keywords.ptbk.md')];
4571
- case 3:
4572
- prepareKeywordsExecutor = _e.apply(void 0, [(_h.pipeline = _j.sent(),
4573
- _h.tools = {
4574
- llm: llmTools,
5605
+ asText: function () {
5606
+ return __awaiter(this, void 0, void 0, function () {
5607
+ var content;
5608
+ return __generator(this, function (_a) {
5609
+ switch (_a.label) {
5610
+ case 0: return [4 /*yield*/, response_1.text()];
5611
+ case 1:
5612
+ content = _a.sent();
5613
+ return [2 /*return*/, content];
5614
+ }
5615
+ });
5616
+ });
4575
5617
  },
4576
- _h)]);
4577
- return [4 /*yield*/, prepareKnowledgeFromMarkdownExecutor({ knowledgeContent: knowledgeContent })];
4578
- case 4:
4579
- result = _j.sent();
4580
- assertsExecutionSuccessful(result);
4581
- outputParameters = result.outputParameters;
4582
- knowledgePiecesRaw = outputParameters.knowledgePieces;
4583
- knowledgeTextPieces = (knowledgePiecesRaw || '').split('\n---\n');
4584
- // <- TODO: [main] !!!!! Smarter split and filter out empty pieces
4585
- if (isVerbose) {
4586
- console.info('knowledgeTextPieces:', knowledgeTextPieces);
5618
+ }];
5619
+ case 2:
5620
+ if (!(isValidFilePath(sourceContent) || /\.[a-z]{1,10}$/i.exec(sourceContent))) return [3 /*break*/, 4];
5621
+ if (!$isRunningInNode()) {
5622
+ throw new EnvironmentMismatchError('Importing knowledge source file works only in Node.js environment');
4587
5623
  }
4588
- return [4 /*yield*/, Promise.all(
4589
- // TODO: [🪂] !! Do not send all at once but in chunks
4590
- knowledgeTextPieces.map(function (knowledgeTextPiece, i) { return __awaiter(_this, void 0, void 0, function () {
4591
- var name, title, knowledgePieceContent, keywords, index, titleResult, _a, titleRaw, keywordsResult, _b, keywordsRaw, embeddingResult, error_1;
4592
- return __generator(this, function (_c) {
4593
- switch (_c.label) {
4594
- case 0:
4595
- name = "piece-".concat(i);
4596
- title = spaceTrim__default["default"](knowledgeTextPiece.substring(0, 100));
4597
- knowledgePieceContent = spaceTrim__default["default"](knowledgeTextPiece);
4598
- keywords = [];
4599
- index = [];
4600
- _c.label = 1;
4601
- case 1:
4602
- _c.trys.push([1, 7, , 8]);
4603
- return [4 /*yield*/, prepareTitleExecutor({ knowledgePieceContent: knowledgePieceContent })];
4604
- case 2:
4605
- titleResult = _c.sent();
4606
- _a = titleResult.outputParameters.title, titleRaw = _a === void 0 ? 'Untitled' : _a;
4607
- title = spaceTrim__default["default"](titleRaw) /* <- TODO: Maybe do in pipeline */;
4608
- name = titleToName(title);
4609
- return [4 /*yield*/, prepareKeywordsExecutor({ knowledgePieceContent: knowledgePieceContent })];
4610
- case 3:
4611
- keywordsResult = _c.sent();
4612
- _b = keywordsResult.outputParameters.keywords, keywordsRaw = _b === void 0 ? '' : _b;
4613
- keywords = (keywordsRaw || '')
4614
- .split(',')
4615
- .map(function (keyword) { return keyword.trim(); })
4616
- .filter(function (keyword) { return keyword !== ''; });
4617
- if (isVerbose) {
4618
- console.info("Keywords for \"".concat(title, "\":"), keywords);
5624
+ if (rootDirname === null) {
5625
+ throw new EnvironmentMismatchError('Can not import file knowledge in non-file pipeline');
5626
+ // <- TODO: [🧠] What is the best error type here`
5627
+ }
5628
+ filename_1 = path.join(rootDirname, sourceContent).split('\\').join('/');
5629
+ fileExtension = getFileExtension(filename_1);
5630
+ mimeType_1 = extensionToMimeType(fileExtension || '');
5631
+ return [4 /*yield*/, $isFileExisting(filename_1)];
5632
+ case 3:
5633
+ if (!(_e.sent())) {
5634
+ 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 "); }));
5635
+ }
5636
+ // TODO: !!!!!! Test security file - file is scoped to the project (maybe do this in `filesystemTools`)
5637
+ return [2 /*return*/, {
5638
+ source: name,
5639
+ filename: filename_1,
5640
+ url: null,
5641
+ mimeType: mimeType_1,
5642
+ asBlob: function () {
5643
+ return __awaiter(this, void 0, void 0, function () {
5644
+ var content;
5645
+ return __generator(this, function (_a) {
5646
+ switch (_a.label) {
5647
+ case 0: return [4 /*yield*/, promises.readFile(filename_1)];
5648
+ case 1:
5649
+ content = _a.sent();
5650
+ // <- Note: Its OK to use sync in tooling for tests
5651
+ return [2 /*return*/, new Blob([
5652
+ content,
5653
+ // <- TODO: !!!!!! Maybe not working
5654
+ ], { type: mimeType_1 })];
4619
5655
  }
4620
- if (!!llmTools.callEmbeddingModel) return [3 /*break*/, 4];
4621
- // TODO: [🟥] Detect browser / node and make it colorfull
4622
- console.error('No callEmbeddingModel function provided');
4623
- return [3 /*break*/, 6];
4624
- case 4: return [4 /*yield*/, llmTools.callEmbeddingModel({
4625
- title: "Embedding for ".concat(title) /* <- Note: No impact on embedding result itself, just for logging */,
4626
- parameters: {},
4627
- content: knowledgePieceContent,
4628
- modelRequirements: {
4629
- modelVariant: 'EMBEDDING',
4630
- },
4631
- })];
4632
- case 5:
4633
- embeddingResult = _c.sent();
4634
- index.push({
4635
- modelName: embeddingResult.modelName,
4636
- position: embeddingResult.content,
4637
- });
4638
- _c.label = 6;
4639
- case 6: return [3 /*break*/, 8];
4640
- case 7:
4641
- error_1 = _c.sent();
4642
- // Note: Here is expected error:
4643
- // > PipelineExecutionError: You have not provided any `LlmExecutionTools` that support model variant "EMBEDDING
4644
- if (!(error_1 instanceof PipelineExecutionError)) {
4645
- throw error_1;
5656
+ });
5657
+ });
5658
+ },
5659
+ asJson: function () {
5660
+ return __awaiter(this, void 0, void 0, function () {
5661
+ var _a, _b;
5662
+ return __generator(this, function (_c) {
5663
+ switch (_c.label) {
5664
+ case 0:
5665
+ _b = (_a = JSON).parse;
5666
+ return [4 /*yield*/, promises.readFile(filename_1, 'utf-8')];
5667
+ case 1: return [2 /*return*/, _b.apply(_a, [_c.sent()])];
4646
5668
  }
4647
- // TODO: [🟥] Detect browser / node and make it colorfull
4648
- console.error(error_1, "<- Note: This error is not critical to prepare the pipeline, just knowledge pieces won't have embeddings");
4649
- return [3 /*break*/, 8];
4650
- case 8: return [2 /*return*/, {
4651
- name: name,
4652
- title: title,
4653
- content: knowledgePieceContent,
4654
- keywords: keywords,
4655
- index: index,
4656
- // <- TODO: [☀] sources,
4657
- }];
4658
- }
4659
- });
4660
- }); }))];
4661
- case 5:
4662
- knowledge = _j.sent();
4663
- return [2 /*return*/, knowledge];
5669
+ });
5670
+ });
5671
+ },
5672
+ asText: function () {
5673
+ return __awaiter(this, void 0, void 0, function () {
5674
+ return __generator(this, function (_a) {
5675
+ switch (_a.label) {
5676
+ case 0: return [4 /*yield*/, promises.readFile(filename_1, 'utf-8')];
5677
+ case 1: return [2 /*return*/, _a.sent()];
5678
+ }
5679
+ });
5680
+ });
5681
+ },
5682
+ }];
5683
+ case 4: return [2 /*return*/, {
5684
+ source: name,
5685
+ filename: null,
5686
+ url: null,
5687
+ mimeType: 'text/markdown',
5688
+ asText: function () {
5689
+ return knowledgeSource.sourceContent;
5690
+ },
5691
+ asJson: function () {
5692
+ throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
5693
+ },
5694
+ asBlob: function () {
5695
+ throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asBlob`');
5696
+ },
5697
+ }];
4664
5698
  }
4665
5699
  });
4666
5700
  });
4667
5701
  }
4668
- /**
4669
- * TODO: [🐝][🔼][main] !!! Export via `@promptbook/markdown`
4670
- * TODO: [🪂] Do it in parallel 11:11
4671
- * Note: No need to aggregate usage here, it is done by intercepting the llmTools
4672
- */
4673
5702
 
4674
5703
  /**
4675
5704
  * Prepares the knowle
@@ -4679,21 +5708,64 @@
4679
5708
  */
4680
5709
  function prepareKnowledgePieces(knowledgeSources, options) {
4681
5710
  return __awaiter(this, void 0, void 0, function () {
4682
- var _a, maxParallelCount, knowledgePrepared;
5711
+ var _a, maxParallelCount, rootDirname, _b, isVerbose, knowledgePreparedUnflatten, knowledgePrepared;
4683
5712
  var _this = this;
4684
- return __generator(this, function (_b) {
4685
- switch (_b.label) {
5713
+ return __generator(this, function (_c) {
5714
+ switch (_c.label) {
4686
5715
  case 0:
4687
- _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? MAX_PARALLEL_COUNT : _a;
4688
- knowledgePrepared = [];
4689
- return [4 /*yield*/, forEachAsync(knowledgeSources, { maxParallelCount: maxParallelCount }, function (knowledgeSource) { return __awaiter(_this, void 0, void 0, function () {
4690
- var partialPieces, pieces;
4691
- return __generator(this, function (_a) {
4692
- switch (_a.label) {
4693
- case 0: return [4 /*yield*/, prepareKnowledgeFromMarkdown(knowledgeSource.sourceContent, // <- TODO: [🐝][main] !!! Unhardcode markdown, detect which type it is - BE AWARE of big package size
4694
- options)];
5716
+ _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? MAX_PARALLEL_COUNT : _a, rootDirname = options.rootDirname, _b = options.isVerbose, isVerbose = _b === void 0 ? IS_VERBOSE : _b;
5717
+ knowledgePreparedUnflatten = new Array(knowledgeSources.length);
5718
+ return [4 /*yield*/, forEachAsync(knowledgeSources, { maxParallelCount: maxParallelCount }, function (knowledgeSource, index) { return __awaiter(_this, void 0, void 0, function () {
5719
+ var partialPieces, sourceHandler, SCRAPERS_1, SCRAPERS_1_1, scraper, partialPiecesUnchecked, e_1_1, pieces;
5720
+ var e_1, _a;
5721
+ return __generator(this, function (_b) {
5722
+ switch (_b.label) {
5723
+ case 0:
5724
+ partialPieces = null;
5725
+ return [4 /*yield*/, makeKnowledgeSourceHandler(knowledgeSource, { rootDirname: rootDirname, isVerbose: isVerbose })];
4695
5726
  case 1:
4696
- partialPieces = _a.sent();
5727
+ sourceHandler = _b.sent();
5728
+ _b.label = 2;
5729
+ case 2:
5730
+ _b.trys.push([2, 7, 8, 9]);
5731
+ SCRAPERS_1 = __values(SCRAPERS), SCRAPERS_1_1 = SCRAPERS_1.next();
5732
+ _b.label = 3;
5733
+ case 3:
5734
+ if (!!SCRAPERS_1_1.done) return [3 /*break*/, 6];
5735
+ scraper = SCRAPERS_1_1.value;
5736
+ if (!scraper.mimeTypes.includes(sourceHandler.mimeType)
5737
+ // <- TODO: [🦔] Implement mime-type wildcards
5738
+ ) {
5739
+ return [3 /*break*/, 5];
5740
+ }
5741
+ return [4 /*yield*/, scraper.scrape(sourceHandler, options)];
5742
+ case 4:
5743
+ partialPiecesUnchecked = _b.sent();
5744
+ if (partialPiecesUnchecked !== null) {
5745
+ partialPieces = partialPiecesUnchecked;
5746
+ return [3 /*break*/, 6];
5747
+ }
5748
+ _b.label = 5;
5749
+ case 5:
5750
+ SCRAPERS_1_1 = SCRAPERS_1.next();
5751
+ return [3 /*break*/, 3];
5752
+ case 6: return [3 /*break*/, 9];
5753
+ case 7:
5754
+ e_1_1 = _b.sent();
5755
+ e_1 = { error: e_1_1 };
5756
+ return [3 /*break*/, 9];
5757
+ case 8:
5758
+ try {
5759
+ if (SCRAPERS_1_1 && !SCRAPERS_1_1.done && (_a = SCRAPERS_1.return)) _a.call(SCRAPERS_1);
5760
+ }
5761
+ finally { if (e_1) throw e_1.error; }
5762
+ return [7 /*endfinally*/];
5763
+ case 9:
5764
+ if (partialPieces === null) {
5765
+ throw new KnowledgeScrapeError(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge from source: ".concat(knowledgeSource.sourceContent, "\n\n No scraper found for the mime type \"").concat(sourceHandler.mimeType, "\"\n\n Available scrapers:\n ").concat(block(SCRAPERS.flatMap(function (scraper) { return scraper.mimeTypes; })
5766
+ .map(function (mimeType) { return "- ".concat(mimeType); })
5767
+ .join('\n')), "\n\n\n "); }));
5768
+ }
4697
5769
  pieces = partialPieces.map(function (partialPiece) { return (__assign(__assign({}, partialPiece), { sources: [
4698
5770
  {
4699
5771
  name: knowledgeSource.name,
@@ -4701,13 +5773,14 @@
4701
5773
  // <- TODO: [❎]
4702
5774
  },
4703
5775
  ] })); });
4704
- knowledgePrepared.push.apply(knowledgePrepared, __spreadArray([], __read(pieces), false));
5776
+ knowledgePreparedUnflatten[index] = pieces;
4705
5777
  return [2 /*return*/];
4706
5778
  }
4707
5779
  });
4708
5780
  }); })];
4709
5781
  case 1:
4710
- _b.sent();
5782
+ _c.sent();
5783
+ knowledgePrepared = knowledgePreparedUnflatten.flat();
4711
5784
  return [2 /*return*/, knowledgePrepared];
4712
5785
  }
4713
5786
  });
@@ -4724,7 +5797,7 @@
4724
5797
  >
4725
5798
  > export async function prepareKnowledgePieces(
4726
5799
  > knowledge: PrepareKnowledgeKnowledge,
4727
- > options: PrepareOptions,
5800
+ > options: PrepareAndScrapeOptions,
4728
5801
  > ):
4729
5802
  */
4730
5803
  /**
@@ -4837,6 +5910,9 @@
4837
5910
  switch (_d.label) {
4838
5911
  case 0:
4839
5912
  llmTools = options.llmTools, _a = options.isVerbose, isVerbose = _a === void 0 ? IS_VERBOSE : _a;
5913
+ if (llmTools === undefined) {
5914
+ throw new MissingToolsError('LLM tools are required for preparing persona');
5915
+ }
4840
5916
  collection = createCollectionFromJson.apply(void 0, __spreadArray([], __read(PipelineCollection), false));
4841
5917
  _b = createPipelineExecutor;
4842
5918
  _c = {};
@@ -4980,7 +6056,7 @@
4980
6056
  */
4981
6057
  function preparePipeline(pipeline, options) {
4982
6058
  return __awaiter(this, void 0, void 0, function () {
4983
- var llmTools, _a, maxParallelCount, _b, isVerbose, parameters, templates,
6059
+ var llmTools, rootDirname, _a, maxParallelCount, _b, isVerbose, parameters, templates,
4984
6060
  /*
4985
6061
  <- TODO: [🧠][🪑] `promptbookVersion` */
4986
6062
  knowledgeSources /*
@@ -4993,8 +6069,11 @@
4993
6069
  if (isPipelinePrepared(pipeline)) {
4994
6070
  return [2 /*return*/, pipeline];
4995
6071
  }
4996
- llmTools = options.llmTools, _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? MAX_PARALLEL_COUNT : _a, _b = options.isVerbose, isVerbose = _b === void 0 ? IS_VERBOSE : _b;
6072
+ llmTools = options.llmTools, rootDirname = options.rootDirname, _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? MAX_PARALLEL_COUNT : _a, _b = options.isVerbose, isVerbose = _b === void 0 ? IS_VERBOSE : _b;
4997
6073
  parameters = pipeline.parameters, templates = pipeline.templates, knowledgeSources = pipeline.knowledgeSources, personas = pipeline.personas;
6074
+ if (llmTools === undefined) {
6075
+ throw new MissingToolsError('LLM tools are required for preparing the pipeline');
6076
+ }
4998
6077
  llmToolsWithUsage = countTotalUsage(llmTools);
4999
6078
  currentPreparation = {
5000
6079
  id: 1,
@@ -5016,6 +6095,7 @@
5016
6095
  switch (_a.label) {
5017
6096
  case 0: return [4 /*yield*/, preparePersona(persona.description, {
5018
6097
  llmTools: llmToolsWithUsage,
6098
+ rootDirname: rootDirname,
5019
6099
  maxParallelCount: maxParallelCount /* <- TODO: [🪂] */,
5020
6100
  isVerbose: isVerbose,
5021
6101
  })];
@@ -5030,11 +6110,7 @@
5030
6110
  case 1:
5031
6111
  _c.sent();
5032
6112
  knowledgeSourcesPrepared = knowledgeSources.map(function (source) { return (__assign(__assign({}, source), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
5033
- return [4 /*yield*/, prepareKnowledgePieces(knowledgeSources /* <- TODO: [🧊] {knowledgeSources, knowledgePieces} */, {
5034
- llmTools: llmToolsWithUsage,
5035
- maxParallelCount: maxParallelCount /* <- TODO: [🪂] */,
5036
- isVerbose: isVerbose,
5037
- })];
6113
+ return [4 /*yield*/, prepareKnowledgePieces(knowledgeSources /* <- TODO: [🧊] {knowledgeSources, knowledgePieces} */, __assign(__assign({}, options), { llmTools: llmToolsWithUsage, rootDirname: rootDirname, maxParallelCount: maxParallelCount /* <- TODO: [🪂] */, isVerbose: isVerbose }))];
5038
6114
  case 2:
5039
6115
  partialknowledgePiecesPrepared = _c.sent();
5040
6116
  knowledgePiecesPrepared = partialknowledgePiecesPrepared.map(function (piece) { return (__assign(__assign({}, piece), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
@@ -5044,6 +6120,7 @@
5044
6120
  knowledgePiecesCount: knowledgePiecesPrepared.length,
5045
6121
  }, {
5046
6122
  llmTools: llmToolsWithUsage,
6123
+ rootDirname: rootDirname,
5047
6124
  maxParallelCount: maxParallelCount /* <- TODO: [🪂] */,
5048
6125
  isVerbose: isVerbose,
5049
6126
  })];
@@ -5066,36 +6143,10 @@
5066
6143
  * @see https://docs.anthropic.com/en/docs/test-and-evaluate/strengthen-guardrails/increase-consistency#specify-the-desired-output-format
5067
6144
  */
5068
6145
 
5069
- /**
5070
- * Tests if given string is valid URL.
5071
- *
5072
- * Note: This does not check if the file exists only if the path is valid
5073
- * @public exported from `@promptbook/utils`
5074
- */
5075
- function isValidFilePath(filePath) {
5076
- if (typeof filePath !== 'string') {
5077
- return false;
5078
- }
5079
- var filePathSlashes = filePath.split('\\').join('/');
5080
- // Absolute Unix path: /hello.txt
5081
- if (/^(\/)/i.test(filePathSlashes)) {
5082
- return true;
5083
- }
5084
- // Absolute Windows path: /hello.txt
5085
- if (/^([A-Z]{1,2}:\/?)\//i.test(filePathSlashes)) {
5086
- return true;
5087
- }
5088
- // Relative path: ./hello.txt
5089
- if (/^(\.\.?\/)+/i.test(filePathSlashes)) {
5090
- return true;
5091
- }
5092
- return false;
5093
- }
5094
-
5095
6146
  /**
5096
6147
  * Parses the knowledge command
5097
6148
  *
5098
- * @see ./KNOWLEDGE-README.md for more details
6149
+ * @see `documentationUrl` for more details
5099
6150
  * @private within the commands folder
5100
6151
  */
5101
6152
  var knowledgeCommandParser = {
@@ -5113,7 +6164,7 @@
5113
6164
  */
5114
6165
  description: "Tells promptbook which external knowledge to use",
5115
6166
  /**
5116
- * Link to discussion
6167
+ * Link to documentation
5117
6168
  */
5118
6169
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/41',
5119
6170
  /**
@@ -5157,11 +6208,8 @@
5157
6208
  */
5158
6209
  $applyToPipelineJson: function (command, $pipelineJson) {
5159
6210
  var sourceContent = command.sourceContent;
5160
- var name = 'source-' + sha256__default["default"](hexEncoder__default["default"].parse(JSON.stringify(sourceContent))).toString( /* hex */);
5161
- // <- TODO: [🥬] Encapsulate sha256 to some private utility function
5162
- // <- TODO: This should be replaced with a better name later in preparation (done with some propper LLM summarization)
5163
6211
  $pipelineJson.knowledgeSources.push({
5164
- name: name,
6212
+ name: sourceContentToName(sourceContent),
5165
6213
  sourceContent: sourceContent,
5166
6214
  });
5167
6215
  },
@@ -5209,7 +6257,7 @@
5209
6257
  /**
5210
6258
  * Parses the template command
5211
6259
  *
5212
- * @see ./TEMPLATE-README.md for more details
6260
+ * @see `documentationUrl` for more details
5213
6261
  * @private within the commands folder
5214
6262
  */
5215
6263
  var templateCommandParser = {
@@ -5245,7 +6293,7 @@
5245
6293
  */
5246
6294
  description: "What should the code template template do",
5247
6295
  /**
5248
- * Link to discussion
6296
+ * Link to documentation
5249
6297
  */
5250
6298
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/64',
5251
6299
  /**
@@ -5470,7 +6518,7 @@
5470
6518
  /**
5471
6519
  * Parses the expect command
5472
6520
  *
5473
- * @see ./EXPECT-README.md for more details
6521
+ * @see `documentationUrl` for more details
5474
6522
  * @private within the commands folder
5475
6523
  */
5476
6524
  var expectCommandParser = {
@@ -5488,7 +6536,7 @@
5488
6536
  */
5489
6537
  description: spaceTrim__default["default"]("\n Expect command describes the desired output of the template (after post-processing)\n It can set limits for the maximum/minimum length of the output, measured in characters, words, sentences, paragraphs or some other shape of the output.\n "),
5490
6538
  /**
5491
- * Link to discussion
6539
+ * Link to documentation
5492
6540
  */
5493
6541
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/30',
5494
6542
  /**
@@ -5648,10 +6696,6 @@
5648
6696
  charType = 'NUMBER';
5649
6697
  normalizedChar = char;
5650
6698
  }
5651
- else if (/^\/$/.test(char)) {
5652
- charType = 'SLASH';
5653
- normalizedChar = char;
5654
- }
5655
6699
  else {
5656
6700
  charType = 'OTHER';
5657
6701
  normalizedChar = '_';
@@ -5858,7 +6902,7 @@
5858
6902
  *
5859
6903
  * Note: @@@ This command is used as foreach for new commands - it should NOT be used in any `.ptbk.md` file
5860
6904
  *
5861
- * @see ./FOREACH-README.md for more details <- TODO: @@@ Write theese README files OR remove this link + add annotation here (to all commands)
6905
+ * @see `documentationUrl` for more details
5862
6906
  * @private within the commands folder
5863
6907
  */
5864
6908
  var foreachCommandParser = {
@@ -5880,7 +6924,7 @@
5880
6924
  */
5881
6925
  description: "@@",
5882
6926
  /**
5883
- * Link to discussion
6927
+ * Link to documentation
5884
6928
  */
5885
6929
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/148',
5886
6930
  /**
@@ -6015,7 +7059,7 @@
6015
7059
  /**
6016
7060
  * Parses the format command
6017
7061
  *
6018
- * @see ./FORMAT-README.md for more details
7062
+ * @see `documentationUrl` for more details
6019
7063
  * @private within the commands folder
6020
7064
  */
6021
7065
  var formatCommandParser = {
@@ -6033,7 +7077,7 @@
6033
7077
  */
6034
7078
  description: spaceTrim__default["default"]("\n Format command describes the desired output of the template (after post-processing)\n It can set limits for the maximum/minimum length of the output, measured in characters, words, sentences, paragraphs or some other shape of the output.\n "),
6035
7079
  /**
6036
- * Link to discussion
7080
+ * Link to documentation
6037
7081
  */
6038
7082
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/30',
6039
7083
  /**
@@ -6089,7 +7133,7 @@
6089
7133
  /**
6090
7134
  * Parses the joker command
6091
7135
  *
6092
- * @see ./JOKER-README.md for more details
7136
+ * @see `documentationUrl` for more details
6093
7137
  * @private within the commands folder
6094
7138
  */
6095
7139
  var jokerCommandParser = {
@@ -6107,7 +7151,7 @@
6107
7151
  */
6108
7152
  description: "Joker parameter is used instead of executing the template result if jokers value meets the expectations requirements",
6109
7153
  /**
6110
- * Link to discussion
7154
+ * Link to documentation
6111
7155
  */
6112
7156
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/66',
6113
7157
  /**
@@ -6168,7 +7212,7 @@
6168
7212
  /**
6169
7213
  * Parses the model command
6170
7214
  *
6171
- * @see ./MODEL-README.md for more details
7215
+ * @see `documentationUrl` for more details
6172
7216
  * @private within the commands folder
6173
7217
  */
6174
7218
  var modelCommandParser = {
@@ -6186,7 +7230,7 @@
6186
7230
  */
6187
7231
  description: "Tells which `modelRequirements` (for example which model) to use for the prompt template execution",
6188
7232
  /**
6189
- * Link to discussion
7233
+ * Link to documentation
6190
7234
  */
6191
7235
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/67',
6192
7236
  /**
@@ -6268,7 +7312,11 @@
6268
7312
  // TODO: [🚜] DRY
6269
7313
  if ($templateJson.modelRequirements[command.key] !== undefined) {
6270
7314
  if ($templateJson.modelRequirements[command.key] === command.value) {
6271
- console.warn("Multiple commands `MODEL ".concat(command.key, " ").concat(command.value, "` in the template \"").concat($templateJson.title || $templateJson.name, "\""));
7315
+ console.warn("Multiple commands `MODEL ".concat({
7316
+ modelName: 'NAME',
7317
+ modelVariant: 'VARIANT',
7318
+ maxTokens: '???',
7319
+ }[command.key], " ").concat(command.value, "` in the template \"").concat($templateJson.title || $templateJson.name, "\""));
6272
7320
  }
6273
7321
  else {
6274
7322
  throw new ParseError(spaceTrim__default["default"]("\n Redefinition of MODEL `".concat(command.key, "` in the template \"").concat($templateJson.title || $templateJson.name, "\"\n\n You have used:\n - MODEL ").concat(command.key, " ").concat($templateJson.modelRequirements[command.key], "\n - MODEL ").concat(command.key, " ").concat(command.value, "\n ")));
@@ -6311,7 +7359,7 @@
6311
7359
  /**
6312
7360
  * Parses the parameter command
6313
7361
  *
6314
- * @see ./PARAMETER-README.md for more details
7362
+ * @see `documentationUrl` for more details
6315
7363
  * @private within the commands folder
6316
7364
  */
6317
7365
  var parameterCommandParser = {
@@ -6336,7 +7384,7 @@
6336
7384
  */
6337
7385
  description: "Describes one parameter of the template",
6338
7386
  /**
6339
- * Link to discussion
7387
+ * Link to documentation
6340
7388
  */
6341
7389
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/68',
6342
7390
  /**
@@ -6421,7 +7469,7 @@
6421
7469
  /**
6422
7470
  * Parses the persona command
6423
7471
  *
6424
- * @see ./PERSONA-README.md for more details
7472
+ * @see `documentationUrl` for more details
6425
7473
  * @private within the commands folder
6426
7474
  */
6427
7475
  var personaCommandParser = {
@@ -6443,7 +7491,7 @@
6443
7491
  */
6444
7492
  description: "Persona command is used to specify who the system is, it will be transformed into system message, top_t,...",
6445
7493
  /**
6446
- * Link to discussion
7494
+ * Link to documentation
6447
7495
  */
6448
7496
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/22',
6449
7497
  /**
@@ -6559,7 +7607,7 @@
6559
7607
  /**
6560
7608
  * Parses the postprocess command
6561
7609
  *
6562
- * @see ./POSTPROCESS-README.md for more details
7610
+ * @see `documentationUrl` for more details
6563
7611
  * @private within the commands folder
6564
7612
  */
6565
7613
  var postprocessCommandParser = {
@@ -6578,7 +7626,7 @@
6578
7626
  */
6579
7627
  description: "Defines the postprocess function to be used on the result from LLM and before the result is validated",
6580
7628
  /**
6581
- * Link to discussion
7629
+ * Link to documentation
6582
7630
  */
6583
7631
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/31',
6584
7632
  /**
@@ -6639,7 +7687,7 @@
6639
7687
  /**
6640
7688
  * Parses the PROMPTBOOK_VERSION command
6641
7689
  *
6642
- * @see ./PROMPTBOOK_VERSION-README.md for more details
7690
+ * @see `documentationUrl` for more details
6643
7691
  * @private within the commands folder
6644
7692
  */
6645
7693
  var promptbookVersionCommandParser = {
@@ -6658,7 +7706,7 @@
6658
7706
  */
6659
7707
  description: "Which version of the promptbook is the .ptbk.md using",
6660
7708
  /**
6661
- * Link to discussion
7709
+ * Link to documentation
6662
7710
  */
6663
7711
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/69',
6664
7712
  /**
@@ -6717,7 +7765,7 @@
6717
7765
  /**
6718
7766
  * Parses the url command
6719
7767
  *
6720
- * @see ./URL-README.md for more details
7768
+ * @see `documentationUrl` for more details
6721
7769
  * @private within the commands folder
6722
7770
  */
6723
7771
  var urlCommandParser = {
@@ -6740,7 +7788,7 @@
6740
7788
  */
6741
7789
  description: "Declares unique URL for the pipeline",
6742
7790
  /**
6743
- * Link to discussion
7791
+ * Link to documentation
6744
7792
  */
6745
7793
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/70',
6746
7794
  /**
@@ -6820,7 +7868,7 @@
6820
7868
  /**
6821
7869
  * Parses the action command
6822
7870
  *
6823
- * @see ./ACTION-README.md for more details
7871
+ * @see `documentationUrl` for more details
6824
7872
  * @private within the commands folder
6825
7873
  */
6826
7874
  var actionCommandParser = {
@@ -6838,7 +7886,7 @@
6838
7886
  */
6839
7887
  description: "Actions influences from the pipeline or template into external world. Like turning on a light, sending an email, etc.",
6840
7888
  /**
6841
- * Link to discussion
7889
+ * Link to documentation
6842
7890
  */
6843
7891
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/72',
6844
7892
  /**
@@ -6890,7 +7938,7 @@
6890
7938
  /**
6891
7939
  * Parses the instrument command
6892
7940
  *
6893
- * @see ./INSTRUMENT-README.md for more details
7941
+ * @see `documentationUrl` for more details
6894
7942
  * @private within the commands folder
6895
7943
  */
6896
7944
  var instrumentCommandParser = {
@@ -6908,7 +7956,7 @@
6908
7956
  */
6909
7957
  description: "Instrument command is used to specify the instrument to be used in the pipeline or template like search, calculate, etc.",
6910
7958
  /**
6911
- * Link to discussion
7959
+ * Link to documentation
6912
7960
  */
6913
7961
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/71',
6914
7962
  /**
@@ -6963,7 +8011,7 @@
6963
8011
  *
6964
8012
  * Note: @@@ This command is used as boilerplate for new commands - it should NOT be used in any `.ptbk.md` file
6965
8013
  *
6966
- * @see ./BOILERPLATE-README.md for more details <- TODO: @@@ Write theese README files OR remove this link + add annotation here (to all commands)
8014
+ * @see `documentationUrl` for more details
6967
8015
  * @private within the commands folder
6968
8016
  */
6969
8017
  var boilerplateCommandParser = {
@@ -6985,7 +8033,7 @@
6985
8033
  */
6986
8034
  description: "@@",
6987
8035
  /**
6988
- * Link to discussion
8036
+ * Link to documentation
6989
8037
  */
6990
8038
  documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
6991
8039
  /**
@@ -7835,16 +8883,17 @@
7835
8883
  * @public exported from `@promptbook/core`
7836
8884
  */
7837
8885
  function pipelineStringToJson(pipelineString, options) {
7838
- if (options === void 0) { options = { llmTools: null }; }
7839
8886
  return __awaiter(this, void 0, void 0, function () {
7840
8887
  var llmTools, pipelineJson;
7841
8888
  return __generator(this, function (_a) {
7842
8889
  switch (_a.label) {
7843
8890
  case 0:
7844
- llmTools = options.llmTools;
8891
+ llmTools = (options || {}).llmTools;
7845
8892
  pipelineJson = pipelineStringToJsonSync(pipelineString);
7846
- if (!(llmTools !== null)) return [3 /*break*/, 2];
7847
- return [4 /*yield*/, preparePipeline(pipelineJson, { llmTools: llmTools })];
8893
+ if (!(llmTools !== undefined)) return [3 /*break*/, 2];
8894
+ return [4 /*yield*/, preparePipeline(pipelineJson, options || {
8895
+ rootDirname: null,
8896
+ })];
7848
8897
  case 1:
7849
8898
  pipelineJson = _a.sent();
7850
8899
  _a.label = 2;
@@ -7861,51 +8910,6 @@
7861
8910
  * TODO: [🧠] Should be in generated JSON file GENERATOR_WARNING
7862
8911
  */
7863
8912
 
7864
- /**
7865
- * Detects if the code is running in a Node.js environment
7866
- *
7867
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
7868
- *
7869
- * @public exported from `@promptbook/utils`
7870
- */
7871
- var $isRunningInNode = new Function("\n try {\n return this === global;\n } catch (e) {\n return false;\n }\n");
7872
-
7873
- /**
7874
- * Checks if the file exists
7875
- *
7876
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the filesystem
7877
- *
7878
- * @private within the repository
7879
- */
7880
- function $isFileExisting(filePath) {
7881
- return __awaiter(this, void 0, void 0, function () {
7882
- var isReadAccessAllowed, isFile;
7883
- return __generator(this, function (_a) {
7884
- switch (_a.label) {
7885
- case 0: return [4 /*yield*/, promises.access(filePath, promises.constants.R_OK)
7886
- .then(function () { return true; })
7887
- .catch(function () { return false; })];
7888
- case 1:
7889
- isReadAccessAllowed = _a.sent();
7890
- if (!isReadAccessAllowed) {
7891
- return [2 /*return*/, false];
7892
- }
7893
- return [4 /*yield*/, promises.stat(filePath)
7894
- .then(function (fileStat) { return fileStat.isFile(); })
7895
- .catch(function () { return false; })];
7896
- case 2:
7897
- isFile = _a.sent();
7898
- return [2 /*return*/, isFile];
7899
- }
7900
- });
7901
- });
7902
- }
7903
- /**
7904
- * Note: [🟢] This code should never be published outside of `@promptbook/node` and `@promptbook/cli` and `@promptbook/cli`
7905
- * TODO: [🐠] This can be a validator - with variants that return true/false and variants that throw errors with meaningless messages
7906
- * TODO: [🖇] What about symlinks?
7907
- */
7908
-
7909
8913
  /**
7910
8914
  * Checks if the directory exists
7911
8915
  *
@@ -7918,9 +8922,13 @@
7918
8922
  var isReadAccessAllowed, isDirectory;
7919
8923
  return __generator(this, function (_a) {
7920
8924
  switch (_a.label) {
7921
- case 0: return [4 /*yield*/, promises.access(directoryPath, promises.constants.R_OK)
7922
- .then(function () { return true; })
7923
- .catch(function () { return false; })];
8925
+ case 0:
8926
+ if (!$isRunningInNode()) {
8927
+ throw new EnvironmentMismatchError('Function `$isDirectoryExisting` works only in Node environment.js');
8928
+ }
8929
+ return [4 /*yield*/, promises.access(directoryPath, promises.constants.R_OK)
8930
+ .then(function () { return true; })
8931
+ .catch(function () { return false; })];
7924
8932
  case 1:
7925
8933
  isReadAccessAllowed = _a.sent();
7926
8934
  if (!isReadAccessAllowed) {
@@ -7937,7 +8945,7 @@
7937
8945
  });
7938
8946
  }
7939
8947
  /**
7940
- * Note: [🟢] This code should never be published outside of `@promptbook/node` and `@promptbook/cli` and `@promptbook/cli`
8948
+ * Note: [🟢 <- TODO: [🦖] !!!!!! Split scrapers into packages and enable] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
7941
8949
  * TODO: [🐠] This can be a validator - with variants that return true/false and variants that throw errors with meaningless messages
7942
8950
  * TODO: [🧠][📂] "directory" vs "folder"
7943
8951
  * TODO: [🖇] What about symlinks?
@@ -7953,26 +8961,32 @@
7953
8961
  * @returns List of all files in the directory
7954
8962
  * @private internal function of `createCollectionFromDirectory`
7955
8963
  */
7956
- function $listAllFiles(path, isRecursive) {
8964
+ function $listAllFiles(path$1, isRecursive) {
7957
8965
  return __awaiter(this, void 0, void 0, function () {
7958
8966
  var dirents, fileNames, _a, _b, dirent, subPath, _c, _d, _e, _f, e_1_1;
7959
8967
  var e_1, _g;
7960
8968
  return __generator(this, function (_h) {
7961
8969
  switch (_h.label) {
7962
- case 0: return [4 /*yield*/, $isDirectoryExisting(path)];
8970
+ case 0:
8971
+ if (!$isRunningInNode()) {
8972
+ throw new EnvironmentMismatchError('Function `$listAllFiles` works only in Node environment.js');
8973
+ }
8974
+ return [4 /*yield*/, $isDirectoryExisting(path$1)];
7963
8975
  case 1:
7964
8976
  if (!(_h.sent())) {
7965
- throw new Error("Directory \"".concat(path, "\" does not exist or is not readable"));
8977
+ throw new Error("Directory \"".concat(path$1, "\" does not exist or is not readable"));
7966
8978
  // <- TODO: Use some custom error class
7967
8979
  }
7968
- return [4 /*yield*/, promises.readdir(path, {
8980
+ return [4 /*yield*/, promises.readdir(path$1, {
7969
8981
  withFileTypes: true /* Note: This is not working: recursive: isRecursive */,
7970
8982
  })];
7971
8983
  case 2:
7972
8984
  dirents = _h.sent();
7973
- fileNames = dirents.filter(function (dirent) { return dirent.isFile(); }).map(function (_a) {
8985
+ fileNames = dirents
8986
+ .filter(function (dirent) { return dirent.isFile(); })
8987
+ .map(function (_a) {
7974
8988
  var name = _a.name;
7975
- return posix.join(path, name);
8989
+ return path.join(path$1, name).split('\\').join('/');
7976
8990
  });
7977
8991
  if (!isRecursive) return [3 /*break*/, 10];
7978
8992
  _h.label = 3;
@@ -7983,13 +8997,13 @@
7983
8997
  case 4:
7984
8998
  if (!!_b.done) return [3 /*break*/, 7];
7985
8999
  dirent = _b.value;
7986
- subPath = posix.join(path, dirent.name);
9000
+ subPath = path.join(path$1, dirent.name);
7987
9001
  _d = (_c = fileNames.push).apply;
7988
9002
  _e = [fileNames];
7989
9003
  _f = [[]];
7990
9004
  return [4 /*yield*/, $listAllFiles(subPath, isRecursive)];
7991
9005
  case 5:
7992
- _d.apply(_c, _e.concat([__spreadArray.apply(void 0, _f.concat([__read.apply(void 0, [(_h.sent())]), false]))]));
9006
+ _d.apply(_c, _e.concat([__spreadArray.apply(void 0, _f.concat([__read.apply(void 0, [(_h.sent()).map(function (filename) { return filename; })]), false]))]));
7993
9007
  _h.label = 6;
7994
9008
  case 6:
7995
9009
  _b = _a.next();
@@ -8011,7 +9025,8 @@
8011
9025
  });
8012
9026
  }
8013
9027
  /**
8014
- * Note: [🟢] This code should never be published outside of `@promptbook/node` and `@promptbook/cli` and `@promptbook/cli`
9028
+ * TODO: [😶] Unite floder listing
9029
+ * Note: [🟢 <- TODO: [🦖] !!!!!! Split scrapers into packages and enable] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
8015
9030
  * TODO: [🖇] What about symlinks?
8016
9031
  */
8017
9032
 
@@ -8114,7 +9129,7 @@
8114
9129
  */
8115
9130
  function createCollectionFromDirectory(path$1, options) {
8116
9131
  return __awaiter(this, void 0, void 0, function () {
8117
- var makedLibraryFilePath, _a, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, collection;
9132
+ var makedLibraryFilePath, _a, llmTools, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, collection;
8118
9133
  var _this = this;
8119
9134
  return __generator(this, function (_f) {
8120
9135
  switch (_f.label) {
@@ -8133,7 +9148,7 @@
8133
9148
  // TODO: !! Implement;
8134
9149
  // TODO: [🌗]
8135
9150
  }
8136
- _a = options || {}, _b = _a.isRecursive, isRecursive = _b === void 0 ? true : _b, _c = _a.isVerbose, isVerbose = _c === void 0 ? IS_VERBOSE : _c, _d = _a.isLazyLoaded, isLazyLoaded = _d === void 0 ? false : _d, _e = _a.isCrashedOnError, isCrashedOnError = _e === void 0 ? true : _e;
9151
+ _a = options || {}, llmTools = _a.llmTools, _b = _a.isRecursive, isRecursive = _b === void 0 ? true : _b, _c = _a.isVerbose, isVerbose = _c === void 0 ? IS_VERBOSE : _c, _d = _a.isLazyLoaded, isLazyLoaded = _d === void 0 ? false : _d, _e = _a.isCrashedOnError, isCrashedOnError = _e === void 0 ? true : _e;
8137
9152
  collection = createCollectionFromPromise(function () { return __awaiter(_this, void 0, void 0, function () {
8138
9153
  var fileNames, collection, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1;
8139
9154
  var e_1, _a;
@@ -8159,20 +9174,24 @@
8159
9174
  });
8160
9175
  collection = new Map();
8161
9176
  _loop_1 = function (fileName) {
8162
- var sourceFile, pipeline, pipelineString, _c, _d, existing, error_1, wrappedErrorMessage;
9177
+ var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, existing, error_1, wrappedErrorMessage;
8163
9178
  return __generator(this, function (_e) {
8164
9179
  switch (_e.label) {
8165
9180
  case 0:
8166
9181
  sourceFile = './' + fileName.split('\\').join('/');
9182
+ rootDirname = path.dirname(sourceFile).split('\\').join('/');
8167
9183
  _e.label = 1;
8168
9184
  case 1:
8169
9185
  _e.trys.push([1, 8, , 9]);
8170
9186
  pipeline = null;
8171
9187
  if (!fileName.endsWith('.ptbk.md')) return [3 /*break*/, 4];
8172
- return [4 /*yield*/, promises.readFile(fileName, 'utf8')];
9188
+ return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
8173
9189
  case 2:
8174
9190
  pipelineString = (_e.sent());
8175
- return [4 /*yield*/, pipelineStringToJson(pipelineString, options)];
9191
+ return [4 /*yield*/, pipelineStringToJson(pipelineString, {
9192
+ llmTools: llmTools,
9193
+ rootDirname: rootDirname,
9194
+ })];
8176
9195
  case 3:
8177
9196
  pipeline = _e.sent();
8178
9197
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
@@ -8180,7 +9199,7 @@
8180
9199
  case 4:
8181
9200
  if (!fileName.endsWith('.ptbk.json')) return [3 /*break*/, 6];
8182
9201
  _d = (_c = JSON).parse;
8183
- return [4 /*yield*/, promises.readFile(fileName, 'utf8')];
9202
+ return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
8184
9203
  case 5:
8185
9204
  // TODO: Handle non-valid JSON files
8186
9205
  pipeline = _d.apply(_c, [_e.sent()]);
@@ -8288,7 +9307,7 @@
8288
9307
  });
8289
9308
  }
8290
9309
  /**
8291
- * Note: [🟢] This code should never be published outside of `@promptbook/node` and `@promptbook/cli` and `@promptbook/cli`
9310
+ * Note: [🟢] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
8292
9311
  * TODO: [🖇] What about symlinks? Maybe option isSymlinksFollowed
8293
9312
  */
8294
9313
 
@@ -8379,7 +9398,7 @@
8379
9398
  * TODO: Add Azure OpenAI
8380
9399
  * TODO: [🧠][🍛]
8381
9400
  * TODO: [🧠] Is there some meaningfull way how to test this util
8382
- * Note: [🟢] This code should never be published outside of `@promptbook/node` and `@promptbook/cli` and `@promptbook/cli`
9401
+ * Note: [🟢] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
8383
9402
  * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
8384
9403
  * TODO: This should be maybe not under `_common` but under `utils`
8385
9404
  * TODO: [🧠] Maybe pass env as argument
@@ -8556,7 +9575,7 @@
8556
9575
  * TODO: @@@ write `createLlmToolsFromEnv` vs `createLlmToolsFromConfigurationFromEnv` vs `createLlmToolsFromConfiguration`
8557
9576
  * TODO: [🧠][🍛] Which name is better `createLlmToolsFromEnv` or `createLlmToolsFromEnvironment`?
8558
9577
  * TODO: [🧠] Is there some meaningfull way how to test this util
8559
- * Note: [🟢] This code should never be published outside of `@promptbook/node` and `@promptbook/cli` and `@promptbook/cli`
9578
+ * Note: [🟢] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
8560
9579
  * TODO: [🥃] Allow `ptbk make` without llm tools
8561
9580
  * TODO: This should be maybe not under `_common` but under `utils`
8562
9581
  */
@@ -8621,40 +9640,32 @@
8621
9640
  * TODO: [🍙] Make some standard order of json properties
8622
9641
  */
8623
9642
 
8624
- /**
8625
- * @@@
8626
- *
8627
- * @private for `FilesStorage`
8628
- */
8629
- function nameToSubfolderPath(name) {
8630
- return [name.substr(0, 1).toLowerCase(), name.substr(1, 1).toLowerCase()];
8631
- }
8632
-
8633
9643
  /**
8634
9644
  * @@@
8635
9645
  *
8636
9646
  * @public exported from `@promptbook/node`
8637
9647
  */
8638
- var FilesStorage = /** @class */ (function () {
8639
- function FilesStorage(options) {
9648
+ var FileCacheStorage = /** @class */ (function () {
9649
+ function FileCacheStorage(options) {
8640
9650
  this.options = options;
8641
9651
  if (!$isRunningInNode()) {
8642
- throw new EnvironmentMismatchError("FilesStorage works only in Node.js environment");
9652
+ throw new EnvironmentMismatchError("FileCacheStorage works only in Node.js environment");
8643
9653
  }
8644
9654
  }
8645
9655
  /**
8646
9656
  * @@@
8647
9657
  */
8648
- FilesStorage.prototype.getFilenameForKey = function (key) {
9658
+ FileCacheStorage.prototype.getFilenameForKey = function (key) {
9659
+ // TODO: [👬] DRY
8649
9660
  var name = titleToName(key);
8650
9661
  var hash = sha256__default["default"](hexEncoder__default["default"].parse(name)).toString( /* hex */);
8651
9662
  // <- TODO: [🥬] Encapsulate sha256 to some private utility function
8652
- return path.join.apply(void 0, __spreadArray(__spreadArray([this.options.cacheFolderPath], __read(nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */)), false), ["".concat(name.substring(0, MAX_FILENAME_LENGTH), ".json")], false));
9663
+ return path.join.apply(void 0, __spreadArray(__spreadArray([this.options.rootFolderPath], __read(nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */)), false), ["".concat(name.substring(0, MAX_FILENAME_LENGTH), ".json")], false));
8653
9664
  };
8654
9665
  /**
8655
9666
  * @@@ Returns the current value associated with the given key, or null if the given key does not exist in the list associated with the object.
8656
9667
  */
8657
- FilesStorage.prototype.getItem = function (key) {
9668
+ FileCacheStorage.prototype.getItem = function (key) {
8658
9669
  return __awaiter(this, void 0, void 0, function () {
8659
9670
  var filename, fileContent, value;
8660
9671
  return __generator(this, function (_a) {
@@ -8679,7 +9690,7 @@
8679
9690
  /**
8680
9691
  * @@@ Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
8681
9692
  */
8682
- FilesStorage.prototype.setItem = function (key, value) {
9693
+ FileCacheStorage.prototype.setItem = function (key, value) {
8683
9694
  return __awaiter(this, void 0, void 0, function () {
8684
9695
  var filename, fileContent;
8685
9696
  return __generator(this, function (_a) {
@@ -8704,7 +9715,7 @@
8704
9715
  /**
8705
9716
  * @@@ Removes the key/value pair with the given key from the list associated with the object, if a key/value pair with the given key exists.
8706
9717
  */
8707
- FilesStorage.prototype.removeItem = function (key) {
9718
+ FileCacheStorage.prototype.removeItem = function (key) {
8708
9719
  return __awaiter(this, void 0, void 0, function () {
8709
9720
  var filename;
8710
9721
  return __generator(this, function (_a) {
@@ -8721,14 +9732,66 @@
8721
9732
  });
8722
9733
  });
8723
9734
  };
8724
- return FilesStorage;
9735
+ return FileCacheStorage;
8725
9736
  }());
8726
9737
  /**
8727
9738
  * TODO: [🌗] Maybe some checkers, not all valid JSONs are desired and valid values
8728
- * Note: [🟢] This code should never be published outside of `@promptbook/node` and `@promptbook/cli` and `@promptbook/cli`
9739
+ * Note: [🟢] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
9740
+ */
9741
+
9742
+ /**
9743
+ * Run multiple commands in a shell in sequence
9744
+ *
9745
+ * Note: There are 2 similar functions in the codebase:
9746
+ * - `$execCommand` which runs a single command
9747
+ * - `$execCommands` which runs multiple commands
9748
+ *
9749
+ * @public exported from `@promptbook/node`
9750
+ */
9751
+ function $execCommands(_a) {
9752
+ var commands = _a.commands, cwd = _a.cwd, crashOnError = _a.crashOnError;
9753
+ return __awaiter(this, void 0, void 0, function () {
9754
+ var commands_1, commands_1_1, command, e_1_1;
9755
+ var e_1, _b;
9756
+ return __generator(this, function (_c) {
9757
+ switch (_c.label) {
9758
+ case 0:
9759
+ _c.trys.push([0, 5, 6, 7]);
9760
+ commands_1 = __values(commands), commands_1_1 = commands_1.next();
9761
+ _c.label = 1;
9762
+ case 1:
9763
+ if (!!commands_1_1.done) return [3 /*break*/, 4];
9764
+ command = commands_1_1.value;
9765
+ return [4 /*yield*/, $execCommand({ command: command, cwd: cwd, crashOnError: crashOnError })];
9766
+ case 2:
9767
+ _c.sent();
9768
+ _c.label = 3;
9769
+ case 3:
9770
+ commands_1_1 = commands_1.next();
9771
+ return [3 /*break*/, 1];
9772
+ case 4: return [3 /*break*/, 7];
9773
+ case 5:
9774
+ e_1_1 = _c.sent();
9775
+ e_1 = { error: e_1_1 };
9776
+ return [3 /*break*/, 7];
9777
+ case 6:
9778
+ try {
9779
+ if (commands_1_1 && !commands_1_1.done && (_b = commands_1.return)) _b.call(commands_1);
9780
+ }
9781
+ finally { if (e_1) throw e_1.error; }
9782
+ return [7 /*endfinally*/];
9783
+ case 7: return [2 /*return*/];
9784
+ }
9785
+ });
9786
+ });
9787
+ }
9788
+ /**
9789
+ * Note: [🟢 <- TODO: [🦖] !!!!!! Split scrapers into packages and enable] Code in this file should never be published outside of `@promptbook/node` and `@promptbook/cli`
8729
9790
  */
8730
9791
 
8731
- exports.FilesStorage = FilesStorage;
9792
+ exports.$execCommand = $execCommand;
9793
+ exports.$execCommands = $execCommands;
9794
+ exports.FileCacheStorage = FileCacheStorage;
8732
9795
  exports.PROMPTBOOK_VERSION = PROMPTBOOK_VERSION;
8733
9796
  exports.createCollectionFromDirectory = createCollectionFromDirectory;
8734
9797
  exports.createLlmToolsFromConfigurationFromEnv = createLlmToolsFromConfigurationFromEnv;