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