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