@promptbook/node 0.81.0-9 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +25 -8
  2. package/esm/index.es.js +1956 -1790
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/books/index.d.ts +38 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +12 -4
  6. package/esm/typings/src/_packages/markdown-utils.index.d.ts +2 -2
  7. package/esm/typings/src/_packages/node.index.d.ts +0 -2
  8. package/esm/typings/src/_packages/templates.index.d.ts +2 -2
  9. package/esm/typings/src/_packages/types.index.d.ts +2 -0
  10. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  11. package/esm/typings/src/_packages/wizzard.index.d.ts +44 -0
  12. package/esm/typings/src/cli/cli-commands/make.d.ts +1 -1
  13. package/esm/typings/src/cli/cli-commands/run.d.ts +2 -2
  14. package/esm/typings/src/collection/constructors/createCollectionFromDirectory.d.ts +11 -0
  15. package/esm/typings/src/collection/constructors/createCollectionFromUrl.d.ts +1 -1
  16. package/esm/typings/src/commands/index.d.ts +1 -1
  17. package/esm/typings/src/config.d.ts +3 -3
  18. package/esm/typings/src/conversion/compilePipeline.d.ts +1 -4
  19. package/esm/typings/src/conversion/{precompilePipeline.d.ts → parsePipeline.d.ts} +3 -3
  20. package/esm/typings/src/conversion/prettify/renderPipelineMermaidOptions.d.ts +3 -3
  21. package/esm/typings/src/conversion/validation/validatePipeline.d.ts +7 -7
  22. package/esm/typings/src/errors/utils/getErrorReportUrl.d.ts +1 -1
  23. package/esm/typings/src/formfactors/generator/GeneratorFormfactorDefinition.d.ts +9 -4
  24. package/esm/typings/src/formfactors/image-generator/ImageGeneratorFormfactorDefinition.d.ts +24 -0
  25. package/esm/typings/src/formfactors/index.d.ts +31 -9
  26. package/esm/typings/src/high-level-abstractions/_common/HighLevelAbstraction.d.ts +1 -1
  27. package/esm/typings/src/high-level-abstractions/index.d.ts +3 -3
  28. package/esm/typings/src/high-level-abstractions/quick-chatbot/QuickChatbotHla.d.ts +3 -0
  29. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  30. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +1 -1
  31. package/esm/typings/src/llm-providers/_common/register/{$provideLlmToolsForCli.d.ts → $provideLlmToolsForWizzardOrCli.d.ts} +2 -2
  32. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  33. package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  34. package/esm/typings/src/llm-providers/anthropic-claude/createAnthropicClaudeExecutionTools.d.ts +2 -2
  35. package/esm/typings/src/llm-providers/anthropic-claude/playground/playground.d.ts +2 -2
  36. package/esm/typings/src/llm-providers/anthropic-claude/register-configuration.d.ts +1 -0
  37. package/esm/typings/src/llm-providers/anthropic-claude/register-constructor.d.ts +2 -0
  38. package/esm/typings/src/llm-providers/azure-openai/register-configuration.d.ts +1 -0
  39. package/esm/typings/src/llm-providers/azure-openai/register-constructor.d.ts +1 -0
  40. package/esm/typings/src/llm-providers/google/register-configuration.d.ts +1 -0
  41. package/esm/typings/src/llm-providers/google/register-constructor.d.ts +1 -0
  42. package/esm/typings/src/llm-providers/openai/playground/playground.d.ts +1 -1
  43. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +2 -0
  44. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +2 -0
  45. package/esm/typings/src/llm-providers/vercel/playground/playground.d.ts +1 -1
  46. package/esm/typings/src/other/templates/getBookTemplates.d.ts +22 -0
  47. package/esm/typings/src/personas/preparePersona.d.ts +4 -4
  48. package/esm/typings/src/pipeline/PipelineString.d.ts +0 -3
  49. package/esm/typings/src/pipeline/book-notation.d.ts +14 -0
  50. package/esm/typings/src/pipeline/isValidPipelineString.d.ts +13 -0
  51. package/esm/typings/src/pipeline/isValidPipelineString.test.d.ts +4 -0
  52. package/esm/typings/src/pipeline/validatePipelineString.d.ts +14 -0
  53. package/esm/typings/src/prepare/isPipelinePrepared.d.ts +3 -1
  54. package/esm/typings/src/prepare/preparePipeline.d.ts +2 -0
  55. package/esm/typings/src/prepare/prepareTasks.d.ts +1 -1
  56. package/esm/typings/src/scrapers/_common/Converter.d.ts +1 -0
  57. package/esm/typings/src/scrapers/_common/Scraper.d.ts +1 -1
  58. package/esm/typings/src/scrapers/_common/ScraperIntermediateSource.d.ts +3 -0
  59. package/esm/typings/src/scrapers/_common/register/ScraperAndConverterMetadata.d.ts +2 -0
  60. package/esm/typings/src/scrapers/_common/utils/scraperFetch.d.ts +3 -0
  61. package/esm/typings/src/scrapers/document/register-constructor.d.ts +1 -0
  62. package/esm/typings/src/scrapers/document/register-metadata.d.ts +1 -0
  63. package/esm/typings/src/scrapers/document-legacy/register-constructor.d.ts +1 -0
  64. package/esm/typings/src/scrapers/document-legacy/register-metadata.d.ts +1 -0
  65. package/esm/typings/src/scrapers/markdown/register-constructor.d.ts +1 -0
  66. package/esm/typings/src/scrapers/markdown/register-metadata.d.ts +1 -0
  67. package/esm/typings/src/scrapers/pdf/PdfScraper.d.ts +1 -0
  68. package/esm/typings/src/scrapers/pdf/createPdfScraper.d.ts +1 -1
  69. package/esm/typings/src/scrapers/pdf/register-constructor.d.ts +1 -0
  70. package/esm/typings/src/scrapers/pdf/register-metadata.d.ts +2 -1
  71. package/esm/typings/src/scrapers/website/createWebsiteScraper.d.ts +3 -1
  72. package/esm/typings/src/scrapers/website/register-constructor.d.ts +1 -0
  73. package/esm/typings/src/scrapers/website/register-metadata.d.ts +1 -0
  74. package/esm/typings/src/scripting/javascript/JavascriptEvalExecutionTools.test.d.ts +1 -1
  75. package/esm/typings/src/scripting/javascript/utils/preserve.d.ts +2 -1
  76. package/esm/typings/src/types/typeAliases.d.ts +8 -2
  77. package/esm/typings/src/utils/markdown/flattenMarkdown.d.ts +1 -1
  78. package/esm/typings/src/utils/markdown/{removeContentComments.d.ts → removeMarkdownComments.d.ts} +2 -2
  79. package/esm/typings/src/utils/organization/$sideEffect.d.ts +9 -0
  80. package/esm/typings/src/utils/serialization/checkSerializableAsJson.d.ts +1 -1
  81. package/esm/typings/src/utils/serialization/isSerializableAsJson.d.ts +2 -2
  82. package/esm/typings/src/utils/validators/filePath/isRootPath.d.ts +12 -0
  83. package/esm/typings/src/utils/validators/filePath/isRootPath.test.d.ts +4 -0
  84. package/esm/typings/src/utils/validators/filePath/isValidFilePath.d.ts +3 -0
  85. package/esm/typings/src/wizzard/$getCompiledBook.d.ts +16 -0
  86. package/esm/typings/src/wizzard/wizzard.d.ts +51 -7
  87. package/package.json +2 -2
  88. package/umd/index.umd.js +1955 -1790
  89. package/umd/index.umd.js.map +1 -1
  90. package/esm/typings/src/other/templates/getBookTemplate.d.ts +0 -21
  91. package/esm/typings/src/scripting/javascript/utils/unknownToString.d.ts +0 -8
  92. /package/esm/typings/src/conversion/{precompilePipeline.test.d.ts → parsePipeline.test.d.ts} +0 -0
  93. /package/esm/typings/src/utils/markdown/{removeContentComments.test.d.ts → removeMarkdownComments.test.d.ts} +0 -0
package/umd/index.umd.js CHANGED
@@ -45,7 +45,7 @@
45
45
  * @generated
46
46
  * @see https://github.com/webgptorg/promptbook
47
47
  */
48
- var PROMPTBOOK_ENGINE_VERSION = '0.81.0-8';
48
+ var PROMPTBOOK_ENGINE_VERSION = '0.81.0-24';
49
49
  /**
50
50
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
51
51
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -215,7 +215,7 @@
215
215
  *
216
216
  * @public exported from `@promptbook/core`
217
217
  */
218
- var DEFAULT_TITLE = "Untitled";
218
+ var DEFAULT_BOOK_TITLE = "\u2728 Untitled Book";
219
219
  // <- TODO: [🧠] Better system for generator warnings - not always "code" and "by `@promptbook/cli`"
220
220
  /**
221
221
  * The maximum number of iterations for a loops
@@ -326,1412 +326,834 @@
326
326
  * TODO: [🧠][🧜‍♂️] Maybe join remoteUrl and path into single value
327
327
  */
328
328
 
329
- /**
330
- * Orders JSON object by keys
331
- *
332
- * @returns The same type of object as the input re-ordered
333
- * @public exported from `@promptbook/utils`
334
- */
335
- function orderJson(options) {
336
- var value = options.value, order = options.order;
337
- var orderedValue = __assign(__assign({}, (order === undefined ? {} : Object.fromEntries(order.map(function (key) { return [key, undefined]; })))), value);
338
- return orderedValue;
339
- }
329
+ var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book.md",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book.md`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book.md",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book.md`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book.md"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book.md",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book.md`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book.md"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book.md",formfactorName:"GENERIC",parameters:[{name:"availableModelNames",description:"List of available model names separated by comma (,)",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are experienced AI engineer, you need to create virtual assistant.\nWrite\n\n## Example\n\n```json\n{\n\"modelName\": \"gpt-4o\",\n\"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n\"temperature\": 0.7\n}\n```\n\n## Instructions\n\n- Your output format is JSON object\n- Write just the JSON object, no other text should be present\n- It contains the following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nPick from the following models:\n\n- {availableModelNames}\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelRequirements",format:"JSON",dependentParameterNames:["availableModelNames","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book.md`\n- INPUT PARAMETER `{availableModelNames}` List of available model names separated by comma (,)\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are experienced AI engineer, you need to create virtual assistant.\nWrite\n\n## Example\n\n\\`\\`\\`json\n{\n\"modelName\": \"gpt-4o\",\n\"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n\"temperature\": 0.7\n}\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON object\n- Write just the JSON object, no other text should be present\n- It contains the following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nPick from the following models:\n\n- {availableModelNames}\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelRequirements}`\n"}],sourceFile:"./books/prepare-persona.book.md"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book.md",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book.md`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book.md"}];
340
330
 
341
331
  /**
342
- * Freezes the given object and all its nested objects recursively
343
- *
344
- * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
345
- * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
332
+ * Function isValidJsonString will tell you if the string is valid JSON or not
346
333
  *
347
- * @returns The same object as the input, but deeply frozen
348
334
  * @public exported from `@promptbook/utils`
349
335
  */
350
- function $deepFreeze(objectValue) {
351
- var e_1, _a;
352
- if (Array.isArray(objectValue)) {
353
- return Object.freeze(objectValue.map(function (item) { return $deepFreeze(item); }));
354
- }
355
- var propertyNames = Object.getOwnPropertyNames(objectValue);
336
+ function isValidJsonString(value /* <- [👨‍⚖️] */) {
356
337
  try {
357
- for (var propertyNames_1 = __values(propertyNames), propertyNames_1_1 = propertyNames_1.next(); !propertyNames_1_1.done; propertyNames_1_1 = propertyNames_1.next()) {
358
- var propertyName = propertyNames_1_1.value;
359
- var value = objectValue[propertyName];
360
- if (value && typeof value === 'object') {
361
- $deepFreeze(value);
362
- }
363
- }
338
+ JSON.parse(value);
339
+ return true;
364
340
  }
365
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
366
- finally {
367
- try {
368
- if (propertyNames_1_1 && !propertyNames_1_1.done && (_a = propertyNames_1.return)) _a.call(propertyNames_1);
341
+ catch (error) {
342
+ if (!(error instanceof Error)) {
343
+ throw error;
369
344
  }
370
- finally { if (e_1) throw e_1.error; }
345
+ if (error.message.includes('Unexpected token')) {
346
+ return false;
347
+ }
348
+ return false;
371
349
  }
372
- Object.freeze(objectValue);
373
- return objectValue;
374
- }
375
- /**
376
- * TODO: [🧠] Is there a way how to meaningfully test this utility
377
- */
378
-
379
- /**
380
- * Make error report URL for the given error
381
- *
382
- * @private !!!!!!
383
- */
384
- function getErrorReportUrl(error) {
385
- var report = {
386
- title: "\uD83D\uDC1C Error report from ".concat(NAME),
387
- body: spaceTrim__default["default"](function (block) { return "\n\n\n `".concat(error.name || 'Error', "` has occurred in the [").concat(NAME, "], please look into it @").concat(ADMIN_GITHUB_NAME, ".\n\n ```\n ").concat(block(error.message || '(no error message)'), "\n ```\n\n\n ## More info:\n\n - **Promptbook engine version:** ").concat(PROMPTBOOK_ENGINE_VERSION, "\n - **Book language version:** ").concat(BOOK_LANGUAGE_VERSION, "\n - **Time:** ").concat(new Date().toISOString(), "\n\n <details>\n <summary>Stack trace:</summary>\n\n ## Stack trace:\n\n ```stacktrace\n ").concat(block(error.stack || '(empty)'), "\n ```\n </details>\n\n "); }),
388
- };
389
- var reportUrl = new URL("https://github.com/webgptorg/promptbook/issues/new");
390
- reportUrl.searchParams.set('labels', 'bug');
391
- reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
392
- reportUrl.searchParams.set('title', report.title);
393
- reportUrl.searchParams.set('body', report.body);
394
- return reportUrl;
395
350
  }
396
351
 
397
352
  /**
398
- * This error type indicates that the error should not happen and its last check before crashing with some other error
353
+ * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
399
354
  *
400
355
  * @public exported from `@promptbook/core`
401
356
  */
402
- var UnexpectedError = /** @class */ (function (_super) {
403
- __extends(UnexpectedError, _super);
404
- function UnexpectedError(message) {
405
- var _this = _super.call(this, spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(message), "\n\n Note: This error should not happen.\n It's probbably a bug in the pipeline collection\n\n Please report issue:\n ").concat(block(getErrorReportUrl(new Error(message)).href), "\n\n Or contact us on ").concat(ADMIN_EMAIL, "\n\n "); })) || this;
406
- _this.name = 'UnexpectedError';
407
- Object.setPrototypeOf(_this, UnexpectedError.prototype);
357
+ var ParseError = /** @class */ (function (_super) {
358
+ __extends(ParseError, _super);
359
+ function ParseError(message) {
360
+ var _this = _super.call(this, message) || this;
361
+ _this.name = 'ParseError';
362
+ Object.setPrototypeOf(_this, ParseError.prototype);
408
363
  return _this;
409
364
  }
410
- return UnexpectedError;
365
+ return ParseError;
411
366
  }(Error));
367
+ /**
368
+ * TODO: Maybe split `ParseError` and `ApplyError`
369
+ */
412
370
 
413
371
  /**
414
- * Checks if the value is [🚉] serializable as JSON
415
- * If not, throws an UnexpectedError with a rich error message and tracking
416
- *
417
- * - Almost all primitives are serializable BUT:
418
- * - `undefined` is not serializable
419
- * - `NaN` is not serializable
420
- * - Objects and arrays are serializable if all their properties are serializable
421
- * - Functions are not serializable
422
- * - Circular references are not serializable
423
- * - `Date` objects are not serializable
424
- * - `Map` and `Set` objects are not serializable
425
- * - `RegExp` objects are not serializable
426
- * - `Error` objects are not serializable
427
- * - `Symbol` objects are not serializable
428
- * - And much more...
372
+ * Function `validatePipelineString` will validate the if the string is a valid pipeline string
373
+ * It does not check if the string is fully logically correct, but if it is a string that can be a pipeline string or the string looks completely different.
429
374
  *
430
- * @throws UnexpectedError if the value is not serializable as JSON
431
- * @public exported from `@promptbook/utils`
375
+ * @param {string} pipelineString the candidate for a pipeline string
376
+ * @returns {PipelineString} the same string as input, but validated as valid
377
+ * @throws {ParseError} if the string is not a valid pipeline string
378
+ * @public exported from `@promptbook/core`
432
379
  */
433
- function checkSerializableAsJson(options) {
434
- var e_1, _a;
435
- var value = options.value, name = options.name, message = options.message;
436
- if (value === undefined) {
437
- throw new UnexpectedError("".concat(name, " is undefined"));
438
- }
439
- else if (value === null) {
440
- return;
441
- }
442
- else if (typeof value === 'boolean') {
443
- return;
444
- }
445
- else if (typeof value === 'number' && !isNaN(value)) {
446
- return;
447
- }
448
- else if (typeof value === 'string') {
449
- return;
450
- }
451
- else if (typeof value === 'symbol') {
452
- throw new UnexpectedError("".concat(name, " is symbol"));
453
- }
454
- else if (typeof value === 'function') {
455
- throw new UnexpectedError("".concat(name, " is function"));
456
- }
457
- else if (typeof value === 'object' && Array.isArray(value)) {
458
- for (var i = 0; i < value.length; i++) {
459
- checkSerializableAsJson({ name: "".concat(name, "[").concat(i, "]"), value: value[i], message: message });
460
- }
461
- }
462
- else if (typeof value === 'object') {
463
- if (value instanceof Date) {
464
- throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is Date\n\n Use `string_date_iso8601` instead\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
465
- }
466
- else if (value instanceof Map) {
467
- throw new UnexpectedError("".concat(name, " is Map"));
468
- }
469
- else if (value instanceof Set) {
470
- throw new UnexpectedError("".concat(name, " is Set"));
471
- }
472
- else if (value instanceof RegExp) {
473
- throw new UnexpectedError("".concat(name, " is RegExp"));
474
- }
475
- else if (value instanceof Error) {
476
- throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is unserialized Error\n\n Use function `serializeError`\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n\n "); }));
477
- }
478
- else {
479
- try {
480
- for (var _b = __values(Object.entries(value)), _c = _b.next(); !_c.done; _c = _b.next()) {
481
- var _d = __read(_c.value, 2), subName = _d[0], subValue = _d[1];
482
- if (subValue === undefined) {
483
- // Note: undefined in object is serializable - it is just omited
484
- continue;
485
- }
486
- checkSerializableAsJson({ name: "".concat(name, ".").concat(subName), value: subValue, message: message });
487
- }
488
- }
489
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
490
- finally {
491
- try {
492
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
493
- }
494
- finally { if (e_1) throw e_1.error; }
495
- }
496
- try {
497
- JSON.stringify(value); // <- TODO: [0]
498
- }
499
- catch (error) {
500
- if (!(error instanceof Error)) {
501
- throw error;
502
- }
503
- throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is not serializable\n\n ").concat(block(error.toString()), "\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
504
- }
505
- /*
506
- TODO: [0] Is there some more elegant way to check circular references?
507
- const seen = new Set();
508
- const stack = [{ value }];
509
- while (stack.length > 0) {
510
- const { value } = stack.pop()!;
511
- if (typeof value === 'object' && value !== null) {
512
- if (seen.has(value)) {
513
- throw new UnexpectedError(`${name} has circular reference`);
514
- }
515
- seen.add(value);
516
- if (Array.isArray(value)) {
517
- stack.push(...value.map((value) => ({ value })));
518
- } else {
519
- stack.push(...Object.values(value).map((value) => ({ value })));
520
- }
521
- }
522
- }
523
- */
524
- return;
525
- }
526
- }
527
- else {
528
- throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is unknown type\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
380
+ function validatePipelineString(pipelineString) {
381
+ if (isValidJsonString(pipelineString)) {
382
+ throw new ParseError('Expected a book, but got a JSON string');
529
383
  }
384
+ // <- TODO: Implement the validation + add tests when the pipeline logic considered as invalid
385
+ return pipelineString;
530
386
  }
531
387
  /**
532
- * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
533
- * TODO: [🧠][main] !!! In-memory cache of same values to prevent multiple checks
534
- * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
388
+ * TODO: [🧠][🈴] Where is the best location for this file
535
389
  */
536
390
 
537
391
  /**
538
- * @@@
392
+ * Prettify the html code
539
393
  *
540
- * @public exported from `@promptbook/utils`
394
+ * @param content raw html code
395
+ * @returns formatted html code
396
+ * @private withing the package because of HUGE size of prettier dependency
541
397
  */
542
- function deepClone(objectValue) {
543
- return JSON.parse(JSON.stringify(objectValue));
544
- /*
545
- !!!!!!!!
546
- TODO: [🧠] Is there a better implementation?
547
- > const propertyNames = Object.getOwnPropertyNames(objectValue);
548
- > for (const propertyName of propertyNames) {
549
- > const value = (objectValue as really_any)[propertyName];
550
- > if (value && typeof value === 'object') {
551
- > deepClone(value);
552
- > }
553
- > }
554
- > return Object.assign({}, objectValue);
555
- */
398
+ function prettifyMarkdown(content) {
399
+ try {
400
+ return prettier.format(content, {
401
+ parser: 'markdown',
402
+ plugins: [parserHtml__default["default"]],
403
+ // TODO: DRY - make some import or auto-copy of .prettierrc
404
+ endOfLine: 'lf',
405
+ tabWidth: 4,
406
+ singleQuote: true,
407
+ trailingComma: 'all',
408
+ arrowParens: 'always',
409
+ printWidth: 120,
410
+ htmlWhitespaceSensitivity: 'ignore',
411
+ jsxBracketSameLine: false,
412
+ bracketSpacing: true,
413
+ });
414
+ }
415
+ catch (error) {
416
+ // TODO: [🟥] Detect browser / node and make it colorfull
417
+ console.error('There was an error with prettifying the markdown, using the original as the fallback', {
418
+ error: error,
419
+ html: content,
420
+ });
421
+ return content;
422
+ }
556
423
  }
424
+
557
425
  /**
558
- * TODO: [🧠] Is there a way how to meaningfully test this utility
426
+ * Makes first letter of a string uppercase
427
+ *
428
+ * @public exported from `@promptbook/utils`
559
429
  */
430
+ function capitalize(word) {
431
+ return word.substring(0, 1).toUpperCase() + word.substring(1);
432
+ }
560
433
 
561
434
  /**
562
- * Utility to export a JSON object from a function
435
+ * Converts promptbook in JSON format to string format
563
436
  *
564
- * 1) Checks if the value is serializable as JSON
565
- * 2) Makes a deep clone of the object
566
- * 2) Orders the object properties
567
- * 2) Deeply freezes the cloned object
568
- *
569
- * Note: This function does not mutates the given object
570
- *
571
- * @returns The same type of object as the input but read-only and re-ordered
572
- * @public exported from `@promptbook/utils`
437
+ * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
438
+ * @param pipelineJson Promptbook in JSON format (.book.json)
439
+ * @returns Promptbook in string format (.book.md)
440
+ * @public exported from `@promptbook/core`
573
441
  */
574
- function exportJson(options) {
575
- var name = options.name, value = options.value, order = options.order, message = options.message;
576
- checkSerializableAsJson({ name: name, value: value, message: message });
577
- var orderedValue =
578
- // TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
579
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
580
- // @ts-ignore
581
- order === undefined
582
- ? deepClone(value)
583
- : orderJson({
584
- value: value,
585
- // <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
586
- order: order,
587
- });
588
- $deepFreeze(orderedValue);
589
- return orderedValue;
442
+ function pipelineJsonToString(pipelineJson) {
443
+ var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
444
+ var title = pipelineJson.title, pipelineUrl = pipelineJson.pipelineUrl, bookVersion = pipelineJson.bookVersion, description = pipelineJson.description, parameters = pipelineJson.parameters, tasks = pipelineJson.tasks;
445
+ var pipelineString = "# ".concat(title);
446
+ if (description) {
447
+ pipelineString += '\n\n';
448
+ pipelineString += description;
449
+ }
450
+ var commands = [];
451
+ if (pipelineUrl) {
452
+ commands.push("PIPELINE URL ".concat(pipelineUrl));
453
+ }
454
+ if (bookVersion !== "undefined") {
455
+ commands.push("BOOK VERSION ".concat(bookVersion));
456
+ }
457
+ // TODO: [main] !!5 This increases size of the bundle and is probbably not necessary
458
+ pipelineString = prettifyMarkdown(pipelineString);
459
+ try {
460
+ for (var _g = __values(parameters.filter(function (_a) {
461
+ var isInput = _a.isInput;
462
+ return isInput;
463
+ })), _h = _g.next(); !_h.done; _h = _g.next()) {
464
+ var parameter = _h.value;
465
+ commands.push("INPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
466
+ }
467
+ }
468
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
469
+ finally {
470
+ try {
471
+ if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
472
+ }
473
+ finally { if (e_1) throw e_1.error; }
474
+ }
475
+ try {
476
+ for (var _j = __values(parameters.filter(function (_a) {
477
+ var isOutput = _a.isOutput;
478
+ return isOutput;
479
+ })), _k = _j.next(); !_k.done; _k = _j.next()) {
480
+ var parameter = _k.value;
481
+ commands.push("OUTPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
482
+ }
483
+ }
484
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
485
+ finally {
486
+ try {
487
+ if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
488
+ }
489
+ finally { if (e_2) throw e_2.error; }
490
+ }
491
+ pipelineString += '\n\n';
492
+ pipelineString += commands.map(function (command) { return "- ".concat(command); }).join('\n');
493
+ try {
494
+ for (var tasks_1 = __values(tasks), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) {
495
+ var task = tasks_1_1.value;
496
+ var
497
+ /* Note: Not using:> name, */
498
+ title_1 = task.title, description_1 = task.description,
499
+ /* Note: dependentParameterNames, */
500
+ jokers = task.jokerParameterNames, taskType = task.taskType, content = task.content, postprocessing = task.postprocessingFunctionNames, expectations = task.expectations, format = task.format, resultingParameterName = task.resultingParameterName;
501
+ pipelineString += '\n\n';
502
+ pipelineString += "## ".concat(title_1);
503
+ if (description_1) {
504
+ pipelineString += '\n\n';
505
+ pipelineString += description_1;
506
+ }
507
+ var commands_1 = [];
508
+ var contentLanguage = 'text';
509
+ if (taskType === 'PROMPT_TASK') {
510
+ var modelRequirements = task.modelRequirements;
511
+ var _l = modelRequirements || {}, modelName = _l.modelName, modelVariant = _l.modelVariant;
512
+ // Note: Do nothing, it is default
513
+ // commands.push(`PROMPT`);
514
+ if (modelVariant) {
515
+ commands_1.push("MODEL VARIANT ".concat(capitalize(modelVariant)));
516
+ }
517
+ if (modelName) {
518
+ commands_1.push("MODEL NAME `".concat(modelName, "`"));
519
+ }
520
+ }
521
+ else if (taskType === 'SIMPLE_TASK') {
522
+ commands_1.push("SIMPLE TEMPLATE");
523
+ // Note: Nothing special here
524
+ }
525
+ else if (taskType === 'SCRIPT_TASK') {
526
+ commands_1.push("SCRIPT");
527
+ if (task.contentLanguage) {
528
+ contentLanguage = task.contentLanguage;
529
+ }
530
+ else {
531
+ contentLanguage = '';
532
+ }
533
+ }
534
+ else if (taskType === 'DIALOG_TASK') {
535
+ commands_1.push("DIALOG");
536
+ // Note: Nothing special here
537
+ } // <- }else if([🅱]
538
+ if (jokers) {
539
+ try {
540
+ for (var jokers_1 = (e_4 = void 0, __values(jokers)), jokers_1_1 = jokers_1.next(); !jokers_1_1.done; jokers_1_1 = jokers_1.next()) {
541
+ var joker = jokers_1_1.value;
542
+ commands_1.push("JOKER {".concat(joker, "}"));
543
+ }
544
+ }
545
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
546
+ finally {
547
+ try {
548
+ if (jokers_1_1 && !jokers_1_1.done && (_d = jokers_1.return)) _d.call(jokers_1);
549
+ }
550
+ finally { if (e_4) throw e_4.error; }
551
+ }
552
+ } /* not else */
553
+ if (postprocessing) {
554
+ try {
555
+ for (var postprocessing_1 = (e_5 = void 0, __values(postprocessing)), postprocessing_1_1 = postprocessing_1.next(); !postprocessing_1_1.done; postprocessing_1_1 = postprocessing_1.next()) {
556
+ var postprocessingFunctionName = postprocessing_1_1.value;
557
+ commands_1.push("POSTPROCESSING `".concat(postprocessingFunctionName, "`"));
558
+ }
559
+ }
560
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
561
+ finally {
562
+ try {
563
+ if (postprocessing_1_1 && !postprocessing_1_1.done && (_e = postprocessing_1.return)) _e.call(postprocessing_1);
564
+ }
565
+ finally { if (e_5) throw e_5.error; }
566
+ }
567
+ } /* not else */
568
+ if (expectations) {
569
+ try {
570
+ for (var _m = (e_6 = void 0, __values(Object.entries(expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
571
+ var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
572
+ if (min === max) {
573
+ commands_1.push("EXPECT EXACTLY ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
574
+ }
575
+ else {
576
+ if (min !== undefined) {
577
+ commands_1.push("EXPECT MIN ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
578
+ } /* not else */
579
+ if (max !== undefined) {
580
+ commands_1.push("EXPECT MAX ".concat(max, " ").concat(capitalize(unit + (max > 1 ? 's' : ''))));
581
+ }
582
+ }
583
+ }
584
+ }
585
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
586
+ finally {
587
+ try {
588
+ if (_o && !_o.done && (_f = _m.return)) _f.call(_m);
589
+ }
590
+ finally { if (e_6) throw e_6.error; }
591
+ }
592
+ } /* not else */
593
+ if (format) {
594
+ if (format === 'JSON') {
595
+ // TODO: @deprecated remove
596
+ commands_1.push("FORMAT JSON");
597
+ }
598
+ } /* not else */
599
+ pipelineString += '\n\n';
600
+ pipelineString += commands_1.map(function (command) { return "- ".concat(command); }).join('\n');
601
+ pipelineString += '\n\n';
602
+ pipelineString += '```' + contentLanguage;
603
+ pipelineString += '\n';
604
+ pipelineString += spaceTrim__default["default"](content);
605
+ // <- TODO: [main] !!3 Escape
606
+ // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
607
+ pipelineString += '\n';
608
+ pipelineString += '```';
609
+ pipelineString += '\n\n';
610
+ pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
611
+ }
612
+ }
613
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
614
+ finally {
615
+ try {
616
+ if (tasks_1_1 && !tasks_1_1.done && (_c = tasks_1.return)) _c.call(tasks_1);
617
+ }
618
+ finally { if (e_3) throw e_3.error; }
619
+ }
620
+ return validatePipelineString(pipelineString);
590
621
  }
591
622
  /**
592
- * TODO: [🧠] Is there a way how to meaningfully test this utility
623
+ * @private internal utility of `pipelineJsonToString`
593
624
  */
594
-
625
+ function taskParameterJsonToString(taskParameterJson) {
626
+ var name = taskParameterJson.name, description = taskParameterJson.description;
627
+ var parameterString = "{".concat(name, "}");
628
+ if (description) {
629
+ parameterString = "".concat(parameterString, " ").concat(description);
630
+ }
631
+ return parameterString;
632
+ }
595
633
  /**
596
- * Order of keys in the pipeline JSON
597
- *
598
- * @public exported from `@promptbook/core`
634
+ * TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
635
+ * TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
636
+ * TODO: [🏛] Maybe make some markdown builder
637
+ * TODO: [🏛] Escape all
638
+ * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
599
639
  */
600
- var ORDER_OF_PIPELINE_JSON = [
601
- // Note: [🍙] In this order will be pipeline serialized
602
- 'title',
603
- 'pipelineUrl',
604
- 'bookVersion',
605
- 'description',
606
- 'formfactorName',
607
- 'parameters',
608
- 'tasks',
609
- 'personas',
610
- 'preparations',
611
- 'knowledgeSources',
612
- 'knowledgePieces',
613
- 'sources', // <- TODO: [🧠] Where should the `sources` be
614
- ];
640
+
615
641
  /**
616
- * Nonce which is used for replacing things in strings
642
+ * Orders JSON object by keys
617
643
  *
618
- * @private within the repository
644
+ * @returns The same type of object as the input re-ordered
645
+ * @public exported from `@promptbook/utils`
619
646
  */
620
- var REPLACING_NONCE = 'u$k42k%!V2zo34w7Fu#@QUHYPW';
647
+ function orderJson(options) {
648
+ var value = options.value, order = options.order;
649
+ var orderedValue = __assign(__assign({}, (order === undefined ? {} : Object.fromEntries(order.map(function (key) { return [key, undefined]; })))), value);
650
+ return orderedValue;
651
+ }
652
+
621
653
  /**
622
- * @@@
654
+ * Freezes the given object and all its nested objects recursively
623
655
  *
624
- * @private within the repository
625
- */
626
- var RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
627
- /**
628
- * @@@
656
+ * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
657
+ * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
629
658
  *
630
- * @private within the repository
659
+ * @returns The same object as the input, but deeply frozen
660
+ * @public exported from `@promptbook/utils`
631
661
  */
632
- var RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
662
+ function $deepFreeze(objectValue) {
663
+ var e_1, _a;
664
+ if (Array.isArray(objectValue)) {
665
+ return Object.freeze(objectValue.map(function (item) { return $deepFreeze(item); }));
666
+ }
667
+ var propertyNames = Object.getOwnPropertyNames(objectValue);
668
+ try {
669
+ for (var propertyNames_1 = __values(propertyNames), propertyNames_1_1 = propertyNames_1.next(); !propertyNames_1_1.done; propertyNames_1_1 = propertyNames_1.next()) {
670
+ var propertyName = propertyNames_1_1.value;
671
+ var value = objectValue[propertyName];
672
+ if (value && typeof value === 'object') {
673
+ $deepFreeze(value);
674
+ }
675
+ }
676
+ }
677
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
678
+ finally {
679
+ try {
680
+ if (propertyNames_1_1 && !propertyNames_1_1.done && (_a = propertyNames_1.return)) _a.call(propertyNames_1);
681
+ }
682
+ finally { if (e_1) throw e_1.error; }
683
+ }
684
+ Object.freeze(objectValue);
685
+ return objectValue;
686
+ }
633
687
  /**
634
- * The names of the parameters that are reserved for special purposes
635
- *
636
- * @public exported from `@promptbook/core`
688
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
637
689
  */
638
- var RESERVED_PARAMETER_NAMES = exportJson({
639
- name: 'RESERVED_PARAMETER_NAMES',
640
- message: "The names of the parameters that are reserved for special purposes",
641
- value: [
642
- 'content',
643
- 'context',
644
- 'knowledge',
645
- 'examples',
646
- 'modelName',
647
- 'currentDate',
648
- // <- TODO: list here all command names
649
- // <- TODO: Add more like 'date', 'modelName',...
650
- // <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
651
- ],
652
- });
690
+
653
691
  /**
654
- * Note: [💞] Ignore a discrepancy between file name and entity name
692
+ * Make error report URL for the given error
693
+ *
694
+ * @private private within the repository
655
695
  */
696
+ function getErrorReportUrl(error) {
697
+ var report = {
698
+ title: "\uD83D\uDC1C Error report from ".concat(NAME),
699
+ body: spaceTrim__default["default"](function (block) { return "\n\n\n `".concat(error.name || 'Error', "` has occurred in the [").concat(NAME, "], please look into it @").concat(ADMIN_GITHUB_NAME, ".\n\n ```\n ").concat(block(error.message || '(no error message)'), "\n ```\n\n\n ## More info:\n\n - **Promptbook engine version:** ").concat(PROMPTBOOK_ENGINE_VERSION, "\n - **Book language version:** ").concat(BOOK_LANGUAGE_VERSION, "\n - **Time:** ").concat(new Date().toISOString(), "\n\n <details>\n <summary>Stack trace:</summary>\n\n ## Stack trace:\n\n ```stacktrace\n ").concat(block(error.stack || '(empty)'), "\n ```\n </details>\n\n "); }),
700
+ };
701
+ var reportUrl = new URL("https://github.com/webgptorg/promptbook/issues/new");
702
+ reportUrl.searchParams.set('labels', 'bug');
703
+ reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
704
+ reportUrl.searchParams.set('title', report.title);
705
+ reportUrl.searchParams.set('body', report.body);
706
+ return reportUrl;
707
+ }
656
708
 
657
709
  /**
658
- * This error type indicates that some tools are missing for pipeline execution or preparation
710
+ * This error type indicates that the error should not happen and its last check before crashing with some other error
659
711
  *
660
712
  * @public exported from `@promptbook/core`
661
713
  */
662
- var MissingToolsError = /** @class */ (function (_super) {
663
- __extends(MissingToolsError, _super);
664
- function MissingToolsError(message) {
665
- 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;
666
- _this.name = 'MissingToolsError';
667
- Object.setPrototypeOf(_this, MissingToolsError.prototype);
714
+ var UnexpectedError = /** @class */ (function (_super) {
715
+ __extends(UnexpectedError, _super);
716
+ function UnexpectedError(message) {
717
+ var _this = _super.call(this, spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(message), "\n\n Note: This error should not happen.\n It's probbably a bug in the pipeline collection\n\n Please report issue:\n ").concat(block(getErrorReportUrl(new Error(message)).href), "\n\n Or contact us on ").concat(ADMIN_EMAIL, "\n\n "); })) || this;
718
+ _this.name = 'UnexpectedError';
719
+ Object.setPrototypeOf(_this, UnexpectedError.prototype);
668
720
  return _this;
669
721
  }
670
- return MissingToolsError;
722
+ return UnexpectedError;
671
723
  }(Error));
672
724
 
673
725
  /**
674
- * Async version of Array.forEach
726
+ * Checks if the value is [🚉] serializable as JSON
727
+ * If not, throws an UnexpectedError with a rich error message and tracking
675
728
  *
676
- * @param array - Array to iterate over
677
- * @param options - Options for the function
678
- * @param callbackfunction - Function to call for each item
729
+ * - Almost all primitives are serializable BUT:
730
+ * - `undefined` is not serializable
731
+ * - `NaN` is not serializable
732
+ * - Objects and arrays are serializable if all their properties are serializable
733
+ * - Functions are not serializable
734
+ * - Circular references are not serializable
735
+ * - `Date` objects are not serializable
736
+ * - `Map` and `Set` objects are not serializable
737
+ * - `RegExp` objects are not serializable
738
+ * - `Error` objects are not serializable
739
+ * - `Symbol` objects are not serializable
740
+ * - And much more...
741
+ *
742
+ * @throws UnexpectedError if the value is not serializable as JSON
679
743
  * @public exported from `@promptbook/utils`
680
- * @deprecated [🪂] Use queues instead
681
744
  */
682
- function forEachAsync(array, options, callbackfunction) {
683
- return __awaiter(this, void 0, void 0, function () {
684
- var _a, maxParallelCount, index, runningTasks, tasks, _loop_1, _b, _c, item, e_1_1;
685
- var e_1, _d;
686
- return __generator(this, function (_e) {
687
- switch (_e.label) {
688
- case 0:
689
- _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? Infinity : _a;
690
- index = 0;
691
- runningTasks = [];
692
- tasks = [];
693
- _loop_1 = function (item) {
694
- var currentIndex, task;
695
- return __generator(this, function (_f) {
696
- switch (_f.label) {
697
- case 0:
698
- currentIndex = index++;
699
- task = callbackfunction(item, currentIndex, array);
700
- tasks.push(task);
701
- runningTasks.push(task);
702
- /* not await */ Promise.resolve(task).then(function () {
703
- runningTasks = runningTasks.filter(function (t) { return t !== task; });
704
- });
705
- if (!(maxParallelCount < runningTasks.length)) return [3 /*break*/, 2];
706
- return [4 /*yield*/, Promise.race(runningTasks)];
707
- case 1:
708
- _f.sent();
709
- _f.label = 2;
710
- case 2: return [2 /*return*/];
711
- }
712
- });
713
- };
714
- _e.label = 1;
715
- case 1:
716
- _e.trys.push([1, 6, 7, 8]);
717
- _b = __values(array), _c = _b.next();
718
- _e.label = 2;
719
- case 2:
720
- if (!!_c.done) return [3 /*break*/, 5];
721
- item = _c.value;
722
- return [5 /*yield**/, _loop_1(item)];
723
- case 3:
724
- _e.sent();
725
- _e.label = 4;
726
- case 4:
727
- _c = _b.next();
728
- return [3 /*break*/, 2];
729
- case 5: return [3 /*break*/, 8];
730
- case 6:
731
- e_1_1 = _e.sent();
732
- e_1 = { error: e_1_1 };
733
- return [3 /*break*/, 8];
734
- case 7:
735
- try {
736
- if (_c && !_c.done && (_d = _b.return)) _d.call(_b);
745
+ function checkSerializableAsJson(options) {
746
+ var e_1, _a;
747
+ var value = options.value, name = options.name, message = options.message;
748
+ if (value === undefined) {
749
+ throw new UnexpectedError("".concat(name, " is undefined"));
750
+ }
751
+ else if (value === null) {
752
+ return;
753
+ }
754
+ else if (typeof value === 'boolean') {
755
+ return;
756
+ }
757
+ else if (typeof value === 'number' && !isNaN(value)) {
758
+ return;
759
+ }
760
+ else if (typeof value === 'string') {
761
+ return;
762
+ }
763
+ else if (typeof value === 'symbol') {
764
+ throw new UnexpectedError("".concat(name, " is symbol"));
765
+ }
766
+ else if (typeof value === 'function') {
767
+ throw new UnexpectedError("".concat(name, " is function"));
768
+ }
769
+ else if (typeof value === 'object' && Array.isArray(value)) {
770
+ for (var i = 0; i < value.length; i++) {
771
+ checkSerializableAsJson({ name: "".concat(name, "[").concat(i, "]"), value: value[i], message: message });
772
+ }
773
+ }
774
+ else if (typeof value === 'object') {
775
+ if (value instanceof Date) {
776
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is Date\n\n Use `string_date_iso8601` instead\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
777
+ }
778
+ else if (value instanceof Map) {
779
+ throw new UnexpectedError("".concat(name, " is Map"));
780
+ }
781
+ else if (value instanceof Set) {
782
+ throw new UnexpectedError("".concat(name, " is Set"));
783
+ }
784
+ else if (value instanceof RegExp) {
785
+ throw new UnexpectedError("".concat(name, " is RegExp"));
786
+ }
787
+ else if (value instanceof Error) {
788
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is unserialized Error\n\n Use function `serializeError`\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n\n "); }));
789
+ }
790
+ else {
791
+ try {
792
+ for (var _b = __values(Object.entries(value)), _c = _b.next(); !_c.done; _c = _b.next()) {
793
+ var _d = __read(_c.value, 2), subName = _d[0], subValue = _d[1];
794
+ if (subValue === undefined) {
795
+ // Note: undefined in object is serializable - it is just omited
796
+ continue;
737
797
  }
738
- finally { if (e_1) throw e_1.error; }
739
- return [7 /*endfinally*/];
740
- case 8: return [4 /*yield*/, Promise.all(tasks)];
741
- case 9:
742
- _e.sent();
743
- return [2 /*return*/];
798
+ checkSerializableAsJson({ name: "".concat(name, ".").concat(subName), value: subValue, message: message });
799
+ }
744
800
  }
745
- });
746
- });
747
- }
748
-
749
- /**
750
- * Represents the usage with no resources consumed
751
- *
752
- * @public exported from `@promptbook/core`
753
- */
754
- var ZERO_USAGE = $deepFreeze({
755
- price: { value: 0 },
756
- input: {
757
- tokensCount: { value: 0 },
758
- charactersCount: { value: 0 },
759
- wordsCount: { value: 0 },
760
- sentencesCount: { value: 0 },
761
- linesCount: { value: 0 },
762
- paragraphsCount: { value: 0 },
763
- pagesCount: { value: 0 },
764
- },
765
- output: {
766
- tokensCount: { value: 0 },
767
- charactersCount: { value: 0 },
768
- wordsCount: { value: 0 },
769
- sentencesCount: { value: 0 },
770
- linesCount: { value: 0 },
771
- paragraphsCount: { value: 0 },
772
- pagesCount: { value: 0 },
773
- },
774
- });
775
- /**
776
- * Represents the usage with unknown resources consumed
777
- *
778
- * @public exported from `@promptbook/core`
779
- */
780
- $deepFreeze({
781
- price: { value: 0, isUncertain: true },
782
- input: {
783
- tokensCount: { value: 0, isUncertain: true },
784
- charactersCount: { value: 0, isUncertain: true },
785
- wordsCount: { value: 0, isUncertain: true },
786
- sentencesCount: { value: 0, isUncertain: true },
787
- linesCount: { value: 0, isUncertain: true },
788
- paragraphsCount: { value: 0, isUncertain: true },
789
- pagesCount: { value: 0, isUncertain: true },
790
- },
791
- output: {
792
- tokensCount: { value: 0, isUncertain: true },
793
- charactersCount: { value: 0, isUncertain: true },
794
- wordsCount: { value: 0, isUncertain: true },
795
- sentencesCount: { value: 0, isUncertain: true },
796
- linesCount: { value: 0, isUncertain: true },
797
- paragraphsCount: { value: 0, isUncertain: true },
798
- pagesCount: { value: 0, isUncertain: true },
799
- },
800
- });
801
- /**
802
- * Note: [💞] Ignore a discrepancy between file name and entity name
803
- */
804
-
805
- /**
806
- * Function `addUsage` will add multiple usages into one
807
- *
808
- * Note: If you provide 0 values, it returns ZERO_USAGE
809
- *
810
- * @public exported from `@promptbook/core`
811
- */
812
- function addUsage() {
813
- var usageItems = [];
814
- for (var _i = 0; _i < arguments.length; _i++) {
815
- usageItems[_i] = arguments[_i];
816
- }
817
- return usageItems.reduce(function (acc, item) {
818
- var e_1, _a, e_2, _b;
819
- var _c;
820
- acc.price.value += ((_c = item.price) === null || _c === void 0 ? void 0 : _c.value) || 0;
821
- try {
822
- for (var _d = __values(Object.keys(acc.input)), _e = _d.next(); !_e.done; _e = _d.next()) {
823
- var key = _e.value;
824
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
825
- //@ts-ignore
826
- if (item.input[key]) {
827
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
828
- //@ts-ignore
829
- acc.input[key].value += item.input[key].value || 0;
830
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
831
- //@ts-ignore
832
- if (item.input[key].isUncertain) {
833
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
834
- //@ts-ignore
835
- acc.input[key].isUncertain = true;
836
- }
801
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
802
+ finally {
803
+ try {
804
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
837
805
  }
806
+ finally { if (e_1) throw e_1.error; }
838
807
  }
839
- }
840
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
841
- finally {
842
808
  try {
843
- if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
809
+ JSON.stringify(value); // <- TODO: [0]
844
810
  }
845
- finally { if (e_1) throw e_1.error; }
846
- }
847
- try {
848
- for (var _f = __values(Object.keys(acc.output)), _g = _f.next(); !_g.done; _g = _f.next()) {
849
- var key = _g.value;
850
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
851
- //@ts-ignore
852
- if (item.output[key]) {
853
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
854
- //@ts-ignore
855
- acc.output[key].value += item.output[key].value || 0;
856
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
857
- //@ts-ignore
858
- if (item.output[key].isUncertain) {
859
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
860
- //@ts-ignore
861
- acc.output[key].isUncertain = true;
862
- }
811
+ catch (error) {
812
+ if (!(error instanceof Error)) {
813
+ throw error;
863
814
  }
815
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is not serializable\n\n ").concat(block(error.stack || error.message), "\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
864
816
  }
865
- }
866
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
867
- finally {
868
- try {
869
- if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
817
+ /*
818
+ TODO: [0] Is there some more elegant way to check circular references?
819
+ const seen = new Set();
820
+ const stack = [{ value }];
821
+ while (stack.length > 0) {
822
+ const { value } = stack.pop()!;
823
+ if (typeof value === 'object' && value !== null) {
824
+ if (seen.has(value)) {
825
+ throw new UnexpectedError(`${name} has circular reference`);
826
+ }
827
+ seen.add(value);
828
+ if (Array.isArray(value)) {
829
+ stack.push(...value.map((value) => ({ value })));
830
+ } else {
831
+ stack.push(...Object.values(value).map((value) => ({ value })));
832
+ }
833
+ }
870
834
  }
871
- finally { if (e_2) throw e_2.error; }
835
+ */
836
+ return;
872
837
  }
873
- return acc;
874
- }, deepClone(ZERO_USAGE));
875
- }
876
-
877
- /**
878
- * Intercepts LLM tools and counts total usage of the tools
879
- *
880
- * @param llmTools LLM tools to be intercepted with usage counting
881
- * @returns LLM tools with same functionality with added total cost counting
882
- * @public exported from `@promptbook/core`
883
- */
884
- function countTotalUsage(llmTools) {
885
- var _this = this;
886
- var totalUsage = ZERO_USAGE;
887
- var proxyTools = {
888
- get title() {
889
- // TODO: [🧠] Maybe put here some suffix
890
- return llmTools.title;
891
- },
892
- get description() {
893
- // TODO: [🧠] Maybe put here some suffix
894
- return llmTools.description;
895
- },
896
- checkConfiguration: function () {
897
- return __awaiter(this, void 0, void 0, function () {
898
- return __generator(this, function (_a) {
899
- return [2 /*return*/, /* not await */ llmTools.checkConfiguration()];
900
- });
901
- });
902
- },
903
- listModels: function () {
904
- return /* not await */ llmTools.listModels();
905
- },
906
- getTotalUsage: function () {
907
- // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
908
- return totalUsage;
909
- },
910
- };
911
- if (llmTools.callChatModel !== undefined) {
912
- proxyTools.callChatModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
913
- var promptResult;
914
- return __generator(this, function (_a) {
915
- switch (_a.label) {
916
- case 0: return [4 /*yield*/, llmTools.callChatModel(prompt)];
917
- case 1:
918
- promptResult = _a.sent();
919
- totalUsage = addUsage(totalUsage, promptResult.usage);
920
- return [2 /*return*/, promptResult];
921
- }
922
- });
923
- }); };
924
- }
925
- if (llmTools.callCompletionModel !== undefined) {
926
- proxyTools.callCompletionModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
927
- var promptResult;
928
- return __generator(this, function (_a) {
929
- switch (_a.label) {
930
- case 0: return [4 /*yield*/, llmTools.callCompletionModel(prompt)];
931
- case 1:
932
- promptResult = _a.sent();
933
- totalUsage = addUsage(totalUsage, promptResult.usage);
934
- return [2 /*return*/, promptResult];
935
- }
936
- });
937
- }); };
938
838
  }
939
- if (llmTools.callEmbeddingModel !== undefined) {
940
- proxyTools.callEmbeddingModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
941
- var promptResult;
942
- return __generator(this, function (_a) {
943
- switch (_a.label) {
944
- case 0: return [4 /*yield*/, llmTools.callEmbeddingModel(prompt)];
945
- case 1:
946
- promptResult = _a.sent();
947
- totalUsage = addUsage(totalUsage, promptResult.usage);
948
- return [2 /*return*/, promptResult];
949
- }
950
- });
951
- }); };
839
+ else {
840
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is unknown type\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
952
841
  }
953
- // <- Note: [🤖]
954
- return proxyTools;
955
842
  }
956
843
  /**
957
- * TODO: [🧠][💸] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
958
- * TODO: [🧠] Is there some meaningfull way how to test this util
959
- * TODO: [🧠][🌯] Maybe a way how to hide ability to `get totalUsage`
960
- * > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
961
- * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
844
+ * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
845
+ * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
846
+ * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
962
847
  */
963
848
 
964
849
  /**
965
- * This error indicates errors during the execution of the pipeline
850
+ * @@@
966
851
  *
967
- * @public exported from `@promptbook/core`
852
+ * @public exported from `@promptbook/utils`
853
+ */
854
+ function deepClone(objectValue) {
855
+ return JSON.parse(JSON.stringify(objectValue));
856
+ /*
857
+ TODO: [🧠] Is there a better implementation?
858
+ > const propertyNames = Object.getOwnPropertyNames(objectValue);
859
+ > for (const propertyName of propertyNames) {
860
+ > const value = (objectValue as really_any)[propertyName];
861
+ > if (value && typeof value === 'object') {
862
+ > deepClone(value);
863
+ > }
864
+ > }
865
+ > return Object.assign({}, objectValue);
866
+ */
867
+ }
868
+ /**
869
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
968
870
  */
969
- var PipelineExecutionError = /** @class */ (function (_super) {
970
- __extends(PipelineExecutionError, _super);
971
- function PipelineExecutionError(message) {
972
- var _this = _super.call(this, message) || this;
973
- _this.name = 'PipelineExecutionError';
974
- Object.setPrototypeOf(_this, PipelineExecutionError.prototype);
975
- return _this;
976
- }
977
- return PipelineExecutionError;
978
- }(Error));
979
871
 
980
872
  /**
981
- * Multiple LLM Execution Tools is a proxy server that uses multiple execution tools internally and exposes the executor interface externally.
873
+ * Utility to export a JSON object from a function
982
874
  *
983
- * Note: Internal utility of `joinLlmExecutionTools` but exposed type
984
- * @public exported from `@promptbook/core`
875
+ * 1) Checks if the value is serializable as JSON
876
+ * 2) Makes a deep clone of the object
877
+ * 2) Orders the object properties
878
+ * 2) Deeply freezes the cloned object
879
+ *
880
+ * Note: This function does not mutates the given object
881
+ *
882
+ * @returns The same type of object as the input but read-only and re-ordered
883
+ * @public exported from `@promptbook/utils`
985
884
  */
986
- var MultipleLlmExecutionTools = /** @class */ (function () {
987
- /**
988
- * Gets array of execution tools in order of priority
989
- */
990
- function MultipleLlmExecutionTools() {
991
- var llmExecutionTools = [];
992
- for (var _i = 0; _i < arguments.length; _i++) {
993
- llmExecutionTools[_i] = arguments[_i];
994
- }
995
- this.llmExecutionTools = llmExecutionTools;
996
- }
997
- Object.defineProperty(MultipleLlmExecutionTools.prototype, "title", {
998
- get: function () {
999
- return 'Multiple LLM Providers';
1000
- },
1001
- enumerable: false,
1002
- configurable: true
1003
- });
1004
- Object.defineProperty(MultipleLlmExecutionTools.prototype, "description", {
1005
- get: function () {
1006
- return this.llmExecutionTools.map(function (_a, index) {
1007
- var title = _a.title;
1008
- return "".concat(index + 1, ") `").concat(title, "`");
1009
- }).join('\n');
1010
- },
1011
- enumerable: false,
1012
- configurable: true
1013
- });
1014
- /**
1015
- * Check the configuration of all execution tools
1016
- */
1017
- MultipleLlmExecutionTools.prototype.checkConfiguration = function () {
1018
- return __awaiter(this, void 0, void 0, function () {
1019
- var _a, _b, llmExecutionTools, e_1_1;
1020
- var e_1, _c;
1021
- return __generator(this, function (_d) {
1022
- switch (_d.label) {
1023
- case 0:
1024
- _d.trys.push([0, 5, 6, 7]);
1025
- _a = __values(this.llmExecutionTools), _b = _a.next();
1026
- _d.label = 1;
1027
- case 1:
1028
- if (!!_b.done) return [3 /*break*/, 4];
1029
- llmExecutionTools = _b.value;
1030
- return [4 /*yield*/, llmExecutionTools.checkConfiguration()];
1031
- case 2:
1032
- _d.sent();
1033
- _d.label = 3;
1034
- case 3:
1035
- _b = _a.next();
1036
- return [3 /*break*/, 1];
1037
- case 4: return [3 /*break*/, 7];
1038
- case 5:
1039
- e_1_1 = _d.sent();
1040
- e_1 = { error: e_1_1 };
1041
- return [3 /*break*/, 7];
1042
- case 6:
1043
- try {
1044
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
1045
- }
1046
- finally { if (e_1) throw e_1.error; }
1047
- return [7 /*endfinally*/];
1048
- case 7: return [2 /*return*/];
1049
- }
1050
- });
1051
- });
1052
- };
1053
- /**
1054
- * List all available models that can be used
1055
- * This lists is a combination of all available models from all execution tools
1056
- */
1057
- MultipleLlmExecutionTools.prototype.listModels = function () {
1058
- return __awaiter(this, void 0, void 0, function () {
1059
- var availableModels, _a, _b, llmExecutionTools, models, e_2_1;
1060
- var e_2, _c;
1061
- return __generator(this, function (_d) {
1062
- switch (_d.label) {
1063
- case 0:
1064
- availableModels = [];
1065
- _d.label = 1;
1066
- case 1:
1067
- _d.trys.push([1, 6, 7, 8]);
1068
- _a = __values(this.llmExecutionTools), _b = _a.next();
1069
- _d.label = 2;
1070
- case 2:
1071
- if (!!_b.done) return [3 /*break*/, 5];
1072
- llmExecutionTools = _b.value;
1073
- return [4 /*yield*/, llmExecutionTools.listModels()];
1074
- case 3:
1075
- models = _d.sent();
1076
- availableModels.push.apply(availableModels, __spreadArray([], __read(models), false));
1077
- _d.label = 4;
1078
- case 4:
1079
- _b = _a.next();
1080
- return [3 /*break*/, 2];
1081
- case 5: return [3 /*break*/, 8];
1082
- case 6:
1083
- e_2_1 = _d.sent();
1084
- e_2 = { error: e_2_1 };
1085
- return [3 /*break*/, 8];
1086
- case 7:
1087
- try {
1088
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
1089
- }
1090
- finally { if (e_2) throw e_2.error; }
1091
- return [7 /*endfinally*/];
1092
- case 8: return [2 /*return*/, availableModels];
1093
- }
1094
- });
1095
- });
1096
- };
1097
- /**
1098
- * Calls the best available chat model
1099
- */
1100
- MultipleLlmExecutionTools.prototype.callChatModel = function (prompt) {
1101
- return this.callCommonModel(prompt);
1102
- };
1103
- /**
1104
- * Calls the best available completion model
1105
- */
1106
- MultipleLlmExecutionTools.prototype.callCompletionModel = function (prompt) {
1107
- return this.callCommonModel(prompt);
1108
- };
1109
- /**
1110
- * Calls the best available embedding model
1111
- */
1112
- MultipleLlmExecutionTools.prototype.callEmbeddingModel = function (prompt) {
1113
- return this.callCommonModel(prompt);
1114
- };
1115
- // <- Note: [🤖]
1116
- /**
1117
- * Calls the best available model
1118
- *
1119
- * Note: This should be private or protected but is public to be usable with duck typing
1120
- */
1121
- MultipleLlmExecutionTools.prototype.callCommonModel = function (prompt) {
1122
- return __awaiter(this, void 0, void 0, function () {
1123
- var errors, _a, _b, llmExecutionTools, _c, error_1, e_3_1;
1124
- var e_3, _d;
1125
- var _this = this;
1126
- return __generator(this, function (_e) {
1127
- switch (_e.label) {
1128
- case 0:
1129
- errors = [];
1130
- _e.label = 1;
1131
- case 1:
1132
- _e.trys.push([1, 15, 16, 17]);
1133
- _a = __values(this.llmExecutionTools), _b = _a.next();
1134
- _e.label = 2;
1135
- case 2:
1136
- if (!!_b.done) return [3 /*break*/, 14];
1137
- llmExecutionTools = _b.value;
1138
- _e.label = 3;
1139
- case 3:
1140
- _e.trys.push([3, 12, , 13]);
1141
- _c = prompt.modelRequirements.modelVariant;
1142
- switch (_c) {
1143
- case 'CHAT': return [3 /*break*/, 4];
1144
- case 'COMPLETION': return [3 /*break*/, 6];
1145
- case 'EMBEDDING': return [3 /*break*/, 8];
1146
- }
1147
- return [3 /*break*/, 10];
1148
- case 4:
1149
- if (llmExecutionTools.callChatModel === undefined) {
1150
- return [3 /*break*/, 13];
1151
- }
1152
- return [4 /*yield*/, llmExecutionTools.callChatModel(prompt)];
1153
- case 5: return [2 /*return*/, _e.sent()];
1154
- case 6:
1155
- if (llmExecutionTools.callCompletionModel === undefined) {
1156
- return [3 /*break*/, 13];
1157
- }
1158
- return [4 /*yield*/, llmExecutionTools.callCompletionModel(prompt)];
1159
- case 7: return [2 /*return*/, _e.sent()];
1160
- case 8:
1161
- if (llmExecutionTools.callEmbeddingModel === undefined) {
1162
- return [3 /*break*/, 13];
1163
- }
1164
- return [4 /*yield*/, llmExecutionTools.callEmbeddingModel(prompt)];
1165
- case 9: return [2 /*return*/, _e.sent()];
1166
- case 10: throw new UnexpectedError("Unknown model variant \"".concat(prompt.modelRequirements.modelVariant, "\""));
1167
- case 11: return [3 /*break*/, 13];
1168
- case 12:
1169
- error_1 = _e.sent();
1170
- if (!(error_1 instanceof Error) || error_1 instanceof UnexpectedError) {
1171
- throw error_1;
1172
- }
1173
- errors.push(error_1);
1174
- return [3 /*break*/, 13];
1175
- case 13:
1176
- _b = _a.next();
1177
- return [3 /*break*/, 2];
1178
- case 14: return [3 /*break*/, 17];
1179
- case 15:
1180
- e_3_1 = _e.sent();
1181
- e_3 = { error: e_3_1 };
1182
- return [3 /*break*/, 17];
1183
- case 16:
1184
- try {
1185
- if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
1186
- }
1187
- finally { if (e_3) throw e_3.error; }
1188
- return [7 /*endfinally*/];
1189
- case 17:
1190
- if (errors.length === 1) {
1191
- throw errors[0];
1192
- }
1193
- else if (errors.length > 1) {
1194
- throw new PipelineExecutionError(
1195
- // TODO: Tell which execution tools failed like
1196
- // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
1197
- // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
1198
- // 3) ...
1199
- spaceTrim__default["default"](function (block) { return "\n All execution tools failed:\n\n ".concat(block(errors
1200
- .map(function (error, i) { return "".concat(i + 1, ") **").concat(error.name || 'Error', ":** ").concat(error.message); })
1201
- .join('\n')), "\n\n "); }));
1202
- }
1203
- else if (this.llmExecutionTools.length === 0) {
1204
- throw new PipelineExecutionError("You have not provided any `LlmExecutionTools`");
1205
- }
1206
- else {
1207
- throw new PipelineExecutionError(spaceTrim__default["default"](function (block) { return "\n You have not provided any `LlmExecutionTools` that support model variant \"".concat(prompt.modelRequirements.modelVariant, "\"\n\n Available `LlmExecutionTools`:\n ").concat(block(_this.description), "\n\n "); }));
1208
- }
1209
- }
1210
- });
885
+ function exportJson(options) {
886
+ var name = options.name, value = options.value, order = options.order, message = options.message;
887
+ checkSerializableAsJson({ name: name, value: value, message: message });
888
+ var orderedValue =
889
+ // TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
890
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
891
+ // @ts-ignore
892
+ order === undefined
893
+ ? deepClone(value)
894
+ : orderJson({
895
+ value: value,
896
+ // <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
897
+ order: order,
1211
898
  });
1212
- };
1213
- return MultipleLlmExecutionTools;
1214
- }());
899
+ $deepFreeze(orderedValue);
900
+ return orderedValue;
901
+ }
1215
902
  /**
1216
- * TODO: [🧠][🎛] Aggregating multiple models - have result not only from one first aviable model BUT all of them
1217
- * TODO: [🏖] If no llmTools have for example not defined `callCompletionModel` this will still return object with defined `callCompletionModel` which just throws `PipelineExecutionError`, make it undefined instead
1218
- * Look how `countTotalUsage` (and `cacheLlmTools`) implements it
903
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
1219
904
  */
1220
905
 
1221
906
  /**
1222
- * Joins multiple LLM Execution Tools into one
1223
- *
1224
- * @returns {LlmExecutionTools} Single wrapper for multiple LlmExecutionTools
1225
- *
1226
- * 0) If there is no LlmExecutionTools, it warns and returns valid but empty LlmExecutionTools
1227
- * 1) If there is only one LlmExecutionTools, it returns it wrapped in a proxy object
1228
- * 2) If there are multiple LlmExecutionTools, first will be used first, second will be used if the first hasn`t defined model variant or fails, etc.
1229
- * 3) When all LlmExecutionTools fail, it throws an error with a list of all errors merged into one
1230
- *
1231
- *
1232
- * Tip: You don't have to use this function directly, just pass an array of LlmExecutionTools to the `ExecutionTools`
907
+ * Order of keys in the pipeline JSON
1233
908
  *
1234
909
  * @public exported from `@promptbook/core`
1235
910
  */
1236
- function joinLlmExecutionTools() {
1237
- var llmExecutionTools = [];
1238
- for (var _i = 0; _i < arguments.length; _i++) {
1239
- llmExecutionTools[_i] = arguments[_i];
1240
- }
1241
- if (llmExecutionTools.length === 0) {
1242
- var warningMessage = spaceTrim__default["default"]("\n You have not provided any `LlmExecutionTools`\n This means that you won't be able to execute any prompts that require large language models like GPT-4 or Anthropic's Claude.\n\n Technically, it's not an error, but it's probably not what you want because it does not make sense to use Promptbook without language models.\n ");
1243
- // TODO: [🟥] Detect browser / node and make it colorfull
1244
- console.warn(warningMessage);
1245
- /*
1246
- return {
1247
- async listModels() {
1248
- // TODO: [🟥] Detect browser / node and make it colorfull
1249
- console.warn(
1250
- spaceTrim(
1251
- (block) => `
1252
-
1253
- You can't list models because you have no LLM Execution Tools defined:
911
+ var ORDER_OF_PIPELINE_JSON = [
912
+ // Note: [🍙] In this order will be pipeline serialized
913
+ 'title',
914
+ 'pipelineUrl',
915
+ 'bookVersion',
916
+ 'description',
917
+ 'formfactorName',
918
+ 'parameters',
919
+ 'tasks',
920
+ 'personas',
921
+ 'preparations',
922
+ 'knowledgeSources',
923
+ 'knowledgePieces',
924
+ 'sources', // <- TODO: [🧠] Where should the `sources` be
925
+ ];
926
+ /**
927
+ * Nonce which is used for replacing things in strings
928
+ *
929
+ * @private within the repository
930
+ */
931
+ var REPLACING_NONCE = 'u$k42k%!V2zo34w7Fu#@QUHYPW';
932
+ /**
933
+ * @@@
934
+ *
935
+ * @private within the repository
936
+ */
937
+ var RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
938
+ /**
939
+ * @@@
940
+ *
941
+ * @private within the repository
942
+ */
943
+ var RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
944
+ /**
945
+ * The names of the parameters that are reserved for special purposes
946
+ *
947
+ * @public exported from `@promptbook/core`
948
+ */
949
+ var RESERVED_PARAMETER_NAMES = exportJson({
950
+ name: 'RESERVED_PARAMETER_NAMES',
951
+ message: "The names of the parameters that are reserved for special purposes",
952
+ value: [
953
+ 'content',
954
+ 'context',
955
+ 'knowledge',
956
+ 'examples',
957
+ 'modelName',
958
+ 'currentDate',
959
+ // <- TODO: list here all command names
960
+ // <- TODO: Add more like 'date', 'modelName',...
961
+ // <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
962
+ ],
963
+ });
964
+ /**
965
+ * Note: [💞] Ignore a discrepancy between file name and entity name
966
+ */
1254
967
 
1255
- tl;dr
968
+ /**
969
+ * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
970
+ *
971
+ * @public exported from `@promptbook/core`
972
+ */
973
+ var PipelineLogicError = /** @class */ (function (_super) {
974
+ __extends(PipelineLogicError, _super);
975
+ function PipelineLogicError(message) {
976
+ var _this = _super.call(this, message) || this;
977
+ _this.name = 'PipelineLogicError';
978
+ Object.setPrototypeOf(_this, PipelineLogicError.prototype);
979
+ return _this;
980
+ }
981
+ return PipelineLogicError;
982
+ }(Error));
1256
983
 
1257
- ${block(warningMessage)}
1258
- `,
1259
- ),
1260
- );
1261
- return [];
1262
- },
1263
- };
1264
- */
984
+ /**
985
+ * Tests if given string is valid semantic version
986
+ *
987
+ * Note: There are two simmilar functions:
988
+ * - `isValidSemanticVersion` which tests any semantic version
989
+ * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
990
+ *
991
+ * @public exported from `@promptbook/utils`
992
+ */
993
+ function isValidSemanticVersion(version) {
994
+ if (typeof version !== 'string') {
995
+ return false;
1265
996
  }
1266
- return new (MultipleLlmExecutionTools.bind.apply(MultipleLlmExecutionTools, __spreadArray([void 0], __read(llmExecutionTools), false)))();
997
+ if (version.startsWith('0.0.0')) {
998
+ return false;
999
+ }
1000
+ return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
1267
1001
  }
1002
+
1268
1003
  /**
1269
- * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
1004
+ * Tests if given string is valid promptbook version
1005
+ * It looks into list of known promptbook versions.
1006
+ *
1007
+ * @see https://www.npmjs.com/package/promptbook?activeTab=versions
1008
+ * 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.
1009
+ * Note: There are two simmilar functions:
1010
+ * - `isValidSemanticVersion` which tests any semantic version
1011
+ * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
1012
+ *
1013
+ * @public exported from `@promptbook/utils`
1270
1014
  */
1271
-
1272
- var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book.md",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book.md`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book.md",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book.md`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book.md"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book.md",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Title should be concise and clear\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book.md`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Title should be concise and clear\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book.md"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book.md",formfactorName:"GENERIC",parameters:[{name:"availableModelNames",description:"List of available model names separated by comma (,)",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are experienced AI engineer, you need to create virtual assistant.\nWrite\n\n## Example\n\n```json\n{\n\"modelName\": \"gpt-4o\",\n\"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n\"temperature\": 0.7\n}\n```\n\n## Instructions\n\n- Your output format is JSON object\n- Write just the JSON object, no other text should be present\n- It contains the following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nPick from the following models:\n\n- {availableModelNames}\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelRequirements",format:"JSON",dependentParameterNames:["availableModelNames","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book.md`\n- INPUT PARAMETER `{availableModelNames}` List of available model names separated by comma (,)\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are experienced AI engineer, you need to create virtual assistant.\nWrite\n\n## Example\n\n\\`\\`\\`json\n{\n\"modelName\": \"gpt-4o\",\n\"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n\"temperature\": 0.7\n}\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON object\n- Write just the JSON object, no other text should be present\n- It contains the following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nPick from the following models:\n\n- {availableModelNames}\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelRequirements}`\n"}],sourceFile:"./books/prepare-persona.book.md"}];
1015
+ function isValidPromptbookVersion(version) {
1016
+ if (!isValidSemanticVersion(version)) {
1017
+ return false;
1018
+ }
1019
+ if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
1020
+ return false;
1021
+ }
1022
+ // <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
1023
+ return true;
1024
+ }
1273
1025
 
1274
1026
  /**
1275
- * Prettify the html code
1027
+ * Checks if an URL is reserved for private networks or localhost.
1276
1028
  *
1277
- * @param content raw html code
1278
- * @returns formatted html code
1279
- * @private withing the package because of HUGE size of prettier dependency
1029
+ * Note: There are two simmilar functions:
1030
+ * - `isUrlOnPrivateNetwork` which tests full URL
1031
+ * - `isHostnameOnPrivateNetwork` *(this one)* which tests just hostname
1032
+ *
1033
+ * @public exported from `@promptbook/utils`
1280
1034
  */
1281
- function prettifyMarkdown(content) {
1282
- try {
1283
- return prettier.format(content, {
1284
- parser: 'markdown',
1285
- plugins: [parserHtml__default["default"]],
1286
- // TODO: DRY - make some import or auto-copy of .prettierrc
1287
- endOfLine: 'lf',
1288
- tabWidth: 4,
1289
- singleQuote: true,
1290
- trailingComma: 'all',
1291
- arrowParens: 'always',
1292
- printWidth: 120,
1293
- htmlWhitespaceSensitivity: 'ignore',
1294
- jsxBracketSameLine: false,
1295
- bracketSpacing: true,
1296
- });
1035
+ function isHostnameOnPrivateNetwork(hostname) {
1036
+ if (hostname === 'example.com' ||
1037
+ hostname === 'localhost' ||
1038
+ hostname.endsWith('.localhost') ||
1039
+ hostname.endsWith('.local') ||
1040
+ hostname.endsWith('.test') ||
1041
+ hostname === '127.0.0.1' ||
1042
+ hostname === '::1') {
1043
+ return true;
1297
1044
  }
1298
- catch (error) {
1299
- // TODO: [🟥] Detect browser / node and make it colorfull
1300
- console.error('There was an error with prettifying the markdown, using the original as the fallback', {
1301
- error: error,
1302
- html: content,
1303
- });
1304
- return content;
1045
+ if (hostname.includes(':')) {
1046
+ // IPv6
1047
+ var ipParts = hostname.split(':');
1048
+ return ipParts[0] === 'fc00' || ipParts[0] === 'fd00' || ipParts[0] === 'fe80';
1049
+ }
1050
+ else {
1051
+ // IPv4
1052
+ var ipParts = hostname.split('.').map(function (part) { return Number.parseInt(part, 10); });
1053
+ return (ipParts[0] === 10 ||
1054
+ (ipParts[0] === 172 && ipParts[1] >= 16 && ipParts[1] <= 31) ||
1055
+ (ipParts[0] === 192 && ipParts[1] === 168));
1305
1056
  }
1306
1057
  }
1307
1058
 
1308
1059
  /**
1309
- * Makes first letter of a string uppercase
1060
+ * Checks if an IP address or hostname is reserved for private networks or localhost.
1061
+ *
1062
+ * Note: There are two simmilar functions:
1063
+ * - `isUrlOnPrivateNetwork` *(this one)* which tests full URL
1064
+ * - `isHostnameOnPrivateNetwork` which tests just hostname
1310
1065
  *
1066
+ * @param {string} ipAddress - The IP address to check.
1067
+ * @returns {boolean} Returns true if the IP address is reserved for private networks or localhost, otherwise false.
1311
1068
  * @public exported from `@promptbook/utils`
1312
1069
  */
1313
- function capitalize(word) {
1314
- return word.substring(0, 1).toUpperCase() + word.substring(1);
1070
+ function isUrlOnPrivateNetwork(url) {
1071
+ if (typeof url === 'string') {
1072
+ url = new URL(url);
1073
+ }
1074
+ return isHostnameOnPrivateNetwork(url.hostname);
1315
1075
  }
1316
1076
 
1317
1077
  /**
1318
- * Converts promptbook in JSON format to string format
1078
+ * Tests if given string is valid URL.
1319
1079
  *
1320
- * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
1321
- * @param pipelineJson Promptbook in JSON format (.book.json)
1322
- * @returns Promptbook in string format (.book.md)
1323
- * @public exported from `@promptbook/core`
1080
+ * Note: Dataurl are considered perfectly valid.
1081
+ * Note: There are two simmilar functions:
1082
+ * - `isValidUrl` which tests any URL
1083
+ * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
1084
+ *
1085
+ * @public exported from `@promptbook/utils`
1324
1086
  */
1325
- function pipelineJsonToString(pipelineJson) {
1326
- var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
1327
- var title = pipelineJson.title, pipelineUrl = pipelineJson.pipelineUrl, bookVersion = pipelineJson.bookVersion, description = pipelineJson.description, parameters = pipelineJson.parameters, tasks = pipelineJson.tasks;
1328
- var pipelineString = "# ".concat(title);
1329
- if (description) {
1330
- pipelineString += '\n\n';
1331
- pipelineString += description;
1332
- }
1333
- var commands = [];
1334
- if (pipelineUrl) {
1335
- commands.push("PIPELINE URL ".concat(pipelineUrl));
1336
- }
1337
- if (bookVersion !== "undefined") {
1338
- commands.push("BOOK VERSION ".concat(bookVersion));
1087
+ function isValidUrl(url) {
1088
+ if (typeof url !== 'string') {
1089
+ return false;
1339
1090
  }
1340
- // TODO: [main] !!!!! This increases size of the bundle and is probbably not necessary
1341
- pipelineString = prettifyMarkdown(pipelineString);
1342
1091
  try {
1343
- for (var _g = __values(parameters.filter(function (_a) {
1344
- var isInput = _a.isInput;
1345
- return isInput;
1346
- })), _h = _g.next(); !_h.done; _h = _g.next()) {
1347
- var parameter = _h.value;
1348
- commands.push("INPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
1092
+ if (url.startsWith('blob:')) {
1093
+ url = url.replace(/^blob:/, '');
1349
1094
  }
1350
- }
1351
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
1352
- finally {
1353
- try {
1354
- if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
1095
+ var urlObject = new URL(url /* because fail is handled */);
1096
+ if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
1097
+ return false;
1355
1098
  }
1356
- finally { if (e_1) throw e_1.error; }
1099
+ return true;
1357
1100
  }
1358
- try {
1359
- for (var _j = __values(parameters.filter(function (_a) {
1360
- var isOutput = _a.isOutput;
1361
- return isOutput;
1362
- })), _k = _j.next(); !_k.done; _k = _j.next()) {
1363
- var parameter = _k.value;
1364
- commands.push("OUTPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
1365
- }
1101
+ catch (error) {
1102
+ return false;
1366
1103
  }
1367
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
1368
- finally {
1369
- try {
1370
- if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
1371
- }
1372
- finally { if (e_2) throw e_2.error; }
1373
- }
1374
- pipelineString += '\n\n';
1375
- pipelineString += commands.map(function (command) { return "- ".concat(command); }).join('\n');
1376
- try {
1377
- for (var tasks_1 = __values(tasks), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) {
1378
- var task = tasks_1_1.value;
1379
- var
1380
- /* Note: Not using:> name, */
1381
- title_1 = task.title, description_1 = task.description,
1382
- /* Note: dependentParameterNames, */
1383
- jokers = task.jokerParameterNames, taskType = task.taskType, content = task.content, postprocessing = task.postprocessingFunctionNames, expectations = task.expectations, format = task.format, resultingParameterName = task.resultingParameterName;
1384
- pipelineString += '\n\n';
1385
- pipelineString += "## ".concat(title_1);
1386
- if (description_1) {
1387
- pipelineString += '\n\n';
1388
- pipelineString += description_1;
1389
- }
1390
- var commands_1 = [];
1391
- var contentLanguage = 'text';
1392
- if (taskType === 'PROMPT_TASK') {
1393
- var modelRequirements = task.modelRequirements;
1394
- var _l = modelRequirements || {}, modelName = _l.modelName, modelVariant = _l.modelVariant;
1395
- // Note: Do nothing, it is default
1396
- // commands.push(`PROMPT`);
1397
- if (modelVariant) {
1398
- commands_1.push("MODEL VARIANT ".concat(capitalize(modelVariant)));
1399
- }
1400
- if (modelName) {
1401
- commands_1.push("MODEL NAME `".concat(modelName, "`"));
1402
- }
1403
- }
1404
- else if (taskType === 'SIMPLE_TASK') {
1405
- commands_1.push("SIMPLE TEMPLATE");
1406
- // Note: Nothing special here
1407
- }
1408
- else if (taskType === 'SCRIPT_TASK') {
1409
- commands_1.push("SCRIPT");
1410
- if (task.contentLanguage) {
1411
- contentLanguage = task.contentLanguage;
1412
- }
1413
- else {
1414
- contentLanguage = '';
1415
- }
1416
- }
1417
- else if (taskType === 'DIALOG_TASK') {
1418
- commands_1.push("DIALOG");
1419
- // Note: Nothing special here
1420
- } // <- }else if([🅱]
1421
- if (jokers) {
1422
- try {
1423
- for (var jokers_1 = (e_4 = void 0, __values(jokers)), jokers_1_1 = jokers_1.next(); !jokers_1_1.done; jokers_1_1 = jokers_1.next()) {
1424
- var joker = jokers_1_1.value;
1425
- commands_1.push("JOKER {".concat(joker, "}"));
1426
- }
1427
- }
1428
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
1429
- finally {
1430
- try {
1431
- if (jokers_1_1 && !jokers_1_1.done && (_d = jokers_1.return)) _d.call(jokers_1);
1432
- }
1433
- finally { if (e_4) throw e_4.error; }
1434
- }
1435
- } /* not else */
1436
- if (postprocessing) {
1437
- try {
1438
- for (var postprocessing_1 = (e_5 = void 0, __values(postprocessing)), postprocessing_1_1 = postprocessing_1.next(); !postprocessing_1_1.done; postprocessing_1_1 = postprocessing_1.next()) {
1439
- var postprocessingFunctionName = postprocessing_1_1.value;
1440
- commands_1.push("POSTPROCESSING `".concat(postprocessingFunctionName, "`"));
1441
- }
1442
- }
1443
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
1444
- finally {
1445
- try {
1446
- if (postprocessing_1_1 && !postprocessing_1_1.done && (_e = postprocessing_1.return)) _e.call(postprocessing_1);
1447
- }
1448
- finally { if (e_5) throw e_5.error; }
1449
- }
1450
- } /* not else */
1451
- if (expectations) {
1452
- try {
1453
- for (var _m = (e_6 = void 0, __values(Object.entries(expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
1454
- var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
1455
- if (min === max) {
1456
- commands_1.push("EXPECT EXACTLY ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
1457
- }
1458
- else {
1459
- if (min !== undefined) {
1460
- commands_1.push("EXPECT MIN ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
1461
- } /* not else */
1462
- if (max !== undefined) {
1463
- commands_1.push("EXPECT MAX ".concat(max, " ").concat(capitalize(unit + (max > 1 ? 's' : ''))));
1464
- }
1465
- }
1466
- }
1467
- }
1468
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
1469
- finally {
1470
- try {
1471
- if (_o && !_o.done && (_f = _m.return)) _f.call(_m);
1472
- }
1473
- finally { if (e_6) throw e_6.error; }
1474
- }
1475
- } /* not else */
1476
- if (format) {
1477
- if (format === 'JSON') {
1478
- // TODO: @deprecated remove
1479
- commands_1.push("FORMAT JSON");
1480
- }
1481
- } /* not else */
1482
- pipelineString += '\n\n';
1483
- pipelineString += commands_1.map(function (command) { return "- ".concat(command); }).join('\n');
1484
- pipelineString += '\n\n';
1485
- pipelineString += '```' + contentLanguage;
1486
- pipelineString += '\n';
1487
- pipelineString += spaceTrim__default["default"](content);
1488
- // <- TODO: [main] !!! Escape
1489
- // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
1490
- pipelineString += '\n';
1491
- pipelineString += '```';
1492
- pipelineString += '\n\n';
1493
- pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!! If the parameter here has description, add it and use taskParameterJsonToString
1494
- }
1495
- }
1496
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
1497
- finally {
1498
- try {
1499
- if (tasks_1_1 && !tasks_1_1.done && (_c = tasks_1.return)) _c.call(tasks_1);
1500
- }
1501
- finally { if (e_3) throw e_3.error; }
1502
- }
1503
- return pipelineString;
1504
- }
1505
- /**
1506
- * @private internal utility of `pipelineJsonToString`
1507
- */
1508
- function taskParameterJsonToString(taskParameterJson) {
1509
- var name = taskParameterJson.name, description = taskParameterJson.description;
1510
- var parameterString = "{".concat(name, "}");
1511
- if (description) {
1512
- parameterString = "".concat(parameterString, " ").concat(description);
1513
- }
1514
- return parameterString;
1515
1104
  }
1516
- /**
1517
- * TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
1518
- * TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
1519
- * TODO: [🏛] Maybe make some markdown builder
1520
- * TODO: [🏛] Escape all
1521
- * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
1522
- */
1523
-
1524
- /**
1525
- * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
1526
- *
1527
- * @public exported from `@promptbook/core`
1528
- */
1529
- var ParseError = /** @class */ (function (_super) {
1530
- __extends(ParseError, _super);
1531
- function ParseError(message) {
1532
- var _this = _super.call(this, message) || this;
1533
- _this.name = 'ParseError';
1534
- Object.setPrototypeOf(_this, ParseError.prototype);
1535
- return _this;
1536
- }
1537
- return ParseError;
1538
- }(Error));
1539
- /**
1540
- * TODO: Maybe split `ParseError` and `ApplyError`
1541
- */
1542
-
1543
- /**
1544
- * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
1545
- *
1546
- * @public exported from `@promptbook/core`
1547
- */
1548
- var PipelineLogicError = /** @class */ (function (_super) {
1549
- __extends(PipelineLogicError, _super);
1550
- function PipelineLogicError(message) {
1551
- var _this = _super.call(this, message) || this;
1552
- _this.name = 'PipelineLogicError';
1553
- Object.setPrototypeOf(_this, PipelineLogicError.prototype);
1554
- return _this;
1555
- }
1556
- return PipelineLogicError;
1557
- }(Error));
1558
1105
 
1559
1106
  /**
1560
- * Tests if given string is valid semantic version
1107
+ * Tests if given string is valid pipeline URL URL.
1561
1108
  *
1562
1109
  * Note: There are two simmilar functions:
1563
- * - `isValidSemanticVersion` which tests any semantic version
1564
- * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
1110
+ * - `isValidUrl` which tests any URL
1111
+ * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
1565
1112
  *
1566
1113
  * @public exported from `@promptbook/utils`
1567
1114
  */
1568
- function isValidSemanticVersion(version) {
1569
- if (typeof version !== 'string') {
1115
+ function isValidPipelineUrl(url) {
1116
+ if (!isValidUrl(url)) {
1570
1117
  return false;
1571
1118
  }
1572
- if (version.startsWith('0.0.0')) {
1119
+ if (!url.startsWith('https://')) {
1573
1120
  return false;
1574
1121
  }
1575
- return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
1576
- }
1577
-
1578
- /**
1579
- * Tests if given string is valid promptbook version
1580
- * It looks into list of known promptbook versions.
1581
- *
1582
- * @see https://www.npmjs.com/package/promptbook?activeTab=versions
1583
- * 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.
1584
- * Note: There are two simmilar functions:
1585
- * - `isValidSemanticVersion` which tests any semantic version
1586
- * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
1587
- *
1588
- * @public exported from `@promptbook/utils`
1589
- */
1590
- function isValidPromptbookVersion(version) {
1591
- if (!isValidSemanticVersion(version)) {
1122
+ if (url.includes('#')) {
1123
+ // TODO: [🐠]
1592
1124
  return false;
1593
1125
  }
1594
- if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
1126
+ if (isUrlOnPrivateNetwork(url)) {
1595
1127
  return false;
1596
1128
  }
1597
- // <- TODO: [main] !!! Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
1598
1129
  return true;
1599
1130
  }
1131
+ /**
1132
+ * TODO: [🐠] Maybe more info why the URL is invalid
1133
+ */
1600
1134
 
1601
1135
  /**
1602
- * Checks if an URL is reserved for private networks or localhost.
1136
+ * Validates PipelineJson if it is logically valid
1603
1137
  *
1604
- * Note: There are two simmilar functions:
1605
- * - `isUrlOnPrivateNetwork` which tests full URL
1606
- * - `isHostnameOnPrivateNetwork` *(this one)* which tests just hostname
1138
+ * It checks:
1139
+ * - if it has correct parameters dependency
1607
1140
  *
1608
- * @public exported from `@promptbook/utils`
1141
+ * It does NOT check:
1142
+ * - if it is valid json
1143
+ * - if it is meaningful
1144
+ *
1145
+ * @param pipeline valid or invalid PipelineJson
1146
+ * @returns the same pipeline if it is logically valid
1147
+ * @throws {PipelineLogicError} on logical error in the pipeline
1148
+ * @public exported from `@promptbook/core`
1609
1149
  */
1610
- function isHostnameOnPrivateNetwork(hostname) {
1611
- if (hostname === 'example.com' ||
1612
- hostname === 'localhost' ||
1613
- hostname.endsWith('.localhost') ||
1614
- hostname.endsWith('.local') ||
1615
- hostname.endsWith('.test') ||
1616
- hostname === '127.0.0.1' ||
1617
- hostname === '::1') {
1618
- return true;
1619
- }
1620
- if (hostname.includes(':')) {
1621
- // IPv6
1622
- var ipParts = hostname.split(':');
1623
- return ipParts[0] === 'fc00' || ipParts[0] === 'fd00' || ipParts[0] === 'fe80';
1624
- }
1625
- else {
1626
- // IPv4
1627
- var ipParts = hostname.split('.').map(function (part) { return Number.parseInt(part, 10); });
1628
- return (ipParts[0] === 10 ||
1629
- (ipParts[0] === 172 && ipParts[1] >= 16 && ipParts[1] <= 31) ||
1630
- (ipParts[0] === 192 && ipParts[1] === 168));
1631
- }
1632
- }
1633
-
1634
- /**
1635
- * Checks if an IP address or hostname is reserved for private networks or localhost.
1636
- *
1637
- * Note: There are two simmilar functions:
1638
- * - `isUrlOnPrivateNetwork` *(this one)* which tests full URL
1639
- * - `isHostnameOnPrivateNetwork` which tests just hostname
1640
- *
1641
- * @param {string} ipAddress - The IP address to check.
1642
- * @returns {boolean} Returns true if the IP address is reserved for private networks or localhost, otherwise false.
1643
- * @public exported from `@promptbook/utils`
1644
- */
1645
- function isUrlOnPrivateNetwork(url) {
1646
- if (typeof url === 'string') {
1647
- url = new URL(url);
1648
- }
1649
- return isHostnameOnPrivateNetwork(url.hostname);
1650
- }
1651
-
1652
- /**
1653
- * Tests if given string is valid URL.
1654
- *
1655
- * Note: Dataurl are considered perfectly valid.
1656
- * Note: There are two simmilar functions:
1657
- * - `isValidUrl` which tests any URL
1658
- * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
1659
- *
1660
- * @public exported from `@promptbook/utils`
1661
- */
1662
- function isValidUrl(url) {
1663
- if (typeof url !== 'string') {
1664
- return false;
1665
- }
1666
- try {
1667
- if (url.startsWith('blob:')) {
1668
- url = url.replace(/^blob:/, '');
1669
- }
1670
- var urlObject = new URL(url /* because fail is handled */);
1671
- if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
1672
- return false;
1673
- }
1674
- return true;
1675
- }
1676
- catch (error) {
1677
- return false;
1678
- }
1679
- }
1680
-
1681
- /**
1682
- * Tests if given string is valid pipeline URL URL.
1683
- *
1684
- * Note: There are two simmilar functions:
1685
- * - `isValidUrl` which tests any URL
1686
- * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
1687
- *
1688
- * @public exported from `@promptbook/utils`
1689
- */
1690
- function isValidPipelineUrl(url) {
1691
- if (!isValidUrl(url)) {
1692
- return false;
1693
- }
1694
- if (!url.startsWith('https://')) {
1695
- return false;
1696
- }
1697
- if (!(url.endsWith('.book.md') || url.endsWith('.book') || url.endsWith('.book.md') || url.endsWith('.ptbk'))) {
1698
- return false;
1699
- }
1700
- if (url.includes('#')) {
1701
- // TODO: [🐠]
1702
- return false;
1703
- }
1704
- if (isUrlOnPrivateNetwork(url)) {
1705
- return false;
1706
- }
1707
- return true;
1708
- }
1709
- /**
1710
- * TODO: [🐠] Maybe more info why the URL is invalid
1711
- */
1712
-
1713
- /**
1714
- * Validates PipelineJson if it is logically valid
1715
- *
1716
- * It checks:
1717
- * - if it has correct parameters dependency
1718
- *
1719
- * It does NOT check:
1720
- * - if it is valid json
1721
- * - if it is meaningful
1722
- *
1723
- * @param pipeline valid or invalid PipelineJson
1724
- * @returns the same pipeline if it is logically valid
1725
- * @throws {PipelineLogicError} on logical error in the pipeline
1726
- * @public exported from `@promptbook/core`
1727
- */
1728
- function validatePipeline(pipeline) {
1729
- if (IS_PIPELINE_LOGIC_VALIDATED) {
1730
- validatePipelineCore(pipeline);
1150
+ function validatePipeline(pipeline) {
1151
+ if (IS_PIPELINE_LOGIC_VALIDATED) {
1152
+ validatePipeline_InnerFunction(pipeline);
1731
1153
  }
1732
1154
  else {
1733
1155
  try {
1734
- validatePipelineCore(pipeline);
1156
+ validatePipeline_InnerFunction(pipeline);
1735
1157
  }
1736
1158
  catch (error) {
1737
1159
  if (!(error instanceof PipelineLogicError)) {
@@ -1745,7 +1167,7 @@
1745
1167
  /**
1746
1168
  * @private internal function for `validatePipeline`
1747
1169
  */
1748
- function validatePipelineCore(pipeline) {
1170
+ function validatePipeline_InnerFunction(pipeline) {
1749
1171
  // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
1750
1172
  var e_1, _a, e_2, _b, e_3, _c;
1751
1173
  var pipelineIdentification = (function () {
@@ -1969,11 +1391,11 @@
1969
1391
  _loop_3();
1970
1392
  }
1971
1393
  // Note: Check that formfactor is corresponding to the pipeline interface
1972
- // TODO: !!!!!! Implement this
1394
+ // TODO: !!6 Implement this
1973
1395
  // pipeline.formfactorName
1974
1396
  }
1975
1397
  /**
1976
- * TODO: !! [🧞‍♀️] Do not allow joker + foreach
1398
+ * TODO: [🧞‍♀️] Do not allow joker + foreach
1977
1399
  * TODO: [🧠] Work with promptbookVersion
1978
1400
  * TODO: Use here some json-schema, Zod or something similar and change it to:
1979
1401
  * > /**
@@ -1985,11 +1407,11 @@
1985
1407
  * > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
1986
1408
  */
1987
1409
  /**
1988
- * TODO: [🧳][main] !!!! Validate that all examples 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
1410
+ * TODO: [🧳][main] !!4 Validate that all examples match expectations
1411
+ * TODO: [🧳][🐝][main] !!4 Validate that knowledge is valid (non-void)
1412
+ * TODO: [🧳][main] !!4 Validate that persona can be used only with CHAT variant
1413
+ * TODO: [🧳][main] !!4 Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
1414
+ * TODO: [🧳][main] !!4 Validate that reserved parameter is not used as joker
1993
1415
  * TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
1994
1416
  * TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
1995
1417
  */
@@ -2125,7 +1547,7 @@
2125
1547
  pipelineJsonToString(unpreparePipeline(pipeline)) !==
2126
1548
  pipelineJsonToString(unpreparePipeline(this.collection.get(pipeline.pipelineUrl)))) {
2127
1549
  var existing = this.collection.get(pipeline.pipelineUrl);
2128
- 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 ")));
1550
+ 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 ")));
2129
1551
  }
2130
1552
  // Note: [🧠] Overwrite existing pipeline with the same URL
2131
1553
  this.collection.set(pipeline.pipelineUrl, pipeline);
@@ -2190,6 +1612,38 @@
2190
1612
  return new (SimplePipelineCollection.bind.apply(SimplePipelineCollection, __spreadArray([void 0], __read(promptbooks), false)))();
2191
1613
  }
2192
1614
 
1615
+ /**
1616
+ * This error type indicates that some tools are missing for pipeline execution or preparation
1617
+ *
1618
+ * @public exported from `@promptbook/core`
1619
+ */
1620
+ var MissingToolsError = /** @class */ (function (_super) {
1621
+ __extends(MissingToolsError, _super);
1622
+ function MissingToolsError(message) {
1623
+ 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;
1624
+ _this.name = 'MissingToolsError';
1625
+ Object.setPrototypeOf(_this, MissingToolsError.prototype);
1626
+ return _this;
1627
+ }
1628
+ return MissingToolsError;
1629
+ }(Error));
1630
+
1631
+ /**
1632
+ * This error indicates errors during the execution of the pipeline
1633
+ *
1634
+ * @public exported from `@promptbook/core`
1635
+ */
1636
+ var PipelineExecutionError = /** @class */ (function (_super) {
1637
+ __extends(PipelineExecutionError, _super);
1638
+ function PipelineExecutionError(message) {
1639
+ var _this = _super.call(this, message) || this;
1640
+ _this.name = 'PipelineExecutionError';
1641
+ Object.setPrototypeOf(_this, PipelineExecutionError.prototype);
1642
+ return _this;
1643
+ }
1644
+ return PipelineExecutionError;
1645
+ }(Error));
1646
+
2193
1647
  /**
2194
1648
  * This error indicates problems parsing the format value
2195
1649
  *
@@ -2429,11 +1883,16 @@
2429
1883
  /**
2430
1884
  * Determine if the pipeline is fully prepared
2431
1885
  *
1886
+ * @see https://github.com/webgptorg/promptbook/discussions/196
1887
+ *
2432
1888
  * @public exported from `@promptbook/core`
2433
1889
  */
2434
1890
  function isPipelinePrepared(pipeline) {
2435
1891
  // Note: Ignoring `pipeline.preparations` @@@
2436
1892
  // Note: Ignoring `pipeline.knowledgePieces` @@@
1893
+ if (pipeline.title === undefined || pipeline.title === '' || pipeline.title === DEFAULT_BOOK_TITLE) {
1894
+ return false;
1895
+ }
2437
1896
  if (!pipeline.personas.every(function (persona) { return persona.modelRequirements !== undefined; })) {
2438
1897
  return false;
2439
1898
  }
@@ -2449,7 +1908,7 @@
2449
1908
  return true;
2450
1909
  }
2451
1910
  /**
2452
- * TODO: [🔃][main] !! If the pipeline was prepared with different version or different set of models, prepare it once again
1911
+ * TODO: [🔃][main] If the pipeline was prepared with different version or different set of models, prepare it once again
2453
1912
  * TODO: [🐠] Maybe base this on `makeValidator`
2454
1913
  * TODO: [🧊] Pipeline can be partially prepared, this should return true ONLY if fully prepared
2455
1914
  * TODO: [🧿] Maybe do same process with same granularity and subfinctions as `preparePipeline`
@@ -2551,76 +2010,204 @@
2551
2010
  }
2552
2011
 
2553
2012
  /**
2554
- * Parses the given script and returns the list of all used variables that are not defined in the script
2013
+ * Represents the usage with no resources consumed
2555
2014
  *
2556
- * @param script from which to extract the variables
2557
- * @returns the list of variable names
2558
- * @throws {ParseError} if the script is invalid
2559
- * @public exported from `@promptbook/utils` <- Note: [👖] This is usable elsewhere than in Promptbook, so keeping in utils
2015
+ * @public exported from `@promptbook/core`
2560
2016
  */
2561
- function extractVariablesFromScript(script) {
2562
- var variables = new Set();
2563
- var originalScript = script;
2564
- script = "(()=>{".concat(script, "})()");
2565
- try {
2566
- for (var i = 0; i < 100 /* <- TODO: This limit to configuration */; i++)
2567
- try {
2568
- eval(script);
2569
- }
2570
- catch (error) {
2571
- if (!(error instanceof ReferenceError)) {
2572
- throw error;
2573
- }
2574
- /*
2575
- Note: Parsing the error
2576
- 🌟 Most devices:
2577
- [PipelineUrlError: thing is not defined]
2578
-
2579
- 🍏 iPhone`s Safari:
2580
- [PipelineUrlError: Can't find variable: thing]
2581
- */
2582
- var variableName = undefined;
2583
- if (error.message.startsWith("Can't")) {
2584
- // 🍏 Case
2585
- variableName = error.message.split(' ').pop();
2586
- }
2587
- else {
2588
- // 🌟 Case
2589
- variableName = error.message.split(' ').shift();
2590
- }
2591
- if (variableName === undefined) {
2592
- throw error;
2593
- }
2594
- if (script.includes(variableName + '(')) {
2595
- script = "const ".concat(variableName, " = ()=>'';") + script;
2596
- }
2597
- else {
2598
- variables.add(variableName);
2599
- script = "const ".concat(variableName, " = '';") + script;
2600
- }
2601
- }
2602
- }
2603
- catch (error) {
2604
- if (!(error instanceof Error)) {
2605
- throw error;
2606
- }
2607
- throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Can not extract variables from the script\n\n ".concat(block(error.toString()), "}\n\n\n Found variables:\n\n ").concat(Array.from(variables)
2608
- .map(function (variableName, i) { return "".concat(i + 1, ") ").concat(variableName); })
2609
- .join('\n'), "\n\n\n The script:\n\n ```javascript\n ").concat(block(originalScript), "\n ```\n "); }));
2610
- }
2611
- return variables;
2612
- }
2017
+ var ZERO_USAGE = $deepFreeze({
2018
+ price: { value: 0 },
2019
+ input: {
2020
+ tokensCount: { value: 0 },
2021
+ charactersCount: { value: 0 },
2022
+ wordsCount: { value: 0 },
2023
+ sentencesCount: { value: 0 },
2024
+ linesCount: { value: 0 },
2025
+ paragraphsCount: { value: 0 },
2026
+ pagesCount: { value: 0 },
2027
+ },
2028
+ output: {
2029
+ tokensCount: { value: 0 },
2030
+ charactersCount: { value: 0 },
2031
+ wordsCount: { value: 0 },
2032
+ sentencesCount: { value: 0 },
2033
+ linesCount: { value: 0 },
2034
+ paragraphsCount: { value: 0 },
2035
+ pagesCount: { value: 0 },
2036
+ },
2037
+ });
2613
2038
  /**
2614
- * TODO: [🔣] Support for multiple languages - python, java,...
2039
+ * Represents the usage with unknown resources consumed
2040
+ *
2041
+ * @public exported from `@promptbook/core`
2042
+ */
2043
+ $deepFreeze({
2044
+ price: { value: 0, isUncertain: true },
2045
+ input: {
2046
+ tokensCount: { value: 0, isUncertain: true },
2047
+ charactersCount: { value: 0, isUncertain: true },
2048
+ wordsCount: { value: 0, isUncertain: true },
2049
+ sentencesCount: { value: 0, isUncertain: true },
2050
+ linesCount: { value: 0, isUncertain: true },
2051
+ paragraphsCount: { value: 0, isUncertain: true },
2052
+ pagesCount: { value: 0, isUncertain: true },
2053
+ },
2054
+ output: {
2055
+ tokensCount: { value: 0, isUncertain: true },
2056
+ charactersCount: { value: 0, isUncertain: true },
2057
+ wordsCount: { value: 0, isUncertain: true },
2058
+ sentencesCount: { value: 0, isUncertain: true },
2059
+ linesCount: { value: 0, isUncertain: true },
2060
+ paragraphsCount: { value: 0, isUncertain: true },
2061
+ pagesCount: { value: 0, isUncertain: true },
2062
+ },
2063
+ });
2064
+ /**
2065
+ * Note: [💞] Ignore a discrepancy between file name and entity name
2615
2066
  */
2616
2067
 
2617
2068
  /**
2618
- * Parses the task and returns the set of all used parameters
2069
+ * Function `addUsage` will add multiple usages into one
2619
2070
  *
2620
- * @param task the task with used parameters
2621
- * @returns the set of parameter names
2622
- * @throws {ParseError} if the script is invalid
2623
- * @public exported from `@promptbook/core` <- Note: [👖] This utility is so tightly interconnected with the Promptbook that it is not exported as util but in core
2071
+ * Note: If you provide 0 values, it returns ZERO_USAGE
2072
+ *
2073
+ * @public exported from `@promptbook/core`
2074
+ */
2075
+ function addUsage() {
2076
+ var usageItems = [];
2077
+ for (var _i = 0; _i < arguments.length; _i++) {
2078
+ usageItems[_i] = arguments[_i];
2079
+ }
2080
+ return usageItems.reduce(function (acc, item) {
2081
+ var e_1, _a, e_2, _b;
2082
+ var _c;
2083
+ acc.price.value += ((_c = item.price) === null || _c === void 0 ? void 0 : _c.value) || 0;
2084
+ try {
2085
+ for (var _d = __values(Object.keys(acc.input)), _e = _d.next(); !_e.done; _e = _d.next()) {
2086
+ var key = _e.value;
2087
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2088
+ //@ts-ignore
2089
+ if (item.input[key]) {
2090
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2091
+ //@ts-ignore
2092
+ acc.input[key].value += item.input[key].value || 0;
2093
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2094
+ //@ts-ignore
2095
+ if (item.input[key].isUncertain) {
2096
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2097
+ //@ts-ignore
2098
+ acc.input[key].isUncertain = true;
2099
+ }
2100
+ }
2101
+ }
2102
+ }
2103
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
2104
+ finally {
2105
+ try {
2106
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
2107
+ }
2108
+ finally { if (e_1) throw e_1.error; }
2109
+ }
2110
+ try {
2111
+ for (var _f = __values(Object.keys(acc.output)), _g = _f.next(); !_g.done; _g = _f.next()) {
2112
+ var key = _g.value;
2113
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2114
+ //@ts-ignore
2115
+ if (item.output[key]) {
2116
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2117
+ //@ts-ignore
2118
+ acc.output[key].value += item.output[key].value || 0;
2119
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2120
+ //@ts-ignore
2121
+ if (item.output[key].isUncertain) {
2122
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2123
+ //@ts-ignore
2124
+ acc.output[key].isUncertain = true;
2125
+ }
2126
+ }
2127
+ }
2128
+ }
2129
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
2130
+ finally {
2131
+ try {
2132
+ if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
2133
+ }
2134
+ finally { if (e_2) throw e_2.error; }
2135
+ }
2136
+ return acc;
2137
+ }, deepClone(ZERO_USAGE));
2138
+ }
2139
+
2140
+ /**
2141
+ * Parses the given script and returns the list of all used variables that are not defined in the script
2142
+ *
2143
+ * @param script from which to extract the variables
2144
+ * @returns the list of variable names
2145
+ * @throws {ParseError} if the script is invalid
2146
+ * @public exported from `@promptbook/utils` <- Note: [👖] This is usable elsewhere than in Promptbook, so keeping in utils
2147
+ */
2148
+ function extractVariablesFromScript(script) {
2149
+ var variables = new Set();
2150
+ var originalScript = script;
2151
+ script = "(()=>{".concat(script, "})()");
2152
+ try {
2153
+ for (var i = 0; i < 100 /* <- TODO: This limit to configuration */; i++)
2154
+ try {
2155
+ eval(script);
2156
+ }
2157
+ catch (error) {
2158
+ if (!(error instanceof ReferenceError)) {
2159
+ throw error;
2160
+ }
2161
+ /*
2162
+ Note: Parsing the error
2163
+ 🌟 Most devices:
2164
+ [PipelineUrlError: thing is not defined]
2165
+
2166
+ 🍏 iPhone`s Safari:
2167
+ [PipelineUrlError: Can't find variable: thing]
2168
+ */
2169
+ var variableName = undefined;
2170
+ if (error.message.startsWith("Can't")) {
2171
+ // 🍏 Case
2172
+ variableName = error.message.split(' ').pop();
2173
+ }
2174
+ else {
2175
+ // 🌟 Case
2176
+ variableName = error.message.split(' ').shift();
2177
+ }
2178
+ if (variableName === undefined) {
2179
+ throw error;
2180
+ }
2181
+ if (script.includes(variableName + '(')) {
2182
+ script = "const ".concat(variableName, " = ()=>'';") + script;
2183
+ }
2184
+ else {
2185
+ variables.add(variableName);
2186
+ script = "const ".concat(variableName, " = '';") + script;
2187
+ }
2188
+ }
2189
+ }
2190
+ catch (error) {
2191
+ if (!(error instanceof Error)) {
2192
+ throw error;
2193
+ }
2194
+ throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Can not extract variables from the script\n ".concat(block(error.stack || error.message), "\n\n Found variables:\n ").concat(Array.from(variables)
2195
+ .map(function (variableName, i) { return "".concat(i + 1, ") ").concat(variableName); })
2196
+ .join('\n'), "\n\n\n The script:\n\n ```javascript\n ").concat(block(originalScript), "\n ```\n "); }));
2197
+ }
2198
+ return variables;
2199
+ }
2200
+ /**
2201
+ * TODO: [🔣] Support for multiple languages - python, java,...
2202
+ */
2203
+
2204
+ /**
2205
+ * Parses the task and returns the set of all used parameters
2206
+ *
2207
+ * @param task the task with used parameters
2208
+ * @returns the set of parameter names
2209
+ * @throws {ParseError} if the script is invalid
2210
+ * @public exported from `@promptbook/core` <- Note: [👖] This utility is so tightly interconnected with the Promptbook that it is not exported as util but in core
2624
2211
  */
2625
2212
  function extractParameterNamesFromTask(task) {
2626
2213
  var e_1, _a, e_2, _b, e_3, _c, e_4, _d;
@@ -2870,242 +2457,513 @@
2870
2457
  mappedData = _a.sent();
2871
2458
  return [2 /*return*/, papaparse.unparse(mappedData, __assign(__assign({}, settings), MANDATORY_CSV_SETTINGS))];
2872
2459
  }
2873
- });
2874
- });
2875
- },
2876
- },
2877
- ],
2878
- };
2879
- /**
2880
- * TODO: [🍓] In `CsvFormatDefinition` implement simple `isValid`
2881
- * TODO: [🍓] In `CsvFormatDefinition` implement partial `canBeValid`
2882
- * TODO: [🍓] In `CsvFormatDefinition` implement `heal
2883
- * TODO: [🍓] In `CsvFormatDefinition` implement `subvalueDefinitions`
2884
- * TODO: [🏢] Allow to expect something inside CSV objects and other formats
2885
- */
2886
-
2887
- /**
2888
- * Function isValidJsonString will tell you if the string is valid JSON or not
2889
- *
2890
- * @public exported from `@promptbook/utils`
2891
- */
2892
- function isValidJsonString(value /* <- [👨‍⚖️] */) {
2893
- try {
2894
- JSON.parse(value);
2895
- return true;
2896
- }
2897
- catch (error) {
2898
- if (!(error instanceof Error)) {
2899
- throw error;
2900
- }
2901
- if (error.message.includes('Unexpected token')) {
2902
- return false;
2903
- }
2904
- return false;
2905
- }
2906
- }
2907
-
2908
- /**
2909
- * Definition for JSON format
2910
- *
2911
- * @private still in development [🏢]
2912
- */
2913
- var JsonFormatDefinition = {
2914
- formatName: 'JSON',
2915
- mimeType: 'application/json',
2916
- isValid: function (value, settings, schema) {
2917
- return isValidJsonString(value);
2918
- },
2919
- canBeValid: function (partialValue, settings, schema) {
2920
- return true;
2921
- },
2922
- heal: function (value, settings, schema) {
2923
- throw new Error('Not implemented');
2924
- },
2925
- subvalueDefinitions: [],
2926
- };
2927
- /**
2928
- * TODO: [🧠] Maybe propper instance of object
2929
- * TODO: [0] Make string_serialized_json
2930
- * TODO: [1] Make type for JSON Settings and Schema
2931
- * TODO: [🧠] What to use for validating JSONs - JSON Schema, ZoD, typescript types/interfaces,...?
2932
- * TODO: [🍓] In `JsonFormatDefinition` implement simple `isValid`
2933
- * TODO: [🍓] In `JsonFormatDefinition` implement partial `canBeValid`
2934
- * TODO: [🍓] In `JsonFormatDefinition` implement `heal
2935
- * TODO: [🍓] In `JsonFormatDefinition` implement `subvalueDefinitions`
2936
- * TODO: [🏢] Allow to expect something inside JSON objects and other formats
2937
- */
2938
-
2939
- /**
2940
- * Definition for any text - this will be always valid
2941
- *
2942
- * Note: This is not useful for validation, but for splitting and mapping with `subvalueDefinitions`
2943
- *
2944
- * @public exported from `@promptbook/core`
2945
- */
2946
- var TextFormatDefinition = {
2947
- formatName: 'TEXT',
2948
- isValid: function (value) {
2949
- return typeof value === 'string';
2950
- },
2951
- canBeValid: function (partialValue) {
2952
- return typeof partialValue === 'string';
2953
- },
2954
- heal: function () {
2955
- throw new UnexpectedError('It does not make sense to call `TextFormatDefinition.heal`');
2956
- },
2957
- subvalueDefinitions: [
2958
- {
2959
- subvalueName: 'LINE',
2960
- mapValues: function (value, outputParameterName, settings, mapCallback) {
2961
- return __awaiter(this, void 0, void 0, function () {
2962
- var lines, mappedLines;
2963
- return __generator(this, function (_a) {
2964
- switch (_a.label) {
2965
- case 0:
2966
- lines = value.split('\n');
2967
- return [4 /*yield*/, Promise.all(lines.map(function (lineContent, lineNumber) {
2968
- // TODO: [🧠] Maybe option to skip empty line
2969
- /* not await */ return mapCallback({
2970
- lineContent: lineContent,
2971
- // TODO: [🧠] Maybe also put here `lineNumber`
2972
- }, lineNumber);
2973
- }))];
2974
- case 1:
2975
- mappedLines = _a.sent();
2976
- return [2 /*return*/, mappedLines.join('\n')];
2460
+ });
2461
+ });
2462
+ },
2463
+ },
2464
+ ],
2465
+ };
2466
+ /**
2467
+ * TODO: [🍓] In `CsvFormatDefinition` implement simple `isValid`
2468
+ * TODO: [🍓] In `CsvFormatDefinition` implement partial `canBeValid`
2469
+ * TODO: [🍓] In `CsvFormatDefinition` implement `heal
2470
+ * TODO: [🍓] In `CsvFormatDefinition` implement `subvalueDefinitions`
2471
+ * TODO: [🏢] Allow to expect something inside CSV objects and other formats
2472
+ */
2473
+
2474
+ /**
2475
+ * Definition for JSON format
2476
+ *
2477
+ * @private still in development [🏢]
2478
+ */
2479
+ var JsonFormatDefinition = {
2480
+ formatName: 'JSON',
2481
+ mimeType: 'application/json',
2482
+ isValid: function (value, settings, schema) {
2483
+ return isValidJsonString(value);
2484
+ },
2485
+ canBeValid: function (partialValue, settings, schema) {
2486
+ return true;
2487
+ },
2488
+ heal: function (value, settings, schema) {
2489
+ throw new Error('Not implemented');
2490
+ },
2491
+ subvalueDefinitions: [],
2492
+ };
2493
+ /**
2494
+ * TODO: [🧠] Maybe propper instance of object
2495
+ * TODO: [0] Make string_serialized_json
2496
+ * TODO: [1] Make type for JSON Settings and Schema
2497
+ * TODO: [🧠] What to use for validating JSONs - JSON Schema, ZoD, typescript types/interfaces,...?
2498
+ * TODO: [🍓] In `JsonFormatDefinition` implement simple `isValid`
2499
+ * TODO: [🍓] In `JsonFormatDefinition` implement partial `canBeValid`
2500
+ * TODO: [🍓] In `JsonFormatDefinition` implement `heal
2501
+ * TODO: [🍓] In `JsonFormatDefinition` implement `subvalueDefinitions`
2502
+ * TODO: [🏢] Allow to expect something inside JSON objects and other formats
2503
+ */
2504
+
2505
+ /**
2506
+ * Definition for any text - this will be always valid
2507
+ *
2508
+ * Note: This is not useful for validation, but for splitting and mapping with `subvalueDefinitions`
2509
+ *
2510
+ * @public exported from `@promptbook/core`
2511
+ */
2512
+ var TextFormatDefinition = {
2513
+ formatName: 'TEXT',
2514
+ isValid: function (value) {
2515
+ return typeof value === 'string';
2516
+ },
2517
+ canBeValid: function (partialValue) {
2518
+ return typeof partialValue === 'string';
2519
+ },
2520
+ heal: function () {
2521
+ throw new UnexpectedError('It does not make sense to call `TextFormatDefinition.heal`');
2522
+ },
2523
+ subvalueDefinitions: [
2524
+ {
2525
+ subvalueName: 'LINE',
2526
+ mapValues: function (value, outputParameterName, settings, mapCallback) {
2527
+ return __awaiter(this, void 0, void 0, function () {
2528
+ var lines, mappedLines;
2529
+ return __generator(this, function (_a) {
2530
+ switch (_a.label) {
2531
+ case 0:
2532
+ lines = value.split('\n');
2533
+ return [4 /*yield*/, Promise.all(lines.map(function (lineContent, lineNumber) {
2534
+ // TODO: [🧠] Maybe option to skip empty line
2535
+ /* not await */ return mapCallback({
2536
+ lineContent: lineContent,
2537
+ // TODO: [🧠] Maybe also put here `lineNumber`
2538
+ }, lineNumber);
2539
+ }))];
2540
+ case 1:
2541
+ mappedLines = _a.sent();
2542
+ return [2 /*return*/, mappedLines.join('\n')];
2543
+ }
2544
+ });
2545
+ });
2546
+ },
2547
+ },
2548
+ // <- TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2549
+ ],
2550
+ };
2551
+ /**
2552
+ * TODO: [1] Make type for XML Text and Schema
2553
+ * TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2554
+ * TODO: [🍓] In `TextFormatDefinition` implement simple `isValid`
2555
+ * TODO: [🍓] In `TextFormatDefinition` implement partial `canBeValid`
2556
+ * TODO: [🍓] In `TextFormatDefinition` implement `heal
2557
+ * TODO: [🍓] In `TextFormatDefinition` implement `subvalueDefinitions`
2558
+ * TODO: [🏢] Allow to expect something inside each item of list and other formats
2559
+ */
2560
+
2561
+ /**
2562
+ * Definition for XML format
2563
+ *
2564
+ * @private still in development [🏢]
2565
+ */
2566
+ var XmlFormatDefinition = {
2567
+ formatName: 'XML',
2568
+ mimeType: 'application/xml',
2569
+ isValid: function (value, settings, schema) {
2570
+ return true;
2571
+ },
2572
+ canBeValid: function (partialValue, settings, schema) {
2573
+ return true;
2574
+ },
2575
+ heal: function (value, settings, schema) {
2576
+ throw new Error('Not implemented');
2577
+ },
2578
+ subvalueDefinitions: [],
2579
+ };
2580
+ /**
2581
+ * TODO: [🧠] Maybe propper instance of object
2582
+ * TODO: [0] Make string_serialized_xml
2583
+ * TODO: [1] Make type for XML Settings and Schema
2584
+ * TODO: [🧠] What to use for validating XMLs - XSD,...
2585
+ * TODO: [🍓] In `XmlFormatDefinition` implement simple `isValid`
2586
+ * TODO: [🍓] In `XmlFormatDefinition` implement partial `canBeValid`
2587
+ * TODO: [🍓] In `XmlFormatDefinition` implement `heal
2588
+ * TODO: [🍓] In `XmlFormatDefinition` implement `subvalueDefinitions`
2589
+ * TODO: [🏢] Allow to expect something inside XML and other formats
2590
+ */
2591
+
2592
+ /**
2593
+ * Definitions for all formats supported by Promptbook
2594
+ *
2595
+ * @private internal index of `...` <- TODO [🏢]
2596
+ */
2597
+ var FORMAT_DEFINITIONS = [
2598
+ JsonFormatDefinition,
2599
+ XmlFormatDefinition,
2600
+ TextFormatDefinition,
2601
+ CsvFormatDefinition,
2602
+ ];
2603
+ /**
2604
+ * Note: [💞] Ignore a discrepancy between file name and entity name
2605
+ */
2606
+
2607
+ /**
2608
+ * Maps available parameters to expected parameters
2609
+ *
2610
+ * The strategy is:
2611
+ * 1) @@@
2612
+ * 2) @@@
2613
+ *
2614
+ * @throws {PipelineExecutionError} @@@
2615
+ * @private within the repository used in `createPipelineExecutor`
2616
+ */
2617
+ function mapAvailableToExpectedParameters(options) {
2618
+ var e_1, _a;
2619
+ var expectedParameters = options.expectedParameters, availableParameters = options.availableParameters;
2620
+ var availableParametersNames = new Set(Object.keys(availableParameters));
2621
+ var expectedParameterNames = new Set(Object.keys(expectedParameters));
2622
+ var mappedParameters = {};
2623
+ try {
2624
+ // Phase 1️⃣: Matching mapping
2625
+ for (var _b = __values(Array.from(union(availableParametersNames, expectedParameterNames))), _c = _b.next(); !_c.done; _c = _b.next()) {
2626
+ var parameterName = _c.value;
2627
+ // Situation: Parameter is available and expected
2628
+ if (availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
2629
+ mappedParameters[parameterName] = availableParameters[parameterName];
2630
+ // <- Note: [👩‍👩‍👧] Maybe detect parameter collision here?
2631
+ availableParametersNames.delete(parameterName);
2632
+ expectedParameterNames.delete(parameterName);
2633
+ }
2634
+ // Situation: Parameter is available but NOT expected
2635
+ else if (availableParametersNames.has(parameterName) && !expectedParameterNames.has(parameterName)) {
2636
+ // [🐱‍👤] Do not pass this parameter to prompt - Maybe use it non-matching mapping
2637
+ }
2638
+ // Situation: Parameter is NOT available BUT expected
2639
+ else if (!availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
2640
+ // Do nothing here - this will be maybe fixed in the non-matching mapping
2641
+ }
2642
+ }
2643
+ }
2644
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
2645
+ finally {
2646
+ try {
2647
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
2648
+ }
2649
+ finally { if (e_1) throw e_1.error; }
2650
+ }
2651
+ if (expectedParameterNames.size === 0) {
2652
+ // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
2653
+ Object.freeze(mappedParameters);
2654
+ return mappedParameters;
2655
+ }
2656
+ // Phase 2️⃣: Non-matching mapping
2657
+ if (expectedParameterNames.size !== availableParametersNames.size) {
2658
+ throw new PipelineExecutionError(spaceTrim__default["default"](function (block) { return "\n Can not map available parameters to expected parameters\n\n Mapped parameters:\n ".concat(block(Object.keys(mappedParameters)
2659
+ .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
2660
+ .join('\n')), "\n\n Expected parameters which can not be mapped:\n ").concat(block(Array.from(expectedParameterNames)
2661
+ .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
2662
+ .join('\n')), "\n\n Remaining available parameters:\n ").concat(block(Array.from(availableParametersNames)
2663
+ .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
2664
+ .join('\n')), "\n\n "); }));
2665
+ }
2666
+ var expectedParameterNamesArray = Array.from(expectedParameterNames);
2667
+ var availableParametersNamesArray = Array.from(availableParametersNames);
2668
+ for (var i = 0; i < expectedParameterNames.size; i++) {
2669
+ mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
2670
+ }
2671
+ // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
2672
+ Object.freeze(mappedParameters);
2673
+ return mappedParameters;
2674
+ }
2675
+
2676
+ /**
2677
+ * Multiple LLM Execution Tools is a proxy server that uses multiple execution tools internally and exposes the executor interface externally.
2678
+ *
2679
+ * Note: Internal utility of `joinLlmExecutionTools` but exposed type
2680
+ * @public exported from `@promptbook/core`
2681
+ */
2682
+ var MultipleLlmExecutionTools = /** @class */ (function () {
2683
+ /**
2684
+ * Gets array of execution tools in order of priority
2685
+ */
2686
+ function MultipleLlmExecutionTools() {
2687
+ var llmExecutionTools = [];
2688
+ for (var _i = 0; _i < arguments.length; _i++) {
2689
+ llmExecutionTools[_i] = arguments[_i];
2690
+ }
2691
+ this.llmExecutionTools = llmExecutionTools;
2692
+ }
2693
+ Object.defineProperty(MultipleLlmExecutionTools.prototype, "title", {
2694
+ get: function () {
2695
+ return 'Multiple LLM Providers';
2696
+ },
2697
+ enumerable: false,
2698
+ configurable: true
2699
+ });
2700
+ Object.defineProperty(MultipleLlmExecutionTools.prototype, "description", {
2701
+ get: function () {
2702
+ return this.llmExecutionTools.map(function (_a, index) {
2703
+ var title = _a.title;
2704
+ return "".concat(index + 1, ") `").concat(title, "`");
2705
+ }).join('\n');
2706
+ },
2707
+ enumerable: false,
2708
+ configurable: true
2709
+ });
2710
+ /**
2711
+ * Check the configuration of all execution tools
2712
+ */
2713
+ MultipleLlmExecutionTools.prototype.checkConfiguration = function () {
2714
+ return __awaiter(this, void 0, void 0, function () {
2715
+ var _a, _b, llmExecutionTools, e_1_1;
2716
+ var e_1, _c;
2717
+ return __generator(this, function (_d) {
2718
+ switch (_d.label) {
2719
+ case 0:
2720
+ _d.trys.push([0, 5, 6, 7]);
2721
+ _a = __values(this.llmExecutionTools), _b = _a.next();
2722
+ _d.label = 1;
2723
+ case 1:
2724
+ if (!!_b.done) return [3 /*break*/, 4];
2725
+ llmExecutionTools = _b.value;
2726
+ return [4 /*yield*/, llmExecutionTools.checkConfiguration()];
2727
+ case 2:
2728
+ _d.sent();
2729
+ _d.label = 3;
2730
+ case 3:
2731
+ _b = _a.next();
2732
+ return [3 /*break*/, 1];
2733
+ case 4: return [3 /*break*/, 7];
2734
+ case 5:
2735
+ e_1_1 = _d.sent();
2736
+ e_1 = { error: e_1_1 };
2737
+ return [3 /*break*/, 7];
2738
+ case 6:
2739
+ try {
2740
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
2741
+ }
2742
+ finally { if (e_1) throw e_1.error; }
2743
+ return [7 /*endfinally*/];
2744
+ case 7: return [2 /*return*/];
2745
+ }
2746
+ });
2747
+ });
2748
+ };
2749
+ /**
2750
+ * List all available models that can be used
2751
+ * This lists is a combination of all available models from all execution tools
2752
+ */
2753
+ MultipleLlmExecutionTools.prototype.listModels = function () {
2754
+ return __awaiter(this, void 0, void 0, function () {
2755
+ var availableModels, _a, _b, llmExecutionTools, models, e_2_1;
2756
+ var e_2, _c;
2757
+ return __generator(this, function (_d) {
2758
+ switch (_d.label) {
2759
+ case 0:
2760
+ availableModels = [];
2761
+ _d.label = 1;
2762
+ case 1:
2763
+ _d.trys.push([1, 6, 7, 8]);
2764
+ _a = __values(this.llmExecutionTools), _b = _a.next();
2765
+ _d.label = 2;
2766
+ case 2:
2767
+ if (!!_b.done) return [3 /*break*/, 5];
2768
+ llmExecutionTools = _b.value;
2769
+ return [4 /*yield*/, llmExecutionTools.listModels()];
2770
+ case 3:
2771
+ models = _d.sent();
2772
+ availableModels.push.apply(availableModels, __spreadArray([], __read(models), false));
2773
+ _d.label = 4;
2774
+ case 4:
2775
+ _b = _a.next();
2776
+ return [3 /*break*/, 2];
2777
+ case 5: return [3 /*break*/, 8];
2778
+ case 6:
2779
+ e_2_1 = _d.sent();
2780
+ e_2 = { error: e_2_1 };
2781
+ return [3 /*break*/, 8];
2782
+ case 7:
2783
+ try {
2784
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
2785
+ }
2786
+ finally { if (e_2) throw e_2.error; }
2787
+ return [7 /*endfinally*/];
2788
+ case 8: return [2 /*return*/, availableModels];
2789
+ }
2790
+ });
2791
+ });
2792
+ };
2793
+ /**
2794
+ * Calls the best available chat model
2795
+ */
2796
+ MultipleLlmExecutionTools.prototype.callChatModel = function (prompt) {
2797
+ return this.callCommonModel(prompt);
2798
+ };
2799
+ /**
2800
+ * Calls the best available completion model
2801
+ */
2802
+ MultipleLlmExecutionTools.prototype.callCompletionModel = function (prompt) {
2803
+ return this.callCommonModel(prompt);
2804
+ };
2805
+ /**
2806
+ * Calls the best available embedding model
2807
+ */
2808
+ MultipleLlmExecutionTools.prototype.callEmbeddingModel = function (prompt) {
2809
+ return this.callCommonModel(prompt);
2810
+ };
2811
+ // <- Note: [🤖]
2812
+ /**
2813
+ * Calls the best available model
2814
+ *
2815
+ * Note: This should be private or protected but is public to be usable with duck typing
2816
+ */
2817
+ MultipleLlmExecutionTools.prototype.callCommonModel = function (prompt) {
2818
+ return __awaiter(this, void 0, void 0, function () {
2819
+ var errors, _a, _b, llmExecutionTools, _c, error_1, e_3_1;
2820
+ var e_3, _d;
2821
+ var _this = this;
2822
+ return __generator(this, function (_e) {
2823
+ switch (_e.label) {
2824
+ case 0:
2825
+ errors = [];
2826
+ _e.label = 1;
2827
+ case 1:
2828
+ _e.trys.push([1, 15, 16, 17]);
2829
+ _a = __values(this.llmExecutionTools), _b = _a.next();
2830
+ _e.label = 2;
2831
+ case 2:
2832
+ if (!!_b.done) return [3 /*break*/, 14];
2833
+ llmExecutionTools = _b.value;
2834
+ _e.label = 3;
2835
+ case 3:
2836
+ _e.trys.push([3, 12, , 13]);
2837
+ _c = prompt.modelRequirements.modelVariant;
2838
+ switch (_c) {
2839
+ case 'CHAT': return [3 /*break*/, 4];
2840
+ case 'COMPLETION': return [3 /*break*/, 6];
2841
+ case 'EMBEDDING': return [3 /*break*/, 8];
2842
+ }
2843
+ return [3 /*break*/, 10];
2844
+ case 4:
2845
+ if (llmExecutionTools.callChatModel === undefined) {
2846
+ return [3 /*break*/, 13];
2847
+ }
2848
+ return [4 /*yield*/, llmExecutionTools.callChatModel(prompt)];
2849
+ case 5: return [2 /*return*/, _e.sent()];
2850
+ case 6:
2851
+ if (llmExecutionTools.callCompletionModel === undefined) {
2852
+ return [3 /*break*/, 13];
2853
+ }
2854
+ return [4 /*yield*/, llmExecutionTools.callCompletionModel(prompt)];
2855
+ case 7: return [2 /*return*/, _e.sent()];
2856
+ case 8:
2857
+ if (llmExecutionTools.callEmbeddingModel === undefined) {
2858
+ return [3 /*break*/, 13];
2859
+ }
2860
+ return [4 /*yield*/, llmExecutionTools.callEmbeddingModel(prompt)];
2861
+ case 9: return [2 /*return*/, _e.sent()];
2862
+ case 10: throw new UnexpectedError("Unknown model variant \"".concat(prompt.modelRequirements.modelVariant, "\""));
2863
+ case 11: return [3 /*break*/, 13];
2864
+ case 12:
2865
+ error_1 = _e.sent();
2866
+ if (!(error_1 instanceof Error) || error_1 instanceof UnexpectedError) {
2867
+ throw error_1;
2868
+ }
2869
+ errors.push(error_1);
2870
+ return [3 /*break*/, 13];
2871
+ case 13:
2872
+ _b = _a.next();
2873
+ return [3 /*break*/, 2];
2874
+ case 14: return [3 /*break*/, 17];
2875
+ case 15:
2876
+ e_3_1 = _e.sent();
2877
+ e_3 = { error: e_3_1 };
2878
+ return [3 /*break*/, 17];
2879
+ case 16:
2880
+ try {
2881
+ if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
2882
+ }
2883
+ finally { if (e_3) throw e_3.error; }
2884
+ return [7 /*endfinally*/];
2885
+ case 17:
2886
+ if (errors.length === 1) {
2887
+ throw errors[0];
2977
2888
  }
2978
- });
2979
- });
2980
- },
2981
- },
2982
- // <- TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2983
- ],
2984
- };
2985
- /**
2986
- * TODO: [1] Make type for XML Text and Schema
2987
- * TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2988
- * TODO: [🍓] In `TextFormatDefinition` implement simple `isValid`
2989
- * TODO: [🍓] In `TextFormatDefinition` implement partial `canBeValid`
2990
- * TODO: [🍓] In `TextFormatDefinition` implement `heal
2991
- * TODO: [🍓] In `TextFormatDefinition` implement `subvalueDefinitions`
2992
- * TODO: [🏢] Allow to expect something inside each item of list and other formats
2993
- */
2994
-
2995
- /**
2996
- * Definition for XML format
2997
- *
2998
- * @private still in development [🏢]
2999
- */
3000
- var XmlFormatDefinition = {
3001
- formatName: 'XML',
3002
- mimeType: 'application/xml',
3003
- isValid: function (value, settings, schema) {
3004
- return true;
3005
- },
3006
- canBeValid: function (partialValue, settings, schema) {
3007
- return true;
3008
- },
3009
- heal: function (value, settings, schema) {
3010
- throw new Error('Not implemented');
3011
- },
3012
- subvalueDefinitions: [],
3013
- };
3014
- /**
3015
- * TODO: [🧠] Maybe propper instance of object
3016
- * TODO: [0] Make string_serialized_xml
3017
- * TODO: [1] Make type for XML Settings and Schema
3018
- * TODO: [🧠] What to use for validating XMLs - XSD,...
3019
- * TODO: [🍓] In `XmlFormatDefinition` implement simple `isValid`
3020
- * TODO: [🍓] In `XmlFormatDefinition` implement partial `canBeValid`
3021
- * TODO: [🍓] In `XmlFormatDefinition` implement `heal
3022
- * TODO: [🍓] In `XmlFormatDefinition` implement `subvalueDefinitions`
3023
- * TODO: [🏢] Allow to expect something inside XML and other formats
3024
- */
3025
-
3026
- /**
3027
- * Definitions for all formats supported by Promptbook
3028
- *
3029
- * @private internal index of `...` <- TODO [🏢]
3030
- */
3031
- var FORMAT_DEFINITIONS = [
3032
- JsonFormatDefinition,
3033
- XmlFormatDefinition,
3034
- TextFormatDefinition,
3035
- CsvFormatDefinition,
3036
- ];
2889
+ else if (errors.length > 1) {
2890
+ throw new PipelineExecutionError(
2891
+ // TODO: Tell which execution tools failed like
2892
+ // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
2893
+ // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
2894
+ // 3) ...
2895
+ spaceTrim__default["default"](function (block) { return "\n All execution tools failed:\n\n ".concat(block(errors
2896
+ .map(function (error, i) { return "".concat(i + 1, ") **").concat(error.name || 'Error', ":** ").concat(error.message); })
2897
+ .join('\n')), "\n\n "); }));
2898
+ }
2899
+ else if (this.llmExecutionTools.length === 0) {
2900
+ throw new PipelineExecutionError("You have not provided any `LlmExecutionTools`");
2901
+ }
2902
+ else {
2903
+ throw new PipelineExecutionError(spaceTrim__default["default"](function (block) { return "\n You have not provided any `LlmExecutionTools` that support model variant \"".concat(prompt.modelRequirements.modelVariant, "\"\n\n Available `LlmExecutionTools`:\n ").concat(block(_this.description), "\n\n "); }));
2904
+ }
2905
+ }
2906
+ });
2907
+ });
2908
+ };
2909
+ return MultipleLlmExecutionTools;
2910
+ }());
3037
2911
  /**
3038
- * Note: [💞] Ignore a discrepancy between file name and entity name
2912
+ * TODO: [🧠][🎛] Aggregating multiple models - have result not only from one first aviable model BUT all of them
2913
+ * TODO: [🏖] If no llmTools have for example not defined `callCompletionModel` this will still return object with defined `callCompletionModel` which just throws `PipelineExecutionError`, make it undefined instead
2914
+ * Look how `countTotalUsage` (and `cacheLlmTools`) implements it
3039
2915
  */
3040
2916
 
3041
2917
  /**
3042
- * Maps available parameters to expected parameters
2918
+ * Joins multiple LLM Execution Tools into one
3043
2919
  *
3044
- * The strategy is:
3045
- * 1) @@@
3046
- * 2) @@@
2920
+ * @returns {LlmExecutionTools} Single wrapper for multiple LlmExecutionTools
3047
2921
  *
3048
- * @throws {PipelineExecutionError} @@@
3049
- * @private within the repository used in `createPipelineExecutor`
3050
- */
3051
- function mapAvailableToExpectedParameters(options) {
3052
- var e_1, _a;
3053
- var expectedParameters = options.expectedParameters, availableParameters = options.availableParameters;
3054
- var availableParametersNames = new Set(Object.keys(availableParameters));
3055
- var expectedParameterNames = new Set(Object.keys(expectedParameters));
3056
- var mappedParameters = {};
3057
- try {
3058
- // Phase 1️⃣: Matching mapping
3059
- for (var _b = __values(Array.from(union(availableParametersNames, expectedParameterNames))), _c = _b.next(); !_c.done; _c = _b.next()) {
3060
- var parameterName = _c.value;
3061
- // Situation: Parameter is available and expected
3062
- if (availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
3063
- mappedParameters[parameterName] = availableParameters[parameterName];
3064
- // <- Note: [👩‍👩‍👧] Maybe detect parameter collision here?
3065
- availableParametersNames.delete(parameterName);
3066
- expectedParameterNames.delete(parameterName);
3067
- }
3068
- // Situation: Parameter is available but NOT expected
3069
- else if (availableParametersNames.has(parameterName) && !expectedParameterNames.has(parameterName)) {
3070
- // [🐱‍👤] Do not pass this parameter to prompt - Maybe use it non-matching mapping
3071
- }
3072
- // Situation: Parameter is NOT available BUT expected
3073
- else if (!availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
3074
- // Do nothing here - this will be maybe fixed in the non-matching mapping
3075
- }
3076
- }
3077
- }
3078
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
3079
- finally {
3080
- try {
3081
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
3082
- }
3083
- finally { if (e_1) throw e_1.error; }
3084
- }
3085
- if (expectedParameterNames.size === 0) {
3086
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
3087
- Object.freeze(mappedParameters);
3088
- return mappedParameters;
3089
- }
3090
- // Phase 2️⃣: Non-matching mapping
3091
- if (expectedParameterNames.size !== availableParametersNames.size) {
3092
- throw new PipelineExecutionError(spaceTrim__default["default"](function (block) { return "\n Can not map available parameters to expected parameters\n\n Mapped parameters:\n ".concat(block(Object.keys(mappedParameters)
3093
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3094
- .join('\n')), "\n\n Expected parameters which can not be mapped:\n ").concat(block(Array.from(expectedParameterNames)
3095
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3096
- .join('\n')), "\n\n Remaining available parameters:\n ").concat(block(Array.from(availableParametersNames)
3097
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3098
- .join('\n')), "\n\n "); }));
2922
+ * 0) If there is no LlmExecutionTools, it warns and returns valid but empty LlmExecutionTools
2923
+ * 1) If there is only one LlmExecutionTools, it returns it wrapped in a proxy object
2924
+ * 2) If there are multiple LlmExecutionTools, first will be used first, second will be used if the first hasn`t defined model variant or fails, etc.
2925
+ * 3) When all LlmExecutionTools fail, it throws an error with a list of all errors merged into one
2926
+ *
2927
+ *
2928
+ * Tip: You don't have to use this function directly, just pass an array of LlmExecutionTools to the `ExecutionTools`
2929
+ *
2930
+ * @public exported from `@promptbook/core`
2931
+ */
2932
+ function joinLlmExecutionTools() {
2933
+ var llmExecutionTools = [];
2934
+ for (var _i = 0; _i < arguments.length; _i++) {
2935
+ llmExecutionTools[_i] = arguments[_i];
3099
2936
  }
3100
- var expectedParameterNamesArray = Array.from(expectedParameterNames);
3101
- var availableParametersNamesArray = Array.from(availableParametersNames);
3102
- for (var i = 0; i < expectedParameterNames.size; i++) {
3103
- mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
2937
+ if (llmExecutionTools.length === 0) {
2938
+ var warningMessage = spaceTrim__default["default"]("\n You have not provided any `LlmExecutionTools`\n This means that you won't be able to execute any prompts that require large language models like GPT-4 or Anthropic's Claude.\n\n Technically, it's not an error, but it's probably not what you want because it does not make sense to use Promptbook without language models.\n ");
2939
+ // TODO: [🟥] Detect browser / node and make it colorfull
2940
+ console.warn(warningMessage);
2941
+ /*
2942
+ return {
2943
+ async listModels() {
2944
+ // TODO: [🟥] Detect browser / node and make it colorfull
2945
+ console.warn(
2946
+ spaceTrim(
2947
+ (block) => `
2948
+
2949
+ You can't list models because you have no LLM Execution Tools defined:
2950
+
2951
+ tl;dr
2952
+
2953
+ ${block(warningMessage)}
2954
+ `,
2955
+ ),
2956
+ );
2957
+ return [];
2958
+ },
2959
+ };
2960
+ */
3104
2961
  }
3105
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
3106
- Object.freeze(mappedParameters);
3107
- return mappedParameters;
2962
+ return new (MultipleLlmExecutionTools.bind.apply(MultipleLlmExecutionTools, __spreadArray([void 0], __read(llmExecutionTools), false)))();
3108
2963
  }
2964
+ /**
2965
+ * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
2966
+ */
3109
2967
 
3110
2968
  /**
3111
2969
  * Extracts all code blocks from markdown.
@@ -3298,6 +3156,8 @@
3298
3156
  throw new PipelineExecutionError("Parameter `{".concat(parameterName, "}` is not defined"));
3299
3157
  }
3300
3158
  parameterValue = valueToString(parameterValue);
3159
+ // Escape curly braces in parameter values to prevent prompt-injection
3160
+ parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
3301
3161
  if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
3302
3162
  parameterValue = parameterValue
3303
3163
  .split('\n')
@@ -3918,7 +3778,7 @@
3918
3778
  promptTitle: task.title,
3919
3779
  promptMessage: templateParameters(task.description || '', parameters),
3920
3780
  defaultValue: templateParameters(preparedContent, parameters),
3921
- // TODO: [🧠] !! Figure out how to define placeholder in .book.md file
3781
+ // TODO: [🧠] Figure out how to define placeholder in .book.md file
3922
3782
  placeholder: undefined,
3923
3783
  priority: priority,
3924
3784
  }))];
@@ -4856,6 +4716,169 @@
4856
4716
  * TODO: [🐚] Change onProgress to object that represents the running execution, can be subscribed via RxJS to and also awaited
4857
4717
  */
4858
4718
 
4719
+ /**
4720
+ * Async version of Array.forEach
4721
+ *
4722
+ * @param array - Array to iterate over
4723
+ * @param options - Options for the function
4724
+ * @param callbackfunction - Function to call for each item
4725
+ * @public exported from `@promptbook/utils`
4726
+ * @deprecated [🪂] Use queues instead
4727
+ */
4728
+ function forEachAsync(array, options, callbackfunction) {
4729
+ return __awaiter(this, void 0, void 0, function () {
4730
+ var _a, maxParallelCount, index, runningTasks, tasks, _loop_1, _b, _c, item, e_1_1;
4731
+ var e_1, _d;
4732
+ return __generator(this, function (_e) {
4733
+ switch (_e.label) {
4734
+ case 0:
4735
+ _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? Infinity : _a;
4736
+ index = 0;
4737
+ runningTasks = [];
4738
+ tasks = [];
4739
+ _loop_1 = function (item) {
4740
+ var currentIndex, task;
4741
+ return __generator(this, function (_f) {
4742
+ switch (_f.label) {
4743
+ case 0:
4744
+ currentIndex = index++;
4745
+ task = callbackfunction(item, currentIndex, array);
4746
+ tasks.push(task);
4747
+ runningTasks.push(task);
4748
+ /* not await */ Promise.resolve(task).then(function () {
4749
+ runningTasks = runningTasks.filter(function (t) { return t !== task; });
4750
+ });
4751
+ if (!(maxParallelCount < runningTasks.length)) return [3 /*break*/, 2];
4752
+ return [4 /*yield*/, Promise.race(runningTasks)];
4753
+ case 1:
4754
+ _f.sent();
4755
+ _f.label = 2;
4756
+ case 2: return [2 /*return*/];
4757
+ }
4758
+ });
4759
+ };
4760
+ _e.label = 1;
4761
+ case 1:
4762
+ _e.trys.push([1, 6, 7, 8]);
4763
+ _b = __values(array), _c = _b.next();
4764
+ _e.label = 2;
4765
+ case 2:
4766
+ if (!!_c.done) return [3 /*break*/, 5];
4767
+ item = _c.value;
4768
+ return [5 /*yield**/, _loop_1(item)];
4769
+ case 3:
4770
+ _e.sent();
4771
+ _e.label = 4;
4772
+ case 4:
4773
+ _c = _b.next();
4774
+ return [3 /*break*/, 2];
4775
+ case 5: return [3 /*break*/, 8];
4776
+ case 6:
4777
+ e_1_1 = _e.sent();
4778
+ e_1 = { error: e_1_1 };
4779
+ return [3 /*break*/, 8];
4780
+ case 7:
4781
+ try {
4782
+ if (_c && !_c.done && (_d = _b.return)) _d.call(_b);
4783
+ }
4784
+ finally { if (e_1) throw e_1.error; }
4785
+ return [7 /*endfinally*/];
4786
+ case 8: return [4 /*yield*/, Promise.all(tasks)];
4787
+ case 9:
4788
+ _e.sent();
4789
+ return [2 /*return*/];
4790
+ }
4791
+ });
4792
+ });
4793
+ }
4794
+
4795
+ /**
4796
+ * Intercepts LLM tools and counts total usage of the tools
4797
+ *
4798
+ * @param llmTools LLM tools to be intercepted with usage counting
4799
+ * @returns LLM tools with same functionality with added total cost counting
4800
+ * @public exported from `@promptbook/core`
4801
+ */
4802
+ function countTotalUsage(llmTools) {
4803
+ var _this = this;
4804
+ var totalUsage = ZERO_USAGE;
4805
+ var proxyTools = {
4806
+ get title() {
4807
+ // TODO: [🧠] Maybe put here some suffix
4808
+ return llmTools.title;
4809
+ },
4810
+ get description() {
4811
+ // TODO: [🧠] Maybe put here some suffix
4812
+ return llmTools.description;
4813
+ },
4814
+ checkConfiguration: function () {
4815
+ return __awaiter(this, void 0, void 0, function () {
4816
+ return __generator(this, function (_a) {
4817
+ return [2 /*return*/, /* not await */ llmTools.checkConfiguration()];
4818
+ });
4819
+ });
4820
+ },
4821
+ listModels: function () {
4822
+ return /* not await */ llmTools.listModels();
4823
+ },
4824
+ getTotalUsage: function () {
4825
+ // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
4826
+ return totalUsage;
4827
+ },
4828
+ };
4829
+ if (llmTools.callChatModel !== undefined) {
4830
+ proxyTools.callChatModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
4831
+ var promptResult;
4832
+ return __generator(this, function (_a) {
4833
+ switch (_a.label) {
4834
+ case 0: return [4 /*yield*/, llmTools.callChatModel(prompt)];
4835
+ case 1:
4836
+ promptResult = _a.sent();
4837
+ totalUsage = addUsage(totalUsage, promptResult.usage);
4838
+ return [2 /*return*/, promptResult];
4839
+ }
4840
+ });
4841
+ }); };
4842
+ }
4843
+ if (llmTools.callCompletionModel !== undefined) {
4844
+ proxyTools.callCompletionModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
4845
+ var promptResult;
4846
+ return __generator(this, function (_a) {
4847
+ switch (_a.label) {
4848
+ case 0: return [4 /*yield*/, llmTools.callCompletionModel(prompt)];
4849
+ case 1:
4850
+ promptResult = _a.sent();
4851
+ totalUsage = addUsage(totalUsage, promptResult.usage);
4852
+ return [2 /*return*/, promptResult];
4853
+ }
4854
+ });
4855
+ }); };
4856
+ }
4857
+ if (llmTools.callEmbeddingModel !== undefined) {
4858
+ proxyTools.callEmbeddingModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
4859
+ var promptResult;
4860
+ return __generator(this, function (_a) {
4861
+ switch (_a.label) {
4862
+ case 0: return [4 /*yield*/, llmTools.callEmbeddingModel(prompt)];
4863
+ case 1:
4864
+ promptResult = _a.sent();
4865
+ totalUsage = addUsage(totalUsage, promptResult.usage);
4866
+ return [2 /*return*/, promptResult];
4867
+ }
4868
+ });
4869
+ }); };
4870
+ }
4871
+ // <- Note: [🤖]
4872
+ return proxyTools;
4873
+ }
4874
+ /**
4875
+ * TODO: [🧠][💸] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
4876
+ * TODO: [🧠] Is there some meaningfull way how to test this util
4877
+ * TODO: [🧠][🌯] Maybe a way how to hide ability to `get totalUsage`
4878
+ * > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
4879
+ * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
4880
+ */
4881
+
4859
4882
  /**
4860
4883
  * Prepares the persona for the pipeline
4861
4884
  *
@@ -4918,10 +4941,10 @@
4918
4941
  });
4919
4942
  }
4920
4943
  /**
4921
- * TODO: [🔃][main] !! If the persona was prepared with different version or different set of models, prepare it once again
4922
- * TODO: [🏢] !! Check validity of `modelName` in pipeline
4923
- * TODO: [🏢] !! Check validity of `systemMessage` in pipeline
4924
- * TODO: [🏢] !! Check validity of `temperature` in pipeline
4944
+ * TODO: [🔃][main] If the persona was prepared with different version or different set of models, prepare it once again
4945
+ * TODO: [🏢] Check validity of `modelName` in pipeline
4946
+ * TODO: [🏢] Check validity of `systemMessage` in pipeline
4947
+ * TODO: [🏢] Check validity of `temperature` in pipeline
4925
4948
  */
4926
4949
 
4927
4950
  /**
@@ -5355,21 +5378,44 @@
5355
5378
  if (typeof filename !== 'string') {
5356
5379
  return false;
5357
5380
  }
5381
+ if (filename.split('\n').length > 1) {
5382
+ return false;
5383
+ }
5384
+ if (filename.split(' ').length >
5385
+ 5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
5386
+ return false;
5387
+ }
5358
5388
  var filenameSlashes = filename.split('\\').join('/');
5359
5389
  // Absolute Unix path: /hello.txt
5360
5390
  if (/^(\/)/i.test(filenameSlashes)) {
5391
+ // console.log(filename, 'Absolute Unix path: /hello.txt');
5361
5392
  return true;
5362
5393
  }
5363
5394
  // Absolute Windows path: /hello.txt
5364
5395
  if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
5396
+ // console.log(filename, 'Absolute Windows path: /hello.txt');
5365
5397
  return true;
5366
5398
  }
5367
5399
  // Relative path: ./hello.txt
5368
5400
  if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
5401
+ // console.log(filename, 'Relative path: ./hello.txt');
5402
+ return true;
5403
+ }
5404
+ // Allow paths like foo/hello
5405
+ if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
5406
+ // console.log(filename, 'Allow paths like foo/hello');
5407
+ return true;
5408
+ }
5409
+ // Allow paths like hello.book
5410
+ if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
5411
+ // console.log(filename, 'Allow paths like hello.book');
5369
5412
  return true;
5370
5413
  }
5371
5414
  return false;
5372
5415
  }
5416
+ /**
5417
+ * TODO: [🍏] Implement for MacOs
5418
+ */
5373
5419
 
5374
5420
  /**
5375
5421
  * The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
@@ -5394,6 +5440,9 @@
5394
5440
  }
5395
5441
  });
5396
5442
  }); };
5443
+ /**
5444
+ * TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
5445
+ */
5397
5446
 
5398
5447
  /**
5399
5448
  * @@@
@@ -5461,7 +5510,7 @@
5461
5510
  },
5462
5511
  }];
5463
5512
  case 2:
5464
- if (!(isValidFilePath(sourceContent) || /\.[a-z]{1,10}$/i.exec(sourceContent))) return [3 /*break*/, 4];
5513
+ if (!isValidFilePath(sourceContent)) return [3 /*break*/, 4];
5465
5514
  if (tools.fs === undefined) {
5466
5515
  throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
5467
5516
  // <- TODO: [🧠] What is the best error type here`
@@ -5476,7 +5525,7 @@
5476
5525
  return [4 /*yield*/, isFileExisting(filename_1, tools.fs)];
5477
5526
  case 3:
5478
5527
  if (!(_f.sent())) {
5479
- 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 "); }));
5528
+ throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Can not make source handler for file which does not exist:\n\n File:\n ".concat(block(sourceContent), "\n\n Full file path:\n ").concat(block(filename_1), "\n "); }));
5480
5529
  }
5481
5530
  // TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
5482
5531
  return [2 /*return*/, {
@@ -5589,7 +5638,7 @@
5589
5638
  partialPieces = __spreadArray([], __read(partialPiecesUnchecked), false);
5590
5639
  return [2 /*return*/, "break"];
5591
5640
  }
5592
- console.warn(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge from source despite the scraper `".concat(scraper.metadata.className, "` supports the mime type \"").concat(sourceHandler.mimeType, "\".\n \n The source:\n > ").concat(block(knowledgeSource.sourceContent
5641
+ console.warn(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge from source despite the scraper `".concat(scraper.metadata.className, "` supports the mime type \"").concat(sourceHandler.mimeType, "\".\n\n The source:\n ").concat(block(knowledgeSource.sourceContent
5593
5642
  .split('\n')
5594
5643
  .map(function (line) { return "> ".concat(line); })
5595
5644
  .join('\n')), "\n\n ").concat(block($registeredScrapersMessage(scrapers)), "\n\n\n "); }));
@@ -5627,7 +5676,7 @@
5627
5676
  return [7 /*endfinally*/];
5628
5677
  case 9:
5629
5678
  if (partialPieces === null) {
5630
- throw new KnowledgeScrapeError(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge\n \n The source:\n > ".concat(block(knowledgeSource.sourceContent
5679
+ throw new KnowledgeScrapeError(spaceTrim__default["default"](function (block) { return "\n Cannot scrape knowledge\n\n The source:\n > ".concat(block(knowledgeSource.sourceContent
5631
5680
  .split('\n')
5632
5681
  .map(function (line) { return "> ".concat(line); })
5633
5682
  .join('\n')), "\n\n No scraper found for the mime type \"").concat(sourceHandler.mimeType, "\"\n\n ").concat(block($registeredScrapersMessage(scrapers)), "\n\n\n "); }));
@@ -5718,7 +5767,7 @@
5718
5767
  * TODO: [😂] Adding knowledge should be convert to async high-level abstractions, simmilar thing with expectations to sync high-level abstractions
5719
5768
  * TODO: [🧠] Add context to each task (if missing)
5720
5769
  * TODO: [🧠] What is better name `prepareTask` or `prepareTaskAndParameters`
5721
- * TODO: [♨][main] !!! Prepare index the examples and maybe tasks
5770
+ * TODO: [♨][main] !!3 Prepare index the examples and maybe tasks
5722
5771
  * TODO: Write tests for `preparePipeline`
5723
5772
  * TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
5724
5773
  * TODO: [🧊] In future one preparation can take data from previous preparation and save tokens and time
@@ -5728,6 +5777,8 @@
5728
5777
  /**
5729
5778
  * Prepare pipeline from string (markdown) format to JSON format
5730
5779
  *
5780
+ * @see https://github.com/webgptorg/promptbook/discussions/196
5781
+ *
5731
5782
  * Note: This function does not validate logic of the pipeline
5732
5783
  * Note: This function acts as part of compilation process
5733
5784
  * Note: When the pipeline is already prepared, it returns the same pipeline
@@ -5740,16 +5791,17 @@
5740
5791
  <- TODO: [🧠][🪑] `promptbookVersion` */
5741
5792
  knowledgeSources /*
5742
5793
  <- TODO: [🧊] `knowledgePieces` */, personas /*
5743
- <- TODO: [🧊] `preparations` */, _llms, llmTools, llmToolsWithUsage, currentPreparation, preparations, preparedPersonas, knowledgeSourcesPrepared, partialknowledgePiecesPrepared, knowledgePiecesPrepared, tasksPrepared /* TODO: parameters: parametersPrepared*/;
5794
+ <- TODO: [🧊] `preparations` */, sources, _llms, llmTools, llmToolsWithUsage, currentPreparation, preparations, title, collection, prepareTitleExecutor, _c, result, outputParameters, titleRaw, preparedPersonas, knowledgeSourcesPrepared, partialknowledgePiecesPrepared, knowledgePiecesPrepared, tasksPrepared /* TODO: parameters: parametersPrepared*/;
5795
+ var _d;
5744
5796
  var _this = this;
5745
- return __generator(this, function (_c) {
5746
- switch (_c.label) {
5797
+ return __generator(this, function (_e) {
5798
+ switch (_e.label) {
5747
5799
  case 0:
5748
5800
  if (isPipelinePrepared(pipeline)) {
5749
5801
  return [2 /*return*/, pipeline];
5750
5802
  }
5751
5803
  rootDirname = options.rootDirname, _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? DEFAULT_MAX_PARALLEL_COUNT : _a, _b = options.isVerbose, isVerbose = _b === void 0 ? DEFAULT_IS_VERBOSE : _b;
5752
- parameters = pipeline.parameters, tasks = pipeline.tasks, knowledgeSources = pipeline.knowledgeSources, personas = pipeline.personas;
5804
+ parameters = pipeline.parameters, tasks = pipeline.tasks, knowledgeSources = pipeline.knowledgeSources, personas = pipeline.personas, sources = pipeline.sources;
5753
5805
  if (tools === undefined || tools.llm === undefined) {
5754
5806
  throw new MissingToolsError('LLM tools are required for preparing the pipeline');
5755
5807
  }
@@ -5767,6 +5819,33 @@
5767
5819
  // <- TODO: [🧊]
5768
5820
  currentPreparation,
5769
5821
  ];
5822
+ title = pipeline.title;
5823
+ if (!(title === undefined || title === '' || title === DEFAULT_BOOK_TITLE)) return [3 /*break*/, 3];
5824
+ collection = createCollectionFromJson.apply(void 0, __spreadArray([], __read(PipelineCollection), false));
5825
+ _c = createPipelineExecutor;
5826
+ _d = {};
5827
+ return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-title.book.md')];
5828
+ case 1:
5829
+ prepareTitleExecutor = _c.apply(void 0, [(_d.pipeline = _e.sent(),
5830
+ _d.tools = tools,
5831
+ _d)]);
5832
+ return [4 /*yield*/, prepareTitleExecutor({
5833
+ book: sources.map(function (_a) {
5834
+ var content = _a.content;
5835
+ return content;
5836
+ }).join('\n\n'),
5837
+ })];
5838
+ case 2:
5839
+ result = _e.sent();
5840
+ assertsExecutionSuccessful(result);
5841
+ outputParameters = result.outputParameters;
5842
+ titleRaw = outputParameters.title;
5843
+ if (isVerbose) {
5844
+ console.info("The title is \"".concat(titleRaw, "\""));
5845
+ }
5846
+ title = titleRaw || DEFAULT_BOOK_TITLE;
5847
+ _e.label = 3;
5848
+ case 3:
5770
5849
  preparedPersonas = new Array(personas.length);
5771
5850
  return [4 /*yield*/, forEachAsync(personas, { maxParallelCount: maxParallelCount /* <- TODO: [🪂] When there are subtasks, this maximul limit can be broken */ }, function (persona, index) { return __awaiter(_this, void 0, void 0, function () {
5772
5851
  var modelRequirements, preparedPersona;
@@ -5785,12 +5864,12 @@
5785
5864
  }
5786
5865
  });
5787
5866
  }); })];
5788
- case 1:
5789
- _c.sent();
5867
+ case 4:
5868
+ _e.sent();
5790
5869
  knowledgeSourcesPrepared = knowledgeSources.map(function (source) { return (__assign(__assign({}, source), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
5791
5870
  return [4 /*yield*/, prepareKnowledgePieces(knowledgeSources /* <- TODO: [🧊] {knowledgeSources, knowledgePieces} */, __assign(__assign({}, tools), { llm: llmToolsWithUsage }), __assign(__assign({}, options), { rootDirname: rootDirname, maxParallelCount: maxParallelCount /* <- TODO: [🪂] */, isVerbose: isVerbose }))];
5792
- case 2:
5793
- partialknowledgePiecesPrepared = _c.sent();
5871
+ case 5:
5872
+ partialknowledgePiecesPrepared = _e.sent();
5794
5873
  knowledgePiecesPrepared = partialknowledgePiecesPrepared.map(function (piece) { return (__assign(__assign({}, piece), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
5795
5874
  return [4 /*yield*/, prepareTasks({
5796
5875
  parameters: parameters,
@@ -5801,8 +5880,8 @@
5801
5880
  maxParallelCount: maxParallelCount /* <- TODO: [🪂] */,
5802
5881
  isVerbose: isVerbose,
5803
5882
  })];
5804
- case 3:
5805
- tasksPrepared = (_c.sent()).tasksPrepared;
5883
+ case 6:
5884
+ tasksPrepared = (_e.sent()).tasksPrepared;
5806
5885
  // ----- /Tasks preparation -----
5807
5886
  // TODO: [😂] Use here all `AsyncHighLevelAbstraction`
5808
5887
  // Note: Count total usage
@@ -5813,7 +5892,7 @@
5813
5892
  order: ORDER_OF_PIPELINE_JSON,
5814
5893
  value: __assign(__assign({}, pipeline), {
5815
5894
  // <- TODO: Probbably deeply clone the pipeline because `$exportJson` freezes the subobjects
5816
- knowledgeSources: knowledgeSourcesPrepared, knowledgePieces: knowledgePiecesPrepared, tasks: __spreadArray([], __read(tasksPrepared), false),
5895
+ title: title, knowledgeSources: knowledgeSourcesPrepared, knowledgePieces: knowledgePiecesPrepared, tasks: __spreadArray([], __read(tasksPrepared), false),
5817
5896
  // <- TODO: [🪓] Here should be no need for spreading new array, just ` tasks: tasksPrepared`
5818
5897
  personas: preparedPersonas, preparations: __spreadArray([], __read(preparations), false) }),
5819
5898
  })];
@@ -5908,7 +5987,7 @@
5908
5987
  if (sourceContent === '') {
5909
5988
  throw new ParseError("Source is not defined");
5910
5989
  }
5911
- // TODO: [main] !!!! Following checks should be applied every link in the `sourceContent`
5990
+ // TODO: [main] !!4 Following checks should be applied every link in the `sourceContent`
5912
5991
  if (sourceContent.startsWith('http://')) {
5913
5992
  throw new ParseError("Source is not secure");
5914
5993
  }
@@ -6080,7 +6159,7 @@
6080
6159
  expectResultingParameterName();
6081
6160
  var parameter = $pipelineJson.parameters.find(function (param) { return param.name === $taskJson.resultingParameterName; });
6082
6161
  if (parameter === undefined) {
6083
- // TODO: !!!!!! Change to logic error for higher level abstractions to work
6162
+ // TODO: !!6 Change to logic error for higher level abstraction of chatbot to work
6084
6163
  throw new ParseError("Parameter `{".concat($taskJson.resultingParameterName, "}` is not defined so can not define example value of it"));
6085
6164
  }
6086
6165
  parameter.exampleValues = parameter.exampleValues || [];
@@ -6091,7 +6170,7 @@
6091
6170
  if (command.taskType === 'KNOWLEDGE') {
6092
6171
  knowledgeCommandParser.$applyToPipelineJson({
6093
6172
  type: 'KNOWLEDGE',
6094
- sourceContent: $taskJson.content, // <- TODO: [🐝][main] !!! Work with KNOWLEDGE which not referring to the source file or website, but its content itself
6173
+ sourceContent: $taskJson.content, // <- TODO: [🐝][main] !!3 Work with KNOWLEDGE which not referring to the source file or website, but its content itself
6095
6174
  }, $pipelineJson);
6096
6175
  $taskJson.isTask = false;
6097
6176
  return;
@@ -6986,20 +7065,24 @@
6986
7065
  */
6987
7066
  var GeneratorFormfactorDefinition = {
6988
7067
  name: 'GENERATOR',
6989
- description: "@@@",
7068
+ description: "Generates any kind (in HTML with possible scripts and css format) of content from input message",
6990
7069
  documentationUrl: "https://github.com/webgptorg/promptbook/discussions/184",
6991
7070
  pipelineInterface: {
6992
7071
  inputParameters: [
6993
- /* @@@ */
6994
7072
  {
6995
- name: 'nonce',
6996
- description: 'Just to prevent GENERATOR to be set as implicit formfactor',
7073
+ name: 'inputMessage',
7074
+ description: "Input message to be image made from",
6997
7075
  isInput: true,
6998
7076
  isOutput: false,
6999
7077
  },
7000
7078
  ],
7001
7079
  outputParameters: [
7002
- /* @@@ */
7080
+ {
7081
+ name: 'result',
7082
+ description: "Result in HTML to be shown to user",
7083
+ isInput: false,
7084
+ isOutput: true,
7085
+ },
7003
7086
  ],
7004
7087
  },
7005
7088
  };
@@ -7031,6 +7114,35 @@
7031
7114
  pipelineInterface: GENERIC_PIPELINE_INTERFACE,
7032
7115
  };
7033
7116
 
7117
+ /**
7118
+ * Image generator is form of app that generates image from input message
7119
+ *
7120
+ * @public exported from `@promptbook/core`
7121
+ */
7122
+ var ImageGeneratorFormfactorDefinition = {
7123
+ name: 'IMAGE_GENERATOR',
7124
+ description: "Generates prompt for image generation from input message",
7125
+ documentationUrl: "https://github.com/webgptorg/promptbook/discussions/184",
7126
+ pipelineInterface: {
7127
+ inputParameters: [
7128
+ {
7129
+ name: 'inputMessage',
7130
+ description: "Input message to be image made from",
7131
+ isInput: true,
7132
+ isOutput: false,
7133
+ },
7134
+ ],
7135
+ outputParameters: [
7136
+ {
7137
+ name: 'prompt',
7138
+ description: "Prompt to be used for image generation",
7139
+ isInput: false,
7140
+ isOutput: true,
7141
+ },
7142
+ ],
7143
+ },
7144
+ };
7145
+
7034
7146
  /**
7035
7147
  * Matcher is form of app that @@@
7036
7148
  *
@@ -7129,6 +7241,7 @@
7129
7241
  SheetsFormfactorDefinition,
7130
7242
  MatcherFormfactorDefinition,
7131
7243
  GeneratorFormfactorDefinition,
7244
+ ImageGeneratorFormfactorDefinition,
7132
7245
  ];
7133
7246
  /**
7134
7247
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -7515,7 +7628,7 @@
7515
7628
  * Note: `$` is used to indicate that this function mutates given `pipelineJson`
7516
7629
  */
7517
7630
  $applyToPipelineJson: function (command, $pipelineJson) {
7518
- // Note: [🍣] Do nothing, its application is implemented separately in `precompilePipeline`
7631
+ // Note: [🍣] Do nothing, its application is implemented separately in `parsePipeline`
7519
7632
  },
7520
7633
  /**
7521
7634
  * Apply the PARAMETER command to the `pipelineJson`
@@ -7523,7 +7636,7 @@
7523
7636
  * Note: `$` is used to indicate that this function mutates given `taskJson`
7524
7637
  */
7525
7638
  $applyToTaskJson: function (command, $taskJson, $pipelineJson) {
7526
- // Note: [🍣] Do nothing, its application is implemented separately in `precompilePipeline`
7639
+ // Note: [🍣] Do nothing, its application is implemented separately in `parsePipeline`
7527
7640
  },
7528
7641
  /**
7529
7642
  * Converts the PARAMETER command back to string
@@ -8018,7 +8131,7 @@
8018
8131
  instrumentCommandParser,
8019
8132
  personaCommandParser,
8020
8133
  foreachCommandParser,
8021
- boilerplateCommandParser, // <- TODO: !! Only in development, remove in production
8134
+ boilerplateCommandParser, // <- TODO: Only in development, remove in production
8022
8135
  // <- Note: [♓️][💩] This is the order of the commands in the pipeline, BUT its not used in parsing and before usage maybe it should be done better
8023
8136
  ];
8024
8137
  /**
@@ -8442,7 +8555,7 @@
8442
8555
  isOutput: true,
8443
8556
  exampleValues: ['Hello, I am a Pavol`s virtual avatar. How can I help you?'],
8444
8557
  });
8445
- // TODO: !!!!!! spaceTrim
8558
+ // TODO: Use spaceTrim in multiline strings
8446
8559
  $pipelineJson.tasks.push({
8447
8560
  taskType: 'PROMPT_TASK',
8448
8561
  name: 'create-an-answer',
@@ -8450,8 +8563,11 @@
8450
8563
  content: 'Write a response to the user message:\n\n**Question from user**\n\n> {userMessage}\n\n**Previous conversation**\n\n> {previousConversationSummary}',
8451
8564
  resultingParameterName: 'chatbotResponse',
8452
8565
  personaName: personaName,
8453
- dependentParameterNames: ['userMessage', 'previousConversationSummary' /* !!!!!!, 'knowledge'*/],
8454
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8566
+ dependentParameterNames: [
8567
+ 'userMessage',
8568
+ 'previousConversationSummary' /* TODO: [🧠][📛], 'knowledge'*/,
8569
+ ],
8570
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8455
8571
  }, {
8456
8572
  taskType: 'PROMPT_TASK',
8457
8573
  name: 'summarize-the-conversation',
@@ -8465,24 +8581,27 @@
8465
8581
  max: 10,
8466
8582
  },
8467
8583
  },
8468
- dependentParameterNames: ['userMessage', 'chatbotResponse' /* !!!!!!, 'knowledge'*/],
8469
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8584
+ dependentParameterNames: ['userMessage', 'chatbotResponse' /* TODO: [🧠][📛], 'knowledge'*/],
8585
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8470
8586
  }, {
8471
8587
  taskType: 'SIMPLE_TASK',
8472
8588
  name: 'title',
8473
8589
  title: 'Title',
8474
8590
  content: '{conversationSummary}',
8475
8591
  resultingParameterName: 'title',
8476
- dependentParameterNames: ['conversationSummary' /* !!!!!!, 'knowledge'*/],
8477
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8592
+ dependentParameterNames: ['conversationSummary' /* TODO: [🧠][📛], 'knowledge'*/],
8593
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8478
8594
  });
8479
8595
  },
8480
8596
  };
8597
+ /**
8598
+ * TODO: [🧠][📛] Should this be here?
8599
+ */
8481
8600
 
8482
8601
  /**
8483
8602
  * All high-level abstractions
8484
8603
  *
8485
- * @private internal index of `precompilePipeline` (= used for sync) and `preparePipeline` (= used for async)
8604
+ * @private internal index of `parsePipeline` (= used for sync) and `preparePipeline` (= used for async)
8486
8605
  */
8487
8606
  var HIGH_LEVEL_ABSTRACTIONS = [
8488
8607
  ImplicitFormfactorHla,
@@ -8616,7 +8735,7 @@
8616
8735
  return;
8617
8736
  }
8618
8737
  if (!section.startsWith('#')) {
8619
- section = "# ".concat(DEFAULT_TITLE, "\n\n").concat(section);
8738
+ section = "# ".concat(DEFAULT_BOOK_TITLE, "\n\n").concat(section);
8620
8739
  }
8621
8740
  sections.push(section);
8622
8741
  buffer = [];
@@ -8671,7 +8790,7 @@
8671
8790
  /**
8672
8791
  * Normalizes the markdown by flattening the structure
8673
8792
  *
8674
- * - It always have h1 - if there is no h1 in the markdown, it will be added "# Untitled"
8793
+ * - It always have h1 - if there is no h1 in the markdown, it will be added `DEFAULT_BOOK_TITLE`
8675
8794
  * - All other headings are normalized to h2
8676
8795
  *
8677
8796
  * @public exported from `@promptbook/markdown-utils`
@@ -8680,7 +8799,7 @@
8680
8799
  var e_1, _a;
8681
8800
  var sections = splitMarkdownIntoSections(markdown);
8682
8801
  if (sections.length === 0) {
8683
- return "# ".concat(DEFAULT_TITLE);
8802
+ return "# ".concat(DEFAULT_BOOK_TITLE);
8684
8803
  }
8685
8804
  var flattenedMarkdown = '';
8686
8805
  var parsedSections = sections.map(parseMarkdownSection);
@@ -8691,7 +8810,7 @@
8691
8810
  }
8692
8811
  else {
8693
8812
  parsedSections.unshift(firstSection);
8694
- flattenedMarkdown += "# ".concat(DEFAULT_TITLE) + "\n\n"; // <- [🧠] Maybe 3 new lines?
8813
+ flattenedMarkdown += "# ".concat(DEFAULT_BOOK_TITLE) + "\n\n"; // <- [🧠] Maybe 3 new lines?
8695
8814
  }
8696
8815
  try {
8697
8816
  for (var parsedSections_1 = __values(parsedSections), parsedSections_1_1 = parsedSections_1.next(); !parsedSections_1_1.done; parsedSections_1_1 = parsedSections_1.next()) {
@@ -8718,13 +8837,13 @@
8718
8837
  */
8719
8838
 
8720
8839
  /**
8721
- * Removes HTML or Markdown comments from a string.
8840
+ * Removes Markdown (or HTML) comments
8722
8841
  *
8723
8842
  * @param {string} content - The string to remove comments from.
8724
8843
  * @returns {string} The input string with all comments removed.
8725
8844
  * @public exported from `@promptbook/markdown-utils`
8726
8845
  */
8727
- function removeContentComments(content) {
8846
+ function removeMarkdownComments(content) {
8728
8847
  return spaceTrim.spaceTrim(content.replace(/<!--(.*?)-->/gs, ''));
8729
8848
  }
8730
8849
 
@@ -8757,7 +8876,7 @@
8757
8876
  *
8758
8877
  * Note: There are 3 similar functions:
8759
8878
  * - `compilePipeline` **(preferred)** - which propperly compiles the promptbook and use embedding for external knowledge
8760
- * - `precompilePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
8879
+ * - `parsePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
8761
8880
  * - `preparePipeline` - just one step in the compilation process
8762
8881
  *
8763
8882
  * Note: This function does not validate logic of the pipeline only the parsing
@@ -8768,10 +8887,10 @@
8768
8887
  * @throws {ParseError} if the promptbook string is not valid
8769
8888
  * @public exported from `@promptbook/core`
8770
8889
  */
8771
- function precompilePipeline(pipelineString) {
8890
+ function parsePipeline(pipelineString) {
8772
8891
  var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
8773
8892
  var $pipelineJson = {
8774
- title: DEFAULT_TITLE,
8893
+ title: DEFAULT_BOOK_TITLE,
8775
8894
  parameters: [],
8776
8895
  tasks: [],
8777
8896
  knowledgeSources: [],
@@ -8782,7 +8901,7 @@
8782
8901
  {
8783
8902
  type: 'BOOK',
8784
8903
  path: null,
8785
- // <- TODO: !!!!!! Pass here path of the file
8904
+ // <- TODO: !!6 Pass here path of the file
8786
8905
  content: pipelineString,
8787
8906
  },
8788
8907
  ],
@@ -8807,16 +8926,29 @@
8807
8926
  if (!(shebangLine_1 || '').includes('ptbk')) {
8808
8927
  throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n It seems that you try to parse a book file which has non-standard shebang line for book files:\n Shebang line must contain 'ptbk'\n\n You have:\n ".concat(block(shebangLine_1 || '(empty line)'), "\n\n It should look like this:\n #!/usr/bin/env ptbk\n\n ").concat(block(getPipelineIdentification()), "\n "); }));
8809
8928
  }
8810
- pipelineString = restLines.join('\n');
8929
+ pipelineString = validatePipelineString(restLines.join('\n'));
8811
8930
  }
8812
- pipelineString = removeContentComments(pipelineString);
8931
+ pipelineString = removeMarkdownComments(pipelineString);
8932
+ pipelineString = spaceTrim.spaceTrim(pipelineString);
8933
+ // <- TODO: [😧] `spaceTrim` should preserve discriminated type *(or at lease `PipelineString`)*
8813
8934
  // ==============
8814
8935
  // Note: 1️⃣◽2️⃣ Process flat pipeline
8815
- // TODO: !!!!!!
8816
- // const isMarkdownBeginningWithHeadline =
8817
- // const isMarkdown
8818
- // const isMarkdown
8819
- // const isMarkdown
8936
+ var isMarkdownBeginningWithHeadline = pipelineString.startsWith('# ');
8937
+ var isLastLineReturnStatement = pipelineString.split('\n').pop().split('`').join('').startsWith('->');
8938
+ // TODO: Also (double)check
8939
+ // > const usedCommands
8940
+ // > const isBlocksUsed
8941
+ // > const returnStatementCount
8942
+ var isFlatPipeline = !isMarkdownBeginningWithHeadline && isLastLineReturnStatement;
8943
+ // console.log({ isMarkdownBeginningWithHeadline, isLastLineReturnStatement, isFlatPipeline });
8944
+ if (isFlatPipeline) {
8945
+ var pipelineStringLines = pipelineString.split('\n');
8946
+ var returnStatement_1 = pipelineStringLines.pop();
8947
+ var prompt_1 = spaceTrim.spaceTrim(pipelineStringLines.join('\n'));
8948
+ pipelineString = validatePipelineString(spaceTrim.spaceTrim(function (block) { return "\n # ".concat(DEFAULT_BOOK_TITLE, "\n\n ## Prompt\n\n ```\n ").concat(block(prompt_1), "\n ```\n\n ").concat(returnStatement_1, "\n "); }));
8949
+ // <- TODO: Maybe use book` notation
8950
+ // console.log(pipelineString);
8951
+ }
8820
8952
  // ==============
8821
8953
  // Note: 1️⃣◽3️⃣ Parse the markdown
8822
8954
  pipelineString = flattenMarkdown(pipelineString) /* <- Note: [🥞] */;
@@ -9208,14 +9340,14 @@
9208
9340
  // =============================================================
9209
9341
  return exportJson({
9210
9342
  name: 'pipelineJson',
9211
- message: "Result of `precompilePipeline`",
9343
+ message: "Result of `parsePipeline`",
9212
9344
  order: ORDER_OF_PIPELINE_JSON,
9213
9345
  value: __assign({ formfactorName: 'GENERIC' }, $pipelineJson),
9214
9346
  });
9215
9347
  }
9216
9348
  /**
9217
9349
  * TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
9218
- * TODO: [main] !!!! Warn if used only sync version
9350
+ * TODO: [main] !!4 Warn if used only sync version
9219
9351
  * TODO: [🚞] Report here line/column of error
9220
9352
  * TODO: Use spaceTrim more effectively
9221
9353
  * TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
@@ -9228,10 +9360,7 @@
9228
9360
  /**
9229
9361
  * Compile pipeline from string (markdown) format to JSON format
9230
9362
  *
9231
- * Note: There are 3 similar functions:
9232
- * - `compilePipeline` **(preferred)** - which propperly compiles the promptbook and use embedding for external knowledge
9233
- * - `precompilePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
9234
- * - `preparePipeline` - just one step in the compilation process
9363
+ * @see https://github.com/webgptorg/promptbook/discussions/196
9235
9364
  *
9236
9365
  * Note: This function does not validate logic of the pipeline only the parsing
9237
9366
  * Note: This function acts as compilation process
@@ -9249,7 +9378,7 @@
9249
9378
  return __generator(this, function (_a) {
9250
9379
  switch (_a.label) {
9251
9380
  case 0:
9252
- pipelineJson = precompilePipeline(pipelineString);
9381
+ pipelineJson = parsePipeline(pipelineString);
9253
9382
  if (!(tools !== undefined && tools.llm !== undefined)) return [3 /*break*/, 2];
9254
9383
  return [4 /*yield*/, preparePipeline(pipelineJson, tools, options || {
9255
9384
  rootDirname: null,
@@ -9258,7 +9387,7 @@
9258
9387
  pipelineJson = _a.sent();
9259
9388
  _a.label = 2;
9260
9389
  case 2:
9261
- // Note: No need to use `$exportJson` because `precompilePipeline` and `preparePipeline` already do that
9390
+ // Note: No need to use `$exportJson` because `parsePipeline` and `preparePipeline` already do that
9262
9391
  return [2 /*return*/, pipelineJson];
9263
9392
  }
9264
9393
  });
@@ -9609,6 +9738,25 @@
9609
9738
  * TODO: [®] DRY Register logic
9610
9739
  */
9611
9740
 
9741
+ /**
9742
+ * Determines if the given path is a root path.
9743
+ *
9744
+ * Note: This does not check if the file exists only if the path is valid
9745
+ * @public exported from `@promptbook/utils`
9746
+ */
9747
+ function isRootPath(value) {
9748
+ if (value === '/') {
9749
+ return true;
9750
+ }
9751
+ if (/^[A-Z]:\\$/i.test(value)) {
9752
+ return true;
9753
+ }
9754
+ return false;
9755
+ }
9756
+ /**
9757
+ * TODO: [🍏] Make for MacOS paths
9758
+ */
9759
+
9612
9760
  /**
9613
9761
  * @@@
9614
9762
  *
@@ -9623,17 +9771,45 @@
9623
9771
  * @public exported from `@promptbook/node`
9624
9772
  */
9625
9773
  function $provideLlmToolsConfigurationFromEnv() {
9626
- if (!$isRunningInNode()) {
9627
- throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9628
- }
9629
- dotenv__namespace.config();
9630
- // TODO: Walk to the root of the project and find the nearest `.env` file
9631
- // @see https://collboard.fra1.cdn.digitaloceanspaces.com/usercontent/education/image/png/1/2/ad/image.png
9632
- var llmToolsConfiguration = $llmToolsMetadataRegister
9633
- .list()
9634
- .map(function (metadata) { return metadata.createConfigurationFromEnv(process.env); })
9635
- .filter(function (configuration) { return configuration !== null; });
9636
- return llmToolsConfiguration;
9774
+ return __awaiter(this, void 0, void 0, function () {
9775
+ var rootDirname, i, envFilename, llmToolsConfiguration;
9776
+ return __generator(this, function (_a) {
9777
+ switch (_a.label) {
9778
+ case 0:
9779
+ if (!$isRunningInNode()) {
9780
+ throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9781
+ }
9782
+ rootDirname = process.cwd();
9783
+ i = 0;
9784
+ _a.label = 1;
9785
+ case 1:
9786
+ if (!(i < LOOP_LIMIT)) return [3 /*break*/, 4];
9787
+ envFilename = path.join(rootDirname, '.env' /* <- TODO: [🕝] Make here more candidates */);
9788
+ return [4 /*yield*/, isFileExisting(envFilename, $provideFilesystemForNode())];
9789
+ case 2:
9790
+ // console.log({ rootDirname, envFilename });
9791
+ if (_a.sent()) {
9792
+ dotenv__namespace.config({ path: envFilename });
9793
+ return [3 /*break*/, 4];
9794
+ }
9795
+ if (isRootPath(rootDirname)) {
9796
+ return [3 /*break*/, 4];
9797
+ }
9798
+ // Note: If the directory does not exist, try the parent directory
9799
+ rootDirname = path.join(rootDirname, '..');
9800
+ _a.label = 3;
9801
+ case 3:
9802
+ i++;
9803
+ return [3 /*break*/, 1];
9804
+ case 4:
9805
+ llmToolsConfiguration = $llmToolsMetadataRegister
9806
+ .list()
9807
+ .map(function (metadata) { return metadata.createConfigurationFromEnv(process.env); })
9808
+ .filter(function (configuration) { return configuration !== null; });
9809
+ return [2 /*return*/, llmToolsConfiguration];
9810
+ }
9811
+ });
9812
+ });
9637
9813
  }
9638
9814
  /**
9639
9815
  * TODO: [🧠][🪁] Maybe do allow to do auto-install if package not registered and not found
@@ -9856,15 +10032,28 @@
9856
10032
  */
9857
10033
  function $provideLlmToolsFromEnv(options) {
9858
10034
  if (options === void 0) { options = {}; }
9859
- if (!$isRunningInNode()) {
9860
- throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9861
- }
9862
- var configuration = $provideLlmToolsConfigurationFromEnv();
9863
- if (configuration.length === 0) {
9864
- // TODO: [🥃]
9865
- throw new Error(spaceTrim__default["default"](function (block) { return "\n No LLM tools found in the environment\n\n Please set one of environment variables:\n - OPENAI_API_KEY\n - ANTHROPIC_CLAUDE_API_KEY\n\n ".concat(block($registeredLlmToolsMessage()), "}\n "); }));
9866
- }
9867
- return createLlmToolsFromConfiguration(configuration, options);
10035
+ return __awaiter(this, void 0, void 0, function () {
10036
+ var configuration;
10037
+ return __generator(this, function (_a) {
10038
+ switch (_a.label) {
10039
+ case 0:
10040
+ if (!$isRunningInNode()) {
10041
+ throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
10042
+ }
10043
+ return [4 /*yield*/, $provideLlmToolsConfigurationFromEnv()];
10044
+ case 1:
10045
+ configuration = _a.sent();
10046
+ if (configuration.length === 0) {
10047
+ if ($llmToolsMetadataRegister.list().length === 0) {
10048
+ throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n No LLM tools registered, this is probably a bug in the Promptbook library\n\n ".concat(block($registeredLlmToolsMessage()), "}\n "); }));
10049
+ }
10050
+ // TODO: [🥃]
10051
+ throw new Error(spaceTrim__default["default"](function (block) { return "\n No LLM tools found in the environment\n\n ".concat(block($registeredLlmToolsMessage()), "}\n "); }));
10052
+ }
10053
+ return [2 /*return*/, createLlmToolsFromConfiguration(configuration, options)];
10054
+ }
10055
+ });
10056
+ });
9868
10057
  }
9869
10058
  /**
9870
10059
  * TODO: @@@ write `$provideLlmToolsFromEnv` vs `$provideLlmToolsConfigurationFromEnv` vs `createLlmToolsFromConfiguration`
@@ -9933,23 +10122,31 @@
9933
10122
  */
9934
10123
 
9935
10124
  /**
9936
- * Extracts code block from markdown.
10125
+ * Detects if the code is running in a browser environment in main thread (Not in a web worker)
9937
10126
  *
9938
- * - When there are multiple or no code blocks the function throws a `ParseError`
10127
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
9939
10128
  *
9940
- * Note: There are multiple simmilar function:
9941
- * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
9942
- * - `extractJsonBlock` extracts exactly one valid JSON code block
9943
- * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
9944
- * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
10129
+ * @public exported from `@promptbook/utils`
10130
+ */
10131
+ new Function("\n try {\n return this === window;\n } catch (e) {\n return false;\n }\n");
10132
+
10133
+ /**
10134
+ * Detects if the code is running in jest environment
9945
10135
  *
9946
- * @public exported from `@promptbook/markdown-utils`
9947
- * @throws {ParseError} if there is not exactly one code block in the markdown
10136
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10137
+ *
10138
+ * @public exported from `@promptbook/utils`
9948
10139
  */
9949
- function extractBlock(markdown) {
9950
- var content = extractOneBlockFromMarkdown(markdown).content;
9951
- return content;
9952
- }
10140
+ new Function("\n try {\n return process.env.JEST_WORKER_ID !== undefined;\n } catch (e) {\n return false;\n }\n");
10141
+
10142
+ /**
10143
+ * Detects if the code is running in a web worker
10144
+ *
10145
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10146
+ *
10147
+ * @public exported from `@promptbook/utils`
10148
+ */
10149
+ new Function("\n try {\n if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {\n return true;\n } else {\n return false;\n }\n } catch (e) {\n return false;\n }\n");
9953
10150
 
9954
10151
  /**
9955
10152
  * Makes first letter of a string uppercase
@@ -9960,6 +10157,21 @@
9960
10157
  return word.substring(0, 1).toLowerCase() + word.substring(1);
9961
10158
  }
9962
10159
 
10160
+ /**
10161
+ * Parses keywords from a string
10162
+ *
10163
+ * @param {string} input
10164
+ * @returns {Set} of keywords without diacritics in lowercase
10165
+ * @public exported from `@promptbook/utils`
10166
+ */
10167
+ function parseKeywordsFromString(input) {
10168
+ var keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
10169
+ .toLowerCase()
10170
+ .split(/[^a-z0-9]+/gs)
10171
+ .filter(function (value) { return value; });
10172
+ return new Set(keywords);
10173
+ }
10174
+
9963
10175
  /**
9964
10176
  * @@@
9965
10177
  *
@@ -10013,20 +10225,39 @@
10013
10225
  return sentence.replace(/\s+/gs, ' ').trim();
10014
10226
  }
10015
10227
 
10228
+ // <- TODO: Auto convert to type `import { ... } from 'type-fest';`
10016
10229
  /**
10017
- * Parses keywords from a string
10230
+ * Tests if the value is [🚉] serializable as JSON
10231
+ *
10232
+ * - Almost all primitives are serializable BUT:
10233
+ * - `undefined` is not serializable
10234
+ * - `NaN` is not serializable
10235
+ * - Objects and arrays are serializable if all their properties are serializable
10236
+ * - Functions are not serializable
10237
+ * - Circular references are not serializable
10238
+ * - `Date` objects are not serializable
10239
+ * - `Map` and `Set` objects are not serializable
10240
+ * - `RegExp` objects are not serializable
10241
+ * - `Error` objects are not serializable
10242
+ * - `Symbol` objects are not serializable
10243
+ * - And much more...
10244
+ *
10018
10245
  *
10019
- * @param {string} input
10020
- * @returns {Set} of keywords without diacritics in lowercase
10021
10246
  * @public exported from `@promptbook/utils`
10022
10247
  */
10023
- function parseKeywordsFromString(input) {
10024
- var keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
10025
- .toLowerCase()
10026
- .split(/[^a-z0-9]+/gs)
10027
- .filter(function (value) { return value; });
10028
- return new Set(keywords);
10248
+ function isSerializableAsJson(value) {
10249
+ try {
10250
+ checkSerializableAsJson({ value: value });
10251
+ return true;
10252
+ }
10253
+ catch (error) {
10254
+ return false;
10255
+ }
10029
10256
  }
10257
+ /**
10258
+ * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
10259
+ * TODO: [🧠][💺] Can be done this on type-level?
10260
+ */
10030
10261
 
10031
10262
  /**
10032
10263
  * Function trimCodeBlock will trim starting and ending code block from the string if it is present.
@@ -10134,6 +10365,25 @@
10134
10365
  * TODO: [🧠] Should this also unwrap the (parenthesis)
10135
10366
  */
10136
10367
 
10368
+ /**
10369
+ * Extracts code block from markdown.
10370
+ *
10371
+ * - When there are multiple or no code blocks the function throws a `ParseError`
10372
+ *
10373
+ * Note: There are multiple simmilar function:
10374
+ * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
10375
+ * - `extractJsonBlock` extracts exactly one valid JSON code block
10376
+ * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
10377
+ * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
10378
+ *
10379
+ * @public exported from `@promptbook/markdown-utils`
10380
+ * @throws {ParseError} if there is not exactly one code block in the markdown
10381
+ */
10382
+ function extractBlock(markdown) {
10383
+ var content = extractOneBlockFromMarkdown(markdown).content;
10384
+ return content;
10385
+ }
10386
+
10137
10387
  /**
10138
10388
  * Does nothing, but preserves the function in the bundle
10139
10389
  * Compiler is tricked into thinking the function is used
@@ -10168,36 +10418,9 @@
10168
10418
  }); })();
10169
10419
  }
10170
10420
  /**
10171
- * TODO: !! [1] This maybe does memory leak
10172
- */
10173
-
10174
- /**
10175
- * Converts anything to string that can be used for debugging and logging
10176
- *
10177
- * @param value String value for logging
10178
- * @private internal util
10421
+ * TODO: Probbably remove in favour of `keepImported`
10422
+ * TODO: [1] This maybe does memory leak
10179
10423
  */
10180
- function unknownToString(value) {
10181
- if (value === undefined) {
10182
- return 'undefined';
10183
- }
10184
- else if (value === null) {
10185
- return 'null';
10186
- }
10187
- else if (['number', 'string', 'boolean'].includes(typeof value)) {
10188
- return typeof value + ' ' + value.toString();
10189
- }
10190
- else if (typeof value === 'object' && Array.isArray(value)) {
10191
- return 'array containing [' + value.map(function (item) { return unknownToString(item); }).join(', ') + ']';
10192
- }
10193
- else if (typeof value === 'object') {
10194
- // TODO: Maybe serialize the object
10195
- return 'object';
10196
- }
10197
- else {
10198
- return 'unknown (Search in promptbook code for [🔹])';
10199
- }
10200
- }
10201
10424
 
10202
10425
  /**
10203
10426
  * ScriptExecutionTools for JavaScript implemented via eval
@@ -10328,7 +10551,7 @@
10328
10551
  case 2:
10329
10552
  result = _a.sent();
10330
10553
  if (typeof result !== 'string') {
10331
- throw new PipelineExecutionError("Script must return a string, but returned ".concat(unknownToString(result)));
10554
+ throw new PipelineExecutionError("Script must return a string, but returned ".concat(valueToString(result)));
10332
10555
  }
10333
10556
  return [3 /*break*/, 4];
10334
10557
  case 3:
@@ -10355,7 +10578,7 @@
10355
10578
  throw error_1;
10356
10579
  case 4:
10357
10580
  if (typeof result !== 'string') {
10358
- throw new PipelineExecutionError("Script must return a string, but ".concat(unknownToString(result)));
10581
+ throw new PipelineExecutionError("Script must return a string, but ".concat(valueToString(result)));
10359
10582
  }
10360
10583
  return [2 /*return*/, result];
10361
10584
  }
@@ -10395,9 +10618,11 @@
10395
10618
  throw new EnvironmentMismatchError('Function `$getExecutionToolsForNode` works only in Node.js environment');
10396
10619
  }
10397
10620
  fs = $provideFilesystemForNode();
10398
- llm = $provideLlmToolsFromEnv(options);
10399
- return [4 /*yield*/, $provideExecutablesForNode(options)];
10621
+ return [4 /*yield*/, $provideLlmToolsFromEnv(options)];
10400
10622
  case 1:
10623
+ llm = _b.sent();
10624
+ return [4 /*yield*/, $provideExecutablesForNode(options)];
10625
+ case 2:
10401
10626
  executables = _b.sent();
10402
10627
  _a = {
10403
10628
  llm: llm,
@@ -10405,7 +10630,7 @@
10405
10630
  executables: executables
10406
10631
  };
10407
10632
  return [4 /*yield*/, $provideScrapersForNode({ fs: fs, llm: llm, executables: executables }, options)];
10408
- case 2:
10633
+ case 3:
10409
10634
  tools = (_a.scrapers = _b.sent(),
10410
10635
  _a.script = [new JavascriptExecutionTools(options)],
10411
10636
  _a);
@@ -10628,7 +10853,7 @@
10628
10853
  */
10629
10854
  function createCollectionFromDirectory(path$1, tools, options) {
10630
10855
  return __awaiter(this, void 0, void 0, function () {
10631
- var makedLibraryFilePath, _a, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, collection;
10856
+ var madeLibraryFilePath, _a, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, rootUrl, collection;
10632
10857
  var _this = this;
10633
10858
  return __generator(this, function (_f) {
10634
10859
  switch (_f.label) {
@@ -10643,18 +10868,18 @@
10643
10868
  throw new EnvironmentMismatchError('Can not create collection without filesystem tools');
10644
10869
  // <- TODO: [🧠] What is the best error type here`
10645
10870
  }
10646
- makedLibraryFilePath = path.join(path$1, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
10871
+ madeLibraryFilePath = path.join(path$1, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
10647
10872
  // <- TODO: [🦒] Allow to override (pass different value into the function)
10648
10873
  , ".json"));
10649
- return [4 /*yield*/, isFileExisting(makedLibraryFilePath, tools.fs)];
10874
+ return [4 /*yield*/, isFileExisting(madeLibraryFilePath, tools.fs)];
10650
10875
  case 3:
10651
10876
  if (!(_f.sent())) ;
10652
10877
  else {
10653
- colors__default["default"].green("(In future, not implemented yet) Using your compiled pipeline collection ".concat(makedLibraryFilePath));
10654
- // TODO: !! Implement;
10878
+ colors__default["default"].green("(In future, not implemented yet) Using your compiled pipeline collection ".concat(madeLibraryFilePath));
10879
+ // TODO: Implement;
10655
10880
  // TODO: [🌗]
10656
10881
  }
10657
- _a = options || {}, _b = _a.isRecursive, isRecursive = _b === void 0 ? true : _b, _c = _a.isVerbose, isVerbose = _c === void 0 ? DEFAULT_IS_VERBOSE : _c, _d = _a.isLazyLoaded, isLazyLoaded = _d === void 0 ? false : _d, _e = _a.isCrashedOnError, isCrashedOnError = _e === void 0 ? true : _e;
10882
+ _a = options || {}, _b = _a.isRecursive, isRecursive = _b === void 0 ? true : _b, _c = _a.isVerbose, isVerbose = _c === void 0 ? DEFAULT_IS_VERBOSE : _c, _d = _a.isLazyLoaded, isLazyLoaded = _d === void 0 ? false : _d, _e = _a.isCrashedOnError, isCrashedOnError = _e === void 0 ? true : _e, rootUrl = _a.rootUrl;
10658
10883
  collection = createCollectionFromPromise(function () { return __awaiter(_this, void 0, void 0, function () {
10659
10884
  var fileNames, collection, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1;
10660
10885
  var e_1, _a;
@@ -10680,34 +10905,35 @@
10680
10905
  });
10681
10906
  collection = new Map();
10682
10907
  _loop_1 = function (fileName) {
10683
- var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, existing, error_1, wrappedErrorMessage;
10684
- return __generator(this, function (_e) {
10685
- switch (_e.label) {
10908
+ var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, _e, pipelineUrl, existing, error_1, wrappedErrorMessage;
10909
+ return __generator(this, function (_f) {
10910
+ switch (_f.label) {
10686
10911
  case 0:
10687
10912
  sourceFile = './' + fileName.split('\\').join('/');
10688
10913
  rootDirname = path.dirname(sourceFile).split('\\').join('/');
10689
- _e.label = 1;
10914
+ _f.label = 1;
10690
10915
  case 1:
10691
- _e.trys.push([1, 8, , 9]);
10916
+ _f.trys.push([1, 8, , 9]);
10692
10917
  pipeline = null;
10693
10918
  if (!fileName.endsWith('.book.md')) return [3 /*break*/, 4];
10919
+ _c = validatePipelineString;
10694
10920
  return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
10695
10921
  case 2:
10696
- pipelineString = (_e.sent());
10922
+ pipelineString = _c.apply(void 0, [_f.sent()]);
10697
10923
  return [4 /*yield*/, compilePipeline(pipelineString, tools, {
10698
10924
  rootDirname: rootDirname,
10699
10925
  })];
10700
10926
  case 3:
10701
- pipeline = _e.sent();
10927
+ pipeline = _f.sent();
10702
10928
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
10703
10929
  return [3 /*break*/, 7];
10704
10930
  case 4:
10705
10931
  if (!fileName.endsWith('.book.json')) return [3 /*break*/, 6];
10706
- _d = (_c = JSON).parse;
10932
+ _e = (_d = JSON).parse;
10707
10933
  return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
10708
10934
  case 5:
10709
10935
  // TODO: Handle non-valid JSON files
10710
- pipeline = _d.apply(_c, [_e.sent()]);
10936
+ pipeline = _e.apply(_d, [_f.sent()]);
10711
10937
  // TODO: [🌗]
10712
10938
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
10713
10939
  return [3 /*break*/, 7];
@@ -10715,10 +10941,24 @@
10715
10941
  if (isVerbose) {
10716
10942
  console.info(colors__default["default"].gray("Skipped file ".concat(fileName.split('\\').join('/'), " \u2013\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060 Not a book")));
10717
10943
  }
10718
- _e.label = 7;
10944
+ _f.label = 7;
10719
10945
  case 7:
10720
10946
  // ---
10721
10947
  if (pipeline !== null) {
10948
+ if (rootUrl !== undefined) {
10949
+ if (pipeline.pipelineUrl === undefined) {
10950
+ pipelineUrl = rootUrl + '/' + fileName.split('\\').join('/');
10951
+ if (isVerbose) {
10952
+ console.info(colors__default["default"].yellow("Implicitly set pipeline URL to ".concat(pipelineUrl, " from ").concat(fileName
10953
+ .split('\\')
10954
+ .join('/'))));
10955
+ }
10956
+ pipeline = __assign(__assign({}, pipeline), { pipelineUrl: pipelineUrl });
10957
+ }
10958
+ else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
10959
+ throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is not a child of the root URL ").concat(rootUrl, " \uD83C\uDF4F\n\n File:\n ").concat(sourceFile || 'Unknown', "\n\n ")));
10960
+ }
10961
+ }
10722
10962
  // TODO: [👠] DRY
10723
10963
  if (pipeline.pipelineUrl === undefined) {
10724
10964
  if (isVerbose) {
@@ -10750,17 +10990,17 @@
10750
10990
  }
10751
10991
  else {
10752
10992
  existing = collection.get(pipeline.pipelineUrl);
10753
- throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL \"".concat(pipeline.pipelineUrl, "\" is already in the collection \uD83C\uDF4F\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
10993
+ throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is already in the collection \uD83C\uDF4F\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
10754
10994
  }
10755
10995
  }
10756
10996
  }
10757
10997
  return [3 /*break*/, 9];
10758
10998
  case 8:
10759
- error_1 = _e.sent();
10999
+ error_1 = _f.sent();
10760
11000
  if (!(error_1 instanceof Error)) {
10761
11001
  throw error_1;
10762
11002
  }
10763
- wrappedErrorMessage = spaceTrim__default["default"](function (block) { return "\n ".concat(error_1.name, " in pipeline ").concat(fileName.split('\\').join('/'), "\u2060:\n\n Original error message:\n ").concat(block(error_1.message), "\n\n Original stack trace:\n ").concat(block(error_1.stack || ''), "\n\n ---\n\n "); }) + '\n';
11003
+ wrappedErrorMessage = spaceTrim__default["default"](function (block) { return "\n ".concat(error_1.name, " in pipeline ").concat(fileName.split('\\').join('/'), "\u2060:\n\n Original error message:\n ").concat(block(error_1.message), "\n\n Original stack trace:\n ").concat(block(error_1.stack || ''), "\n\n ---\n\n "); }) + '\n';
10764
11004
  if (isCrashedOnError) {
10765
11005
  throw new CollectionError(wrappedErrorMessage);
10766
11006
  }
@@ -10816,40 +11056,6 @@
10816
11056
  * TODO: Maybe move from `@promptbook/node` to `@promptbook/core` as we removes direct dependency on `fs`
10817
11057
  */
10818
11058
 
10819
- // <- TODO: !!!!!!! Auto convert to type `import { ... } from 'type-fest';`
10820
- /**
10821
- * Tests if the value is [🚉] serializable as JSON
10822
- *
10823
- * - Almost all primitives are serializable BUT:
10824
- * - `undefined` is not serializable
10825
- * - `NaN` is not serializable
10826
- * - Objects and arrays are serializable if all their properties are serializable
10827
- * - Functions are not serializable
10828
- * - Circular references are not serializable
10829
- * - `Date` objects are not serializable
10830
- * - `Map` and `Set` objects are not serializable
10831
- * - `RegExp` objects are not serializable
10832
- * - `Error` objects are not serializable
10833
- * - `Symbol` objects are not serializable
10834
- * - And much more...
10835
- *
10836
- *
10837
- * @public exported from `@promptbook/utils`
10838
- */
10839
- function isSerializableAsJson(value) {
10840
- try {
10841
- checkSerializableAsJson({ value: value });
10842
- return true;
10843
- }
10844
- catch (error) {
10845
- return false;
10846
- }
10847
- }
10848
- /**
10849
- * TODO: [🧠][main] !!! In-memory cache of same values to prevent multiple checks
10850
- * TODO: [🧠][💺] Can be done this on type-level?
10851
- */
10852
-
10853
11059
  /**
10854
11060
  * Stringify the PipelineJson with proper formatting
10855
11061
  *
@@ -11199,46 +11405,6 @@
11199
11405
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
11200
11406
  */
11201
11407
 
11202
- /**
11203
- * @@@
11204
- *
11205
- * @public exported from `@promptbook/node`
11206
- */
11207
- var wizzard = {
11208
- /**
11209
- * @@@!!!!!!
11210
- */
11211
- execute: function (book, inputParameters, onProgress) {
11212
- return __awaiter(this, void 0, void 0, function () {
11213
- var tools, collection, pipeline, pipelineExecutor, result;
11214
- return __generator(this, function (_a) {
11215
- switch (_a.label) {
11216
- case 0: return [4 /*yield*/, $provideExecutionToolsForNode()];
11217
- case 1:
11218
- tools = _a.sent();
11219
- return [4 /*yield*/, createCollectionFromDirectory('./books', tools)];
11220
- case 2:
11221
- collection = _a.sent();
11222
- return [4 /*yield*/, collection.getPipelineByUrl(book)];
11223
- case 3:
11224
- pipeline = _a.sent();
11225
- pipelineExecutor = createPipelineExecutor({ pipeline: pipeline, tools: tools });
11226
- return [4 /*yield*/, pipelineExecutor(inputParameters, onProgress)];
11227
- case 4:
11228
- result = _a.sent();
11229
- // ▶ Fail if the execution was not successful
11230
- assertsExecutionSuccessful(result);
11231
- // ▶ Return the result
11232
- return [2 /*return*/, result];
11233
- }
11234
- });
11235
- });
11236
- },
11237
- };
11238
- /**
11239
- * TODO: !!!!!! Add to readmes - one markdown here imported in all packages
11240
- */
11241
-
11242
11408
  exports.$execCommand = $execCommand;
11243
11409
  exports.$execCommands = $execCommands;
11244
11410
  exports.$provideExecutablesForNode = $provideExecutablesForNode;
@@ -11251,7 +11417,6 @@
11251
11417
  exports.FileCacheStorage = FileCacheStorage;
11252
11418
  exports.PROMPTBOOK_ENGINE_VERSION = PROMPTBOOK_ENGINE_VERSION;
11253
11419
  exports.createCollectionFromDirectory = createCollectionFromDirectory;
11254
- exports.wizzard = wizzard;
11255
11420
 
11256
11421
  Object.defineProperty(exports, '__esModule', { value: true });
11257
11422