@promptbook/node 0.94.0 → 0.98.0-10

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 (75) hide show
  1. package/README.md +6 -2
  2. package/esm/index.es.js +300 -237
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/src/_packages/anthropic-claude.index.d.ts +2 -2
  5. package/esm/typings/src/_packages/cli.index.d.ts +4 -0
  6. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  7. package/esm/typings/src/_packages/openai.index.d.ts +10 -0
  8. package/esm/typings/src/_packages/types.index.d.ts +14 -4
  9. package/esm/typings/src/_packages/{wizzard.index.d.ts → wizard.index.d.ts} +6 -2
  10. package/esm/typings/src/cli/cli-commands/prettify.d.ts +1 -1
  11. package/esm/typings/src/cli/cli-commands/test-command.d.ts +1 -1
  12. package/esm/typings/src/config.d.ts +1 -1
  13. package/esm/typings/src/conversion/archive/loadArchive.d.ts +1 -1
  14. package/esm/typings/src/conversion/archive/saveArchive.d.ts +2 -2
  15. package/esm/typings/src/conversion/prettify/renderPipelineMermaidOptions.d.ts +1 -1
  16. package/esm/typings/src/dialogs/callback/CallbackInterfaceTools.d.ts +1 -1
  17. package/esm/typings/src/execution/AbstractTaskResult.d.ts +2 -2
  18. package/esm/typings/src/execution/createPipelineExecutor/$OngoingTaskResult.d.ts +8 -0
  19. package/esm/typings/src/execution/createPipelineExecutor/00-CreatePipelineExecutorOptions.d.ts +1 -1
  20. package/esm/typings/src/execution/execution-report/ExecutionPromptReportJson.d.ts +2 -2
  21. package/esm/typings/src/execution/translation/automatic-translate/translateMessages.d.ts +1 -1
  22. package/esm/typings/src/execution/utils/validatePromptResult.d.ts +53 -0
  23. package/esm/typings/src/llm-providers/_common/register/{$provideLlmToolsForWizzardOrCli.d.ts → $provideLlmToolsForWizardOrCli.d.ts} +2 -2
  24. package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +3 -3
  25. package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionToolsOptions.d.ts +2 -2
  26. package/esm/typings/src/llm-providers/anthropic-claude/register-configuration.d.ts +1 -1
  27. package/esm/typings/src/llm-providers/anthropic-claude/register-constructor.d.ts +1 -1
  28. package/esm/typings/src/llm-providers/azure-openai/register-configuration.d.ts +1 -1
  29. package/esm/typings/src/llm-providers/azure-openai/register-constructor.d.ts +1 -1
  30. package/esm/typings/src/llm-providers/deepseek/register-configuration.d.ts +1 -1
  31. package/esm/typings/src/llm-providers/deepseek/register-constructor.d.ts +1 -1
  32. package/esm/typings/src/llm-providers/google/register-configuration.d.ts +1 -1
  33. package/esm/typings/src/llm-providers/google/register-constructor.d.ts +1 -1
  34. package/esm/typings/src/llm-providers/ollama/register-configuration.d.ts +1 -1
  35. package/esm/typings/src/llm-providers/ollama/register-constructor.d.ts +1 -1
  36. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +1 -1
  37. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionToolsOptions.d.ts +2 -2
  38. package/esm/typings/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +4 -4
  39. package/esm/typings/src/llm-providers/openai/OpenAiCompatibleExecutionToolsOptions.d.ts +52 -0
  40. package/esm/typings/src/llm-providers/openai/OpenAiExecutionToolsOptions.d.ts +3 -5
  41. package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +74 -0
  42. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +13 -2
  43. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +16 -2
  44. package/esm/typings/src/remote-server/socket-types/listModels/PromptbookServer_ListModels_Request.d.ts +1 -1
  45. package/esm/typings/src/scrapers/_boilerplate/createBoilerplateScraper.d.ts +1 -1
  46. package/esm/typings/src/scrapers/_boilerplate/register-constructor.d.ts +1 -1
  47. package/esm/typings/src/scrapers/_boilerplate/register-metadata.d.ts +2 -2
  48. package/esm/typings/src/scrapers/_common/prepareKnowledgePieces.d.ts +1 -1
  49. package/esm/typings/src/scrapers/_common/register/ScraperAndConverterMetadata.d.ts +1 -1
  50. package/esm/typings/src/scrapers/document/createDocumentScraper.d.ts +1 -1
  51. package/esm/typings/src/scrapers/document/register-constructor.d.ts +1 -1
  52. package/esm/typings/src/scrapers/document/register-metadata.d.ts +2 -2
  53. package/esm/typings/src/scrapers/document-legacy/createLegacyDocumentScraper.d.ts +1 -1
  54. package/esm/typings/src/scrapers/document-legacy/register-constructor.d.ts +1 -1
  55. package/esm/typings/src/scrapers/document-legacy/register-metadata.d.ts +2 -2
  56. package/esm/typings/src/scrapers/markdown/createMarkdownScraper.d.ts +1 -4
  57. package/esm/typings/src/scrapers/markdown/register-constructor.d.ts +1 -1
  58. package/esm/typings/src/scrapers/markdown/register-metadata.d.ts +2 -2
  59. package/esm/typings/src/scrapers/markitdown/createMarkitdownScraper.d.ts +1 -1
  60. package/esm/typings/src/scrapers/markitdown/register-constructor.d.ts +1 -1
  61. package/esm/typings/src/scrapers/markitdown/register-metadata.d.ts +2 -2
  62. package/esm/typings/src/scrapers/pdf/createPdfScraper.d.ts +1 -1
  63. package/esm/typings/src/scrapers/pdf/register-constructor.d.ts +1 -1
  64. package/esm/typings/src/scrapers/pdf/register-metadata.d.ts +2 -2
  65. package/esm/typings/src/scrapers/website/createWebsiteScraper.d.ts +1 -1
  66. package/esm/typings/src/scrapers/website/register-constructor.d.ts +1 -1
  67. package/esm/typings/src/scrapers/website/register-metadata.d.ts +2 -2
  68. package/esm/typings/src/types/typeAliases.d.ts +1 -1
  69. package/esm/typings/src/utils/files/listAllFiles.d.ts +1 -1
  70. package/esm/typings/src/version.d.ts +1 -1
  71. package/esm/typings/src/{wizzard → wizard}/$getCompiledBook.d.ts +2 -2
  72. package/esm/typings/src/{wizzard/wizzard.d.ts → wizard/wizard.d.ts} +6 -6
  73. package/package.json +2 -14
  74. package/umd/index.umd.js +300 -237
  75. package/umd/index.umd.js.map +1 -1
package/esm/index.es.js CHANGED
@@ -30,7 +30,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
30
30
  * @generated
31
31
  * @see https://github.com/webgptorg/promptbook
32
32
  */
33
- const PROMPTBOOK_ENGINE_VERSION = '0.94.0';
33
+ const PROMPTBOOK_ENGINE_VERSION = '0.98.0-10';
34
34
  /**
35
35
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
36
36
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -174,7 +174,7 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [🤹‍♂️]
174
174
  *
175
175
  * @public exported from `@promptbook/core`
176
176
  */
177
- const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [🤹‍♂️]
177
+ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [🤹‍♂️]
178
178
  // <- TODO: [🕝] Make also `BOOKS_DIRNAME_ALTERNATIVES`
179
179
  // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
180
180
  /**
@@ -196,11 +196,11 @@ const DEFAULT_SCRAPE_CACHE_DIRNAME = './.promptbook/scrape-cache';
196
196
  /*
197
197
  TODO: [🌃]
198
198
  /**
199
- * Id of application for the wizzard when using remote server
199
+ * Id of application for the wizard when using remote server
200
200
  *
201
201
  * @public exported from `@promptbook/core`
202
202
  * /
203
- ex-port const WIZZARD_APP_ID: string_app_id = 'wizzard';
203
+ ex-port const WIZARD_APP_ID: string_app_id = 'wizard';
204
204
  */
205
205
  /**
206
206
  * The name of the builded pipeline collection made by CLI `ptbk make` and for lookup in `createCollectionFromDirectory`
@@ -344,7 +344,7 @@ function jsonParse(value) {
344
344
  throw new Error(spaceTrim((block) => `
345
345
  ${block(error.message)}
346
346
 
347
- The JSON text:
347
+ The expected JSON text:
348
348
  ${block(value)}
349
349
  `));
350
350
  }
@@ -522,7 +522,7 @@ function checkSerializableAsJson(options) {
522
522
  else {
523
523
  for (const [subName, subValue] of Object.entries(value)) {
524
524
  if (subValue === undefined) {
525
- // Note: undefined in object is serializable - it is just omited
525
+ // Note: undefined in object is serializable - it is just omitted
526
526
  continue;
527
527
  }
528
528
  checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
@@ -1131,7 +1131,7 @@ function validatePipeline_InnerFunction(pipeline) {
1131
1131
  * @param fs Filesystem tools
1132
1132
  * @returns Pipelines loaded from the archive
1133
1133
  *
1134
- * @private utility of Prompbook
1134
+ * @private utility of Promptbook
1135
1135
  */
1136
1136
  async function loadArchive(filePath, fs) {
1137
1137
  if (!filePath.endsWith('.bookc')) {
@@ -1153,7 +1153,7 @@ async function loadArchive(filePath, fs) {
1153
1153
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
1154
1154
  */
1155
1155
 
1156
- var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",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`\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"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",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`\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"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with 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\nHere are the available models:\n\n```json\n{availableModels}\n```\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:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with 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\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\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`-> {modelsRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
1156
+ var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",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`\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"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",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`\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"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with 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\nHere are the available models:\n\n```json\n{availableModels}\n```\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:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with 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\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\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`-> {modelsRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
1157
1157
 
1158
1158
  /**
1159
1159
  * Checks if value is valid email
@@ -1295,7 +1295,7 @@ function prettifyMarkdown(content) {
1295
1295
  });
1296
1296
  }
1297
1297
  catch (error) {
1298
- // TODO: [🟥] Detect browser / node and make it colorfull
1298
+ // TODO: [🟥] Detect browser / node and make it colorful
1299
1299
  console.error('There was an error with prettifying the markdown, using the original as the fallback', {
1300
1300
  error,
1301
1301
  html: content,
@@ -1586,7 +1586,7 @@ class SimplePipelineCollection {
1586
1586
 
1587
1587
  Note: You have probably forgotten to run "ptbk make" to update the collection
1588
1588
  Note: Pipelines with the same URL are not allowed
1589
- Only exepction is when the pipelines are identical
1589
+ Only exception is when the pipelines are identical
1590
1590
 
1591
1591
  `));
1592
1592
  }
@@ -3075,13 +3075,13 @@ function joinLlmExecutionTools(...llmExecutionTools) {
3075
3075
 
3076
3076
  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.
3077
3077
  `);
3078
- // TODO: [🟥] Detect browser / node and make it colorfull
3078
+ // TODO: [🟥] Detect browser / node and make it colorful
3079
3079
  console.warn(warningMessage);
3080
3080
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
3081
3081
  /*
3082
3082
  return {
3083
3083
  async listModels() {
3084
- // TODO: [🟥] Detect browser / node and make it colorfull
3084
+ // TODO: [🟥] Detect browser / node and make it colorful
3085
3085
  console.warn(
3086
3086
  spaceTrim(
3087
3087
  (block) => `
@@ -3105,108 +3105,6 @@ function joinLlmExecutionTools(...llmExecutionTools) {
3105
3105
  * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
3106
3106
  */
3107
3107
 
3108
- /**
3109
- * Extracts all code blocks from markdown.
3110
- *
3111
- * Note: There are multiple similar functions:
3112
- * - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
3113
- * - `extractJsonBlock` extracts exactly one valid JSON code block
3114
- * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
3115
- * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
3116
- *
3117
- * @param markdown any valid markdown
3118
- * @returns code blocks with language and content
3119
- * @throws {ParseError} if block is not closed properly
3120
- * @public exported from `@promptbook/markdown-utils`
3121
- */
3122
- function extractAllBlocksFromMarkdown(markdown) {
3123
- const codeBlocks = [];
3124
- const lines = markdown.split('\n');
3125
- // Note: [0] Ensure that the last block notated by gt > will be closed
3126
- lines.push('');
3127
- let currentCodeBlock = null;
3128
- for (const line of lines) {
3129
- if (line.startsWith('> ') || line === '>') {
3130
- if (currentCodeBlock === null) {
3131
- currentCodeBlock = { blockNotation: '>', language: null, content: '' };
3132
- } /* not else */
3133
- if (currentCodeBlock.blockNotation === '>') {
3134
- if (currentCodeBlock.content !== '') {
3135
- currentCodeBlock.content += '\n';
3136
- }
3137
- currentCodeBlock.content += line.slice(2);
3138
- }
3139
- }
3140
- else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '>' /* <- Note: [0] */) {
3141
- codeBlocks.push(currentCodeBlock);
3142
- currentCodeBlock = null;
3143
- }
3144
- /* not else */
3145
- if (line.startsWith('```')) {
3146
- const language = line.slice(3).trim() || null;
3147
- if (currentCodeBlock === null) {
3148
- currentCodeBlock = { blockNotation: '```', language, content: '' };
3149
- }
3150
- else {
3151
- if (language !== null) {
3152
- throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed and already opening new ${language} code block`);
3153
- }
3154
- codeBlocks.push(currentCodeBlock);
3155
- currentCodeBlock = null;
3156
- }
3157
- }
3158
- else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '```') {
3159
- if (currentCodeBlock.content !== '') {
3160
- currentCodeBlock.content += '\n';
3161
- }
3162
- currentCodeBlock.content += line.split('\\`\\`\\`').join('```') /* <- TODO: Maybe make proper unescape */;
3163
- }
3164
- }
3165
- if (currentCodeBlock !== null) {
3166
- throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed at the end of the markdown`);
3167
- }
3168
- return codeBlocks;
3169
- }
3170
- /**
3171
- * TODO: Maybe name for `blockNotation` instead of '```' and '>'
3172
- */
3173
-
3174
- /**
3175
- * Extracts extracts exactly one valid JSON code block
3176
- *
3177
- * - When given string is a valid JSON as it is, it just returns it
3178
- * - When there is no JSON code block the function throws a `ParseError`
3179
- * - When there are multiple JSON code blocks the function throws a `ParseError`
3180
- *
3181
- * Note: It is not important if marked as ```json BUT if it is VALID JSON
3182
- * Note: There are multiple similar function:
3183
- * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
3184
- * - `extractJsonBlock` extracts exactly one valid JSON code block
3185
- * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
3186
- * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
3187
- *
3188
- * @public exported from `@promptbook/markdown-utils`
3189
- * @throws {ParseError} if there is no valid JSON block in the markdown
3190
- */
3191
- function extractJsonBlock(markdown) {
3192
- if (isValidJsonString(markdown)) {
3193
- return markdown;
3194
- }
3195
- const codeBlocks = extractAllBlocksFromMarkdown(markdown);
3196
- const jsonBlocks = codeBlocks.filter(({ content }) => isValidJsonString(content));
3197
- if (jsonBlocks.length === 0) {
3198
- throw new Error('There is no valid JSON block in the markdown');
3199
- }
3200
- if (jsonBlocks.length > 1) {
3201
- throw new Error('There are multiple JSON code blocks in the markdown');
3202
- }
3203
- return jsonBlocks[0].content;
3204
- }
3205
- /**
3206
- * TODO: Add some auto-healing logic + extract YAML, JSON5, TOML, etc.
3207
- * TODO: [🏢] Make this logic part of `JsonFormatParser` or `isValidJsonString`
3208
- */
3209
-
3210
3108
  /**
3211
3109
  * Takes an item or an array of items and returns an array of items
3212
3110
  *
@@ -3314,6 +3212,108 @@ function templateParameters(template, parameters) {
3314
3212
  return replacedTemplates;
3315
3213
  }
3316
3214
 
3215
+ /**
3216
+ * Extracts all code blocks from markdown.
3217
+ *
3218
+ * Note: There are multiple similar functions:
3219
+ * - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
3220
+ * - `extractJsonBlock` extracts exactly one valid JSON code block
3221
+ * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
3222
+ * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
3223
+ *
3224
+ * @param markdown any valid markdown
3225
+ * @returns code blocks with language and content
3226
+ * @throws {ParseError} if block is not closed properly
3227
+ * @public exported from `@promptbook/markdown-utils`
3228
+ */
3229
+ function extractAllBlocksFromMarkdown(markdown) {
3230
+ const codeBlocks = [];
3231
+ const lines = markdown.split('\n');
3232
+ // Note: [0] Ensure that the last block notated by gt > will be closed
3233
+ lines.push('');
3234
+ let currentCodeBlock = null;
3235
+ for (const line of lines) {
3236
+ if (line.startsWith('> ') || line === '>') {
3237
+ if (currentCodeBlock === null) {
3238
+ currentCodeBlock = { blockNotation: '>', language: null, content: '' };
3239
+ } /* not else */
3240
+ if (currentCodeBlock.blockNotation === '>') {
3241
+ if (currentCodeBlock.content !== '') {
3242
+ currentCodeBlock.content += '\n';
3243
+ }
3244
+ currentCodeBlock.content += line.slice(2);
3245
+ }
3246
+ }
3247
+ else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '>' /* <- Note: [0] */) {
3248
+ codeBlocks.push(currentCodeBlock);
3249
+ currentCodeBlock = null;
3250
+ }
3251
+ /* not else */
3252
+ if (line.startsWith('```')) {
3253
+ const language = line.slice(3).trim() || null;
3254
+ if (currentCodeBlock === null) {
3255
+ currentCodeBlock = { blockNotation: '```', language, content: '' };
3256
+ }
3257
+ else {
3258
+ if (language !== null) {
3259
+ throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed and already opening new ${language} code block`);
3260
+ }
3261
+ codeBlocks.push(currentCodeBlock);
3262
+ currentCodeBlock = null;
3263
+ }
3264
+ }
3265
+ else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '```') {
3266
+ if (currentCodeBlock.content !== '') {
3267
+ currentCodeBlock.content += '\n';
3268
+ }
3269
+ currentCodeBlock.content += line.split('\\`\\`\\`').join('```') /* <- TODO: Maybe make proper unescape */;
3270
+ }
3271
+ }
3272
+ if (currentCodeBlock !== null) {
3273
+ throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed at the end of the markdown`);
3274
+ }
3275
+ return codeBlocks;
3276
+ }
3277
+ /**
3278
+ * TODO: Maybe name for `blockNotation` instead of '```' and '>'
3279
+ */
3280
+
3281
+ /**
3282
+ * Extracts extracts exactly one valid JSON code block
3283
+ *
3284
+ * - When given string is a valid JSON as it is, it just returns it
3285
+ * - When there is no JSON code block the function throws a `ParseError`
3286
+ * - When there are multiple JSON code blocks the function throws a `ParseError`
3287
+ *
3288
+ * Note: It is not important if marked as ```json BUT if it is VALID JSON
3289
+ * Note: There are multiple similar function:
3290
+ * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
3291
+ * - `extractJsonBlock` extracts exactly one valid JSON code block
3292
+ * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
3293
+ * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
3294
+ *
3295
+ * @public exported from `@promptbook/markdown-utils`
3296
+ * @throws {ParseError} if there is no valid JSON block in the markdown
3297
+ */
3298
+ function extractJsonBlock(markdown) {
3299
+ if (isValidJsonString(markdown)) {
3300
+ return markdown;
3301
+ }
3302
+ const codeBlocks = extractAllBlocksFromMarkdown(markdown);
3303
+ const jsonBlocks = codeBlocks.filter(({ content }) => isValidJsonString(content));
3304
+ if (jsonBlocks.length === 0) {
3305
+ throw new Error('There is no valid JSON block in the markdown');
3306
+ }
3307
+ if (jsonBlocks.length > 1) {
3308
+ throw new Error('There are multiple JSON code blocks in the markdown');
3309
+ }
3310
+ return jsonBlocks[0].content;
3311
+ }
3312
+ /**
3313
+ * TODO: Add some auto-healing logic + extract YAML, JSON5, TOML, etc.
3314
+ * TODO: [🏢] Make this logic part of `JsonFormatParser` or `isValidJsonString`
3315
+ */
3316
+
3317
3317
  /**
3318
3318
  * Counts number of characters in the text
3319
3319
  *
@@ -3735,6 +3735,68 @@ function checkExpectations(expectations, value) {
3735
3735
  * Note: [💝] and [🤠] are interconnected together
3736
3736
  */
3737
3737
 
3738
+ /**
3739
+ * Validates a prompt result against expectations and format requirements.
3740
+ * This function provides a common abstraction for result validation that can be used
3741
+ * by both execution logic and caching logic to ensure consistency.
3742
+ *
3743
+ * @param options - The validation options including result string, expectations, and format
3744
+ * @returns Validation result with processed string and validity status
3745
+ * @private internal function of `createPipelineExecutor` and `cacheLlmTools`
3746
+ */
3747
+ function validatePromptResult(options) {
3748
+ const { resultString, expectations, format } = options;
3749
+ let processedResultString = resultString;
3750
+ let validationError;
3751
+ try {
3752
+ // TODO: [💝] Unite object for expecting amount and format
3753
+ if (format) {
3754
+ if (format === 'JSON') {
3755
+ if (!isValidJsonString(processedResultString)) {
3756
+ // TODO: [🏢] Do more universally via `FormatParser`
3757
+ try {
3758
+ processedResultString = extractJsonBlock(processedResultString);
3759
+ }
3760
+ catch (error) {
3761
+ keepUnused(error);
3762
+ throw new ExpectError(spaceTrim$1((block) => `
3763
+ Expected valid JSON string
3764
+
3765
+ The expected JSON text:
3766
+ ${block(processedResultString)}
3767
+ `));
3768
+ }
3769
+ }
3770
+ }
3771
+ else {
3772
+ throw new UnexpectedError(`Unknown format "${format}"`);
3773
+ }
3774
+ }
3775
+ // TODO: [💝] Unite object for expecting amount and format
3776
+ if (expectations) {
3777
+ checkExpectations(expectations, processedResultString);
3778
+ }
3779
+ return {
3780
+ isValid: true,
3781
+ processedResultString,
3782
+ };
3783
+ }
3784
+ catch (error) {
3785
+ if (error instanceof ExpectError) {
3786
+ validationError = error;
3787
+ }
3788
+ else {
3789
+ // Re-throw non-ExpectError errors (like UnexpectedError)
3790
+ throw error;
3791
+ }
3792
+ return {
3793
+ isValid: false,
3794
+ processedResultString,
3795
+ error: validationError,
3796
+ };
3797
+ }
3798
+ }
3799
+
3738
3800
  /**
3739
3801
  * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
3740
3802
  * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
@@ -3752,17 +3814,18 @@ async function executeAttempts(options) {
3752
3814
  $resultString: null,
3753
3815
  $expectError: null,
3754
3816
  $scriptPipelineExecutionErrors: [],
3817
+ $failedResults: [], // Track all failed attempts
3755
3818
  };
3756
3819
  // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
3757
3820
  const _llms = arrayableToArray(tools.llm);
3758
3821
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
3759
- attempts: for (let attempt = -jokerParameterNames.length; attempt < maxAttempts; attempt++) {
3760
- const isJokerAttempt = attempt < 0;
3761
- const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
3822
+ attempts: for (let attemptIndex = -jokerParameterNames.length; attemptIndex < maxAttempts; attemptIndex++) {
3823
+ const isJokerAttempt = attemptIndex < 0;
3824
+ const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attemptIndex];
3762
3825
  // TODO: [🧠][🍭] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
3763
3826
  if (isJokerAttempt && !jokerParameterName) {
3764
3827
  throw new UnexpectedError(spaceTrim$1((block) => `
3765
- Joker not found in attempt ${attempt}
3828
+ Joker not found in attempt ${attemptIndex}
3766
3829
 
3767
3830
  ${block(pipelineIdentification)}
3768
3831
  `));
@@ -3960,35 +4023,18 @@ async function executeAttempts(options) {
3960
4023
  }
3961
4024
  }
3962
4025
  // TODO: [💝] Unite object for expecting amount and format
3963
- if (task.format) {
3964
- if (task.format === 'JSON') {
3965
- if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
3966
- // TODO: [🏢] Do more universally via `FormatParser`
3967
- try {
3968
- $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
3969
- }
3970
- catch (error) {
3971
- keepUnused(error);
3972
- throw new ExpectError(spaceTrim$1((block) => `
3973
- Expected valid JSON string
3974
-
3975
- ${block(
3976
- /*<- Note: No need for `pipelineIdentification`, it will be catched and added later */ '')}
3977
- `));
3978
- }
3979
- }
3980
- }
3981
- else {
3982
- throw new UnexpectedError(spaceTrim$1((block) => `
3983
- Unknown format "${task.format}"
3984
-
3985
- ${block(pipelineIdentification)}
3986
- `));
4026
+ // Use the common validation function for both format and expectations
4027
+ if (task.format || task.expectations) {
4028
+ const validationResult = validatePromptResult({
4029
+ resultString: $ongoingTaskResult.$resultString || '',
4030
+ expectations: task.expectations,
4031
+ format: task.format,
4032
+ });
4033
+ if (!validationResult.isValid) {
4034
+ throw validationResult.error;
3987
4035
  }
3988
- }
3989
- // TODO: [💝] Unite object for expecting amount and format
3990
- if (task.expectations) {
3991
- checkExpectations(task.expectations, $ongoingTaskResult.$resultString || '');
4036
+ // Update the result string in case format processing modified it (e.g., JSON extraction)
4037
+ $ongoingTaskResult.$resultString = validationResult.processedResultString;
3992
4038
  }
3993
4039
  break attempts;
3994
4040
  }
@@ -3997,6 +4043,15 @@ async function executeAttempts(options) {
3997
4043
  throw error;
3998
4044
  }
3999
4045
  $ongoingTaskResult.$expectError = error;
4046
+ // Store each failed attempt
4047
+ if (!Array.isArray($ongoingTaskResult.$failedResults)) {
4048
+ $ongoingTaskResult.$failedResults = [];
4049
+ }
4050
+ $ongoingTaskResult.$failedResults.push({
4051
+ attemptIndex,
4052
+ result: $ongoingTaskResult.$resultString,
4053
+ error: error,
4054
+ });
4000
4055
  }
4001
4056
  finally {
4002
4057
  if (!isJokerAttempt &&
@@ -4018,35 +4073,41 @@ async function executeAttempts(options) {
4018
4073
  });
4019
4074
  }
4020
4075
  }
4021
- if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
4076
+ if ($ongoingTaskResult.$expectError !== null && attemptIndex === maxAttempts - 1) {
4077
+ // Note: Create a summary of all failures
4078
+ const failuresSummary = $ongoingTaskResult.$failedResults
4079
+ .map((failure) => spaceTrim$1((block) => {
4080
+ var _a, _b;
4081
+ return `
4082
+ Attempt ${failure.attemptIndex + 1}:
4083
+ Error ${((_a = failure.error) === null || _a === void 0 ? void 0 : _a.name) || ''}:
4084
+ ${block((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message.split('\n').map((line) => `> ${line}`).join('\n'))}
4085
+
4086
+ Result:
4087
+ ${block(failure.result === null
4088
+ ? 'null'
4089
+ : spaceTrim$1(failure.result)
4090
+ .split('\n')
4091
+ .map((line) => `> ${line}`)
4092
+ .join('\n'))}
4093
+ `;
4094
+ }))
4095
+ .join('\n\n---\n\n');
4022
4096
  throw new PipelineExecutionError(spaceTrim$1((block) => {
4023
- var _a, _b, _c;
4097
+ var _a;
4024
4098
  return `
4025
4099
  LLM execution failed ${maxExecutionAttempts}x
4026
4100
 
4027
4101
  ${block(pipelineIdentification)}
4028
4102
 
4029
- ---
4030
4103
  The Prompt:
4031
4104
  ${block((((_a = $ongoingTaskResult.$prompt) === null || _a === void 0 ? void 0 : _a.content) || '')
4032
4105
  .split('\n')
4033
4106
  .map((line) => `> ${line}`)
4034
4107
  .join('\n'))}
4035
4108
 
4036
- Last error ${((_b = $ongoingTaskResult.$expectError) === null || _b === void 0 ? void 0 : _b.name) || ''}:
4037
- ${block((((_c = $ongoingTaskResult.$expectError) === null || _c === void 0 ? void 0 : _c.message) || '')
4038
- .split('\n')
4039
- .map((line) => `> ${line}`)
4040
- .join('\n'))}
4041
-
4042
- Last result:
4043
- ${block($ongoingTaskResult.$resultString === null
4044
- ? 'null'
4045
- : spaceTrim$1($ongoingTaskResult.$resultString)
4046
- .split('\n')
4047
- .map((line) => `> ${line}`)
4048
- .join('\n'))}
4049
- ---
4109
+ All Failed Attempts:
4110
+ ${block(failuresSummary)}
4050
4111
  `;
4051
4112
  }));
4052
4113
  }
@@ -4266,10 +4327,10 @@ function knowledgePiecesToString(knowledgePieces) {
4266
4327
  */
4267
4328
  async function getKnowledgeForTask(options) {
4268
4329
  const { tools, preparedPipeline, task, parameters } = options;
4269
- const firstKnowlegePiece = preparedPipeline.knowledgePieces[0];
4270
- const firstKnowlegeIndex = firstKnowlegePiece === null || firstKnowlegePiece === void 0 ? void 0 : firstKnowlegePiece.index[0];
4330
+ const firstKnowledgePiece = preparedPipeline.knowledgePieces[0];
4331
+ const firstKnowledgeIndex = firstKnowledgePiece === null || firstKnowledgePiece === void 0 ? void 0 : firstKnowledgePiece.index[0];
4271
4332
  // <- TODO: Do not use just first knowledge piece and first index to determine embedding model, use also keyword search
4272
- if (firstKnowlegePiece === undefined || firstKnowlegeIndex === undefined) {
4333
+ if (firstKnowledgePiece === undefined || firstKnowledgeIndex === undefined) {
4273
4334
  return ''; // <- Note: Np knowledge present, return empty string
4274
4335
  }
4275
4336
  try {
@@ -4280,7 +4341,7 @@ async function getKnowledgeForTask(options) {
4280
4341
  title: 'Knowledge Search',
4281
4342
  modelRequirements: {
4282
4343
  modelVariant: 'EMBEDDING',
4283
- modelName: firstKnowlegeIndex.modelName,
4344
+ modelName: firstKnowledgeIndex.modelName,
4284
4345
  },
4285
4346
  content: task.content,
4286
4347
  parameters,
@@ -4288,7 +4349,7 @@ async function getKnowledgeForTask(options) {
4288
4349
  const taskEmbeddingResult = await llmTools.callEmbeddingModel(taskEmbeddingPrompt);
4289
4350
  const knowledgePiecesWithRelevance = preparedPipeline.knowledgePieces.map((knowledgePiece) => {
4290
4351
  const { index } = knowledgePiece;
4291
- const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowlegeIndex.modelName);
4352
+ const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowledgeIndex.modelName);
4292
4353
  // <- TODO: Do not use just first knowledge piece and first index to determine embedding model
4293
4354
  if (knowledgePieceIndex === undefined) {
4294
4355
  return {
@@ -4309,8 +4370,8 @@ async function getKnowledgeForTask(options) {
4309
4370
  task,
4310
4371
  taskEmbeddingPrompt,
4311
4372
  taskEmbeddingResult,
4312
- firstKnowlegePiece,
4313
- firstKnowlegeIndex,
4373
+ firstKnowledgePiece,
4374
+ firstKnowledgeIndex,
4314
4375
  knowledgePiecesWithRelevance,
4315
4376
  knowledgePiecesSorted,
4316
4377
  knowledgePiecesLimited,
@@ -4379,7 +4440,7 @@ async function getReservedParametersForTask(options) {
4379
4440
  * @private internal utility of `createPipelineExecutor`
4380
4441
  */
4381
4442
  async function executeTask(options) {
4382
- const { currentTask, preparedPipeline, parametersToPass, tools, onProgress, $executionReport, pipelineIdentification, maxExecutionAttempts, maxParallelCount, csvSettings, isVerbose, rootDirname, cacheDirname, intermediateFilesStrategy, isAutoInstalled, isNotPreparedWarningSupressed, } = options;
4443
+ const { currentTask, preparedPipeline, parametersToPass, tools, onProgress, $executionReport, pipelineIdentification, maxExecutionAttempts, maxParallelCount, csvSettings, isVerbose, rootDirname, cacheDirname, intermediateFilesStrategy, isAutoInstalled, isNotPreparedWarningSuppressed, } = options;
4383
4444
  const priority = preparedPipeline.tasks.length - preparedPipeline.tasks.indexOf(currentTask);
4384
4445
  // Note: Check consistency of used and dependent parameters which was also done in `validatePipeline`, but it’s good to doublecheck
4385
4446
  const usedParameterNames = extractParameterNamesFromTask(currentTask);
@@ -4467,7 +4528,7 @@ async function executeTask(options) {
4467
4528
  cacheDirname,
4468
4529
  intermediateFilesStrategy,
4469
4530
  isAutoInstalled,
4470
- isNotPreparedWarningSupressed,
4531
+ isNotPreparedWarningSuppressed,
4471
4532
  });
4472
4533
  await onProgress({
4473
4534
  outputParameters: {
@@ -4562,7 +4623,7 @@ async function executePipeline(options) {
4562
4623
  }
4563
4624
  return exportJson({
4564
4625
  name: `executionReport`,
4565
- message: `Unuccessful PipelineExecutorResult (with missing parameter {${parameter.name}}) PipelineExecutorResult`,
4626
+ message: `Unsuccessful PipelineExecutorResult (with missing parameter {${parameter.name}}) PipelineExecutorResult`,
4566
4627
  order: [],
4567
4628
  value: {
4568
4629
  isSuccessful: false,
@@ -4599,7 +4660,7 @@ async function executePipeline(options) {
4599
4660
  return exportJson({
4600
4661
  name: 'pipelineExecutorResult',
4601
4662
  message: spaceTrim$1((block) => `
4602
- Unuccessful PipelineExecutorResult (with extra parameter {${parameter.name}}) PipelineExecutorResult
4663
+ Unsuccessful PipelineExecutorResult (with extra parameter {${parameter.name}}) PipelineExecutorResult
4603
4664
 
4604
4665
  ${block(pipelineIdentification)}
4605
4666
  `),
@@ -4740,7 +4801,7 @@ async function executePipeline(options) {
4740
4801
  }
4741
4802
  return exportJson({
4742
4803
  name: 'pipelineExecutorResult',
4743
- message: `Unuccessful PipelineExecutorResult (with misc errors) PipelineExecutorResult`,
4804
+ message: `Unsuccessful PipelineExecutorResult (with misc errors) PipelineExecutorResult`,
4744
4805
  order: [],
4745
4806
  value: {
4746
4807
  isSuccessful: false,
@@ -4791,7 +4852,7 @@ async function executePipeline(options) {
4791
4852
  * @public exported from `@promptbook/core`
4792
4853
  */
4793
4854
  function createPipelineExecutor(options) {
4794
- const { pipeline, tools, maxExecutionAttempts = DEFAULT_MAX_EXECUTION_ATTEMPTS, maxParallelCount = DEFAULT_MAX_PARALLEL_COUNT, csvSettings = DEFAULT_CSV_SETTINGS, isVerbose = DEFAULT_IS_VERBOSE, isNotPreparedWarningSupressed = false, cacheDirname = DEFAULT_SCRAPE_CACHE_DIRNAME, intermediateFilesStrategy = DEFAULT_INTERMEDIATE_FILES_STRATEGY, isAutoInstalled = DEFAULT_IS_AUTO_INSTALLED, rootDirname = null, } = options;
4855
+ const { pipeline, tools, maxExecutionAttempts = DEFAULT_MAX_EXECUTION_ATTEMPTS, maxParallelCount = DEFAULT_MAX_PARALLEL_COUNT, csvSettings = DEFAULT_CSV_SETTINGS, isVerbose = DEFAULT_IS_VERBOSE, isNotPreparedWarningSuppressed = false, cacheDirname = DEFAULT_SCRAPE_CACHE_DIRNAME, intermediateFilesStrategy = DEFAULT_INTERMEDIATE_FILES_STRATEGY, isAutoInstalled = DEFAULT_IS_AUTO_INSTALLED, rootDirname = null, } = options;
4795
4856
  validatePipeline(pipeline);
4796
4857
  const pipelineIdentification = (() => {
4797
4858
  // Note: This is a 😐 implementation of [🚞]
@@ -4808,7 +4869,7 @@ function createPipelineExecutor(options) {
4808
4869
  if (isPipelinePrepared(pipeline)) {
4809
4870
  preparedPipeline = pipeline;
4810
4871
  }
4811
- else if (isNotPreparedWarningSupressed !== true) {
4872
+ else if (isNotPreparedWarningSuppressed !== true) {
4812
4873
  console.warn(spaceTrim$1((block) => `
4813
4874
  Pipeline is not prepared
4814
4875
 
@@ -4841,7 +4902,7 @@ function createPipelineExecutor(options) {
4841
4902
  maxParallelCount,
4842
4903
  csvSettings,
4843
4904
  isVerbose,
4844
- isNotPreparedWarningSupressed,
4905
+ isNotPreparedWarningSuppressed,
4845
4906
  rootDirname,
4846
4907
  cacheDirname,
4847
4908
  intermediateFilesStrategy,
@@ -4850,7 +4911,7 @@ function createPipelineExecutor(options) {
4850
4911
  assertsError(error);
4851
4912
  return exportJson({
4852
4913
  name: 'pipelineExecutorResult',
4853
- message: `Unuccessful PipelineExecutorResult, last catch`,
4914
+ message: `Unsuccessful PipelineExecutorResult, last catch`,
4854
4915
  order: [],
4855
4916
  value: {
4856
4917
  isSuccessful: false,
@@ -4920,12 +4981,12 @@ function countUsage(llmTools) {
4920
4981
  get title() {
4921
4982
  return `${llmTools.title} (+usage)`;
4922
4983
  // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
4923
- // <- TODO: [🧈][🧠] Does it make sence to suffix "(+usage)"?
4984
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
4924
4985
  },
4925
4986
  get description() {
4926
4987
  return `${llmTools.description} (+usage)`;
4927
4988
  // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
4928
- // <- TODO: [🧈][🧠] Does it make sence to suffix "(+usage)"?
4989
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
4929
4990
  },
4930
4991
  checkConfiguration() {
4931
4992
  return /* not await */ llmTools.checkConfiguration();
@@ -5212,17 +5273,17 @@ function $registeredScrapersMessage(availableScrapers) {
5212
5273
  * Mixes registered scrapers from $scrapersMetadataRegister and $scrapersRegister
5213
5274
  */
5214
5275
  const all = [];
5215
- for (const { packageName, className, mimeTypes, documentationUrl, isAvilableInBrowser, } of $scrapersMetadataRegister.list()) {
5276
+ for (const { packageName, className, mimeTypes, documentationUrl, isAvailableInBrowser, } of $scrapersMetadataRegister.list()) {
5216
5277
  if (all.some((item) => item.packageName === packageName && item.className === className)) {
5217
5278
  continue;
5218
5279
  }
5219
- all.push({ packageName, className, mimeTypes, documentationUrl, isAvilableInBrowser });
5280
+ all.push({ packageName, className, mimeTypes, documentationUrl, isAvailableInBrowser });
5220
5281
  }
5221
- for (const { packageName, className, mimeTypes, documentationUrl, isAvilableInBrowser, } of $scrapersRegister.list()) {
5282
+ for (const { packageName, className, mimeTypes, documentationUrl, isAvailableInBrowser, } of $scrapersRegister.list()) {
5222
5283
  if (all.some((item) => item.packageName === packageName && item.className === className)) {
5223
5284
  continue;
5224
5285
  }
5225
- all.push({ packageName, className, mimeTypes, documentationUrl, isAvilableInBrowser });
5286
+ all.push({ packageName, className, mimeTypes, documentationUrl, isAvailableInBrowser });
5226
5287
  }
5227
5288
  for (const { metadata } of availableScrapers) {
5228
5289
  all.push(metadata);
@@ -5234,8 +5295,8 @@ function $registeredScrapersMessage(availableScrapers) {
5234
5295
  const isInstalled = $scrapersRegister
5235
5296
  .list()
5236
5297
  .find(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
5237
- const isAvilableInTools = availableScrapers.some(({ metadata: { packageName, className } }) => metadata.packageName === packageName && metadata.className === className);
5238
- return { ...metadata, isMetadataAviailable, isInstalled, isAvilableInTools };
5298
+ const isAvailableInTools = availableScrapers.some(({ metadata: { packageName, className } }) => metadata.packageName === packageName && metadata.className === className);
5299
+ return { ...metadata, isMetadataAviailable, isInstalled, isAvailableInTools };
5239
5300
  });
5240
5301
  if (metadata.length === 0) {
5241
5302
  return spaceTrim(`
@@ -5248,7 +5309,7 @@ function $registeredScrapersMessage(availableScrapers) {
5248
5309
  return spaceTrim((block) => `
5249
5310
  Available scrapers are:
5250
5311
  ${block(metadata
5251
- .map(({ packageName, className, isMetadataAviailable, isInstalled, mimeTypes, isAvilableInBrowser, isAvilableInTools, }, i) => {
5312
+ .map(({ packageName, className, isMetadataAviailable, isInstalled, mimeTypes, isAvailableInBrowser, isAvailableInTools, }, i) => {
5252
5313
  const more = [];
5253
5314
  // TODO: [🧠] Maybe use `documentationUrl`
5254
5315
  if (isMetadataAviailable) {
@@ -5257,16 +5318,16 @@ function $registeredScrapersMessage(availableScrapers) {
5257
5318
  if (isInstalled) {
5258
5319
  more.push(`🟩 Installed`);
5259
5320
  } // not else
5260
- if (isAvilableInTools) {
5321
+ if (isAvailableInTools) {
5261
5322
  more.push(`🟦 Available in tools`);
5262
5323
  } // not else
5263
5324
  if (!isMetadataAviailable && isInstalled) {
5264
5325
  more.push(`When no metadata registered but scraper is installed, it is an unexpected behavior`);
5265
5326
  } // not else
5266
- if (!isInstalled && isAvilableInTools) {
5327
+ if (!isInstalled && isAvailableInTools) {
5267
5328
  more.push(`When the scraper is not installed but available in tools, it is an unexpected compatibility behavior`);
5268
5329
  } // not else
5269
- if (!isAvilableInBrowser) {
5330
+ if (!isAvailableInBrowser) {
5270
5331
  more.push(`Not usable in browser`);
5271
5332
  }
5272
5333
  const moreText = more.length === 0 ? '' : ` *(${more.join('; ')})*`;
@@ -5732,7 +5793,7 @@ TODO: [🧊] This is how it can look in future
5732
5793
  /**
5733
5794
  * TODO: [🧊] In future one preparation can take data from previous preparation and save tokens and time
5734
5795
  * Put `knowledgePieces` into `PrepareKnowledgeOptions`
5735
- * TODO: [🪂] More than max things can run in parallel by acident [1,[2a,2b,_],[3a,3b,_]]
5796
+ * TODO: [🪂] More than max things can run in parallel by accident [1,[2a,2b,_],[3a,3b,_]]
5736
5797
  * TODO: [🧠][❎] Do here proper M:N mapping
5737
5798
  * [x] One source can make multiple pieces
5738
5799
  * [ ] One piece can have multiple sources
@@ -6620,7 +6681,7 @@ const expectCommandParser = {
6620
6681
  $taskJson.expectations[unit] = $taskJson.expectations[unit] || {};
6621
6682
  if (command.sign === 'MINIMUM' || command.sign === 'EXACTLY') {
6622
6683
  if ($taskJson.expectations[unit].min !== undefined) {
6623
- throw new ParseError(`Already defined minumum ${$taskJson.expectations[unit].min} ${command.unit.toLowerCase()}, now trying to redefine it to ${command.amount}`);
6684
+ throw new ParseError(`Already defined minimum ${$taskJson.expectations[unit].min} ${command.unit.toLowerCase()}, now trying to redefine it to ${command.amount}`);
6624
6685
  }
6625
6686
  $taskJson.expectations[unit].min = command.amount;
6626
6687
  } /* not else */
@@ -10117,6 +10178,46 @@ async function $provideLlmToolsConfigurationFromEnv() {
10117
10178
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
10118
10179
  */
10119
10180
 
10181
+ /**
10182
+ * Detects if the code is running in a browser environment in main thread (Not in a web worker)
10183
+ *
10184
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10185
+ *
10186
+ * @public exported from `@promptbook/utils`
10187
+ */
10188
+ const $isRunningInBrowser = new Function(`
10189
+ try {
10190
+ return this === window;
10191
+ } catch (e) {
10192
+ return false;
10193
+ }
10194
+ `);
10195
+ /**
10196
+ * TODO: [🎺]
10197
+ */
10198
+
10199
+ /**
10200
+ * Detects if the code is running in a web worker
10201
+ *
10202
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10203
+ *
10204
+ * @public exported from `@promptbook/utils`
10205
+ */
10206
+ const $isRunningInWebWorker = new Function(`
10207
+ try {
10208
+ if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
10209
+ return true;
10210
+ } else {
10211
+ return false;
10212
+ }
10213
+ } catch (e) {
10214
+ return false;
10215
+ }
10216
+ `);
10217
+ /**
10218
+ * TODO: [🎺]
10219
+ */
10220
+
10120
10221
  /**
10121
10222
  * Creates LLM execution tools from provided configuration objects
10122
10223
  *
@@ -10137,8 +10238,10 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
10137
10238
  .list()
10138
10239
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
10139
10240
  if (registeredItem === undefined) {
10241
+ console.log('!!! $llmToolsRegister.list()', $llmToolsRegister.list());
10140
10242
  throw new Error(spaceTrim((block) => `
10141
10243
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
10244
+ Running in ${!$isRunningInBrowser() ? '' : 'browser environment'}${!$isRunningInNode() ? '' : 'node environment'}${!$isRunningInWebWorker() ? '' : 'worker environment'}
10142
10245
 
10143
10246
  You have probably forgotten install and import the provider package.
10144
10247
  To fix this issue, you can:
@@ -10265,24 +10368,6 @@ async function $provideScrapersForNode(tools, options) {
10265
10368
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
10266
10369
  */
10267
10370
 
10268
- /**
10269
- * Detects if the code is running in a browser environment in main thread (Not in a web worker)
10270
- *
10271
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10272
- *
10273
- * @public exported from `@promptbook/utils`
10274
- */
10275
- new Function(`
10276
- try {
10277
- return this === window;
10278
- } catch (e) {
10279
- return false;
10280
- }
10281
- `);
10282
- /**
10283
- * TODO: [🎺]
10284
- */
10285
-
10286
10371
  /**
10287
10372
  * Detects if the code is running in jest environment
10288
10373
  *
@@ -10301,28 +10386,6 @@ new Function(`
10301
10386
  * TODO: [🎺]
10302
10387
  */
10303
10388
 
10304
- /**
10305
- * Detects if the code is running in a web worker
10306
- *
10307
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
10308
- *
10309
- * @public exported from `@promptbook/utils`
10310
- */
10311
- new Function(`
10312
- try {
10313
- if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
10314
- return true;
10315
- } else {
10316
- return false;
10317
- }
10318
- } catch (e) {
10319
- return false;
10320
- }
10321
- `);
10322
- /**
10323
- * TODO: [🎺]
10324
- */
10325
-
10326
10389
  /**
10327
10390
  * Makes first letter of a string uppercase
10328
10391
  *
@@ -10873,7 +10936,7 @@ async function listAllFiles(path, isRecursive, fs) {
10873
10936
  return fileNames;
10874
10937
  }
10875
10938
  /**
10876
- * TODO: [😶] Unite floder listing
10939
+ * TODO: [😶] Unite folder listing
10877
10940
  * Note: Not [~🟢~] because it is not directly dependent on `fs
10878
10941
  * TODO: [🖇] What about symlinks?
10879
10942
  */
@@ -11020,7 +11083,7 @@ async function createCollectionFromDirectory(rootPath, tools, options) {
11020
11083
  if (isCrashedOnError) {
11021
11084
  throw new CollectionError(wrappedErrorMessage);
11022
11085
  }
11023
- // TODO: [🟥] Detect browser / node and make it colorfull
11086
+ // TODO: [🟥] Detect browser / node and make it colorful
11024
11087
  console.error(wrappedErrorMessage);
11025
11088
  }
11026
11089
  }
@@ -11087,7 +11150,7 @@ async function createCollectionFromDirectory(rootPath, tools, options) {
11087
11150
 
11088
11151
  Note: You have probably forgotten to run "ptbk make" to update the collection
11089
11152
  Note: Pipelines with the same URL are not allowed
11090
- Only exepction is when the pipelines are identical
11153
+ Only exception is when the pipelines are identical
11091
11154
 
11092
11155
  `));
11093
11156
  }
@@ -11111,7 +11174,7 @@ async function createCollectionFromDirectory(rootPath, tools, options) {
11111
11174
  if (isCrashedOnError) {
11112
11175
  throw new CollectionError(wrappedErrorMessage);
11113
11176
  }
11114
- // TODO: [🟥] Detect browser / node and make it colorfull
11177
+ // TODO: [🟥] Detect browser / node and make it colorful
11115
11178
  console.error(wrappedErrorMessage);
11116
11179
  }
11117
11180
  }