@promptbook/node 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/README.md +3 -4
- package/esm/index.es.js +1079 -988
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/conversion/archive/loadArchive.d.ts +16 -0
- package/esm/typings/src/conversion/archive/saveArchive.d.ts +18 -0
- package/esm/typings/src/conversion/validation/_importPipeline.d.ts +3 -10
- package/package.json +3 -2
- package/umd/index.umd.js +1083 -992
- package/umd/index.umd.js.map +1 -1
- package/esm/typings/src/utils/editable/utils/stringifyPipelineJson.test.d.ts +0 -1
package/umd/index.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('fs/promises'), require('path'), require('spacetrim'), require('prettier'), require('prettier/parser-html'), require('rxjs'), require('crypto'), require('waitasecond'), require('papaparse'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('crypto-js'), require('mime-types'), require('child_process'), require('dotenv')) :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', 'colors', 'fs/promises', 'path', 'spacetrim', 'prettier', 'prettier/parser-html', 'rxjs', 'crypto', 'waitasecond', 'papaparse', 'crypto-js/enc-hex', 'crypto-js/sha256', 'crypto-js', 'mime-types', 'child_process', 'dotenv'], factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-node"] = {}, global.colors, global.promises, global.path, global.spaceTrim, global.prettier, global.parserHtml, global.rxjs, global.crypto, global.waitasecond, global.papaparse, global.hexEncoder, global.sha256, global.cryptoJs, global.mimeTypes, global.child_process, global.dotenv));
|
|
5
|
-
})(this, (function (exports, colors, promises, path, spaceTrim, prettier, parserHtml, rxjs, crypto, waitasecond, papaparse, hexEncoder, sha256, cryptoJs, mimeTypes, child_process, dotenv) { 'use strict';
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('fs/promises'), require('path'), require('spacetrim'), require('jszip'), require('prettier'), require('prettier/parser-html'), require('rxjs'), require('crypto'), require('waitasecond'), require('papaparse'), require('crypto-js/enc-hex'), require('crypto-js/sha256'), require('crypto-js'), require('mime-types'), require('child_process'), require('dotenv')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'colors', 'fs/promises', 'path', 'spacetrim', 'jszip', 'prettier', 'prettier/parser-html', 'rxjs', 'crypto', 'waitasecond', 'papaparse', 'crypto-js/enc-hex', 'crypto-js/sha256', 'crypto-js', 'mime-types', 'child_process', 'dotenv'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-node"] = {}, global.colors, global.promises, global.path, global.spaceTrim, global.JSZip, global.prettier, global.parserHtml, global.rxjs, global.crypto, global.waitasecond, global.papaparse, global.hexEncoder, global.sha256, global.cryptoJs, global.mimeTypes, global.child_process, global.dotenv));
|
|
5
|
+
})(this, (function (exports, colors, promises, path, spaceTrim, JSZip, prettier, parserHtml, rxjs, crypto, waitasecond, papaparse, hexEncoder, sha256, cryptoJs, mimeTypes, child_process, dotenv) { 'use strict';
|
|
6
6
|
|
|
7
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
8
|
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
var colors__default = /*#__PURE__*/_interopDefaultLegacy(colors);
|
|
28
28
|
var spaceTrim__default = /*#__PURE__*/_interopDefaultLegacy(spaceTrim);
|
|
29
|
+
var JSZip__default = /*#__PURE__*/_interopDefaultLegacy(JSZip);
|
|
29
30
|
var parserHtml__default = /*#__PURE__*/_interopDefaultLegacy(parserHtml);
|
|
30
31
|
var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
|
|
31
32
|
var sha256__default = /*#__PURE__*/_interopDefaultLegacy(sha256);
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
* @generated
|
|
46
47
|
* @see https://github.com/webgptorg/promptbook
|
|
47
48
|
*/
|
|
48
|
-
var PROMPTBOOK_ENGINE_VERSION = '0.86.
|
|
49
|
+
var PROMPTBOOK_ENGINE_VERSION = '0.86.10';
|
|
49
50
|
/**
|
|
50
51
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
51
52
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -353,1119 +354,1174 @@
|
|
|
353
354
|
* TODO: [🧠][🧜♂️] Maybe join remoteUrl and path into single value
|
|
354
355
|
*/
|
|
355
356
|
|
|
356
|
-
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"}];
|
|
357
|
-
|
|
358
357
|
/**
|
|
359
|
-
*
|
|
358
|
+
* Make error report URL for the given error
|
|
360
359
|
*
|
|
361
|
-
* @
|
|
360
|
+
* @private private within the repository
|
|
362
361
|
*/
|
|
363
|
-
function
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
362
|
+
function getErrorReportUrl(error) {
|
|
363
|
+
var report = {
|
|
364
|
+
title: "\uD83D\uDC1C Error report from ".concat(NAME),
|
|
365
|
+
body: spaceTrim__default["default"](function (block) { return "\n\n\n `".concat(error.name || 'Error', "` has occurred in the [").concat(NAME, "], please look into it @").concat(ADMIN_GITHUB_NAME, ".\n\n ```\n ").concat(block(error.message || '(no error message)'), "\n ```\n\n\n ## More info:\n\n - **Promptbook engine version:** ").concat(PROMPTBOOK_ENGINE_VERSION, "\n - **Book language version:** ").concat(BOOK_LANGUAGE_VERSION, "\n - **Time:** ").concat(new Date().toISOString(), "\n\n <details>\n <summary>Stack trace:</summary>\n\n ## Stack trace:\n\n ```stacktrace\n ").concat(block(error.stack || '(empty)'), "\n ```\n </details>\n\n "); }),
|
|
366
|
+
};
|
|
367
|
+
var reportUrl = new URL("https://github.com/webgptorg/promptbook/issues/new");
|
|
368
|
+
reportUrl.searchParams.set('labels', 'bug');
|
|
369
|
+
reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
|
|
370
|
+
reportUrl.searchParams.set('title', report.title);
|
|
371
|
+
reportUrl.searchParams.set('body', report.body);
|
|
372
|
+
return reportUrl;
|
|
371
373
|
}
|
|
372
374
|
|
|
373
375
|
/**
|
|
374
|
-
*
|
|
376
|
+
* This error type indicates that the error should not happen and its last check before crashing with some other error
|
|
375
377
|
*
|
|
376
|
-
*
|
|
377
|
-
* @public exported from `@promptbook/utils`
|
|
378
|
+
* @public exported from `@promptbook/core`
|
|
378
379
|
*/
|
|
379
|
-
function
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (filename.split(' ').length >
|
|
387
|
-
5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
|
|
388
|
-
return false;
|
|
389
|
-
}
|
|
390
|
-
var filenameSlashes = filename.split('\\').join('/');
|
|
391
|
-
// Absolute Unix path: /hello.txt
|
|
392
|
-
if (/^(\/)/i.test(filenameSlashes)) {
|
|
393
|
-
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
394
|
-
return true;
|
|
395
|
-
}
|
|
396
|
-
// Absolute Windows path: /hello.txt
|
|
397
|
-
if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
|
|
398
|
-
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
399
|
-
return true;
|
|
400
|
-
}
|
|
401
|
-
// Relative path: ./hello.txt
|
|
402
|
-
if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
|
|
403
|
-
// console.log(filename, 'Relative path: ./hello.txt');
|
|
404
|
-
return true;
|
|
405
|
-
}
|
|
406
|
-
// Allow paths like foo/hello
|
|
407
|
-
if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
|
|
408
|
-
// console.log(filename, 'Allow paths like foo/hello');
|
|
409
|
-
return true;
|
|
410
|
-
}
|
|
411
|
-
// Allow paths like hello.book
|
|
412
|
-
if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
|
|
413
|
-
// console.log(filename, 'Allow paths like hello.book');
|
|
414
|
-
return true;
|
|
380
|
+
var UnexpectedError = /** @class */ (function (_super) {
|
|
381
|
+
__extends(UnexpectedError, _super);
|
|
382
|
+
function UnexpectedError(message) {
|
|
383
|
+
var _this = _super.call(this, spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(message), "\n\n Note: This error should not happen.\n It's probbably a bug in the pipeline collection\n\n Please report issue:\n ").concat(block(getErrorReportUrl(new Error(message)).href), "\n\n Or contact us on ").concat(ADMIN_EMAIL, "\n\n "); })) || this;
|
|
384
|
+
_this.name = 'UnexpectedError';
|
|
385
|
+
Object.setPrototypeOf(_this, UnexpectedError.prototype);
|
|
386
|
+
return _this;
|
|
415
387
|
}
|
|
416
|
-
return
|
|
417
|
-
}
|
|
388
|
+
return UnexpectedError;
|
|
389
|
+
}(Error));
|
|
390
|
+
|
|
418
391
|
/**
|
|
419
|
-
*
|
|
392
|
+
* Orders JSON object by keys
|
|
393
|
+
*
|
|
394
|
+
* @returns The same type of object as the input re-ordered
|
|
395
|
+
* @public exported from `@promptbook/utils`
|
|
420
396
|
*/
|
|
397
|
+
function orderJson(options) {
|
|
398
|
+
var value = options.value, order = options.order;
|
|
399
|
+
var orderedValue = __assign(__assign({}, (order === undefined ? {} : Object.fromEntries(order.map(function (key) { return [key, undefined]; })))), value);
|
|
400
|
+
return orderedValue;
|
|
401
|
+
}
|
|
421
402
|
|
|
422
403
|
/**
|
|
423
|
-
*
|
|
404
|
+
* Freezes the given object and all its nested objects recursively
|
|
424
405
|
*
|
|
425
|
-
* Note:
|
|
426
|
-
* Note:
|
|
427
|
-
* - `isValidUrl` which tests any URL
|
|
428
|
-
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
406
|
+
* Note: `$` is used to indicate that this function is not a pure function - it mutates given object
|
|
407
|
+
* Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
|
|
429
408
|
*
|
|
409
|
+
* @returns The same object as the input, but deeply frozen
|
|
430
410
|
* @public exported from `@promptbook/utils`
|
|
431
411
|
*/
|
|
432
|
-
function
|
|
433
|
-
|
|
434
|
-
|
|
412
|
+
function $deepFreeze(objectValue) {
|
|
413
|
+
var e_1, _a;
|
|
414
|
+
if (Array.isArray(objectValue)) {
|
|
415
|
+
return Object.freeze(objectValue.map(function (item) { return $deepFreeze(item); }));
|
|
435
416
|
}
|
|
417
|
+
var propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
436
418
|
try {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
419
|
+
for (var propertyNames_1 = __values(propertyNames), propertyNames_1_1 = propertyNames_1.next(); !propertyNames_1_1.done; propertyNames_1_1 = propertyNames_1.next()) {
|
|
420
|
+
var propertyName = propertyNames_1_1.value;
|
|
421
|
+
var value = objectValue[propertyName];
|
|
422
|
+
if (value && typeof value === 'object') {
|
|
423
|
+
$deepFreeze(value);
|
|
424
|
+
}
|
|
443
425
|
}
|
|
444
|
-
return true;
|
|
445
426
|
}
|
|
446
|
-
catch (
|
|
447
|
-
|
|
427
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
428
|
+
finally {
|
|
429
|
+
try {
|
|
430
|
+
if (propertyNames_1_1 && !propertyNames_1_1.done && (_a = propertyNames_1.return)) _a.call(propertyNames_1);
|
|
431
|
+
}
|
|
432
|
+
finally { if (e_1) throw e_1.error; }
|
|
448
433
|
}
|
|
434
|
+
Object.freeze(objectValue);
|
|
435
|
+
return objectValue;
|
|
449
436
|
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
453
|
-
*
|
|
454
|
-
* @public exported from `@promptbook/core`
|
|
455
|
-
*/
|
|
456
|
-
var ParseError = /** @class */ (function (_super) {
|
|
457
|
-
__extends(ParseError, _super);
|
|
458
|
-
function ParseError(message) {
|
|
459
|
-
var _this = _super.call(this, message) || this;
|
|
460
|
-
_this.name = 'ParseError';
|
|
461
|
-
Object.setPrototypeOf(_this, ParseError.prototype);
|
|
462
|
-
return _this;
|
|
463
|
-
}
|
|
464
|
-
return ParseError;
|
|
465
|
-
}(Error));
|
|
466
437
|
/**
|
|
467
|
-
* TODO:
|
|
438
|
+
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
468
439
|
*/
|
|
469
440
|
|
|
470
441
|
/**
|
|
471
|
-
*
|
|
442
|
+
* Checks if the value is [🚉] serializable as JSON
|
|
443
|
+
* If not, throws an UnexpectedError with a rich error message and tracking
|
|
444
|
+
*
|
|
445
|
+
* - Almost all primitives are serializable BUT:
|
|
446
|
+
* - `undefined` is not serializable
|
|
447
|
+
* - `NaN` is not serializable
|
|
448
|
+
* - Objects and arrays are serializable if all their properties are serializable
|
|
449
|
+
* - Functions are not serializable
|
|
450
|
+
* - Circular references are not serializable
|
|
451
|
+
* - `Date` objects are not serializable
|
|
452
|
+
* - `Map` and `Set` objects are not serializable
|
|
453
|
+
* - `RegExp` objects are not serializable
|
|
454
|
+
* - `Error` objects are not serializable
|
|
455
|
+
* - `Symbol` objects are not serializable
|
|
456
|
+
* - And much more...
|
|
472
457
|
*
|
|
458
|
+
* @throws UnexpectedError if the value is not serializable as JSON
|
|
473
459
|
* @public exported from `@promptbook/utils`
|
|
474
460
|
*/
|
|
475
|
-
function
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
461
|
+
function checkSerializableAsJson(options) {
|
|
462
|
+
var e_1, _a;
|
|
463
|
+
var value = options.value, name = options.name, message = options.message;
|
|
464
|
+
if (value === undefined) {
|
|
465
|
+
throw new UnexpectedError("".concat(name, " is undefined"));
|
|
479
466
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
throw error;
|
|
483
|
-
}
|
|
484
|
-
if (error.message.includes('Unexpected token')) {
|
|
485
|
-
return false;
|
|
486
|
-
}
|
|
487
|
-
return false;
|
|
467
|
+
else if (value === null) {
|
|
468
|
+
return;
|
|
488
469
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Function `validatePipelineString` will validate the if the string is a valid pipeline string
|
|
493
|
-
* 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.
|
|
494
|
-
*
|
|
495
|
-
* @param {string} pipelineString the candidate for a pipeline string
|
|
496
|
-
* @returns {PipelineString} the same string as input, but validated as valid
|
|
497
|
-
* @throws {ParseError} if the string is not a valid pipeline string
|
|
498
|
-
* @public exported from `@promptbook/core`
|
|
499
|
-
*/
|
|
500
|
-
function validatePipelineString(pipelineString) {
|
|
501
|
-
if (isValidJsonString(pipelineString)) {
|
|
502
|
-
throw new ParseError('Expected a book, but got a JSON string');
|
|
470
|
+
else if (typeof value === 'boolean') {
|
|
471
|
+
return;
|
|
503
472
|
}
|
|
504
|
-
else if (
|
|
505
|
-
|
|
473
|
+
else if (typeof value === 'number' && !isNaN(value)) {
|
|
474
|
+
return;
|
|
506
475
|
}
|
|
507
|
-
else if (
|
|
508
|
-
|
|
476
|
+
else if (typeof value === 'string') {
|
|
477
|
+
return;
|
|
509
478
|
}
|
|
510
|
-
else if (
|
|
511
|
-
throw new
|
|
479
|
+
else if (typeof value === 'symbol') {
|
|
480
|
+
throw new UnexpectedError("".concat(name, " is symbol"));
|
|
512
481
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* TODO: [🧠][🈴] Where is the best location for this file
|
|
518
|
-
*/
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* Prettify the html code
|
|
522
|
-
*
|
|
523
|
-
* @param content raw html code
|
|
524
|
-
* @returns formatted html code
|
|
525
|
-
* @private withing the package because of HUGE size of prettier dependency
|
|
526
|
-
*/
|
|
527
|
-
function prettifyMarkdown(content) {
|
|
528
|
-
try {
|
|
529
|
-
return prettier.format(content, {
|
|
530
|
-
parser: 'markdown',
|
|
531
|
-
plugins: [parserHtml__default["default"]],
|
|
532
|
-
// TODO: DRY - make some import or auto-copy of .prettierrc
|
|
533
|
-
endOfLine: 'lf',
|
|
534
|
-
tabWidth: 4,
|
|
535
|
-
singleQuote: true,
|
|
536
|
-
trailingComma: 'all',
|
|
537
|
-
arrowParens: 'always',
|
|
538
|
-
printWidth: 120,
|
|
539
|
-
htmlWhitespaceSensitivity: 'ignore',
|
|
540
|
-
jsxBracketSameLine: false,
|
|
541
|
-
bracketSpacing: true,
|
|
542
|
-
});
|
|
482
|
+
else if (typeof value === 'function') {
|
|
483
|
+
throw new UnexpectedError("".concat(name, " is function"));
|
|
543
484
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
html: content,
|
|
549
|
-
});
|
|
550
|
-
return content;
|
|
485
|
+
else if (typeof value === 'object' && Array.isArray(value)) {
|
|
486
|
+
for (var i = 0; i < value.length; i++) {
|
|
487
|
+
checkSerializableAsJson({ name: "".concat(name, "[").concat(i, "]"), value: value[i], message: message });
|
|
488
|
+
}
|
|
551
489
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
* Makes first letter of a string uppercase
|
|
556
|
-
*
|
|
557
|
-
* @public exported from `@promptbook/utils`
|
|
558
|
-
*/
|
|
559
|
-
function capitalize(word) {
|
|
560
|
-
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Converts promptbook in JSON format to string format
|
|
565
|
-
*
|
|
566
|
-
* @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
|
|
567
|
-
* @param pipelineJson Promptbook in JSON format (.bookc)
|
|
568
|
-
* @returns Promptbook in string format (.book.md)
|
|
569
|
-
* @public exported from `@promptbook/core`
|
|
570
|
-
*/
|
|
571
|
-
function pipelineJsonToString(pipelineJson) {
|
|
572
|
-
var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
|
|
573
|
-
var title = pipelineJson.title, pipelineUrl = pipelineJson.pipelineUrl, bookVersion = pipelineJson.bookVersion, description = pipelineJson.description, parameters = pipelineJson.parameters, tasks = pipelineJson.tasks;
|
|
574
|
-
var pipelineString = "# ".concat(title);
|
|
575
|
-
if (description) {
|
|
576
|
-
pipelineString += '\n\n';
|
|
577
|
-
pipelineString += description;
|
|
578
|
-
}
|
|
579
|
-
var commands = [];
|
|
580
|
-
if (pipelineUrl) {
|
|
581
|
-
commands.push("PIPELINE URL ".concat(pipelineUrl));
|
|
582
|
-
}
|
|
583
|
-
if (bookVersion !== "undefined") {
|
|
584
|
-
commands.push("BOOK VERSION ".concat(bookVersion));
|
|
585
|
-
}
|
|
586
|
-
// TODO: [main] !!5 This increases size of the bundle and is probbably not necessary
|
|
587
|
-
pipelineString = prettifyMarkdown(pipelineString);
|
|
588
|
-
try {
|
|
589
|
-
for (var _g = __values(parameters.filter(function (_a) {
|
|
590
|
-
var isInput = _a.isInput;
|
|
591
|
-
return isInput;
|
|
592
|
-
})), _h = _g.next(); !_h.done; _h = _g.next()) {
|
|
593
|
-
var parameter = _h.value;
|
|
594
|
-
commands.push("INPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
|
|
490
|
+
else if (typeof value === 'object') {
|
|
491
|
+
if (value instanceof Date) {
|
|
492
|
+
throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is Date\n\n Use `string_date_iso8601` instead\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
|
|
595
493
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
finally {
|
|
599
|
-
try {
|
|
600
|
-
if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
|
|
494
|
+
else if (value instanceof Map) {
|
|
495
|
+
throw new UnexpectedError("".concat(name, " is Map"));
|
|
601
496
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
try {
|
|
605
|
-
for (var _j = __values(parameters.filter(function (_a) {
|
|
606
|
-
var isOutput = _a.isOutput;
|
|
607
|
-
return isOutput;
|
|
608
|
-
})), _k = _j.next(); !_k.done; _k = _j.next()) {
|
|
609
|
-
var parameter = _k.value;
|
|
610
|
-
commands.push("OUTPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
|
|
497
|
+
else if (value instanceof Set) {
|
|
498
|
+
throw new UnexpectedError("".concat(name, " is Set"));
|
|
611
499
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
finally {
|
|
615
|
-
try {
|
|
616
|
-
if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
|
|
500
|
+
else if (value instanceof RegExp) {
|
|
501
|
+
throw new UnexpectedError("".concat(name, " is RegExp"));
|
|
617
502
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
pipelineString += '\n\n';
|
|
631
|
-
pipelineString += "## ".concat(title_1);
|
|
632
|
-
if (description_1) {
|
|
633
|
-
pipelineString += '\n\n';
|
|
634
|
-
pipelineString += description_1;
|
|
635
|
-
}
|
|
636
|
-
var commands_1 = [];
|
|
637
|
-
var contentLanguage = 'text';
|
|
638
|
-
if (taskType === 'PROMPT_TASK') {
|
|
639
|
-
var modelRequirements = task.modelRequirements;
|
|
640
|
-
var _l = modelRequirements || {}, modelName = _l.modelName, modelVariant = _l.modelVariant;
|
|
641
|
-
// Note: Do nothing, it is default
|
|
642
|
-
// commands.push(`PROMPT`);
|
|
643
|
-
if (modelVariant) {
|
|
644
|
-
commands_1.push("MODEL VARIANT ".concat(capitalize(modelVariant)));
|
|
503
|
+
else if (value instanceof Error) {
|
|
504
|
+
throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is unserialized Error\n\n Use function `serializeError`\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n\n "); }));
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
try {
|
|
508
|
+
for (var _b = __values(Object.entries(value)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
509
|
+
var _d = __read(_c.value, 2), subName = _d[0], subValue = _d[1];
|
|
510
|
+
if (subValue === undefined) {
|
|
511
|
+
// Note: undefined in object is serializable - it is just omited
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
checkSerializableAsJson({ name: "".concat(name, ".").concat(subName), value: subValue, message: message });
|
|
645
515
|
}
|
|
646
|
-
|
|
647
|
-
|
|
516
|
+
}
|
|
517
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
518
|
+
finally {
|
|
519
|
+
try {
|
|
520
|
+
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
648
521
|
}
|
|
522
|
+
finally { if (e_1) throw e_1.error; }
|
|
649
523
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
// Note: Nothing special here
|
|
524
|
+
try {
|
|
525
|
+
JSON.stringify(value); // <- TODO: [0]
|
|
653
526
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
contentLanguage = task.contentLanguage;
|
|
658
|
-
}
|
|
659
|
-
else {
|
|
660
|
-
contentLanguage = '';
|
|
527
|
+
catch (error) {
|
|
528
|
+
if (!(error instanceof Error)) {
|
|
529
|
+
throw error;
|
|
661
530
|
}
|
|
531
|
+
throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is not serializable\n\n ").concat(block(error.stack || error.message), "\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
|
|
662
532
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
675
|
-
finally {
|
|
676
|
-
try {
|
|
677
|
-
if (jokers_1_1 && !jokers_1_1.done && (_d = jokers_1.return)) _d.call(jokers_1);
|
|
678
|
-
}
|
|
679
|
-
finally { if (e_4) throw e_4.error; }
|
|
680
|
-
}
|
|
681
|
-
} /* not else */
|
|
682
|
-
if (postprocessing) {
|
|
683
|
-
try {
|
|
684
|
-
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()) {
|
|
685
|
-
var postprocessingFunctionName = postprocessing_1_1.value;
|
|
686
|
-
commands_1.push("POSTPROCESSING `".concat(postprocessingFunctionName, "`"));
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
690
|
-
finally {
|
|
691
|
-
try {
|
|
692
|
-
if (postprocessing_1_1 && !postprocessing_1_1.done && (_e = postprocessing_1.return)) _e.call(postprocessing_1);
|
|
693
|
-
}
|
|
694
|
-
finally { if (e_5) throw e_5.error; }
|
|
695
|
-
}
|
|
696
|
-
} /* not else */
|
|
697
|
-
if (expectations) {
|
|
698
|
-
try {
|
|
699
|
-
for (var _m = (e_6 = void 0, __values(Object.entries(expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
|
|
700
|
-
var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
|
|
701
|
-
if (min === max) {
|
|
702
|
-
commands_1.push("EXPECT EXACTLY ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
|
|
703
|
-
}
|
|
704
|
-
else {
|
|
705
|
-
if (min !== undefined) {
|
|
706
|
-
commands_1.push("EXPECT MIN ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
|
|
707
|
-
} /* not else */
|
|
708
|
-
if (max !== undefined) {
|
|
709
|
-
commands_1.push("EXPECT MAX ".concat(max, " ").concat(capitalize(unit + (max > 1 ? 's' : ''))));
|
|
710
|
-
}
|
|
711
|
-
}
|
|
533
|
+
/*
|
|
534
|
+
TODO: [0] Is there some more elegant way to check circular references?
|
|
535
|
+
const seen = new Set();
|
|
536
|
+
const stack = [{ value }];
|
|
537
|
+
while (stack.length > 0) {
|
|
538
|
+
const { value } = stack.pop()!;
|
|
539
|
+
if (typeof value === 'object' && value !== null) {
|
|
540
|
+
if (seen.has(value)) {
|
|
541
|
+
throw new UnexpectedError(`${name} has circular reference`);
|
|
712
542
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
543
|
+
seen.add(value);
|
|
544
|
+
if (Array.isArray(value)) {
|
|
545
|
+
stack.push(...value.map((value) => ({ value })));
|
|
546
|
+
} else {
|
|
547
|
+
stack.push(...Object.values(value).map((value) => ({ value })));
|
|
718
548
|
}
|
|
719
|
-
finally { if (e_6) throw e_6.error; }
|
|
720
549
|
}
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
// TODO: @deprecated remove
|
|
725
|
-
commands_1.push("FORMAT JSON");
|
|
726
|
-
}
|
|
727
|
-
} /* not else */
|
|
728
|
-
pipelineString += '\n\n';
|
|
729
|
-
pipelineString += commands_1.map(function (command) { return "- ".concat(command); }).join('\n');
|
|
730
|
-
pipelineString += '\n\n';
|
|
731
|
-
pipelineString += '```' + contentLanguage;
|
|
732
|
-
pipelineString += '\n';
|
|
733
|
-
pipelineString += spaceTrim__default["default"](content);
|
|
734
|
-
// <- TODO: [main] !!3 Escape
|
|
735
|
-
// <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
|
|
736
|
-
pipelineString += '\n';
|
|
737
|
-
pipelineString += '```';
|
|
738
|
-
pipelineString += '\n\n';
|
|
739
|
-
pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
|
|
550
|
+
}
|
|
551
|
+
*/
|
|
552
|
+
return;
|
|
740
553
|
}
|
|
741
554
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
555
|
+
else {
|
|
556
|
+
throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is unknown type\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
|
|
561
|
+
* TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
|
|
562
|
+
* Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
|
|
563
|
+
*/
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* @@@
|
|
567
|
+
*
|
|
568
|
+
* @public exported from `@promptbook/utils`
|
|
569
|
+
*/
|
|
570
|
+
function deepClone(objectValue) {
|
|
571
|
+
return JSON.parse(JSON.stringify(objectValue));
|
|
572
|
+
/*
|
|
573
|
+
TODO: [🧠] Is there a better implementation?
|
|
574
|
+
> const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
575
|
+
> for (const propertyName of propertyNames) {
|
|
576
|
+
> const value = (objectValue as really_any)[propertyName];
|
|
577
|
+
> if (value && typeof value === 'object') {
|
|
578
|
+
> deepClone(value);
|
|
579
|
+
> }
|
|
580
|
+
> }
|
|
581
|
+
> return Object.assign({}, objectValue);
|
|
582
|
+
*/
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
586
|
+
*/
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Utility to export a JSON object from a function
|
|
590
|
+
*
|
|
591
|
+
* 1) Checks if the value is serializable as JSON
|
|
592
|
+
* 2) Makes a deep clone of the object
|
|
593
|
+
* 2) Orders the object properties
|
|
594
|
+
* 2) Deeply freezes the cloned object
|
|
595
|
+
*
|
|
596
|
+
* Note: This function does not mutates the given object
|
|
597
|
+
*
|
|
598
|
+
* @returns The same type of object as the input but read-only and re-ordered
|
|
599
|
+
* @public exported from `@promptbook/utils`
|
|
600
|
+
*/
|
|
601
|
+
function exportJson(options) {
|
|
602
|
+
var name = options.name, value = options.value, order = options.order, message = options.message;
|
|
603
|
+
checkSerializableAsJson({ name: name, value: value, message: message });
|
|
604
|
+
var orderedValue =
|
|
605
|
+
// TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
|
|
606
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
607
|
+
// @ts-ignore
|
|
608
|
+
order === undefined
|
|
609
|
+
? deepClone(value)
|
|
610
|
+
: orderJson({
|
|
611
|
+
value: value,
|
|
612
|
+
// <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
|
|
613
|
+
order: order,
|
|
614
|
+
});
|
|
615
|
+
$deepFreeze(orderedValue);
|
|
616
|
+
return orderedValue;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
620
|
+
*/
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Order of keys in the pipeline JSON
|
|
624
|
+
*
|
|
625
|
+
* @public exported from `@promptbook/core`
|
|
626
|
+
*/
|
|
627
|
+
var ORDER_OF_PIPELINE_JSON = [
|
|
628
|
+
// Note: [🍙] In this order will be pipeline serialized
|
|
629
|
+
'title',
|
|
630
|
+
'pipelineUrl',
|
|
631
|
+
'bookVersion',
|
|
632
|
+
'description',
|
|
633
|
+
'formfactorName',
|
|
634
|
+
'parameters',
|
|
635
|
+
'tasks',
|
|
636
|
+
'personas',
|
|
637
|
+
'preparations',
|
|
638
|
+
'knowledgeSources',
|
|
639
|
+
'knowledgePieces',
|
|
640
|
+
'sources', // <- TODO: [🧠] Where should the `sources` be
|
|
641
|
+
];
|
|
642
|
+
/**
|
|
643
|
+
* Nonce which is used for replacing things in strings
|
|
644
|
+
*
|
|
645
|
+
* @private within the repository
|
|
646
|
+
*/
|
|
647
|
+
var REPLACING_NONCE = 'ptbkauk42kV2dzao34faw7FudQUHYPtW';
|
|
648
|
+
/**
|
|
649
|
+
* @@@
|
|
650
|
+
*
|
|
651
|
+
* @private within the repository
|
|
652
|
+
*/
|
|
653
|
+
var RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
|
|
654
|
+
/**
|
|
655
|
+
* @@@
|
|
656
|
+
*
|
|
657
|
+
* @private within the repository
|
|
658
|
+
*/
|
|
659
|
+
var RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
|
|
660
|
+
/**
|
|
661
|
+
* The names of the parameters that are reserved for special purposes
|
|
662
|
+
*
|
|
663
|
+
* @public exported from `@promptbook/core`
|
|
664
|
+
*/
|
|
665
|
+
var RESERVED_PARAMETER_NAMES = exportJson({
|
|
666
|
+
name: 'RESERVED_PARAMETER_NAMES',
|
|
667
|
+
message: "The names of the parameters that are reserved for special purposes",
|
|
668
|
+
value: [
|
|
669
|
+
'content',
|
|
670
|
+
'context',
|
|
671
|
+
'knowledge',
|
|
672
|
+
'examples',
|
|
673
|
+
'modelName',
|
|
674
|
+
'currentDate',
|
|
675
|
+
// <- TODO: list here all command names
|
|
676
|
+
// <- TODO: Add more like 'date', 'modelName',...
|
|
677
|
+
// <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
|
|
678
|
+
],
|
|
679
|
+
});
|
|
680
|
+
/**
|
|
681
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
682
|
+
*/
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
686
|
+
*
|
|
687
|
+
* @public exported from `@promptbook/core`
|
|
688
|
+
*/
|
|
689
|
+
var ParseError = /** @class */ (function (_super) {
|
|
690
|
+
__extends(ParseError, _super);
|
|
691
|
+
function ParseError(message) {
|
|
692
|
+
var _this = _super.call(this, message) || this;
|
|
693
|
+
_this.name = 'ParseError';
|
|
694
|
+
Object.setPrototypeOf(_this, ParseError.prototype);
|
|
695
|
+
return _this;
|
|
748
696
|
}
|
|
749
|
-
return
|
|
750
|
-
}
|
|
697
|
+
return ParseError;
|
|
698
|
+
}(Error));
|
|
751
699
|
/**
|
|
752
|
-
*
|
|
700
|
+
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
753
701
|
*/
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
|
|
705
|
+
*
|
|
706
|
+
* @public exported from `@promptbook/core`
|
|
707
|
+
*/
|
|
708
|
+
var PipelineLogicError = /** @class */ (function (_super) {
|
|
709
|
+
__extends(PipelineLogicError, _super);
|
|
710
|
+
function PipelineLogicError(message) {
|
|
711
|
+
var _this = _super.call(this, message) || this;
|
|
712
|
+
_this.name = 'PipelineLogicError';
|
|
713
|
+
Object.setPrototypeOf(_this, PipelineLogicError.prototype);
|
|
714
|
+
return _this;
|
|
759
715
|
}
|
|
760
|
-
return
|
|
761
|
-
}
|
|
716
|
+
return PipelineLogicError;
|
|
717
|
+
}(Error));
|
|
718
|
+
|
|
762
719
|
/**
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
720
|
+
* Tests if given string is valid semantic version
|
|
721
|
+
*
|
|
722
|
+
* Note: There are two simmilar functions:
|
|
723
|
+
* - `isValidSemanticVersion` which tests any semantic version
|
|
724
|
+
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
725
|
+
*
|
|
726
|
+
* @public exported from `@promptbook/utils`
|
|
768
727
|
*/
|
|
728
|
+
function isValidSemanticVersion(version) {
|
|
729
|
+
if (typeof version !== 'string') {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
if (version.startsWith('0.0.0')) {
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
735
|
+
return /^\d+\.\d+\.\d+(-\d+)?$/i.test(version);
|
|
736
|
+
}
|
|
769
737
|
|
|
770
738
|
/**
|
|
771
|
-
*
|
|
739
|
+
* Tests if given string is valid promptbook version
|
|
740
|
+
* It looks into list of known promptbook versions.
|
|
741
|
+
*
|
|
742
|
+
* @see https://www.npmjs.com/package/promptbook?activeTab=versions
|
|
743
|
+
* 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.
|
|
744
|
+
* Note: There are two simmilar functions:
|
|
745
|
+
* - `isValidSemanticVersion` which tests any semantic version
|
|
746
|
+
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
772
747
|
*
|
|
773
|
-
* @returns The same type of object as the input re-ordered
|
|
774
748
|
* @public exported from `@promptbook/utils`
|
|
775
749
|
*/
|
|
776
|
-
function
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
750
|
+
function isValidPromptbookVersion(version) {
|
|
751
|
+
if (!isValidSemanticVersion(version)) {
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
if ( /* version === '1.0.0' || */version === '2.0.0' || version === '3.0.0') {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
// <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
|
|
758
|
+
return true;
|
|
780
759
|
}
|
|
781
760
|
|
|
782
761
|
/**
|
|
783
|
-
*
|
|
762
|
+
* Tests if given string is valid URL.
|
|
784
763
|
*
|
|
785
|
-
* Note:
|
|
786
|
-
* Note:
|
|
764
|
+
* Note: Dataurl are considered perfectly valid.
|
|
765
|
+
* Note: There are two simmilar functions:
|
|
766
|
+
* - `isValidUrl` which tests any URL
|
|
767
|
+
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
787
768
|
*
|
|
788
|
-
* @returns The same object as the input, but deeply frozen
|
|
789
769
|
* @public exported from `@promptbook/utils`
|
|
790
770
|
*/
|
|
791
|
-
function
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
return Object.freeze(objectValue.map(function (item) { return $deepFreeze(item); }));
|
|
771
|
+
function isValidUrl(url) {
|
|
772
|
+
if (typeof url !== 'string') {
|
|
773
|
+
return false;
|
|
795
774
|
}
|
|
796
|
-
var propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
797
775
|
try {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
var value = objectValue[propertyName];
|
|
801
|
-
if (value && typeof value === 'object') {
|
|
802
|
-
$deepFreeze(value);
|
|
803
|
-
}
|
|
776
|
+
if (url.startsWith('blob:')) {
|
|
777
|
+
url = url.replace(/^blob:/, '');
|
|
804
778
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
try {
|
|
809
|
-
if (propertyNames_1_1 && !propertyNames_1_1.done && (_a = propertyNames_1.return)) _a.call(propertyNames_1);
|
|
779
|
+
var urlObject = new URL(url /* because fail is handled */);
|
|
780
|
+
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
781
|
+
return false;
|
|
810
782
|
}
|
|
811
|
-
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
return false;
|
|
812
787
|
}
|
|
813
|
-
Object.freeze(objectValue);
|
|
814
|
-
return objectValue;
|
|
815
788
|
}
|
|
816
|
-
/**
|
|
817
|
-
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
818
|
-
*/
|
|
819
789
|
|
|
820
790
|
/**
|
|
821
|
-
*
|
|
791
|
+
* Tests if given string is valid pipeline URL URL.
|
|
822
792
|
*
|
|
823
|
-
*
|
|
793
|
+
* Note: There are two simmilar functions:
|
|
794
|
+
* - `isValidUrl` which tests any URL
|
|
795
|
+
* - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
|
|
796
|
+
*
|
|
797
|
+
* @public exported from `@promptbook/utils`
|
|
824
798
|
*/
|
|
825
|
-
function
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
799
|
+
function isValidPipelineUrl(url) {
|
|
800
|
+
if (!isValidUrl(url)) {
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
if (!url.startsWith('https://') && !url.startsWith('http://') /* <- Note: [👣] */) {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
if (url.includes('#')) {
|
|
807
|
+
// TODO: [🐠]
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
/*
|
|
811
|
+
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
812
|
+
if (isUrlOnPrivateNetwork(url)) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
*/
|
|
816
|
+
return true;
|
|
836
817
|
}
|
|
837
|
-
|
|
838
818
|
/**
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
* @public exported from `@promptbook/core`
|
|
819
|
+
* TODO: [🐠] Maybe more info why the URL is invalid
|
|
842
820
|
*/
|
|
843
|
-
var UnexpectedError = /** @class */ (function (_super) {
|
|
844
|
-
__extends(UnexpectedError, _super);
|
|
845
|
-
function UnexpectedError(message) {
|
|
846
|
-
var _this = _super.call(this, spaceTrim.spaceTrim(function (block) { return "\n ".concat(block(message), "\n\n Note: This error should not happen.\n It's probbably a bug in the pipeline collection\n\n Please report issue:\n ").concat(block(getErrorReportUrl(new Error(message)).href), "\n\n Or contact us on ").concat(ADMIN_EMAIL, "\n\n "); })) || this;
|
|
847
|
-
_this.name = 'UnexpectedError';
|
|
848
|
-
Object.setPrototypeOf(_this, UnexpectedError.prototype);
|
|
849
|
-
return _this;
|
|
850
|
-
}
|
|
851
|
-
return UnexpectedError;
|
|
852
|
-
}(Error));
|
|
853
821
|
|
|
854
822
|
/**
|
|
855
|
-
*
|
|
856
|
-
* If not, throws an UnexpectedError with a rich error message and tracking
|
|
823
|
+
* Validates PipelineJson if it is logically valid
|
|
857
824
|
*
|
|
858
|
-
*
|
|
859
|
-
* -
|
|
860
|
-
* - `NaN` is not serializable
|
|
861
|
-
* - Objects and arrays are serializable if all their properties are serializable
|
|
862
|
-
* - Functions are not serializable
|
|
863
|
-
* - Circular references are not serializable
|
|
864
|
-
* - `Date` objects are not serializable
|
|
865
|
-
* - `Map` and `Set` objects are not serializable
|
|
866
|
-
* - `RegExp` objects are not serializable
|
|
867
|
-
* - `Error` objects are not serializable
|
|
868
|
-
* - `Symbol` objects are not serializable
|
|
869
|
-
* - And much more...
|
|
825
|
+
* It checks:
|
|
826
|
+
* - if it has correct parameters dependency
|
|
870
827
|
*
|
|
871
|
-
*
|
|
872
|
-
*
|
|
828
|
+
* It does NOT check:
|
|
829
|
+
* - if it is valid json
|
|
830
|
+
* - if it is meaningful
|
|
831
|
+
*
|
|
832
|
+
* @param pipeline valid or invalid PipelineJson
|
|
833
|
+
* @returns the same pipeline if it is logically valid
|
|
834
|
+
* @throws {PipelineLogicError} on logical error in the pipeline
|
|
835
|
+
* @public exported from `@promptbook/core`
|
|
873
836
|
*/
|
|
874
|
-
function
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
if (value === undefined) {
|
|
878
|
-
throw new UnexpectedError("".concat(name, " is undefined"));
|
|
879
|
-
}
|
|
880
|
-
else if (value === null) {
|
|
881
|
-
return;
|
|
837
|
+
function validatePipeline(pipeline) {
|
|
838
|
+
if (IS_PIPELINE_LOGIC_VALIDATED) {
|
|
839
|
+
validatePipeline_InnerFunction(pipeline);
|
|
882
840
|
}
|
|
883
|
-
else
|
|
884
|
-
|
|
841
|
+
else {
|
|
842
|
+
try {
|
|
843
|
+
validatePipeline_InnerFunction(pipeline);
|
|
844
|
+
}
|
|
845
|
+
catch (error) {
|
|
846
|
+
if (!(error instanceof PipelineLogicError)) {
|
|
847
|
+
throw error;
|
|
848
|
+
}
|
|
849
|
+
console.error(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is not valid but logic errors are temporarily disabled via `IS_PIPELINE_LOGIC_VALIDATED`\n\n ".concat(block(error.message), "\n "); }));
|
|
850
|
+
}
|
|
885
851
|
}
|
|
886
|
-
|
|
887
|
-
|
|
852
|
+
return pipeline;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* @private internal function for `validatePipeline`
|
|
856
|
+
*/
|
|
857
|
+
function validatePipeline_InnerFunction(pipeline) {
|
|
858
|
+
// TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
|
|
859
|
+
var e_1, _a, e_2, _b, e_3, _c;
|
|
860
|
+
var pipelineIdentification = (function () {
|
|
861
|
+
// Note: This is a 😐 implementation of [🚞]
|
|
862
|
+
var _ = [];
|
|
863
|
+
if (pipeline.sourceFile !== undefined) {
|
|
864
|
+
_.push("File: ".concat(pipeline.sourceFile));
|
|
865
|
+
}
|
|
866
|
+
if (pipeline.pipelineUrl !== undefined) {
|
|
867
|
+
_.push("Url: ".concat(pipeline.pipelineUrl));
|
|
868
|
+
}
|
|
869
|
+
return _.join('\n');
|
|
870
|
+
})();
|
|
871
|
+
if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
|
|
872
|
+
// <- Note: [🚲]
|
|
873
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid promptbook URL \"".concat(pipeline.pipelineUrl, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
888
874
|
}
|
|
889
|
-
|
|
890
|
-
|
|
875
|
+
if (pipeline.bookVersion !== undefined && !isValidPromptbookVersion(pipeline.bookVersion)) {
|
|
876
|
+
// <- Note: [🚲]
|
|
877
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid Promptbook Version \"".concat(pipeline.bookVersion, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
891
878
|
}
|
|
892
|
-
|
|
893
|
-
|
|
879
|
+
// TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
|
|
880
|
+
if (!Array.isArray(pipeline.parameters)) {
|
|
881
|
+
// TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
|
|
882
|
+
throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.parameters` expected to be an array, but got ".concat(typeof pipeline.parameters, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
894
883
|
}
|
|
895
|
-
|
|
896
|
-
|
|
884
|
+
// TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
|
|
885
|
+
if (!Array.isArray(pipeline.tasks)) {
|
|
886
|
+
// TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
|
|
887
|
+
throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.tasks` expected to be an array, but got ".concat(typeof pipeline.tasks, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
897
888
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
889
|
+
var _loop_1 = function (parameter) {
|
|
890
|
+
if (parameter.isInput && parameter.isOutput) {
|
|
891
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n\n Parameter `{".concat(parameter.name, "}` can not be both input and output\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
901
892
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
893
|
+
// Note: Testing that parameter is either intermediate or output BUT not created and unused
|
|
894
|
+
if (!parameter.isInput &&
|
|
895
|
+
!parameter.isOutput &&
|
|
896
|
+
!pipeline.tasks.some(function (task) { return task.dependentParameterNames.includes(parameter.name); })) {
|
|
897
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(parameter.name, "}` is created but not used\n\n You can declare {").concat(parameter.name, "} as output parameter by adding in the header:\n - OUTPUT PARAMETER `{").concat(parameter.name, "}` ").concat(parameter.description || '', "\n\n ").concat(block(pipelineIdentification), "\n\n "); }));
|
|
906
898
|
}
|
|
907
|
-
|
|
908
|
-
|
|
899
|
+
// Note: Testing that parameter is either input or result of some task
|
|
900
|
+
if (!parameter.isInput && !pipeline.tasks.some(function (task) { return task.resultingParameterName === parameter.name; })) {
|
|
901
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(parameter.name, "}` is declared but not defined\n\n You can do one of these:\n 1) Remove declaration of `{").concat(parameter.name, "}`\n 2) Add task that results in `-> {").concat(parameter.name, "}`\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
909
902
|
}
|
|
910
|
-
|
|
911
|
-
|
|
903
|
+
};
|
|
904
|
+
try {
|
|
905
|
+
/*
|
|
906
|
+
TODO: [🧠][🅾] Should be empty pipeline valid or not
|
|
907
|
+
// Note: Check that pipeline has some tasks
|
|
908
|
+
if (pipeline.tasks.length === 0) {
|
|
909
|
+
throw new PipelineLogicError(
|
|
910
|
+
spaceTrim(
|
|
911
|
+
(block) => `
|
|
912
|
+
Pipeline must have at least one task
|
|
913
|
+
|
|
914
|
+
${block(pipelineIdentification)}
|
|
915
|
+
`,
|
|
916
|
+
),
|
|
917
|
+
);
|
|
912
918
|
}
|
|
913
|
-
|
|
914
|
-
|
|
919
|
+
*/
|
|
920
|
+
// Note: Check each parameter individually
|
|
921
|
+
for (var _d = __values(pipeline.parameters), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
922
|
+
var parameter = _e.value;
|
|
923
|
+
_loop_1(parameter);
|
|
915
924
|
}
|
|
916
|
-
|
|
917
|
-
|
|
925
|
+
}
|
|
926
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
927
|
+
finally {
|
|
928
|
+
try {
|
|
929
|
+
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
|
918
930
|
}
|
|
919
|
-
|
|
931
|
+
finally { if (e_1) throw e_1.error; }
|
|
932
|
+
}
|
|
933
|
+
// Note: All input parameters are defined - so that they can be used as result of some task
|
|
934
|
+
var definedParameters = new Set(pipeline.parameters.filter(function (_a) {
|
|
935
|
+
var isInput = _a.isInput;
|
|
936
|
+
return isInput;
|
|
937
|
+
}).map(function (_a) {
|
|
938
|
+
var name = _a.name;
|
|
939
|
+
return name;
|
|
940
|
+
}));
|
|
941
|
+
var _loop_2 = function (task) {
|
|
942
|
+
var e_4, _h, e_5, _j;
|
|
943
|
+
if (definedParameters.has(task.resultingParameterName)) {
|
|
944
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(task.resultingParameterName, "}` is defined multiple times\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
945
|
+
}
|
|
946
|
+
if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
|
|
947
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter name {".concat(task.resultingParameterName, "} is reserved, please use different name\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
948
|
+
}
|
|
949
|
+
definedParameters.add(task.resultingParameterName);
|
|
950
|
+
if (task.jokerParameterNames && task.jokerParameterNames.length > 0) {
|
|
951
|
+
if (!task.format &&
|
|
952
|
+
!task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
|
|
953
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Joker parameters are used for {".concat(task.resultingParameterName, "} but no expectations are defined\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
954
|
+
}
|
|
955
|
+
var _loop_4 = function (joker) {
|
|
956
|
+
if (!task.dependentParameterNames.includes(joker)) {
|
|
957
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(joker, "}` is used for {").concat(task.resultingParameterName, "} as joker but not in `dependentParameterNames`\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
958
|
+
}
|
|
959
|
+
};
|
|
920
960
|
try {
|
|
921
|
-
for (var
|
|
922
|
-
var
|
|
923
|
-
|
|
924
|
-
// Note: undefined in object is serializable - it is just omited
|
|
925
|
-
continue;
|
|
926
|
-
}
|
|
927
|
-
checkSerializableAsJson({ name: "".concat(name, ".").concat(subName), value: subValue, message: message });
|
|
961
|
+
for (var _k = (e_4 = void 0, __values(task.jokerParameterNames)), _l = _k.next(); !_l.done; _l = _k.next()) {
|
|
962
|
+
var joker = _l.value;
|
|
963
|
+
_loop_4(joker);
|
|
928
964
|
}
|
|
929
965
|
}
|
|
930
|
-
catch (
|
|
966
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
931
967
|
finally {
|
|
932
968
|
try {
|
|
933
|
-
if (
|
|
969
|
+
if (_l && !_l.done && (_h = _k.return)) _h.call(_k);
|
|
934
970
|
}
|
|
935
|
-
finally { if (
|
|
971
|
+
finally { if (e_4) throw e_4.error; }
|
|
936
972
|
}
|
|
973
|
+
}
|
|
974
|
+
if (task.expectations) {
|
|
975
|
+
var _loop_5 = function (unit, min, max) {
|
|
976
|
+
if (min !== undefined && max !== undefined && min > max) {
|
|
977
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation (=".concat(min, ") of ").concat(unit, " is higher than max expectation (=").concat(max, ")\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
978
|
+
}
|
|
979
|
+
if (min !== undefined && min < 0) {
|
|
980
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation of ".concat(unit, " must be zero or positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
981
|
+
}
|
|
982
|
+
if (max !== undefined && max <= 0) {
|
|
983
|
+
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Max expectation of ".concat(unit, " must be positive\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
984
|
+
}
|
|
985
|
+
};
|
|
937
986
|
try {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
if (!(error instanceof Error)) {
|
|
942
|
-
throw error;
|
|
987
|
+
for (var _m = (e_5 = void 0, __values(Object.entries(task.expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
|
|
988
|
+
var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
|
|
989
|
+
_loop_5(unit, min, max);
|
|
943
990
|
}
|
|
944
|
-
throw new UnexpectedError(spaceTrim__default["default"](function (block) { return "\n `".concat(name, "` is not serializable\n\n ").concat(block(error.stack || error.message), "\n\n Additional message for `").concat(name, "`:\n ").concat(block(message || '(nothing)'), "\n "); }));
|
|
945
991
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
while (stack.length > 0) {
|
|
951
|
-
const { value } = stack.pop()!;
|
|
952
|
-
if (typeof value === 'object' && value !== null) {
|
|
953
|
-
if (seen.has(value)) {
|
|
954
|
-
throw new UnexpectedError(`${name} has circular reference`);
|
|
955
|
-
}
|
|
956
|
-
seen.add(value);
|
|
957
|
-
if (Array.isArray(value)) {
|
|
958
|
-
stack.push(...value.map((value) => ({ value })));
|
|
959
|
-
} else {
|
|
960
|
-
stack.push(...Object.values(value).map((value) => ({ value })));
|
|
961
|
-
}
|
|
992
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
993
|
+
finally {
|
|
994
|
+
try {
|
|
995
|
+
if (_o && !_o.done && (_j = _m.return)) _j.call(_m);
|
|
962
996
|
}
|
|
997
|
+
finally { if (e_5) throw e_5.error; }
|
|
963
998
|
}
|
|
964
|
-
|
|
965
|
-
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
try {
|
|
1002
|
+
// Note: Checking each task individually
|
|
1003
|
+
for (var _f = __values(pipeline.tasks), _g = _f.next(); !_g.done; _g = _f.next()) {
|
|
1004
|
+
var task = _g.value;
|
|
1005
|
+
_loop_2(task);
|
|
966
1006
|
}
|
|
967
1007
|
}
|
|
968
|
-
|
|
969
|
-
|
|
1008
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
1009
|
+
finally {
|
|
1010
|
+
try {
|
|
1011
|
+
if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
|
|
1012
|
+
}
|
|
1013
|
+
finally { if (e_2) throw e_2.error; }
|
|
1014
|
+
}
|
|
1015
|
+
// Note: Detect circular dependencies
|
|
1016
|
+
var resovedParameters = pipeline.parameters
|
|
1017
|
+
.filter(function (_a) {
|
|
1018
|
+
var isInput = _a.isInput;
|
|
1019
|
+
return isInput;
|
|
1020
|
+
})
|
|
1021
|
+
.map(function (_a) {
|
|
1022
|
+
var name = _a.name;
|
|
1023
|
+
return name;
|
|
1024
|
+
});
|
|
1025
|
+
try {
|
|
1026
|
+
// Note: All reserved parameters are resolved
|
|
1027
|
+
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()) {
|
|
1028
|
+
var reservedParameterName = RESERVED_PARAMETER_NAMES_1_1.value;
|
|
1029
|
+
resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), [reservedParameterName], false);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
1033
|
+
finally {
|
|
1034
|
+
try {
|
|
1035
|
+
if (RESERVED_PARAMETER_NAMES_1_1 && !RESERVED_PARAMETER_NAMES_1_1.done && (_c = RESERVED_PARAMETER_NAMES_1.return)) _c.call(RESERVED_PARAMETER_NAMES_1);
|
|
1036
|
+
}
|
|
1037
|
+
finally { if (e_3) throw e_3.error; }
|
|
1038
|
+
}
|
|
1039
|
+
var unresovedTasks = __spreadArray([], __read(pipeline.tasks), false);
|
|
1040
|
+
var loopLimit = LOOP_LIMIT;
|
|
1041
|
+
var _loop_3 = function () {
|
|
1042
|
+
if (loopLimit-- < 0) {
|
|
1043
|
+
// Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
|
|
1044
|
+
throw new UnexpectedError(spaceTrim.spaceTrim(function (block) { return "\n Loop limit reached during detection of circular dependencies in `validatePipeline`\n\n ".concat(block(pipelineIdentification), "\n "); }));
|
|
1045
|
+
}
|
|
1046
|
+
var currentlyResovedTasks = unresovedTasks.filter(function (task) {
|
|
1047
|
+
return task.dependentParameterNames.every(function (name) { return resovedParameters.includes(name); });
|
|
1048
|
+
});
|
|
1049
|
+
if (currentlyResovedTasks.length === 0) {
|
|
1050
|
+
throw new PipelineLogicError(
|
|
1051
|
+
// TODO: [🐎] DRY
|
|
1052
|
+
spaceTrim.spaceTrim(function (block) { return "\n\n Can not resolve some parameters:\n Either you are using a parameter that is not defined, or there are some circular dependencies.\n\n ".concat(block(pipelineIdentification), "\n\n **Can not resolve:**\n ").concat(block(unresovedTasks
|
|
1053
|
+
.map(function (_a) {
|
|
1054
|
+
var resultingParameterName = _a.resultingParameterName, dependentParameterNames = _a.dependentParameterNames;
|
|
1055
|
+
return "- Parameter `{".concat(resultingParameterName, "}` which depends on ").concat(dependentParameterNames
|
|
1056
|
+
.map(function (dependentParameterName) { return "`{".concat(dependentParameterName, "}`"); })
|
|
1057
|
+
.join(' and '));
|
|
1058
|
+
})
|
|
1059
|
+
.join('\n')), "\n\n **Resolved:**\n ").concat(block(resovedParameters
|
|
1060
|
+
.filter(function (name) {
|
|
1061
|
+
return !RESERVED_PARAMETER_NAMES.includes(name);
|
|
1062
|
+
})
|
|
1063
|
+
.map(function (name) { return "- Parameter `{".concat(name, "}`"); })
|
|
1064
|
+
.join('\n')), "\n\n\n **Reserved (which are available):**\n ").concat(block(resovedParameters
|
|
1065
|
+
.filter(function (name) {
|
|
1066
|
+
return RESERVED_PARAMETER_NAMES.includes(name);
|
|
1067
|
+
})
|
|
1068
|
+
.map(function (name) { return "- Parameter `{".concat(name, "}`"); })
|
|
1069
|
+
.join('\n')), "\n\n\n "); }));
|
|
1070
|
+
}
|
|
1071
|
+
resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), __read(currentlyResovedTasks.map(function (_a) {
|
|
1072
|
+
var resultingParameterName = _a.resultingParameterName;
|
|
1073
|
+
return resultingParameterName;
|
|
1074
|
+
})), false);
|
|
1075
|
+
unresovedTasks = unresovedTasks.filter(function (task) { return !currentlyResovedTasks.includes(task); });
|
|
1076
|
+
};
|
|
1077
|
+
while (unresovedTasks.length > 0) {
|
|
1078
|
+
_loop_3();
|
|
970
1079
|
}
|
|
1080
|
+
// Note: Check that formfactor is corresponding to the pipeline interface
|
|
1081
|
+
// TODO: !!6 Implement this
|
|
1082
|
+
// pipeline.formfactorName
|
|
971
1083
|
}
|
|
972
1084
|
/**
|
|
973
|
-
* TODO:
|
|
974
|
-
* TODO: [🧠]
|
|
975
|
-
*
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
*
|
|
1085
|
+
* TODO: [🧞♀️] Do not allow joker + foreach
|
|
1086
|
+
* TODO: [🧠] Work with promptbookVersion
|
|
1087
|
+
* TODO: Use here some json-schema, Zod or something similar and change it to:
|
|
1088
|
+
* > /**
|
|
1089
|
+
* > * Validates PipelineJson if it is logically valid.
|
|
1090
|
+
* > *
|
|
1091
|
+
* > * It checks:
|
|
1092
|
+
* > * - it has a valid structure
|
|
1093
|
+
* > * - ...
|
|
1094
|
+
* > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
|
|
982
1095
|
*/
|
|
983
|
-
function deepClone(objectValue) {
|
|
984
|
-
return JSON.parse(JSON.stringify(objectValue));
|
|
985
|
-
/*
|
|
986
|
-
TODO: [🧠] Is there a better implementation?
|
|
987
|
-
> const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
988
|
-
> for (const propertyName of propertyNames) {
|
|
989
|
-
> const value = (objectValue as really_any)[propertyName];
|
|
990
|
-
> if (value && typeof value === 'object') {
|
|
991
|
-
> deepClone(value);
|
|
992
|
-
> }
|
|
993
|
-
> }
|
|
994
|
-
> return Object.assign({}, objectValue);
|
|
995
|
-
*/
|
|
996
|
-
}
|
|
997
1096
|
/**
|
|
998
|
-
* TODO: [
|
|
1097
|
+
* TODO: [🧳][main] !!4 Validate that all examples match expectations
|
|
1098
|
+
* TODO: [🧳][🐝][main] !!4 Validate that knowledge is valid (non-void)
|
|
1099
|
+
* TODO: [🧳][main] !!4 Validate that persona can be used only with CHAT variant
|
|
1100
|
+
* TODO: [🧳][main] !!4 Validate that parameter with reserved name not used RESERVED_PARAMETER_NAMES
|
|
1101
|
+
* TODO: [🧳][main] !!4 Validate that reserved parameter is not used as joker
|
|
1102
|
+
* TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
|
|
1103
|
+
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
999
1104
|
*/
|
|
1000
1105
|
|
|
1001
1106
|
/**
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1004
|
-
* 1) Checks if the value is serializable as JSON
|
|
1005
|
-
* 2) Makes a deep clone of the object
|
|
1006
|
-
* 2) Orders the object properties
|
|
1007
|
-
* 2) Deeply freezes the cloned object
|
|
1107
|
+
* Loads the books from the archive file with `.bookc` extension
|
|
1008
1108
|
*
|
|
1009
|
-
*
|
|
1109
|
+
* @param filePath Path to the archive file with `.bookc` extension
|
|
1110
|
+
* @param fs Filesystem tools
|
|
1111
|
+
* @returns Pipelines loaded from the archive
|
|
1010
1112
|
*
|
|
1011
|
-
* @
|
|
1012
|
-
* @public exported from `@promptbook/utils`
|
|
1113
|
+
* @private utility of Prompbook
|
|
1013
1114
|
*/
|
|
1014
|
-
function
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1115
|
+
function loadArchive(filePath, fs) {
|
|
1116
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1117
|
+
var data, archive, indexFile, collectionJson, _a, _b, collectionJson_1, collectionJson_1_1, pipeline;
|
|
1118
|
+
var e_1, _c;
|
|
1119
|
+
return __generator(this, function (_d) {
|
|
1120
|
+
switch (_d.label) {
|
|
1121
|
+
case 0:
|
|
1122
|
+
if (!filePath.endsWith('.bookc')) {
|
|
1123
|
+
throw new UnexpectedError("Archive file must have '.bookc' extension");
|
|
1124
|
+
}
|
|
1125
|
+
return [4 /*yield*/, fs.readFile(filePath)];
|
|
1126
|
+
case 1:
|
|
1127
|
+
data = _d.sent();
|
|
1128
|
+
return [4 /*yield*/, JSZip__default["default"].loadAsync(data)];
|
|
1129
|
+
case 2:
|
|
1130
|
+
archive = _d.sent();
|
|
1131
|
+
indexFile = archive.file('index.book.json');
|
|
1132
|
+
if (!indexFile) {
|
|
1133
|
+
throw new UnexpectedError("Archive does not contain 'index.book.json' file");
|
|
1134
|
+
}
|
|
1135
|
+
_b = (_a = JSON).parse;
|
|
1136
|
+
return [4 /*yield*/, indexFile.async('text')];
|
|
1137
|
+
case 3:
|
|
1138
|
+
collectionJson = _b.apply(_a, [_d.sent()]);
|
|
1139
|
+
try {
|
|
1140
|
+
for (collectionJson_1 = __values(collectionJson), collectionJson_1_1 = collectionJson_1.next(); !collectionJson_1_1.done; collectionJson_1_1 = collectionJson_1.next()) {
|
|
1141
|
+
pipeline = collectionJson_1_1.value;
|
|
1142
|
+
validatePipeline(pipeline);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
1146
|
+
finally {
|
|
1147
|
+
try {
|
|
1148
|
+
if (collectionJson_1_1 && !collectionJson_1_1.done && (_c = collectionJson_1.return)) _c.call(collectionJson_1);
|
|
1149
|
+
}
|
|
1150
|
+
finally { if (e_1) throw e_1.error; }
|
|
1151
|
+
}
|
|
1152
|
+
return [2 /*return*/, collectionJson];
|
|
1153
|
+
}
|
|
1027
1154
|
});
|
|
1028
|
-
|
|
1029
|
-
return orderedValue;
|
|
1155
|
+
});
|
|
1030
1156
|
}
|
|
1031
1157
|
/**
|
|
1032
|
-
*
|
|
1158
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
1033
1159
|
*/
|
|
1034
1160
|
|
|
1035
|
-
/**
|
|
1036
|
-
* Order of keys in the pipeline JSON
|
|
1037
|
-
*
|
|
1038
|
-
* @public exported from `@promptbook/core`
|
|
1039
|
-
*/
|
|
1040
|
-
var ORDER_OF_PIPELINE_JSON = [
|
|
1041
|
-
// Note: [🍙] In this order will be pipeline serialized
|
|
1042
|
-
'title',
|
|
1043
|
-
'pipelineUrl',
|
|
1044
|
-
'bookVersion',
|
|
1045
|
-
'description',
|
|
1046
|
-
'formfactorName',
|
|
1047
|
-
'parameters',
|
|
1048
|
-
'tasks',
|
|
1049
|
-
'personas',
|
|
1050
|
-
'preparations',
|
|
1051
|
-
'knowledgeSources',
|
|
1052
|
-
'knowledgePieces',
|
|
1053
|
-
'sources', // <- TODO: [🧠] Where should the `sources` be
|
|
1054
|
-
];
|
|
1055
|
-
/**
|
|
1056
|
-
* Nonce which is used for replacing things in strings
|
|
1057
|
-
*
|
|
1058
|
-
* @private within the repository
|
|
1059
|
-
*/
|
|
1060
|
-
var REPLACING_NONCE = 'ptbkauk42kV2dzao34faw7FudQUHYPtW';
|
|
1061
|
-
/**
|
|
1062
|
-
* @@@
|
|
1063
|
-
*
|
|
1064
|
-
* @private within the repository
|
|
1065
|
-
*/
|
|
1066
|
-
var RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
|
|
1067
|
-
/**
|
|
1068
|
-
* @@@
|
|
1069
|
-
*
|
|
1070
|
-
* @private within the repository
|
|
1071
|
-
*/
|
|
1072
|
-
var RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
|
|
1073
|
-
/**
|
|
1074
|
-
* The names of the parameters that are reserved for special purposes
|
|
1075
|
-
*
|
|
1076
|
-
* @public exported from `@promptbook/core`
|
|
1077
|
-
*/
|
|
1078
|
-
var RESERVED_PARAMETER_NAMES = exportJson({
|
|
1079
|
-
name: 'RESERVED_PARAMETER_NAMES',
|
|
1080
|
-
message: "The names of the parameters that are reserved for special purposes",
|
|
1081
|
-
value: [
|
|
1082
|
-
'content',
|
|
1083
|
-
'context',
|
|
1084
|
-
'knowledge',
|
|
1085
|
-
'examples',
|
|
1086
|
-
'modelName',
|
|
1087
|
-
'currentDate',
|
|
1088
|
-
// <- TODO: list here all command names
|
|
1089
|
-
// <- TODO: Add more like 'date', 'modelName',...
|
|
1090
|
-
// <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
|
|
1091
|
-
],
|
|
1092
|
-
});
|
|
1093
|
-
/**
|
|
1094
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1095
|
-
*/
|
|
1161
|
+
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"}];
|
|
1096
1162
|
|
|
1097
1163
|
/**
|
|
1098
|
-
*
|
|
1164
|
+
* Checks if value is valid email
|
|
1099
1165
|
*
|
|
1100
|
-
* @public exported from `@promptbook/
|
|
1166
|
+
* @public exported from `@promptbook/utils`
|
|
1101
1167
|
*/
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
var _this = _super.call(this, message) || this;
|
|
1106
|
-
_this.name = 'PipelineLogicError';
|
|
1107
|
-
Object.setPrototypeOf(_this, PipelineLogicError.prototype);
|
|
1108
|
-
return _this;
|
|
1168
|
+
function isValidEmail(email) {
|
|
1169
|
+
if (typeof email !== 'string') {
|
|
1170
|
+
return false;
|
|
1109
1171
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1172
|
+
if (email.split('\n').length > 1) {
|
|
1173
|
+
return false;
|
|
1174
|
+
}
|
|
1175
|
+
return /^.+@.+\..+$/.test(email);
|
|
1176
|
+
}
|
|
1112
1177
|
|
|
1113
1178
|
/**
|
|
1114
|
-
* Tests if given string is valid
|
|
1115
|
-
*
|
|
1116
|
-
* Note: There are two simmilar functions:
|
|
1117
|
-
* - `isValidSemanticVersion` which tests any semantic version
|
|
1118
|
-
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
1179
|
+
* Tests if given string is valid URL.
|
|
1119
1180
|
*
|
|
1181
|
+
* Note: This does not check if the file exists only if the path is valid
|
|
1120
1182
|
* @public exported from `@promptbook/utils`
|
|
1121
1183
|
*/
|
|
1122
|
-
function
|
|
1123
|
-
if (typeof
|
|
1184
|
+
function isValidFilePath(filename) {
|
|
1185
|
+
if (typeof filename !== 'string') {
|
|
1124
1186
|
return false;
|
|
1125
1187
|
}
|
|
1126
|
-
if (
|
|
1188
|
+
if (filename.split('\n').length > 1) {
|
|
1127
1189
|
return false;
|
|
1128
1190
|
}
|
|
1129
|
-
|
|
1191
|
+
if (filename.split(' ').length >
|
|
1192
|
+
5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1195
|
+
var filenameSlashes = filename.split('\\').join('/');
|
|
1196
|
+
// Absolute Unix path: /hello.txt
|
|
1197
|
+
if (/^(\/)/i.test(filenameSlashes)) {
|
|
1198
|
+
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
1199
|
+
return true;
|
|
1200
|
+
}
|
|
1201
|
+
// Absolute Windows path: /hello.txt
|
|
1202
|
+
if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
|
|
1203
|
+
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
1204
|
+
return true;
|
|
1205
|
+
}
|
|
1206
|
+
// Relative path: ./hello.txt
|
|
1207
|
+
if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
|
|
1208
|
+
// console.log(filename, 'Relative path: ./hello.txt');
|
|
1209
|
+
return true;
|
|
1210
|
+
}
|
|
1211
|
+
// Allow paths like foo/hello
|
|
1212
|
+
if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
|
|
1213
|
+
// console.log(filename, 'Allow paths like foo/hello');
|
|
1214
|
+
return true;
|
|
1215
|
+
}
|
|
1216
|
+
// Allow paths like hello.book
|
|
1217
|
+
if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
|
|
1218
|
+
// console.log(filename, 'Allow paths like hello.book');
|
|
1219
|
+
return true;
|
|
1220
|
+
}
|
|
1221
|
+
return false;
|
|
1130
1222
|
}
|
|
1223
|
+
/**
|
|
1224
|
+
* TODO: [🍏] Implement for MacOs
|
|
1225
|
+
*/
|
|
1131
1226
|
|
|
1132
1227
|
/**
|
|
1133
|
-
*
|
|
1134
|
-
* It looks into list of known promptbook versions.
|
|
1135
|
-
*
|
|
1136
|
-
* @see https://www.npmjs.com/package/promptbook?activeTab=versions
|
|
1137
|
-
* 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.
|
|
1138
|
-
* Note: There are two simmilar functions:
|
|
1139
|
-
* - `isValidSemanticVersion` which tests any semantic version
|
|
1140
|
-
* - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
|
|
1228
|
+
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
1141
1229
|
*
|
|
1142
1230
|
* @public exported from `@promptbook/utils`
|
|
1143
1231
|
*/
|
|
1144
|
-
function
|
|
1145
|
-
|
|
1146
|
-
|
|
1232
|
+
function isValidJsonString(value /* <- [👨⚖️] */) {
|
|
1233
|
+
try {
|
|
1234
|
+
JSON.parse(value);
|
|
1235
|
+
return true;
|
|
1147
1236
|
}
|
|
1148
|
-
|
|
1237
|
+
catch (error) {
|
|
1238
|
+
if (!(error instanceof Error)) {
|
|
1239
|
+
throw error;
|
|
1240
|
+
}
|
|
1241
|
+
if (error.message.includes('Unexpected token')) {
|
|
1242
|
+
return false;
|
|
1243
|
+
}
|
|
1149
1244
|
return false;
|
|
1150
1245
|
}
|
|
1151
|
-
// <- TODO: [main] !!3 Check isValidPromptbookVersion against PROMPTBOOK_ENGINE_VERSIONS
|
|
1152
|
-
return true;
|
|
1153
1246
|
}
|
|
1154
|
-
|
|
1155
|
-
/**
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
* Note: There are two simmilar functions:
|
|
1159
|
-
* - `isValidUrl` which tests any URL
|
|
1160
|
-
* - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
|
|
1247
|
+
|
|
1248
|
+
/**
|
|
1249
|
+
* Function `validatePipelineString` will validate the if the string is a valid pipeline string
|
|
1250
|
+
* 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.
|
|
1161
1251
|
*
|
|
1162
|
-
* @
|
|
1252
|
+
* @param {string} pipelineString the candidate for a pipeline string
|
|
1253
|
+
* @returns {PipelineString} the same string as input, but validated as valid
|
|
1254
|
+
* @throws {ParseError} if the string is not a valid pipeline string
|
|
1255
|
+
* @public exported from `@promptbook/core`
|
|
1163
1256
|
*/
|
|
1164
|
-
function
|
|
1165
|
-
if (
|
|
1166
|
-
|
|
1257
|
+
function validatePipelineString(pipelineString) {
|
|
1258
|
+
if (isValidJsonString(pipelineString)) {
|
|
1259
|
+
throw new ParseError('Expected a book, but got a JSON string');
|
|
1167
1260
|
}
|
|
1168
|
-
if (
|
|
1169
|
-
|
|
1261
|
+
else if (isValidUrl(pipelineString)) {
|
|
1262
|
+
throw new ParseError("Expected a book, but got just the URL \"".concat(pipelineString, "\""));
|
|
1170
1263
|
}
|
|
1171
|
-
if (
|
|
1172
|
-
|
|
1173
|
-
return false;
|
|
1264
|
+
else if (isValidFilePath(pipelineString)) {
|
|
1265
|
+
throw new ParseError("Expected a book, but got just the file path \"".concat(pipelineString, "\""));
|
|
1174
1266
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
if (isUrlOnPrivateNetwork(url)) {
|
|
1178
|
-
return false;
|
|
1267
|
+
else if (isValidEmail(pipelineString)) {
|
|
1268
|
+
throw new ParseError("Expected a book, but got just the email \"".concat(pipelineString, "\""));
|
|
1179
1269
|
}
|
|
1180
|
-
|
|
1181
|
-
return
|
|
1270
|
+
// <- TODO: Implement the validation + add tests when the pipeline logic considered as invalid
|
|
1271
|
+
return pipelineString;
|
|
1182
1272
|
}
|
|
1183
1273
|
/**
|
|
1184
|
-
* TODO: [
|
|
1274
|
+
* TODO: [🧠][🈴] Where is the best location for this file
|
|
1185
1275
|
*/
|
|
1186
1276
|
|
|
1187
1277
|
/**
|
|
1188
|
-
*
|
|
1189
|
-
*
|
|
1190
|
-
* It checks:
|
|
1191
|
-
* - if it has correct parameters dependency
|
|
1192
|
-
*
|
|
1193
|
-
* It does NOT check:
|
|
1194
|
-
* - if it is valid json
|
|
1195
|
-
* - if it is meaningful
|
|
1278
|
+
* Prettify the html code
|
|
1196
1279
|
*
|
|
1197
|
-
* @param
|
|
1198
|
-
* @returns
|
|
1199
|
-
* @
|
|
1200
|
-
* @public exported from `@promptbook/core`
|
|
1280
|
+
* @param content raw html code
|
|
1281
|
+
* @returns formatted html code
|
|
1282
|
+
* @private withing the package because of HUGE size of prettier dependency
|
|
1201
1283
|
*/
|
|
1202
|
-
function
|
|
1203
|
-
|
|
1204
|
-
|
|
1284
|
+
function prettifyMarkdown(content) {
|
|
1285
|
+
try {
|
|
1286
|
+
return prettier.format(content, {
|
|
1287
|
+
parser: 'markdown',
|
|
1288
|
+
plugins: [parserHtml__default["default"]],
|
|
1289
|
+
// TODO: DRY - make some import or auto-copy of .prettierrc
|
|
1290
|
+
endOfLine: 'lf',
|
|
1291
|
+
tabWidth: 4,
|
|
1292
|
+
singleQuote: true,
|
|
1293
|
+
trailingComma: 'all',
|
|
1294
|
+
arrowParens: 'always',
|
|
1295
|
+
printWidth: 120,
|
|
1296
|
+
htmlWhitespaceSensitivity: 'ignore',
|
|
1297
|
+
jsxBracketSameLine: false,
|
|
1298
|
+
bracketSpacing: true,
|
|
1299
|
+
});
|
|
1205
1300
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
}
|
|
1214
|
-
console.error(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is not valid but logic errors are temporarily disabled via `IS_PIPELINE_LOGIC_VALIDATED`\n\n ".concat(block(error.message), "\n "); }));
|
|
1215
|
-
}
|
|
1301
|
+
catch (error) {
|
|
1302
|
+
// TODO: [🟥] Detect browser / node and make it colorfull
|
|
1303
|
+
console.error('There was an error with prettifying the markdown, using the original as the fallback', {
|
|
1304
|
+
error: error,
|
|
1305
|
+
html: content,
|
|
1306
|
+
});
|
|
1307
|
+
return content;
|
|
1216
1308
|
}
|
|
1217
|
-
return pipeline;
|
|
1218
1309
|
}
|
|
1310
|
+
|
|
1219
1311
|
/**
|
|
1220
|
-
*
|
|
1312
|
+
* Makes first letter of a string uppercase
|
|
1313
|
+
*
|
|
1314
|
+
* @public exported from `@promptbook/utils`
|
|
1221
1315
|
*/
|
|
1222
|
-
function
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
// <- Note: [🚲]
|
|
1242
|
-
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Invalid Promptbook Version \"".concat(pipeline.bookVersion, "\"\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
1316
|
+
function capitalize(word) {
|
|
1317
|
+
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* Converts promptbook in JSON format to string format
|
|
1322
|
+
*
|
|
1323
|
+
* @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
|
|
1324
|
+
* @param pipelineJson Promptbook in JSON format (.bookc)
|
|
1325
|
+
* @returns Promptbook in string format (.book.md)
|
|
1326
|
+
* @public exported from `@promptbook/core`
|
|
1327
|
+
*/
|
|
1328
|
+
function pipelineJsonToString(pipelineJson) {
|
|
1329
|
+
var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f;
|
|
1330
|
+
var title = pipelineJson.title, pipelineUrl = pipelineJson.pipelineUrl, bookVersion = pipelineJson.bookVersion, description = pipelineJson.description, parameters = pipelineJson.parameters, tasks = pipelineJson.tasks;
|
|
1331
|
+
var pipelineString = "# ".concat(title);
|
|
1332
|
+
if (description) {
|
|
1333
|
+
pipelineString += '\n\n';
|
|
1334
|
+
pipelineString += description;
|
|
1243
1335
|
}
|
|
1244
|
-
|
|
1245
|
-
if (
|
|
1246
|
-
|
|
1247
|
-
throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.parameters` expected to be an array, but got ".concat(typeof pipeline.parameters, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
1336
|
+
var commands = [];
|
|
1337
|
+
if (pipelineUrl) {
|
|
1338
|
+
commands.push("PIPELINE URL ".concat(pipelineUrl));
|
|
1248
1339
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
// TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
|
|
1252
|
-
throw new ParseError(spaceTrim.spaceTrim(function (block) { return "\n Pipeline is valid JSON but with wrong structure\n\n `PipelineJson.tasks` expected to be an array, but got ".concat(typeof pipeline.tasks, "\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
1340
|
+
if (bookVersion !== "undefined") {
|
|
1341
|
+
commands.push("BOOK VERSION ".concat(bookVersion));
|
|
1253
1342
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n\n Parameter `{".concat(parameter.name, "}` can not be both input and output\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
1257
|
-
}
|
|
1258
|
-
// Note: Testing that parameter is either intermediate or output BUT not created and unused
|
|
1259
|
-
if (!parameter.isInput &&
|
|
1260
|
-
!parameter.isOutput &&
|
|
1261
|
-
!pipeline.tasks.some(function (task) { return task.dependentParameterNames.includes(parameter.name); })) {
|
|
1262
|
-
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(parameter.name, "}` is created but not used\n\n You can declare {").concat(parameter.name, "} as output parameter by adding in the header:\n - OUTPUT PARAMETER `{").concat(parameter.name, "}` ").concat(parameter.description || '', "\n\n ").concat(block(pipelineIdentification), "\n\n "); }));
|
|
1263
|
-
}
|
|
1264
|
-
// Note: Testing that parameter is either input or result of some task
|
|
1265
|
-
if (!parameter.isInput && !pipeline.tasks.some(function (task) { return task.resultingParameterName === parameter.name; })) {
|
|
1266
|
-
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(parameter.name, "}` is declared but not defined\n\n You can do one of these:\n 1) Remove declaration of `{").concat(parameter.name, "}`\n 2) Add task that results in `-> {").concat(parameter.name, "}`\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
1267
|
-
}
|
|
1268
|
-
};
|
|
1343
|
+
// TODO: [main] !!5 This increases size of the bundle and is probbably not necessary
|
|
1344
|
+
pipelineString = prettifyMarkdown(pipelineString);
|
|
1269
1345
|
try {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
(block) => `
|
|
1277
|
-
Pipeline must have at least one task
|
|
1278
|
-
|
|
1279
|
-
${block(pipelineIdentification)}
|
|
1280
|
-
`,
|
|
1281
|
-
),
|
|
1282
|
-
);
|
|
1283
|
-
}
|
|
1284
|
-
*/
|
|
1285
|
-
// Note: Check each parameter individually
|
|
1286
|
-
for (var _d = __values(pipeline.parameters), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
1287
|
-
var parameter = _e.value;
|
|
1288
|
-
_loop_1(parameter);
|
|
1346
|
+
for (var _g = __values(parameters.filter(function (_a) {
|
|
1347
|
+
var isInput = _a.isInput;
|
|
1348
|
+
return isInput;
|
|
1349
|
+
})), _h = _g.next(); !_h.done; _h = _g.next()) {
|
|
1350
|
+
var parameter = _h.value;
|
|
1351
|
+
commands.push("INPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
|
|
1289
1352
|
}
|
|
1290
1353
|
}
|
|
1291
1354
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
1292
1355
|
finally {
|
|
1293
1356
|
try {
|
|
1294
|
-
if (
|
|
1357
|
+
if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
|
|
1295
1358
|
}
|
|
1296
1359
|
finally { if (e_1) throw e_1.error; }
|
|
1297
1360
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
}));
|
|
1306
|
-
var _loop_2 = function (task) {
|
|
1307
|
-
var e_4, _h, e_5, _j;
|
|
1308
|
-
if (definedParameters.has(task.resultingParameterName)) {
|
|
1309
|
-
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Parameter `{".concat(task.resultingParameterName, "}` is defined multiple times\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
1361
|
+
try {
|
|
1362
|
+
for (var _j = __values(parameters.filter(function (_a) {
|
|
1363
|
+
var isOutput = _a.isOutput;
|
|
1364
|
+
return isOutput;
|
|
1365
|
+
})), _k = _j.next(); !_k.done; _k = _j.next()) {
|
|
1366
|
+
var parameter = _k.value;
|
|
1367
|
+
commands.push("OUTPUT PARAMETER ".concat(taskParameterJsonToString(parameter)));
|
|
1310
1368
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1369
|
+
}
|
|
1370
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
1371
|
+
finally {
|
|
1372
|
+
try {
|
|
1373
|
+
if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
|
|
1313
1374
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1375
|
+
finally { if (e_2) throw e_2.error; }
|
|
1376
|
+
}
|
|
1377
|
+
pipelineString += '\n\n';
|
|
1378
|
+
pipelineString += commands.map(function (command) { return "- ".concat(command); }).join('\n');
|
|
1379
|
+
try {
|
|
1380
|
+
for (var tasks_1 = __values(tasks), tasks_1_1 = tasks_1.next(); !tasks_1_1.done; tasks_1_1 = tasks_1.next()) {
|
|
1381
|
+
var task = tasks_1_1.value;
|
|
1382
|
+
var
|
|
1383
|
+
/* Note: Not using:> name, */
|
|
1384
|
+
title_1 = task.title, description_1 = task.description,
|
|
1385
|
+
/* Note: dependentParameterNames, */
|
|
1386
|
+
jokers = task.jokerParameterNames, taskType = task.taskType, content = task.content, postprocessing = task.postprocessingFunctionNames, expectations = task.expectations, format = task.format, resultingParameterName = task.resultingParameterName;
|
|
1387
|
+
pipelineString += '\n\n';
|
|
1388
|
+
pipelineString += "## ".concat(title_1);
|
|
1389
|
+
if (description_1) {
|
|
1390
|
+
pipelineString += '\n\n';
|
|
1391
|
+
pipelineString += description_1;
|
|
1392
|
+
}
|
|
1393
|
+
var commands_1 = [];
|
|
1394
|
+
var contentLanguage = 'text';
|
|
1395
|
+
if (taskType === 'PROMPT_TASK') {
|
|
1396
|
+
var modelRequirements = task.modelRequirements;
|
|
1397
|
+
var _l = modelRequirements || {}, modelName = _l.modelName, modelVariant = _l.modelVariant;
|
|
1398
|
+
// Note: Do nothing, it is default
|
|
1399
|
+
// commands.push(`PROMPT`);
|
|
1400
|
+
if (modelVariant) {
|
|
1401
|
+
commands_1.push("MODEL VARIANT ".concat(capitalize(modelVariant)));
|
|
1402
|
+
}
|
|
1403
|
+
if (modelName) {
|
|
1404
|
+
commands_1.push("MODEL NAME `".concat(modelName, "`"));
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
else if (taskType === 'SIMPLE_TASK') {
|
|
1408
|
+
commands_1.push("SIMPLE TEMPLATE");
|
|
1409
|
+
// Note: Nothing special here
|
|
1319
1410
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1411
|
+
else if (taskType === 'SCRIPT_TASK') {
|
|
1412
|
+
commands_1.push("SCRIPT");
|
|
1413
|
+
if (task.contentLanguage) {
|
|
1414
|
+
contentLanguage = task.contentLanguage;
|
|
1323
1415
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
for (var _k = (e_4 = void 0, __values(task.jokerParameterNames)), _l = _k.next(); !_l.done; _l = _k.next()) {
|
|
1327
|
-
var joker = _l.value;
|
|
1328
|
-
_loop_4(joker);
|
|
1416
|
+
else {
|
|
1417
|
+
contentLanguage = '';
|
|
1329
1418
|
}
|
|
1330
1419
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1420
|
+
else if (taskType === 'DIALOG_TASK') {
|
|
1421
|
+
commands_1.push("DIALOG");
|
|
1422
|
+
// Note: Nothing special here
|
|
1423
|
+
} // <- }else if([🅱]
|
|
1424
|
+
if (jokers) {
|
|
1333
1425
|
try {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
}
|
|
1339
|
-
if (task.expectations) {
|
|
1340
|
-
var _loop_5 = function (unit, min, max) {
|
|
1341
|
-
if (min !== undefined && max !== undefined && min > max) {
|
|
1342
|
-
throw new PipelineLogicError(spaceTrim.spaceTrim(function (block) { return "\n Min expectation (=".concat(min, ") of ").concat(unit, " is higher than max expectation (=").concat(max, ")\n\n ").concat(block(pipelineIdentification), "\n "); }));
|
|
1426
|
+
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()) {
|
|
1427
|
+
var joker = jokers_1_1.value;
|
|
1428
|
+
commands_1.push("JOKER {".concat(joker, "}"));
|
|
1429
|
+
}
|
|
1343
1430
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1431
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
1432
|
+
finally {
|
|
1433
|
+
try {
|
|
1434
|
+
if (jokers_1_1 && !jokers_1_1.done && (_d = jokers_1.return)) _d.call(jokers_1);
|
|
1435
|
+
}
|
|
1436
|
+
finally { if (e_4) throw e_4.error; }
|
|
1346
1437
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1438
|
+
} /* not else */
|
|
1439
|
+
if (postprocessing) {
|
|
1440
|
+
try {
|
|
1441
|
+
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()) {
|
|
1442
|
+
var postprocessingFunctionName = postprocessing_1_1.value;
|
|
1443
|
+
commands_1.push("POSTPROCESSING `".concat(postprocessingFunctionName, "`"));
|
|
1444
|
+
}
|
|
1349
1445
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1446
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
1447
|
+
finally {
|
|
1448
|
+
try {
|
|
1449
|
+
if (postprocessing_1_1 && !postprocessing_1_1.done && (_e = postprocessing_1.return)) _e.call(postprocessing_1);
|
|
1450
|
+
}
|
|
1451
|
+
finally { if (e_5) throw e_5.error; }
|
|
1355
1452
|
}
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
finally {
|
|
1453
|
+
} /* not else */
|
|
1454
|
+
if (expectations) {
|
|
1359
1455
|
try {
|
|
1360
|
-
|
|
1456
|
+
for (var _m = (e_6 = void 0, __values(Object.entries(expectations))), _o = _m.next(); !_o.done; _o = _m.next()) {
|
|
1457
|
+
var _p = __read(_o.value, 2), unit = _p[0], _q = _p[1], min = _q.min, max = _q.max;
|
|
1458
|
+
if (min === max) {
|
|
1459
|
+
commands_1.push("EXPECT EXACTLY ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
|
|
1460
|
+
}
|
|
1461
|
+
else {
|
|
1462
|
+
if (min !== undefined) {
|
|
1463
|
+
commands_1.push("EXPECT MIN ".concat(min, " ").concat(capitalize(unit + (min > 1 ? 's' : ''))));
|
|
1464
|
+
} /* not else */
|
|
1465
|
+
if (max !== undefined) {
|
|
1466
|
+
commands_1.push("EXPECT MAX ".concat(max, " ").concat(capitalize(unit + (max > 1 ? 's' : ''))));
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1361
1470
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
return name;
|
|
1389
|
-
});
|
|
1390
|
-
try {
|
|
1391
|
-
// Note: All reserved parameters are resolved
|
|
1392
|
-
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()) {
|
|
1393
|
-
var reservedParameterName = RESERVED_PARAMETER_NAMES_1_1.value;
|
|
1394
|
-
resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), [reservedParameterName], false);
|
|
1471
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
1472
|
+
finally {
|
|
1473
|
+
try {
|
|
1474
|
+
if (_o && !_o.done && (_f = _m.return)) _f.call(_m);
|
|
1475
|
+
}
|
|
1476
|
+
finally { if (e_6) throw e_6.error; }
|
|
1477
|
+
}
|
|
1478
|
+
} /* not else */
|
|
1479
|
+
if (format) {
|
|
1480
|
+
if (format === 'JSON') {
|
|
1481
|
+
// TODO: @deprecated remove
|
|
1482
|
+
commands_1.push("FORMAT JSON");
|
|
1483
|
+
}
|
|
1484
|
+
} /* not else */
|
|
1485
|
+
pipelineString += '\n\n';
|
|
1486
|
+
pipelineString += commands_1.map(function (command) { return "- ".concat(command); }).join('\n');
|
|
1487
|
+
pipelineString += '\n\n';
|
|
1488
|
+
pipelineString += '```' + contentLanguage;
|
|
1489
|
+
pipelineString += '\n';
|
|
1490
|
+
pipelineString += spaceTrim__default["default"](content);
|
|
1491
|
+
// <- TODO: [main] !!3 Escape
|
|
1492
|
+
// <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
|
|
1493
|
+
pipelineString += '\n';
|
|
1494
|
+
pipelineString += '```';
|
|
1495
|
+
pipelineString += '\n\n';
|
|
1496
|
+
pipelineString += "`-> {".concat(resultingParameterName, "}`"); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
|
|
1395
1497
|
}
|
|
1396
1498
|
}
|
|
1397
1499
|
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
1398
1500
|
finally {
|
|
1399
1501
|
try {
|
|
1400
|
-
if (
|
|
1502
|
+
if (tasks_1_1 && !tasks_1_1.done && (_c = tasks_1.return)) _c.call(tasks_1);
|
|
1401
1503
|
}
|
|
1402
1504
|
finally { if (e_3) throw e_3.error; }
|
|
1403
1505
|
}
|
|
1404
|
-
|
|
1405
|
-
var loopLimit = LOOP_LIMIT;
|
|
1406
|
-
var _loop_3 = function () {
|
|
1407
|
-
if (loopLimit-- < 0) {
|
|
1408
|
-
// Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
|
|
1409
|
-
throw new UnexpectedError(spaceTrim.spaceTrim(function (block) { return "\n Loop limit reached during detection of circular dependencies in `validatePipeline`\n\n ".concat(block(pipelineIdentification), "\n "); }));
|
|
1410
|
-
}
|
|
1411
|
-
var currentlyResovedTasks = unresovedTasks.filter(function (task) {
|
|
1412
|
-
return task.dependentParameterNames.every(function (name) { return resovedParameters.includes(name); });
|
|
1413
|
-
});
|
|
1414
|
-
if (currentlyResovedTasks.length === 0) {
|
|
1415
|
-
throw new PipelineLogicError(
|
|
1416
|
-
// TODO: [🐎] DRY
|
|
1417
|
-
spaceTrim.spaceTrim(function (block) { return "\n\n Can not resolve some parameters:\n Either you are using a parameter that is not defined, or there are some circular dependencies.\n\n ".concat(block(pipelineIdentification), "\n\n **Can not resolve:**\n ").concat(block(unresovedTasks
|
|
1418
|
-
.map(function (_a) {
|
|
1419
|
-
var resultingParameterName = _a.resultingParameterName, dependentParameterNames = _a.dependentParameterNames;
|
|
1420
|
-
return "- Parameter `{".concat(resultingParameterName, "}` which depends on ").concat(dependentParameterNames
|
|
1421
|
-
.map(function (dependentParameterName) { return "`{".concat(dependentParameterName, "}`"); })
|
|
1422
|
-
.join(' and '));
|
|
1423
|
-
})
|
|
1424
|
-
.join('\n')), "\n\n **Resolved:**\n ").concat(block(resovedParameters
|
|
1425
|
-
.filter(function (name) {
|
|
1426
|
-
return !RESERVED_PARAMETER_NAMES.includes(name);
|
|
1427
|
-
})
|
|
1428
|
-
.map(function (name) { return "- Parameter `{".concat(name, "}`"); })
|
|
1429
|
-
.join('\n')), "\n\n\n **Reserved (which are available):**\n ").concat(block(resovedParameters
|
|
1430
|
-
.filter(function (name) {
|
|
1431
|
-
return RESERVED_PARAMETER_NAMES.includes(name);
|
|
1432
|
-
})
|
|
1433
|
-
.map(function (name) { return "- Parameter `{".concat(name, "}`"); })
|
|
1434
|
-
.join('\n')), "\n\n\n "); }));
|
|
1435
|
-
}
|
|
1436
|
-
resovedParameters = __spreadArray(__spreadArray([], __read(resovedParameters), false), __read(currentlyResovedTasks.map(function (_a) {
|
|
1437
|
-
var resultingParameterName = _a.resultingParameterName;
|
|
1438
|
-
return resultingParameterName;
|
|
1439
|
-
})), false);
|
|
1440
|
-
unresovedTasks = unresovedTasks.filter(function (task) { return !currentlyResovedTasks.includes(task); });
|
|
1441
|
-
};
|
|
1442
|
-
while (unresovedTasks.length > 0) {
|
|
1443
|
-
_loop_3();
|
|
1444
|
-
}
|
|
1445
|
-
// Note: Check that formfactor is corresponding to the pipeline interface
|
|
1446
|
-
// TODO: !!6 Implement this
|
|
1447
|
-
// pipeline.formfactorName
|
|
1506
|
+
return validatePipelineString(pipelineString);
|
|
1448
1507
|
}
|
|
1449
1508
|
/**
|
|
1450
|
-
*
|
|
1451
|
-
* TODO: [🧠] Work with promptbookVersion
|
|
1452
|
-
* TODO: Use here some json-schema, Zod or something similar and change it to:
|
|
1453
|
-
* > /**
|
|
1454
|
-
* > * Validates PipelineJson if it is logically valid.
|
|
1455
|
-
* > *
|
|
1456
|
-
* > * It checks:
|
|
1457
|
-
* > * - it has a valid structure
|
|
1458
|
-
* > * - ...
|
|
1459
|
-
* > ex port function validatePipeline(promptbook: really_unknown): asserts promptbook is PipelineJson {
|
|
1509
|
+
* @private internal utility of `pipelineJsonToString`
|
|
1460
1510
|
*/
|
|
1511
|
+
function taskParameterJsonToString(taskParameterJson) {
|
|
1512
|
+
var name = taskParameterJson.name, description = taskParameterJson.description;
|
|
1513
|
+
var parameterString = "{".concat(name, "}");
|
|
1514
|
+
if (description) {
|
|
1515
|
+
parameterString = "".concat(parameterString, " ").concat(description);
|
|
1516
|
+
}
|
|
1517
|
+
return parameterString;
|
|
1518
|
+
}
|
|
1461
1519
|
/**
|
|
1462
|
-
* TODO: [
|
|
1463
|
-
* TODO: [
|
|
1464
|
-
* TODO: [
|
|
1465
|
-
* TODO: [
|
|
1466
|
-
* TODO: [
|
|
1467
|
-
* TODO: [🧠] Validation not only logic itself but imports around - files and websites and rerefenced pipelines exists
|
|
1468
|
-
* TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
1520
|
+
* TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
|
|
1521
|
+
* TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
|
|
1522
|
+
* TODO: [🏛] Maybe make some markdown builder
|
|
1523
|
+
* TODO: [🏛] Escape all
|
|
1524
|
+
* TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
|
|
1469
1525
|
*/
|
|
1470
1526
|
|
|
1471
1527
|
/**
|
|
@@ -11295,17 +11351,17 @@
|
|
|
11295
11351
|
}
|
|
11296
11352
|
_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;
|
|
11297
11353
|
collection = createCollectionFromPromise(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
11298
|
-
var fileNames, collection, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1;
|
|
11299
|
-
var e_1, _a;
|
|
11300
|
-
return __generator(this, function (
|
|
11301
|
-
switch (
|
|
11354
|
+
var fileNames, collection, pipelinesWithFilenames, _loop_1, fileNames_1, fileNames_1_1, fileName, e_1_1, _loop_2, pipelinesWithFilenames_1, pipelinesWithFilenames_1_1, pipelineWithFilenames;
|
|
11355
|
+
var e_1, _a, e_2, _b;
|
|
11356
|
+
return __generator(this, function (_c) {
|
|
11357
|
+
switch (_c.label) {
|
|
11302
11358
|
case 0:
|
|
11303
11359
|
if (isVerbose) {
|
|
11304
11360
|
console.info(colors__default["default"].cyan("Creating pipeline collection from path ".concat(rootPath.split('\\').join('/'))));
|
|
11305
11361
|
}
|
|
11306
11362
|
return [4 /*yield*/, listAllFiles(rootPath, isRecursive, tools.fs)];
|
|
11307
11363
|
case 1:
|
|
11308
|
-
fileNames =
|
|
11364
|
+
fileNames = _c.sent();
|
|
11309
11365
|
// Note: First load compiled `.bookc` files and then source `.book` files
|
|
11310
11366
|
// `.bookc` are already compiled and can be used faster
|
|
11311
11367
|
fileNames.sort(function (a, b) {
|
|
@@ -11318,100 +11374,54 @@
|
|
|
11318
11374
|
return 0;
|
|
11319
11375
|
});
|
|
11320
11376
|
collection = new Map();
|
|
11377
|
+
pipelinesWithFilenames = [];
|
|
11321
11378
|
_loop_1 = function (fileName) {
|
|
11322
|
-
var sourceFile, rootDirname,
|
|
11323
|
-
return __generator(this, function (
|
|
11324
|
-
switch (
|
|
11379
|
+
var sourceFile, rootDirname, pipelineString, _d, pipeline, _e, _f, _g, _h, error_1, wrappedErrorMessage;
|
|
11380
|
+
return __generator(this, function (_j) {
|
|
11381
|
+
switch (_j.label) {
|
|
11325
11382
|
case 0:
|
|
11326
11383
|
sourceFile = './' + fileName.split('\\').join('/');
|
|
11327
11384
|
rootDirname = path.dirname(sourceFile).split('\\').join('/');
|
|
11328
|
-
|
|
11385
|
+
_j.label = 1;
|
|
11329
11386
|
case 1:
|
|
11330
|
-
|
|
11331
|
-
pipeline = null;
|
|
11387
|
+
_j.trys.push([1, 8, , 9]);
|
|
11332
11388
|
if (!(fileName.endsWith('.book') || fileName.endsWith('.book.md'))) return [3 /*break*/, 4];
|
|
11333
|
-
|
|
11389
|
+
_d = validatePipelineString;
|
|
11334
11390
|
return [4 /*yield*/, promises.readFile(fileName, 'utf-8')];
|
|
11335
11391
|
case 2:
|
|
11336
|
-
pipelineString =
|
|
11392
|
+
pipelineString = _d.apply(void 0, [_j.sent()]);
|
|
11337
11393
|
return [4 /*yield*/, compilePipeline(pipelineString, tools, {
|
|
11338
11394
|
rootDirname: rootDirname,
|
|
11339
11395
|
})];
|
|
11340
11396
|
case 3:
|
|
11341
|
-
pipeline =
|
|
11342
|
-
pipeline
|
|
11397
|
+
pipeline = _j.sent();
|
|
11398
|
+
pipelinesWithFilenames.push({ fileName: fileName, sourceFile: sourceFile, pipeline: __assign(__assign({}, pipeline), { sourceFile: sourceFile }) });
|
|
11343
11399
|
return [3 /*break*/, 7];
|
|
11344
11400
|
case 4:
|
|
11345
11401
|
if (!(fileName.endsWith('.bookc') || fileName.endsWith('.book.json'))) return [3 /*break*/, 6];
|
|
11346
|
-
|
|
11347
|
-
|
|
11402
|
+
_f =
|
|
11403
|
+
// TODO: Handle non-valid JSON files
|
|
11404
|
+
(_e = pipelinesWithFilenames.push).apply;
|
|
11405
|
+
_g = [
|
|
11406
|
+
// TODO: Handle non-valid JSON files
|
|
11407
|
+
pipelinesWithFilenames];
|
|
11408
|
+
_h = [[]];
|
|
11409
|
+
return [4 /*yield*/, loadArchive(fileName, tools.fs)];
|
|
11348
11410
|
case 5:
|
|
11349
11411
|
// TODO: Handle non-valid JSON files
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
|
|
11412
|
+
_f.apply(_e, _g.concat([__spreadArray.apply(void 0, _h.concat([__read.apply(void 0, [(_j.sent()).map(function (pipeline) {
|
|
11413
|
+
// TODO: [🌗]
|
|
11414
|
+
return ({ fileName: fileName, sourceFile: sourceFile, pipeline: __assign(__assign({}, pipeline), { sourceFile: sourceFile }) });
|
|
11415
|
+
})]), false]))]));
|
|
11353
11416
|
return [3 /*break*/, 7];
|
|
11354
11417
|
case 6:
|
|
11355
11418
|
if (isVerbose) {
|
|
11356
11419
|
console.info(colors__default["default"].gray("Skipped file ".concat(fileName.split('\\').join('/'), " \u2013\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060 Not a book")));
|
|
11357
11420
|
}
|
|
11358
|
-
|
|
11359
|
-
case 7:
|
|
11360
|
-
// ---
|
|
11361
|
-
if (pipeline !== null) {
|
|
11362
|
-
if (rootUrl !== undefined) {
|
|
11363
|
-
if (pipeline.pipelineUrl === undefined) {
|
|
11364
|
-
pipelineUrl = rootUrl + '/' + path.relative(rootPath, fileName).split('\\').join('/');
|
|
11365
|
-
// console.log({ pipelineUrl, rootPath, rootUrl, fileName });
|
|
11366
|
-
if (isVerbose) {
|
|
11367
|
-
console.info(colors__default["default"].yellow("Implicitly set pipeline URL to ".concat(pipelineUrl, " from ").concat(fileName
|
|
11368
|
-
.split('\\')
|
|
11369
|
-
.join('/'))));
|
|
11370
|
-
}
|
|
11371
|
-
pipeline = __assign(__assign({}, pipeline), { pipelineUrl: pipelineUrl });
|
|
11372
|
-
}
|
|
11373
|
-
else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
|
|
11374
|
-
throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is not a child of the root URL ").concat(rootUrl, " \uD83C\uDF4F\n\n File:\n ").concat(sourceFile || 'Unknown', "\n\n ")));
|
|
11375
|
-
}
|
|
11376
|
-
}
|
|
11377
|
-
// TODO: [👠] DRY
|
|
11378
|
-
if (pipeline.pipelineUrl === undefined) {
|
|
11379
|
-
if (isVerbose) {
|
|
11380
|
-
console.info(colors__default["default"].yellow("Can not load pipeline from ".concat(fileName
|
|
11381
|
-
.split('\\')
|
|
11382
|
-
.join('/'), " because of missing URL")));
|
|
11383
|
-
}
|
|
11384
|
-
}
|
|
11385
|
-
else {
|
|
11386
|
-
// Note: [🐨] Pipeline is checked multiple times
|
|
11387
|
-
// TODO: Maybe once is enough BUT be sure to check it - better to check it multiple times than not at all
|
|
11388
|
-
validatePipeline(pipeline);
|
|
11389
|
-
if (
|
|
11390
|
-
// TODO: [🐽] comparePipelines(pipeline1,pipeline2): 'IDENTICAL' |'IDENTICAL_UNPREPARED' | 'IDENTICAL_INTERFACE' | 'DIFFERENT'
|
|
11391
|
-
!collection.has(pipeline.pipelineUrl)) {
|
|
11392
|
-
if (isVerbose) {
|
|
11393
|
-
console.info(colors__default["default"].green("Loaded pipeline ".concat(fileName.split('\\').join('/'), "\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060")));
|
|
11394
|
-
}
|
|
11395
|
-
// Note: [🦄] Pipeline with same url uniqueness will be double-checked automatically in SimplePipelineCollection
|
|
11396
|
-
collection.set(pipeline.pipelineUrl, pipeline);
|
|
11397
|
-
}
|
|
11398
|
-
else if (pipelineJsonToString(unpreparePipeline(pipeline)) ===
|
|
11399
|
-
pipelineJsonToString(unpreparePipeline(collection.get(pipeline.pipelineUrl)))) {
|
|
11400
|
-
if (isVerbose) {
|
|
11401
|
-
console.info(colors__default["default"].gray("Skipped pipeline ".concat(fileName
|
|
11402
|
-
.split('\\')
|
|
11403
|
-
.join('/'), " \u2013\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060 Already identical pipeline in the collection")));
|
|
11404
|
-
}
|
|
11405
|
-
}
|
|
11406
|
-
else {
|
|
11407
|
-
existing = collection.get(pipeline.pipelineUrl);
|
|
11408
|
-
throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is already in the collection \uD83C\uDF4F\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
|
|
11409
|
-
}
|
|
11410
|
-
}
|
|
11411
|
-
}
|
|
11412
|
-
return [3 /*break*/, 9];
|
|
11421
|
+
_j.label = 7;
|
|
11422
|
+
case 7: return [3 /*break*/, 9];
|
|
11413
11423
|
case 8:
|
|
11414
|
-
error_1 =
|
|
11424
|
+
error_1 = _j.sent();
|
|
11415
11425
|
if (!(error_1 instanceof Error)) {
|
|
11416
11426
|
throw error_1;
|
|
11417
11427
|
}
|
|
@@ -11426,24 +11436,24 @@
|
|
|
11426
11436
|
}
|
|
11427
11437
|
});
|
|
11428
11438
|
};
|
|
11429
|
-
|
|
11439
|
+
_c.label = 2;
|
|
11430
11440
|
case 2:
|
|
11431
|
-
|
|
11441
|
+
_c.trys.push([2, 7, 8, 9]);
|
|
11432
11442
|
fileNames_1 = __values(fileNames), fileNames_1_1 = fileNames_1.next();
|
|
11433
|
-
|
|
11443
|
+
_c.label = 3;
|
|
11434
11444
|
case 3:
|
|
11435
11445
|
if (!!fileNames_1_1.done) return [3 /*break*/, 6];
|
|
11436
11446
|
fileName = fileNames_1_1.value;
|
|
11437
11447
|
return [5 /*yield**/, _loop_1(fileName)];
|
|
11438
11448
|
case 4:
|
|
11439
|
-
|
|
11440
|
-
|
|
11449
|
+
_c.sent();
|
|
11450
|
+
_c.label = 5;
|
|
11441
11451
|
case 5:
|
|
11442
11452
|
fileNames_1_1 = fileNames_1.next();
|
|
11443
11453
|
return [3 /*break*/, 3];
|
|
11444
11454
|
case 6: return [3 /*break*/, 9];
|
|
11445
11455
|
case 7:
|
|
11446
|
-
e_1_1 =
|
|
11456
|
+
e_1_1 = _c.sent();
|
|
11447
11457
|
e_1 = { error: e_1_1 };
|
|
11448
11458
|
return [3 /*break*/, 9];
|
|
11449
11459
|
case 8:
|
|
@@ -11452,7 +11462,86 @@
|
|
|
11452
11462
|
}
|
|
11453
11463
|
finally { if (e_1) throw e_1.error; }
|
|
11454
11464
|
return [7 /*endfinally*/];
|
|
11455
|
-
case 9:
|
|
11465
|
+
case 9:
|
|
11466
|
+
_loop_2 = function (pipelineWithFilenames) {
|
|
11467
|
+
var fileName = pipelineWithFilenames.fileName, sourceFile = pipelineWithFilenames.sourceFile;
|
|
11468
|
+
var pipeline = pipelineWithFilenames.pipeline;
|
|
11469
|
+
try {
|
|
11470
|
+
if (rootUrl !== undefined) {
|
|
11471
|
+
if (pipeline.pipelineUrl === undefined) {
|
|
11472
|
+
var pipelineUrl = rootUrl + '/' + path.relative(rootPath, fileName).split('\\').join('/');
|
|
11473
|
+
// console.log({ pipelineUrl, rootPath, rootUrl, fileName });
|
|
11474
|
+
if (isVerbose) {
|
|
11475
|
+
console.info(colors__default["default"].yellow("Implicitly set pipeline URL to ".concat(pipelineUrl, " from ").concat(fileName
|
|
11476
|
+
.split('\\')
|
|
11477
|
+
.join('/'))));
|
|
11478
|
+
}
|
|
11479
|
+
pipeline = __assign(__assign({}, pipeline), { pipelineUrl: pipelineUrl });
|
|
11480
|
+
}
|
|
11481
|
+
else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
|
|
11482
|
+
throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is not a child of the root URL ").concat(rootUrl, " \uD83C\uDF4F\n\n File:\n ").concat(sourceFile || 'Unknown', "\n\n ")));
|
|
11483
|
+
}
|
|
11484
|
+
}
|
|
11485
|
+
// TODO: [👠] DRY
|
|
11486
|
+
if (pipeline.pipelineUrl === undefined) {
|
|
11487
|
+
if (isVerbose) {
|
|
11488
|
+
console.info(colors__default["default"].yellow("Can not load pipeline from ".concat(fileName.split('\\').join('/'), " because of missing URL")));
|
|
11489
|
+
}
|
|
11490
|
+
}
|
|
11491
|
+
else {
|
|
11492
|
+
// Note: [🐨] Pipeline is checked multiple times
|
|
11493
|
+
// TODO: Maybe once is enough BUT be sure to check it - better to check it multiple times than not at all
|
|
11494
|
+
validatePipeline(pipeline);
|
|
11495
|
+
if (
|
|
11496
|
+
// TODO: [🐽] comparePipelines(pipeline1,pipeline2): 'IDENTICAL' |'IDENTICAL_UNPREPARED' | 'IDENTICAL_INTERFACE' | 'DIFFERENT'
|
|
11497
|
+
!collection.has(pipeline.pipelineUrl)) {
|
|
11498
|
+
if (isVerbose) {
|
|
11499
|
+
console.info(colors__default["default"].green("Loaded pipeline ".concat(fileName.split('\\').join('/'), "\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060")));
|
|
11500
|
+
}
|
|
11501
|
+
// Note: [🦄] Pipeline with same url uniqueness will be double-checked automatically in SimplePipelineCollection
|
|
11502
|
+
collection.set(pipeline.pipelineUrl, pipeline);
|
|
11503
|
+
}
|
|
11504
|
+
else if (pipelineJsonToString(unpreparePipeline(pipeline)) ===
|
|
11505
|
+
pipelineJsonToString(unpreparePipeline(collection.get(pipeline.pipelineUrl)))) {
|
|
11506
|
+
if (isVerbose) {
|
|
11507
|
+
console.info(colors__default["default"].gray("Skipped pipeline ".concat(fileName
|
|
11508
|
+
.split('\\')
|
|
11509
|
+
.join('/'), " \u2013\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060\u2060 Already identical pipeline in the collection")));
|
|
11510
|
+
}
|
|
11511
|
+
}
|
|
11512
|
+
else {
|
|
11513
|
+
var existing = collection.get(pipeline.pipelineUrl);
|
|
11514
|
+
throw new PipelineUrlError(spaceTrim__default["default"]("\n Pipeline with URL ".concat(pipeline.pipelineUrl, " is already in the collection \uD83C\uDF4F\n\n Conflicting files:\n ").concat(existing.sourceFile || 'Unknown', "\n ").concat(pipeline.sourceFile || 'Unknown', "\n\n Note: You have probably forgotten to run \"ptbk make\" to update the collection\n Note: Pipelines with the same URL are not allowed\n Only exepction is when the pipelines are identical\n\n ")));
|
|
11515
|
+
}
|
|
11516
|
+
}
|
|
11517
|
+
}
|
|
11518
|
+
catch (error) {
|
|
11519
|
+
if (!(error instanceof Error)) {
|
|
11520
|
+
throw error;
|
|
11521
|
+
}
|
|
11522
|
+
// TODO: [7] DRY
|
|
11523
|
+
var wrappedErrorMessage = spaceTrim__default["default"](function (block) { return "\n ".concat(error.name, " in pipeline ").concat(fileName.split('\\').join('/'), "\u2060:\n\n Original error message:\n ").concat(block(error.message), "\n\n Original stack trace:\n ").concat(block(error.stack || ''), "\n\n ---\n\n "); }) + '\n';
|
|
11524
|
+
if (isCrashedOnError) {
|
|
11525
|
+
throw new CollectionError(wrappedErrorMessage);
|
|
11526
|
+
}
|
|
11527
|
+
// TODO: [🟥] Detect browser / node and make it colorfull
|
|
11528
|
+
console.error(wrappedErrorMessage);
|
|
11529
|
+
}
|
|
11530
|
+
};
|
|
11531
|
+
try {
|
|
11532
|
+
for (pipelinesWithFilenames_1 = __values(pipelinesWithFilenames), pipelinesWithFilenames_1_1 = pipelinesWithFilenames_1.next(); !pipelinesWithFilenames_1_1.done; pipelinesWithFilenames_1_1 = pipelinesWithFilenames_1.next()) {
|
|
11533
|
+
pipelineWithFilenames = pipelinesWithFilenames_1_1.value;
|
|
11534
|
+
_loop_2(pipelineWithFilenames);
|
|
11535
|
+
}
|
|
11536
|
+
}
|
|
11537
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
11538
|
+
finally {
|
|
11539
|
+
try {
|
|
11540
|
+
if (pipelinesWithFilenames_1_1 && !pipelinesWithFilenames_1_1.done && (_b = pipelinesWithFilenames_1.return)) _b.call(pipelinesWithFilenames_1);
|
|
11541
|
+
}
|
|
11542
|
+
finally { if (e_2) throw e_2.error; }
|
|
11543
|
+
}
|
|
11544
|
+
return [2 /*return*/, Array.from(collection.values())];
|
|
11456
11545
|
}
|
|
11457
11546
|
});
|
|
11458
11547
|
}); });
|
|
@@ -11517,9 +11606,11 @@
|
|
|
11517
11606
|
FileCacheStorage.prototype.getFilenameForKey = function (key) {
|
|
11518
11607
|
// TODO: [👬] DRY
|
|
11519
11608
|
var name = titleToName(key);
|
|
11609
|
+
var nameStart = name.split('-', 2)[0] || 'unnamed';
|
|
11520
11610
|
var hash = sha256__default["default"](hexEncoder__default["default"].parse(name)).toString( /* hex */);
|
|
11521
11611
|
// <- TODO: [🥬] Encapsulate sha256 to some private utility function
|
|
11522
|
-
return path.join.apply(void 0, __spreadArray(__spreadArray([this.options.rootFolderPath
|
|
11612
|
+
return path.join.apply(void 0, __spreadArray(__spreadArray([this.options.rootFolderPath,
|
|
11613
|
+
nameStart], __read(nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */)), false), ["".concat(name.substring(0, MAX_FILENAME_LENGTH), ".json")], false));
|
|
11523
11614
|
};
|
|
11524
11615
|
/**
|
|
11525
11616
|
* @@@ 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.
|