@promptbook/node 0.81.0-8 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +25 -8
  2. package/esm/index.es.js +1947 -1765
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/books/index.d.ts +38 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +12 -4
  6. package/esm/typings/src/_packages/markdown-utils.index.d.ts +2 -2
  7. package/esm/typings/src/_packages/node.index.d.ts +0 -2
  8. package/esm/typings/src/_packages/templates.index.d.ts +2 -2
  9. package/esm/typings/src/_packages/types.index.d.ts +4 -0
  10. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  11. package/esm/typings/src/_packages/wizzard.index.d.ts +44 -0
  12. package/esm/typings/src/cli/cli-commands/make.d.ts +1 -1
  13. package/esm/typings/src/cli/cli-commands/run.d.ts +2 -2
  14. package/esm/typings/src/collection/constructors/createCollectionFromDirectory.d.ts +11 -0
  15. package/esm/typings/src/collection/constructors/createCollectionFromUrl.d.ts +1 -1
  16. package/esm/typings/src/commands/index.d.ts +1 -1
  17. package/esm/typings/src/config.d.ts +3 -3
  18. package/esm/typings/src/conversion/compilePipeline.d.ts +1 -4
  19. package/esm/typings/src/conversion/{precompilePipeline.d.ts → parsePipeline.d.ts} +3 -3
  20. package/esm/typings/src/conversion/prettify/renderPipelineMermaidOptions.d.ts +3 -3
  21. package/esm/typings/src/conversion/validation/validatePipeline.d.ts +7 -7
  22. package/esm/typings/src/errors/utils/getErrorReportUrl.d.ts +1 -1
  23. package/esm/typings/src/execution/PipelineExecutor.d.ts +2 -2
  24. package/esm/typings/src/execution/createPipelineExecutor/10-executePipeline.d.ts +2 -2
  25. package/esm/typings/src/formfactors/generator/GeneratorFormfactorDefinition.d.ts +9 -4
  26. package/esm/typings/src/formfactors/image-generator/ImageGeneratorFormfactorDefinition.d.ts +24 -0
  27. package/esm/typings/src/formfactors/index.d.ts +31 -9
  28. package/esm/typings/src/high-level-abstractions/_common/HighLevelAbstraction.d.ts +1 -1
  29. package/esm/typings/src/high-level-abstractions/index.d.ts +3 -3
  30. package/esm/typings/src/high-level-abstractions/quick-chatbot/QuickChatbotHla.d.ts +3 -0
  31. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  32. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +1 -1
  33. package/esm/typings/src/llm-providers/_common/register/{$provideLlmToolsForCli.d.ts → $provideLlmToolsForWizzardOrCli.d.ts} +2 -2
  34. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  35. package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  36. package/esm/typings/src/llm-providers/anthropic-claude/createAnthropicClaudeExecutionTools.d.ts +2 -2
  37. package/esm/typings/src/llm-providers/anthropic-claude/playground/playground.d.ts +2 -2
  38. package/esm/typings/src/llm-providers/anthropic-claude/register-configuration.d.ts +1 -0
  39. package/esm/typings/src/llm-providers/anthropic-claude/register-constructor.d.ts +2 -0
  40. package/esm/typings/src/llm-providers/azure-openai/register-configuration.d.ts +1 -0
  41. package/esm/typings/src/llm-providers/azure-openai/register-constructor.d.ts +1 -0
  42. package/esm/typings/src/llm-providers/google/register-configuration.d.ts +1 -0
  43. package/esm/typings/src/llm-providers/google/register-constructor.d.ts +1 -0
  44. package/esm/typings/src/llm-providers/openai/playground/playground.d.ts +1 -1
  45. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +2 -0
  46. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +2 -0
  47. package/esm/typings/src/llm-providers/vercel/playground/playground.d.ts +1 -1
  48. package/esm/typings/src/other/templates/getBookTemplates.d.ts +22 -0
  49. package/esm/typings/src/personas/preparePersona.d.ts +4 -4
  50. package/esm/typings/src/pipeline/PipelineString.d.ts +0 -3
  51. package/esm/typings/src/pipeline/book-notation.d.ts +14 -0
  52. package/esm/typings/src/pipeline/isValidPipelineString.d.ts +13 -0
  53. package/esm/typings/src/pipeline/isValidPipelineString.test.d.ts +4 -0
  54. package/esm/typings/src/pipeline/validatePipelineString.d.ts +14 -0
  55. package/esm/typings/src/prepare/isPipelinePrepared.d.ts +3 -1
  56. package/esm/typings/src/prepare/preparePipeline.d.ts +2 -0
  57. package/esm/typings/src/prepare/prepareTasks.d.ts +1 -1
  58. package/esm/typings/src/scrapers/_common/Converter.d.ts +1 -0
  59. package/esm/typings/src/scrapers/_common/Scraper.d.ts +1 -1
  60. package/esm/typings/src/scrapers/_common/ScraperIntermediateSource.d.ts +3 -0
  61. package/esm/typings/src/scrapers/_common/register/ScraperAndConverterMetadata.d.ts +2 -0
  62. package/esm/typings/src/scrapers/_common/utils/scraperFetch.d.ts +3 -0
  63. package/esm/typings/src/scrapers/document/register-constructor.d.ts +1 -0
  64. package/esm/typings/src/scrapers/document/register-metadata.d.ts +1 -0
  65. package/esm/typings/src/scrapers/document-legacy/register-constructor.d.ts +1 -0
  66. package/esm/typings/src/scrapers/document-legacy/register-metadata.d.ts +1 -0
  67. package/esm/typings/src/scrapers/markdown/register-constructor.d.ts +1 -0
  68. package/esm/typings/src/scrapers/markdown/register-metadata.d.ts +1 -0
  69. package/esm/typings/src/scrapers/pdf/PdfScraper.d.ts +1 -0
  70. package/esm/typings/src/scrapers/pdf/createPdfScraper.d.ts +1 -1
  71. package/esm/typings/src/scrapers/pdf/register-constructor.d.ts +1 -0
  72. package/esm/typings/src/scrapers/pdf/register-metadata.d.ts +2 -1
  73. package/esm/typings/src/scrapers/website/createWebsiteScraper.d.ts +3 -1
  74. package/esm/typings/src/scrapers/website/register-constructor.d.ts +1 -0
  75. package/esm/typings/src/scrapers/website/register-metadata.d.ts +1 -0
  76. package/esm/typings/src/scripting/javascript/JavascriptEvalExecutionTools.test.d.ts +1 -1
  77. package/esm/typings/src/scripting/javascript/utils/preserve.d.ts +2 -1
  78. package/esm/typings/src/types/typeAliases.d.ts +16 -2
  79. package/esm/typings/src/utils/markdown/flattenMarkdown.d.ts +1 -1
  80. package/esm/typings/src/utils/markdown/{removeContentComments.d.ts → removeMarkdownComments.d.ts} +2 -2
  81. package/esm/typings/src/utils/organization/$sideEffect.d.ts +9 -0
  82. package/esm/typings/src/utils/serialization/checkSerializableAsJson.d.ts +1 -1
  83. package/esm/typings/src/utils/serialization/isSerializableAsJson.d.ts +2 -2
  84. package/esm/typings/src/utils/validators/filePath/isRootPath.d.ts +12 -0
  85. package/esm/typings/src/utils/validators/filePath/isRootPath.test.d.ts +4 -0
  86. package/esm/typings/src/utils/validators/filePath/isValidFilePath.d.ts +3 -0
  87. package/esm/typings/src/wizzard/$getCompiledBook.d.ts +16 -0
  88. package/esm/typings/src/wizzard/wizzard.d.ts +52 -8
  89. package/package.json +2 -2
  90. package/umd/index.umd.js +1946 -1765
  91. package/umd/index.umd.js.map +1 -1
  92. package/esm/typings/src/other/templates/getBookTemplate.d.ts +0 -21
  93. package/esm/typings/src/scripting/javascript/utils/unknownToString.d.ts +0 -8
  94. /package/esm/typings/src/conversion/{precompilePipeline.test.d.ts → parsePipeline.test.d.ts} +0 -0
  95. /package/esm/typings/src/utils/markdown/{removeContentComments.test.d.ts → removeMarkdownComments.test.d.ts} +0 -0
package/umd/index.umd.js CHANGED
@@ -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-7';
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`
@@ -2458,6 +1917,81 @@
2458
1917
  * - [♨] Are tasks prepared
2459
1918
  */
2460
1919
 
1920
+ /**
1921
+ * Format either small or big number
1922
+ *
1923
+ * @public exported from `@promptbook/utils`
1924
+ */
1925
+ function numberToString(value) {
1926
+ if (value === 0) {
1927
+ return '0';
1928
+ }
1929
+ else if (Number.isNaN(value)) {
1930
+ return VALUE_STRINGS.nan;
1931
+ }
1932
+ else if (value === Infinity) {
1933
+ return VALUE_STRINGS.infinity;
1934
+ }
1935
+ else if (value === -Infinity) {
1936
+ return VALUE_STRINGS.negativeInfinity;
1937
+ }
1938
+ for (var exponent = 0; exponent < 15; exponent++) {
1939
+ var factor = Math.pow(10, exponent);
1940
+ var valueRounded = Math.round(value * factor) / factor;
1941
+ if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
1942
+ return valueRounded.toFixed(exponent);
1943
+ }
1944
+ }
1945
+ return value.toString();
1946
+ }
1947
+
1948
+ /**
1949
+ * Function `valueToString` will convert the given value to string
1950
+ * This is useful and used in the `templateParameters` function
1951
+ *
1952
+ * Note: This function is not just calling `toString` method
1953
+ * It's more complex and can handle this conversion specifically for LLM models
1954
+ * See `VALUE_STRINGS`
1955
+ *
1956
+ * Note: There are 2 similar functions
1957
+ * - `valueToString` converts value to string for LLM models as human-readable string
1958
+ * - `asSerializable` converts value to string to preserve full information to be able to convert it back
1959
+ *
1960
+ * @public exported from `@promptbook/utils`
1961
+ */
1962
+ function valueToString(value) {
1963
+ try {
1964
+ if (value === '') {
1965
+ return VALUE_STRINGS.empty;
1966
+ }
1967
+ else if (value === null) {
1968
+ return VALUE_STRINGS.null;
1969
+ }
1970
+ else if (value === undefined) {
1971
+ return VALUE_STRINGS.undefined;
1972
+ }
1973
+ else if (typeof value === 'string') {
1974
+ return value;
1975
+ }
1976
+ else if (typeof value === 'number') {
1977
+ return numberToString(value);
1978
+ }
1979
+ else if (value instanceof Date) {
1980
+ return value.toISOString();
1981
+ }
1982
+ else {
1983
+ return JSON.stringify(value);
1984
+ }
1985
+ }
1986
+ catch (error) {
1987
+ if (!(error instanceof Error)) {
1988
+ throw error;
1989
+ }
1990
+ console.error(error);
1991
+ return VALUE_STRINGS.unserializable;
1992
+ }
1993
+ }
1994
+
2461
1995
  /**
2462
1996
  * Serializes an error into a [🚉] JSON-serializable object
2463
1997
  *
@@ -2476,47 +2010,175 @@
2476
2010
  }
2477
2011
 
2478
2012
  /**
2479
- * 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
2480
2014
  *
2481
- * @param script from which to extract the variables
2482
- * @returns the list of variable names
2483
- * @throws {ParseError} if the script is invalid
2484
- * @public exported from `@promptbook/utils` <- Note: [👖] This is usable elsewhere than in Promptbook, so keeping in utils
2015
+ * @public exported from `@promptbook/core`
2485
2016
  */
2486
- function extractVariablesFromScript(script) {
2487
- var variables = new Set();
2488
- var originalScript = script;
2489
- script = "(()=>{".concat(script, "})()");
2490
- try {
2491
- for (var i = 0; i < 100 /* <- TODO: This limit to configuration */; i++)
2492
- try {
2493
- eval(script);
2494
- }
2495
- catch (error) {
2496
- if (!(error instanceof ReferenceError)) {
2497
- throw error;
2498
- }
2499
- /*
2500
- Note: Parsing the error
2501
- 🌟 Most devices:
2502
- [PipelineUrlError: thing is not defined]
2503
-
2504
- 🍏 iPhone`s Safari:
2505
- [PipelineUrlError: Can't find variable: thing]
2506
- */
2507
- var variableName = undefined;
2508
- if (error.message.startsWith("Can't")) {
2509
- // 🍏 Case
2510
- variableName = error.message.split(' ').pop();
2511
- }
2512
- else {
2513
- // 🌟 Case
2514
- variableName = error.message.split(' ').shift();
2515
- }
2516
- if (variableName === undefined) {
2517
- throw error;
2518
- }
2519
- if (script.includes(variableName + '(')) {
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
+ });
2038
+ /**
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
2066
+ */
2067
+
2068
+ /**
2069
+ * Function `addUsage` will add multiple usages into one
2070
+ *
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 + '(')) {
2520
2182
  script = "const ".concat(variableName, " = ()=>'';") + script;
2521
2183
  }
2522
2184
  else {
@@ -2529,7 +2191,7 @@
2529
2191
  if (!(error instanceof Error)) {
2530
2192
  throw error;
2531
2193
  }
2532
- 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)
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)
2533
2195
  .map(function (variableName, i) { return "".concat(i + 1, ") ").concat(variableName); })
2534
2196
  .join('\n'), "\n\n\n The script:\n\n ```javascript\n ").concat(block(originalScript), "\n ```\n "); }));
2535
2197
  }
@@ -2809,27 +2471,6 @@
2809
2471
  * TODO: [🏢] Allow to expect something inside CSV objects and other formats
2810
2472
  */
2811
2473
 
2812
- /**
2813
- * Function isValidJsonString will tell you if the string is valid JSON or not
2814
- *
2815
- * @public exported from `@promptbook/utils`
2816
- */
2817
- function isValidJsonString(value /* <- [👨‍⚖️] */) {
2818
- try {
2819
- JSON.parse(value);
2820
- return true;
2821
- }
2822
- catch (error) {
2823
- if (!(error instanceof Error)) {
2824
- throw error;
2825
- }
2826
- if (error.message.includes('Unexpected token')) {
2827
- return false;
2828
- }
2829
- return false;
2830
- }
2831
- }
2832
-
2833
2474
  /**
2834
2475
  * Definition for JSON format
2835
2476
  *
@@ -2900,137 +2541,429 @@
2900
2541
  mappedLines = _a.sent();
2901
2542
  return [2 /*return*/, mappedLines.join('\n')];
2902
2543
  }
2903
- });
2904
- });
2905
- },
2906
- },
2907
- // <- TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2908
- ],
2909
- };
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];
2888
+ }
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
+ }());
2910
2911
  /**
2911
- * TODO: [1] Make type for XML Text and Schema
2912
- * TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2913
- * TODO: [🍓] In `TextFormatDefinition` implement simple `isValid`
2914
- * TODO: [🍓] In `TextFormatDefinition` implement partial `canBeValid`
2915
- * TODO: [🍓] In `TextFormatDefinition` implement `heal
2916
- * TODO: [🍓] In `TextFormatDefinition` implement `subvalueDefinitions`
2917
- * TODO: [🏢] Allow to expect something inside each item of list and other formats
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
2918
2915
  */
2919
2916
 
2920
2917
  /**
2921
- * Definition for XML format
2918
+ * Joins multiple LLM Execution Tools into one
2922
2919
  *
2923
- * @private still in development [🏢]
2924
- */
2925
- var XmlFormatDefinition = {
2926
- formatName: 'XML',
2927
- mimeType: 'application/xml',
2928
- isValid: function (value, settings, schema) {
2929
- return true;
2930
- },
2931
- canBeValid: function (partialValue, settings, schema) {
2932
- return true;
2933
- },
2934
- heal: function (value, settings, schema) {
2935
- throw new Error('Not implemented');
2936
- },
2937
- subvalueDefinitions: [],
2938
- };
2939
- /**
2940
- * TODO: [🧠] Maybe propper instance of object
2941
- * TODO: [0] Make string_serialized_xml
2942
- * TODO: [1] Make type for XML Settings and Schema
2943
- * TODO: [🧠] What to use for validating XMLs - XSD,...
2944
- * TODO: [🍓] In `XmlFormatDefinition` implement simple `isValid`
2945
- * TODO: [🍓] In `XmlFormatDefinition` implement partial `canBeValid`
2946
- * TODO: [🍓] In `XmlFormatDefinition` implement `heal
2947
- * TODO: [🍓] In `XmlFormatDefinition` implement `subvalueDefinitions`
2948
- * TODO: [🏢] Allow to expect something inside XML and other formats
2949
- */
2950
-
2951
- /**
2952
- * Definitions for all formats supported by Promptbook
2920
+ * @returns {LlmExecutionTools} Single wrapper for multiple LlmExecutionTools
2953
2921
  *
2954
- * @private internal index of `...` <- TODO [🏢]
2955
- */
2956
- var FORMAT_DEFINITIONS = [
2957
- JsonFormatDefinition,
2958
- XmlFormatDefinition,
2959
- TextFormatDefinition,
2960
- CsvFormatDefinition,
2961
- ];
2962
- /**
2963
- * Note: [💞] Ignore a discrepancy between file name and entity name
2964
- */
2965
-
2966
- /**
2967
- * Maps available parameters to expected parameters
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
2968
2926
  *
2969
- * The strategy is:
2970
- * 1) @@@
2971
- * 2) @@@
2972
2927
  *
2973
- * @throws {PipelineExecutionError} @@@
2974
- * @private within the repository used in `createPipelineExecutor`
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`
2975
2931
  */
2976
- function mapAvailableToExpectedParameters(options) {
2977
- var e_1, _a;
2978
- var expectedParameters = options.expectedParameters, availableParameters = options.availableParameters;
2979
- var availableParametersNames = new Set(Object.keys(availableParameters));
2980
- var expectedParameterNames = new Set(Object.keys(expectedParameters));
2981
- var mappedParameters = {};
2982
- try {
2983
- // Phase 1️⃣: Matching mapping
2984
- for (var _b = __values(Array.from(union(availableParametersNames, expectedParameterNames))), _c = _b.next(); !_c.done; _c = _b.next()) {
2985
- var parameterName = _c.value;
2986
- // Situation: Parameter is available and expected
2987
- if (availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
2988
- mappedParameters[parameterName] = availableParameters[parameterName];
2989
- // <- Note: [👩‍👩‍👧] Maybe detect parameter collision here?
2990
- availableParametersNames.delete(parameterName);
2991
- expectedParameterNames.delete(parameterName);
2992
- }
2993
- // Situation: Parameter is available but NOT expected
2994
- else if (availableParametersNames.has(parameterName) && !expectedParameterNames.has(parameterName)) {
2995
- // [🐱‍👤] Do not pass this parameter to prompt - Maybe use it non-matching mapping
2996
- }
2997
- // Situation: Parameter is NOT available BUT expected
2998
- else if (!availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
2999
- // Do nothing here - this will be maybe fixed in the non-matching mapping
3000
- }
3001
- }
3002
- }
3003
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
3004
- finally {
3005
- try {
3006
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
3007
- }
3008
- finally { if (e_1) throw e_1.error; }
3009
- }
3010
- if (expectedParameterNames.size === 0) {
3011
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
3012
- Object.freeze(mappedParameters);
3013
- return mappedParameters;
3014
- }
3015
- // Phase 2️⃣: Non-matching mapping
3016
- if (expectedParameterNames.size !== availableParametersNames.size) {
3017
- 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)
3018
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3019
- .join('\n')), "\n\n Expected parameters which can not be mapped:\n ").concat(block(Array.from(expectedParameterNames)
3020
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3021
- .join('\n')), "\n\n Remaining available parameters:\n ").concat(block(Array.from(availableParametersNames)
3022
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3023
- .join('\n')), "\n\n "); }));
2932
+ function joinLlmExecutionTools() {
2933
+ var llmExecutionTools = [];
2934
+ for (var _i = 0; _i < arguments.length; _i++) {
2935
+ llmExecutionTools[_i] = arguments[_i];
3024
2936
  }
3025
- var expectedParameterNamesArray = Array.from(expectedParameterNames);
3026
- var availableParametersNamesArray = Array.from(availableParametersNames);
3027
- for (var i = 0; i < expectedParameterNames.size; i++) {
3028
- 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
+ */
3029
2961
  }
3030
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
3031
- Object.freeze(mappedParameters);
3032
- return mappedParameters;
2962
+ return new (MultipleLlmExecutionTools.bind.apply(MultipleLlmExecutionTools, __spreadArray([void 0], __read(llmExecutionTools), false)))();
3033
2963
  }
2964
+ /**
2965
+ * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
2966
+ */
3034
2967
 
3035
2968
  /**
3036
2969
  * Extracts all code blocks from markdown.
@@ -3148,98 +3081,23 @@
3148
3081
  * TODO: [🏢] Make this logic part of `JsonFormatDefinition` or `isValidJsonString`
3149
3082
  */
3150
3083
 
3151
- /**
3152
- * Takes an item or an array of items and returns an array of items
3153
- *
3154
- * 1) Any item except array and undefined returns array with that one item (also null)
3155
- * 2) Undefined returns empty array
3156
- * 3) Array returns itself
3157
- *
3158
- * @private internal utility
3159
- */
3160
- function arrayableToArray(input) {
3161
- if (input === undefined) {
3162
- return [];
3163
- }
3164
- if (input instanceof Array) {
3165
- return input;
3166
- }
3167
- return [input];
3168
- }
3169
-
3170
- /**
3171
- * Format either small or big number
3172
- *
3173
- * @public exported from `@promptbook/utils`
3174
- */
3175
- function numberToString(value) {
3176
- if (value === 0) {
3177
- return '0';
3178
- }
3179
- else if (Number.isNaN(value)) {
3180
- return VALUE_STRINGS.nan;
3181
- }
3182
- else if (value === Infinity) {
3183
- return VALUE_STRINGS.infinity;
3184
- }
3185
- else if (value === -Infinity) {
3186
- return VALUE_STRINGS.negativeInfinity;
3187
- }
3188
- for (var exponent = 0; exponent < 15; exponent++) {
3189
- var factor = Math.pow(10, exponent);
3190
- var valueRounded = Math.round(value * factor) / factor;
3191
- if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
3192
- return valueRounded.toFixed(exponent);
3193
- }
3194
- }
3195
- return value.toString();
3196
- }
3197
-
3198
- /**
3199
- * Function `valueToString` will convert the given value to string
3200
- * This is useful and used in the `templateParameters` function
3201
- *
3202
- * Note: This function is not just calling `toString` method
3203
- * It's more complex and can handle this conversion specifically for LLM models
3204
- * See `VALUE_STRINGS`
3084
+ /**
3085
+ * Takes an item or an array of items and returns an array of items
3205
3086
  *
3206
- * Note: There are 2 similar functions
3207
- * - `valueToString` converts value to string for LLM models as human-readable string
3208
- * - `asSerializable` converts value to string to preserve full information to be able to convert it back
3087
+ * 1) Any item except array and undefined returns array with that one item (also null)
3088
+ * 2) Undefined returns empty array
3089
+ * 3) Array returns itself
3209
3090
  *
3210
- * @public exported from `@promptbook/utils`
3091
+ * @private internal utility
3211
3092
  */
3212
- function valueToString(value) {
3213
- try {
3214
- if (value === '') {
3215
- return VALUE_STRINGS.empty;
3216
- }
3217
- else if (value === null) {
3218
- return VALUE_STRINGS.null;
3219
- }
3220
- else if (value === undefined) {
3221
- return VALUE_STRINGS.undefined;
3222
- }
3223
- else if (typeof value === 'string') {
3224
- return value;
3225
- }
3226
- else if (typeof value === 'number') {
3227
- return numberToString(value);
3228
- }
3229
- else if (value instanceof Date) {
3230
- return value.toISOString();
3231
- }
3232
- else {
3233
- return JSON.stringify(value);
3234
- }
3093
+ function arrayableToArray(input) {
3094
+ if (input === undefined) {
3095
+ return [];
3235
3096
  }
3236
- catch (error) {
3237
- if (!(error instanceof Error)) {
3238
- throw error;
3239
- }
3240
- console.error(error);
3241
- return VALUE_STRINGS.unserializable;
3097
+ if (input instanceof Array) {
3098
+ return input;
3242
3099
  }
3100
+ return [input];
3243
3101
  }
3244
3102
 
3245
3103
  /**
@@ -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
  }))];
@@ -4618,7 +4478,10 @@
4618
4478
  finally { if (e_2) throw e_2.error; }
4619
4479
  return [7 /*endfinally*/];
4620
4480
  case 19:
4621
- parametersToPass = inputParameters;
4481
+ parametersToPass = Object.fromEntries(Object.entries(inputParameters).map(function (_a) {
4482
+ var _b = __read(_a, 2), key = _b[0], value = _b[1];
4483
+ return [key, valueToString(value)];
4484
+ }));
4622
4485
  _g.label = 20;
4623
4486
  case 20:
4624
4487
  _g.trys.push([20, 25, , 28]);
@@ -4853,6 +4716,169 @@
4853
4716
  * TODO: [🐚] Change onProgress to object that represents the running execution, can be subscribed via RxJS to and also awaited
4854
4717
  */
4855
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
+
4856
4882
  /**
4857
4883
  * Prepares the persona for the pipeline
4858
4884
  *
@@ -4915,10 +4941,10 @@
4915
4941
  });
4916
4942
  }
4917
4943
  /**
4918
- * TODO: [🔃][main] !! If the persona was prepared with different version or different set of models, prepare it once again
4919
- * TODO: [🏢] !! Check validity of `modelName` in pipeline
4920
- * TODO: [🏢] !! Check validity of `systemMessage` in pipeline
4921
- * 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
4922
4948
  */
4923
4949
 
4924
4950
  /**
@@ -5352,21 +5378,44 @@
5352
5378
  if (typeof filename !== 'string') {
5353
5379
  return false;
5354
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
+ }
5355
5388
  var filenameSlashes = filename.split('\\').join('/');
5356
5389
  // Absolute Unix path: /hello.txt
5357
5390
  if (/^(\/)/i.test(filenameSlashes)) {
5391
+ // console.log(filename, 'Absolute Unix path: /hello.txt');
5358
5392
  return true;
5359
5393
  }
5360
5394
  // Absolute Windows path: /hello.txt
5361
5395
  if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
5396
+ // console.log(filename, 'Absolute Windows path: /hello.txt');
5362
5397
  return true;
5363
5398
  }
5364
5399
  // Relative path: ./hello.txt
5365
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');
5366
5412
  return true;
5367
5413
  }
5368
5414
  return false;
5369
5415
  }
5416
+ /**
5417
+ * TODO: [🍏] Implement for MacOs
5418
+ */
5370
5419
 
5371
5420
  /**
5372
5421
  * The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
@@ -5391,6 +5440,9 @@
5391
5440
  }
5392
5441
  });
5393
5442
  }); };
5443
+ /**
5444
+ * TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
5445
+ */
5394
5446
 
5395
5447
  /**
5396
5448
  * @@@
@@ -5458,7 +5510,7 @@
5458
5510
  },
5459
5511
  }];
5460
5512
  case 2:
5461
- if (!(isValidFilePath(sourceContent) || /\.[a-z]{1,10}$/i.exec(sourceContent))) return [3 /*break*/, 4];
5513
+ if (!isValidFilePath(sourceContent)) return [3 /*break*/, 4];
5462
5514
  if (tools.fs === undefined) {
5463
5515
  throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
5464
5516
  // <- TODO: [🧠] What is the best error type here`
@@ -5473,7 +5525,7 @@
5473
5525
  return [4 /*yield*/, isFileExisting(filename_1, tools.fs)];
5474
5526
  case 3:
5475
5527
  if (!(_f.sent())) {
5476
- 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 "); }));
5477
5529
  }
5478
5530
  // TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
5479
5531
  return [2 /*return*/, {
@@ -5586,7 +5638,7 @@
5586
5638
  partialPieces = __spreadArray([], __read(partialPiecesUnchecked), false);
5587
5639
  return [2 /*return*/, "break"];
5588
5640
  }
5589
- 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
5590
5642
  .split('\n')
5591
5643
  .map(function (line) { return "> ".concat(line); })
5592
5644
  .join('\n')), "\n\n ").concat(block($registeredScrapersMessage(scrapers)), "\n\n\n "); }));
@@ -5624,7 +5676,7 @@
5624
5676
  return [7 /*endfinally*/];
5625
5677
  case 9:
5626
5678
  if (partialPieces === null) {
5627
- 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
5628
5680
  .split('\n')
5629
5681
  .map(function (line) { return "> ".concat(line); })
5630
5682
  .join('\n')), "\n\n No scraper found for the mime type \"").concat(sourceHandler.mimeType, "\"\n\n ").concat(block($registeredScrapersMessage(scrapers)), "\n\n\n "); }));
@@ -5715,7 +5767,7 @@
5715
5767
  * TODO: [😂] Adding knowledge should be convert to async high-level abstractions, simmilar thing with expectations to sync high-level abstractions
5716
5768
  * TODO: [🧠] Add context to each task (if missing)
5717
5769
  * TODO: [🧠] What is better name `prepareTask` or `prepareTaskAndParameters`
5718
- * TODO: [♨][main] !!! Prepare index the examples and maybe tasks
5770
+ * TODO: [♨][main] !!3 Prepare index the examples and maybe tasks
5719
5771
  * TODO: Write tests for `preparePipeline`
5720
5772
  * TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
5721
5773
  * TODO: [🧊] In future one preparation can take data from previous preparation and save tokens and time
@@ -5725,6 +5777,8 @@
5725
5777
  /**
5726
5778
  * Prepare pipeline from string (markdown) format to JSON format
5727
5779
  *
5780
+ * @see https://github.com/webgptorg/promptbook/discussions/196
5781
+ *
5728
5782
  * Note: This function does not validate logic of the pipeline
5729
5783
  * Note: This function acts as part of compilation process
5730
5784
  * Note: When the pipeline is already prepared, it returns the same pipeline
@@ -5737,16 +5791,17 @@
5737
5791
  <- TODO: [🧠][🪑] `promptbookVersion` */
5738
5792
  knowledgeSources /*
5739
5793
  <- TODO: [🧊] `knowledgePieces` */, personas /*
5740
- <- 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;
5741
5796
  var _this = this;
5742
- return __generator(this, function (_c) {
5743
- switch (_c.label) {
5797
+ return __generator(this, function (_e) {
5798
+ switch (_e.label) {
5744
5799
  case 0:
5745
5800
  if (isPipelinePrepared(pipeline)) {
5746
5801
  return [2 /*return*/, pipeline];
5747
5802
  }
5748
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;
5749
- 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;
5750
5805
  if (tools === undefined || tools.llm === undefined) {
5751
5806
  throw new MissingToolsError('LLM tools are required for preparing the pipeline');
5752
5807
  }
@@ -5764,6 +5819,33 @@
5764
5819
  // <- TODO: [🧊]
5765
5820
  currentPreparation,
5766
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:
5767
5849
  preparedPersonas = new Array(personas.length);
5768
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 () {
5769
5851
  var modelRequirements, preparedPersona;
@@ -5782,12 +5864,12 @@
5782
5864
  }
5783
5865
  });
5784
5866
  }); })];
5785
- case 1:
5786
- _c.sent();
5867
+ case 4:
5868
+ _e.sent();
5787
5869
  knowledgeSourcesPrepared = knowledgeSources.map(function (source) { return (__assign(__assign({}, source), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
5788
5870
  return [4 /*yield*/, prepareKnowledgePieces(knowledgeSources /* <- TODO: [🧊] {knowledgeSources, knowledgePieces} */, __assign(__assign({}, tools), { llm: llmToolsWithUsage }), __assign(__assign({}, options), { rootDirname: rootDirname, maxParallelCount: maxParallelCount /* <- TODO: [🪂] */, isVerbose: isVerbose }))];
5789
- case 2:
5790
- partialknowledgePiecesPrepared = _c.sent();
5871
+ case 5:
5872
+ partialknowledgePiecesPrepared = _e.sent();
5791
5873
  knowledgePiecesPrepared = partialknowledgePiecesPrepared.map(function (piece) { return (__assign(__assign({}, piece), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
5792
5874
  return [4 /*yield*/, prepareTasks({
5793
5875
  parameters: parameters,
@@ -5798,8 +5880,8 @@
5798
5880
  maxParallelCount: maxParallelCount /* <- TODO: [🪂] */,
5799
5881
  isVerbose: isVerbose,
5800
5882
  })];
5801
- case 3:
5802
- tasksPrepared = (_c.sent()).tasksPrepared;
5883
+ case 6:
5884
+ tasksPrepared = (_e.sent()).tasksPrepared;
5803
5885
  // ----- /Tasks preparation -----
5804
5886
  // TODO: [😂] Use here all `AsyncHighLevelAbstraction`
5805
5887
  // Note: Count total usage
@@ -5810,7 +5892,7 @@
5810
5892
  order: ORDER_OF_PIPELINE_JSON,
5811
5893
  value: __assign(__assign({}, pipeline), {
5812
5894
  // <- TODO: Probbably deeply clone the pipeline because `$exportJson` freezes the subobjects
5813
- knowledgeSources: knowledgeSourcesPrepared, knowledgePieces: knowledgePiecesPrepared, tasks: __spreadArray([], __read(tasksPrepared), false),
5895
+ title: title, knowledgeSources: knowledgeSourcesPrepared, knowledgePieces: knowledgePiecesPrepared, tasks: __spreadArray([], __read(tasksPrepared), false),
5814
5896
  // <- TODO: [🪓] Here should be no need for spreading new array, just ` tasks: tasksPrepared`
5815
5897
  personas: preparedPersonas, preparations: __spreadArray([], __read(preparations), false) }),
5816
5898
  })];
@@ -5905,7 +5987,7 @@
5905
5987
  if (sourceContent === '') {
5906
5988
  throw new ParseError("Source is not defined");
5907
5989
  }
5908
- // 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`
5909
5991
  if (sourceContent.startsWith('http://')) {
5910
5992
  throw new ParseError("Source is not secure");
5911
5993
  }
@@ -6077,7 +6159,7 @@
6077
6159
  expectResultingParameterName();
6078
6160
  var parameter = $pipelineJson.parameters.find(function (param) { return param.name === $taskJson.resultingParameterName; });
6079
6161
  if (parameter === undefined) {
6080
- // 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
6081
6163
  throw new ParseError("Parameter `{".concat($taskJson.resultingParameterName, "}` is not defined so can not define example value of it"));
6082
6164
  }
6083
6165
  parameter.exampleValues = parameter.exampleValues || [];
@@ -6088,7 +6170,7 @@
6088
6170
  if (command.taskType === 'KNOWLEDGE') {
6089
6171
  knowledgeCommandParser.$applyToPipelineJson({
6090
6172
  type: 'KNOWLEDGE',
6091
- 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
6092
6174
  }, $pipelineJson);
6093
6175
  $taskJson.isTask = false;
6094
6176
  return;
@@ -6983,20 +7065,24 @@
6983
7065
  */
6984
7066
  var GeneratorFormfactorDefinition = {
6985
7067
  name: 'GENERATOR',
6986
- description: "@@@",
7068
+ description: "Generates any kind (in HTML with possible scripts and css format) of content from input message",
6987
7069
  documentationUrl: "https://github.com/webgptorg/promptbook/discussions/184",
6988
7070
  pipelineInterface: {
6989
7071
  inputParameters: [
6990
- /* @@@ */
6991
7072
  {
6992
- name: 'nonce',
6993
- description: 'Just to prevent GENERATOR to be set as implicit formfactor',
7073
+ name: 'inputMessage',
7074
+ description: "Input message to be image made from",
6994
7075
  isInput: true,
6995
7076
  isOutput: false,
6996
7077
  },
6997
7078
  ],
6998
7079
  outputParameters: [
6999
- /* @@@ */
7080
+ {
7081
+ name: 'result',
7082
+ description: "Result in HTML to be shown to user",
7083
+ isInput: false,
7084
+ isOutput: true,
7085
+ },
7000
7086
  ],
7001
7087
  },
7002
7088
  };
@@ -7028,6 +7114,35 @@
7028
7114
  pipelineInterface: GENERIC_PIPELINE_INTERFACE,
7029
7115
  };
7030
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
+
7031
7146
  /**
7032
7147
  * Matcher is form of app that @@@
7033
7148
  *
@@ -7126,6 +7241,7 @@
7126
7241
  SheetsFormfactorDefinition,
7127
7242
  MatcherFormfactorDefinition,
7128
7243
  GeneratorFormfactorDefinition,
7244
+ ImageGeneratorFormfactorDefinition,
7129
7245
  ];
7130
7246
  /**
7131
7247
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -7512,7 +7628,7 @@
7512
7628
  * Note: `$` is used to indicate that this function mutates given `pipelineJson`
7513
7629
  */
7514
7630
  $applyToPipelineJson: function (command, $pipelineJson) {
7515
- // Note: [🍣] Do nothing, its application is implemented separately in `precompilePipeline`
7631
+ // Note: [🍣] Do nothing, its application is implemented separately in `parsePipeline`
7516
7632
  },
7517
7633
  /**
7518
7634
  * Apply the PARAMETER command to the `pipelineJson`
@@ -7520,7 +7636,7 @@
7520
7636
  * Note: `$` is used to indicate that this function mutates given `taskJson`
7521
7637
  */
7522
7638
  $applyToTaskJson: function (command, $taskJson, $pipelineJson) {
7523
- // Note: [🍣] Do nothing, its application is implemented separately in `precompilePipeline`
7639
+ // Note: [🍣] Do nothing, its application is implemented separately in `parsePipeline`
7524
7640
  },
7525
7641
  /**
7526
7642
  * Converts the PARAMETER command back to string
@@ -8015,7 +8131,7 @@
8015
8131
  instrumentCommandParser,
8016
8132
  personaCommandParser,
8017
8133
  foreachCommandParser,
8018
- boilerplateCommandParser, // <- TODO: !! Only in development, remove in production
8134
+ boilerplateCommandParser, // <- TODO: Only in development, remove in production
8019
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
8020
8136
  ];
8021
8137
  /**
@@ -8439,7 +8555,7 @@
8439
8555
  isOutput: true,
8440
8556
  exampleValues: ['Hello, I am a Pavol`s virtual avatar. How can I help you?'],
8441
8557
  });
8442
- // TODO: !!!!!! spaceTrim
8558
+ // TODO: Use spaceTrim in multiline strings
8443
8559
  $pipelineJson.tasks.push({
8444
8560
  taskType: 'PROMPT_TASK',
8445
8561
  name: 'create-an-answer',
@@ -8447,8 +8563,11 @@
8447
8563
  content: 'Write a response to the user message:\n\n**Question from user**\n\n> {userMessage}\n\n**Previous conversation**\n\n> {previousConversationSummary}',
8448
8564
  resultingParameterName: 'chatbotResponse',
8449
8565
  personaName: personaName,
8450
- dependentParameterNames: ['userMessage', 'previousConversationSummary' /* !!!!!!, 'knowledge'*/],
8451
- // !!!!!! 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}',
8452
8571
  }, {
8453
8572
  taskType: 'PROMPT_TASK',
8454
8573
  name: 'summarize-the-conversation',
@@ -8462,24 +8581,27 @@
8462
8581
  max: 10,
8463
8582
  },
8464
8583
  },
8465
- dependentParameterNames: ['userMessage', 'chatbotResponse' /* !!!!!!, 'knowledge'*/],
8466
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8584
+ dependentParameterNames: ['userMessage', 'chatbotResponse' /* TODO: [🧠][📛], 'knowledge'*/],
8585
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8467
8586
  }, {
8468
8587
  taskType: 'SIMPLE_TASK',
8469
8588
  name: 'title',
8470
8589
  title: 'Title',
8471
8590
  content: '{conversationSummary}',
8472
8591
  resultingParameterName: 'title',
8473
- dependentParameterNames: ['conversationSummary' /* !!!!!!, 'knowledge'*/],
8474
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8592
+ dependentParameterNames: ['conversationSummary' /* TODO: [🧠][📛], 'knowledge'*/],
8593
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8475
8594
  });
8476
8595
  },
8477
8596
  };
8597
+ /**
8598
+ * TODO: [🧠][📛] Should this be here?
8599
+ */
8478
8600
 
8479
8601
  /**
8480
8602
  * All high-level abstractions
8481
8603
  *
8482
- * @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)
8483
8605
  */
8484
8606
  var HIGH_LEVEL_ABSTRACTIONS = [
8485
8607
  ImplicitFormfactorHla,
@@ -8613,7 +8735,7 @@
8613
8735
  return;
8614
8736
  }
8615
8737
  if (!section.startsWith('#')) {
8616
- section = "# ".concat(DEFAULT_TITLE, "\n\n").concat(section);
8738
+ section = "# ".concat(DEFAULT_BOOK_TITLE, "\n\n").concat(section);
8617
8739
  }
8618
8740
  sections.push(section);
8619
8741
  buffer = [];
@@ -8668,7 +8790,7 @@
8668
8790
  /**
8669
8791
  * Normalizes the markdown by flattening the structure
8670
8792
  *
8671
- * - 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`
8672
8794
  * - All other headings are normalized to h2
8673
8795
  *
8674
8796
  * @public exported from `@promptbook/markdown-utils`
@@ -8677,7 +8799,7 @@
8677
8799
  var e_1, _a;
8678
8800
  var sections = splitMarkdownIntoSections(markdown);
8679
8801
  if (sections.length === 0) {
8680
- return "# ".concat(DEFAULT_TITLE);
8802
+ return "# ".concat(DEFAULT_BOOK_TITLE);
8681
8803
  }
8682
8804
  var flattenedMarkdown = '';
8683
8805
  var parsedSections = sections.map(parseMarkdownSection);
@@ -8688,7 +8810,7 @@
8688
8810
  }
8689
8811
  else {
8690
8812
  parsedSections.unshift(firstSection);
8691
- flattenedMarkdown += "# ".concat(DEFAULT_TITLE) + "\n\n"; // <- [🧠] Maybe 3 new lines?
8813
+ flattenedMarkdown += "# ".concat(DEFAULT_BOOK_TITLE) + "\n\n"; // <- [🧠] Maybe 3 new lines?
8692
8814
  }
8693
8815
  try {
8694
8816
  for (var parsedSections_1 = __values(parsedSections), parsedSections_1_1 = parsedSections_1.next(); !parsedSections_1_1.done; parsedSections_1_1 = parsedSections_1.next()) {
@@ -8715,13 +8837,13 @@
8715
8837
  */
8716
8838
 
8717
8839
  /**
8718
- * Removes HTML or Markdown comments from a string.
8840
+ * Removes Markdown (or HTML) comments
8719
8841
  *
8720
8842
  * @param {string} content - The string to remove comments from.
8721
8843
  * @returns {string} The input string with all comments removed.
8722
8844
  * @public exported from `@promptbook/markdown-utils`
8723
8845
  */
8724
- function removeContentComments(content) {
8846
+ function removeMarkdownComments(content) {
8725
8847
  return spaceTrim.spaceTrim(content.replace(/<!--(.*?)-->/gs, ''));
8726
8848
  }
8727
8849
 
@@ -8754,7 +8876,7 @@
8754
8876
  *
8755
8877
  * Note: There are 3 similar functions:
8756
8878
  * - `compilePipeline` **(preferred)** - which propperly compiles the promptbook and use embedding for external knowledge
8757
- * - `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
8758
8880
  * - `preparePipeline` - just one step in the compilation process
8759
8881
  *
8760
8882
  * Note: This function does not validate logic of the pipeline only the parsing
@@ -8765,10 +8887,10 @@
8765
8887
  * @throws {ParseError} if the promptbook string is not valid
8766
8888
  * @public exported from `@promptbook/core`
8767
8889
  */
8768
- function precompilePipeline(pipelineString) {
8890
+ function parsePipeline(pipelineString) {
8769
8891
  var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
8770
8892
  var $pipelineJson = {
8771
- title: DEFAULT_TITLE,
8893
+ title: DEFAULT_BOOK_TITLE,
8772
8894
  parameters: [],
8773
8895
  tasks: [],
8774
8896
  knowledgeSources: [],
@@ -8779,7 +8901,7 @@
8779
8901
  {
8780
8902
  type: 'BOOK',
8781
8903
  path: null,
8782
- // <- TODO: !!!!!! Pass here path of the file
8904
+ // <- TODO: !!6 Pass here path of the file
8783
8905
  content: pipelineString,
8784
8906
  },
8785
8907
  ],
@@ -8797,18 +8919,44 @@
8797
8919
  }
8798
8920
  // =============================================================
8799
8921
  // Note: 1️⃣ Parsing of the markdown into object
8922
+ // ==============
8923
+ // Note: 1️⃣◽1️⃣ Remove #!shebang and comments
8800
8924
  if (pipelineString.startsWith('#!')) {
8801
8925
  var _g = __read(pipelineString.split('\n')), shebangLine_1 = _g[0], restLines = _g.slice(1);
8802
8926
  if (!(shebangLine_1 || '').includes('ptbk')) {
8803
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 "); }));
8804
8928
  }
8805
- pipelineString = restLines.join('\n');
8806
- }
8807
- pipelineString = removeContentComments(pipelineString);
8929
+ pipelineString = validatePipelineString(restLines.join('\n'));
8930
+ }
8931
+ pipelineString = removeMarkdownComments(pipelineString);
8932
+ pipelineString = spaceTrim.spaceTrim(pipelineString);
8933
+ // <- TODO: [😧] `spaceTrim` should preserve discriminated type *(or at lease `PipelineString`)*
8934
+ // ==============
8935
+ // Note: 1️⃣◽2️⃣ Process flat pipeline
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
+ }
8952
+ // ==============
8953
+ // Note: 1️⃣◽3️⃣ Parse the markdown
8808
8954
  pipelineString = flattenMarkdown(pipelineString) /* <- Note: [🥞] */;
8809
8955
  pipelineString = pipelineString.replaceAll(/`\{(?<parameterName>[a-z0-9_]+)\}`/gi, '{$<parameterName>}');
8810
8956
  pipelineString = pipelineString.replaceAll(/`->\s+\{(?<parameterName>[a-z0-9_]+)\}`/gi, '-> {$<parameterName>}');
8811
8957
  var _h = __read(splitMarkdownIntoSections(pipelineString).map(parseMarkdownSection)), pipelineHead = _h[0], pipelineSections = _h.slice(1); /* <- Note: [🥞] */
8958
+ // ==============
8959
+ // Note: 1️⃣◽4️⃣ Check markdown structure
8812
8960
  if (pipelineHead === undefined) {
8813
8961
  throw new UnexpectedError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline head is not defined\n\n ".concat(block(getPipelineIdentification()), "\n\n This should never happen, because the pipeline already flattened\n "); }));
8814
8962
  }
@@ -9192,14 +9340,14 @@
9192
9340
  // =============================================================
9193
9341
  return exportJson({
9194
9342
  name: 'pipelineJson',
9195
- message: "Result of `precompilePipeline`",
9343
+ message: "Result of `parsePipeline`",
9196
9344
  order: ORDER_OF_PIPELINE_JSON,
9197
9345
  value: __assign({ formfactorName: 'GENERIC' }, $pipelineJson),
9198
9346
  });
9199
9347
  }
9200
9348
  /**
9201
9349
  * TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
9202
- * TODO: [main] !!!! Warn if used only sync version
9350
+ * TODO: [main] !!4 Warn if used only sync version
9203
9351
  * TODO: [🚞] Report here line/column of error
9204
9352
  * TODO: Use spaceTrim more effectively
9205
9353
  * TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
@@ -9212,10 +9360,7 @@
9212
9360
  /**
9213
9361
  * Compile pipeline from string (markdown) format to JSON format
9214
9362
  *
9215
- * Note: There are 3 similar functions:
9216
- * - `compilePipeline` **(preferred)** - which propperly compiles the promptbook and use embedding for external knowledge
9217
- * - `precompilePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
9218
- * - `preparePipeline` - just one step in the compilation process
9363
+ * @see https://github.com/webgptorg/promptbook/discussions/196
9219
9364
  *
9220
9365
  * Note: This function does not validate logic of the pipeline only the parsing
9221
9366
  * Note: This function acts as compilation process
@@ -9233,7 +9378,7 @@
9233
9378
  return __generator(this, function (_a) {
9234
9379
  switch (_a.label) {
9235
9380
  case 0:
9236
- pipelineJson = precompilePipeline(pipelineString);
9381
+ pipelineJson = parsePipeline(pipelineString);
9237
9382
  if (!(tools !== undefined && tools.llm !== undefined)) return [3 /*break*/, 2];
9238
9383
  return [4 /*yield*/, preparePipeline(pipelineJson, tools, options || {
9239
9384
  rootDirname: null,
@@ -9242,7 +9387,7 @@
9242
9387
  pipelineJson = _a.sent();
9243
9388
  _a.label = 2;
9244
9389
  case 2:
9245
- // 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
9246
9391
  return [2 /*return*/, pipelineJson];
9247
9392
  }
9248
9393
  });
@@ -9593,6 +9738,25 @@
9593
9738
  * TODO: [®] DRY Register logic
9594
9739
  */
9595
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
+
9596
9760
  /**
9597
9761
  * @@@
9598
9762
  *
@@ -9607,17 +9771,45 @@
9607
9771
  * @public exported from `@promptbook/node`
9608
9772
  */
9609
9773
  function $provideLlmToolsConfigurationFromEnv() {
9610
- if (!$isRunningInNode()) {
9611
- throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9612
- }
9613
- dotenv__namespace.config();
9614
- // TODO: Walk to the root of the project and find the nearest `.env` file
9615
- // @see https://collboard.fra1.cdn.digitaloceanspaces.com/usercontent/education/image/png/1/2/ad/image.png
9616
- var llmToolsConfiguration = $llmToolsMetadataRegister
9617
- .list()
9618
- .map(function (metadata) { return metadata.createConfigurationFromEnv(process.env); })
9619
- .filter(function (configuration) { return configuration !== null; });
9620
- 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
+ });
9621
9813
  }
9622
9814
  /**
9623
9815
  * TODO: [🧠][🪁] Maybe do allow to do auto-install if package not registered and not found
@@ -9840,15 +10032,28 @@
9840
10032
  */
9841
10033
  function $provideLlmToolsFromEnv(options) {
9842
10034
  if (options === void 0) { options = {}; }
9843
- if (!$isRunningInNode()) {
9844
- throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9845
- }
9846
- var configuration = $provideLlmToolsConfigurationFromEnv();
9847
- if (configuration.length === 0) {
9848
- // TODO: [🥃]
9849
- 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 "); }));
9850
- }
9851
- 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
+ });
9852
10057
  }
9853
10058
  /**
9854
10059
  * TODO: @@@ write `$provideLlmToolsFromEnv` vs `$provideLlmToolsConfigurationFromEnv` vs `createLlmToolsFromConfiguration`
@@ -9917,23 +10122,31 @@
9917
10122
  */
9918
10123
 
9919
10124
  /**
9920
- * Extracts code block from markdown.
10125
+ * Detects if the code is running in a browser environment in main thread (Not in a web worker)
9921
10126
  *
9922
- * - 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
9923
10128
  *
9924
- * Note: There are multiple simmilar function:
9925
- * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
9926
- * - `extractJsonBlock` extracts exactly one valid JSON code block
9927
- * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
9928
- * - `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
9929
10135
  *
9930
- * @public exported from `@promptbook/markdown-utils`
9931
- * @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`
9932
10139
  */
9933
- function extractBlock(markdown) {
9934
- var content = extractOneBlockFromMarkdown(markdown).content;
9935
- return content;
9936
- }
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");
9937
10150
 
9938
10151
  /**
9939
10152
  * Makes first letter of a string uppercase
@@ -9944,6 +10157,21 @@
9944
10157
  return word.substring(0, 1).toLowerCase() + word.substring(1);
9945
10158
  }
9946
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
+
9947
10175
  /**
9948
10176
  * @@@
9949
10177
  *
@@ -9997,20 +10225,39 @@
9997
10225
  return sentence.replace(/\s+/gs, ' ').trim();
9998
10226
  }
9999
10227
 
10228
+ // <- TODO: Auto convert to type `import { ... } from 'type-fest';`
10000
10229
  /**
10001
- * 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
+ *
10002
10245
  *
10003
- * @param {string} input
10004
- * @returns {Set} of keywords without diacritics in lowercase
10005
10246
  * @public exported from `@promptbook/utils`
10006
10247
  */
10007
- function parseKeywordsFromString(input) {
10008
- var keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
10009
- .toLowerCase()
10010
- .split(/[^a-z0-9]+/gs)
10011
- .filter(function (value) { return value; });
10012
- 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
+ }
10013
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
+ */
10014
10261
 
10015
10262
  /**
10016
10263
  * Function trimCodeBlock will trim starting and ending code block from the string if it is present.
@@ -10118,6 +10365,25 @@
10118
10365
  * TODO: [🧠] Should this also unwrap the (parenthesis)
10119
10366
  */
10120
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
+
10121
10387
  /**
10122
10388
  * Does nothing, but preserves the function in the bundle
10123
10389
  * Compiler is tricked into thinking the function is used
@@ -10152,36 +10418,9 @@
10152
10418
  }); })();
10153
10419
  }
10154
10420
  /**
10155
- * TODO: !! [1] This maybe does memory leak
10156
- */
10157
-
10158
- /**
10159
- * Converts anything to string that can be used for debugging and logging
10160
- *
10161
- * @param value String value for logging
10162
- * @private internal util
10421
+ * TODO: Probbably remove in favour of `keepImported`
10422
+ * TODO: [1] This maybe does memory leak
10163
10423
  */
10164
- function unknownToString(value) {
10165
- if (value === undefined) {
10166
- return 'undefined';
10167
- }
10168
- else if (value === null) {
10169
- return 'null';
10170
- }
10171
- else if (['number', 'string', 'boolean'].includes(typeof value)) {
10172
- return typeof value + ' ' + value.toString();
10173
- }
10174
- else if (typeof value === 'object' && Array.isArray(value)) {
10175
- return 'array containing [' + value.map(function (item) { return unknownToString(item); }).join(', ') + ']';
10176
- }
10177
- else if (typeof value === 'object') {
10178
- // TODO: Maybe serialize the object
10179
- return 'object';
10180
- }
10181
- else {
10182
- return 'unknown (Search in promptbook code for [🔹])';
10183
- }
10184
- }
10185
10424
 
10186
10425
  /**
10187
10426
  * ScriptExecutionTools for JavaScript implemented via eval
@@ -10312,7 +10551,7 @@
10312
10551
  case 2:
10313
10552
  result = _a.sent();
10314
10553
  if (typeof result !== 'string') {
10315
- 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)));
10316
10555
  }
10317
10556
  return [3 /*break*/, 4];
10318
10557
  case 3:
@@ -10339,7 +10578,7 @@
10339
10578
  throw error_1;
10340
10579
  case 4:
10341
10580
  if (typeof result !== 'string') {
10342
- 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)));
10343
10582
  }
10344
10583
  return [2 /*return*/, result];
10345
10584
  }
@@ -10379,9 +10618,11 @@
10379
10618
  throw new EnvironmentMismatchError('Function `$getExecutionToolsForNode` works only in Node.js environment');
10380
10619
  }
10381
10620
  fs = $provideFilesystemForNode();
10382
- llm = $provideLlmToolsFromEnv(options);
10383
- return [4 /*yield*/, $provideExecutablesForNode(options)];
10621
+ return [4 /*yield*/, $provideLlmToolsFromEnv(options)];
10384
10622
  case 1:
10623
+ llm = _b.sent();
10624
+ return [4 /*yield*/, $provideExecutablesForNode(options)];
10625
+ case 2:
10385
10626
  executables = _b.sent();
10386
10627
  _a = {
10387
10628
  llm: llm,
@@ -10389,7 +10630,7 @@
10389
10630
  executables: executables
10390
10631
  };
10391
10632
  return [4 /*yield*/, $provideScrapersForNode({ fs: fs, llm: llm, executables: executables }, options)];
10392
- case 2:
10633
+ case 3:
10393
10634
  tools = (_a.scrapers = _b.sent(),
10394
10635
  _a.script = [new JavascriptExecutionTools(options)],
10395
10636
  _a);
@@ -10612,7 +10853,7 @@
10612
10853
  */
10613
10854
  function createCollectionFromDirectory(path$1, tools, options) {
10614
10855
  return __awaiter(this, void 0, void 0, function () {
10615
- 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;
10616
10857
  var _this = this;
10617
10858
  return __generator(this, function (_f) {
10618
10859
  switch (_f.label) {
@@ -10627,18 +10868,18 @@
10627
10868
  throw new EnvironmentMismatchError('Can not create collection without filesystem tools');
10628
10869
  // <- TODO: [🧠] What is the best error type here`
10629
10870
  }
10630
- makedLibraryFilePath = path.join(path$1, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
10871
+ madeLibraryFilePath = path.join(path$1, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
10631
10872
  // <- TODO: [🦒] Allow to override (pass different value into the function)
10632
10873
  , ".json"));
10633
- return [4 /*yield*/, isFileExisting(makedLibraryFilePath, tools.fs)];
10874
+ return [4 /*yield*/, isFileExisting(madeLibraryFilePath, tools.fs)];
10634
10875
  case 3:
10635
10876
  if (!(_f.sent())) ;
10636
10877
  else {
10637
- colors__default["default"].green("(In future, not implemented yet) Using your compiled pipeline collection ".concat(makedLibraryFilePath));
10638
- // TODO: !! Implement;
10878
+ colors__default["default"].green("(In future, not implemented yet) Using your compiled pipeline collection ".concat(madeLibraryFilePath));
10879
+ // TODO: Implement;
10639
10880
  // TODO: [🌗]
10640
10881
  }
10641
- _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;
10642
10883
  collection = createCollectionFromPromise(function () { return __awaiter(_this, void 0, void 0, function () {
10643
10884
  var fileNames, collection, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1;
10644
10885
  var e_1, _a;
@@ -10664,34 +10905,35 @@
10664
10905
  });
10665
10906
  collection = new Map();
10666
10907
  _loop_1 = function (fileName) {
10667
- var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, existing, error_1, wrappedErrorMessage;
10668
- return __generator(this, function (_e) {
10669
- 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) {
10670
10911
  case 0:
10671
10912
  sourceFile = './' + fileName.split('\\').join('/');
10672
10913
  rootDirname = path.dirname(sourceFile).split('\\').join('/');
10673
- _e.label = 1;
10914
+ _f.label = 1;
10674
10915
  case 1:
10675
- _e.trys.push([1, 8, , 9]);
10916
+ _f.trys.push([1, 8, , 9]);
10676
10917
  pipeline = null;
10677
10918
  if (!fileName.endsWith('.book.md')) return [3 /*break*/, 4];
10919
+ _c = validatePipelineString;
10678
10920
  return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
10679
10921
  case 2:
10680
- pipelineString = (_e.sent());
10922
+ pipelineString = _c.apply(void 0, [_f.sent()]);
10681
10923
  return [4 /*yield*/, compilePipeline(pipelineString, tools, {
10682
10924
  rootDirname: rootDirname,
10683
10925
  })];
10684
10926
  case 3:
10685
- pipeline = _e.sent();
10927
+ pipeline = _f.sent();
10686
10928
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
10687
10929
  return [3 /*break*/, 7];
10688
10930
  case 4:
10689
10931
  if (!fileName.endsWith('.book.json')) return [3 /*break*/, 6];
10690
- _d = (_c = JSON).parse;
10932
+ _e = (_d = JSON).parse;
10691
10933
  return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
10692
10934
  case 5:
10693
10935
  // TODO: Handle non-valid JSON files
10694
- pipeline = _d.apply(_c, [_e.sent()]);
10936
+ pipeline = _e.apply(_d, [_f.sent()]);
10695
10937
  // TODO: [🌗]
10696
10938
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
10697
10939
  return [3 /*break*/, 7];
@@ -10699,10 +10941,24 @@
10699
10941
  if (isVerbose) {
10700
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")));
10701
10943
  }
10702
- _e.label = 7;
10944
+ _f.label = 7;
10703
10945
  case 7:
10704
10946
  // ---
10705
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
+ }
10706
10962
  // TODO: [👠] DRY
10707
10963
  if (pipeline.pipelineUrl === undefined) {
10708
10964
  if (isVerbose) {
@@ -10734,17 +10990,17 @@
10734
10990
  }
10735
10991
  else {
10736
10992
  existing = collection.get(pipeline.pipelineUrl);
10737
- 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 ")));
10738
10994
  }
10739
10995
  }
10740
10996
  }
10741
10997
  return [3 /*break*/, 9];
10742
10998
  case 8:
10743
- error_1 = _e.sent();
10999
+ error_1 = _f.sent();
10744
11000
  if (!(error_1 instanceof Error)) {
10745
11001
  throw error_1;
10746
11002
  }
10747
- 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';
10748
11004
  if (isCrashedOnError) {
10749
11005
  throw new CollectionError(wrappedErrorMessage);
10750
11006
  }
@@ -10800,40 +11056,6 @@
10800
11056
  * TODO: Maybe move from `@promptbook/node` to `@promptbook/core` as we removes direct dependency on `fs`
10801
11057
  */
10802
11058
 
10803
- // <- TODO: !!!!!!! Auto convert to type `import { ... } from 'type-fest';`
10804
- /**
10805
- * Tests if the value is [🚉] serializable as JSON
10806
- *
10807
- * - Almost all primitives are serializable BUT:
10808
- * - `undefined` is not serializable
10809
- * - `NaN` is not serializable
10810
- * - Objects and arrays are serializable if all their properties are serializable
10811
- * - Functions are not serializable
10812
- * - Circular references are not serializable
10813
- * - `Date` objects are not serializable
10814
- * - `Map` and `Set` objects are not serializable
10815
- * - `RegExp` objects are not serializable
10816
- * - `Error` objects are not serializable
10817
- * - `Symbol` objects are not serializable
10818
- * - And much more...
10819
- *
10820
- *
10821
- * @public exported from `@promptbook/utils`
10822
- */
10823
- function isSerializableAsJson(value) {
10824
- try {
10825
- checkSerializableAsJson({ value: value });
10826
- return true;
10827
- }
10828
- catch (error) {
10829
- return false;
10830
- }
10831
- }
10832
- /**
10833
- * TODO: [🧠][main] !!! In-memory cache of same values to prevent multiple checks
10834
- * TODO: [🧠][💺] Can be done this on type-level?
10835
- */
10836
-
10837
11059
  /**
10838
11060
  * Stringify the PipelineJson with proper formatting
10839
11061
  *
@@ -11183,46 +11405,6 @@
11183
11405
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
11184
11406
  */
11185
11407
 
11186
- /**
11187
- * @@@
11188
- *
11189
- * @public exported from `@promptbook/node`
11190
- */
11191
- var wizzard = {
11192
- /**
11193
- * @@@!!!!!!
11194
- */
11195
- run: function (book, inputParameters, onProgress) {
11196
- return __awaiter(this, void 0, void 0, function () {
11197
- var tools, collection, pipeline, pipelineExecutor, result;
11198
- return __generator(this, function (_a) {
11199
- switch (_a.label) {
11200
- case 0: return [4 /*yield*/, $provideExecutionToolsForNode()];
11201
- case 1:
11202
- tools = _a.sent();
11203
- return [4 /*yield*/, createCollectionFromDirectory('./books', tools)];
11204
- case 2:
11205
- collection = _a.sent();
11206
- return [4 /*yield*/, collection.getPipelineByUrl(book)];
11207
- case 3:
11208
- pipeline = _a.sent();
11209
- pipelineExecutor = createPipelineExecutor({ pipeline: pipeline, tools: tools });
11210
- return [4 /*yield*/, pipelineExecutor(inputParameters, onProgress)];
11211
- case 4:
11212
- result = _a.sent();
11213
- // ▶ Fail if the execution was not successful
11214
- assertsExecutionSuccessful(result);
11215
- // ▶ Return the result
11216
- return [2 /*return*/, result];
11217
- }
11218
- });
11219
- });
11220
- },
11221
- };
11222
- /**
11223
- * TODO: !!!!!! Add to readmes - one markdown here imported in all packages
11224
- */
11225
-
11226
11408
  exports.$execCommand = $execCommand;
11227
11409
  exports.$execCommands = $execCommands;
11228
11410
  exports.$provideExecutablesForNode = $provideExecutablesForNode;
@@ -11235,7 +11417,6 @@
11235
11417
  exports.FileCacheStorage = FileCacheStorage;
11236
11418
  exports.PROMPTBOOK_ENGINE_VERSION = PROMPTBOOK_ENGINE_VERSION;
11237
11419
  exports.createCollectionFromDirectory = createCollectionFromDirectory;
11238
- exports.wizzard = wizzard;
11239
11420
 
11240
11421
  Object.defineProperty(exports, '__esModule', { value: true });
11241
11422