@promptbook/cli 0.86.6 → 0.86.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.
package/umd/index.umd.js CHANGED
@@ -54,7 +54,7 @@
54
54
  * @generated
55
55
  * @see https://github.com/webgptorg/promptbook
56
56
  */
57
- var PROMPTBOOK_ENGINE_VERSION = '0.86.6';
57
+ var PROMPTBOOK_ENGINE_VERSION = '0.86.10';
58
58
  /**
59
59
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
60
60
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1516,9 +1516,11 @@
1516
1516
  FileCacheStorage.prototype.getFilenameForKey = function (key) {
1517
1517
  // TODO: [👬] DRY
1518
1518
  var name = titleToName(key);
1519
+ var nameStart = name.split('-', 2)[0] || 'unnamed';
1519
1520
  var hash = sha256__default["default"](hexEncoder__default["default"].parse(name)).toString( /* hex */);
1520
1521
  // <- TODO: [🥬] Encapsulate sha256 to some private utility function
1521
- return path.join.apply(void 0, __spreadArray(__spreadArray([this.options.rootFolderPath], __read(nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */)), false), ["".concat(name.substring(0, MAX_FILENAME_LENGTH), ".json")], false));
1522
+ return path.join.apply(void 0, __spreadArray(__spreadArray([this.options.rootFolderPath,
1523
+ nameStart], __read(nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */)), false), ["".concat(name.substring(0, MAX_FILENAME_LENGTH), ".json")], false));
1522
1524
  };
1523
1525
  /**
1524
1526
  * @@@ Returns the current value associated with the given key, or null if the given key does not exist in the list associated with the object.
@@ -1704,13 +1706,14 @@
1704
1706
  return /* not await */ llmTools.listModels();
1705
1707
  } });
1706
1708
  var callCommonModel = function (prompt) { return __awaiter(_this, void 0, void 0, function () {
1707
- var key, cacheItem, _a, promptResult, _b;
1709
+ var parameters, content, modelRequirements, key, cacheItem, _a, promptResult, _b;
1708
1710
  return __generator(this, function (_c) {
1709
1711
  switch (_c.label) {
1710
1712
  case 0:
1713
+ parameters = prompt.parameters, content = prompt.content, modelRequirements = prompt.modelRequirements;
1711
1714
  key = titleToName(prompt.title.substring(0, MAX_FILENAME_LENGTH - 10) +
1712
1715
  '-' +
1713
- sha256__default["default"](hexEncoder__default["default"].parse(JSON.stringify(prompt.parameters))).toString( /* hex */));
1716
+ sha256__default["default"](hexEncoder__default["default"].parse(JSON.stringify({ parameters: parameters, content: content, modelRequirements: modelRequirements }))).toString( /* hex */));
1714
1717
  if (!!isCacheReloaded) return [3 /*break*/, 2];
1715
1718
  return [4 /*yield*/, storage.getItem(key)];
1716
1719
  case 1:
@@ -3680,23 +3683,6 @@
3680
3683
  * TODO: [🧠] Maybe clear `sourceFile` or clear when exposing through API or remote server
3681
3684
  */
3682
3685
 
3683
- 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:"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`\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"},{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"}];
3684
-
3685
- /**
3686
- * Checks if value is valid email
3687
- *
3688
- * @public exported from `@promptbook/utils`
3689
- */
3690
- function isValidEmail(email) {
3691
- if (typeof email !== 'string') {
3692
- return false;
3693
- }
3694
- if (email.split('\n').length > 1) {
3695
- return false;
3696
- }
3697
- return /^.+@.+\..+$/.test(email);
3698
- }
3699
-
3700
3686
  /**
3701
3687
  * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
3702
3688
  *
@@ -3717,677 +3703,749 @@
3717
3703
  */
3718
3704
 
3719
3705
  /**
3720
- * Function isValidJsonString will tell you if the string is valid JSON or not
3706
+ * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
3721
3707
  *
3722
- * @public exported from `@promptbook/utils`
3708
+ * @public exported from `@promptbook/core`
3723
3709
  */
3724
- function isValidJsonString(value /* <- [👨‍⚖️] */) {
3725
- try {
3726
- JSON.parse(value);
3727
- return true;
3728
- }
3729
- catch (error) {
3730
- if (!(error instanceof Error)) {
3731
- throw error;
3732
- }
3733
- if (error.message.includes('Unexpected token')) {
3734
- return false;
3735
- }
3736
- return false;
3710
+ var PipelineLogicError = /** @class */ (function (_super) {
3711
+ __extends(PipelineLogicError, _super);
3712
+ function PipelineLogicError(message) {
3713
+ var _this = _super.call(this, message) || this;
3714
+ _this.name = 'PipelineLogicError';
3715
+ Object.setPrototypeOf(_this, PipelineLogicError.prototype);
3716
+ return _this;
3737
3717
  }
3738
- }
3718
+ return PipelineLogicError;
3719
+ }(Error));
3739
3720
 
3740
3721
  /**
3741
- * Function `validatePipelineString` will validate the if the string is a valid pipeline string
3742
- * 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.
3722
+ * Tests if given string is valid semantic version
3743
3723
  *
3744
- * @param {string} pipelineString the candidate for a pipeline string
3745
- * @returns {PipelineString} the same string as input, but validated as valid
3746
- * @throws {ParseError} if the string is not a valid pipeline string
3747
- * @public exported from `@promptbook/core`
3724
+ * Note: There are two simmilar functions:
3725
+ * - `isValidSemanticVersion` which tests any semantic version
3726
+ * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
3727
+ *
3728
+ * @public exported from `@promptbook/utils`
3748
3729
  */
3749
- function validatePipelineString(pipelineString) {
3750
- if (isValidJsonString(pipelineString)) {
3751
- throw new ParseError('Expected a book, but got a JSON string');
3752
- }
3753
- else if (isValidUrl(pipelineString)) {
3754
- throw new ParseError("Expected a book, but got just the URL \"".concat(pipelineString, "\""));
3755
- }
3756
- else if (isValidFilePath(pipelineString)) {
3757
- throw new ParseError("Expected a book, but got just the file path \"".concat(pipelineString, "\""));
3730
+ function isValidSemanticVersion(version) {
3731
+ if (typeof version !== 'string') {
3732
+ return false;
3758
3733
  }
3759
- else if (isValidEmail(pipelineString)) {
3760
- throw new ParseError("Expected a book, but got just the email \"".concat(pipelineString, "\""));
3734
+ if (version.startsWith('0.0.0')) {
3735
+ return false;
3761
3736
  }
3762
- // <- TODO: Implement the validation + add tests when the pipeline logic considered as invalid
3763
- return pipelineString;
3737
+ return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
3764
3738
  }
3765
- /**
3766
- * TODO: [🧠][🈴] Where is the best location for this file
3767
- */
3768
3739
 
3769
3740
  /**
3770
- * Prettify the html code
3741
+ * Tests if given string is valid promptbook version
3742
+ * It looks into list of known promptbook versions.
3771
3743
  *
3772
- * @param content raw html code
3773
- * @returns formatted html code
3774
- * @private withing the package because of HUGE size of prettier dependency
3744
+ * @see https://www.npmjs.com/package/promptbook?activeTab=versions
3745
+ * 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.
3746
+ * Note: There are two simmilar functions:
3747
+ * - `isValidSemanticVersion` which tests any semantic version
3748
+ * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
3749
+ *
3750
+ * @public exported from `@promptbook/utils`
3775
3751
  */
3776
- function prettifyMarkdown(content) {
3777
- try {
3778
- return prettier.format(content, {
3779
- parser: 'markdown',
3780
- plugins: [parserHtml__default["default"]],
3781
- // TODO: DRY - make some import or auto-copy of .prettierrc
3782
- endOfLine: 'lf',
3783
- tabWidth: 4,
3784
- singleQuote: true,
3785
- trailingComma: 'all',
3786
- arrowParens: 'always',
3787
- printWidth: 120,
3788
- htmlWhitespaceSensitivity: 'ignore',
3789
- jsxBracketSameLine: false,
3790
- bracketSpacing: true,
3791
- });
3752
+ function isValidPromptbookVersion(version) {
3753
+ if (!isValidSemanticVersion(version)) {
3754
+ return false;
3792
3755
  }
3793
- catch (error) {
3794
- // TODO: [🟥] Detect browser / node and make it colorfull
3795
- console.error('There was an error with prettifying the markdown, using the original as the fallback', {
3796
- error: error,
3797
- html: content,
3798
- });
3799
- return content;
3756
+ if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
3757
+ return false;
3800
3758
  }
3759
+ // <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
3760
+ return true;
3801
3761
  }
3802
3762
 
3803
3763
  /**
3804
- * Makes first letter of a string uppercase
3764
+ * Tests if given string is valid pipeline URL URL.
3765
+ *
3766
+ * Note: There are two simmilar functions:
3767
+ * - `isValidUrl` which tests any URL
3768
+ * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
3805
3769
  *
3806
3770
  * @public exported from `@promptbook/utils`
3807
3771
  */
3808
- function capitalize(word) {
3809
- return word.substring(0, 1).toUpperCase() + word.substring(1);
3772
+ function isValidPipelineUrl(url) {
3773
+ if (!isValidUrl(url)) {
3774
+ return false;
3775
+ }
3776
+ if (!url.startsWith('https://') && !url.startsWith('http://') /* <- Note: [👣] */) {
3777
+ return false;
3778
+ }
3779
+ if (url.includes('#')) {
3780
+ // TODO: [🐠]
3781
+ return false;
3782
+ }
3783
+ /*
3784
+ Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
3785
+ if (isUrlOnPrivateNetwork(url)) {
3786
+ return false;
3787
+ }
3788
+ */
3789
+ return true;
3810
3790
  }
3791
+ /**
3792
+ * TODO: [🐠] Maybe more info why the URL is invalid
3793
+ */
3811
3794
 
3812
3795
  /**
3813
- * Converts promptbook in JSON format to string format
3796
+ * Validates PipelineJson if it is logically valid
3814
3797
  *
3815
- * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
3816
- * @param pipelineJson Promptbook in JSON format (.bookc)
3817
- * @returns Promptbook in string format (.book.md)
3798
+ * It checks:
3799
+ * - if it has correct parameters dependency
3800
+ *
3801
+ * It does NOT check:
3802
+ * - if it is valid json
3803
+ * - if it is meaningful
3804
+ *
3805
+ * @param pipeline valid or invalid PipelineJson
3806
+ * @returns the same pipeline if it is logically valid
3807
+ * @throws {PipelineLogicError} on logical error in the pipeline
3818
3808
  * @public exported from `@promptbook/core`
3819
3809
  */
3820
- function pipelineJsonToString(pipelineJson) {
3821
- var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
3822
- var title = pipelineJson.title, pipelineUrl = pipelineJson.pipelineUrl, bookVersion = pipelineJson.bookVersion, description = pipelineJson.description, parameters = pipelineJson.parameters, tasks = pipelineJson.tasks;
3823
- var pipelineString = "# ".concat(title);
3824
- if (description) {
3825
- pipelineString += '\n\n';
3826
- pipelineString += description;
3827
- }
3828
- var commands = [];
3829
- if (pipelineUrl) {
3830
- commands.push("PIPELINE URL ".concat(pipelineUrl));
3831
- }
3832
- if (bookVersion !== "undefined") {
3833
- commands.push("BOOK VERSION ".concat(bookVersion));
3834
- }
3835
- // TODO: [main] !!5 This increases size of the bundle and is probbably not necessary
3836
- pipelineString = prettifyMarkdown(pipelineString);
3837
- try {
3838
- for (var _g = __values(parameters.filter(function (_a) {
3839
- var isInput = _a.isInput;
3840
- return isInput;
3841
- })), _h = _g.next(); !_h.done; _h = _g.next()) {
3842
- var parameter = _h.value;
3843
- commands.push("INPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
3844
- }
3810
+ function validatePipeline(pipeline) {
3811
+ if (IS_PIPELINE_LOGIC_VALIDATED) {
3812
+ validatePipeline_InnerFunction(pipeline);
3845
3813
  }
3846
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
3847
- finally {
3814
+ else {
3848
3815
  try {
3849
- if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
3816
+ validatePipeline_InnerFunction(pipeline);
3850
3817
  }
3851
- finally { if (e_1) throw e_1.error; }
3852
- }
3853
- try {
3854
- for (var _j = __values(parameters.filter(function (_a) {
3855
- var isOutput = _a.isOutput;
3856
- return isOutput;
3857
- })), _k = _j.next(); !_k.done; _k = _j.next()) {
3858
- var parameter = _k.value;
3859
- commands.push("OUTPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
3818
+ catch (error) {
3819
+ if (!(error instanceof PipelineLogicError)) {
3820
+ throw error;
3821
+ }
3822
+ console.error(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is not valid but logic errors are temporarily disabled via `IS_PIPELINE_LOGIC_VALIDATED`\n\n ".concat(block(error.message), "\n "); }));
3860
3823
  }
3861
3824
  }
3862
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
3863
- finally {
3864
- try {
3865
- if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
3825
+ return pipeline;
3826
+ }
3827
+ /**
3828
+ * @private internal function for `validatePipeline`
3829
+ */
3830
+ function validatePipeline_InnerFunction(pipeline) {
3831
+ // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
3832
+ var e_1, _a, e_2, _b, e_3, _c;
3833
+ var pipelineIdentification = (function () {
3834
+ // Note: This is a 😐 implementation of [🚞]
3835
+ var _ = [];
3836
+ if (pipeline.sourceFile !== undefined) {
3837
+ _.push("File: ".concat(pipeline.sourceFile));
3866
3838
  }
3867
- finally { if (e_2) throw e_2.error; }
3839
+ if (pipeline.pipelineUrl !== undefined) {
3840
+ _.push("Url: ".concat(pipeline.pipelineUrl));
3841
+ }
3842
+ return _.join('\n');
3843
+ })();
3844
+ if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
3845
+ // <- Note: [🚲]
3846
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid promptbook URL \"".concat(pipeline.pipelineUrl, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
3868
3847
  }
3869
- pipelineString += '\n\n';
3870
- pipelineString += commands.map(function (command) { return "- ".concat(command); }).join('\n');
3871
- try {
3872
- for (var tasks_1 = __values(tasks), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) {
3873
- var task = tasks_1_1.value;
3874
- var
3875
- /* Note: Not using:> name, */
3876
- title_1 = task.title, description_1 = task.description,
3877
- /* Note: dependentParameterNames, */
3878
- jokers = task.jokerParameterNames, taskType = task.taskType, content = task.content, postprocessing = task.postprocessingFunctionNames, expectations = task.expectations, format = task.format, resultingParameterName = task.resultingParameterName;
3879
- pipelineString += '\n\n';
3880
- pipelineString += "## ".concat(title_1);
3881
- if (description_1) {
3882
- pipelineString += '\n\n';
3883
- pipelineString += description_1;
3884
- }
3885
- var commands_1 = [];
3886
- var contentLanguage = 'text';
3887
- if (taskType === 'PROMPT_TASK') {
3888
- var modelRequirements = task.modelRequirements;
3889
- var _l = modelRequirements || {}, modelName = _l.modelName, modelVariant = _l.modelVariant;
3890
- // Note: Do nothing, it is default
3891
- // commands.push(`PROMPT`);
3892
- if (modelVariant) {
3893
- commands_1.push("MODEL VARIANT ".concat(capitalize(modelVariant)));
3894
- }
3895
- if (modelName) {
3896
- commands_1.push("MODEL NAME `".concat(modelName, "`"));
3897
- }
3898
- }
3899
- else if (taskType === 'SIMPLE_TASK') {
3900
- commands_1.push("SIMPLE TEMPLATE");
3901
- // Note: Nothing special here
3848
+ if (pipeline.bookVersion !== undefined && !isValidPromptbookVersion(pipeline.bookVersion)) {
3849
+ // <- Note: [🚲]
3850
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid Promptbook Version \"".concat(pipeline.bookVersion, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
3851
+ }
3852
+ // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
3853
+ if (!Array.isArray(pipeline.parameters)) {
3854
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
3855
+ throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.parameters` expected to be an array, but got ".concat(typeof pipeline.parameters, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
3856
+ }
3857
+ // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
3858
+ if (!Array.isArray(pipeline.tasks)) {
3859
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
3860
+ throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.tasks` expected to be an array, but got ".concat(typeof pipeline.tasks, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
3861
+ }
3862
+ var _loop_1 = function (parameter) {
3863
+ if (parameter.isInput && parameter.isOutput) {
3864
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n\n Parameter `{".concat(parameter.name, "}` can not be both input and output\n\n ").concat(block(pipelineIdentification), "\n "); }));
3865
+ }
3866
+ // Note: Testing that parameter is either intermediate or output BUT not created and unused
3867
+ if (!parameter.isInput &&
3868
+ !parameter.isOutput &&
3869
+ !pipeline.tasks.some(function (task) { return task.dependentParameterNames.includes(parameter.name); })) {
3870
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(parameter.name, "}` is created but not used\n\n You can declare {").concat(parameter.name, "} as output parameter by adding in the header:\n - OUTPUT PARAMETER `{").concat(parameter.name, "}` ").concat(parameter.description || '', "\n\n ").concat(block(pipelineIdentification), "\n\n "); }));
3871
+ }
3872
+ // Note: Testing that parameter is either input or result of some task
3873
+ if (!parameter.isInput && !pipeline.tasks.some(function (task) { return task.resultingParameterName === parameter.name; })) {
3874
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(parameter.name, "}` is declared but not defined\n\n You can do one of these:\n 1) Remove declaration of `{").concat(parameter.name, "}`\n 2) Add task that results in `-> {").concat(parameter.name, "}`\n\n ").concat(block(pipelineIdentification), "\n "); }));
3875
+ }
3876
+ };
3877
+ try {
3878
+ /*
3879
+ TODO: [🧠][🅾] Should be empty pipeline valid or not
3880
+ // Note: Check that pipeline has some tasks
3881
+ if (pipeline.tasks.length === 0) {
3882
+ throw new PipelineLogicError(
3883
+ spaceTrim(
3884
+ (block) => `
3885
+ Pipeline must have at least one task
3886
+
3887
+ ${block(pipelineIdentification)}
3888
+ `,
3889
+ ),
3890
+ );
3891
+ }
3892
+ */
3893
+ // Note: Check each parameter individually
3894
+ for (var _d = __values(pipeline.parameters), _e = _d.next(); !_e.done; _e = _d.next()) {
3895
+ var parameter = _e.value;
3896
+ _loop_1(parameter);
3897
+ }
3898
+ }
3899
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
3900
+ finally {
3901
+ try {
3902
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
3903
+ }
3904
+ finally { if (e_1) throw e_1.error; }
3905
+ }
3906
+ // Note: All input parameters are defined - so that they can be used as result of some task
3907
+ var definedParameters = new Set(pipeline.parameters.filter(function (_a) {
3908
+ var isInput = _a.isInput;
3909
+ return isInput;
3910
+ }).map(function (_a) {
3911
+ var name = _a.name;
3912
+ return name;
3913
+ }));
3914
+ var _loop_2 = function (task) {
3915
+ var e_4, _h, e_5, _j;
3916
+ if (definedParameters.has(task.resultingParameterName)) {
3917
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(task.resultingParameterName, "}` is defined multiple times\n\n ").concat(block(pipelineIdentification), "\n "); }));
3918
+ }
3919
+ if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
3920
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter name {".concat(task.resultingParameterName, "} is reserved, please use different name\n\n ").concat(block(pipelineIdentification), "\n "); }));
3921
+ }
3922
+ definedParameters.add(task.resultingParameterName);
3923
+ if (task.jokerParameterNames && task.jokerParameterNames.length > 0) {
3924
+ if (!task.format &&
3925
+ !task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
3926
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Joker parameters are used for {".concat(task.resultingParameterName, "} but no expectations are defined\n\n ").concat(block(pipelineIdentification), "\n "); }));
3902
3927
  }
3903
- else if (taskType === 'SCRIPT_TASK') {
3904
- commands_1.push("SCRIPT");
3905
- if (task.contentLanguage) {
3906
- contentLanguage = task.contentLanguage;
3928
+ var _loop_4 = function (joker) {
3929
+ if (!task.dependentParameterNames.includes(joker)) {
3930
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(joker, "}` is used for {").concat(task.resultingParameterName, "} as joker but not in `dependentParameterNames`\n\n ").concat(block(pipelineIdentification), "\n "); }));
3907
3931
  }
3908
- else {
3909
- contentLanguage = '';
3932
+ };
3933
+ try {
3934
+ for (var _k = (e_4 = void 0, __values(task.jokerParameterNames)), _l = _k.next(); !_l.done; _l = _k.next()) {
3935
+ var joker = _l.value;
3936
+ _loop_4(joker);
3910
3937
  }
3911
3938
  }
3912
- else if (taskType === 'DIALOG_TASK') {
3913
- commands_1.push("DIALOG");
3914
- // Note: Nothing special here
3915
- } // <- }else if([🅱]
3916
- if (jokers) {
3939
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
3940
+ finally {
3917
3941
  try {
3918
- 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()) {
3919
- var joker = jokers_1_1.value;
3920
- commands_1.push("JOKER {".concat(joker, "}"));
3921
- }
3922
- }
3923
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
3924
- finally {
3925
- try {
3926
- if (jokers_1_1 && !jokers_1_1.done && (_d = jokers_1.return)) _d.call(jokers_1);
3927
- }
3928
- finally { if (e_4) throw e_4.error; }
3942
+ if (_l && !_l.done && (_h = _k.return)) _h.call(_k);
3929
3943
  }
3930
- } /* not else */
3931
- if (postprocessing) {
3932
- try {
3933
- 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()) {
3934
- var postprocessingFunctionName = postprocessing_1_1.value;
3935
- commands_1.push("POSTPROCESSING `".concat(postprocessingFunctionName, "`"));
3936
- }
3944
+ finally { if (e_4) throw e_4.error; }
3945
+ }
3946
+ }
3947
+ if (task.expectations) {
3948
+ var _loop_5 = function (unit, min, max) {
3949
+ if (min !== undefined && max !== undefined && min > max) {
3950
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation (=".concat(min, ") of ").concat(unit, " is higher than max expectation (=").concat(max, ")\n\n ").concat(block(pipelineIdentification), "\n "); }));
3937
3951
  }
3938
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
3939
- finally {
3940
- try {
3941
- if (postprocessing_1_1 && !postprocessing_1_1.done && (_e = postprocessing_1.return)) _e.call(postprocessing_1);
3942
- }
3943
- finally { if (e_5) throw e_5.error; }
3952
+ if (min !== undefined && min < 0) {
3953
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation of ".concat(unit, " must be zero or positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
3944
3954
  }
3945
- } /* not else */
3946
- if (expectations) {
3947
- try {
3948
- for (var _m = (e_6 = void 0, __values(Object.entries(expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
3949
- var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
3950
- if (min === max) {
3951
- commands_1.push("EXPECT EXACTLY ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
3952
- }
3953
- else {
3954
- if (min !== undefined) {
3955
- commands_1.push("EXPECT MIN ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
3956
- } /* not else */
3957
- if (max !== undefined) {
3958
- commands_1.push("EXPECT MAX ".concat(max, " ").concat(capitalize(unit + (max > 1 ? 's' : ''))));
3959
- }
3960
- }
3961
- }
3955
+ if (max !== undefined && max <= 0) {
3956
+ throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Max expectation of ".concat(unit, " must be positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
3962
3957
  }
3963
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
3964
- finally {
3965
- try {
3966
- if (_o && !_o.done && (_f = _m.return)) _f.call(_m);
3967
- }
3968
- finally { if (e_6) throw e_6.error; }
3958
+ };
3959
+ try {
3960
+ for (var _m = (e_5 = void 0, __values(Object.entries(task.expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
3961
+ var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
3962
+ _loop_5(unit, min, max);
3969
3963
  }
3970
- } /* not else */
3971
- if (format) {
3972
- if (format === 'JSON') {
3973
- // TODO: @deprecated remove
3974
- commands_1.push("FORMAT JSON");
3964
+ }
3965
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
3966
+ finally {
3967
+ try {
3968
+ if (_o && !_o.done && (_j = _m.return)) _j.call(_m);
3975
3969
  }
3976
- } /* not else */
3977
- pipelineString += '\n\n';
3978
- pipelineString += commands_1.map(function (command) { return "- ".concat(command); }).join('\n');
3979
- pipelineString += '\n\n';
3980
- pipelineString += '```' + contentLanguage;
3981
- pipelineString += '\n';
3982
- pipelineString += spaceTrim__default["default"](content);
3983
- // <- TODO: [main] !!3 Escape
3984
- // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
3985
- pipelineString += '\n';
3986
- pipelineString += '```';
3987
- pipelineString += '\n\n';
3988
- pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
3970
+ finally { if (e_5) throw e_5.error; }
3971
+ }
3972
+ }
3973
+ };
3974
+ try {
3975
+ // Note: Checking each task individually
3976
+ for (var _f = __values(pipeline.tasks), _g = _f.next(); !_g.done; _g = _f.next()) {
3977
+ var task = _g.value;
3978
+ _loop_2(task);
3979
+ }
3980
+ }
3981
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
3982
+ finally {
3983
+ try {
3984
+ if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
3985
+ }
3986
+ finally { if (e_2) throw e_2.error; }
3987
+ }
3988
+ // Note: Detect circular dependencies
3989
+ var resovedParameters = pipeline.parameters
3990
+ .filter(function (_a) {
3991
+ var isInput = _a.isInput;
3992
+ return isInput;
3993
+ })
3994
+ .map(function (_a) {
3995
+ var name = _a.name;
3996
+ return name;
3997
+ });
3998
+ try {
3999
+ // Note: All reserved parameters are resolved
4000
+ for (var RESERVED_PARAMETER_NAMES_1 = __values(RESERVED_PARAMETER_NAMES), RESERVED_PARAMETER_NAMES_1_1 = RESERVED_PARAMETER_NAMES_1.next(); !RESERVED_PARAMETER_NAMES_1_1.done; RESERVED_PARAMETER_NAMES_1_1 = RESERVED_PARAMETER_NAMES_1.next()) {
4001
+ var reservedParameterName = RESERVED_PARAMETER_NAMES_1_1.value;
4002
+ resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), [reservedParameterName], false);
3989
4003
  }
3990
4004
  }
3991
4005
  catch (e_3_1) { e_3 = { error: e_3_1 }; }
3992
4006
  finally {
3993
4007
  try {
3994
- if (tasks_1_1 && !tasks_1_1.done && (_c = tasks_1.return)) _c.call(tasks_1);
4008
+ if (RESERVED_PARAMETER_NAMES_1_1 && !RESERVED_PARAMETER_NAMES_1_1.done && (_c = RESERVED_PARAMETER_NAMES_1.return)) _c.call(RESERVED_PARAMETER_NAMES_1);
3995
4009
  }
3996
4010
  finally { if (e_3) throw e_3.error; }
3997
4011
  }
3998
- return validatePipelineString(pipelineString);
4012
+ var unresovedTasks = __spreadArray([], __read(pipeline.tasks), false);
4013
+ var loopLimit = LOOP_LIMIT;
4014
+ var _loop_3 = function () {
4015
+ if (loopLimit-- < 0) {
4016
+ // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
4017
+ throw new UnexpectedError(spaceTrim.spaceTrim(function (block) { return "\n Loop limit reached during detection of circular dependencies in `validatePipeline`\n\n ".concat(block(pipelineIdentification), "\n "); }));
4018
+ }
4019
+ var currentlyResovedTasks = unresovedTasks.filter(function (task) {
4020
+ return task.dependentParameterNames.every(function (name) { return resovedParameters.includes(name); });
4021
+ });
4022
+ if (currentlyResovedTasks.length === 0) {
4023
+ throw new PipelineLogicError(
4024
+ // TODO: [🐎] DRY
4025
+ spaceTrim.spaceTrim(function (block) { return "\n\n Can not resolve some parameters:\n Either you are using a parameter that is not defined, or there are some circular dependencies.\n\n ".concat(block(pipelineIdentification), "\n\n **Can not resolve:**\n ").concat(block(unresovedTasks
4026
+ .map(function (_a) {
4027
+ var resultingParameterName = _a.resultingParameterName, dependentParameterNames = _a.dependentParameterNames;
4028
+ return "- Parameter `{".concat(resultingParameterName, "}` which depends on ").concat(dependentParameterNames
4029
+ .map(function (dependentParameterName) { return "`{".concat(dependentParameterName, "}`"); })
4030
+ .join(' and '));
4031
+ })
4032
+ .join('\n')), "\n\n **Resolved:**\n ").concat(block(resovedParameters
4033
+ .filter(function (name) {
4034
+ return !RESERVED_PARAMETER_NAMES.includes(name);
4035
+ })
4036
+ .map(function (name) { return "- Parameter `{".concat(name, "}`"); })
4037
+ .join('\n')), "\n\n\n **Reserved (which are available):**\n ").concat(block(resovedParameters
4038
+ .filter(function (name) {
4039
+ return RESERVED_PARAMETER_NAMES.includes(name);
4040
+ })
4041
+ .map(function (name) { return "- Parameter `{".concat(name, "}`"); })
4042
+ .join('\n')), "\n\n\n "); }));
4043
+ }
4044
+ resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), __read(currentlyResovedTasks.map(function (_a) {
4045
+ var resultingParameterName = _a.resultingParameterName;
4046
+ return resultingParameterName;
4047
+ })), false);
4048
+ unresovedTasks = unresovedTasks.filter(function (task) { return !currentlyResovedTasks.includes(task); });
4049
+ };
4050
+ while (unresovedTasks.length > 0) {
4051
+ _loop_3();
4052
+ }
4053
+ // Note: Check that formfactor is corresponding to the pipeline interface
4054
+ // TODO: !!6 Implement this
4055
+ // pipeline.formfactorName
3999
4056
  }
4000
4057
  /**
4001
- * @private internal utility of `pipelineJsonToString`
4058
+ * TODO: [🧞‍♀️] Do not allow joker + foreach
4059
+ * TODO: [🧠] Work with promptbookVersion
4060
+ * TODO: Use here some json-schema, Zod or something similar and change it to:
4061
+ * > /**
4062
+ * > * Validates PipelineJson if it is logically valid.
4063
+ * > *
4064
+ * > * It checks:
4065
+ * > * - it has a valid structure
4066
+ * > * - ...
4067
+ * > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
4002
4068
  */
4003
- function taskParameterJsonToString(taskParameterJson) {
4004
- var name = taskParameterJson.name, description = taskParameterJson.description;
4005
- var parameterString = "{".concat(name, "}");
4006
- if (description) {
4007
- parameterString = "".concat(parameterString, " ").concat(description);
4008
- }
4009
- return parameterString;
4010
- }
4011
4069
  /**
4012
- * TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
4013
- * TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
4014
- * TODO: [🏛] Maybe make some markdown builder
4015
- * TODO: [🏛] Escape all
4016
- * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
4070
+ * TODO: [🧳][main] !!4 Validate that all examples match expectations
4071
+ * TODO: [🧳][🐝][main] !!4 Validate that knowledge is valid (non-void)
4072
+ * TODO: [🧳][main] !!4 Validate that persona can be used only with CHAT variant
4073
+ * TODO: [🧳][main] !!4 Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
4074
+ * TODO: [🧳][main] !!4 Validate that reserved parameter is not used as joker
4075
+ * TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
4076
+ * TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
4017
4077
  */
4018
4078
 
4019
4079
  /**
4020
- * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
4080
+ * Loads the books from the archive file with `.bookc` extension
4021
4081
  *
4022
- * @public exported from `@promptbook/core`
4082
+ * @param filePath Path to the archive file with `.bookc` extension
4083
+ * @param fs Filesystem tools
4084
+ * @returns Pipelines loaded from the archive
4085
+ *
4086
+ * @private utility of Prompbook
4023
4087
  */
4024
- var PipelineLogicError = /** @class */ (function (_super) {
4025
- __extends(PipelineLogicError, _super);
4026
- function PipelineLogicError(message) {
4027
- var _this = _super.call(this, message) || this;
4028
- _this.name = 'PipelineLogicError';
4029
- Object.setPrototypeOf(_this, PipelineLogicError.prototype);
4030
- return _this;
4031
- }
4032
- return PipelineLogicError;
4033
- }(Error));
4088
+ function loadArchive(filePath, fs) {
4089
+ return __awaiter(this, void 0, void 0, function () {
4090
+ var data, archive, indexFile, collectionJson, _a, _b, collectionJson_1, collectionJson_1_1, pipeline;
4091
+ var e_1, _c;
4092
+ return __generator(this, function (_d) {
4093
+ switch (_d.label) {
4094
+ case 0:
4095
+ if (!filePath.endsWith('.bookc')) {
4096
+ throw new UnexpectedError("Archive file must have '.bookc' extension");
4097
+ }
4098
+ return [4 /*yield*/, fs.readFile(filePath)];
4099
+ case 1:
4100
+ data = _d.sent();
4101
+ return [4 /*yield*/, JSZip__default["default"].loadAsync(data)];
4102
+ case 2:
4103
+ archive = _d.sent();
4104
+ indexFile = archive.file('index.book.json');
4105
+ if (!indexFile) {
4106
+ throw new UnexpectedError("Archive does not contain 'index.book.json' file");
4107
+ }
4108
+ _b = (_a = JSON).parse;
4109
+ return [4 /*yield*/, indexFile.async('text')];
4110
+ case 3:
4111
+ collectionJson = _b.apply(_a, [_d.sent()]);
4112
+ try {
4113
+ for (collectionJson_1 = __values(collectionJson), collectionJson_1_1 = collectionJson_1.next(); !collectionJson_1_1.done; collectionJson_1_1 = collectionJson_1.next()) {
4114
+ pipeline = collectionJson_1_1.value;
4115
+ validatePipeline(pipeline);
4116
+ }
4117
+ }
4118
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
4119
+ finally {
4120
+ try {
4121
+ if (collectionJson_1_1 && !collectionJson_1_1.done && (_c = collectionJson_1.return)) _c.call(collectionJson_1);
4122
+ }
4123
+ finally { if (e_1) throw e_1.error; }
4124
+ }
4125
+ return [2 /*return*/, collectionJson];
4126
+ }
4127
+ });
4128
+ });
4129
+ }
4130
+ /**
4131
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
4132
+ */
4133
+
4134
+ 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:"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`\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"},{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"}];
4034
4135
 
4035
4136
  /**
4036
- * Tests if given string is valid semantic version
4037
- *
4038
- * Note: There are two simmilar functions:
4039
- * - `isValidSemanticVersion` which tests any semantic version
4040
- * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
4137
+ * Checks if value is valid email
4041
4138
  *
4042
4139
  * @public exported from `@promptbook/utils`
4043
4140
  */
4044
- function isValidSemanticVersion(version) {
4045
- if (typeof version !== 'string') {
4141
+ function isValidEmail(email) {
4142
+ if (typeof email !== 'string') {
4046
4143
  return false;
4047
4144
  }
4048
- if (version.startsWith('0.0.0')) {
4145
+ if (email.split('\n').length > 1) {
4049
4146
  return false;
4050
4147
  }
4051
- return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
4148
+ return /^.+@.+\..+$/.test(email);
4052
4149
  }
4053
4150
 
4054
4151
  /**
4055
- * Tests if given string is valid promptbook version
4056
- * It looks into list of known promptbook versions.
4057
- *
4058
- * @see https://www.npmjs.com/package/promptbook?activeTab=versions
4059
- * 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.
4060
- * Note: There are two simmilar functions:
4061
- * - `isValidSemanticVersion` which tests any semantic version
4062
- * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
4152
+ * Function isValidJsonString will tell you if the string is valid JSON or not
4063
4153
  *
4064
4154
  * @public exported from `@promptbook/utils`
4065
4155
  */
4066
- function isValidPromptbookVersion(version) {
4067
- if (!isValidSemanticVersion(version)) {
4068
- return false;
4156
+ function isValidJsonString(value /* <- [👨‍⚖️] */) {
4157
+ try {
4158
+ JSON.parse(value);
4159
+ return true;
4069
4160
  }
4070
- if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
4161
+ catch (error) {
4162
+ if (!(error instanceof Error)) {
4163
+ throw error;
4164
+ }
4165
+ if (error.message.includes('Unexpected token')) {
4166
+ return false;
4167
+ }
4071
4168
  return false;
4072
4169
  }
4073
- // <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
4074
- return true;
4075
4170
  }
4076
4171
 
4077
4172
  /**
4078
- * Tests if given string is valid pipeline URL URL.
4079
- *
4080
- * Note: There are two simmilar functions:
4081
- * - `isValidUrl` which tests any URL
4082
- * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
4173
+ * Function `validatePipelineString` will validate the if the string is a valid pipeline string
4174
+ * 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.
4083
4175
  *
4084
- * @public exported from `@promptbook/utils`
4176
+ * @param {string} pipelineString the candidate for a pipeline string
4177
+ * @returns {PipelineString} the same string as input, but validated as valid
4178
+ * @throws {ParseError} if the string is not a valid pipeline string
4179
+ * @public exported from `@promptbook/core`
4085
4180
  */
4086
- function isValidPipelineUrl(url) {
4087
- if (!isValidUrl(url)) {
4088
- return false;
4181
+ function validatePipelineString(pipelineString) {
4182
+ if (isValidJsonString(pipelineString)) {
4183
+ throw new ParseError('Expected a book, but got a JSON string');
4089
4184
  }
4090
- if (!url.startsWith('https://') && !url.startsWith('http://') /* <- Note: [👣] */) {
4091
- return false;
4185
+ else if (isValidUrl(pipelineString)) {
4186
+ throw new ParseError("Expected a book, but got just the URL \"".concat(pipelineString, "\""));
4092
4187
  }
4093
- if (url.includes('#')) {
4094
- // TODO: [🐠]
4095
- return false;
4188
+ else if (isValidFilePath(pipelineString)) {
4189
+ throw new ParseError("Expected a book, but got just the file path \"".concat(pipelineString, "\""));
4096
4190
  }
4097
- /*
4098
- Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
4099
- if (isUrlOnPrivateNetwork(url)) {
4100
- return false;
4191
+ else if (isValidEmail(pipelineString)) {
4192
+ throw new ParseError("Expected a book, but got just the email \"".concat(pipelineString, "\""));
4101
4193
  }
4102
- */
4103
- return true;
4194
+ // <- TODO: Implement the validation + add tests when the pipeline logic considered as invalid
4195
+ return pipelineString;
4104
4196
  }
4105
4197
  /**
4106
- * TODO: [🐠] Maybe more info why the URL is invalid
4198
+ * TODO: [🧠][🈴] Where is the best location for this file
4107
4199
  */
4108
4200
 
4109
4201
  /**
4110
- * Validates PipelineJson if it is logically valid
4111
- *
4112
- * It checks:
4113
- * - if it has correct parameters dependency
4114
- *
4115
- * It does NOT check:
4116
- * - if it is valid json
4117
- * - if it is meaningful
4202
+ * Prettify the html code
4118
4203
  *
4119
- * @param pipeline valid or invalid PipelineJson
4120
- * @returns the same pipeline if it is logically valid
4121
- * @throws {PipelineLogicError} on logical error in the pipeline
4122
- * @public exported from `@promptbook/core`
4204
+ * @param content raw html code
4205
+ * @returns formatted html code
4206
+ * @private withing the package because of HUGE size of prettier dependency
4123
4207
  */
4124
- function validatePipeline(pipeline) {
4125
- if (IS_PIPELINE_LOGIC_VALIDATED) {
4126
- validatePipeline_InnerFunction(pipeline);
4208
+ function prettifyMarkdown(content) {
4209
+ try {
4210
+ return prettier.format(content, {
4211
+ parser: 'markdown',
4212
+ plugins: [parserHtml__default["default"]],
4213
+ // TODO: DRY - make some import or auto-copy of .prettierrc
4214
+ endOfLine: 'lf',
4215
+ tabWidth: 4,
4216
+ singleQuote: true,
4217
+ trailingComma: 'all',
4218
+ arrowParens: 'always',
4219
+ printWidth: 120,
4220
+ htmlWhitespaceSensitivity: 'ignore',
4221
+ jsxBracketSameLine: false,
4222
+ bracketSpacing: true,
4223
+ });
4127
4224
  }
4128
- else {
4129
- try {
4130
- validatePipeline_InnerFunction(pipeline);
4131
- }
4132
- catch (error) {
4133
- if (!(error instanceof PipelineLogicError)) {
4134
- throw error;
4135
- }
4136
- console.error(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is not valid but logic errors are temporarily disabled via `IS_PIPELINE_LOGIC_VALIDATED`\n\n ".concat(block(error.message), "\n "); }));
4137
- }
4225
+ catch (error) {
4226
+ // TODO: [🟥] Detect browser / node and make it colorfull
4227
+ console.error('There was an error with prettifying the markdown, using the original as the fallback', {
4228
+ error: error,
4229
+ html: content,
4230
+ });
4231
+ return content;
4138
4232
  }
4139
- return pipeline;
4140
4233
  }
4234
+
4141
4235
  /**
4142
- * @private internal function for `validatePipeline`
4236
+ * Makes first letter of a string uppercase
4237
+ *
4238
+ * @public exported from `@promptbook/utils`
4143
4239
  */
4144
- function validatePipeline_InnerFunction(pipeline) {
4145
- // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
4146
- var e_1, _a, e_2, _b, e_3, _c;
4147
- var pipelineIdentification = (function () {
4148
- // Note: This is a 😐 implementation of [🚞]
4149
- var _ = [];
4150
- if (pipeline.sourceFile !== undefined) {
4151
- _.push("File: ".concat(pipeline.sourceFile));
4152
- }
4153
- if (pipeline.pipelineUrl !== undefined) {
4154
- _.push("Url: ".concat(pipeline.pipelineUrl));
4155
- }
4156
- return _.join('\n');
4157
- })();
4158
- if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
4159
- // <- Note: [🚲]
4160
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid promptbook URL \"".concat(pipeline.pipelineUrl, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
4161
- }
4162
- if (pipeline.bookVersion !== undefined && !isValidPromptbookVersion(pipeline.bookVersion)) {
4163
- // <- Note: [🚲]
4164
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid Promptbook Version \"".concat(pipeline.bookVersion, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
4240
+ function capitalize(word) {
4241
+ return word.substring(0, 1).toUpperCase() + word.substring(1);
4242
+ }
4243
+
4244
+ /**
4245
+ * Converts promptbook in JSON format to string format
4246
+ *
4247
+ * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
4248
+ * @param pipelineJson Promptbook in JSON format (.bookc)
4249
+ * @returns Promptbook in string format (.book.md)
4250
+ * @public exported from `@promptbook/core`
4251
+ */
4252
+ function pipelineJsonToString(pipelineJson) {
4253
+ var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
4254
+ var title = pipelineJson.title, pipelineUrl = pipelineJson.pipelineUrl, bookVersion = pipelineJson.bookVersion, description = pipelineJson.description, parameters = pipelineJson.parameters, tasks = pipelineJson.tasks;
4255
+ var pipelineString = "# ".concat(title);
4256
+ if (description) {
4257
+ pipelineString += '\n\n';
4258
+ pipelineString += description;
4165
4259
  }
4166
- // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
4167
- if (!Array.isArray(pipeline.parameters)) {
4168
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
4169
- throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.parameters` expected to be an array, but got ".concat(typeof pipeline.parameters, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
4260
+ var commands = [];
4261
+ if (pipelineUrl) {
4262
+ commands.push("PIPELINE URL ".concat(pipelineUrl));
4170
4263
  }
4171
- // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
4172
- if (!Array.isArray(pipeline.tasks)) {
4173
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
4174
- throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.tasks` expected to be an array, but got ".concat(typeof pipeline.tasks, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
4264
+ if (bookVersion !== "undefined") {
4265
+ commands.push("BOOK VERSION ".concat(bookVersion));
4175
4266
  }
4176
- var _loop_1 = function (parameter) {
4177
- if (parameter.isInput && parameter.isOutput) {
4178
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n\n Parameter `{".concat(parameter.name, "}` can not be both input and output\n\n ").concat(block(pipelineIdentification), "\n "); }));
4179
- }
4180
- // Note: Testing that parameter is either intermediate or output BUT not created and unused
4181
- if (!parameter.isInput &&
4182
- !parameter.isOutput &&
4183
- !pipeline.tasks.some(function (task) { return task.dependentParameterNames.includes(parameter.name); })) {
4184
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(parameter.name, "}` is created but not used\n\n You can declare {").concat(parameter.name, "} as output parameter by adding in the header:\n - OUTPUT PARAMETER `{").concat(parameter.name, "}` ").concat(parameter.description || '', "\n\n ").concat(block(pipelineIdentification), "\n\n "); }));
4185
- }
4186
- // Note: Testing that parameter is either input or result of some task
4187
- if (!parameter.isInput && !pipeline.tasks.some(function (task) { return task.resultingParameterName === parameter.name; })) {
4188
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(parameter.name, "}` is declared but not defined\n\n You can do one of these:\n 1) Remove declaration of `{").concat(parameter.name, "}`\n 2) Add task that results in `-> {").concat(parameter.name, "}`\n\n ").concat(block(pipelineIdentification), "\n "); }));
4189
- }
4190
- };
4267
+ // TODO: [main] !!5 This increases size of the bundle and is probbably not necessary
4268
+ pipelineString = prettifyMarkdown(pipelineString);
4191
4269
  try {
4192
- /*
4193
- TODO: [🧠][🅾] Should be empty pipeline valid or not
4194
- // Note: Check that pipeline has some tasks
4195
- if (pipeline.tasks.length === 0) {
4196
- throw new PipelineLogicError(
4197
- spaceTrim(
4198
- (block) => `
4199
- Pipeline must have at least one task
4200
-
4201
- ${block(pipelineIdentification)}
4202
- `,
4203
- ),
4204
- );
4205
- }
4206
- */
4207
- // Note: Check each parameter individually
4208
- for (var _d = __values(pipeline.parameters), _e = _d.next(); !_e.done; _e = _d.next()) {
4209
- var parameter = _e.value;
4210
- _loop_1(parameter);
4270
+ for (var _g = __values(parameters.filter(function (_a) {
4271
+ var isInput = _a.isInput;
4272
+ return isInput;
4273
+ })), _h = _g.next(); !_h.done; _h = _g.next()) {
4274
+ var parameter = _h.value;
4275
+ commands.push("INPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
4211
4276
  }
4212
4277
  }
4213
4278
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
4214
4279
  finally {
4215
4280
  try {
4216
- if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
4281
+ if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
4217
4282
  }
4218
4283
  finally { if (e_1) throw e_1.error; }
4219
4284
  }
4220
- // Note: All input parameters are defined - so that they can be used as result of some task
4221
- var definedParameters = new Set(pipeline.parameters.filter(function (_a) {
4222
- var isInput = _a.isInput;
4223
- return isInput;
4224
- }).map(function (_a) {
4225
- var name = _a.name;
4226
- return name;
4227
- }));
4228
- var _loop_2 = function (task) {
4229
- var e_4, _h, e_5, _j;
4230
- if (definedParameters.has(task.resultingParameterName)) {
4231
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(task.resultingParameterName, "}` is defined multiple times\n\n ").concat(block(pipelineIdentification), "\n "); }));
4285
+ try {
4286
+ for (var _j = __values(parameters.filter(function (_a) {
4287
+ var isOutput = _a.isOutput;
4288
+ return isOutput;
4289
+ })), _k = _j.next(); !_k.done; _k = _j.next()) {
4290
+ var parameter = _k.value;
4291
+ commands.push("OUTPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
4232
4292
  }
4233
- if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
4234
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter name {".concat(task.resultingParameterName, "} is reserved, please use different name\n\n ").concat(block(pipelineIdentification), "\n "); }));
4293
+ }
4294
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
4295
+ finally {
4296
+ try {
4297
+ if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
4235
4298
  }
4236
- definedParameters.add(task.resultingParameterName);
4237
- if (task.jokerParameterNames && task.jokerParameterNames.length > 0) {
4238
- if (!task.format &&
4239
- !task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
4240
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Joker parameters are used for {".concat(task.resultingParameterName, "} but no expectations are defined\n\n ").concat(block(pipelineIdentification), "\n "); }));
4299
+ finally { if (e_2) throw e_2.error; }
4300
+ }
4301
+ pipelineString += '\n\n';
4302
+ pipelineString += commands.map(function (command) { return "- ".concat(command); }).join('\n');
4303
+ try {
4304
+ for (var tasks_1 = __values(tasks), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) {
4305
+ var task = tasks_1_1.value;
4306
+ var
4307
+ /* Note: Not using:> name, */
4308
+ title_1 = task.title, description_1 = task.description,
4309
+ /* Note: dependentParameterNames, */
4310
+ jokers = task.jokerParameterNames, taskType = task.taskType, content = task.content, postprocessing = task.postprocessingFunctionNames, expectations = task.expectations, format = task.format, resultingParameterName = task.resultingParameterName;
4311
+ pipelineString += '\n\n';
4312
+ pipelineString += "## ".concat(title_1);
4313
+ if (description_1) {
4314
+ pipelineString += '\n\n';
4315
+ pipelineString += description_1;
4241
4316
  }
4242
- var _loop_4 = function (joker) {
4243
- if (!task.dependentParameterNames.includes(joker)) {
4244
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(joker, "}` is used for {").concat(task.resultingParameterName, "} as joker but not in `dependentParameterNames`\n\n ").concat(block(pipelineIdentification), "\n "); }));
4317
+ var commands_1 = [];
4318
+ var contentLanguage = 'text';
4319
+ if (taskType === 'PROMPT_TASK') {
4320
+ var modelRequirements = task.modelRequirements;
4321
+ var _l = modelRequirements || {}, modelName = _l.modelName, modelVariant = _l.modelVariant;
4322
+ // Note: Do nothing, it is default
4323
+ // commands.push(`PROMPT`);
4324
+ if (modelVariant) {
4325
+ commands_1.push("MODEL VARIANT ".concat(capitalize(modelVariant)));
4245
4326
  }
4246
- };
4247
- try {
4248
- for (var _k = (e_4 = void 0, __values(task.jokerParameterNames)), _l = _k.next(); !_l.done; _l = _k.next()) {
4249
- var joker = _l.value;
4250
- _loop_4(joker);
4327
+ if (modelName) {
4328
+ commands_1.push("MODEL NAME `".concat(modelName, "`"));
4251
4329
  }
4252
4330
  }
4253
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
4254
- finally {
4255
- try {
4256
- if (_l && !_l.done && (_h = _k.return)) _h.call(_k);
4331
+ else if (taskType === 'SIMPLE_TASK') {
4332
+ commands_1.push("SIMPLE TEMPLATE");
4333
+ // Note: Nothing special here
4334
+ }
4335
+ else if (taskType === 'SCRIPT_TASK') {
4336
+ commands_1.push("SCRIPT");
4337
+ if (task.contentLanguage) {
4338
+ contentLanguage = task.contentLanguage;
4339
+ }
4340
+ else {
4341
+ contentLanguage = '';
4257
4342
  }
4258
- finally { if (e_4) throw e_4.error; }
4259
4343
  }
4260
- }
4261
- if (task.expectations) {
4262
- var _loop_5 = function (unit, min, max) {
4263
- if (min !== undefined && max !== undefined && min > max) {
4264
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation (=".concat(min, ") of ").concat(unit, " is higher than max expectation (=").concat(max, ")\n\n ").concat(block(pipelineIdentification), "\n "); }));
4344
+ else if (taskType === 'DIALOG_TASK') {
4345
+ commands_1.push("DIALOG");
4346
+ // Note: Nothing special here
4347
+ } // <- }else if([🅱]
4348
+ if (jokers) {
4349
+ try {
4350
+ 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()) {
4351
+ var joker = jokers_1_1.value;
4352
+ commands_1.push("JOKER {".concat(joker, "}"));
4353
+ }
4265
4354
  }
4266
- if (min !== undefined && min < 0) {
4267
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation of ".concat(unit, " must be zero or positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
4355
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
4356
+ finally {
4357
+ try {
4358
+ if (jokers_1_1 && !jokers_1_1.done && (_d = jokers_1.return)) _d.call(jokers_1);
4359
+ }
4360
+ finally { if (e_4) throw e_4.error; }
4268
4361
  }
4269
- if (max !== undefined && max <= 0) {
4270
- throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Max expectation of ".concat(unit, " must be positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
4362
+ } /* not else */
4363
+ if (postprocessing) {
4364
+ try {
4365
+ 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()) {
4366
+ var postprocessingFunctionName = postprocessing_1_1.value;
4367
+ commands_1.push("POSTPROCESSING `".concat(postprocessingFunctionName, "`"));
4368
+ }
4369
+ }
4370
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
4371
+ finally {
4372
+ try {
4373
+ if (postprocessing_1_1 && !postprocessing_1_1.done && (_e = postprocessing_1.return)) _e.call(postprocessing_1);
4374
+ }
4375
+ finally { if (e_5) throw e_5.error; }
4376
+ }
4377
+ } /* not else */
4378
+ if (expectations) {
4379
+ try {
4380
+ for (var _m = (e_6 = void 0, __values(Object.entries(expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
4381
+ var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
4382
+ if (min === max) {
4383
+ commands_1.push("EXPECT EXACTLY ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
4384
+ }
4385
+ else {
4386
+ if (min !== undefined) {
4387
+ commands_1.push("EXPECT MIN ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
4388
+ } /* not else */
4389
+ if (max !== undefined) {
4390
+ commands_1.push("EXPECT MAX ".concat(max, " ").concat(capitalize(unit + (max > 1 ? 's' : ''))));
4391
+ }
4392
+ }
4393
+ }
4271
4394
  }
4272
- };
4273
- try {
4274
- for (var _m = (e_5 = void 0, __values(Object.entries(task.expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
4275
- var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
4276
- _loop_5(unit, min, max);
4395
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
4396
+ finally {
4397
+ try {
4398
+ if (_o && !_o.done && (_f = _m.return)) _f.call(_m);
4399
+ }
4400
+ finally { if (e_6) throw e_6.error; }
4277
4401
  }
4278
- }
4279
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
4280
- finally {
4281
- try {
4282
- if (_o && !_o.done && (_j = _m.return)) _j.call(_m);
4402
+ } /* not else */
4403
+ if (format) {
4404
+ if (format === 'JSON') {
4405
+ // TODO: @deprecated remove
4406
+ commands_1.push("FORMAT JSON");
4283
4407
  }
4284
- finally { if (e_5) throw e_5.error; }
4285
- }
4286
- }
4287
- };
4288
- try {
4289
- // Note: Checking each task individually
4290
- for (var _f = __values(pipeline.tasks), _g = _f.next(); !_g.done; _g = _f.next()) {
4291
- var task = _g.value;
4292
- _loop_2(task);
4293
- }
4294
- }
4295
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
4296
- finally {
4297
- try {
4298
- if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
4299
- }
4300
- finally { if (e_2) throw e_2.error; }
4301
- }
4302
- // Note: Detect circular dependencies
4303
- var resovedParameters = pipeline.parameters
4304
- .filter(function (_a) {
4305
- var isInput = _a.isInput;
4306
- return isInput;
4307
- })
4308
- .map(function (_a) {
4309
- var name = _a.name;
4310
- return name;
4311
- });
4312
- try {
4313
- // Note: All reserved parameters are resolved
4314
- for (var RESERVED_PARAMETER_NAMES_1 = __values(RESERVED_PARAMETER_NAMES), RESERVED_PARAMETER_NAMES_1_1 = RESERVED_PARAMETER_NAMES_1.next(); !RESERVED_PARAMETER_NAMES_1_1.done; RESERVED_PARAMETER_NAMES_1_1 = RESERVED_PARAMETER_NAMES_1.next()) {
4315
- var reservedParameterName = RESERVED_PARAMETER_NAMES_1_1.value;
4316
- resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), [reservedParameterName], false);
4408
+ } /* not else */
4409
+ pipelineString += '\n\n';
4410
+ pipelineString += commands_1.map(function (command) { return "- ".concat(command); }).join('\n');
4411
+ pipelineString += '\n\n';
4412
+ pipelineString += '```' + contentLanguage;
4413
+ pipelineString += '\n';
4414
+ pipelineString += spaceTrim__default["default"](content);
4415
+ // <- TODO: [main] !!3 Escape
4416
+ // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
4417
+ pipelineString += '\n';
4418
+ pipelineString += '```';
4419
+ pipelineString += '\n\n';
4420
+ pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
4317
4421
  }
4318
4422
  }
4319
4423
  catch (e_3_1) { e_3 = { error: e_3_1 }; }
4320
4424
  finally {
4321
4425
  try {
4322
- if (RESERVED_PARAMETER_NAMES_1_1 && !RESERVED_PARAMETER_NAMES_1_1.done && (_c = RESERVED_PARAMETER_NAMES_1.return)) _c.call(RESERVED_PARAMETER_NAMES_1);
4426
+ if (tasks_1_1 && !tasks_1_1.done && (_c = tasks_1.return)) _c.call(tasks_1);
4323
4427
  }
4324
4428
  finally { if (e_3) throw e_3.error; }
4325
4429
  }
4326
- var unresovedTasks = __spreadArray([], __read(pipeline.tasks), false);
4327
- var loopLimit = LOOP_LIMIT;
4328
- var _loop_3 = function () {
4329
- if (loopLimit-- < 0) {
4330
- // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
4331
- throw new UnexpectedError(spaceTrim.spaceTrim(function (block) { return "\n Loop limit reached during detection of circular dependencies in `validatePipeline`\n\n ".concat(block(pipelineIdentification), "\n "); }));
4332
- }
4333
- var currentlyResovedTasks = unresovedTasks.filter(function (task) {
4334
- return task.dependentParameterNames.every(function (name) { return resovedParameters.includes(name); });
4335
- });
4336
- if (currentlyResovedTasks.length === 0) {
4337
- throw new PipelineLogicError(
4338
- // TODO: [🐎] DRY
4339
- spaceTrim.spaceTrim(function (block) { return "\n\n Can not resolve some parameters:\n Either you are using a parameter that is not defined, or there are some circular dependencies.\n\n ".concat(block(pipelineIdentification), "\n\n **Can not resolve:**\n ").concat(block(unresovedTasks
4340
- .map(function (_a) {
4341
- var resultingParameterName = _a.resultingParameterName, dependentParameterNames = _a.dependentParameterNames;
4342
- return "- Parameter `{".concat(resultingParameterName, "}` which depends on ").concat(dependentParameterNames
4343
- .map(function (dependentParameterName) { return "`{".concat(dependentParameterName, "}`"); })
4344
- .join(' and '));
4345
- })
4346
- .join('\n')), "\n\n **Resolved:**\n ").concat(block(resovedParameters
4347
- .filter(function (name) {
4348
- return !RESERVED_PARAMETER_NAMES.includes(name);
4349
- })
4350
- .map(function (name) { return "- Parameter `{".concat(name, "}`"); })
4351
- .join('\n')), "\n\n\n **Reserved (which are available):**\n ").concat(block(resovedParameters
4352
- .filter(function (name) {
4353
- return RESERVED_PARAMETER_NAMES.includes(name);
4354
- })
4355
- .map(function (name) { return "- Parameter `{".concat(name, "}`"); })
4356
- .join('\n')), "\n\n\n "); }));
4357
- }
4358
- resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), __read(currentlyResovedTasks.map(function (_a) {
4359
- var resultingParameterName = _a.resultingParameterName;
4360
- return resultingParameterName;
4361
- })), false);
4362
- unresovedTasks = unresovedTasks.filter(function (task) { return !currentlyResovedTasks.includes(task); });
4363
- };
4364
- while (unresovedTasks.length > 0) {
4365
- _loop_3();
4366
- }
4367
- // Note: Check that formfactor is corresponding to the pipeline interface
4368
- // TODO: !!6 Implement this
4369
- // pipeline.formfactorName
4430
+ return validatePipelineString(pipelineString);
4370
4431
  }
4371
4432
  /**
4372
- * TODO: [🧞‍♀️] Do not allow joker + foreach
4373
- * TODO: [🧠] Work with promptbookVersion
4374
- * TODO: Use here some json-schema, Zod or something similar and change it to:
4375
- * > /**
4376
- * > * Validates PipelineJson if it is logically valid.
4377
- * > *
4378
- * > * It checks:
4379
- * > * - it has a valid structure
4380
- * > * - ...
4381
- * > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
4433
+ * @private internal utility of `pipelineJsonToString`
4382
4434
  */
4435
+ function taskParameterJsonToString(taskParameterJson) {
4436
+ var name = taskParameterJson.name, description = taskParameterJson.description;
4437
+ var parameterString = "{".concat(name, "}");
4438
+ if (description) {
4439
+ parameterString = "".concat(parameterString, " ").concat(description);
4440
+ }
4441
+ return parameterString;
4442
+ }
4383
4443
  /**
4384
- * TODO: [🧳][main] !!4 Validate that all examples match expectations
4385
- * TODO: [🧳][🐝][main] !!4 Validate that knowledge is valid (non-void)
4386
- * TODO: [🧳][main] !!4 Validate that persona can be used only with CHAT variant
4387
- * TODO: [🧳][main] !!4 Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
4388
- * TODO: [🧳][main] !!4 Validate that reserved parameter is not used as joker
4389
- * TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
4390
- * TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
4444
+ * TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
4445
+ * TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
4446
+ * TODO: [🏛] Maybe make some markdown builder
4447
+ * TODO: [🏛] Escape all
4448
+ * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
4391
4449
  */
4392
4450
 
4393
4451
  /**
@@ -12034,17 +12092,17 @@
12034
12092
  }
12035
12093
  _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;
12036
12094
  collection = createCollectionFromPromise(function () { return __awaiter(_this, void 0, void 0, function () {
12037
- var fileNames, collection, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1;
12038
- var e_1, _a;
12039
- return __generator(this, function (_b) {
12040
- switch (_b.label) {
12095
+ var fileNames, collection, pipelinesWithFilenames, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1, _loop_2, pipelinesWithFilenames_1, pipelinesWithFilenames_1_1, pipelineWithFilenames;
12096
+ var e_1, _a, e_2, _b;
12097
+ return __generator(this, function (_c) {
12098
+ switch (_c.label) {
12041
12099
  case 0:
12042
12100
  if (isVerbose) {
12043
12101
  console.info(colors__default["default"].cyan("Creating pipeline collection from path ".concat(rootPath.split('\\').join('/'))));
12044
12102
  }
12045
12103
  return [4 /*yield*/, listAllFiles(rootPath, isRecursive, tools.fs)];
12046
12104
  case 1:
12047
- fileNames = _b.sent();
12105
+ fileNames = _c.sent();
12048
12106
  // Note: First load compiled `.bookc` files and then source `.book` files
12049
12107
  // `.bookc` are already compiled and can be used faster
12050
12108
  fileNames.sort(function (a, b) {
@@ -12057,100 +12115,54 @@
12057
12115
  return 0;
12058
12116
  });
12059
12117
  collection = new Map();
12118
+ pipelinesWithFilenames = [];
12060
12119
  _loop_1 = function (fileName) {
12061
- var sourceFile, rootDirname, pipeline, pipelineString, _c, _d, _e, pipelineUrl, existing, error_1, wrappedErrorMessage;
12062
- return __generator(this, function (_f) {
12063
- switch (_f.label) {
12120
+ var sourceFile, rootDirname, pipelineString, _d, pipeline, _e, _f, _g, _h, error_1, wrappedErrorMessage;
12121
+ return __generator(this, function (_j) {
12122
+ switch (_j.label) {
12064
12123
  case 0:
12065
12124
  sourceFile = './' + fileName.split('\\').join('/');
12066
12125
  rootDirname = path.dirname(sourceFile).split('\\').join('/');
12067
- _f.label = 1;
12126
+ _j.label = 1;
12068
12127
  case 1:
12069
- _f.trys.push([1, 8, , 9]);
12070
- pipeline = null;
12128
+ _j.trys.push([1, 8, , 9]);
12071
12129
  if (!(fileName.endsWith('.book') || fileName.endsWith('.book.md'))) return [3 /*break*/, 4];
12072
- _c = validatePipelineString;
12130
+ _d = validatePipelineString;
12073
12131
  return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
12074
12132
  case 2:
12075
- pipelineString = _c.apply(void 0, [_f.sent()]);
12133
+ pipelineString = _d.apply(void 0, [_j.sent()]);
12076
12134
  return [4 /*yield*/, compilePipeline(pipelineString, tools, {
12077
12135
  rootDirname: rootDirname,
12078
12136
  })];
12079
12137
  case 3:
12080
- pipeline = _f.sent();
12081
- pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
12138
+ pipeline = _j.sent();
12139
+ pipelinesWithFilenames.push({ fileName: fileName, sourceFile: sourceFile, pipeline: __assign(__assign({}, pipeline), { sourceFile: sourceFile }) });
12082
12140
  return [3 /*break*/, 7];
12083
12141
  case 4:
12084
12142
  if (!(fileName.endsWith('.bookc') || fileName.endsWith('.book.json'))) return [3 /*break*/, 6];
12085
- _e = (_d = JSON).parse;
12086
- return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
12143
+ _f =
12144
+ // TODO: Handle non-valid JSON files
12145
+ (_e = pipelinesWithFilenames.push).apply;
12146
+ _g = [
12147
+ // TODO: Handle non-valid JSON files
12148
+ pipelinesWithFilenames];
12149
+ _h = [[]];
12150
+ return [4 /*yield*/, loadArchive(fileName, tools.fs)];
12087
12151
  case 5:
12088
12152
  // TODO: Handle non-valid JSON files
12089
- pipeline = _e.apply(_d, [_f.sent()]);
12090
- // TODO: [🌗]
12091
- pipeline = __assign(__assign({}, pipeline), { sourceFile: sourceFile });
12153
+ _f.apply(_e, _g.concat([__spreadArray.apply(void 0, _h.concat([__read.apply(void 0, [(_j.sent()).map(function (pipeline) {
12154
+ // TODO: [🌗]
12155
+ return ({ fileName: fileName, sourceFile: sourceFile, pipeline: __assign(__assign({}, pipeline), { sourceFile: sourceFile }) });
12156
+ })]), false]))]));
12092
12157
  return [3 /*break*/, 7];
12093
12158
  case 6:
12094
12159
  if (isVerbose) {
12095
12160
  console.info(colors__default["default"].gray("Skipped file ".concat(fileName.split('\\').join('/'), " \u2013\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060 Not a book")));
12096
12161
  }
12097
- _f.label = 7;
12098
- case 7:
12099
- // ---
12100
- if (pipeline !== null) {
12101
- if (rootUrl !== undefined) {
12102
- if (pipeline.pipelineUrl === undefined) {
12103
- pipelineUrl = rootUrl + '/' + path.relative(rootPath, fileName).split('\\').join('/');
12104
- // console.log({ pipelineUrl, rootPath, rootUrl, fileName });
12105
- if (isVerbose) {
12106
- console.info(colors__default["default"].yellow("Implicitly set pipeline URL to ".concat(pipelineUrl, " from ").concat(fileName
12107
- .split('\\')
12108
- .join('/'))));
12109
- }
12110
- pipeline = __assign(__assign({}, pipeline), { pipelineUrl: pipelineUrl });
12111
- }
12112
- else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
12113
- throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is not a child of the root URL ").concat(rootUrl, " \uD83C\uDF4F\n\n File:\n ").concat(sourceFile || 'Unknown', "\n\n ")));
12114
- }
12115
- }
12116
- // TODO: [👠] DRY
12117
- if (pipeline.pipelineUrl === undefined) {
12118
- if (isVerbose) {
12119
- console.info(colors__default["default"].yellow("Can not load pipeline from ".concat(fileName
12120
- .split('\\')
12121
- .join('/'), " because of missing URL")));
12122
- }
12123
- }
12124
- else {
12125
- // Note: [🐨] Pipeline is checked multiple times
12126
- // TODO: Maybe once is enough BUT be sure to check it - better to check it multiple times than not at all
12127
- validatePipeline(pipeline);
12128
- if (
12129
- // TODO: [🐽] comparePipelines(pipeline1,pipeline2): 'IDENTICAL' |'IDENTICAL_UNPREPARED' | 'IDENTICAL_INTERFACE' | 'DIFFERENT'
12130
- !collection.has(pipeline.pipelineUrl)) {
12131
- if (isVerbose) {
12132
- console.info(colors__default["default"].green("Loaded pipeline ".concat(fileName.split('\\').join('/'), "\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060")));
12133
- }
12134
- // Note: [🦄] Pipeline with same url uniqueness will be double-checked automatically in SimplePipelineCollection
12135
- collection.set(pipeline.pipelineUrl, pipeline);
12136
- }
12137
- else if (pipelineJsonToString(unpreparePipeline(pipeline)) ===
12138
- pipelineJsonToString(unpreparePipeline(collection.get(pipeline.pipelineUrl)))) {
12139
- if (isVerbose) {
12140
- console.info(colors__default["default"].gray("Skipped pipeline ".concat(fileName
12141
- .split('\\')
12142
- .join('/'), " \u2013\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060 Already identical pipeline in the collection")));
12143
- }
12144
- }
12145
- else {
12146
- existing = collection.get(pipeline.pipelineUrl);
12147
- throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is already in the collection \uD83C\uDF4F\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
12148
- }
12149
- }
12150
- }
12151
- return [3 /*break*/, 9];
12162
+ _j.label = 7;
12163
+ case 7: return [3 /*break*/, 9];
12152
12164
  case 8:
12153
- error_1 = _f.sent();
12165
+ error_1 = _j.sent();
12154
12166
  if (!(error_1 instanceof Error)) {
12155
12167
  throw error_1;
12156
12168
  }
@@ -12165,24 +12177,24 @@
12165
12177
  }
12166
12178
  });
12167
12179
  };
12168
- _b.label = 2;
12180
+ _c.label = 2;
12169
12181
  case 2:
12170
- _b.trys.push([2, 7, 8, 9]);
12182
+ _c.trys.push([2, 7, 8, 9]);
12171
12183
  fileNames_1 = __values(fileNames), fileNames_1_1 = fileNames_1.next();
12172
- _b.label = 3;
12184
+ _c.label = 3;
12173
12185
  case 3:
12174
12186
  if (!!fileNames_1_1.done) return [3 /*break*/, 6];
12175
12187
  fileName = fileNames_1_1.value;
12176
12188
  return [5 /*yield**/, _loop_1(fileName)];
12177
12189
  case 4:
12178
- _b.sent();
12179
- _b.label = 5;
12190
+ _c.sent();
12191
+ _c.label = 5;
12180
12192
  case 5:
12181
12193
  fileNames_1_1 = fileNames_1.next();
12182
12194
  return [3 /*break*/, 3];
12183
12195
  case 6: return [3 /*break*/, 9];
12184
12196
  case 7:
12185
- e_1_1 = _b.sent();
12197
+ e_1_1 = _c.sent();
12186
12198
  e_1 = { error: e_1_1 };
12187
12199
  return [3 /*break*/, 9];
12188
12200
  case 8:
@@ -12191,7 +12203,86 @@
12191
12203
  }
12192
12204
  finally { if (e_1) throw e_1.error; }
12193
12205
  return [7 /*endfinally*/];
12194
- case 9: return [2 /*return*/, Array.from(collection.values())];
12206
+ case 9:
12207
+ _loop_2 = function (pipelineWithFilenames) {
12208
+ var fileName = pipelineWithFilenames.fileName, sourceFile = pipelineWithFilenames.sourceFile;
12209
+ var pipeline = pipelineWithFilenames.pipeline;
12210
+ try {
12211
+ if (rootUrl !== undefined) {
12212
+ if (pipeline.pipelineUrl === undefined) {
12213
+ var pipelineUrl = rootUrl + '/' + path.relative(rootPath, fileName).split('\\').join('/');
12214
+ // console.log({ pipelineUrl, rootPath, rootUrl, fileName });
12215
+ if (isVerbose) {
12216
+ console.info(colors__default["default"].yellow("Implicitly set pipeline URL to ".concat(pipelineUrl, " from ").concat(fileName
12217
+ .split('\\')
12218
+ .join('/'))));
12219
+ }
12220
+ pipeline = __assign(__assign({}, pipeline), { pipelineUrl: pipelineUrl });
12221
+ }
12222
+ else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
12223
+ throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is not a child of the root URL ").concat(rootUrl, " \uD83C\uDF4F\n\n File:\n ").concat(sourceFile || 'Unknown', "\n\n ")));
12224
+ }
12225
+ }
12226
+ // TODO: [👠] DRY
12227
+ if (pipeline.pipelineUrl === undefined) {
12228
+ if (isVerbose) {
12229
+ console.info(colors__default["default"].yellow("Can not load pipeline from ".concat(fileName.split('\\').join('/'), " because of missing URL")));
12230
+ }
12231
+ }
12232
+ else {
12233
+ // Note: [🐨] Pipeline is checked multiple times
12234
+ // TODO: Maybe once is enough BUT be sure to check it - better to check it multiple times than not at all
12235
+ validatePipeline(pipeline);
12236
+ if (
12237
+ // TODO: [🐽] comparePipelines(pipeline1,pipeline2): 'IDENTICAL' |'IDENTICAL_UNPREPARED' | 'IDENTICAL_INTERFACE' | 'DIFFERENT'
12238
+ !collection.has(pipeline.pipelineUrl)) {
12239
+ if (isVerbose) {
12240
+ console.info(colors__default["default"].green("Loaded pipeline ".concat(fileName.split('\\').join('/'), "\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060")));
12241
+ }
12242
+ // Note: [🦄] Pipeline with same url uniqueness will be double-checked automatically in SimplePipelineCollection
12243
+ collection.set(pipeline.pipelineUrl, pipeline);
12244
+ }
12245
+ else if (pipelineJsonToString(unpreparePipeline(pipeline)) ===
12246
+ pipelineJsonToString(unpreparePipeline(collection.get(pipeline.pipelineUrl)))) {
12247
+ if (isVerbose) {
12248
+ console.info(colors__default["default"].gray("Skipped pipeline ".concat(fileName
12249
+ .split('\\')
12250
+ .join('/'), " \u2013\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060 Already identical pipeline in the collection")));
12251
+ }
12252
+ }
12253
+ else {
12254
+ var existing = collection.get(pipeline.pipelineUrl);
12255
+ throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is already in the collection \uD83C\uDF4F\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
12256
+ }
12257
+ }
12258
+ }
12259
+ catch (error) {
12260
+ if (!(error instanceof Error)) {
12261
+ throw error;
12262
+ }
12263
+ // TODO: [7] DRY
12264
+ var wrappedErrorMessage = spaceTrim__default["default"](function (block) { return "\n ".concat(error.name, " in pipeline ").concat(fileName.split('\\').join('/'), "\u2060:\n\n Original error message:\n ").concat(block(error.message), "\n\n Original stack trace:\n ").concat(block(error.stack || ''), "\n\n ---\n\n "); }) + '\n';
12265
+ if (isCrashedOnError) {
12266
+ throw new CollectionError(wrappedErrorMessage);
12267
+ }
12268
+ // TODO: [🟥] Detect browser / node and make it colorfull
12269
+ console.error(wrappedErrorMessage);
12270
+ }
12271
+ };
12272
+ try {
12273
+ for (pipelinesWithFilenames_1 = __values(pipelinesWithFilenames), pipelinesWithFilenames_1_1 = pipelinesWithFilenames_1.next(); !pipelinesWithFilenames_1_1.done; pipelinesWithFilenames_1_1 = pipelinesWithFilenames_1.next()) {
12274
+ pipelineWithFilenames = pipelinesWithFilenames_1_1.value;
12275
+ _loop_2(pipelineWithFilenames);
12276
+ }
12277
+ }
12278
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
12279
+ finally {
12280
+ try {
12281
+ if (pipelinesWithFilenames_1_1 && !pipelinesWithFilenames_1_1.done && (_b = pipelinesWithFilenames_1.return)) _b.call(pipelinesWithFilenames_1);
12282
+ }
12283
+ finally { if (e_2) throw e_2.error; }
12284
+ }
12285
+ return [2 /*return*/, Array.from(collection.values())];
12195
12286
  }
12196
12287
  });
12197
12288
  }); });
@@ -12210,6 +12301,59 @@
12210
12301
  * TODO: Maybe move from `@promptbook/node` to `@promptbook/core` as we removes direct dependency on `fs`
12211
12302
  */
12212
12303
 
12304
+ /**
12305
+ * Saves the given books into an archive file with `.bookc` extension
12306
+ *
12307
+ * @param filePath Path to the archive file with `.bookc` extension
12308
+ * @param books Pipelines to be saved in the archive
12309
+ * @param fs Filesystem tools
12310
+ *
12311
+ * @private utility of Prompbook
12312
+ */
12313
+ function saveArchive(filePath, collectionJson, fs) {
12314
+ return __awaiter(this, void 0, void 0, function () {
12315
+ var collectionJson_1, collectionJson_1_1, pipelineJson, archive, collectionJsonString, data;
12316
+ var e_1, _a;
12317
+ return __generator(this, function (_b) {
12318
+ switch (_b.label) {
12319
+ case 0:
12320
+ if (!filePath.endsWith('.bookc')) {
12321
+ throw new UnexpectedError("Archive file must have '.bookc' extension");
12322
+ }
12323
+ try {
12324
+ // Note: We want to ensure that the generated JSONs are logically correct
12325
+ for (collectionJson_1 = __values(collectionJson), collectionJson_1_1 = collectionJson_1.next(); !collectionJson_1_1.done; collectionJson_1_1 = collectionJson_1.next()) {
12326
+ pipelineJson = collectionJson_1_1.value;
12327
+ validatePipeline(pipelineJson);
12328
+ }
12329
+ }
12330
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
12331
+ finally {
12332
+ try {
12333
+ if (collectionJson_1_1 && !collectionJson_1_1.done && (_a = collectionJson_1.return)) _a.call(collectionJson_1);
12334
+ }
12335
+ finally { if (e_1) throw e_1.error; }
12336
+ }
12337
+ archive = new JSZip__default["default"]();
12338
+ collectionJsonString = stringifyPipelineJson(collectionJson);
12339
+ archive.file('index.book.json', collectionJsonString);
12340
+ return [4 /*yield*/, archive.generateAsync({ type: 'nodebuffer', streamFiles: true })];
12341
+ case 1:
12342
+ data = _b.sent();
12343
+ return [4 /*yield*/, fs.writeFile(filePath, data)];
12344
+ case 2:
12345
+ _b.sent();
12346
+ return [2 /*return*/];
12347
+ }
12348
+ });
12349
+ });
12350
+ }
12351
+ /**
12352
+ * TODO: Add metadata to zip
12353
+ * TODO: Compression level and other zip options from config
12354
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
12355
+ */
12356
+
12213
12357
  /**
12214
12358
  * Function usageToWorktime will take usage and estimate saved worktime in hours of reading / writing
12215
12359
  *
@@ -12305,7 +12449,7 @@
12305
12449
  makeCommand.action(function (path$1, _a) {
12306
12450
  var projectName = _a.projectName, rootUrl = _a.rootUrl, format = _a.format, functionName = _a.functionName, validation = _a.validation, isCacheReloaded = _a.reload, isVerbose = _a.verbose, output = _a.output;
12307
12451
  return __awaiter(_this, void 0, void 0, function () {
12308
- var formats, validations, prepareAndScrapeOptions, fs, llm, executables, tools, collection, pipelinesUrls, validations_1, validations_1_1, validation_1, pipelinesUrls_1, pipelinesUrls_1_1, pipelineUrl, pipeline, e_1_1, e_2_1, collectionJson, collectionJsonString, collectionJsonItems, saveFile, bookcBundle;
12452
+ var formats, validations, prepareAndScrapeOptions, fs, llm, executables, tools, collection, pipelinesUrls, validations_1, validations_1_1, validation_1, pipelinesUrls_1, pipelinesUrls_1_1, pipelineUrl, pipeline, e_1_1, e_2_1, collectionJson, collectionJsonString, collectionJsonItems, saveFile;
12309
12453
  var _b, e_2, _c, e_1, _d;
12310
12454
  var _this = this;
12311
12455
  return __generator(this, function (_e) {
@@ -12441,7 +12585,7 @@
12441
12585
  return spaceTrim__default["default"](collectionJsonString.substring(1, collectionJsonString.length - 1));
12442
12586
  })();
12443
12587
  saveFile = function (extension, content) { return __awaiter(_this, void 0, void 0, function () {
12444
- var filename, data;
12588
+ var filename;
12445
12589
  return __generator(this, function (_a) {
12446
12590
  switch (_a.label) {
12447
12591
  case 0:
@@ -12458,15 +12602,12 @@
12458
12602
  return [4 /*yield*/, promises.writeFile(filename, content, 'utf-8')];
12459
12603
  case 2:
12460
12604
  _a.sent();
12461
- return [3 /*break*/, 6];
12462
- case 3: return [4 /*yield*/, content.generateAsync({ type: 'nodebuffer', streamFiles: true })];
12605
+ return [3 /*break*/, 5];
12606
+ case 3: return [4 /*yield*/, saveArchive(filename, content, fs)];
12463
12607
  case 4:
12464
- data = _a.sent();
12465
- return [4 /*yield*/, promises.writeFile(filename, data)];
12466
- case 5:
12467
12608
  _a.sent();
12468
- _a.label = 6;
12469
- case 6:
12609
+ _a.label = 5;
12610
+ case 5:
12470
12611
  // Note: Log despite of verbose mode
12471
12612
  console.info(colors__default["default"].green("Made ".concat(filename.split('\\').join('/'))));
12472
12613
  return [2 /*return*/];
@@ -12475,9 +12616,7 @@
12475
12616
  }); };
12476
12617
  if (!formats.includes('bookc')) return [3 /*break*/, 22];
12477
12618
  formats = formats.filter(function (format) { return format !== 'bookc'; });
12478
- bookcBundle = new JSZip__default["default"]();
12479
- bookcBundle.file('index.book.json', collectionJsonString);
12480
- return [4 /*yield*/, saveFile('bookc', bookcBundle)];
12619
+ return [4 /*yield*/, saveFile('bookc', collectionJson)];
12481
12620
  case 21:
12482
12621
  _e.sent();
12483
12622
  _e.label = 22;
@@ -13029,31 +13168,31 @@
13029
13168
  */
13030
13169
  function $getCompiledBook(tools, pipelineSource, options) {
13031
13170
  return __awaiter(this, void 0, void 0, function () {
13032
- var fs, fetch, filePathRaw, filePath, filePathCandidates, filePathCandidates_1, filePathCandidates_1_1, filePathCandidate, pipelineString, _a, pipelineJson, compiledFilePath, bookcBundle, data, e_1_1, rootDirname, _loop_1, i, state_1, response_1, pipelineString, pipelineJson, pipelineJson;
13171
+ var fs, fetch, filePathRaw, filePath, filePathCandidates, filePathCandidates_1, filePathCandidates_1_1, filePathCandidate, pipelineString, _a, pipelineJson, compiledFilePath, e_1_1, rootDirname, _loop_1, i, state_1, response_1, pipelineString, pipelineJson, pipelineJson;
13033
13172
  var e_1, _b;
13034
13173
  var _this = this;
13035
13174
  return __generator(this, function (_c) {
13036
13175
  switch (_c.label) {
13037
13176
  case 0:
13038
13177
  fs = tools.fs, fetch = tools.fetch;
13039
- if (!isValidFilePath(pipelineSource)) return [3 /*break*/, 12];
13178
+ if (!isValidFilePath(pipelineSource)) return [3 /*break*/, 11];
13040
13179
  filePathRaw = pipelineSource;
13041
13180
  filePath = null;
13042
13181
  filePathCandidates = [filePathRaw, "".concat(filePathRaw, ".md"), "".concat(filePathRaw, ".book"), "".concat(filePathRaw, ".book")];
13043
13182
  filePathCandidates = __spreadArray(__spreadArray([], __read(filePathCandidates), false), __read(filePathCandidates.map(function (path) { return path.split('\\').join('/'); })), false);
13044
13183
  _c.label = 1;
13045
13184
  case 1:
13046
- _c.trys.push([1, 10, 11, 12]);
13185
+ _c.trys.push([1, 9, 10, 11]);
13047
13186
  filePathCandidates_1 = __values(filePathCandidates), filePathCandidates_1_1 = filePathCandidates_1.next();
13048
13187
  _c.label = 2;
13049
13188
  case 2:
13050
- if (!!filePathCandidates_1_1.done) return [3 /*break*/, 9];
13189
+ if (!!filePathCandidates_1_1.done) return [3 /*break*/, 8];
13051
13190
  filePathCandidate = filePathCandidates_1_1.value;
13052
13191
  return [4 /*yield*/, isFileExisting(filePathCandidate, fs)
13053
13192
  // <- TODO: Also test that among the candidates the file is book not just any file
13054
13193
  ];
13055
13194
  case 3:
13056
- if (!_c.sent()) return [3 /*break*/, 8];
13195
+ if (!_c.sent()) return [3 /*break*/, 7];
13057
13196
  filePath = filePathCandidate;
13058
13197
  _a = validatePipelineString;
13059
13198
  return [4 /*yield*/, fs.readFile(filePath, 'utf-8')];
@@ -13063,31 +13202,26 @@
13063
13202
  case 5:
13064
13203
  pipelineJson = _c.sent();
13065
13204
  compiledFilePath = filePath.replace('.book.md', '.book').replace('.book', '.bookc');
13066
- bookcBundle = new JSZip__default["default"]();
13067
- bookcBundle.file('index.book.json', JSON.stringify([pipelineJson]));
13068
- return [4 /*yield*/, bookcBundle.generateAsync({ type: 'nodebuffer', streamFiles: true })];
13205
+ return [4 /*yield*/, saveArchive(compiledFilePath, [pipelineJson], fs)];
13069
13206
  case 6:
13070
- data = _c.sent();
13071
- return [4 /*yield*/, fs.writeFile(compiledFilePath, data)];
13072
- case 7:
13073
13207
  _c.sent();
13074
13208
  return [2 /*return*/, pipelineJson];
13075
- case 8:
13209
+ case 7:
13076
13210
  filePathCandidates_1_1 = filePathCandidates_1.next();
13077
13211
  return [3 /*break*/, 2];
13078
- case 9: return [3 /*break*/, 12];
13079
- case 10:
13212
+ case 8: return [3 /*break*/, 11];
13213
+ case 9:
13080
13214
  e_1_1 = _c.sent();
13081
13215
  e_1 = { error: e_1_1 };
13082
- return [3 /*break*/, 12];
13083
- case 11:
13216
+ return [3 /*break*/, 11];
13217
+ case 10:
13084
13218
  try {
13085
13219
  if (filePathCandidates_1_1 && !filePathCandidates_1_1.done && (_b = filePathCandidates_1.return)) _b.call(filePathCandidates_1);
13086
13220
  }
13087
13221
  finally { if (e_1) throw e_1.error; }
13088
13222
  return [7 /*endfinally*/];
13089
- case 12:
13090
- if (!isValidPipelineUrl(pipelineSource)) return [3 /*break*/, 16];
13223
+ case 11:
13224
+ if (!isValidPipelineUrl(pipelineSource)) return [3 /*break*/, 15];
13091
13225
  rootDirname = process.cwd();
13092
13226
  _loop_1 = function (i) {
13093
13227
  var booksDirname, collection_1, pipeline;
@@ -13138,47 +13272,47 @@
13138
13272
  });
13139
13273
  };
13140
13274
  i = 0;
13141
- _c.label = 13;
13142
- case 13:
13143
- if (!(i < LOOP_LIMIT)) return [3 /*break*/, 16];
13275
+ _c.label = 12;
13276
+ case 12:
13277
+ if (!(i < LOOP_LIMIT)) return [3 /*break*/, 15];
13144
13278
  return [5 /*yield**/, _loop_1(i)];
13145
- case 14:
13279
+ case 13:
13146
13280
  state_1 = _c.sent();
13147
13281
  if (typeof state_1 === "object")
13148
13282
  return [2 /*return*/, state_1.value];
13149
13283
  switch (state_1) {
13150
- case "break-up_to_root": return [3 /*break*/, 16];
13284
+ case "break-up_to_root": return [3 /*break*/, 15];
13151
13285
  }
13152
- _c.label = 15;
13153
- case 15:
13286
+ _c.label = 14;
13287
+ case 14:
13154
13288
  i++;
13155
- return [3 /*break*/, 13];
13156
- case 16:
13157
- if (!isValidPipelineUrl(pipelineSource)) return [3 /*break*/, 20];
13289
+ return [3 /*break*/, 12];
13290
+ case 15:
13291
+ if (!isValidPipelineUrl(pipelineSource)) return [3 /*break*/, 19];
13158
13292
  return [4 /*yield*/, fetch(pipelineSource)];
13159
- case 17:
13293
+ case 16:
13160
13294
  response_1 = _c.sent();
13161
13295
  if (response_1.status >= 300) {
13162
13296
  throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Book not found on URL:\n ".concat(block(pipelineSource), "\n\n Request failed with status ").concat(block(response_1.status.toString()), " ").concat(block(response_1.statusText), "\n "); }));
13163
13297
  }
13164
13298
  return [4 /*yield*/, response_1.text()];
13165
- case 18:
13299
+ case 17:
13166
13300
  pipelineString = _c.sent();
13167
13301
  // console.log({ pipelineString });
13168
13302
  if (!isValidPipelineString(pipelineString)) {
13169
13303
  throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Book not found on URL:\n ".concat(block(pipelineSource), "\n\n Requested URL does not seem to contain a valid book\n "); }));
13170
13304
  }
13171
13305
  return [4 /*yield*/, compilePipeline(pipelineString, tools, __assign({ rootDirname: null }, options))];
13172
- case 19:
13306
+ case 18:
13173
13307
  pipelineJson = _c.sent();
13174
13308
  return [2 /*return*/, pipelineJson];
13175
- case 20:
13176
- if (!isValidPipelineString(pipelineSource)) return [3 /*break*/, 22];
13309
+ case 19:
13310
+ if (!isValidPipelineString(pipelineSource)) return [3 /*break*/, 21];
13177
13311
  return [4 /*yield*/, compilePipeline(pipelineSource, tools, __assign({ rootDirname: null }, options))];
13178
- case 21:
13312
+ case 20:
13179
13313
  pipelineJson = _c.sent();
13180
13314
  return [2 /*return*/, pipelineJson];
13181
- case 22: /* not else */ throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Book not found:\n ".concat(block(pipelineSource), "\n\n Pipelines can be loaded from:\n 1) As a file ./books/write-cv.book\n 2) As a URL https://promptbook.studio/hejny/write-cv.book found in ./books folder recursively\n 2) As a URL https://promptbook.studio/hejny/write-cv.book fetched from the internet\n 3) As a string\n\n\n "); }));
13315
+ case 21: /* not else */ throw new NotFoundError(spaceTrim__default["default"](function (block) { return "\n Book not found:\n ".concat(block(pipelineSource), "\n\n Pipelines can be loaded from:\n 1) As a file ./books/write-cv.book\n 2) As a URL https://promptbook.studio/hejny/write-cv.book found in ./books folder recursively\n 2) As a URL https://promptbook.studio/hejny/write-cv.book fetched from the internet\n 3) As a string\n\n\n "); }));
13182
13316
  }
13183
13317
  });
13184
13318
  });