@promptbook/node 0.81.0-8 → 0.82.0-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 (110) hide show
  1. package/README.md +25 -4
  2. package/esm/index.es.js +1948 -1766
  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/remote-client.index.d.ts +7 -3
  9. package/esm/typings/src/_packages/remote-server.index.d.ts +2 -2
  10. package/esm/typings/src/_packages/templates.index.d.ts +2 -2
  11. package/esm/typings/src/_packages/types.index.d.ts +34 -30
  12. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  13. package/esm/typings/src/_packages/wizzard.index.d.ts +44 -0
  14. package/esm/typings/src/cli/cli-commands/make.d.ts +1 -1
  15. package/esm/typings/src/cli/cli-commands/run.d.ts +2 -2
  16. package/esm/typings/src/collection/constructors/createCollectionFromDirectory.d.ts +11 -0
  17. package/esm/typings/src/collection/constructors/createCollectionFromUrl.d.ts +1 -1
  18. package/esm/typings/src/commands/index.d.ts +1 -1
  19. package/esm/typings/src/config.d.ts +3 -3
  20. package/esm/typings/src/conversion/compilePipeline.d.ts +1 -4
  21. package/esm/typings/src/conversion/compilePipelineOnRemoteServer.d.ts +18 -0
  22. package/esm/typings/src/conversion/{precompilePipeline.d.ts → parsePipeline.d.ts} +3 -3
  23. package/esm/typings/src/conversion/prettify/renderPipelineMermaidOptions.d.ts +3 -3
  24. package/esm/typings/src/conversion/validation/validatePipeline.d.ts +7 -7
  25. package/esm/typings/src/errors/utils/getErrorReportUrl.d.ts +1 -1
  26. package/esm/typings/src/execution/PipelineExecutor.d.ts +2 -2
  27. package/esm/typings/src/execution/createPipelineExecutor/10-executePipeline.d.ts +2 -2
  28. package/esm/typings/src/formfactors/generator/GeneratorFormfactorDefinition.d.ts +9 -4
  29. package/esm/typings/src/formfactors/image-generator/ImageGeneratorFormfactorDefinition.d.ts +24 -0
  30. package/esm/typings/src/formfactors/index.d.ts +31 -9
  31. package/esm/typings/src/high-level-abstractions/_common/HighLevelAbstraction.d.ts +1 -1
  32. package/esm/typings/src/high-level-abstractions/index.d.ts +3 -3
  33. package/esm/typings/src/high-level-abstractions/quick-chatbot/QuickChatbotHla.d.ts +3 -0
  34. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  35. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +1 -1
  36. package/esm/typings/src/llm-providers/_common/register/{$provideLlmToolsForCli.d.ts → $provideLlmToolsForWizzardOrCli.d.ts} +2 -2
  37. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  38. package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionToolsOptions.d.ts +1 -1
  39. package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  40. package/esm/typings/src/llm-providers/anthropic-claude/createAnthropicClaudeExecutionTools.d.ts +2 -2
  41. package/esm/typings/src/llm-providers/anthropic-claude/playground/playground.d.ts +2 -2
  42. package/esm/typings/src/llm-providers/anthropic-claude/register-configuration.d.ts +1 -0
  43. package/esm/typings/src/llm-providers/anthropic-claude/register-constructor.d.ts +2 -0
  44. package/esm/typings/src/llm-providers/azure-openai/register-configuration.d.ts +1 -0
  45. package/esm/typings/src/llm-providers/azure-openai/register-constructor.d.ts +1 -0
  46. package/esm/typings/src/llm-providers/google/register-configuration.d.ts +1 -0
  47. package/esm/typings/src/llm-providers/google/register-constructor.d.ts +1 -0
  48. package/esm/typings/src/llm-providers/openai/playground/playground.d.ts +1 -1
  49. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +2 -0
  50. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +2 -0
  51. package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -1
  52. package/esm/typings/src/llm-providers/vercel/playground/playground.d.ts +1 -1
  53. package/esm/typings/src/other/templates/getBookTemplates.d.ts +22 -0
  54. package/esm/typings/src/personas/preparePersona.d.ts +4 -4
  55. package/esm/typings/src/pipeline/PipelineString.d.ts +0 -3
  56. package/esm/typings/src/pipeline/book-notation.d.ts +14 -0
  57. package/esm/typings/src/pipeline/isValidPipelineString.d.ts +13 -0
  58. package/esm/typings/src/pipeline/isValidPipelineString.test.d.ts +4 -0
  59. package/esm/typings/src/pipeline/validatePipelineString.d.ts +14 -0
  60. package/esm/typings/src/prepare/isPipelinePrepared.d.ts +3 -1
  61. package/esm/typings/src/prepare/preparePipeline.d.ts +4 -2
  62. package/esm/typings/src/prepare/preparePipelineOnRemoteServer.d.ts +14 -0
  63. package/esm/typings/src/prepare/prepareTasks.d.ts +1 -1
  64. package/esm/typings/src/{llm-providers/remote → remote-server}/interfaces/PromptbookServer_Error.d.ts +1 -1
  65. package/esm/typings/src/{llm-providers/remote → remote-server}/interfaces/PromptbookServer_ListModels_Request.d.ts +4 -4
  66. package/esm/typings/src/{llm-providers/remote → remote-server}/interfaces/PromptbookServer_ListModels_Response.d.ts +1 -1
  67. package/esm/typings/src/{llm-providers/remote → remote-server}/interfaces/PromptbookServer_Prompt_Progress.d.ts +1 -1
  68. package/esm/typings/src/{llm-providers/remote → remote-server}/interfaces/PromptbookServer_Prompt_Request.d.ts +5 -5
  69. package/esm/typings/src/{llm-providers/remote → remote-server}/interfaces/PromptbookServer_Prompt_Response.d.ts +1 -1
  70. package/esm/typings/src/{llm-providers/remote → remote-server}/interfaces/RemoteLlmExecutionToolsOptions.d.ts +7 -7
  71. package/esm/typings/src/{llm-providers/remote → remote-server}/interfaces/RemoteServerOptions.d.ts +10 -10
  72. package/esm/typings/src/scrapers/_common/Converter.d.ts +1 -0
  73. package/esm/typings/src/scrapers/_common/Scraper.d.ts +1 -1
  74. package/esm/typings/src/scrapers/_common/ScraperIntermediateSource.d.ts +3 -0
  75. package/esm/typings/src/scrapers/_common/register/ScraperAndConverterMetadata.d.ts +2 -0
  76. package/esm/typings/src/scrapers/_common/utils/scraperFetch.d.ts +3 -0
  77. package/esm/typings/src/scrapers/document/register-constructor.d.ts +1 -0
  78. package/esm/typings/src/scrapers/document/register-metadata.d.ts +1 -0
  79. package/esm/typings/src/scrapers/document-legacy/register-constructor.d.ts +1 -0
  80. package/esm/typings/src/scrapers/document-legacy/register-metadata.d.ts +1 -0
  81. package/esm/typings/src/scrapers/markdown/register-constructor.d.ts +1 -0
  82. package/esm/typings/src/scrapers/markdown/register-metadata.d.ts +1 -0
  83. package/esm/typings/src/scrapers/pdf/PdfScraper.d.ts +1 -0
  84. package/esm/typings/src/scrapers/pdf/createPdfScraper.d.ts +1 -1
  85. package/esm/typings/src/scrapers/pdf/register-constructor.d.ts +1 -0
  86. package/esm/typings/src/scrapers/pdf/register-metadata.d.ts +2 -1
  87. package/esm/typings/src/scrapers/website/createWebsiteScraper.d.ts +3 -1
  88. package/esm/typings/src/scrapers/website/register-constructor.d.ts +1 -0
  89. package/esm/typings/src/scrapers/website/register-metadata.d.ts +1 -0
  90. package/esm/typings/src/scripting/javascript/JavascriptEvalExecutionTools.test.d.ts +1 -1
  91. package/esm/typings/src/scripting/javascript/utils/preserve.d.ts +2 -1
  92. package/esm/typings/src/types/typeAliases.d.ts +16 -2
  93. package/esm/typings/src/utils/markdown/flattenMarkdown.d.ts +1 -1
  94. package/esm/typings/src/utils/markdown/{removeContentComments.d.ts → removeMarkdownComments.d.ts} +2 -2
  95. package/esm/typings/src/utils/organization/$sideEffect.d.ts +9 -0
  96. package/esm/typings/src/utils/serialization/checkSerializableAsJson.d.ts +1 -1
  97. package/esm/typings/src/utils/serialization/isSerializableAsJson.d.ts +2 -2
  98. package/esm/typings/src/utils/validators/filePath/isRootPath.d.ts +12 -0
  99. package/esm/typings/src/utils/validators/filePath/isRootPath.test.d.ts +4 -0
  100. package/esm/typings/src/utils/validators/filePath/isValidFilePath.d.ts +3 -0
  101. package/esm/typings/src/wizzard/$getCompiledBook.d.ts +16 -0
  102. package/esm/typings/src/wizzard/wizzard.d.ts +52 -8
  103. package/package.json +2 -2
  104. package/umd/index.umd.js +1947 -1766
  105. package/umd/index.umd.js.map +1 -1
  106. package/esm/typings/src/other/templates/getBookTemplate.d.ts +0 -21
  107. package/esm/typings/src/scripting/javascript/utils/unknownToString.d.ts +0 -8
  108. /package/esm/typings/src/conversion/{precompilePipeline.test.d.ts → parsePipeline.test.d.ts} +0 -0
  109. /package/esm/typings/src/{llm-providers/remote → remote-server}/startRemoteServer.d.ts +0 -0
  110. /package/esm/typings/src/utils/markdown/{removeContentComments.test.d.ts → removeMarkdownComments.test.d.ts} +0 -0
package/esm/index.es.js CHANGED
@@ -28,7 +28,7 @@ var BOOK_LANGUAGE_VERSION = '1.0.0';
28
28
  * @generated
29
29
  * @see https://github.com/webgptorg/promptbook
30
30
  */
31
- var PROMPTBOOK_ENGINE_VERSION = '0.81.0-7';
31
+ var PROMPTBOOK_ENGINE_VERSION = '0.81.0-23';
32
32
  /**
33
33
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
34
34
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -198,7 +198,7 @@ var ADMIN_GITHUB_NAME = 'hejny';
198
198
  *
199
199
  * @public exported from `@promptbook/core`
200
200
  */
201
- var DEFAULT_TITLE = "Untitled";
201
+ var DEFAULT_BOOK_TITLE = "\u2728 Untitled Book";
202
202
  // <- TODO: [🧠] Better system for generator warnings - not always "code" and "by `@promptbook/cli`"
203
203
  /**
204
204
  * The maximum number of iterations for a loops
@@ -309,1412 +309,834 @@ true);
309
309
  * TODO: [🧠][🧜‍♂️] Maybe join remoteUrl and path into single value
310
310
  */
311
311
 
312
- /**
313
- * Orders JSON object by keys
314
- *
315
- * @returns The same type of object as the input re-ordered
316
- * @public exported from `@promptbook/utils`
317
- */
318
- function orderJson(options) {
319
- var value = options.value, order = options.order;
320
- var orderedValue = __assign(__assign({}, (order === undefined ? {} : Object.fromEntries(order.map(function (key) { return [key, undefined]; })))), value);
321
- return orderedValue;
322
- }
312
+ 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- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\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- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\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 task:\n\n> {book}\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear\n- Title starts with emoticon",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 task:\n\n> {book}\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear\n- Title starts with emoticon\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book.md"}];
323
313
 
324
314
  /**
325
- * Freezes the given object and all its nested objects recursively
326
- *
327
- * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
328
- * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
315
+ * Function isValidJsonString will tell you if the string is valid JSON or not
329
316
  *
330
- * @returns The same object as the input, but deeply frozen
331
317
  * @public exported from `@promptbook/utils`
332
318
  */
333
- function $deepFreeze(objectValue) {
334
- var e_1, _a;
335
- if (Array.isArray(objectValue)) {
336
- return Object.freeze(objectValue.map(function (item) { return $deepFreeze(item); }));
337
- }
338
- var propertyNames = Object.getOwnPropertyNames(objectValue);
319
+ function isValidJsonString(value /* <- [👨‍⚖️] */) {
339
320
  try {
340
- for (var propertyNames_1 = __values(propertyNames), propertyNames_1_1 = propertyNames_1.next(); !propertyNames_1_1.done; propertyNames_1_1 = propertyNames_1.next()) {
341
- var propertyName = propertyNames_1_1.value;
342
- var value = objectValue[propertyName];
343
- if (value && typeof value === 'object') {
344
- $deepFreeze(value);
345
- }
346
- }
321
+ JSON.parse(value);
322
+ return true;
347
323
  }
348
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
349
- finally {
350
- try {
351
- if (propertyNames_1_1 && !propertyNames_1_1.done && (_a = propertyNames_1.return)) _a.call(propertyNames_1);
324
+ catch (error) {
325
+ if (!(error instanceof Error)) {
326
+ throw error;
352
327
  }
353
- finally { if (e_1) throw e_1.error; }
328
+ if (error.message.includes('Unexpected token')) {
329
+ return false;
330
+ }
331
+ return false;
354
332
  }
355
- Object.freeze(objectValue);
356
- return objectValue;
357
- }
358
- /**
359
- * TODO: [🧠] Is there a way how to meaningfully test this utility
360
- */
361
-
362
- /**
363
- * Make error report URL for the given error
364
- *
365
- * @private !!!!!!
366
- */
367
- function getErrorReportUrl(error) {
368
- var report = {
369
- title: "\uD83D\uDC1C Error report from ".concat(NAME),
370
- body: spaceTrim(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 "); }),
371
- };
372
- var reportUrl = new URL("https://github.com/webgptorg/promptbook/issues/new");
373
- reportUrl.searchParams.set('labels', 'bug');
374
- reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
375
- reportUrl.searchParams.set('title', report.title);
376
- reportUrl.searchParams.set('body', report.body);
377
- return reportUrl;
378
333
  }
379
334
 
380
335
  /**
381
- * This error type indicates that the error should not happen and its last check before crashing with some other error
336
+ * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
382
337
  *
383
338
  * @public exported from `@promptbook/core`
384
339
  */
385
- var UnexpectedError = /** @class */ (function (_super) {
386
- __extends(UnexpectedError, _super);
387
- function UnexpectedError(message) {
388
- var _this = _super.call(this, spaceTrim$1(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;
389
- _this.name = 'UnexpectedError';
390
- Object.setPrototypeOf(_this, UnexpectedError.prototype);
340
+ var ParseError = /** @class */ (function (_super) {
341
+ __extends(ParseError, _super);
342
+ function ParseError(message) {
343
+ var _this = _super.call(this, message) || this;
344
+ _this.name = 'ParseError';
345
+ Object.setPrototypeOf(_this, ParseError.prototype);
391
346
  return _this;
392
347
  }
393
- return UnexpectedError;
348
+ return ParseError;
394
349
  }(Error));
350
+ /**
351
+ * TODO: Maybe split `ParseError` and `ApplyError`
352
+ */
395
353
 
396
354
  /**
397
- * Checks if the value is [🚉] serializable as JSON
398
- * If not, throws an UnexpectedError with a rich error message and tracking
399
- *
400
- * - Almost all primitives are serializable BUT:
401
- * - `undefined` is not serializable
402
- * - `NaN` is not serializable
403
- * - Objects and arrays are serializable if all their properties are serializable
404
- * - Functions are not serializable
405
- * - Circular references are not serializable
406
- * - `Date` objects are not serializable
407
- * - `Map` and `Set` objects are not serializable
408
- * - `RegExp` objects are not serializable
409
- * - `Error` objects are not serializable
410
- * - `Symbol` objects are not serializable
411
- * - And much more...
355
+ * Function `validatePipelineString` will validate the if the string is a valid pipeline string
356
+ * 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.
412
357
  *
413
- * @throws UnexpectedError if the value is not serializable as JSON
414
- * @public exported from `@promptbook/utils`
358
+ * @param {string} pipelineString the candidate for a pipeline string
359
+ * @returns {PipelineString} the same string as input, but validated as valid
360
+ * @throws {ParseError} if the string is not a valid pipeline string
361
+ * @public exported from `@promptbook/core`
415
362
  */
416
- function checkSerializableAsJson(options) {
417
- var e_1, _a;
418
- var value = options.value, name = options.name, message = options.message;
419
- if (value === undefined) {
420
- throw new UnexpectedError("".concat(name, " is undefined"));
421
- }
422
- else if (value === null) {
423
- return;
424
- }
425
- else if (typeof value === 'boolean') {
426
- return;
427
- }
428
- else if (typeof value === 'number' && !isNaN(value)) {
429
- return;
430
- }
431
- else if (typeof value === 'string') {
432
- return;
433
- }
434
- else if (typeof value === 'symbol') {
435
- throw new UnexpectedError("".concat(name, " is symbol"));
436
- }
437
- else if (typeof value === 'function') {
438
- throw new UnexpectedError("".concat(name, " is function"));
439
- }
440
- else if (typeof value === 'object' && Array.isArray(value)) {
441
- for (var i = 0; i < value.length; i++) {
442
- checkSerializableAsJson({ name: "".concat(name, "[").concat(i, "]"), value: value[i], message: message });
443
- }
444
- }
445
- else if (typeof value === 'object') {
446
- if (value instanceof Date) {
447
- throw new UnexpectedError(spaceTrim(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 "); }));
448
- }
449
- else if (value instanceof Map) {
450
- throw new UnexpectedError("".concat(name, " is Map"));
451
- }
452
- else if (value instanceof Set) {
453
- throw new UnexpectedError("".concat(name, " is Set"));
454
- }
455
- else if (value instanceof RegExp) {
456
- throw new UnexpectedError("".concat(name, " is RegExp"));
457
- }
458
- else if (value instanceof Error) {
459
- throw new UnexpectedError(spaceTrim(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 "); }));
460
- }
461
- else {
462
- try {
463
- for (var _b = __values(Object.entries(value)), _c = _b.next(); !_c.done; _c = _b.next()) {
464
- var _d = __read(_c.value, 2), subName = _d[0], subValue = _d[1];
465
- if (subValue === undefined) {
466
- // Note: undefined in object is serializable - it is just omited
467
- continue;
468
- }
469
- checkSerializableAsJson({ name: "".concat(name, ".").concat(subName), value: subValue, message: message });
470
- }
471
- }
472
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
473
- finally {
474
- try {
475
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
476
- }
477
- finally { if (e_1) throw e_1.error; }
478
- }
479
- try {
480
- JSON.stringify(value); // <- TODO: [0]
481
- }
482
- catch (error) {
483
- if (!(error instanceof Error)) {
484
- throw error;
485
- }
486
- throw new UnexpectedError(spaceTrim(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 "); }));
487
- }
488
- /*
489
- TODO: [0] Is there some more elegant way to check circular references?
490
- const seen = new Set();
491
- const stack = [{ value }];
492
- while (stack.length > 0) {
493
- const { value } = stack.pop()!;
494
- if (typeof value === 'object' && value !== null) {
495
- if (seen.has(value)) {
496
- throw new UnexpectedError(`${name} has circular reference`);
497
- }
498
- seen.add(value);
499
- if (Array.isArray(value)) {
500
- stack.push(...value.map((value) => ({ value })));
501
- } else {
502
- stack.push(...Object.values(value).map((value) => ({ value })));
503
- }
504
- }
505
- }
506
- */
507
- return;
508
- }
509
- }
510
- else {
511
- throw new UnexpectedError(spaceTrim(function (block) { return "\n `".concat(name, "` is unknown type\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
363
+ function validatePipelineString(pipelineString) {
364
+ if (isValidJsonString(pipelineString)) {
365
+ throw new ParseError('Expected a book, but got a JSON string');
512
366
  }
367
+ // <- TODO: Implement the validation + add tests when the pipeline logic considered as invalid
368
+ return pipelineString;
513
369
  }
514
370
  /**
515
- * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
516
- * TODO: [🧠][main] !!! In-memory cache of same values to prevent multiple checks
517
- * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
371
+ * TODO: [🧠][🈴] Where is the best location for this file
518
372
  */
519
373
 
520
374
  /**
521
- * @@@
375
+ * Prettify the html code
522
376
  *
523
- * @public exported from `@promptbook/utils`
377
+ * @param content raw html code
378
+ * @returns formatted html code
379
+ * @private withing the package because of HUGE size of prettier dependency
524
380
  */
525
- function deepClone(objectValue) {
526
- return JSON.parse(JSON.stringify(objectValue));
527
- /*
528
- !!!!!!!!
529
- TODO: [🧠] Is there a better implementation?
530
- > const propertyNames = Object.getOwnPropertyNames(objectValue);
531
- > for (const propertyName of propertyNames) {
532
- > const value = (objectValue as really_any)[propertyName];
533
- > if (value && typeof value === 'object') {
534
- > deepClone(value);
535
- > }
536
- > }
537
- > return Object.assign({}, objectValue);
538
- */
381
+ function prettifyMarkdown(content) {
382
+ try {
383
+ return format(content, {
384
+ parser: 'markdown',
385
+ plugins: [parserHtml],
386
+ // TODO: DRY - make some import or auto-copy of .prettierrc
387
+ endOfLine: 'lf',
388
+ tabWidth: 4,
389
+ singleQuote: true,
390
+ trailingComma: 'all',
391
+ arrowParens: 'always',
392
+ printWidth: 120,
393
+ htmlWhitespaceSensitivity: 'ignore',
394
+ jsxBracketSameLine: false,
395
+ bracketSpacing: true,
396
+ });
397
+ }
398
+ catch (error) {
399
+ // TODO: [🟥] Detect browser / node and make it colorfull
400
+ console.error('There was an error with prettifying the markdown, using the original as the fallback', {
401
+ error: error,
402
+ html: content,
403
+ });
404
+ return content;
405
+ }
539
406
  }
407
+
540
408
  /**
541
- * TODO: [🧠] Is there a way how to meaningfully test this utility
409
+ * Makes first letter of a string uppercase
410
+ *
411
+ * @public exported from `@promptbook/utils`
542
412
  */
413
+ function capitalize(word) {
414
+ return word.substring(0, 1).toUpperCase() + word.substring(1);
415
+ }
543
416
 
544
417
  /**
545
- * Utility to export a JSON object from a function
418
+ * Converts promptbook in JSON format to string format
546
419
  *
547
- * 1) Checks if the value is serializable as JSON
548
- * 2) Makes a deep clone of the object
549
- * 2) Orders the object properties
550
- * 2) Deeply freezes the cloned object
551
- *
552
- * Note: This function does not mutates the given object
553
- *
554
- * @returns The same type of object as the input but read-only and re-ordered
555
- * @public exported from `@promptbook/utils`
420
+ * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
421
+ * @param pipelineJson Promptbook in JSON format (.book.json)
422
+ * @returns Promptbook in string format (.book.md)
423
+ * @public exported from `@promptbook/core`
556
424
  */
557
- function exportJson(options) {
558
- var name = options.name, value = options.value, order = options.order, message = options.message;
559
- checkSerializableAsJson({ name: name, value: value, message: message });
560
- var orderedValue =
561
- // TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
562
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
563
- // @ts-ignore
564
- order === undefined
565
- ? deepClone(value)
566
- : orderJson({
567
- value: value,
568
- // <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
569
- order: order,
570
- });
571
- $deepFreeze(orderedValue);
572
- return orderedValue;
425
+ function pipelineJsonToString(pipelineJson) {
426
+ var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
427
+ var title = pipelineJson.title, pipelineUrl = pipelineJson.pipelineUrl, bookVersion = pipelineJson.bookVersion, description = pipelineJson.description, parameters = pipelineJson.parameters, tasks = pipelineJson.tasks;
428
+ var pipelineString = "# ".concat(title);
429
+ if (description) {
430
+ pipelineString += '\n\n';
431
+ pipelineString += description;
432
+ }
433
+ var commands = [];
434
+ if (pipelineUrl) {
435
+ commands.push("PIPELINE URL ".concat(pipelineUrl));
436
+ }
437
+ if (bookVersion !== "undefined") {
438
+ commands.push("BOOK VERSION ".concat(bookVersion));
439
+ }
440
+ // TODO: [main] !!5 This increases size of the bundle and is probbably not necessary
441
+ pipelineString = prettifyMarkdown(pipelineString);
442
+ try {
443
+ for (var _g = __values(parameters.filter(function (_a) {
444
+ var isInput = _a.isInput;
445
+ return isInput;
446
+ })), _h = _g.next(); !_h.done; _h = _g.next()) {
447
+ var parameter = _h.value;
448
+ commands.push("INPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
449
+ }
450
+ }
451
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
452
+ finally {
453
+ try {
454
+ if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
455
+ }
456
+ finally { if (e_1) throw e_1.error; }
457
+ }
458
+ try {
459
+ for (var _j = __values(parameters.filter(function (_a) {
460
+ var isOutput = _a.isOutput;
461
+ return isOutput;
462
+ })), _k = _j.next(); !_k.done; _k = _j.next()) {
463
+ var parameter = _k.value;
464
+ commands.push("OUTPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
465
+ }
466
+ }
467
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
468
+ finally {
469
+ try {
470
+ if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
471
+ }
472
+ finally { if (e_2) throw e_2.error; }
473
+ }
474
+ pipelineString += '\n\n';
475
+ pipelineString += commands.map(function (command) { return "- ".concat(command); }).join('\n');
476
+ try {
477
+ for (var tasks_1 = __values(tasks), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) {
478
+ var task = tasks_1_1.value;
479
+ var
480
+ /* Note: Not using:> name, */
481
+ title_1 = task.title, description_1 = task.description,
482
+ /* Note: dependentParameterNames, */
483
+ jokers = task.jokerParameterNames, taskType = task.taskType, content = task.content, postprocessing = task.postprocessingFunctionNames, expectations = task.expectations, format = task.format, resultingParameterName = task.resultingParameterName;
484
+ pipelineString += '\n\n';
485
+ pipelineString += "## ".concat(title_1);
486
+ if (description_1) {
487
+ pipelineString += '\n\n';
488
+ pipelineString += description_1;
489
+ }
490
+ var commands_1 = [];
491
+ var contentLanguage = 'text';
492
+ if (taskType === 'PROMPT_TASK') {
493
+ var modelRequirements = task.modelRequirements;
494
+ var _l = modelRequirements || {}, modelName = _l.modelName, modelVariant = _l.modelVariant;
495
+ // Note: Do nothing, it is default
496
+ // commands.push(`PROMPT`);
497
+ if (modelVariant) {
498
+ commands_1.push("MODEL VARIANT ".concat(capitalize(modelVariant)));
499
+ }
500
+ if (modelName) {
501
+ commands_1.push("MODEL NAME `".concat(modelName, "`"));
502
+ }
503
+ }
504
+ else if (taskType === 'SIMPLE_TASK') {
505
+ commands_1.push("SIMPLE TEMPLATE");
506
+ // Note: Nothing special here
507
+ }
508
+ else if (taskType === 'SCRIPT_TASK') {
509
+ commands_1.push("SCRIPT");
510
+ if (task.contentLanguage) {
511
+ contentLanguage = task.contentLanguage;
512
+ }
513
+ else {
514
+ contentLanguage = '';
515
+ }
516
+ }
517
+ else if (taskType === 'DIALOG_TASK') {
518
+ commands_1.push("DIALOG");
519
+ // Note: Nothing special here
520
+ } // <- }else if([🅱]
521
+ if (jokers) {
522
+ try {
523
+ 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()) {
524
+ var joker = jokers_1_1.value;
525
+ commands_1.push("JOKER {".concat(joker, "}"));
526
+ }
527
+ }
528
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
529
+ finally {
530
+ try {
531
+ if (jokers_1_1 && !jokers_1_1.done && (_d = jokers_1.return)) _d.call(jokers_1);
532
+ }
533
+ finally { if (e_4) throw e_4.error; }
534
+ }
535
+ } /* not else */
536
+ if (postprocessing) {
537
+ try {
538
+ 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()) {
539
+ var postprocessingFunctionName = postprocessing_1_1.value;
540
+ commands_1.push("POSTPROCESSING `".concat(postprocessingFunctionName, "`"));
541
+ }
542
+ }
543
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
544
+ finally {
545
+ try {
546
+ if (postprocessing_1_1 && !postprocessing_1_1.done && (_e = postprocessing_1.return)) _e.call(postprocessing_1);
547
+ }
548
+ finally { if (e_5) throw e_5.error; }
549
+ }
550
+ } /* not else */
551
+ if (expectations) {
552
+ try {
553
+ for (var _m = (e_6 = void 0, __values(Object.entries(expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
554
+ var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
555
+ if (min === max) {
556
+ commands_1.push("EXPECT EXACTLY ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
557
+ }
558
+ else {
559
+ if (min !== undefined) {
560
+ commands_1.push("EXPECT MIN ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
561
+ } /* not else */
562
+ if (max !== undefined) {
563
+ commands_1.push("EXPECT MAX ".concat(max, " ").concat(capitalize(unit + (max > 1 ? 's' : ''))));
564
+ }
565
+ }
566
+ }
567
+ }
568
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
569
+ finally {
570
+ try {
571
+ if (_o && !_o.done && (_f = _m.return)) _f.call(_m);
572
+ }
573
+ finally { if (e_6) throw e_6.error; }
574
+ }
575
+ } /* not else */
576
+ if (format) {
577
+ if (format === 'JSON') {
578
+ // TODO: @deprecated remove
579
+ commands_1.push("FORMAT JSON");
580
+ }
581
+ } /* not else */
582
+ pipelineString += '\n\n';
583
+ pipelineString += commands_1.map(function (command) { return "- ".concat(command); }).join('\n');
584
+ pipelineString += '\n\n';
585
+ pipelineString += '```' + contentLanguage;
586
+ pipelineString += '\n';
587
+ pipelineString += spaceTrim(content);
588
+ // <- TODO: [main] !!3 Escape
589
+ // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
590
+ pipelineString += '\n';
591
+ pipelineString += '```';
592
+ pipelineString += '\n\n';
593
+ pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
594
+ }
595
+ }
596
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
597
+ finally {
598
+ try {
599
+ if (tasks_1_1 && !tasks_1_1.done && (_c = tasks_1.return)) _c.call(tasks_1);
600
+ }
601
+ finally { if (e_3) throw e_3.error; }
602
+ }
603
+ return validatePipelineString(pipelineString);
573
604
  }
574
605
  /**
575
- * TODO: [🧠] Is there a way how to meaningfully test this utility
606
+ * @private internal utility of `pipelineJsonToString`
576
607
  */
577
-
608
+ function taskParameterJsonToString(taskParameterJson) {
609
+ var name = taskParameterJson.name, description = taskParameterJson.description;
610
+ var parameterString = "{".concat(name, "}");
611
+ if (description) {
612
+ parameterString = "".concat(parameterString, " ").concat(description);
613
+ }
614
+ return parameterString;
615
+ }
578
616
  /**
579
- * Order of keys in the pipeline JSON
580
- *
581
- * @public exported from `@promptbook/core`
617
+ * TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
618
+ * TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
619
+ * TODO: [🏛] Maybe make some markdown builder
620
+ * TODO: [🏛] Escape all
621
+ * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
582
622
  */
583
- var ORDER_OF_PIPELINE_JSON = [
584
- // Note: [🍙] In this order will be pipeline serialized
585
- 'title',
586
- 'pipelineUrl',
587
- 'bookVersion',
588
- 'description',
589
- 'formfactorName',
590
- 'parameters',
591
- 'tasks',
592
- 'personas',
593
- 'preparations',
594
- 'knowledgeSources',
595
- 'knowledgePieces',
596
- 'sources', // <- TODO: [🧠] Where should the `sources` be
597
- ];
623
+
598
624
  /**
599
- * Nonce which is used for replacing things in strings
625
+ * Orders JSON object by keys
600
626
  *
601
- * @private within the repository
627
+ * @returns The same type of object as the input re-ordered
628
+ * @public exported from `@promptbook/utils`
602
629
  */
603
- var REPLACING_NONCE = 'u$k42k%!V2zo34w7Fu#@QUHYPW';
630
+ function orderJson(options) {
631
+ var value = options.value, order = options.order;
632
+ var orderedValue = __assign(__assign({}, (order === undefined ? {} : Object.fromEntries(order.map(function (key) { return [key, undefined]; })))), value);
633
+ return orderedValue;
634
+ }
635
+
604
636
  /**
605
- * @@@
637
+ * Freezes the given object and all its nested objects recursively
606
638
  *
607
- * @private within the repository
608
- */
609
- var RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
610
- /**
611
- * @@@
639
+ * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
640
+ * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
612
641
  *
613
- * @private within the repository
642
+ * @returns The same object as the input, but deeply frozen
643
+ * @public exported from `@promptbook/utils`
614
644
  */
615
- var RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
645
+ function $deepFreeze(objectValue) {
646
+ var e_1, _a;
647
+ if (Array.isArray(objectValue)) {
648
+ return Object.freeze(objectValue.map(function (item) { return $deepFreeze(item); }));
649
+ }
650
+ var propertyNames = Object.getOwnPropertyNames(objectValue);
651
+ try {
652
+ for (var propertyNames_1 = __values(propertyNames), propertyNames_1_1 = propertyNames_1.next(); !propertyNames_1_1.done; propertyNames_1_1 = propertyNames_1.next()) {
653
+ var propertyName = propertyNames_1_1.value;
654
+ var value = objectValue[propertyName];
655
+ if (value && typeof value === 'object') {
656
+ $deepFreeze(value);
657
+ }
658
+ }
659
+ }
660
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
661
+ finally {
662
+ try {
663
+ if (propertyNames_1_1 && !propertyNames_1_1.done && (_a = propertyNames_1.return)) _a.call(propertyNames_1);
664
+ }
665
+ finally { if (e_1) throw e_1.error; }
666
+ }
667
+ Object.freeze(objectValue);
668
+ return objectValue;
669
+ }
616
670
  /**
617
- * The names of the parameters that are reserved for special purposes
618
- *
619
- * @public exported from `@promptbook/core`
671
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
620
672
  */
621
- var RESERVED_PARAMETER_NAMES = exportJson({
622
- name: 'RESERVED_PARAMETER_NAMES',
623
- message: "The names of the parameters that are reserved for special purposes",
624
- value: [
625
- 'content',
626
- 'context',
627
- 'knowledge',
628
- 'examples',
629
- 'modelName',
630
- 'currentDate',
631
- // <- TODO: list here all command names
632
- // <- TODO: Add more like 'date', 'modelName',...
633
- // <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
634
- ],
635
- });
673
+
636
674
  /**
637
- * Note: [💞] Ignore a discrepancy between file name and entity name
675
+ * Make error report URL for the given error
676
+ *
677
+ * @private private within the repository
638
678
  */
679
+ function getErrorReportUrl(error) {
680
+ var report = {
681
+ title: "\uD83D\uDC1C Error report from ".concat(NAME),
682
+ body: spaceTrim(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 "); }),
683
+ };
684
+ var reportUrl = new URL("https://github.com/webgptorg/promptbook/issues/new");
685
+ reportUrl.searchParams.set('labels', 'bug');
686
+ reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
687
+ reportUrl.searchParams.set('title', report.title);
688
+ reportUrl.searchParams.set('body', report.body);
689
+ return reportUrl;
690
+ }
639
691
 
640
692
  /**
641
- * This error type indicates that some tools are missing for pipeline execution or preparation
693
+ * This error type indicates that the error should not happen and its last check before crashing with some other error
642
694
  *
643
695
  * @public exported from `@promptbook/core`
644
696
  */
645
- var MissingToolsError = /** @class */ (function (_super) {
646
- __extends(MissingToolsError, _super);
647
- function MissingToolsError(message) {
648
- var _this = _super.call(this, spaceTrim$1(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;
649
- _this.name = 'MissingToolsError';
650
- Object.setPrototypeOf(_this, MissingToolsError.prototype);
697
+ var UnexpectedError = /** @class */ (function (_super) {
698
+ __extends(UnexpectedError, _super);
699
+ function UnexpectedError(message) {
700
+ var _this = _super.call(this, spaceTrim$1(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;
701
+ _this.name = 'UnexpectedError';
702
+ Object.setPrototypeOf(_this, UnexpectedError.prototype);
651
703
  return _this;
652
704
  }
653
- return MissingToolsError;
705
+ return UnexpectedError;
654
706
  }(Error));
655
707
 
656
708
  /**
657
- * Async version of Array.forEach
709
+ * Checks if the value is [🚉] serializable as JSON
710
+ * If not, throws an UnexpectedError with a rich error message and tracking
658
711
  *
659
- * @param array - Array to iterate over
660
- * @param options - Options for the function
661
- * @param callbackfunction - Function to call for each item
712
+ * - Almost all primitives are serializable BUT:
713
+ * - `undefined` is not serializable
714
+ * - `NaN` is not serializable
715
+ * - Objects and arrays are serializable if all their properties are serializable
716
+ * - Functions are not serializable
717
+ * - Circular references are not serializable
718
+ * - `Date` objects are not serializable
719
+ * - `Map` and `Set` objects are not serializable
720
+ * - `RegExp` objects are not serializable
721
+ * - `Error` objects are not serializable
722
+ * - `Symbol` objects are not serializable
723
+ * - And much more...
724
+ *
725
+ * @throws UnexpectedError if the value is not serializable as JSON
662
726
  * @public exported from `@promptbook/utils`
663
- * @deprecated [🪂] Use queues instead
664
727
  */
665
- function forEachAsync(array, options, callbackfunction) {
666
- return __awaiter(this, void 0, void 0, function () {
667
- var _a, maxParallelCount, index, runningTasks, tasks, _loop_1, _b, _c, item, e_1_1;
668
- var e_1, _d;
669
- return __generator(this, function (_e) {
670
- switch (_e.label) {
671
- case 0:
672
- _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? Infinity : _a;
673
- index = 0;
674
- runningTasks = [];
675
- tasks = [];
676
- _loop_1 = function (item) {
677
- var currentIndex, task;
678
- return __generator(this, function (_f) {
679
- switch (_f.label) {
680
- case 0:
681
- currentIndex = index++;
682
- task = callbackfunction(item, currentIndex, array);
683
- tasks.push(task);
684
- runningTasks.push(task);
685
- /* not await */ Promise.resolve(task).then(function () {
686
- runningTasks = runningTasks.filter(function (t) { return t !== task; });
687
- });
688
- if (!(maxParallelCount < runningTasks.length)) return [3 /*break*/, 2];
689
- return [4 /*yield*/, Promise.race(runningTasks)];
690
- case 1:
691
- _f.sent();
692
- _f.label = 2;
693
- case 2: return [2 /*return*/];
694
- }
695
- });
696
- };
697
- _e.label = 1;
698
- case 1:
699
- _e.trys.push([1, 6, 7, 8]);
700
- _b = __values(array), _c = _b.next();
701
- _e.label = 2;
702
- case 2:
703
- if (!!_c.done) return [3 /*break*/, 5];
704
- item = _c.value;
705
- return [5 /*yield**/, _loop_1(item)];
706
- case 3:
707
- _e.sent();
708
- _e.label = 4;
709
- case 4:
710
- _c = _b.next();
711
- return [3 /*break*/, 2];
712
- case 5: return [3 /*break*/, 8];
713
- case 6:
714
- e_1_1 = _e.sent();
715
- e_1 = { error: e_1_1 };
716
- return [3 /*break*/, 8];
717
- case 7:
718
- try {
719
- if (_c && !_c.done && (_d = _b.return)) _d.call(_b);
728
+ function checkSerializableAsJson(options) {
729
+ var e_1, _a;
730
+ var value = options.value, name = options.name, message = options.message;
731
+ if (value === undefined) {
732
+ throw new UnexpectedError("".concat(name, " is undefined"));
733
+ }
734
+ else if (value === null) {
735
+ return;
736
+ }
737
+ else if (typeof value === 'boolean') {
738
+ return;
739
+ }
740
+ else if (typeof value === 'number' && !isNaN(value)) {
741
+ return;
742
+ }
743
+ else if (typeof value === 'string') {
744
+ return;
745
+ }
746
+ else if (typeof value === 'symbol') {
747
+ throw new UnexpectedError("".concat(name, " is symbol"));
748
+ }
749
+ else if (typeof value === 'function') {
750
+ throw new UnexpectedError("".concat(name, " is function"));
751
+ }
752
+ else if (typeof value === 'object' && Array.isArray(value)) {
753
+ for (var i = 0; i < value.length; i++) {
754
+ checkSerializableAsJson({ name: "".concat(name, "[").concat(i, "]"), value: value[i], message: message });
755
+ }
756
+ }
757
+ else if (typeof value === 'object') {
758
+ if (value instanceof Date) {
759
+ throw new UnexpectedError(spaceTrim(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 "); }));
760
+ }
761
+ else if (value instanceof Map) {
762
+ throw new UnexpectedError("".concat(name, " is Map"));
763
+ }
764
+ else if (value instanceof Set) {
765
+ throw new UnexpectedError("".concat(name, " is Set"));
766
+ }
767
+ else if (value instanceof RegExp) {
768
+ throw new UnexpectedError("".concat(name, " is RegExp"));
769
+ }
770
+ else if (value instanceof Error) {
771
+ throw new UnexpectedError(spaceTrim(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 "); }));
772
+ }
773
+ else {
774
+ try {
775
+ for (var _b = __values(Object.entries(value)), _c = _b.next(); !_c.done; _c = _b.next()) {
776
+ var _d = __read(_c.value, 2), subName = _d[0], subValue = _d[1];
777
+ if (subValue === undefined) {
778
+ // Note: undefined in object is serializable - it is just omited
779
+ continue;
720
780
  }
721
- finally { if (e_1) throw e_1.error; }
722
- return [7 /*endfinally*/];
723
- case 8: return [4 /*yield*/, Promise.all(tasks)];
724
- case 9:
725
- _e.sent();
726
- return [2 /*return*/];
781
+ checkSerializableAsJson({ name: "".concat(name, ".").concat(subName), value: subValue, message: message });
782
+ }
727
783
  }
728
- });
729
- });
730
- }
731
-
732
- /**
733
- * Represents the usage with no resources consumed
734
- *
735
- * @public exported from `@promptbook/core`
736
- */
737
- var ZERO_USAGE = $deepFreeze({
738
- price: { value: 0 },
739
- input: {
740
- tokensCount: { value: 0 },
741
- charactersCount: { value: 0 },
742
- wordsCount: { value: 0 },
743
- sentencesCount: { value: 0 },
744
- linesCount: { value: 0 },
745
- paragraphsCount: { value: 0 },
746
- pagesCount: { value: 0 },
747
- },
748
- output: {
749
- tokensCount: { value: 0 },
750
- charactersCount: { value: 0 },
751
- wordsCount: { value: 0 },
752
- sentencesCount: { value: 0 },
753
- linesCount: { value: 0 },
754
- paragraphsCount: { value: 0 },
755
- pagesCount: { value: 0 },
756
- },
757
- });
758
- /**
759
- * Represents the usage with unknown resources consumed
760
- *
761
- * @public exported from `@promptbook/core`
762
- */
763
- $deepFreeze({
764
- price: { value: 0, isUncertain: true },
765
- input: {
766
- tokensCount: { value: 0, isUncertain: true },
767
- charactersCount: { value: 0, isUncertain: true },
768
- wordsCount: { value: 0, isUncertain: true },
769
- sentencesCount: { value: 0, isUncertain: true },
770
- linesCount: { value: 0, isUncertain: true },
771
- paragraphsCount: { value: 0, isUncertain: true },
772
- pagesCount: { value: 0, isUncertain: true },
773
- },
774
- output: {
775
- tokensCount: { value: 0, isUncertain: true },
776
- charactersCount: { value: 0, isUncertain: true },
777
- wordsCount: { value: 0, isUncertain: true },
778
- sentencesCount: { value: 0, isUncertain: true },
779
- linesCount: { value: 0, isUncertain: true },
780
- paragraphsCount: { value: 0, isUncertain: true },
781
- pagesCount: { value: 0, isUncertain: true },
782
- },
783
- });
784
- /**
785
- * Note: [💞] Ignore a discrepancy between file name and entity name
786
- */
787
-
788
- /**
789
- * Function `addUsage` will add multiple usages into one
790
- *
791
- * Note: If you provide 0 values, it returns ZERO_USAGE
792
- *
793
- * @public exported from `@promptbook/core`
794
- */
795
- function addUsage() {
796
- var usageItems = [];
797
- for (var _i = 0; _i < arguments.length; _i++) {
798
- usageItems[_i] = arguments[_i];
799
- }
800
- return usageItems.reduce(function (acc, item) {
801
- var e_1, _a, e_2, _b;
802
- var _c;
803
- acc.price.value += ((_c = item.price) === null || _c === void 0 ? void 0 : _c.value) || 0;
804
- try {
805
- for (var _d = __values(Object.keys(acc.input)), _e = _d.next(); !_e.done; _e = _d.next()) {
806
- var key = _e.value;
807
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
808
- //@ts-ignore
809
- if (item.input[key]) {
810
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
811
- //@ts-ignore
812
- acc.input[key].value += item.input[key].value || 0;
813
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
814
- //@ts-ignore
815
- if (item.input[key].isUncertain) {
816
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
817
- //@ts-ignore
818
- acc.input[key].isUncertain = true;
819
- }
784
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
785
+ finally {
786
+ try {
787
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
820
788
  }
789
+ finally { if (e_1) throw e_1.error; }
821
790
  }
822
- }
823
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
824
- finally {
825
791
  try {
826
- if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
792
+ JSON.stringify(value); // <- TODO: [0]
827
793
  }
828
- finally { if (e_1) throw e_1.error; }
829
- }
830
- try {
831
- for (var _f = __values(Object.keys(acc.output)), _g = _f.next(); !_g.done; _g = _f.next()) {
832
- var key = _g.value;
833
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
834
- //@ts-ignore
835
- if (item.output[key]) {
836
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
837
- //@ts-ignore
838
- acc.output[key].value += item.output[key].value || 0;
839
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
840
- //@ts-ignore
841
- if (item.output[key].isUncertain) {
842
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
843
- //@ts-ignore
844
- acc.output[key].isUncertain = true;
845
- }
794
+ catch (error) {
795
+ if (!(error instanceof Error)) {
796
+ throw error;
846
797
  }
798
+ throw new UnexpectedError(spaceTrim(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 "); }));
847
799
  }
848
- }
849
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
850
- finally {
851
- try {
852
- if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
800
+ /*
801
+ TODO: [0] Is there some more elegant way to check circular references?
802
+ const seen = new Set();
803
+ const stack = [{ value }];
804
+ while (stack.length > 0) {
805
+ const { value } = stack.pop()!;
806
+ if (typeof value === 'object' && value !== null) {
807
+ if (seen.has(value)) {
808
+ throw new UnexpectedError(`${name} has circular reference`);
809
+ }
810
+ seen.add(value);
811
+ if (Array.isArray(value)) {
812
+ stack.push(...value.map((value) => ({ value })));
813
+ } else {
814
+ stack.push(...Object.values(value).map((value) => ({ value })));
815
+ }
816
+ }
853
817
  }
854
- finally { if (e_2) throw e_2.error; }
818
+ */
819
+ return;
855
820
  }
856
- return acc;
857
- }, deepClone(ZERO_USAGE));
858
- }
859
-
860
- /**
861
- * Intercepts LLM tools and counts total usage of the tools
862
- *
863
- * @param llmTools LLM tools to be intercepted with usage counting
864
- * @returns LLM tools with same functionality with added total cost counting
865
- * @public exported from `@promptbook/core`
866
- */
867
- function countTotalUsage(llmTools) {
868
- var _this = this;
869
- var totalUsage = ZERO_USAGE;
870
- var proxyTools = {
871
- get title() {
872
- // TODO: [🧠] Maybe put here some suffix
873
- return llmTools.title;
874
- },
875
- get description() {
876
- // TODO: [🧠] Maybe put here some suffix
877
- return llmTools.description;
878
- },
879
- checkConfiguration: function () {
880
- return __awaiter(this, void 0, void 0, function () {
881
- return __generator(this, function (_a) {
882
- return [2 /*return*/, /* not await */ llmTools.checkConfiguration()];
883
- });
884
- });
885
- },
886
- listModels: function () {
887
- return /* not await */ llmTools.listModels();
888
- },
889
- getTotalUsage: function () {
890
- // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
891
- return totalUsage;
892
- },
893
- };
894
- if (llmTools.callChatModel !== undefined) {
895
- proxyTools.callChatModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
896
- var promptResult;
897
- return __generator(this, function (_a) {
898
- switch (_a.label) {
899
- case 0: return [4 /*yield*/, llmTools.callChatModel(prompt)];
900
- case 1:
901
- promptResult = _a.sent();
902
- totalUsage = addUsage(totalUsage, promptResult.usage);
903
- return [2 /*return*/, promptResult];
904
- }
905
- });
906
- }); };
907
- }
908
- if (llmTools.callCompletionModel !== undefined) {
909
- proxyTools.callCompletionModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
910
- var promptResult;
911
- return __generator(this, function (_a) {
912
- switch (_a.label) {
913
- case 0: return [4 /*yield*/, llmTools.callCompletionModel(prompt)];
914
- case 1:
915
- promptResult = _a.sent();
916
- totalUsage = addUsage(totalUsage, promptResult.usage);
917
- return [2 /*return*/, promptResult];
918
- }
919
- });
920
- }); };
921
821
  }
922
- if (llmTools.callEmbeddingModel !== undefined) {
923
- proxyTools.callEmbeddingModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
924
- var promptResult;
925
- return __generator(this, function (_a) {
926
- switch (_a.label) {
927
- case 0: return [4 /*yield*/, llmTools.callEmbeddingModel(prompt)];
928
- case 1:
929
- promptResult = _a.sent();
930
- totalUsage = addUsage(totalUsage, promptResult.usage);
931
- return [2 /*return*/, promptResult];
932
- }
933
- });
934
- }); };
822
+ else {
823
+ throw new UnexpectedError(spaceTrim(function (block) { return "\n `".concat(name, "` is unknown type\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
935
824
  }
936
- // <- Note: [🤖]
937
- return proxyTools;
938
825
  }
939
826
  /**
940
- * TODO: [🧠][💸] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
941
- * TODO: [🧠] Is there some meaningfull way how to test this util
942
- * TODO: [🧠][🌯] Maybe a way how to hide ability to `get totalUsage`
943
- * > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
944
- * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
827
+ * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
828
+ * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
829
+ * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
945
830
  */
946
831
 
947
832
  /**
948
- * This error indicates errors during the execution of the pipeline
833
+ * @@@
949
834
  *
950
- * @public exported from `@promptbook/core`
835
+ * @public exported from `@promptbook/utils`
836
+ */
837
+ function deepClone(objectValue) {
838
+ return JSON.parse(JSON.stringify(objectValue));
839
+ /*
840
+ TODO: [🧠] Is there a better implementation?
841
+ > const propertyNames = Object.getOwnPropertyNames(objectValue);
842
+ > for (const propertyName of propertyNames) {
843
+ > const value = (objectValue as really_any)[propertyName];
844
+ > if (value && typeof value === 'object') {
845
+ > deepClone(value);
846
+ > }
847
+ > }
848
+ > return Object.assign({}, objectValue);
849
+ */
850
+ }
851
+ /**
852
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
951
853
  */
952
- var PipelineExecutionError = /** @class */ (function (_super) {
953
- __extends(PipelineExecutionError, _super);
954
- function PipelineExecutionError(message) {
955
- var _this = _super.call(this, message) || this;
956
- _this.name = 'PipelineExecutionError';
957
- Object.setPrototypeOf(_this, PipelineExecutionError.prototype);
958
- return _this;
959
- }
960
- return PipelineExecutionError;
961
- }(Error));
962
854
 
963
855
  /**
964
- * Multiple LLM Execution Tools is a proxy server that uses multiple execution tools internally and exposes the executor interface externally.
856
+ * Utility to export a JSON object from a function
965
857
  *
966
- * Note: Internal utility of `joinLlmExecutionTools` but exposed type
967
- * @public exported from `@promptbook/core`
858
+ * 1) Checks if the value is serializable as JSON
859
+ * 2) Makes a deep clone of the object
860
+ * 2) Orders the object properties
861
+ * 2) Deeply freezes the cloned object
862
+ *
863
+ * Note: This function does not mutates the given object
864
+ *
865
+ * @returns The same type of object as the input but read-only and re-ordered
866
+ * @public exported from `@promptbook/utils`
968
867
  */
969
- var MultipleLlmExecutionTools = /** @class */ (function () {
970
- /**
971
- * Gets array of execution tools in order of priority
972
- */
973
- function MultipleLlmExecutionTools() {
974
- var llmExecutionTools = [];
975
- for (var _i = 0; _i < arguments.length; _i++) {
976
- llmExecutionTools[_i] = arguments[_i];
977
- }
978
- this.llmExecutionTools = llmExecutionTools;
979
- }
980
- Object.defineProperty(MultipleLlmExecutionTools.prototype, "title", {
981
- get: function () {
982
- return 'Multiple LLM Providers';
983
- },
984
- enumerable: false,
985
- configurable: true
986
- });
987
- Object.defineProperty(MultipleLlmExecutionTools.prototype, "description", {
988
- get: function () {
989
- return this.llmExecutionTools.map(function (_a, index) {
990
- var title = _a.title;
991
- return "".concat(index + 1, ") `").concat(title, "`");
992
- }).join('\n');
993
- },
994
- enumerable: false,
995
- configurable: true
996
- });
997
- /**
998
- * Check the configuration of all execution tools
999
- */
1000
- MultipleLlmExecutionTools.prototype.checkConfiguration = function () {
1001
- return __awaiter(this, void 0, void 0, function () {
1002
- var _a, _b, llmExecutionTools, e_1_1;
1003
- var e_1, _c;
1004
- return __generator(this, function (_d) {
1005
- switch (_d.label) {
1006
- case 0:
1007
- _d.trys.push([0, 5, 6, 7]);
1008
- _a = __values(this.llmExecutionTools), _b = _a.next();
1009
- _d.label = 1;
1010
- case 1:
1011
- if (!!_b.done) return [3 /*break*/, 4];
1012
- llmExecutionTools = _b.value;
1013
- return [4 /*yield*/, llmExecutionTools.checkConfiguration()];
1014
- case 2:
1015
- _d.sent();
1016
- _d.label = 3;
1017
- case 3:
1018
- _b = _a.next();
1019
- return [3 /*break*/, 1];
1020
- case 4: return [3 /*break*/, 7];
1021
- case 5:
1022
- e_1_1 = _d.sent();
1023
- e_1 = { error: e_1_1 };
1024
- return [3 /*break*/, 7];
1025
- case 6:
1026
- try {
1027
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
1028
- }
1029
- finally { if (e_1) throw e_1.error; }
1030
- return [7 /*endfinally*/];
1031
- case 7: return [2 /*return*/];
1032
- }
1033
- });
1034
- });
1035
- };
1036
- /**
1037
- * List all available models that can be used
1038
- * This lists is a combination of all available models from all execution tools
1039
- */
1040
- MultipleLlmExecutionTools.prototype.listModels = function () {
1041
- return __awaiter(this, void 0, void 0, function () {
1042
- var availableModels, _a, _b, llmExecutionTools, models, e_2_1;
1043
- var e_2, _c;
1044
- return __generator(this, function (_d) {
1045
- switch (_d.label) {
1046
- case 0:
1047
- availableModels = [];
1048
- _d.label = 1;
1049
- case 1:
1050
- _d.trys.push([1, 6, 7, 8]);
1051
- _a = __values(this.llmExecutionTools), _b = _a.next();
1052
- _d.label = 2;
1053
- case 2:
1054
- if (!!_b.done) return [3 /*break*/, 5];
1055
- llmExecutionTools = _b.value;
1056
- return [4 /*yield*/, llmExecutionTools.listModels()];
1057
- case 3:
1058
- models = _d.sent();
1059
- availableModels.push.apply(availableModels, __spreadArray([], __read(models), false));
1060
- _d.label = 4;
1061
- case 4:
1062
- _b = _a.next();
1063
- return [3 /*break*/, 2];
1064
- case 5: return [3 /*break*/, 8];
1065
- case 6:
1066
- e_2_1 = _d.sent();
1067
- e_2 = { error: e_2_1 };
1068
- return [3 /*break*/, 8];
1069
- case 7:
1070
- try {
1071
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
1072
- }
1073
- finally { if (e_2) throw e_2.error; }
1074
- return [7 /*endfinally*/];
1075
- case 8: return [2 /*return*/, availableModels];
1076
- }
1077
- });
1078
- });
1079
- };
1080
- /**
1081
- * Calls the best available chat model
1082
- */
1083
- MultipleLlmExecutionTools.prototype.callChatModel = function (prompt) {
1084
- return this.callCommonModel(prompt);
1085
- };
1086
- /**
1087
- * Calls the best available completion model
1088
- */
1089
- MultipleLlmExecutionTools.prototype.callCompletionModel = function (prompt) {
1090
- return this.callCommonModel(prompt);
1091
- };
1092
- /**
1093
- * Calls the best available embedding model
1094
- */
1095
- MultipleLlmExecutionTools.prototype.callEmbeddingModel = function (prompt) {
1096
- return this.callCommonModel(prompt);
1097
- };
1098
- // <- Note: [🤖]
1099
- /**
1100
- * Calls the best available model
1101
- *
1102
- * Note: This should be private or protected but is public to be usable with duck typing
1103
- */
1104
- MultipleLlmExecutionTools.prototype.callCommonModel = function (prompt) {
1105
- return __awaiter(this, void 0, void 0, function () {
1106
- var errors, _a, _b, llmExecutionTools, _c, error_1, e_3_1;
1107
- var e_3, _d;
1108
- var _this = this;
1109
- return __generator(this, function (_e) {
1110
- switch (_e.label) {
1111
- case 0:
1112
- errors = [];
1113
- _e.label = 1;
1114
- case 1:
1115
- _e.trys.push([1, 15, 16, 17]);
1116
- _a = __values(this.llmExecutionTools), _b = _a.next();
1117
- _e.label = 2;
1118
- case 2:
1119
- if (!!_b.done) return [3 /*break*/, 14];
1120
- llmExecutionTools = _b.value;
1121
- _e.label = 3;
1122
- case 3:
1123
- _e.trys.push([3, 12, , 13]);
1124
- _c = prompt.modelRequirements.modelVariant;
1125
- switch (_c) {
1126
- case 'CHAT': return [3 /*break*/, 4];
1127
- case 'COMPLETION': return [3 /*break*/, 6];
1128
- case 'EMBEDDING': return [3 /*break*/, 8];
1129
- }
1130
- return [3 /*break*/, 10];
1131
- case 4:
1132
- if (llmExecutionTools.callChatModel === undefined) {
1133
- return [3 /*break*/, 13];
1134
- }
1135
- return [4 /*yield*/, llmExecutionTools.callChatModel(prompt)];
1136
- case 5: return [2 /*return*/, _e.sent()];
1137
- case 6:
1138
- if (llmExecutionTools.callCompletionModel === undefined) {
1139
- return [3 /*break*/, 13];
1140
- }
1141
- return [4 /*yield*/, llmExecutionTools.callCompletionModel(prompt)];
1142
- case 7: return [2 /*return*/, _e.sent()];
1143
- case 8:
1144
- if (llmExecutionTools.callEmbeddingModel === undefined) {
1145
- return [3 /*break*/, 13];
1146
- }
1147
- return [4 /*yield*/, llmExecutionTools.callEmbeddingModel(prompt)];
1148
- case 9: return [2 /*return*/, _e.sent()];
1149
- case 10: throw new UnexpectedError("Unknown model variant \"".concat(prompt.modelRequirements.modelVariant, "\""));
1150
- case 11: return [3 /*break*/, 13];
1151
- case 12:
1152
- error_1 = _e.sent();
1153
- if (!(error_1 instanceof Error) || error_1 instanceof UnexpectedError) {
1154
- throw error_1;
1155
- }
1156
- errors.push(error_1);
1157
- return [3 /*break*/, 13];
1158
- case 13:
1159
- _b = _a.next();
1160
- return [3 /*break*/, 2];
1161
- case 14: return [3 /*break*/, 17];
1162
- case 15:
1163
- e_3_1 = _e.sent();
1164
- e_3 = { error: e_3_1 };
1165
- return [3 /*break*/, 17];
1166
- case 16:
1167
- try {
1168
- if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
1169
- }
1170
- finally { if (e_3) throw e_3.error; }
1171
- return [7 /*endfinally*/];
1172
- case 17:
1173
- if (errors.length === 1) {
1174
- throw errors[0];
1175
- }
1176
- else if (errors.length > 1) {
1177
- throw new PipelineExecutionError(
1178
- // TODO: Tell which execution tools failed like
1179
- // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
1180
- // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
1181
- // 3) ...
1182
- spaceTrim(function (block) { return "\n All execution tools failed:\n\n ".concat(block(errors
1183
- .map(function (error, i) { return "".concat(i + 1, ") **").concat(error.name || 'Error', ":** ").concat(error.message); })
1184
- .join('\n')), "\n\n "); }));
1185
- }
1186
- else if (this.llmExecutionTools.length === 0) {
1187
- throw new PipelineExecutionError("You have not provided any `LlmExecutionTools`");
1188
- }
1189
- else {
1190
- throw new PipelineExecutionError(spaceTrim(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 "); }));
1191
- }
1192
- }
1193
- });
868
+ function exportJson(options) {
869
+ var name = options.name, value = options.value, order = options.order, message = options.message;
870
+ checkSerializableAsJson({ name: name, value: value, message: message });
871
+ var orderedValue =
872
+ // TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
873
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
874
+ // @ts-ignore
875
+ order === undefined
876
+ ? deepClone(value)
877
+ : orderJson({
878
+ value: value,
879
+ // <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
880
+ order: order,
1194
881
  });
1195
- };
1196
- return MultipleLlmExecutionTools;
1197
- }());
882
+ $deepFreeze(orderedValue);
883
+ return orderedValue;
884
+ }
1198
885
  /**
1199
- * TODO: [🧠][🎛] Aggregating multiple models - have result not only from one first aviable model BUT all of them
1200
- * 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
1201
- * Look how `countTotalUsage` (and `cacheLlmTools`) implements it
886
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
1202
887
  */
1203
888
 
1204
889
  /**
1205
- * Joins multiple LLM Execution Tools into one
1206
- *
1207
- * @returns {LlmExecutionTools} Single wrapper for multiple LlmExecutionTools
1208
- *
1209
- * 0) If there is no LlmExecutionTools, it warns and returns valid but empty LlmExecutionTools
1210
- * 1) If there is only one LlmExecutionTools, it returns it wrapped in a proxy object
1211
- * 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.
1212
- * 3) When all LlmExecutionTools fail, it throws an error with a list of all errors merged into one
1213
- *
1214
- *
1215
- * Tip: You don't have to use this function directly, just pass an array of LlmExecutionTools to the `ExecutionTools`
890
+ * Order of keys in the pipeline JSON
1216
891
  *
1217
892
  * @public exported from `@promptbook/core`
1218
893
  */
1219
- function joinLlmExecutionTools() {
1220
- var llmExecutionTools = [];
1221
- for (var _i = 0; _i < arguments.length; _i++) {
1222
- llmExecutionTools[_i] = arguments[_i];
1223
- }
1224
- if (llmExecutionTools.length === 0) {
1225
- var warningMessage = spaceTrim("\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 ");
1226
- // TODO: [🟥] Detect browser / node and make it colorfull
1227
- console.warn(warningMessage);
1228
- /*
1229
- return {
1230
- async listModels() {
1231
- // TODO: [🟥] Detect browser / node and make it colorfull
1232
- console.warn(
1233
- spaceTrim(
1234
- (block) => `
1235
-
1236
- You can't list models because you have no LLM Execution Tools defined:
894
+ var ORDER_OF_PIPELINE_JSON = [
895
+ // Note: [🍙] In this order will be pipeline serialized
896
+ 'title',
897
+ 'pipelineUrl',
898
+ 'bookVersion',
899
+ 'description',
900
+ 'formfactorName',
901
+ 'parameters',
902
+ 'tasks',
903
+ 'personas',
904
+ 'preparations',
905
+ 'knowledgeSources',
906
+ 'knowledgePieces',
907
+ 'sources', // <- TODO: [🧠] Where should the `sources` be
908
+ ];
909
+ /**
910
+ * Nonce which is used for replacing things in strings
911
+ *
912
+ * @private within the repository
913
+ */
914
+ var REPLACING_NONCE = 'u$k42k%!V2zo34w7Fu#@QUHYPW';
915
+ /**
916
+ * @@@
917
+ *
918
+ * @private within the repository
919
+ */
920
+ var RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
921
+ /**
922
+ * @@@
923
+ *
924
+ * @private within the repository
925
+ */
926
+ var RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
927
+ /**
928
+ * The names of the parameters that are reserved for special purposes
929
+ *
930
+ * @public exported from `@promptbook/core`
931
+ */
932
+ var RESERVED_PARAMETER_NAMES = exportJson({
933
+ name: 'RESERVED_PARAMETER_NAMES',
934
+ message: "The names of the parameters that are reserved for special purposes",
935
+ value: [
936
+ 'content',
937
+ 'context',
938
+ 'knowledge',
939
+ 'examples',
940
+ 'modelName',
941
+ 'currentDate',
942
+ // <- TODO: list here all command names
943
+ // <- TODO: Add more like 'date', 'modelName',...
944
+ // <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
945
+ ],
946
+ });
947
+ /**
948
+ * Note: [💞] Ignore a discrepancy between file name and entity name
949
+ */
1237
950
 
1238
- tl;dr
951
+ /**
952
+ * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
953
+ *
954
+ * @public exported from `@promptbook/core`
955
+ */
956
+ var PipelineLogicError = /** @class */ (function (_super) {
957
+ __extends(PipelineLogicError, _super);
958
+ function PipelineLogicError(message) {
959
+ var _this = _super.call(this, message) || this;
960
+ _this.name = 'PipelineLogicError';
961
+ Object.setPrototypeOf(_this, PipelineLogicError.prototype);
962
+ return _this;
963
+ }
964
+ return PipelineLogicError;
965
+ }(Error));
1239
966
 
1240
- ${block(warningMessage)}
1241
- `,
1242
- ),
1243
- );
1244
- return [];
1245
- },
1246
- };
1247
- */
967
+ /**
968
+ * Tests if given string is valid semantic version
969
+ *
970
+ * Note: There are two simmilar functions:
971
+ * - `isValidSemanticVersion` which tests any semantic version
972
+ * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
973
+ *
974
+ * @public exported from `@promptbook/utils`
975
+ */
976
+ function isValidSemanticVersion(version) {
977
+ if (typeof version !== 'string') {
978
+ return false;
1248
979
  }
1249
- return new (MultipleLlmExecutionTools.bind.apply(MultipleLlmExecutionTools, __spreadArray([void 0], __read(llmExecutionTools), false)))();
980
+ if (version.startsWith('0.0.0')) {
981
+ return false;
982
+ }
983
+ return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
1250
984
  }
985
+
1251
986
  /**
1252
- * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
987
+ * Tests if given string is valid promptbook version
988
+ * It looks into list of known promptbook versions.
989
+ *
990
+ * @see https://www.npmjs.com/package/promptbook?activeTab=versions
991
+ * 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.
992
+ * Note: There are two simmilar functions:
993
+ * - `isValidSemanticVersion` which tests any semantic version
994
+ * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
995
+ *
996
+ * @public exported from `@promptbook/utils`
1253
997
  */
1254
-
1255
- 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"}];
998
+ function isValidPromptbookVersion(version) {
999
+ if (!isValidSemanticVersion(version)) {
1000
+ return false;
1001
+ }
1002
+ if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
1003
+ return false;
1004
+ }
1005
+ // <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
1006
+ return true;
1007
+ }
1256
1008
 
1257
1009
  /**
1258
- * Prettify the html code
1010
+ * Checks if an URL is reserved for private networks or localhost.
1259
1011
  *
1260
- * @param content raw html code
1261
- * @returns formatted html code
1262
- * @private withing the package because of HUGE size of prettier dependency
1012
+ * Note: There are two simmilar functions:
1013
+ * - `isUrlOnPrivateNetwork` which tests full URL
1014
+ * - `isHostnameOnPrivateNetwork` *(this one)* which tests just hostname
1015
+ *
1016
+ * @public exported from `@promptbook/utils`
1263
1017
  */
1264
- function prettifyMarkdown(content) {
1265
- try {
1266
- return format(content, {
1267
- parser: 'markdown',
1268
- plugins: [parserHtml],
1269
- // TODO: DRY - make some import or auto-copy of .prettierrc
1270
- endOfLine: 'lf',
1271
- tabWidth: 4,
1272
- singleQuote: true,
1273
- trailingComma: 'all',
1274
- arrowParens: 'always',
1275
- printWidth: 120,
1276
- htmlWhitespaceSensitivity: 'ignore',
1277
- jsxBracketSameLine: false,
1278
- bracketSpacing: true,
1279
- });
1018
+ function isHostnameOnPrivateNetwork(hostname) {
1019
+ if (hostname === 'example.com' ||
1020
+ hostname === 'localhost' ||
1021
+ hostname.endsWith('.localhost') ||
1022
+ hostname.endsWith('.local') ||
1023
+ hostname.endsWith('.test') ||
1024
+ hostname === '127.0.0.1' ||
1025
+ hostname === '::1') {
1026
+ return true;
1280
1027
  }
1281
- catch (error) {
1282
- // TODO: [🟥] Detect browser / node and make it colorfull
1283
- console.error('There was an error with prettifying the markdown, using the original as the fallback', {
1284
- error: error,
1285
- html: content,
1286
- });
1287
- return content;
1028
+ if (hostname.includes(':')) {
1029
+ // IPv6
1030
+ var ipParts = hostname.split(':');
1031
+ return ipParts[0] === 'fc00' || ipParts[0] === 'fd00' || ipParts[0] === 'fe80';
1032
+ }
1033
+ else {
1034
+ // IPv4
1035
+ var ipParts = hostname.split('.').map(function (part) { return Number.parseInt(part, 10); });
1036
+ return (ipParts[0] === 10 ||
1037
+ (ipParts[0] === 172 && ipParts[1] >= 16 && ipParts[1] <= 31) ||
1038
+ (ipParts[0] === 192 && ipParts[1] === 168));
1288
1039
  }
1289
1040
  }
1290
1041
 
1291
1042
  /**
1292
- * Makes first letter of a string uppercase
1043
+ * Checks if an IP address or hostname is reserved for private networks or localhost.
1044
+ *
1045
+ * Note: There are two simmilar functions:
1046
+ * - `isUrlOnPrivateNetwork` *(this one)* which tests full URL
1047
+ * - `isHostnameOnPrivateNetwork` which tests just hostname
1293
1048
  *
1049
+ * @param {string} ipAddress - The IP address to check.
1050
+ * @returns {boolean} Returns true if the IP address is reserved for private networks or localhost, otherwise false.
1294
1051
  * @public exported from `@promptbook/utils`
1295
1052
  */
1296
- function capitalize(word) {
1297
- return word.substring(0, 1).toUpperCase() + word.substring(1);
1053
+ function isUrlOnPrivateNetwork(url) {
1054
+ if (typeof url === 'string') {
1055
+ url = new URL(url);
1056
+ }
1057
+ return isHostnameOnPrivateNetwork(url.hostname);
1298
1058
  }
1299
1059
 
1300
1060
  /**
1301
- * Converts promptbook in JSON format to string format
1061
+ * Tests if given string is valid URL.
1302
1062
  *
1303
- * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
1304
- * @param pipelineJson Promptbook in JSON format (.book.json)
1305
- * @returns Promptbook in string format (.book.md)
1306
- * @public exported from `@promptbook/core`
1063
+ * Note: Dataurl are considered perfectly valid.
1064
+ * Note: There are two simmilar functions:
1065
+ * - `isValidUrl` which tests any URL
1066
+ * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
1067
+ *
1068
+ * @public exported from `@promptbook/utils`
1307
1069
  */
1308
- function pipelineJsonToString(pipelineJson) {
1309
- var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
1310
- var title = pipelineJson.title, pipelineUrl = pipelineJson.pipelineUrl, bookVersion = pipelineJson.bookVersion, description = pipelineJson.description, parameters = pipelineJson.parameters, tasks = pipelineJson.tasks;
1311
- var pipelineString = "# ".concat(title);
1312
- if (description) {
1313
- pipelineString += '\n\n';
1314
- pipelineString += description;
1315
- }
1316
- var commands = [];
1317
- if (pipelineUrl) {
1318
- commands.push("PIPELINE URL ".concat(pipelineUrl));
1319
- }
1320
- if (bookVersion !== "undefined") {
1321
- commands.push("BOOK VERSION ".concat(bookVersion));
1070
+ function isValidUrl(url) {
1071
+ if (typeof url !== 'string') {
1072
+ return false;
1322
1073
  }
1323
- // TODO: [main] !!!!! This increases size of the bundle and is probbably not necessary
1324
- pipelineString = prettifyMarkdown(pipelineString);
1325
1074
  try {
1326
- for (var _g = __values(parameters.filter(function (_a) {
1327
- var isInput = _a.isInput;
1328
- return isInput;
1329
- })), _h = _g.next(); !_h.done; _h = _g.next()) {
1330
- var parameter = _h.value;
1331
- commands.push("INPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
1075
+ if (url.startsWith('blob:')) {
1076
+ url = url.replace(/^blob:/, '');
1332
1077
  }
1333
- }
1334
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
1335
- finally {
1336
- try {
1337
- if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
1078
+ var urlObject = new URL(url /* because fail is handled */);
1079
+ if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
1080
+ return false;
1338
1081
  }
1339
- finally { if (e_1) throw e_1.error; }
1082
+ return true;
1340
1083
  }
1341
- try {
1342
- for (var _j = __values(parameters.filter(function (_a) {
1343
- var isOutput = _a.isOutput;
1344
- return isOutput;
1345
- })), _k = _j.next(); !_k.done; _k = _j.next()) {
1346
- var parameter = _k.value;
1347
- commands.push("OUTPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
1348
- }
1084
+ catch (error) {
1085
+ return false;
1349
1086
  }
1350
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
1351
- finally {
1352
- try {
1353
- if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
1354
- }
1355
- finally { if (e_2) throw e_2.error; }
1356
- }
1357
- pipelineString += '\n\n';
1358
- pipelineString += commands.map(function (command) { return "- ".concat(command); }).join('\n');
1359
- try {
1360
- for (var tasks_1 = __values(tasks), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) {
1361
- var task = tasks_1_1.value;
1362
- var
1363
- /* Note: Not using:> name, */
1364
- title_1 = task.title, description_1 = task.description,
1365
- /* Note: dependentParameterNames, */
1366
- jokers = task.jokerParameterNames, taskType = task.taskType, content = task.content, postprocessing = task.postprocessingFunctionNames, expectations = task.expectations, format = task.format, resultingParameterName = task.resultingParameterName;
1367
- pipelineString += '\n\n';
1368
- pipelineString += "## ".concat(title_1);
1369
- if (description_1) {
1370
- pipelineString += '\n\n';
1371
- pipelineString += description_1;
1372
- }
1373
- var commands_1 = [];
1374
- var contentLanguage = 'text';
1375
- if (taskType === 'PROMPT_TASK') {
1376
- var modelRequirements = task.modelRequirements;
1377
- var _l = modelRequirements || {}, modelName = _l.modelName, modelVariant = _l.modelVariant;
1378
- // Note: Do nothing, it is default
1379
- // commands.push(`PROMPT`);
1380
- if (modelVariant) {
1381
- commands_1.push("MODEL VARIANT ".concat(capitalize(modelVariant)));
1382
- }
1383
- if (modelName) {
1384
- commands_1.push("MODEL NAME `".concat(modelName, "`"));
1385
- }
1386
- }
1387
- else if (taskType === 'SIMPLE_TASK') {
1388
- commands_1.push("SIMPLE TEMPLATE");
1389
- // Note: Nothing special here
1390
- }
1391
- else if (taskType === 'SCRIPT_TASK') {
1392
- commands_1.push("SCRIPT");
1393
- if (task.contentLanguage) {
1394
- contentLanguage = task.contentLanguage;
1395
- }
1396
- else {
1397
- contentLanguage = '';
1398
- }
1399
- }
1400
- else if (taskType === 'DIALOG_TASK') {
1401
- commands_1.push("DIALOG");
1402
- // Note: Nothing special here
1403
- } // <- }else if([🅱]
1404
- if (jokers) {
1405
- try {
1406
- 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()) {
1407
- var joker = jokers_1_1.value;
1408
- commands_1.push("JOKER {".concat(joker, "}"));
1409
- }
1410
- }
1411
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
1412
- finally {
1413
- try {
1414
- if (jokers_1_1 && !jokers_1_1.done && (_d = jokers_1.return)) _d.call(jokers_1);
1415
- }
1416
- finally { if (e_4) throw e_4.error; }
1417
- }
1418
- } /* not else */
1419
- if (postprocessing) {
1420
- try {
1421
- 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()) {
1422
- var postprocessingFunctionName = postprocessing_1_1.value;
1423
- commands_1.push("POSTPROCESSING `".concat(postprocessingFunctionName, "`"));
1424
- }
1425
- }
1426
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
1427
- finally {
1428
- try {
1429
- if (postprocessing_1_1 && !postprocessing_1_1.done && (_e = postprocessing_1.return)) _e.call(postprocessing_1);
1430
- }
1431
- finally { if (e_5) throw e_5.error; }
1432
- }
1433
- } /* not else */
1434
- if (expectations) {
1435
- try {
1436
- for (var _m = (e_6 = void 0, __values(Object.entries(expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
1437
- var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
1438
- if (min === max) {
1439
- commands_1.push("EXPECT EXACTLY ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
1440
- }
1441
- else {
1442
- if (min !== undefined) {
1443
- commands_1.push("EXPECT MIN ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
1444
- } /* not else */
1445
- if (max !== undefined) {
1446
- commands_1.push("EXPECT MAX ".concat(max, " ").concat(capitalize(unit + (max > 1 ? 's' : ''))));
1447
- }
1448
- }
1449
- }
1450
- }
1451
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
1452
- finally {
1453
- try {
1454
- if (_o && !_o.done && (_f = _m.return)) _f.call(_m);
1455
- }
1456
- finally { if (e_6) throw e_6.error; }
1457
- }
1458
- } /* not else */
1459
- if (format) {
1460
- if (format === 'JSON') {
1461
- // TODO: @deprecated remove
1462
- commands_1.push("FORMAT JSON");
1463
- }
1464
- } /* not else */
1465
- pipelineString += '\n\n';
1466
- pipelineString += commands_1.map(function (command) { return "- ".concat(command); }).join('\n');
1467
- pipelineString += '\n\n';
1468
- pipelineString += '```' + contentLanguage;
1469
- pipelineString += '\n';
1470
- pipelineString += spaceTrim(content);
1471
- // <- TODO: [main] !!! Escape
1472
- // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
1473
- pipelineString += '\n';
1474
- pipelineString += '```';
1475
- pipelineString += '\n\n';
1476
- pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!! If the parameter here has description, add it and use taskParameterJsonToString
1477
- }
1478
- }
1479
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
1480
- finally {
1481
- try {
1482
- if (tasks_1_1 && !tasks_1_1.done && (_c = tasks_1.return)) _c.call(tasks_1);
1483
- }
1484
- finally { if (e_3) throw e_3.error; }
1485
- }
1486
- return pipelineString;
1487
- }
1488
- /**
1489
- * @private internal utility of `pipelineJsonToString`
1490
- */
1491
- function taskParameterJsonToString(taskParameterJson) {
1492
- var name = taskParameterJson.name, description = taskParameterJson.description;
1493
- var parameterString = "{".concat(name, "}");
1494
- if (description) {
1495
- parameterString = "".concat(parameterString, " ").concat(description);
1496
- }
1497
- return parameterString;
1498
1087
  }
1499
- /**
1500
- * TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
1501
- * TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
1502
- * TODO: [🏛] Maybe make some markdown builder
1503
- * TODO: [🏛] Escape all
1504
- * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
1505
- */
1506
-
1507
- /**
1508
- * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
1509
- *
1510
- * @public exported from `@promptbook/core`
1511
- */
1512
- var ParseError = /** @class */ (function (_super) {
1513
- __extends(ParseError, _super);
1514
- function ParseError(message) {
1515
- var _this = _super.call(this, message) || this;
1516
- _this.name = 'ParseError';
1517
- Object.setPrototypeOf(_this, ParseError.prototype);
1518
- return _this;
1519
- }
1520
- return ParseError;
1521
- }(Error));
1522
- /**
1523
- * TODO: Maybe split `ParseError` and `ApplyError`
1524
- */
1525
-
1526
- /**
1527
- * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
1528
- *
1529
- * @public exported from `@promptbook/core`
1530
- */
1531
- var PipelineLogicError = /** @class */ (function (_super) {
1532
- __extends(PipelineLogicError, _super);
1533
- function PipelineLogicError(message) {
1534
- var _this = _super.call(this, message) || this;
1535
- _this.name = 'PipelineLogicError';
1536
- Object.setPrototypeOf(_this, PipelineLogicError.prototype);
1537
- return _this;
1538
- }
1539
- return PipelineLogicError;
1540
- }(Error));
1541
1088
 
1542
1089
  /**
1543
- * Tests if given string is valid semantic version
1090
+ * Tests if given string is valid pipeline URL URL.
1544
1091
  *
1545
1092
  * Note: There are two simmilar functions:
1546
- * - `isValidSemanticVersion` which tests any semantic version
1547
- * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
1093
+ * - `isValidUrl` which tests any URL
1094
+ * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
1548
1095
  *
1549
1096
  * @public exported from `@promptbook/utils`
1550
1097
  */
1551
- function isValidSemanticVersion(version) {
1552
- if (typeof version !== 'string') {
1098
+ function isValidPipelineUrl(url) {
1099
+ if (!isValidUrl(url)) {
1553
1100
  return false;
1554
1101
  }
1555
- if (version.startsWith('0.0.0')) {
1102
+ if (!url.startsWith('https://')) {
1556
1103
  return false;
1557
1104
  }
1558
- return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
1559
- }
1560
-
1561
- /**
1562
- * Tests if given string is valid promptbook version
1563
- * It looks into list of known promptbook versions.
1564
- *
1565
- * @see https://www.npmjs.com/package/promptbook?activeTab=versions
1566
- * 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.
1567
- * Note: There are two simmilar functions:
1568
- * - `isValidSemanticVersion` which tests any semantic version
1569
- * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
1570
- *
1571
- * @public exported from `@promptbook/utils`
1572
- */
1573
- function isValidPromptbookVersion(version) {
1574
- if (!isValidSemanticVersion(version)) {
1105
+ if (url.includes('#')) {
1106
+ // TODO: [🐠]
1575
1107
  return false;
1576
1108
  }
1577
- if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
1109
+ if (isUrlOnPrivateNetwork(url)) {
1578
1110
  return false;
1579
1111
  }
1580
- // <- TODO: [main] !!! Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
1581
1112
  return true;
1582
1113
  }
1114
+ /**
1115
+ * TODO: [🐠] Maybe more info why the URL is invalid
1116
+ */
1583
1117
 
1584
1118
  /**
1585
- * Checks if an URL is reserved for private networks or localhost.
1119
+ * Validates PipelineJson if it is logically valid
1586
1120
  *
1587
- * Note: There are two simmilar functions:
1588
- * - `isUrlOnPrivateNetwork` which tests full URL
1589
- * - `isHostnameOnPrivateNetwork` *(this one)* which tests just hostname
1121
+ * It checks:
1122
+ * - if it has correct parameters dependency
1590
1123
  *
1591
- * @public exported from `@promptbook/utils`
1124
+ * It does NOT check:
1125
+ * - if it is valid json
1126
+ * - if it is meaningful
1127
+ *
1128
+ * @param pipeline valid or invalid PipelineJson
1129
+ * @returns the same pipeline if it is logically valid
1130
+ * @throws {PipelineLogicError} on logical error in the pipeline
1131
+ * @public exported from `@promptbook/core`
1592
1132
  */
1593
- function isHostnameOnPrivateNetwork(hostname) {
1594
- if (hostname === 'example.com' ||
1595
- hostname === 'localhost' ||
1596
- hostname.endsWith('.localhost') ||
1597
- hostname.endsWith('.local') ||
1598
- hostname.endsWith('.test') ||
1599
- hostname === '127.0.0.1' ||
1600
- hostname === '::1') {
1601
- return true;
1602
- }
1603
- if (hostname.includes(':')) {
1604
- // IPv6
1605
- var ipParts = hostname.split(':');
1606
- return ipParts[0] === 'fc00' || ipParts[0] === 'fd00' || ipParts[0] === 'fe80';
1607
- }
1608
- else {
1609
- // IPv4
1610
- var ipParts = hostname.split('.').map(function (part) { return Number.parseInt(part, 10); });
1611
- return (ipParts[0] === 10 ||
1612
- (ipParts[0] === 172 && ipParts[1] >= 16 && ipParts[1] <= 31) ||
1613
- (ipParts[0] === 192 && ipParts[1] === 168));
1614
- }
1615
- }
1616
-
1617
- /**
1618
- * Checks if an IP address or hostname is reserved for private networks or localhost.
1619
- *
1620
- * Note: There are two simmilar functions:
1621
- * - `isUrlOnPrivateNetwork` *(this one)* which tests full URL
1622
- * - `isHostnameOnPrivateNetwork` which tests just hostname
1623
- *
1624
- * @param {string} ipAddress - The IP address to check.
1625
- * @returns {boolean} Returns true if the IP address is reserved for private networks or localhost, otherwise false.
1626
- * @public exported from `@promptbook/utils`
1627
- */
1628
- function isUrlOnPrivateNetwork(url) {
1629
- if (typeof url === 'string') {
1630
- url = new URL(url);
1631
- }
1632
- return isHostnameOnPrivateNetwork(url.hostname);
1633
- }
1634
-
1635
- /**
1636
- * Tests if given string is valid URL.
1637
- *
1638
- * Note: Dataurl are considered perfectly valid.
1639
- * Note: There are two simmilar functions:
1640
- * - `isValidUrl` which tests any URL
1641
- * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
1642
- *
1643
- * @public exported from `@promptbook/utils`
1644
- */
1645
- function isValidUrl(url) {
1646
- if (typeof url !== 'string') {
1647
- return false;
1648
- }
1649
- try {
1650
- if (url.startsWith('blob:')) {
1651
- url = url.replace(/^blob:/, '');
1652
- }
1653
- var urlObject = new URL(url /* because fail is handled */);
1654
- if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
1655
- return false;
1656
- }
1657
- return true;
1658
- }
1659
- catch (error) {
1660
- return false;
1661
- }
1662
- }
1663
-
1664
- /**
1665
- * Tests if given string is valid pipeline URL URL.
1666
- *
1667
- * Note: There are two simmilar functions:
1668
- * - `isValidUrl` which tests any URL
1669
- * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
1670
- *
1671
- * @public exported from `@promptbook/utils`
1672
- */
1673
- function isValidPipelineUrl(url) {
1674
- if (!isValidUrl(url)) {
1675
- return false;
1676
- }
1677
- if (!url.startsWith('https://')) {
1678
- return false;
1679
- }
1680
- if (!(url.endsWith('.book.md') || url.endsWith('.book') || url.endsWith('.book.md') || url.endsWith('.ptbk'))) {
1681
- return false;
1682
- }
1683
- if (url.includes('#')) {
1684
- // TODO: [🐠]
1685
- return false;
1686
- }
1687
- if (isUrlOnPrivateNetwork(url)) {
1688
- return false;
1689
- }
1690
- return true;
1691
- }
1692
- /**
1693
- * TODO: [🐠] Maybe more info why the URL is invalid
1694
- */
1695
-
1696
- /**
1697
- * Validates PipelineJson if it is logically valid
1698
- *
1699
- * It checks:
1700
- * - if it has correct parameters dependency
1701
- *
1702
- * It does NOT check:
1703
- * - if it is valid json
1704
- * - if it is meaningful
1705
- *
1706
- * @param pipeline valid or invalid PipelineJson
1707
- * @returns the same pipeline if it is logically valid
1708
- * @throws {PipelineLogicError} on logical error in the pipeline
1709
- * @public exported from `@promptbook/core`
1710
- */
1711
- function validatePipeline(pipeline) {
1712
- if (IS_PIPELINE_LOGIC_VALIDATED) {
1713
- validatePipelineCore(pipeline);
1133
+ function validatePipeline(pipeline) {
1134
+ if (IS_PIPELINE_LOGIC_VALIDATED) {
1135
+ validatePipeline_InnerFunction(pipeline);
1714
1136
  }
1715
1137
  else {
1716
1138
  try {
1717
- validatePipelineCore(pipeline);
1139
+ validatePipeline_InnerFunction(pipeline);
1718
1140
  }
1719
1141
  catch (error) {
1720
1142
  if (!(error instanceof PipelineLogicError)) {
@@ -1728,7 +1150,7 @@ function validatePipeline(pipeline) {
1728
1150
  /**
1729
1151
  * @private internal function for `validatePipeline`
1730
1152
  */
1731
- function validatePipelineCore(pipeline) {
1153
+ function validatePipeline_InnerFunction(pipeline) {
1732
1154
  // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
1733
1155
  var e_1, _a, e_2, _b, e_3, _c;
1734
1156
  var pipelineIdentification = (function () {
@@ -1952,11 +1374,11 @@ function validatePipelineCore(pipeline) {
1952
1374
  _loop_3();
1953
1375
  }
1954
1376
  // Note: Check that formfactor is corresponding to the pipeline interface
1955
- // TODO: !!!!!! Implement this
1377
+ // TODO: !!6 Implement this
1956
1378
  // pipeline.formfactorName
1957
1379
  }
1958
1380
  /**
1959
- * TODO: !! [🧞‍♀️] Do not allow joker + foreach
1381
+ * TODO: [🧞‍♀️] Do not allow joker + foreach
1960
1382
  * TODO: [🧠] Work with promptbookVersion
1961
1383
  * TODO: Use here some json-schema, Zod or something similar and change it to:
1962
1384
  * > /**
@@ -1968,11 +1390,11 @@ function validatePipelineCore(pipeline) {
1968
1390
  * > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
1969
1391
  */
1970
1392
  /**
1971
- * TODO: [🧳][main] !!!! Validate that all examples match expectations
1972
- * TODO: [🧳][🐝][main] !!!! Validate that knowledge is valid (non-void)
1973
- * TODO: [🧳][main] !!!! Validate that persona can be used only with CHAT variant
1974
- * TODO: [🧳][main] !!!! Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
1975
- * TODO: [🧳][main] !!!! Validate that reserved parameter is not used as joker
1393
+ * TODO: [🧳][main] !!4 Validate that all examples match expectations
1394
+ * TODO: [🧳][🐝][main] !!4 Validate that knowledge is valid (non-void)
1395
+ * TODO: [🧳][main] !!4 Validate that persona can be used only with CHAT variant
1396
+ * TODO: [🧳][main] !!4 Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
1397
+ * TODO: [🧳][main] !!4 Validate that reserved parameter is not used as joker
1976
1398
  * TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
1977
1399
  * TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
1978
1400
  */
@@ -2108,7 +1530,7 @@ var SimplePipelineCollection = /** @class */ (function () {
2108
1530
  pipelineJsonToString(unpreparePipeline(pipeline)) !==
2109
1531
  pipelineJsonToString(unpreparePipeline(this.collection.get(pipeline.pipelineUrl)))) {
2110
1532
  var existing = this.collection.get(pipeline.pipelineUrl);
2111
- throw new PipelineUrlError(spaceTrim$1("\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 ")));
1533
+ throw new PipelineUrlError(spaceTrim$1("\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 ")));
2112
1534
  }
2113
1535
  // Note: [🧠] Overwrite existing pipeline with the same URL
2114
1536
  this.collection.set(pipeline.pipelineUrl, pipeline);
@@ -2173,6 +1595,38 @@ function createCollectionFromJson() {
2173
1595
  return new (SimplePipelineCollection.bind.apply(SimplePipelineCollection, __spreadArray([void 0], __read(promptbooks), false)))();
2174
1596
  }
2175
1597
 
1598
+ /**
1599
+ * This error type indicates that some tools are missing for pipeline execution or preparation
1600
+ *
1601
+ * @public exported from `@promptbook/core`
1602
+ */
1603
+ var MissingToolsError = /** @class */ (function (_super) {
1604
+ __extends(MissingToolsError, _super);
1605
+ function MissingToolsError(message) {
1606
+ var _this = _super.call(this, spaceTrim$1(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;
1607
+ _this.name = 'MissingToolsError';
1608
+ Object.setPrototypeOf(_this, MissingToolsError.prototype);
1609
+ return _this;
1610
+ }
1611
+ return MissingToolsError;
1612
+ }(Error));
1613
+
1614
+ /**
1615
+ * This error indicates errors during the execution of the pipeline
1616
+ *
1617
+ * @public exported from `@promptbook/core`
1618
+ */
1619
+ var PipelineExecutionError = /** @class */ (function (_super) {
1620
+ __extends(PipelineExecutionError, _super);
1621
+ function PipelineExecutionError(message) {
1622
+ var _this = _super.call(this, message) || this;
1623
+ _this.name = 'PipelineExecutionError';
1624
+ Object.setPrototypeOf(_this, PipelineExecutionError.prototype);
1625
+ return _this;
1626
+ }
1627
+ return PipelineExecutionError;
1628
+ }(Error));
1629
+
2176
1630
  /**
2177
1631
  * This error indicates problems parsing the format value
2178
1632
  *
@@ -2412,11 +1866,16 @@ function assertsExecutionSuccessful(executionResult) {
2412
1866
  /**
2413
1867
  * Determine if the pipeline is fully prepared
2414
1868
  *
1869
+ * @see https://github.com/webgptorg/promptbook/discussions/196
1870
+ *
2415
1871
  * @public exported from `@promptbook/core`
2416
1872
  */
2417
1873
  function isPipelinePrepared(pipeline) {
2418
1874
  // Note: Ignoring `pipeline.preparations` @@@
2419
1875
  // Note: Ignoring `pipeline.knowledgePieces` @@@
1876
+ if (pipeline.title === undefined || pipeline.title === '' || pipeline.title === DEFAULT_BOOK_TITLE) {
1877
+ return false;
1878
+ }
2420
1879
  if (!pipeline.personas.every(function (persona) { return persona.modelRequirements !== undefined; })) {
2421
1880
  return false;
2422
1881
  }
@@ -2432,7 +1891,7 @@ function isPipelinePrepared(pipeline) {
2432
1891
  return true;
2433
1892
  }
2434
1893
  /**
2435
- * TODO: [🔃][main] !! If the pipeline was prepared with different version or different set of models, prepare it once again
1894
+ * TODO: [🔃][main] If the pipeline was prepared with different version or different set of models, prepare it once again
2436
1895
  * TODO: [🐠] Maybe base this on `makeValidator`
2437
1896
  * TODO: [🧊] Pipeline can be partially prepared, this should return true ONLY if fully prepared
2438
1897
  * TODO: [🧿] Maybe do same process with same granularity and subfinctions as `preparePipeline`
@@ -2441,6 +1900,81 @@ function isPipelinePrepared(pipeline) {
2441
1900
  * - [♨] Are tasks prepared
2442
1901
  */
2443
1902
 
1903
+ /**
1904
+ * Format either small or big number
1905
+ *
1906
+ * @public exported from `@promptbook/utils`
1907
+ */
1908
+ function numberToString(value) {
1909
+ if (value === 0) {
1910
+ return '0';
1911
+ }
1912
+ else if (Number.isNaN(value)) {
1913
+ return VALUE_STRINGS.nan;
1914
+ }
1915
+ else if (value === Infinity) {
1916
+ return VALUE_STRINGS.infinity;
1917
+ }
1918
+ else if (value === -Infinity) {
1919
+ return VALUE_STRINGS.negativeInfinity;
1920
+ }
1921
+ for (var exponent = 0; exponent < 15; exponent++) {
1922
+ var factor = Math.pow(10, exponent);
1923
+ var valueRounded = Math.round(value * factor) / factor;
1924
+ if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
1925
+ return valueRounded.toFixed(exponent);
1926
+ }
1927
+ }
1928
+ return value.toString();
1929
+ }
1930
+
1931
+ /**
1932
+ * Function `valueToString` will convert the given value to string
1933
+ * This is useful and used in the `templateParameters` function
1934
+ *
1935
+ * Note: This function is not just calling `toString` method
1936
+ * It's more complex and can handle this conversion specifically for LLM models
1937
+ * See `VALUE_STRINGS`
1938
+ *
1939
+ * Note: There are 2 similar functions
1940
+ * - `valueToString` converts value to string for LLM models as human-readable string
1941
+ * - `asSerializable` converts value to string to preserve full information to be able to convert it back
1942
+ *
1943
+ * @public exported from `@promptbook/utils`
1944
+ */
1945
+ function valueToString(value) {
1946
+ try {
1947
+ if (value === '') {
1948
+ return VALUE_STRINGS.empty;
1949
+ }
1950
+ else if (value === null) {
1951
+ return VALUE_STRINGS.null;
1952
+ }
1953
+ else if (value === undefined) {
1954
+ return VALUE_STRINGS.undefined;
1955
+ }
1956
+ else if (typeof value === 'string') {
1957
+ return value;
1958
+ }
1959
+ else if (typeof value === 'number') {
1960
+ return numberToString(value);
1961
+ }
1962
+ else if (value instanceof Date) {
1963
+ return value.toISOString();
1964
+ }
1965
+ else {
1966
+ return JSON.stringify(value);
1967
+ }
1968
+ }
1969
+ catch (error) {
1970
+ if (!(error instanceof Error)) {
1971
+ throw error;
1972
+ }
1973
+ console.error(error);
1974
+ return VALUE_STRINGS.unserializable;
1975
+ }
1976
+ }
1977
+
2444
1978
  /**
2445
1979
  * Serializes an error into a [🚉] JSON-serializable object
2446
1980
  *
@@ -2459,47 +1993,175 @@ function serializeError(error) {
2459
1993
  }
2460
1994
 
2461
1995
  /**
2462
- * Parses the given script and returns the list of all used variables that are not defined in the script
1996
+ * Represents the usage with no resources consumed
2463
1997
  *
2464
- * @param script from which to extract the variables
2465
- * @returns the list of variable names
2466
- * @throws {ParseError} if the script is invalid
2467
- * @public exported from `@promptbook/utils` <- Note: [👖] This is usable elsewhere than in Promptbook, so keeping in utils
1998
+ * @public exported from `@promptbook/core`
2468
1999
  */
2469
- function extractVariablesFromScript(script) {
2470
- var variables = new Set();
2471
- var originalScript = script;
2472
- script = "(()=>{".concat(script, "})()");
2473
- try {
2474
- for (var i = 0; i < 100 /* <- TODO: This limit to configuration */; i++)
2475
- try {
2476
- eval(script);
2477
- }
2478
- catch (error) {
2479
- if (!(error instanceof ReferenceError)) {
2480
- throw error;
2481
- }
2482
- /*
2483
- Note: Parsing the error
2484
- 🌟 Most devices:
2485
- [PipelineUrlError: thing is not defined]
2486
-
2487
- 🍏 iPhone`s Safari:
2488
- [PipelineUrlError: Can't find variable: thing]
2489
- */
2490
- var variableName = undefined;
2491
- if (error.message.startsWith("Can't")) {
2492
- // 🍏 Case
2493
- variableName = error.message.split(' ').pop();
2494
- }
2495
- else {
2496
- // 🌟 Case
2497
- variableName = error.message.split(' ').shift();
2498
- }
2499
- if (variableName === undefined) {
2500
- throw error;
2501
- }
2502
- if (script.includes(variableName + '(')) {
2000
+ var ZERO_USAGE = $deepFreeze({
2001
+ price: { value: 0 },
2002
+ input: {
2003
+ tokensCount: { value: 0 },
2004
+ charactersCount: { value: 0 },
2005
+ wordsCount: { value: 0 },
2006
+ sentencesCount: { value: 0 },
2007
+ linesCount: { value: 0 },
2008
+ paragraphsCount: { value: 0 },
2009
+ pagesCount: { value: 0 },
2010
+ },
2011
+ output: {
2012
+ tokensCount: { value: 0 },
2013
+ charactersCount: { value: 0 },
2014
+ wordsCount: { value: 0 },
2015
+ sentencesCount: { value: 0 },
2016
+ linesCount: { value: 0 },
2017
+ paragraphsCount: { value: 0 },
2018
+ pagesCount: { value: 0 },
2019
+ },
2020
+ });
2021
+ /**
2022
+ * Represents the usage with unknown resources consumed
2023
+ *
2024
+ * @public exported from `@promptbook/core`
2025
+ */
2026
+ $deepFreeze({
2027
+ price: { value: 0, isUncertain: true },
2028
+ input: {
2029
+ tokensCount: { value: 0, isUncertain: true },
2030
+ charactersCount: { value: 0, isUncertain: true },
2031
+ wordsCount: { value: 0, isUncertain: true },
2032
+ sentencesCount: { value: 0, isUncertain: true },
2033
+ linesCount: { value: 0, isUncertain: true },
2034
+ paragraphsCount: { value: 0, isUncertain: true },
2035
+ pagesCount: { value: 0, isUncertain: true },
2036
+ },
2037
+ output: {
2038
+ tokensCount: { value: 0, isUncertain: true },
2039
+ charactersCount: { value: 0, isUncertain: true },
2040
+ wordsCount: { value: 0, isUncertain: true },
2041
+ sentencesCount: { value: 0, isUncertain: true },
2042
+ linesCount: { value: 0, isUncertain: true },
2043
+ paragraphsCount: { value: 0, isUncertain: true },
2044
+ pagesCount: { value: 0, isUncertain: true },
2045
+ },
2046
+ });
2047
+ /**
2048
+ * Note: [💞] Ignore a discrepancy between file name and entity name
2049
+ */
2050
+
2051
+ /**
2052
+ * Function `addUsage` will add multiple usages into one
2053
+ *
2054
+ * Note: If you provide 0 values, it returns ZERO_USAGE
2055
+ *
2056
+ * @public exported from `@promptbook/core`
2057
+ */
2058
+ function addUsage() {
2059
+ var usageItems = [];
2060
+ for (var _i = 0; _i < arguments.length; _i++) {
2061
+ usageItems[_i] = arguments[_i];
2062
+ }
2063
+ return usageItems.reduce(function (acc, item) {
2064
+ var e_1, _a, e_2, _b;
2065
+ var _c;
2066
+ acc.price.value += ((_c = item.price) === null || _c === void 0 ? void 0 : _c.value) || 0;
2067
+ try {
2068
+ for (var _d = __values(Object.keys(acc.input)), _e = _d.next(); !_e.done; _e = _d.next()) {
2069
+ var key = _e.value;
2070
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2071
+ //@ts-ignore
2072
+ if (item.input[key]) {
2073
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2074
+ //@ts-ignore
2075
+ acc.input[key].value += item.input[key].value || 0;
2076
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2077
+ //@ts-ignore
2078
+ if (item.input[key].isUncertain) {
2079
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2080
+ //@ts-ignore
2081
+ acc.input[key].isUncertain = true;
2082
+ }
2083
+ }
2084
+ }
2085
+ }
2086
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
2087
+ finally {
2088
+ try {
2089
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
2090
+ }
2091
+ finally { if (e_1) throw e_1.error; }
2092
+ }
2093
+ try {
2094
+ for (var _f = __values(Object.keys(acc.output)), _g = _f.next(); !_g.done; _g = _f.next()) {
2095
+ var key = _g.value;
2096
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2097
+ //@ts-ignore
2098
+ if (item.output[key]) {
2099
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2100
+ //@ts-ignore
2101
+ acc.output[key].value += item.output[key].value || 0;
2102
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2103
+ //@ts-ignore
2104
+ if (item.output[key].isUncertain) {
2105
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2106
+ //@ts-ignore
2107
+ acc.output[key].isUncertain = true;
2108
+ }
2109
+ }
2110
+ }
2111
+ }
2112
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
2113
+ finally {
2114
+ try {
2115
+ if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
2116
+ }
2117
+ finally { if (e_2) throw e_2.error; }
2118
+ }
2119
+ return acc;
2120
+ }, deepClone(ZERO_USAGE));
2121
+ }
2122
+
2123
+ /**
2124
+ * Parses the given script and returns the list of all used variables that are not defined in the script
2125
+ *
2126
+ * @param script from which to extract the variables
2127
+ * @returns the list of variable names
2128
+ * @throws {ParseError} if the script is invalid
2129
+ * @public exported from `@promptbook/utils` <- Note: [👖] This is usable elsewhere than in Promptbook, so keeping in utils
2130
+ */
2131
+ function extractVariablesFromScript(script) {
2132
+ var variables = new Set();
2133
+ var originalScript = script;
2134
+ script = "(()=>{".concat(script, "})()");
2135
+ try {
2136
+ for (var i = 0; i < 100 /* <- TODO: This limit to configuration */; i++)
2137
+ try {
2138
+ eval(script);
2139
+ }
2140
+ catch (error) {
2141
+ if (!(error instanceof ReferenceError)) {
2142
+ throw error;
2143
+ }
2144
+ /*
2145
+ Note: Parsing the error
2146
+ 🌟 Most devices:
2147
+ [PipelineUrlError: thing is not defined]
2148
+
2149
+ 🍏 iPhone`s Safari:
2150
+ [PipelineUrlError: Can't find variable: thing]
2151
+ */
2152
+ var variableName = undefined;
2153
+ if (error.message.startsWith("Can't")) {
2154
+ // 🍏 Case
2155
+ variableName = error.message.split(' ').pop();
2156
+ }
2157
+ else {
2158
+ // 🌟 Case
2159
+ variableName = error.message.split(' ').shift();
2160
+ }
2161
+ if (variableName === undefined) {
2162
+ throw error;
2163
+ }
2164
+ if (script.includes(variableName + '(')) {
2503
2165
  script = "const ".concat(variableName, " = ()=>'';") + script;
2504
2166
  }
2505
2167
  else {
@@ -2512,7 +2174,7 @@ function extractVariablesFromScript(script) {
2512
2174
  if (!(error instanceof Error)) {
2513
2175
  throw error;
2514
2176
  }
2515
- throw new ParseError(spaceTrim$1(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)
2177
+ throw new ParseError(spaceTrim$1(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)
2516
2178
  .map(function (variableName, i) { return "".concat(i + 1, ") ").concat(variableName); })
2517
2179
  .join('\n'), "\n\n\n The script:\n\n ```javascript\n ").concat(block(originalScript), "\n ```\n "); }));
2518
2180
  }
@@ -2792,27 +2454,6 @@ var CsvFormatDefinition = {
2792
2454
  * TODO: [🏢] Allow to expect something inside CSV objects and other formats
2793
2455
  */
2794
2456
 
2795
- /**
2796
- * Function isValidJsonString will tell you if the string is valid JSON or not
2797
- *
2798
- * @public exported from `@promptbook/utils`
2799
- */
2800
- function isValidJsonString(value /* <- [👨‍⚖️] */) {
2801
- try {
2802
- JSON.parse(value);
2803
- return true;
2804
- }
2805
- catch (error) {
2806
- if (!(error instanceof Error)) {
2807
- throw error;
2808
- }
2809
- if (error.message.includes('Unexpected token')) {
2810
- return false;
2811
- }
2812
- return false;
2813
- }
2814
- }
2815
-
2816
2457
  /**
2817
2458
  * Definition for JSON format
2818
2459
  *
@@ -2883,137 +2524,429 @@ var TextFormatDefinition = {
2883
2524
  mappedLines = _a.sent();
2884
2525
  return [2 /*return*/, mappedLines.join('\n')];
2885
2526
  }
2886
- });
2887
- });
2888
- },
2889
- },
2890
- // <- TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2891
- ],
2892
- };
2527
+ });
2528
+ });
2529
+ },
2530
+ },
2531
+ // <- TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2532
+ ],
2533
+ };
2534
+ /**
2535
+ * TODO: [1] Make type for XML Text and Schema
2536
+ * TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2537
+ * TODO: [🍓] In `TextFormatDefinition` implement simple `isValid`
2538
+ * TODO: [🍓] In `TextFormatDefinition` implement partial `canBeValid`
2539
+ * TODO: [🍓] In `TextFormatDefinition` implement `heal
2540
+ * TODO: [🍓] In `TextFormatDefinition` implement `subvalueDefinitions`
2541
+ * TODO: [🏢] Allow to expect something inside each item of list and other formats
2542
+ */
2543
+
2544
+ /**
2545
+ * Definition for XML format
2546
+ *
2547
+ * @private still in development [🏢]
2548
+ */
2549
+ var XmlFormatDefinition = {
2550
+ formatName: 'XML',
2551
+ mimeType: 'application/xml',
2552
+ isValid: function (value, settings, schema) {
2553
+ return true;
2554
+ },
2555
+ canBeValid: function (partialValue, settings, schema) {
2556
+ return true;
2557
+ },
2558
+ heal: function (value, settings, schema) {
2559
+ throw new Error('Not implemented');
2560
+ },
2561
+ subvalueDefinitions: [],
2562
+ };
2563
+ /**
2564
+ * TODO: [🧠] Maybe propper instance of object
2565
+ * TODO: [0] Make string_serialized_xml
2566
+ * TODO: [1] Make type for XML Settings and Schema
2567
+ * TODO: [🧠] What to use for validating XMLs - XSD,...
2568
+ * TODO: [🍓] In `XmlFormatDefinition` implement simple `isValid`
2569
+ * TODO: [🍓] In `XmlFormatDefinition` implement partial `canBeValid`
2570
+ * TODO: [🍓] In `XmlFormatDefinition` implement `heal
2571
+ * TODO: [🍓] In `XmlFormatDefinition` implement `subvalueDefinitions`
2572
+ * TODO: [🏢] Allow to expect something inside XML and other formats
2573
+ */
2574
+
2575
+ /**
2576
+ * Definitions for all formats supported by Promptbook
2577
+ *
2578
+ * @private internal index of `...` <- TODO [🏢]
2579
+ */
2580
+ var FORMAT_DEFINITIONS = [
2581
+ JsonFormatDefinition,
2582
+ XmlFormatDefinition,
2583
+ TextFormatDefinition,
2584
+ CsvFormatDefinition,
2585
+ ];
2586
+ /**
2587
+ * Note: [💞] Ignore a discrepancy between file name and entity name
2588
+ */
2589
+
2590
+ /**
2591
+ * Maps available parameters to expected parameters
2592
+ *
2593
+ * The strategy is:
2594
+ * 1) @@@
2595
+ * 2) @@@
2596
+ *
2597
+ * @throws {PipelineExecutionError} @@@
2598
+ * @private within the repository used in `createPipelineExecutor`
2599
+ */
2600
+ function mapAvailableToExpectedParameters(options) {
2601
+ var e_1, _a;
2602
+ var expectedParameters = options.expectedParameters, availableParameters = options.availableParameters;
2603
+ var availableParametersNames = new Set(Object.keys(availableParameters));
2604
+ var expectedParameterNames = new Set(Object.keys(expectedParameters));
2605
+ var mappedParameters = {};
2606
+ try {
2607
+ // Phase 1️⃣: Matching mapping
2608
+ for (var _b = __values(Array.from(union(availableParametersNames, expectedParameterNames))), _c = _b.next(); !_c.done; _c = _b.next()) {
2609
+ var parameterName = _c.value;
2610
+ // Situation: Parameter is available and expected
2611
+ if (availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
2612
+ mappedParameters[parameterName] = availableParameters[parameterName];
2613
+ // <- Note: [👩‍👩‍👧] Maybe detect parameter collision here?
2614
+ availableParametersNames.delete(parameterName);
2615
+ expectedParameterNames.delete(parameterName);
2616
+ }
2617
+ // Situation: Parameter is available but NOT expected
2618
+ else if (availableParametersNames.has(parameterName) && !expectedParameterNames.has(parameterName)) {
2619
+ // [🐱‍👤] Do not pass this parameter to prompt - Maybe use it non-matching mapping
2620
+ }
2621
+ // Situation: Parameter is NOT available BUT expected
2622
+ else if (!availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
2623
+ // Do nothing here - this will be maybe fixed in the non-matching mapping
2624
+ }
2625
+ }
2626
+ }
2627
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
2628
+ finally {
2629
+ try {
2630
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
2631
+ }
2632
+ finally { if (e_1) throw e_1.error; }
2633
+ }
2634
+ if (expectedParameterNames.size === 0) {
2635
+ // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
2636
+ Object.freeze(mappedParameters);
2637
+ return mappedParameters;
2638
+ }
2639
+ // Phase 2️⃣: Non-matching mapping
2640
+ if (expectedParameterNames.size !== availableParametersNames.size) {
2641
+ throw new PipelineExecutionError(spaceTrim(function (block) { return "\n Can not map available parameters to expected parameters\n\n Mapped parameters:\n ".concat(block(Object.keys(mappedParameters)
2642
+ .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
2643
+ .join('\n')), "\n\n Expected parameters which can not be mapped:\n ").concat(block(Array.from(expectedParameterNames)
2644
+ .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
2645
+ .join('\n')), "\n\n Remaining available parameters:\n ").concat(block(Array.from(availableParametersNames)
2646
+ .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
2647
+ .join('\n')), "\n\n "); }));
2648
+ }
2649
+ var expectedParameterNamesArray = Array.from(expectedParameterNames);
2650
+ var availableParametersNamesArray = Array.from(availableParametersNames);
2651
+ for (var i = 0; i < expectedParameterNames.size; i++) {
2652
+ mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
2653
+ }
2654
+ // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
2655
+ Object.freeze(mappedParameters);
2656
+ return mappedParameters;
2657
+ }
2658
+
2659
+ /**
2660
+ * Multiple LLM Execution Tools is a proxy server that uses multiple execution tools internally and exposes the executor interface externally.
2661
+ *
2662
+ * Note: Internal utility of `joinLlmExecutionTools` but exposed type
2663
+ * @public exported from `@promptbook/core`
2664
+ */
2665
+ var MultipleLlmExecutionTools = /** @class */ (function () {
2666
+ /**
2667
+ * Gets array of execution tools in order of priority
2668
+ */
2669
+ function MultipleLlmExecutionTools() {
2670
+ var llmExecutionTools = [];
2671
+ for (var _i = 0; _i < arguments.length; _i++) {
2672
+ llmExecutionTools[_i] = arguments[_i];
2673
+ }
2674
+ this.llmExecutionTools = llmExecutionTools;
2675
+ }
2676
+ Object.defineProperty(MultipleLlmExecutionTools.prototype, "title", {
2677
+ get: function () {
2678
+ return 'Multiple LLM Providers';
2679
+ },
2680
+ enumerable: false,
2681
+ configurable: true
2682
+ });
2683
+ Object.defineProperty(MultipleLlmExecutionTools.prototype, "description", {
2684
+ get: function () {
2685
+ return this.llmExecutionTools.map(function (_a, index) {
2686
+ var title = _a.title;
2687
+ return "".concat(index + 1, ") `").concat(title, "`");
2688
+ }).join('\n');
2689
+ },
2690
+ enumerable: false,
2691
+ configurable: true
2692
+ });
2693
+ /**
2694
+ * Check the configuration of all execution tools
2695
+ */
2696
+ MultipleLlmExecutionTools.prototype.checkConfiguration = function () {
2697
+ return __awaiter(this, void 0, void 0, function () {
2698
+ var _a, _b, llmExecutionTools, e_1_1;
2699
+ var e_1, _c;
2700
+ return __generator(this, function (_d) {
2701
+ switch (_d.label) {
2702
+ case 0:
2703
+ _d.trys.push([0, 5, 6, 7]);
2704
+ _a = __values(this.llmExecutionTools), _b = _a.next();
2705
+ _d.label = 1;
2706
+ case 1:
2707
+ if (!!_b.done) return [3 /*break*/, 4];
2708
+ llmExecutionTools = _b.value;
2709
+ return [4 /*yield*/, llmExecutionTools.checkConfiguration()];
2710
+ case 2:
2711
+ _d.sent();
2712
+ _d.label = 3;
2713
+ case 3:
2714
+ _b = _a.next();
2715
+ return [3 /*break*/, 1];
2716
+ case 4: return [3 /*break*/, 7];
2717
+ case 5:
2718
+ e_1_1 = _d.sent();
2719
+ e_1 = { error: e_1_1 };
2720
+ return [3 /*break*/, 7];
2721
+ case 6:
2722
+ try {
2723
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
2724
+ }
2725
+ finally { if (e_1) throw e_1.error; }
2726
+ return [7 /*endfinally*/];
2727
+ case 7: return [2 /*return*/];
2728
+ }
2729
+ });
2730
+ });
2731
+ };
2732
+ /**
2733
+ * List all available models that can be used
2734
+ * This lists is a combination of all available models from all execution tools
2735
+ */
2736
+ MultipleLlmExecutionTools.prototype.listModels = function () {
2737
+ return __awaiter(this, void 0, void 0, function () {
2738
+ var availableModels, _a, _b, llmExecutionTools, models, e_2_1;
2739
+ var e_2, _c;
2740
+ return __generator(this, function (_d) {
2741
+ switch (_d.label) {
2742
+ case 0:
2743
+ availableModels = [];
2744
+ _d.label = 1;
2745
+ case 1:
2746
+ _d.trys.push([1, 6, 7, 8]);
2747
+ _a = __values(this.llmExecutionTools), _b = _a.next();
2748
+ _d.label = 2;
2749
+ case 2:
2750
+ if (!!_b.done) return [3 /*break*/, 5];
2751
+ llmExecutionTools = _b.value;
2752
+ return [4 /*yield*/, llmExecutionTools.listModels()];
2753
+ case 3:
2754
+ models = _d.sent();
2755
+ availableModels.push.apply(availableModels, __spreadArray([], __read(models), false));
2756
+ _d.label = 4;
2757
+ case 4:
2758
+ _b = _a.next();
2759
+ return [3 /*break*/, 2];
2760
+ case 5: return [3 /*break*/, 8];
2761
+ case 6:
2762
+ e_2_1 = _d.sent();
2763
+ e_2 = { error: e_2_1 };
2764
+ return [3 /*break*/, 8];
2765
+ case 7:
2766
+ try {
2767
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
2768
+ }
2769
+ finally { if (e_2) throw e_2.error; }
2770
+ return [7 /*endfinally*/];
2771
+ case 8: return [2 /*return*/, availableModels];
2772
+ }
2773
+ });
2774
+ });
2775
+ };
2776
+ /**
2777
+ * Calls the best available chat model
2778
+ */
2779
+ MultipleLlmExecutionTools.prototype.callChatModel = function (prompt) {
2780
+ return this.callCommonModel(prompt);
2781
+ };
2782
+ /**
2783
+ * Calls the best available completion model
2784
+ */
2785
+ MultipleLlmExecutionTools.prototype.callCompletionModel = function (prompt) {
2786
+ return this.callCommonModel(prompt);
2787
+ };
2788
+ /**
2789
+ * Calls the best available embedding model
2790
+ */
2791
+ MultipleLlmExecutionTools.prototype.callEmbeddingModel = function (prompt) {
2792
+ return this.callCommonModel(prompt);
2793
+ };
2794
+ // <- Note: [🤖]
2795
+ /**
2796
+ * Calls the best available model
2797
+ *
2798
+ * Note: This should be private or protected but is public to be usable with duck typing
2799
+ */
2800
+ MultipleLlmExecutionTools.prototype.callCommonModel = function (prompt) {
2801
+ return __awaiter(this, void 0, void 0, function () {
2802
+ var errors, _a, _b, llmExecutionTools, _c, error_1, e_3_1;
2803
+ var e_3, _d;
2804
+ var _this = this;
2805
+ return __generator(this, function (_e) {
2806
+ switch (_e.label) {
2807
+ case 0:
2808
+ errors = [];
2809
+ _e.label = 1;
2810
+ case 1:
2811
+ _e.trys.push([1, 15, 16, 17]);
2812
+ _a = __values(this.llmExecutionTools), _b = _a.next();
2813
+ _e.label = 2;
2814
+ case 2:
2815
+ if (!!_b.done) return [3 /*break*/, 14];
2816
+ llmExecutionTools = _b.value;
2817
+ _e.label = 3;
2818
+ case 3:
2819
+ _e.trys.push([3, 12, , 13]);
2820
+ _c = prompt.modelRequirements.modelVariant;
2821
+ switch (_c) {
2822
+ case 'CHAT': return [3 /*break*/, 4];
2823
+ case 'COMPLETION': return [3 /*break*/, 6];
2824
+ case 'EMBEDDING': return [3 /*break*/, 8];
2825
+ }
2826
+ return [3 /*break*/, 10];
2827
+ case 4:
2828
+ if (llmExecutionTools.callChatModel === undefined) {
2829
+ return [3 /*break*/, 13];
2830
+ }
2831
+ return [4 /*yield*/, llmExecutionTools.callChatModel(prompt)];
2832
+ case 5: return [2 /*return*/, _e.sent()];
2833
+ case 6:
2834
+ if (llmExecutionTools.callCompletionModel === undefined) {
2835
+ return [3 /*break*/, 13];
2836
+ }
2837
+ return [4 /*yield*/, llmExecutionTools.callCompletionModel(prompt)];
2838
+ case 7: return [2 /*return*/, _e.sent()];
2839
+ case 8:
2840
+ if (llmExecutionTools.callEmbeddingModel === undefined) {
2841
+ return [3 /*break*/, 13];
2842
+ }
2843
+ return [4 /*yield*/, llmExecutionTools.callEmbeddingModel(prompt)];
2844
+ case 9: return [2 /*return*/, _e.sent()];
2845
+ case 10: throw new UnexpectedError("Unknown model variant \"".concat(prompt.modelRequirements.modelVariant, "\""));
2846
+ case 11: return [3 /*break*/, 13];
2847
+ case 12:
2848
+ error_1 = _e.sent();
2849
+ if (!(error_1 instanceof Error) || error_1 instanceof UnexpectedError) {
2850
+ throw error_1;
2851
+ }
2852
+ errors.push(error_1);
2853
+ return [3 /*break*/, 13];
2854
+ case 13:
2855
+ _b = _a.next();
2856
+ return [3 /*break*/, 2];
2857
+ case 14: return [3 /*break*/, 17];
2858
+ case 15:
2859
+ e_3_1 = _e.sent();
2860
+ e_3 = { error: e_3_1 };
2861
+ return [3 /*break*/, 17];
2862
+ case 16:
2863
+ try {
2864
+ if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
2865
+ }
2866
+ finally { if (e_3) throw e_3.error; }
2867
+ return [7 /*endfinally*/];
2868
+ case 17:
2869
+ if (errors.length === 1) {
2870
+ throw errors[0];
2871
+ }
2872
+ else if (errors.length > 1) {
2873
+ throw new PipelineExecutionError(
2874
+ // TODO: Tell which execution tools failed like
2875
+ // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
2876
+ // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
2877
+ // 3) ...
2878
+ spaceTrim(function (block) { return "\n All execution tools failed:\n\n ".concat(block(errors
2879
+ .map(function (error, i) { return "".concat(i + 1, ") **").concat(error.name || 'Error', ":** ").concat(error.message); })
2880
+ .join('\n')), "\n\n "); }));
2881
+ }
2882
+ else if (this.llmExecutionTools.length === 0) {
2883
+ throw new PipelineExecutionError("You have not provided any `LlmExecutionTools`");
2884
+ }
2885
+ else {
2886
+ throw new PipelineExecutionError(spaceTrim(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 "); }));
2887
+ }
2888
+ }
2889
+ });
2890
+ });
2891
+ };
2892
+ return MultipleLlmExecutionTools;
2893
+ }());
2893
2894
  /**
2894
- * TODO: [1] Make type for XML Text and Schema
2895
- * TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages aviable as subvalues
2896
- * TODO: [🍓] In `TextFormatDefinition` implement simple `isValid`
2897
- * TODO: [🍓] In `TextFormatDefinition` implement partial `canBeValid`
2898
- * TODO: [🍓] In `TextFormatDefinition` implement `heal
2899
- * TODO: [🍓] In `TextFormatDefinition` implement `subvalueDefinitions`
2900
- * TODO: [🏢] Allow to expect something inside each item of list and other formats
2895
+ * TODO: [🧠][🎛] Aggregating multiple models - have result not only from one first aviable model BUT all of them
2896
+ * 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
2897
+ * Look how `countTotalUsage` (and `cacheLlmTools`) implements it
2901
2898
  */
2902
2899
 
2903
2900
  /**
2904
- * Definition for XML format
2901
+ * Joins multiple LLM Execution Tools into one
2905
2902
  *
2906
- * @private still in development [🏢]
2907
- */
2908
- var XmlFormatDefinition = {
2909
- formatName: 'XML',
2910
- mimeType: 'application/xml',
2911
- isValid: function (value, settings, schema) {
2912
- return true;
2913
- },
2914
- canBeValid: function (partialValue, settings, schema) {
2915
- return true;
2916
- },
2917
- heal: function (value, settings, schema) {
2918
- throw new Error('Not implemented');
2919
- },
2920
- subvalueDefinitions: [],
2921
- };
2922
- /**
2923
- * TODO: [🧠] Maybe propper instance of object
2924
- * TODO: [0] Make string_serialized_xml
2925
- * TODO: [1] Make type for XML Settings and Schema
2926
- * TODO: [🧠] What to use for validating XMLs - XSD,...
2927
- * TODO: [🍓] In `XmlFormatDefinition` implement simple `isValid`
2928
- * TODO: [🍓] In `XmlFormatDefinition` implement partial `canBeValid`
2929
- * TODO: [🍓] In `XmlFormatDefinition` implement `heal
2930
- * TODO: [🍓] In `XmlFormatDefinition` implement `subvalueDefinitions`
2931
- * TODO: [🏢] Allow to expect something inside XML and other formats
2932
- */
2933
-
2934
- /**
2935
- * Definitions for all formats supported by Promptbook
2903
+ * @returns {LlmExecutionTools} Single wrapper for multiple LlmExecutionTools
2936
2904
  *
2937
- * @private internal index of `...` <- TODO [🏢]
2938
- */
2939
- var FORMAT_DEFINITIONS = [
2940
- JsonFormatDefinition,
2941
- XmlFormatDefinition,
2942
- TextFormatDefinition,
2943
- CsvFormatDefinition,
2944
- ];
2945
- /**
2946
- * Note: [💞] Ignore a discrepancy between file name and entity name
2947
- */
2948
-
2949
- /**
2950
- * Maps available parameters to expected parameters
2905
+ * 0) If there is no LlmExecutionTools, it warns and returns valid but empty LlmExecutionTools
2906
+ * 1) If there is only one LlmExecutionTools, it returns it wrapped in a proxy object
2907
+ * 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.
2908
+ * 3) When all LlmExecutionTools fail, it throws an error with a list of all errors merged into one
2951
2909
  *
2952
- * The strategy is:
2953
- * 1) @@@
2954
- * 2) @@@
2955
2910
  *
2956
- * @throws {PipelineExecutionError} @@@
2957
- * @private within the repository used in `createPipelineExecutor`
2911
+ * Tip: You don't have to use this function directly, just pass an array of LlmExecutionTools to the `ExecutionTools`
2912
+ *
2913
+ * @public exported from `@promptbook/core`
2958
2914
  */
2959
- function mapAvailableToExpectedParameters(options) {
2960
- var e_1, _a;
2961
- var expectedParameters = options.expectedParameters, availableParameters = options.availableParameters;
2962
- var availableParametersNames = new Set(Object.keys(availableParameters));
2963
- var expectedParameterNames = new Set(Object.keys(expectedParameters));
2964
- var mappedParameters = {};
2965
- try {
2966
- // Phase 1️⃣: Matching mapping
2967
- for (var _b = __values(Array.from(union(availableParametersNames, expectedParameterNames))), _c = _b.next(); !_c.done; _c = _b.next()) {
2968
- var parameterName = _c.value;
2969
- // Situation: Parameter is available and expected
2970
- if (availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
2971
- mappedParameters[parameterName] = availableParameters[parameterName];
2972
- // <- Note: [👩‍👩‍👧] Maybe detect parameter collision here?
2973
- availableParametersNames.delete(parameterName);
2974
- expectedParameterNames.delete(parameterName);
2975
- }
2976
- // Situation: Parameter is available but NOT expected
2977
- else if (availableParametersNames.has(parameterName) && !expectedParameterNames.has(parameterName)) {
2978
- // [🐱‍👤] Do not pass this parameter to prompt - Maybe use it non-matching mapping
2979
- }
2980
- // Situation: Parameter is NOT available BUT expected
2981
- else if (!availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) {
2982
- // Do nothing here - this will be maybe fixed in the non-matching mapping
2983
- }
2984
- }
2985
- }
2986
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
2987
- finally {
2988
- try {
2989
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
2990
- }
2991
- finally { if (e_1) throw e_1.error; }
2992
- }
2993
- if (expectedParameterNames.size === 0) {
2994
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
2995
- Object.freeze(mappedParameters);
2996
- return mappedParameters;
2997
- }
2998
- // Phase 2️⃣: Non-matching mapping
2999
- if (expectedParameterNames.size !== availableParametersNames.size) {
3000
- throw new PipelineExecutionError(spaceTrim(function (block) { return "\n Can not map available parameters to expected parameters\n\n Mapped parameters:\n ".concat(block(Object.keys(mappedParameters)
3001
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3002
- .join('\n')), "\n\n Expected parameters which can not be mapped:\n ").concat(block(Array.from(expectedParameterNames)
3003
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3004
- .join('\n')), "\n\n Remaining available parameters:\n ").concat(block(Array.from(availableParametersNames)
3005
- .map(function (parameterName) { return "- {".concat(parameterName, "}"); })
3006
- .join('\n')), "\n\n "); }));
2915
+ function joinLlmExecutionTools() {
2916
+ var llmExecutionTools = [];
2917
+ for (var _i = 0; _i < arguments.length; _i++) {
2918
+ llmExecutionTools[_i] = arguments[_i];
3007
2919
  }
3008
- var expectedParameterNamesArray = Array.from(expectedParameterNames);
3009
- var availableParametersNamesArray = Array.from(availableParametersNames);
3010
- for (var i = 0; i < expectedParameterNames.size; i++) {
3011
- mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
2920
+ if (llmExecutionTools.length === 0) {
2921
+ var warningMessage = spaceTrim("\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 ");
2922
+ // TODO: [🟥] Detect browser / node and make it colorfull
2923
+ console.warn(warningMessage);
2924
+ /*
2925
+ return {
2926
+ async listModels() {
2927
+ // TODO: [🟥] Detect browser / node and make it colorfull
2928
+ console.warn(
2929
+ spaceTrim(
2930
+ (block) => `
2931
+
2932
+ You can't list models because you have no LLM Execution Tools defined:
2933
+
2934
+ tl;dr
2935
+
2936
+ ${block(warningMessage)}
2937
+ `,
2938
+ ),
2939
+ );
2940
+ return [];
2941
+ },
2942
+ };
2943
+ */
3012
2944
  }
3013
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
3014
- Object.freeze(mappedParameters);
3015
- return mappedParameters;
2945
+ return new (MultipleLlmExecutionTools.bind.apply(MultipleLlmExecutionTools, __spreadArray([void 0], __read(llmExecutionTools), false)))();
3016
2946
  }
2947
+ /**
2948
+ * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
2949
+ */
3017
2950
 
3018
2951
  /**
3019
2952
  * Extracts all code blocks from markdown.
@@ -3132,97 +3065,22 @@ function extractJsonBlock(markdown) {
3132
3065
  */
3133
3066
 
3134
3067
  /**
3135
- * Takes an item or an array of items and returns an array of items
3136
- *
3137
- * 1) Any item except array and undefined returns array with that one item (also null)
3138
- * 2) Undefined returns empty array
3139
- * 3) Array returns itself
3140
- *
3141
- * @private internal utility
3142
- */
3143
- function arrayableToArray(input) {
3144
- if (input === undefined) {
3145
- return [];
3146
- }
3147
- if (input instanceof Array) {
3148
- return input;
3149
- }
3150
- return [input];
3151
- }
3152
-
3153
- /**
3154
- * Format either small or big number
3155
- *
3156
- * @public exported from `@promptbook/utils`
3157
- */
3158
- function numberToString(value) {
3159
- if (value === 0) {
3160
- return '0';
3161
- }
3162
- else if (Number.isNaN(value)) {
3163
- return VALUE_STRINGS.nan;
3164
- }
3165
- else if (value === Infinity) {
3166
- return VALUE_STRINGS.infinity;
3167
- }
3168
- else if (value === -Infinity) {
3169
- return VALUE_STRINGS.negativeInfinity;
3170
- }
3171
- for (var exponent = 0; exponent < 15; exponent++) {
3172
- var factor = Math.pow(10, exponent);
3173
- var valueRounded = Math.round(value * factor) / factor;
3174
- if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
3175
- return valueRounded.toFixed(exponent);
3176
- }
3177
- }
3178
- return value.toString();
3179
- }
3180
-
3181
- /**
3182
- * Function `valueToString` will convert the given value to string
3183
- * This is useful and used in the `templateParameters` function
3184
- *
3185
- * Note: This function is not just calling `toString` method
3186
- * It's more complex and can handle this conversion specifically for LLM models
3187
- * See `VALUE_STRINGS`
3068
+ * Takes an item or an array of items and returns an array of items
3188
3069
  *
3189
- * Note: There are 2 similar functions
3190
- * - `valueToString` converts value to string for LLM models as human-readable string
3191
- * - `asSerializable` converts value to string to preserve full information to be able to convert it back
3070
+ * 1) Any item except array and undefined returns array with that one item (also null)
3071
+ * 2) Undefined returns empty array
3072
+ * 3) Array returns itself
3192
3073
  *
3193
- * @public exported from `@promptbook/utils`
3074
+ * @private internal utility
3194
3075
  */
3195
- function valueToString(value) {
3196
- try {
3197
- if (value === '') {
3198
- return VALUE_STRINGS.empty;
3199
- }
3200
- else if (value === null) {
3201
- return VALUE_STRINGS.null;
3202
- }
3203
- else if (value === undefined) {
3204
- return VALUE_STRINGS.undefined;
3205
- }
3206
- else if (typeof value === 'string') {
3207
- return value;
3208
- }
3209
- else if (typeof value === 'number') {
3210
- return numberToString(value);
3211
- }
3212
- else if (value instanceof Date) {
3213
- return value.toISOString();
3214
- }
3215
- else {
3216
- return JSON.stringify(value);
3217
- }
3076
+ function arrayableToArray(input) {
3077
+ if (input === undefined) {
3078
+ return [];
3218
3079
  }
3219
- catch (error) {
3220
- if (!(error instanceof Error)) {
3221
- throw error;
3222
- }
3223
- console.error(error);
3224
- return VALUE_STRINGS.unserializable;
3080
+ if (input instanceof Array) {
3081
+ return input;
3225
3082
  }
3083
+ return [input];
3226
3084
  }
3227
3085
 
3228
3086
  /**
@@ -3281,6 +3139,8 @@ function templateParameters(template, parameters) {
3281
3139
  throw new PipelineExecutionError("Parameter `{".concat(parameterName, "}` is not defined"));
3282
3140
  }
3283
3141
  parameterValue = valueToString(parameterValue);
3142
+ // Escape curly braces in parameter values to prevent prompt-injection
3143
+ parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
3284
3144
  if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
3285
3145
  parameterValue = parameterValue
3286
3146
  .split('\n')
@@ -3901,7 +3761,7 @@ function executeAttempts(options) {
3901
3761
  promptTitle: task.title,
3902
3762
  promptMessage: templateParameters(task.description || '', parameters),
3903
3763
  defaultValue: templateParameters(preparedContent, parameters),
3904
- // TODO: [🧠] !! Figure out how to define placeholder in .book.md file
3764
+ // TODO: [🧠] Figure out how to define placeholder in .book.md file
3905
3765
  placeholder: undefined,
3906
3766
  priority: priority,
3907
3767
  }))];
@@ -4601,7 +4461,10 @@ function executePipeline(options) {
4601
4461
  finally { if (e_2) throw e_2.error; }
4602
4462
  return [7 /*endfinally*/];
4603
4463
  case 19:
4604
- parametersToPass = inputParameters;
4464
+ parametersToPass = Object.fromEntries(Object.entries(inputParameters).map(function (_a) {
4465
+ var _b = __read(_a, 2), key = _b[0], value = _b[1];
4466
+ return [key, valueToString(value)];
4467
+ }));
4605
4468
  _g.label = 20;
4606
4469
  case 20:
4607
4470
  _g.trys.push([20, 25, , 28]);
@@ -4836,6 +4699,169 @@ function createPipelineExecutor(options) {
4836
4699
  * TODO: [🐚] Change onProgress to object that represents the running execution, can be subscribed via RxJS to and also awaited
4837
4700
  */
4838
4701
 
4702
+ /**
4703
+ * Async version of Array.forEach
4704
+ *
4705
+ * @param array - Array to iterate over
4706
+ * @param options - Options for the function
4707
+ * @param callbackfunction - Function to call for each item
4708
+ * @public exported from `@promptbook/utils`
4709
+ * @deprecated [🪂] Use queues instead
4710
+ */
4711
+ function forEachAsync(array, options, callbackfunction) {
4712
+ return __awaiter(this, void 0, void 0, function () {
4713
+ var _a, maxParallelCount, index, runningTasks, tasks, _loop_1, _b, _c, item, e_1_1;
4714
+ var e_1, _d;
4715
+ return __generator(this, function (_e) {
4716
+ switch (_e.label) {
4717
+ case 0:
4718
+ _a = options.maxParallelCount, maxParallelCount = _a === void 0 ? Infinity : _a;
4719
+ index = 0;
4720
+ runningTasks = [];
4721
+ tasks = [];
4722
+ _loop_1 = function (item) {
4723
+ var currentIndex, task;
4724
+ return __generator(this, function (_f) {
4725
+ switch (_f.label) {
4726
+ case 0:
4727
+ currentIndex = index++;
4728
+ task = callbackfunction(item, currentIndex, array);
4729
+ tasks.push(task);
4730
+ runningTasks.push(task);
4731
+ /* not await */ Promise.resolve(task).then(function () {
4732
+ runningTasks = runningTasks.filter(function (t) { return t !== task; });
4733
+ });
4734
+ if (!(maxParallelCount < runningTasks.length)) return [3 /*break*/, 2];
4735
+ return [4 /*yield*/, Promise.race(runningTasks)];
4736
+ case 1:
4737
+ _f.sent();
4738
+ _f.label = 2;
4739
+ case 2: return [2 /*return*/];
4740
+ }
4741
+ });
4742
+ };
4743
+ _e.label = 1;
4744
+ case 1:
4745
+ _e.trys.push([1, 6, 7, 8]);
4746
+ _b = __values(array), _c = _b.next();
4747
+ _e.label = 2;
4748
+ case 2:
4749
+ if (!!_c.done) return [3 /*break*/, 5];
4750
+ item = _c.value;
4751
+ return [5 /*yield**/, _loop_1(item)];
4752
+ case 3:
4753
+ _e.sent();
4754
+ _e.label = 4;
4755
+ case 4:
4756
+ _c = _b.next();
4757
+ return [3 /*break*/, 2];
4758
+ case 5: return [3 /*break*/, 8];
4759
+ case 6:
4760
+ e_1_1 = _e.sent();
4761
+ e_1 = { error: e_1_1 };
4762
+ return [3 /*break*/, 8];
4763
+ case 7:
4764
+ try {
4765
+ if (_c && !_c.done && (_d = _b.return)) _d.call(_b);
4766
+ }
4767
+ finally { if (e_1) throw e_1.error; }
4768
+ return [7 /*endfinally*/];
4769
+ case 8: return [4 /*yield*/, Promise.all(tasks)];
4770
+ case 9:
4771
+ _e.sent();
4772
+ return [2 /*return*/];
4773
+ }
4774
+ });
4775
+ });
4776
+ }
4777
+
4778
+ /**
4779
+ * Intercepts LLM tools and counts total usage of the tools
4780
+ *
4781
+ * @param llmTools LLM tools to be intercepted with usage counting
4782
+ * @returns LLM tools with same functionality with added total cost counting
4783
+ * @public exported from `@promptbook/core`
4784
+ */
4785
+ function countTotalUsage(llmTools) {
4786
+ var _this = this;
4787
+ var totalUsage = ZERO_USAGE;
4788
+ var proxyTools = {
4789
+ get title() {
4790
+ // TODO: [🧠] Maybe put here some suffix
4791
+ return llmTools.title;
4792
+ },
4793
+ get description() {
4794
+ // TODO: [🧠] Maybe put here some suffix
4795
+ return llmTools.description;
4796
+ },
4797
+ checkConfiguration: function () {
4798
+ return __awaiter(this, void 0, void 0, function () {
4799
+ return __generator(this, function (_a) {
4800
+ return [2 /*return*/, /* not await */ llmTools.checkConfiguration()];
4801
+ });
4802
+ });
4803
+ },
4804
+ listModels: function () {
4805
+ return /* not await */ llmTools.listModels();
4806
+ },
4807
+ getTotalUsage: function () {
4808
+ // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
4809
+ return totalUsage;
4810
+ },
4811
+ };
4812
+ if (llmTools.callChatModel !== undefined) {
4813
+ proxyTools.callChatModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
4814
+ var promptResult;
4815
+ return __generator(this, function (_a) {
4816
+ switch (_a.label) {
4817
+ case 0: return [4 /*yield*/, llmTools.callChatModel(prompt)];
4818
+ case 1:
4819
+ promptResult = _a.sent();
4820
+ totalUsage = addUsage(totalUsage, promptResult.usage);
4821
+ return [2 /*return*/, promptResult];
4822
+ }
4823
+ });
4824
+ }); };
4825
+ }
4826
+ if (llmTools.callCompletionModel !== undefined) {
4827
+ proxyTools.callCompletionModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
4828
+ var promptResult;
4829
+ return __generator(this, function (_a) {
4830
+ switch (_a.label) {
4831
+ case 0: return [4 /*yield*/, llmTools.callCompletionModel(prompt)];
4832
+ case 1:
4833
+ promptResult = _a.sent();
4834
+ totalUsage = addUsage(totalUsage, promptResult.usage);
4835
+ return [2 /*return*/, promptResult];
4836
+ }
4837
+ });
4838
+ }); };
4839
+ }
4840
+ if (llmTools.callEmbeddingModel !== undefined) {
4841
+ proxyTools.callEmbeddingModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
4842
+ var promptResult;
4843
+ return __generator(this, function (_a) {
4844
+ switch (_a.label) {
4845
+ case 0: return [4 /*yield*/, llmTools.callEmbeddingModel(prompt)];
4846
+ case 1:
4847
+ promptResult = _a.sent();
4848
+ totalUsage = addUsage(totalUsage, promptResult.usage);
4849
+ return [2 /*return*/, promptResult];
4850
+ }
4851
+ });
4852
+ }); };
4853
+ }
4854
+ // <- Note: [🤖]
4855
+ return proxyTools;
4856
+ }
4857
+ /**
4858
+ * TODO: [🧠][💸] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
4859
+ * TODO: [🧠] Is there some meaningfull way how to test this util
4860
+ * TODO: [🧠][🌯] Maybe a way how to hide ability to `get totalUsage`
4861
+ * > const [llmToolsWithUsage,getUsage] = countTotalUsage(llmTools);
4862
+ * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
4863
+ */
4864
+
4839
4865
  /**
4840
4866
  * Prepares the persona for the pipeline
4841
4867
  *
@@ -4898,10 +4924,10 @@ function preparePersona(personaDescription, tools, options) {
4898
4924
  });
4899
4925
  }
4900
4926
  /**
4901
- * TODO: [🔃][main] !! If the persona was prepared with different version or different set of models, prepare it once again
4902
- * TODO: [🏢] !! Check validity of `modelName` in pipeline
4903
- * TODO: [🏢] !! Check validity of `systemMessage` in pipeline
4904
- * TODO: [🏢] !! Check validity of `temperature` in pipeline
4927
+ * TODO: [🔃][main] If the persona was prepared with different version or different set of models, prepare it once again
4928
+ * TODO: [🏢] Check validity of `modelName` in pipeline
4929
+ * TODO: [🏢] Check validity of `systemMessage` in pipeline
4930
+ * TODO: [🏢] Check validity of `temperature` in pipeline
4905
4931
  */
4906
4932
 
4907
4933
  /**
@@ -5335,21 +5361,44 @@ function isValidFilePath(filename) {
5335
5361
  if (typeof filename !== 'string') {
5336
5362
  return false;
5337
5363
  }
5364
+ if (filename.split('\n').length > 1) {
5365
+ return false;
5366
+ }
5367
+ if (filename.split(' ').length >
5368
+ 5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
5369
+ return false;
5370
+ }
5338
5371
  var filenameSlashes = filename.split('\\').join('/');
5339
5372
  // Absolute Unix path: /hello.txt
5340
5373
  if (/^(\/)/i.test(filenameSlashes)) {
5374
+ // console.log(filename, 'Absolute Unix path: /hello.txt');
5341
5375
  return true;
5342
5376
  }
5343
5377
  // Absolute Windows path: /hello.txt
5344
5378
  if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
5379
+ // console.log(filename, 'Absolute Windows path: /hello.txt');
5345
5380
  return true;
5346
5381
  }
5347
5382
  // Relative path: ./hello.txt
5348
5383
  if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
5384
+ // console.log(filename, 'Relative path: ./hello.txt');
5385
+ return true;
5386
+ }
5387
+ // Allow paths like foo/hello
5388
+ if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
5389
+ // console.log(filename, 'Allow paths like foo/hello');
5390
+ return true;
5391
+ }
5392
+ // Allow paths like hello.book
5393
+ if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
5394
+ // console.log(filename, 'Allow paths like hello.book');
5349
5395
  return true;
5350
5396
  }
5351
5397
  return false;
5352
5398
  }
5399
+ /**
5400
+ * TODO: [🍏] Implement for MacOs
5401
+ */
5353
5402
 
5354
5403
  /**
5355
5404
  * The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
@@ -5374,6 +5423,9 @@ var scraperFetch = function (url, init) { return __awaiter(void 0, void 0, void
5374
5423
  }
5375
5424
  });
5376
5425
  }); };
5426
+ /**
5427
+ * TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
5428
+ */
5377
5429
 
5378
5430
  /**
5379
5431
  * @@@
@@ -5441,7 +5493,7 @@ function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
5441
5493
  },
5442
5494
  }];
5443
5495
  case 2:
5444
- if (!(isValidFilePath(sourceContent) || /\.[a-z]{1,10}$/i.exec(sourceContent))) return [3 /*break*/, 4];
5496
+ if (!isValidFilePath(sourceContent)) return [3 /*break*/, 4];
5445
5497
  if (tools.fs === undefined) {
5446
5498
  throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
5447
5499
  // <- TODO: [🧠] What is the best error type here`
@@ -5456,7 +5508,7 @@ function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
5456
5508
  return [4 /*yield*/, isFileExisting(filename_1, tools.fs)];
5457
5509
  case 3:
5458
5510
  if (!(_f.sent())) {
5459
- throw new NotFoundError(spaceTrim(function (block) { return "\n Can not make source handler for file which does not exist:\n\n File:\n ".concat(block(filename_1), "\n "); }));
5511
+ throw new NotFoundError(spaceTrim(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 "); }));
5460
5512
  }
5461
5513
  // TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
5462
5514
  return [2 /*return*/, {
@@ -5569,7 +5621,7 @@ function prepareKnowledgePieces(knowledgeSources, tools, options) {
5569
5621
  partialPieces = __spreadArray([], __read(partialPiecesUnchecked), false);
5570
5622
  return [2 /*return*/, "break"];
5571
5623
  }
5572
- console.warn(spaceTrim(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
5624
+ console.warn(spaceTrim(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
5573
5625
  .split('\n')
5574
5626
  .map(function (line) { return "> ".concat(line); })
5575
5627
  .join('\n')), "\n\n ").concat(block($registeredScrapersMessage(scrapers)), "\n\n\n "); }));
@@ -5607,7 +5659,7 @@ function prepareKnowledgePieces(knowledgeSources, tools, options) {
5607
5659
  return [7 /*endfinally*/];
5608
5660
  case 9:
5609
5661
  if (partialPieces === null) {
5610
- throw new KnowledgeScrapeError(spaceTrim(function (block) { return "\n Cannot scrape knowledge\n \n The source:\n > ".concat(block(knowledgeSource.sourceContent
5662
+ throw new KnowledgeScrapeError(spaceTrim(function (block) { return "\n Cannot scrape knowledge\n\n The source:\n > ".concat(block(knowledgeSource.sourceContent
5611
5663
  .split('\n')
5612
5664
  .map(function (line) { return "> ".concat(line); })
5613
5665
  .join('\n')), "\n\n No scraper found for the mime type \"").concat(sourceHandler.mimeType, "\"\n\n ").concat(block($registeredScrapersMessage(scrapers)), "\n\n\n "); }));
@@ -5698,7 +5750,7 @@ function prepareTasks(pipeline, tools, options) {
5698
5750
  * TODO: [😂] Adding knowledge should be convert to async high-level abstractions, simmilar thing with expectations to sync high-level abstractions
5699
5751
  * TODO: [🧠] Add context to each task (if missing)
5700
5752
  * TODO: [🧠] What is better name `prepareTask` or `prepareTaskAndParameters`
5701
- * TODO: [♨][main] !!! Prepare index the examples and maybe tasks
5753
+ * TODO: [♨][main] !!3 Prepare index the examples and maybe tasks
5702
5754
  * TODO: Write tests for `preparePipeline`
5703
5755
  * TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
5704
5756
  * TODO: [🧊] In future one preparation can take data from previous preparation and save tokens and time
@@ -5706,7 +5758,9 @@ function prepareTasks(pipeline, tools, options) {
5706
5758
  */
5707
5759
 
5708
5760
  /**
5709
- * Prepare pipeline from string (markdown) format to JSON format
5761
+ * Prepare pipeline locally
5762
+ *
5763
+ * @see https://github.com/webgptorg/promptbook/discussions/196
5710
5764
  *
5711
5765
  * Note: This function does not validate logic of the pipeline
5712
5766
  * Note: This function acts as part of compilation process
@@ -5720,16 +5774,17 @@ function preparePipeline(pipeline, tools, options) {
5720
5774
  <- TODO: [🧠][🪑] `promptbookVersion` */
5721
5775
  knowledgeSources /*
5722
5776
  <- TODO: [🧊] `knowledgePieces` */, personas /*
5723
- <- TODO: [🧊] `preparations` */, _llms, llmTools, llmToolsWithUsage, currentPreparation, preparations, preparedPersonas, knowledgeSourcesPrepared, partialknowledgePiecesPrepared, knowledgePiecesPrepared, tasksPrepared /* TODO: parameters: parametersPrepared*/;
5777
+ <- TODO: [🧊] `preparations` */, sources, _llms, llmTools, llmToolsWithUsage, currentPreparation, preparations, title, collection, prepareTitleExecutor, _c, result, outputParameters, titleRaw, preparedPersonas, knowledgeSourcesPrepared, partialknowledgePiecesPrepared, knowledgePiecesPrepared, tasksPrepared /* TODO: parameters: parametersPrepared*/;
5778
+ var _d;
5724
5779
  var _this = this;
5725
- return __generator(this, function (_c) {
5726
- switch (_c.label) {
5780
+ return __generator(this, function (_e) {
5781
+ switch (_e.label) {
5727
5782
  case 0:
5728
5783
  if (isPipelinePrepared(pipeline)) {
5729
5784
  return [2 /*return*/, pipeline];
5730
5785
  }
5731
5786
  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;
5732
- parameters = pipeline.parameters, tasks = pipeline.tasks, knowledgeSources = pipeline.knowledgeSources, personas = pipeline.personas;
5787
+ parameters = pipeline.parameters, tasks = pipeline.tasks, knowledgeSources = pipeline.knowledgeSources, personas = pipeline.personas, sources = pipeline.sources;
5733
5788
  if (tools === undefined || tools.llm === undefined) {
5734
5789
  throw new MissingToolsError('LLM tools are required for preparing the pipeline');
5735
5790
  }
@@ -5747,6 +5802,33 @@ function preparePipeline(pipeline, tools, options) {
5747
5802
  // <- TODO: [🧊]
5748
5803
  currentPreparation,
5749
5804
  ];
5805
+ title = pipeline.title;
5806
+ if (!(title === undefined || title === '' || title === DEFAULT_BOOK_TITLE)) return [3 /*break*/, 3];
5807
+ collection = createCollectionFromJson.apply(void 0, __spreadArray([], __read(PipelineCollection), false));
5808
+ _c = createPipelineExecutor;
5809
+ _d = {};
5810
+ return [4 /*yield*/, collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-title.book.md')];
5811
+ case 1:
5812
+ prepareTitleExecutor = _c.apply(void 0, [(_d.pipeline = _e.sent(),
5813
+ _d.tools = tools,
5814
+ _d)]);
5815
+ return [4 /*yield*/, prepareTitleExecutor({
5816
+ book: sources.map(function (_a) {
5817
+ var content = _a.content;
5818
+ return content;
5819
+ }).join('\n\n'),
5820
+ })];
5821
+ case 2:
5822
+ result = _e.sent();
5823
+ assertsExecutionSuccessful(result);
5824
+ outputParameters = result.outputParameters;
5825
+ titleRaw = outputParameters.title;
5826
+ if (isVerbose) {
5827
+ console.info("The title is \"".concat(titleRaw, "\""));
5828
+ }
5829
+ title = titleRaw || DEFAULT_BOOK_TITLE;
5830
+ _e.label = 3;
5831
+ case 3:
5750
5832
  preparedPersonas = new Array(personas.length);
5751
5833
  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 () {
5752
5834
  var modelRequirements, preparedPersona;
@@ -5765,12 +5847,12 @@ function preparePipeline(pipeline, tools, options) {
5765
5847
  }
5766
5848
  });
5767
5849
  }); })];
5768
- case 1:
5769
- _c.sent();
5850
+ case 4:
5851
+ _e.sent();
5770
5852
  knowledgeSourcesPrepared = knowledgeSources.map(function (source) { return (__assign(__assign({}, source), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
5771
5853
  return [4 /*yield*/, prepareKnowledgePieces(knowledgeSources /* <- TODO: [🧊] {knowledgeSources, knowledgePieces} */, __assign(__assign({}, tools), { llm: llmToolsWithUsage }), __assign(__assign({}, options), { rootDirname: rootDirname, maxParallelCount: maxParallelCount /* <- TODO: [🪂] */, isVerbose: isVerbose }))];
5772
- case 2:
5773
- partialknowledgePiecesPrepared = _c.sent();
5854
+ case 5:
5855
+ partialknowledgePiecesPrepared = _e.sent();
5774
5856
  knowledgePiecesPrepared = partialknowledgePiecesPrepared.map(function (piece) { return (__assign(__assign({}, piece), { preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id] })); });
5775
5857
  return [4 /*yield*/, prepareTasks({
5776
5858
  parameters: parameters,
@@ -5781,8 +5863,8 @@ function preparePipeline(pipeline, tools, options) {
5781
5863
  maxParallelCount: maxParallelCount /* <- TODO: [🪂] */,
5782
5864
  isVerbose: isVerbose,
5783
5865
  })];
5784
- case 3:
5785
- tasksPrepared = (_c.sent()).tasksPrepared;
5866
+ case 6:
5867
+ tasksPrepared = (_e.sent()).tasksPrepared;
5786
5868
  // ----- /Tasks preparation -----
5787
5869
  // TODO: [😂] Use here all `AsyncHighLevelAbstraction`
5788
5870
  // Note: Count total usage
@@ -5793,7 +5875,7 @@ function preparePipeline(pipeline, tools, options) {
5793
5875
  order: ORDER_OF_PIPELINE_JSON,
5794
5876
  value: __assign(__assign({}, pipeline), {
5795
5877
  // <- TODO: Probbably deeply clone the pipeline because `$exportJson` freezes the subobjects
5796
- knowledgeSources: knowledgeSourcesPrepared, knowledgePieces: knowledgePiecesPrepared, tasks: __spreadArray([], __read(tasksPrepared), false),
5878
+ title: title, knowledgeSources: knowledgeSourcesPrepared, knowledgePieces: knowledgePiecesPrepared, tasks: __spreadArray([], __read(tasksPrepared), false),
5797
5879
  // <- TODO: [🪓] Here should be no need for spreading new array, just ` tasks: tasksPrepared`
5798
5880
  personas: preparedPersonas, preparations: __spreadArray([], __read(preparations), false) }),
5799
5881
  })];
@@ -5802,7 +5884,7 @@ function preparePipeline(pipeline, tools, options) {
5802
5884
  });
5803
5885
  }
5804
5886
  /**
5805
- * TODO: Write tests for `preparePipeline`
5887
+ * TODO: Write tests for `preparePipeline` and `preparePipelineOnRemoteServer`
5806
5888
  * TODO: [🏏] Leverage the batch API and build queues @see https://platform.openai.com/docs/guides/batch
5807
5889
  * TODO: [🧊] In future one preparation can take data from previous preparation and save tokens and time
5808
5890
  * TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
@@ -5888,7 +5970,7 @@ var knowledgeCommandParser = {
5888
5970
  if (sourceContent === '') {
5889
5971
  throw new ParseError("Source is not defined");
5890
5972
  }
5891
- // TODO: [main] !!!! Following checks should be applied every link in the `sourceContent`
5973
+ // TODO: [main] !!4 Following checks should be applied every link in the `sourceContent`
5892
5974
  if (sourceContent.startsWith('http://')) {
5893
5975
  throw new ParseError("Source is not secure");
5894
5976
  }
@@ -6060,7 +6142,7 @@ var sectionCommandParser = {
6060
6142
  expectResultingParameterName();
6061
6143
  var parameter = $pipelineJson.parameters.find(function (param) { return param.name === $taskJson.resultingParameterName; });
6062
6144
  if (parameter === undefined) {
6063
- // TODO: !!!!!! Change to logic error for higher level abstractions to work
6145
+ // TODO: !!6 Change to logic error for higher level abstraction of chatbot to work
6064
6146
  throw new ParseError("Parameter `{".concat($taskJson.resultingParameterName, "}` is not defined so can not define example value of it"));
6065
6147
  }
6066
6148
  parameter.exampleValues = parameter.exampleValues || [];
@@ -6071,7 +6153,7 @@ var sectionCommandParser = {
6071
6153
  if (command.taskType === 'KNOWLEDGE') {
6072
6154
  knowledgeCommandParser.$applyToPipelineJson({
6073
6155
  type: 'KNOWLEDGE',
6074
- sourceContent: $taskJson.content, // <- TODO: [🐝][main] !!! Work with KNOWLEDGE which not referring to the source file or website, but its content itself
6156
+ sourceContent: $taskJson.content, // <- TODO: [🐝][main] !!3 Work with KNOWLEDGE which not referring to the source file or website, but its content itself
6075
6157
  }, $pipelineJson);
6076
6158
  $taskJson.isTask = false;
6077
6159
  return;
@@ -6966,20 +7048,24 @@ var ChatbotFormfactorDefinition = {
6966
7048
  */
6967
7049
  var GeneratorFormfactorDefinition = {
6968
7050
  name: 'GENERATOR',
6969
- description: "@@@",
7051
+ description: "Generates any kind (in HTML with possible scripts and css format) of content from input message",
6970
7052
  documentationUrl: "https://github.com/webgptorg/promptbook/discussions/184",
6971
7053
  pipelineInterface: {
6972
7054
  inputParameters: [
6973
- /* @@@ */
6974
7055
  {
6975
- name: 'nonce',
6976
- description: 'Just to prevent GENERATOR to be set as implicit formfactor',
7056
+ name: 'inputMessage',
7057
+ description: "Input message to be image made from",
6977
7058
  isInput: true,
6978
7059
  isOutput: false,
6979
7060
  },
6980
7061
  ],
6981
7062
  outputParameters: [
6982
- /* @@@ */
7063
+ {
7064
+ name: 'result',
7065
+ description: "Result in HTML to be shown to user",
7066
+ isInput: false,
7067
+ isOutput: true,
7068
+ },
6983
7069
  ],
6984
7070
  },
6985
7071
  };
@@ -7011,6 +7097,35 @@ var GenericFormfactorDefinition = {
7011
7097
  pipelineInterface: GENERIC_PIPELINE_INTERFACE,
7012
7098
  };
7013
7099
 
7100
+ /**
7101
+ * Image generator is form of app that generates image from input message
7102
+ *
7103
+ * @public exported from `@promptbook/core`
7104
+ */
7105
+ var ImageGeneratorFormfactorDefinition = {
7106
+ name: 'IMAGE_GENERATOR',
7107
+ description: "Generates prompt for image generation from input message",
7108
+ documentationUrl: "https://github.com/webgptorg/promptbook/discussions/184",
7109
+ pipelineInterface: {
7110
+ inputParameters: [
7111
+ {
7112
+ name: 'inputMessage',
7113
+ description: "Input message to be image made from",
7114
+ isInput: true,
7115
+ isOutput: false,
7116
+ },
7117
+ ],
7118
+ outputParameters: [
7119
+ {
7120
+ name: 'prompt',
7121
+ description: "Prompt to be used for image generation",
7122
+ isInput: false,
7123
+ isOutput: true,
7124
+ },
7125
+ ],
7126
+ },
7127
+ };
7128
+
7014
7129
  /**
7015
7130
  * Matcher is form of app that @@@
7016
7131
  *
@@ -7109,6 +7224,7 @@ var FORMFACTOR_DEFINITIONS = [
7109
7224
  SheetsFormfactorDefinition,
7110
7225
  MatcherFormfactorDefinition,
7111
7226
  GeneratorFormfactorDefinition,
7227
+ ImageGeneratorFormfactorDefinition,
7112
7228
  ];
7113
7229
  /**
7114
7230
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -7495,7 +7611,7 @@ var parameterCommandParser = {
7495
7611
  * Note: `$` is used to indicate that this function mutates given `pipelineJson`
7496
7612
  */
7497
7613
  $applyToPipelineJson: function (command, $pipelineJson) {
7498
- // Note: [🍣] Do nothing, its application is implemented separately in `precompilePipeline`
7614
+ // Note: [🍣] Do nothing, its application is implemented separately in `parsePipeline`
7499
7615
  },
7500
7616
  /**
7501
7617
  * Apply the PARAMETER command to the `pipelineJson`
@@ -7503,7 +7619,7 @@ var parameterCommandParser = {
7503
7619
  * Note: `$` is used to indicate that this function mutates given `taskJson`
7504
7620
  */
7505
7621
  $applyToTaskJson: function (command, $taskJson, $pipelineJson) {
7506
- // Note: [🍣] Do nothing, its application is implemented separately in `precompilePipeline`
7622
+ // Note: [🍣] Do nothing, its application is implemented separately in `parsePipeline`
7507
7623
  },
7508
7624
  /**
7509
7625
  * Converts the PARAMETER command back to string
@@ -7998,7 +8114,7 @@ var COMMANDS = [
7998
8114
  instrumentCommandParser,
7999
8115
  personaCommandParser,
8000
8116
  foreachCommandParser,
8001
- boilerplateCommandParser, // <- TODO: !! Only in development, remove in production
8117
+ boilerplateCommandParser, // <- TODO: Only in development, remove in production
8002
8118
  // <- 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
8003
8119
  ];
8004
8120
  /**
@@ -8422,7 +8538,7 @@ var QuickChatbotHla = {
8422
8538
  isOutput: true,
8423
8539
  exampleValues: ['Hello, I am a Pavol`s virtual avatar. How can I help you?'],
8424
8540
  });
8425
- // TODO: !!!!!! spaceTrim
8541
+ // TODO: Use spaceTrim in multiline strings
8426
8542
  $pipelineJson.tasks.push({
8427
8543
  taskType: 'PROMPT_TASK',
8428
8544
  name: 'create-an-answer',
@@ -8430,8 +8546,11 @@ var QuickChatbotHla = {
8430
8546
  content: 'Write a response to the user message:\n\n**Question from user**\n\n> {userMessage}\n\n**Previous conversation**\n\n> {previousConversationSummary}',
8431
8547
  resultingParameterName: 'chatbotResponse',
8432
8548
  personaName: personaName,
8433
- dependentParameterNames: ['userMessage', 'previousConversationSummary' /* !!!!!!, 'knowledge'*/],
8434
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8549
+ dependentParameterNames: [
8550
+ 'userMessage',
8551
+ 'previousConversationSummary' /* TODO: [🧠][📛], 'knowledge'*/,
8552
+ ],
8553
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8435
8554
  }, {
8436
8555
  taskType: 'PROMPT_TASK',
8437
8556
  name: 'summarize-the-conversation',
@@ -8445,24 +8564,27 @@ var QuickChatbotHla = {
8445
8564
  max: 10,
8446
8565
  },
8447
8566
  },
8448
- dependentParameterNames: ['userMessage', 'chatbotResponse' /* !!!!!!, 'knowledge'*/],
8449
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8567
+ dependentParameterNames: ['userMessage', 'chatbotResponse' /* TODO: [🧠][📛], 'knowledge'*/],
8568
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8450
8569
  }, {
8451
8570
  taskType: 'SIMPLE_TASK',
8452
8571
  name: 'title',
8453
8572
  title: 'Title',
8454
8573
  content: '{conversationSummary}',
8455
8574
  resultingParameterName: 'title',
8456
- dependentParameterNames: ['conversationSummary' /* !!!!!!, 'knowledge'*/],
8457
- // !!!!!! preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8575
+ dependentParameterNames: ['conversationSummary' /* TODO: [🧠][📛], 'knowledge'*/],
8576
+ // TODO: [🧠][📛] preparedContent: '{content}\n\n## Knowledge\n\n{knowledge}',
8458
8577
  });
8459
8578
  },
8460
8579
  };
8580
+ /**
8581
+ * TODO: [🧠][📛] Should this be here?
8582
+ */
8461
8583
 
8462
8584
  /**
8463
8585
  * All high-level abstractions
8464
8586
  *
8465
- * @private internal index of `precompilePipeline` (= used for sync) and `preparePipeline` (= used for async)
8587
+ * @private internal index of `parsePipeline` (= used for sync) and `preparePipeline` (= used for async)
8466
8588
  */
8467
8589
  var HIGH_LEVEL_ABSTRACTIONS = [
8468
8590
  ImplicitFormfactorHla,
@@ -8596,7 +8718,7 @@ function splitMarkdownIntoSections(markdown) {
8596
8718
  return;
8597
8719
  }
8598
8720
  if (!section.startsWith('#')) {
8599
- section = "# ".concat(DEFAULT_TITLE, "\n\n").concat(section);
8721
+ section = "# ".concat(DEFAULT_BOOK_TITLE, "\n\n").concat(section);
8600
8722
  }
8601
8723
  sections.push(section);
8602
8724
  buffer = [];
@@ -8651,7 +8773,7 @@ function splitMarkdownIntoSections(markdown) {
8651
8773
  /**
8652
8774
  * Normalizes the markdown by flattening the structure
8653
8775
  *
8654
- * - It always have h1 - if there is no h1 in the markdown, it will be added "# Untitled"
8776
+ * - It always have h1 - if there is no h1 in the markdown, it will be added `DEFAULT_BOOK_TITLE`
8655
8777
  * - All other headings are normalized to h2
8656
8778
  *
8657
8779
  * @public exported from `@promptbook/markdown-utils`
@@ -8660,7 +8782,7 @@ function flattenMarkdown(markdown) {
8660
8782
  var e_1, _a;
8661
8783
  var sections = splitMarkdownIntoSections(markdown);
8662
8784
  if (sections.length === 0) {
8663
- return "# ".concat(DEFAULT_TITLE);
8785
+ return "# ".concat(DEFAULT_BOOK_TITLE);
8664
8786
  }
8665
8787
  var flattenedMarkdown = '';
8666
8788
  var parsedSections = sections.map(parseMarkdownSection);
@@ -8671,7 +8793,7 @@ function flattenMarkdown(markdown) {
8671
8793
  }
8672
8794
  else {
8673
8795
  parsedSections.unshift(firstSection);
8674
- flattenedMarkdown += "# ".concat(DEFAULT_TITLE) + "\n\n"; // <- [🧠] Maybe 3 new lines?
8796
+ flattenedMarkdown += "# ".concat(DEFAULT_BOOK_TITLE) + "\n\n"; // <- [🧠] Maybe 3 new lines?
8675
8797
  }
8676
8798
  try {
8677
8799
  for (var parsedSections_1 = __values(parsedSections), parsedSections_1_1 = parsedSections_1.next(); !parsedSections_1_1.done; parsedSections_1_1 = parsedSections_1.next()) {
@@ -8698,13 +8820,13 @@ function flattenMarkdown(markdown) {
8698
8820
  */
8699
8821
 
8700
8822
  /**
8701
- * Removes HTML or Markdown comments from a string.
8823
+ * Removes Markdown (or HTML) comments
8702
8824
  *
8703
8825
  * @param {string} content - The string to remove comments from.
8704
8826
  * @returns {string} The input string with all comments removed.
8705
8827
  * @public exported from `@promptbook/markdown-utils`
8706
8828
  */
8707
- function removeContentComments(content) {
8829
+ function removeMarkdownComments(content) {
8708
8830
  return spaceTrim$1(content.replace(/<!--(.*?)-->/gs, ''));
8709
8831
  }
8710
8832
 
@@ -8737,7 +8859,7 @@ function titleToName(value) {
8737
8859
  *
8738
8860
  * Note: There are 3 similar functions:
8739
8861
  * - `compilePipeline` **(preferred)** - which propperly compiles the promptbook and use embedding for external knowledge
8740
- * - `precompilePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
8862
+ * - `parsePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
8741
8863
  * - `preparePipeline` - just one step in the compilation process
8742
8864
  *
8743
8865
  * Note: This function does not validate logic of the pipeline only the parsing
@@ -8748,10 +8870,10 @@ function titleToName(value) {
8748
8870
  * @throws {ParseError} if the promptbook string is not valid
8749
8871
  * @public exported from `@promptbook/core`
8750
8872
  */
8751
- function precompilePipeline(pipelineString) {
8873
+ function parsePipeline(pipelineString) {
8752
8874
  var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
8753
8875
  var $pipelineJson = {
8754
- title: DEFAULT_TITLE,
8876
+ title: DEFAULT_BOOK_TITLE,
8755
8877
  parameters: [],
8756
8878
  tasks: [],
8757
8879
  knowledgeSources: [],
@@ -8762,7 +8884,7 @@ function precompilePipeline(pipelineString) {
8762
8884
  {
8763
8885
  type: 'BOOK',
8764
8886
  path: null,
8765
- // <- TODO: !!!!!! Pass here path of the file
8887
+ // <- TODO: !!6 Pass here path of the file
8766
8888
  content: pipelineString,
8767
8889
  },
8768
8890
  ],
@@ -8780,18 +8902,44 @@ function precompilePipeline(pipelineString) {
8780
8902
  }
8781
8903
  // =============================================================
8782
8904
  // Note: 1️⃣ Parsing of the markdown into object
8905
+ // ==============
8906
+ // Note: 1️⃣◽1️⃣ Remove #!shebang and comments
8783
8907
  if (pipelineString.startsWith('#!')) {
8784
8908
  var _g = __read(pipelineString.split('\n')), shebangLine_1 = _g[0], restLines = _g.slice(1);
8785
8909
  if (!(shebangLine_1 || '').includes('ptbk')) {
8786
8910
  throw new ParseError(spaceTrim$1(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 "); }));
8787
8911
  }
8788
- pipelineString = restLines.join('\n');
8789
- }
8790
- pipelineString = removeContentComments(pipelineString);
8912
+ pipelineString = validatePipelineString(restLines.join('\n'));
8913
+ }
8914
+ pipelineString = removeMarkdownComments(pipelineString);
8915
+ pipelineString = spaceTrim$1(pipelineString);
8916
+ // <- TODO: [😧] `spaceTrim` should preserve discriminated type *(or at lease `PipelineString`)*
8917
+ // ==============
8918
+ // Note: 1️⃣◽2️⃣ Process flat pipeline
8919
+ var isMarkdownBeginningWithHeadline = pipelineString.startsWith('# ');
8920
+ var isLastLineReturnStatement = pipelineString.split('\n').pop().split('`').join('').startsWith('->');
8921
+ // TODO: Also (double)check
8922
+ // > const usedCommands
8923
+ // > const isBlocksUsed
8924
+ // > const returnStatementCount
8925
+ var isFlatPipeline = !isMarkdownBeginningWithHeadline && isLastLineReturnStatement;
8926
+ // console.log({ isMarkdownBeginningWithHeadline, isLastLineReturnStatement, isFlatPipeline });
8927
+ if (isFlatPipeline) {
8928
+ var pipelineStringLines = pipelineString.split('\n');
8929
+ var returnStatement_1 = pipelineStringLines.pop();
8930
+ var prompt_1 = spaceTrim$1(pipelineStringLines.join('\n'));
8931
+ pipelineString = validatePipelineString(spaceTrim$1(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 "); }));
8932
+ // <- TODO: Maybe use book` notation
8933
+ // console.log(pipelineString);
8934
+ }
8935
+ // ==============
8936
+ // Note: 1️⃣◽3️⃣ Parse the markdown
8791
8937
  pipelineString = flattenMarkdown(pipelineString) /* <- Note: [🥞] */;
8792
8938
  pipelineString = pipelineString.replaceAll(/`\{(?<parameterName>[a-z0-9_]+)\}`/gi, '{$<parameterName>}');
8793
8939
  pipelineString = pipelineString.replaceAll(/`->\s+\{(?<parameterName>[a-z0-9_]+)\}`/gi, '-> {$<parameterName>}');
8794
8940
  var _h = __read(splitMarkdownIntoSections(pipelineString).map(parseMarkdownSection)), pipelineHead = _h[0], pipelineSections = _h.slice(1); /* <- Note: [🥞] */
8941
+ // ==============
8942
+ // Note: 1️⃣◽4️⃣ Check markdown structure
8795
8943
  if (pipelineHead === undefined) {
8796
8944
  throw new UnexpectedError(spaceTrim$1(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 "); }));
8797
8945
  }
@@ -9175,14 +9323,14 @@ function precompilePipeline(pipelineString) {
9175
9323
  // =============================================================
9176
9324
  return exportJson({
9177
9325
  name: 'pipelineJson',
9178
- message: "Result of `precompilePipeline`",
9326
+ message: "Result of `parsePipeline`",
9179
9327
  order: ORDER_OF_PIPELINE_JSON,
9180
9328
  value: __assign({ formfactorName: 'GENERIC' }, $pipelineJson),
9181
9329
  });
9182
9330
  }
9183
9331
  /**
9184
9332
  * TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
9185
- * TODO: [main] !!!! Warn if used only sync version
9333
+ * TODO: [main] !!4 Warn if used only sync version
9186
9334
  * TODO: [🚞] Report here line/column of error
9187
9335
  * TODO: Use spaceTrim more effectively
9188
9336
  * TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
@@ -9195,10 +9343,7 @@ function precompilePipeline(pipelineString) {
9195
9343
  /**
9196
9344
  * Compile pipeline from string (markdown) format to JSON format
9197
9345
  *
9198
- * Note: There are 3 similar functions:
9199
- * - `compilePipeline` **(preferred)** - which propperly compiles the promptbook and use embedding for external knowledge
9200
- * - `precompilePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
9201
- * - `preparePipeline` - just one step in the compilation process
9346
+ * @see https://github.com/webgptorg/promptbook/discussions/196
9202
9347
  *
9203
9348
  * Note: This function does not validate logic of the pipeline only the parsing
9204
9349
  * Note: This function acts as compilation process
@@ -9216,7 +9361,7 @@ function compilePipeline(pipelineString, tools, options) {
9216
9361
  return __generator(this, function (_a) {
9217
9362
  switch (_a.label) {
9218
9363
  case 0:
9219
- pipelineJson = precompilePipeline(pipelineString);
9364
+ pipelineJson = parsePipeline(pipelineString);
9220
9365
  if (!(tools !== undefined && tools.llm !== undefined)) return [3 /*break*/, 2];
9221
9366
  return [4 /*yield*/, preparePipeline(pipelineJson, tools, options || {
9222
9367
  rootDirname: null,
@@ -9225,7 +9370,7 @@ function compilePipeline(pipelineString, tools, options) {
9225
9370
  pipelineJson = _a.sent();
9226
9371
  _a.label = 2;
9227
9372
  case 2:
9228
- // Note: No need to use `$exportJson` because `precompilePipeline` and `preparePipeline` already do that
9373
+ // Note: No need to use `$exportJson` because `parsePipeline` and `preparePipeline` already do that
9229
9374
  return [2 /*return*/, pipelineJson];
9230
9375
  }
9231
9376
  });
@@ -9576,6 +9721,25 @@ var $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
9576
9721
  * TODO: [®] DRY Register logic
9577
9722
  */
9578
9723
 
9724
+ /**
9725
+ * Determines if the given path is a root path.
9726
+ *
9727
+ * Note: This does not check if the file exists only if the path is valid
9728
+ * @public exported from `@promptbook/utils`
9729
+ */
9730
+ function isRootPath(value) {
9731
+ if (value === '/') {
9732
+ return true;
9733
+ }
9734
+ if (/^[A-Z]:\\$/i.test(value)) {
9735
+ return true;
9736
+ }
9737
+ return false;
9738
+ }
9739
+ /**
9740
+ * TODO: [🍏] Make for MacOS paths
9741
+ */
9742
+
9579
9743
  /**
9580
9744
  * @@@
9581
9745
  *
@@ -9590,17 +9754,45 @@ var $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
9590
9754
  * @public exported from `@promptbook/node`
9591
9755
  */
9592
9756
  function $provideLlmToolsConfigurationFromEnv() {
9593
- if (!$isRunningInNode()) {
9594
- throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9595
- }
9596
- dotenv.config();
9597
- // TODO: Walk to the root of the project and find the nearest `.env` file
9598
- // @see https://collboard.fra1.cdn.digitaloceanspaces.com/usercontent/education/image/png/1/2/ad/image.png
9599
- var llmToolsConfiguration = $llmToolsMetadataRegister
9600
- .list()
9601
- .map(function (metadata) { return metadata.createConfigurationFromEnv(process.env); })
9602
- .filter(function (configuration) { return configuration !== null; });
9603
- return llmToolsConfiguration;
9757
+ return __awaiter(this, void 0, void 0, function () {
9758
+ var rootDirname, i, envFilename, llmToolsConfiguration;
9759
+ return __generator(this, function (_a) {
9760
+ switch (_a.label) {
9761
+ case 0:
9762
+ if (!$isRunningInNode()) {
9763
+ throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9764
+ }
9765
+ rootDirname = process.cwd();
9766
+ i = 0;
9767
+ _a.label = 1;
9768
+ case 1:
9769
+ if (!(i < LOOP_LIMIT)) return [3 /*break*/, 4];
9770
+ envFilename = join(rootDirname, '.env' /* <- TODO: [🕝] Make here more candidates */);
9771
+ return [4 /*yield*/, isFileExisting(envFilename, $provideFilesystemForNode())];
9772
+ case 2:
9773
+ // console.log({ rootDirname, envFilename });
9774
+ if (_a.sent()) {
9775
+ dotenv.config({ path: envFilename });
9776
+ return [3 /*break*/, 4];
9777
+ }
9778
+ if (isRootPath(rootDirname)) {
9779
+ return [3 /*break*/, 4];
9780
+ }
9781
+ // Note: If the directory does not exist, try the parent directory
9782
+ rootDirname = join(rootDirname, '..');
9783
+ _a.label = 3;
9784
+ case 3:
9785
+ i++;
9786
+ return [3 /*break*/, 1];
9787
+ case 4:
9788
+ llmToolsConfiguration = $llmToolsMetadataRegister
9789
+ .list()
9790
+ .map(function (metadata) { return metadata.createConfigurationFromEnv(process.env); })
9791
+ .filter(function (configuration) { return configuration !== null; });
9792
+ return [2 /*return*/, llmToolsConfiguration];
9793
+ }
9794
+ });
9795
+ });
9604
9796
  }
9605
9797
  /**
9606
9798
  * TODO: [🧠][🪁] Maybe do allow to do auto-install if package not registered and not found
@@ -9823,15 +10015,28 @@ function createLlmToolsFromConfiguration(configuration, options) {
9823
10015
  */
9824
10016
  function $provideLlmToolsFromEnv(options) {
9825
10017
  if (options === void 0) { options = {}; }
9826
- if (!$isRunningInNode()) {
9827
- throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9828
- }
9829
- var configuration = $provideLlmToolsConfigurationFromEnv();
9830
- if (configuration.length === 0) {
9831
- // TODO: [🥃]
9832
- throw new Error(spaceTrim(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 "); }));
9833
- }
9834
- return createLlmToolsFromConfiguration(configuration, options);
10018
+ return __awaiter(this, void 0, void 0, function () {
10019
+ var configuration;
10020
+ return __generator(this, function (_a) {
10021
+ switch (_a.label) {
10022
+ case 0:
10023
+ if (!$isRunningInNode()) {
10024
+ throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
10025
+ }
10026
+ return [4 /*yield*/, $provideLlmToolsConfigurationFromEnv()];
10027
+ case 1:
10028
+ configuration = _a.sent();
10029
+ if (configuration.length === 0) {
10030
+ if ($llmToolsMetadataRegister.list().length === 0) {
10031
+ throw new UnexpectedError(spaceTrim(function (block) { return "\n No LLM tools registered, this is probably a bug in the Promptbook library\n\n ".concat(block($registeredLlmToolsMessage()), "}\n "); }));
10032
+ }
10033
+ // TODO: [🥃]
10034
+ throw new Error(spaceTrim(function (block) { return "\n No LLM tools found in the environment\n\n ".concat(block($registeredLlmToolsMessage()), "}\n "); }));
10035
+ }
10036
+ return [2 /*return*/, createLlmToolsFromConfiguration(configuration, options)];
10037
+ }
10038
+ });
10039
+ });
9835
10040
  }
9836
10041
  /**
9837
10042
  * TODO: @@@ write `$provideLlmToolsFromEnv` vs `$provideLlmToolsConfigurationFromEnv` vs `createLlmToolsFromConfiguration`
@@ -9900,23 +10105,31 @@ function $provideScrapersForNode(tools, options) {
9900
10105
  */
9901
10106
 
9902
10107
  /**
9903
- * Extracts code block from markdown.
10108
+ * Detects if the code is running in a browser environment in main thread (Not in a web worker)
9904
10109
  *
9905
- * - When there are multiple or no code blocks the function throws a `ParseError`
10110
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
9906
10111
  *
9907
- * Note: There are multiple simmilar function:
9908
- * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
9909
- * - `extractJsonBlock` extracts exactly one valid JSON code block
9910
- * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
9911
- * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
10112
+ * @public exported from `@promptbook/utils`
10113
+ */
10114
+ new Function("\n try {\n return this === window;\n } catch (e) {\n return false;\n }\n");
10115
+
10116
+ /**
10117
+ * Detects if the code is running in jest environment
9912
10118
  *
9913
- * @public exported from `@promptbook/markdown-utils`
9914
- * @throws {ParseError} if there is not exactly one code block in the markdown
10119
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10120
+ *
10121
+ * @public exported from `@promptbook/utils`
9915
10122
  */
9916
- function extractBlock(markdown) {
9917
- var content = extractOneBlockFromMarkdown(markdown).content;
9918
- return content;
9919
- }
10123
+ new Function("\n try {\n return process.env.JEST_WORKER_ID !== undefined;\n } catch (e) {\n return false;\n }\n");
10124
+
10125
+ /**
10126
+ * Detects if the code is running in a web worker
10127
+ *
10128
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10129
+ *
10130
+ * @public exported from `@promptbook/utils`
10131
+ */
10132
+ 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");
9920
10133
 
9921
10134
  /**
9922
10135
  * Makes first letter of a string uppercase
@@ -9927,6 +10140,21 @@ function decapitalize(word) {
9927
10140
  return word.substring(0, 1).toLowerCase() + word.substring(1);
9928
10141
  }
9929
10142
 
10143
+ /**
10144
+ * Parses keywords from a string
10145
+ *
10146
+ * @param {string} input
10147
+ * @returns {Set} of keywords without diacritics in lowercase
10148
+ * @public exported from `@promptbook/utils`
10149
+ */
10150
+ function parseKeywordsFromString(input) {
10151
+ var keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
10152
+ .toLowerCase()
10153
+ .split(/[^a-z0-9]+/gs)
10154
+ .filter(function (value) { return value; });
10155
+ return new Set(keywords);
10156
+ }
10157
+
9930
10158
  /**
9931
10159
  * @@@
9932
10160
  *
@@ -9980,20 +10208,39 @@ function normalizeWhitespaces(sentence) {
9980
10208
  return sentence.replace(/\s+/gs, ' ').trim();
9981
10209
  }
9982
10210
 
10211
+ // <- TODO: Auto convert to type `import { ... } from 'type-fest';`
9983
10212
  /**
9984
- * Parses keywords from a string
10213
+ * Tests if the value is [🚉] serializable as JSON
10214
+ *
10215
+ * - Almost all primitives are serializable BUT:
10216
+ * - `undefined` is not serializable
10217
+ * - `NaN` is not serializable
10218
+ * - Objects and arrays are serializable if all their properties are serializable
10219
+ * - Functions are not serializable
10220
+ * - Circular references are not serializable
10221
+ * - `Date` objects are not serializable
10222
+ * - `Map` and `Set` objects are not serializable
10223
+ * - `RegExp` objects are not serializable
10224
+ * - `Error` objects are not serializable
10225
+ * - `Symbol` objects are not serializable
10226
+ * - And much more...
10227
+ *
9985
10228
  *
9986
- * @param {string} input
9987
- * @returns {Set} of keywords without diacritics in lowercase
9988
10229
  * @public exported from `@promptbook/utils`
9989
10230
  */
9990
- function parseKeywordsFromString(input) {
9991
- var keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
9992
- .toLowerCase()
9993
- .split(/[^a-z0-9]+/gs)
9994
- .filter(function (value) { return value; });
9995
- return new Set(keywords);
10231
+ function isSerializableAsJson(value) {
10232
+ try {
10233
+ checkSerializableAsJson({ value: value });
10234
+ return true;
10235
+ }
10236
+ catch (error) {
10237
+ return false;
10238
+ }
9996
10239
  }
10240
+ /**
10241
+ * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
10242
+ * TODO: [🧠][💺] Can be done this on type-level?
10243
+ */
9997
10244
 
9998
10245
  /**
9999
10246
  * Function trimCodeBlock will trim starting and ending code block from the string if it is present.
@@ -10101,6 +10348,25 @@ function unwrapResult(text, options) {
10101
10348
  * TODO: [🧠] Should this also unwrap the (parenthesis)
10102
10349
  */
10103
10350
 
10351
+ /**
10352
+ * Extracts code block from markdown.
10353
+ *
10354
+ * - When there are multiple or no code blocks the function throws a `ParseError`
10355
+ *
10356
+ * Note: There are multiple simmilar function:
10357
+ * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
10358
+ * - `extractJsonBlock` extracts exactly one valid JSON code block
10359
+ * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
10360
+ * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
10361
+ *
10362
+ * @public exported from `@promptbook/markdown-utils`
10363
+ * @throws {ParseError} if there is not exactly one code block in the markdown
10364
+ */
10365
+ function extractBlock(markdown) {
10366
+ var content = extractOneBlockFromMarkdown(markdown).content;
10367
+ return content;
10368
+ }
10369
+
10104
10370
  /**
10105
10371
  * Does nothing, but preserves the function in the bundle
10106
10372
  * Compiler is tricked into thinking the function is used
@@ -10135,36 +10401,9 @@ function preserve(func) {
10135
10401
  }); })();
10136
10402
  }
10137
10403
  /**
10138
- * TODO: !! [1] This maybe does memory leak
10139
- */
10140
-
10141
- /**
10142
- * Converts anything to string that can be used for debugging and logging
10143
- *
10144
- * @param value String value for logging
10145
- * @private internal util
10404
+ * TODO: Probbably remove in favour of `keepImported`
10405
+ * TODO: [1] This maybe does memory leak
10146
10406
  */
10147
- function unknownToString(value) {
10148
- if (value === undefined) {
10149
- return 'undefined';
10150
- }
10151
- else if (value === null) {
10152
- return 'null';
10153
- }
10154
- else if (['number', 'string', 'boolean'].includes(typeof value)) {
10155
- return typeof value + ' ' + value.toString();
10156
- }
10157
- else if (typeof value === 'object' && Array.isArray(value)) {
10158
- return 'array containing [' + value.map(function (item) { return unknownToString(item); }).join(', ') + ']';
10159
- }
10160
- else if (typeof value === 'object') {
10161
- // TODO: Maybe serialize the object
10162
- return 'object';
10163
- }
10164
- else {
10165
- return 'unknown (Search in promptbook code for [🔹])';
10166
- }
10167
- }
10168
10407
 
10169
10408
  /**
10170
10409
  * ScriptExecutionTools for JavaScript implemented via eval
@@ -10295,7 +10534,7 @@ var JavascriptEvalExecutionTools = /** @class */ (function () {
10295
10534
  case 2:
10296
10535
  result = _a.sent();
10297
10536
  if (typeof result !== 'string') {
10298
- throw new PipelineExecutionError("Script must return a string, but returned ".concat(unknownToString(result)));
10537
+ throw new PipelineExecutionError("Script must return a string, but returned ".concat(valueToString(result)));
10299
10538
  }
10300
10539
  return [3 /*break*/, 4];
10301
10540
  case 3:
@@ -10322,7 +10561,7 @@ var JavascriptEvalExecutionTools = /** @class */ (function () {
10322
10561
  throw error_1;
10323
10562
  case 4:
10324
10563
  if (typeof result !== 'string') {
10325
- throw new PipelineExecutionError("Script must return a string, but ".concat(unknownToString(result)));
10564
+ throw new PipelineExecutionError("Script must return a string, but ".concat(valueToString(result)));
10326
10565
  }
10327
10566
  return [2 /*return*/, result];
10328
10567
  }
@@ -10362,9 +10601,11 @@ function $provideExecutionToolsForNode(options) {
10362
10601
  throw new EnvironmentMismatchError('Function `$getExecutionToolsForNode` works only in Node.js environment');
10363
10602
  }
10364
10603
  fs = $provideFilesystemForNode();
10365
- llm = $provideLlmToolsFromEnv(options);
10366
- return [4 /*yield*/, $provideExecutablesForNode(options)];
10604
+ return [4 /*yield*/, $provideLlmToolsFromEnv(options)];
10367
10605
  case 1:
10606
+ llm = _b.sent();
10607
+ return [4 /*yield*/, $provideExecutablesForNode(options)];
10608
+ case 2:
10368
10609
  executables = _b.sent();
10369
10610
  _a = {
10370
10611
  llm: llm,
@@ -10372,7 +10613,7 @@ function $provideExecutionToolsForNode(options) {
10372
10613
  executables: executables
10373
10614
  };
10374
10615
  return [4 /*yield*/, $provideScrapersForNode({ fs: fs, llm: llm, executables: executables }, options)];
10375
- case 2:
10616
+ case 3:
10376
10617
  tools = (_a.scrapers = _b.sent(),
10377
10618
  _a.script = [new JavascriptExecutionTools(options)],
10378
10619
  _a);
@@ -10595,7 +10836,7 @@ function createCollectionFromPromise(promptbookSourcesPromiseOrFactory) {
10595
10836
  */
10596
10837
  function createCollectionFromDirectory(path, tools, options) {
10597
10838
  return __awaiter(this, void 0, void 0, function () {
10598
- var makedLibraryFilePath, _a, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, collection;
10839
+ var madeLibraryFilePath, _a, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, rootUrl, collection;
10599
10840
  var _this = this;
10600
10841
  return __generator(this, function (_f) {
10601
10842
  switch (_f.label) {
@@ -10610,18 +10851,18 @@ function createCollectionFromDirectory(path, tools, options) {
10610
10851
  throw new EnvironmentMismatchError('Can not create collection without filesystem tools');
10611
10852
  // <- TODO: [🧠] What is the best error type here`
10612
10853
  }
10613
- makedLibraryFilePath = join(path, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
10854
+ madeLibraryFilePath = join(path, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
10614
10855
  // <- TODO: [🦒] Allow to override (pass different value into the function)
10615
10856
  , ".json"));
10616
- return [4 /*yield*/, isFileExisting(makedLibraryFilePath, tools.fs)];
10857
+ return [4 /*yield*/, isFileExisting(madeLibraryFilePath, tools.fs)];
10617
10858
  case 3:
10618
10859
  if (!(_f.sent())) ;
10619
10860
  else {
10620
- colors.green("(In future, not implemented yet) Using your compiled pipeline collection ".concat(makedLibraryFilePath));
10621
- // TODO: !! Implement;
10861
+ colors.green("(In future, not implemented yet) Using your compiled pipeline collection ".concat(madeLibraryFilePath));
10862
+ // TODO: Implement;
10622
10863
  // TODO: [🌗]
10623
10864
  }
10624
- _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;
10865
+ _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;
10625
10866
  collection = createCollectionFromPromise(function () { return __awaiter(_this, void 0, void 0, function () {
10626
10867
  var fileNames, collection, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1;
10627
10868
  var e_1, _a;
@@ -10647,34 +10888,35 @@ function createCollectionFromDirectory(path, tools, options) {
10647
10888
  });
10648
10889
  collection = new Map();
10649
10890
  _loop_1 = function (fileName) {
10650
- var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, existing, error_1, wrappedErrorMessage;
10651
- return __generator(this, function (_e) {
10652
- switch (_e.label) {
10891
+ var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, _e, pipelineUrl, existing, error_1, wrappedErrorMessage;
10892
+ return __generator(this, function (_f) {
10893
+ switch (_f.label) {
10653
10894
  case 0:
10654
10895
  sourceFile = './' + fileName.split('\\').join('/');
10655
10896
  rootDirname = dirname(sourceFile).split('\\').join('/');
10656
- _e.label = 1;
10897
+ _f.label = 1;
10657
10898
  case 1:
10658
- _e.trys.push([1, 8, , 9]);
10899
+ _f.trys.push([1, 8, , 9]);
10659
10900
  pipeline = null;
10660
10901
  if (!fileName.endsWith('.book.md')) return [3 /*break*/, 4];
10902
+ _c = validatePipelineString;
10661
10903
  return [4 /*yield*/, readFile(fileName, 'utf-8')];
10662
10904
  case 2:
10663
- pipelineString = (_e.sent());
10905
+ pipelineString = _c.apply(void 0, [_f.sent()]);
10664
10906
  return [4 /*yield*/, compilePipeline(pipelineString, tools, {
10665
10907
  rootDirname: rootDirname,
10666
10908
  })];
10667
10909
  case 3:
10668
- pipeline = _e.sent();
10910
+ pipeline = _f.sent();
10669
10911
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
10670
10912
  return [3 /*break*/, 7];
10671
10913
  case 4:
10672
10914
  if (!fileName.endsWith('.book.json')) return [3 /*break*/, 6];
10673
- _d = (_c = JSON).parse;
10915
+ _e = (_d = JSON).parse;
10674
10916
  return [4 /*yield*/, readFile(fileName, 'utf-8')];
10675
10917
  case 5:
10676
10918
  // TODO: Handle non-valid JSON files
10677
- pipeline = _d.apply(_c, [_e.sent()]);
10919
+ pipeline = _e.apply(_d, [_f.sent()]);
10678
10920
  // TODO: [🌗]
10679
10921
  pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
10680
10922
  return [3 /*break*/, 7];
@@ -10682,10 +10924,24 @@ function createCollectionFromDirectory(path, tools, options) {
10682
10924
  if (isVerbose) {
10683
10925
  console.info(colors.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")));
10684
10926
  }
10685
- _e.label = 7;
10927
+ _f.label = 7;
10686
10928
  case 7:
10687
10929
  // ---
10688
10930
  if (pipeline !== null) {
10931
+ if (rootUrl !== undefined) {
10932
+ if (pipeline.pipelineUrl === undefined) {
10933
+ pipelineUrl = rootUrl + '/' + fileName.split('\\').join('/');
10934
+ if (isVerbose) {
10935
+ console.info(colors.yellow("Implicitly set pipeline URL to ".concat(pipelineUrl, " from ").concat(fileName
10936
+ .split('\\')
10937
+ .join('/'))));
10938
+ }
10939
+ pipeline = __assign(__assign({}, pipeline), { pipelineUrl: pipelineUrl });
10940
+ }
10941
+ else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
10942
+ throw new PipelineUrlError(spaceTrim("\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 ")));
10943
+ }
10944
+ }
10689
10945
  // TODO: [👠] DRY
10690
10946
  if (pipeline.pipelineUrl === undefined) {
10691
10947
  if (isVerbose) {
@@ -10717,17 +10973,17 @@ function createCollectionFromDirectory(path, tools, options) {
10717
10973
  }
10718
10974
  else {
10719
10975
  existing = collection.get(pipeline.pipelineUrl);
10720
- throw new PipelineUrlError(spaceTrim("\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 ")));
10976
+ throw new PipelineUrlError(spaceTrim("\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 ")));
10721
10977
  }
10722
10978
  }
10723
10979
  }
10724
10980
  return [3 /*break*/, 9];
10725
10981
  case 8:
10726
- error_1 = _e.sent();
10982
+ error_1 = _f.sent();
10727
10983
  if (!(error_1 instanceof Error)) {
10728
10984
  throw error_1;
10729
10985
  }
10730
- wrappedErrorMessage = spaceTrim(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';
10986
+ wrappedErrorMessage = spaceTrim(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';
10731
10987
  if (isCrashedOnError) {
10732
10988
  throw new CollectionError(wrappedErrorMessage);
10733
10989
  }
@@ -10783,40 +11039,6 @@ function createCollectionFromDirectory(path, tools, options) {
10783
11039
  * TODO: Maybe move from `@promptbook/node` to `@promptbook/core` as we removes direct dependency on `fs`
10784
11040
  */
10785
11041
 
10786
- // <- TODO: !!!!!!! Auto convert to type `import { ... } from 'type-fest';`
10787
- /**
10788
- * Tests if the value is [🚉] serializable as JSON
10789
- *
10790
- * - Almost all primitives are serializable BUT:
10791
- * - `undefined` is not serializable
10792
- * - `NaN` is not serializable
10793
- * - Objects and arrays are serializable if all their properties are serializable
10794
- * - Functions are not serializable
10795
- * - Circular references are not serializable
10796
- * - `Date` objects are not serializable
10797
- * - `Map` and `Set` objects are not serializable
10798
- * - `RegExp` objects are not serializable
10799
- * - `Error` objects are not serializable
10800
- * - `Symbol` objects are not serializable
10801
- * - And much more...
10802
- *
10803
- *
10804
- * @public exported from `@promptbook/utils`
10805
- */
10806
- function isSerializableAsJson(value) {
10807
- try {
10808
- checkSerializableAsJson({ value: value });
10809
- return true;
10810
- }
10811
- catch (error) {
10812
- return false;
10813
- }
10814
- }
10815
- /**
10816
- * TODO: [🧠][main] !!! In-memory cache of same values to prevent multiple checks
10817
- * TODO: [🧠][💺] Can be done this on type-level?
10818
- */
10819
-
10820
11042
  /**
10821
11043
  * Stringify the PipelineJson with proper formatting
10822
11044
  *
@@ -11166,45 +11388,5 @@ function $execCommands(_a) {
11166
11388
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
11167
11389
  */
11168
11390
 
11169
- /**
11170
- * @@@
11171
- *
11172
- * @public exported from `@promptbook/node`
11173
- */
11174
- var wizzard = {
11175
- /**
11176
- * @@@!!!!!!
11177
- */
11178
- run: function (book, inputParameters, onProgress) {
11179
- return __awaiter(this, void 0, void 0, function () {
11180
- var tools, collection, pipeline, pipelineExecutor, result;
11181
- return __generator(this, function (_a) {
11182
- switch (_a.label) {
11183
- case 0: return [4 /*yield*/, $provideExecutionToolsForNode()];
11184
- case 1:
11185
- tools = _a.sent();
11186
- return [4 /*yield*/, createCollectionFromDirectory('./books', tools)];
11187
- case 2:
11188
- collection = _a.sent();
11189
- return [4 /*yield*/, collection.getPipelineByUrl(book)];
11190
- case 3:
11191
- pipeline = _a.sent();
11192
- pipelineExecutor = createPipelineExecutor({ pipeline: pipeline, tools: tools });
11193
- return [4 /*yield*/, pipelineExecutor(inputParameters, onProgress)];
11194
- case 4:
11195
- result = _a.sent();
11196
- // ▶ Fail if the execution was not successful
11197
- assertsExecutionSuccessful(result);
11198
- // ▶ Return the result
11199
- return [2 /*return*/, result];
11200
- }
11201
- });
11202
- });
11203
- },
11204
- };
11205
- /**
11206
- * TODO: !!!!!! Add to readmes - one markdown here imported in all packages
11207
- */
11208
-
11209
- export { $execCommand, $execCommands, $provideExecutablesForNode, $provideExecutionToolsForNode, $provideFilesystemForNode, $provideLlmToolsConfigurationFromEnv, $provideLlmToolsFromEnv, $provideScrapersForNode, BOOK_LANGUAGE_VERSION, FileCacheStorage, PROMPTBOOK_ENGINE_VERSION, createCollectionFromDirectory, wizzard };
11391
+ export { $execCommand, $execCommands, $provideExecutablesForNode, $provideExecutionToolsForNode, $provideFilesystemForNode, $provideLlmToolsConfigurationFromEnv, $provideLlmToolsFromEnv, $provideScrapersForNode, BOOK_LANGUAGE_VERSION, FileCacheStorage, PROMPTBOOK_ENGINE_VERSION, createCollectionFromDirectory };
11210
11392
  //# sourceMappingURL=index.es.js.map